Skip to main content

ryo_app/
intent.rs

1//! Intent: Public DSL for code transformation
2//!
3//! # Architecture: Intent vs MutationSpec
4//!
5//! `Intent` is the **user-facing DSL** for `ryo run` command.
6//! It provides a high-level, pattern-based interface for specifying transformations.
7//!
8//! ```text
9//! ┌─────────────────────────────────────────────────────────────────┐
10//! │  Intent (this module)                                           │
11//! │  - Public DSL: `ryo run -f goal.json`                           │
12//! │  - Pattern-based: Pattern::Glob("*Config")                      │
13//! │  - High-level: one Intent may affect multiple symbols           │
14//! └───────────────────────────┬─────────────────────────────────────┘
15//!                             ↓ Symbol Resolution
16//! ┌─────────────────────────────────────────────────────────────────┐
17//! │  MutationSpec (ryo-executor::executor::spec)                    │
18//! │  - Execution-level: concrete targets                            │
19//! │  - One Intent → N MutationSpecs (after pattern expansion)       │
20//! │  - Direct SymbolId/name targeting                               │
21//! └───────────────────────────┬─────────────────────────────────────┘
22//!                             ↓ Execution
23//! ┌─────────────────────────────────────────────────────────────────┐
24//! │  AST Mutation                                                   │
25//! └─────────────────────────────────────────────────────────────────┘
26//! ```
27//!
28//! ## Intent vs MutationSpec: When to Use
29//!
30//! | Layer | Use Case | Example |
31//! |-------|----------|---------|
32//! | Intent | CLI users, DSL files | `AddField { target: "*Config", ... }` |
33//! | MutationSpec | Suggest system, programmatic API | `AddField { struct_name: "AppConfig", ... }` |
34//!
35//! The `Suggest` system bypasses Intent and generates `MutationSpec` directly,
36//! since it already has resolved symbols from analysis.
37//!
38//! # IntentExtractor (NL → Goal)
39//!
40//! ```text
41//! ┌─────────────────────────────────────────────────────────────────┐
42//! │  User Query (Natural Language)                                  │
43//! │  "ConfigをDatabaseConfigに統一"                                 │
44//! └───────────────────────────┬─────────────────────────────────────┘
45//!                             ↓
46//! ┌─────────────────────────────────────────────────────────────────┐
47//! │  IntentExtractor                                                │
48//! │  ・責務: NLクエリ → Goal (Intent + Scope + Constraints)         │
49//! │  ・LLM呼び出し1回のみ                                           │
50//! │  ・検索・変換には一切関与しない                                 │
51//! └───────────────────────────┬─────────────────────────────────────┘
52//!                             ↓
53//! ┌─────────────────────────────────────────────────────────────────┐
54//! │  Goal                                                           │
55//! │  ・intent: RenameIdent { from: "Config", to: "DatabaseConfig" } │
56//! │  ・scope: ScopeHint { file_patterns: ["**/*.rs"], ... }         │
57//! │  ・constraints: [MustCompile]                                   │
58//! └─────────────────────────────────────────────────────────────────┘
59//! ```
60
61use ryo_executor::executor::{EnumToTraitStrategy, MatchHandling};
62#[cfg(feature = "schemars")]
63use schemars::JsonSchema;
64use serde::{Deserialize, Serialize};
65
66fn default_true() -> bool {
67    true
68}
69
70fn default_confidence() -> f64 {
71    1.0
72}
73
74fn default_ident_kind_any() -> IdentKind {
75    IdentKind::Any
76}
77
78fn default_variant_type() -> String {
79    "unit".to_string()
80}
81
82fn default_method_body() -> String {
83    "todo!()".to_string()
84}
85
86// Re-export ItemKind from ryo-source (canonical definition)
87pub use ryo_source::ItemKind;
88
89// ============================================================================
90// Goal: IntentExtractorの出力
91// ============================================================================
92
93/// コンフリクト解決戦略
94#[cfg_attr(feature = "schemars", derive(JsonSchema))]
95#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
96pub enum ConflictStrategy {
97    /// Intentsの順序で実行(同じターゲットでも順序付けで解決)
98    #[default]
99    IntentOrder,
100    /// コンフリクトでエラー(厳密モード)
101    Fail,
102    /// 並列実行可能なもののみ実行、それ以外はスキップ
103    ParallelOnly,
104}
105
106/// 抽出されたゴール
107///
108/// IntentExtractorの唯一の出力物。これ以降の処理には関与しない。
109#[cfg_attr(feature = "schemars", derive(JsonSchema))]
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct Goal {
112    /// 元のクエリテキスト(DSL直接実行時は空でOK)
113    #[serde(default)]
114    pub query: String,
115
116    /// 抽出されたIntent(複数可)
117    pub intents: Vec<Intent>,
118
119    /// 検索スコープのヒント(並列Discovery用)
120    #[serde(default)]
121    pub scope: ScopeHint,
122
123    /// 実行時の制約条件
124    #[serde(default)]
125    pub constraints: Vec<Constraint>,
126
127    /// コンフリクト解決戦略
128    #[serde(default)]
129    pub conflict_strategy: ConflictStrategy,
130
131    /// 抽出時の信頼度(0.0-1.0)
132    #[serde(default = "default_confidence")]
133    pub confidence: f64,
134}
135
136// ============================================================================
137// Intent: 変換意図
138// ============================================================================
139
140/// 変換意図
141///
142/// # Future Intents (Not Yet Implemented)
143///
144/// The following Intents are planned for future implementation:
145///
146/// - `Decorator` → AddFunction + Rename (wrap function with logging/timing/retry)
147/// - `ExtractFunction` → AddFunction + ReplaceExpr (extract statements into new function)
148/// - `InlineFunction` → ReplaceExpr + RemoveFunction (inline function calls)
149///
150/// These will be implemented as compositions of existing MutationSpecs.
151#[cfg_attr(feature = "schemars", derive(JsonSchema))]
152#[derive(Debug, Clone, Serialize, Deserialize)]
153#[serde(tag = "type", deny_unknown_fields)]
154pub enum Intent {
155    // === 識別子リネーム系 ===
156    /// 単一識別子のリネーム
157    RenameIdent {
158        // --- Target (3-field) ---
159        /// SymbolId(O(1)確定、"7v2"形式)
160        #[serde(default, skip_serializing_if = "Option::is_none")]
161        symbol_id: Option<String>,
162        /// シンボルパス("tokio::net::TcpStream"形式)
163        #[serde(default, skip_serializing_if = "Option::is_none")]
164        symbol_path: Option<String>,
165        /// リネーム元の識別子名(診断用、フォールバック検索)
166        #[serde(
167            default,
168            skip_serializing_if = "Option::is_none",
169            alias = "target",
170            alias = "target_name"
171        )]
172        target_ident: Option<String>,
173        // --- Rename attributes ---
174        to: String,
175        #[serde(default = "default_ident_kind_any")]
176        kind: IdentKind,
177    },
178
179    // === 構造変更系 ===
180    /// 可視性変更
181    ChangeVisibility {
182        /// SymbolId(O(1)確定、"7v2"形式)
183        #[serde(default, skip_serializing_if = "Option::is_none")]
184        symbol_id: Option<String>,
185        /// シンボルパス("crate::module::Type"形式)
186        #[serde(default, skip_serializing_if = "Option::is_none")]
187        symbol_path: Option<String>,
188        /// ターゲット名(診断用、フォールバック検索)
189        #[serde(
190            default,
191            skip_serializing_if = "Option::is_none",
192            alias = "target",
193            alias = "target_name"
194        )]
195        target_item: Option<String>,
196        /// 変更後のvisibility
197        to: Visibility,
198    },
199
200    /// アイテムを別モジュールに移動
201    MoveItem {
202        // --- Target (3-field) ---
203        /// SymbolId(O(1)確定、"7v2"形式)
204        #[serde(default, skip_serializing_if = "Option::is_none")]
205        symbol_id: Option<String>,
206        /// シンボルパス("tokio::net::TcpStream"形式)
207        #[serde(default, skip_serializing_if = "Option::is_none")]
208        symbol_path: Option<String>,
209        /// 移動対象の名前(診断用、フォールバック検索)
210        #[serde(
211            default,
212            skip_serializing_if = "Option::is_none",
213            alias = "target",
214            alias = "target_name"
215        )]
216        target_item: Option<String>,
217        // --- Move attributes ---
218        to_module: String,
219    },
220
221    /// implからトレイトを抽出
222    ExtractTrait {
223        // --- Target impl (3-field) ---
224        /// SymbolId(O(1)確定、"7v2"形式)
225        #[serde(default, skip_serializing_if = "Option::is_none")]
226        symbol_id: Option<String>,
227        /// シンボルパス("tokio::net::TcpStream"形式)
228        #[serde(default, skip_serializing_if = "Option::is_none")]
229        symbol_path: Option<String>,
230        /// impl対象の型名(診断用、フォールバック検索)
231        #[serde(
232            default,
233            skip_serializing_if = "Option::is_none",
234            alias = "target",
235            alias = "target_name"
236        )]
237        target_type: Option<String>,
238        // --- Extract attributes ---
239        trait_name: String,
240        /// 抽出するメソッド(空 = 全メソッド)
241        #[serde(default)]
242        methods: Vec<String>,
243    },
244
245    /// トレイトをインライン化(implに戻す)
246    InlineTrait {
247        // --- Target trait (3-field) ---
248        /// Trait SymbolId
249        #[serde(default, skip_serializing_if = "Option::is_none")]
250        trait_symbol_id: Option<String>,
251        /// Trait シンボルパス
252        #[serde(default, skip_serializing_if = "Option::is_none")]
253        trait_symbol_path: Option<String>,
254        /// Trait名(診断用、フォールバック検索)
255        #[serde(
256            default,
257            skip_serializing_if = "Option::is_none",
258            alias = "target",
259            alias = "target_name"
260        )]
261        target_trait: Option<String>,
262        // --- Target struct (3-field) ---
263        /// Struct SymbolId
264        #[serde(default, skip_serializing_if = "Option::is_none")]
265        struct_symbol_id: Option<String>,
266        /// Struct シンボルパス
267        #[serde(default, skip_serializing_if = "Option::is_none")]
268        struct_symbol_path: Option<String>,
269        /// Struct名(診断用、フォールバック検索)
270        #[serde(default, skip_serializing_if = "Option::is_none")]
271        target_struct: Option<String>,
272        // --- Inline attributes ---
273        /// トレイト定義を削除するか
274        #[serde(default = "default_true")]
275        remove_trait: bool,
276    },
277
278    /// EnumをTraitに変換(Replace Conditional with Polymorphism)
279    ///
280    /// Enum variants become struct implementations of the generated trait.
281    /// Use `strategy` to control type annotation replacement.
282    EnumToTrait {
283        // --- Target enum (3-field) ---
284        /// SymbolId(O(1)確定、"7v2"形式)
285        #[serde(default, skip_serializing_if = "Option::is_none")]
286        symbol_id: Option<String>,
287        /// シンボルパス("tokio::net::TcpStream"形式)
288        #[serde(default, skip_serializing_if = "Option::is_none")]
289        symbol_path: Option<String>,
290        /// 変換対象のEnum名(診断用、フォールバック検索)
291        #[serde(
292            default,
293            skip_serializing_if = "Option::is_none",
294            alias = "target",
295            alias = "target_name"
296        )]
297        target_enum: Option<String>,
298        // --- Conversion attributes ---
299        /// 生成するTrait名(省略時はEnum名を使用)
300        #[serde(default)]
301        new_trait_name: Option<String>,
302        /// 元のEnumを削除するか
303        #[serde(default = "default_true")]
304        remove_enum: bool,
305        /// 変換戦略: Dynamic (Box<dyn>), Static (impl), MarkerOnly
306        #[serde(default)]
307        #[cfg_attr(feature = "schemars", schemars(skip))]
308        strategy: EnumToTraitStrategy,
309        /// match式の処理方法
310        #[serde(default)]
311        #[cfg_attr(feature = "schemars", schemars(skip))]
312        match_handling: MatchHandling,
313    },
314
315    // === モジュール操作系 ===
316    // Note: AddMod was consolidated into CreateMod.
317    /// モジュール宣言を削除
318    RemoveMod {
319        /// 親モジュールパス (空 = crate root)
320        #[serde(default)]
321        parent_mod: Vec<String>,
322        /// 削除するモジュール名
323        mod_name: String,
324    },
325
326    /// モジュールファイルを作成
327    CreateMod {
328        /// 親モジュールパス (空 = crate root)
329        #[serde(default)]
330        parent_mod: Vec<String>,
331        /// 作成するモジュール名
332        mod_name: String,
333        /// 初期コンテンツ
334        #[serde(default)]
335        content: String,
336        /// public かどうか
337        #[serde(default)]
338        is_pub: bool,
339    },
340
341    // === フィールド操作系 ===
342    /// フィールドを追加
343    AddField {
344        // --- Target struct (3-field) ---
345        /// SymbolId(O(1)確定、"7v2"形式)
346        #[serde(default, skip_serializing_if = "Option::is_none")]
347        symbol_id: Option<String>,
348        /// シンボルパス("tokio::net::TcpStream"形式)
349        #[serde(default, skip_serializing_if = "Option::is_none")]
350        symbol_path: Option<String>,
351        /// 対象の構造体名(診断用、フォールバック検索)
352        #[serde(
353            default,
354            skip_serializing_if = "Option::is_none",
355            alias = "target",
356            alias = "target_name"
357        )]
358        target_struct: Option<String>,
359        // --- Field attributes ---
360        field_name: String,
361        field_type: String,
362        #[serde(default)]
363        is_pub: bool,
364    },
365
366    /// フィールドを削除
367    RemoveField {
368        // --- Target struct (3-field) ---
369        /// SymbolId(O(1)確定、"7v2"形式)
370        #[serde(default, skip_serializing_if = "Option::is_none")]
371        symbol_id: Option<String>,
372        /// シンボルパス("tokio::net::TcpStream"形式)
373        #[serde(default, skip_serializing_if = "Option::is_none")]
374        symbol_path: Option<String>,
375        /// 対象の構造体名(診断用、フォールバック検索)
376        #[serde(
377            default,
378            skip_serializing_if = "Option::is_none",
379            alias = "target",
380            alias = "target_name"
381        )]
382        target_struct: Option<String>,
383        // --- Field attributes ---
384        field_name: String,
385    },
386
387    // === Derive操作系 ===
388    /// Deriveマクロを追加
389    AddDerive {
390        // --- Target type (3-field) ---
391        /// SymbolId(O(1)確定、"7v2"形式)
392        #[serde(default, skip_serializing_if = "Option::is_none")]
393        symbol_id: Option<String>,
394        /// シンボルパス("tokio::net::TcpStream"形式)
395        #[serde(default, skip_serializing_if = "Option::is_none")]
396        symbol_path: Option<String>,
397        /// 対象の型名(診断用、フォールバック検索)
398        #[serde(
399            default,
400            skip_serializing_if = "Option::is_none",
401            alias = "target",
402            alias = "target_name"
403        )]
404        target_type: Option<String>,
405        // --- Derive attributes ---
406        derives: Vec<String>,
407    },
408
409    /// Deriveマクロを削除
410    RemoveDerive {
411        // --- Target type (3-field) ---
412        /// SymbolId(O(1)確定、"7v2"形式)
413        #[serde(default, skip_serializing_if = "Option::is_none")]
414        symbol_id: Option<String>,
415        /// シンボルパス("tokio::net::TcpStream"形式)
416        #[serde(default, skip_serializing_if = "Option::is_none")]
417        symbol_path: Option<String>,
418        /// 対象の型名(診断用、フォールバック検索)
419        #[serde(
420            default,
421            skip_serializing_if = "Option::is_none",
422            alias = "target",
423            alias = "target_name"
424        )]
425        target_type: Option<String>,
426        // --- Derive attributes ---
427        derives: Vec<String>,
428    },
429
430    // === Enum操作系 ===
431    /// Enumを追加
432    AddEnum {
433        /// 追加先モジュールパス(必須: "my_crate", "my_crate::domain"等)
434        symbol_path: String,
435        name: String,
436        #[serde(default)]
437        variants: Vec<String>,
438        #[serde(default)]
439        is_pub: bool,
440        #[serde(default)]
441        derives: Vec<String>,
442    },
443
444    /// Enumにバリアントを追加
445    AddVariant {
446        // --- Target enum (3-field) ---
447        /// SymbolId(O(1)確定、"7v2"形式)
448        #[serde(default, skip_serializing_if = "Option::is_none")]
449        symbol_id: Option<String>,
450        /// シンボルパス("tokio::net::TcpStream"形式)
451        #[serde(default, skip_serializing_if = "Option::is_none")]
452        symbol_path: Option<String>,
453        /// 対象のEnum名(診断用、フォールバック検索)
454        #[serde(
455            default,
456            skip_serializing_if = "Option::is_none",
457            alias = "target",
458            alias = "target_name"
459        )]
460        target_enum: Option<String>,
461        // --- Variant attributes ---
462        variant_name: String,
463        /// "unit", "tuple:Type1,Type2", "struct:field1:Type1,field2:Type2"
464        #[serde(default = "default_variant_type")]
465        variant_type: String,
466    },
467
468    /// Enumからバリアントを削除
469    RemoveVariant {
470        // --- Target enum (3-field) ---
471        /// SymbolId(O(1)確定、"7v2"形式)
472        #[serde(default, skip_serializing_if = "Option::is_none")]
473        symbol_id: Option<String>,
474        /// シンボルパス("tokio::net::TcpStream"形式)
475        #[serde(default, skip_serializing_if = "Option::is_none")]
476        symbol_path: Option<String>,
477        /// 対象のEnum名(診断用、フォールバック検索)
478        #[serde(
479            default,
480            skip_serializing_if = "Option::is_none",
481            alias = "target",
482            alias = "target_name"
483        )]
484        target_enum: Option<String>,
485        // --- Variant attributes ---
486        variant_name: String,
487    },
488
489    /// Match式にarmを追加(Cascade用)
490    ///
491    /// AddVariantと組み合わせて使用。CascadeAnalyzerで生成された
492    /// AddMatchArmをIntentとして渡すことで、網羅性エラーを自動修正。
493    AddMatchArm {
494        // --- Target function (3-field pattern) ---
495        /// SymbolId(O(1)確定、"7v2"形式)
496        #[serde(default, skip_serializing_if = "Option::is_none")]
497        symbol_id: Option<String>,
498        /// 関数のシンボルパス (e.g., "crate::handlers::process")
499        #[serde(default, skip_serializing_if = "Option::is_none")]
500        symbol_path: Option<String>,
501        /// 関数名(symbol_pathから自動抽出可能、明示指定も可)
502        #[serde(default, skip_serializing_if = "Option::is_none")]
503        target_fn: Option<String>,
504        // --- Match arm parameters ---
505        /// 対象のenum型名
506        enum_name: String,
507        /// 追加するパターン (e.g., "Status::Cancelled")
508        pattern: String,
509        /// Armのbody (e.g., "todo!()")
510        #[serde(default = "default_method_body")]
511        body: String,
512    },
513
514    /// Match式からarmを削除(Cascade用)
515    ///
516    /// RemoveVariantと組み合わせて使用。バリアント削除後の
517    /// 不要なmatch armを削除する。
518    RemoveMatchArm {
519        // --- Target function (3-field pattern) ---
520        /// SymbolId(O(1)確定、"7v2"形式)
521        #[serde(default, skip_serializing_if = "Option::is_none")]
522        symbol_id: Option<String>,
523        /// 関数のシンボルパス (e.g., "crate::handlers::process")
524        #[serde(default, skip_serializing_if = "Option::is_none")]
525        symbol_path: Option<String>,
526        /// 関数名(symbol_pathから自動抽出可能、明示指定も可)
527        #[serde(default, skip_serializing_if = "Option::is_none")]
528        target_fn: Option<String>,
529        // --- Match arm parameters ---
530        /// 対象のenum型名
531        enum_name: String,
532        /// 削除するパターン (e.g., "Status::Completed")
533        pattern: String,
534    },
535
536    /// Match armを置換(パターン + body をセットで変更)
537    ///
538    /// ReplaceExprはbody(式)のみを置換するが、このIntentはパターンも
539    /// 同時に置換できる。例えば `{ start: _, end: _ }` を `{ start, end }`
540    /// に変更しつつ、bodyも新しい実装に置き換える場合に使用する。
541    ReplaceMatchArm {
542        // --- Target function (3-field pattern) ---
543        /// SymbolId(O(1)確定、"7v2"形式)
544        #[serde(default, skip_serializing_if = "Option::is_none")]
545        symbol_id: Option<String>,
546        /// 関数のシンボルパス (e.g., "crate::handlers::process")
547        #[serde(default, skip_serializing_if = "Option::is_none")]
548        symbol_path: Option<String>,
549        /// 関数名(symbol_pathから自動抽出可能、明示指定も可)
550        #[serde(default, skip_serializing_if = "Option::is_none")]
551        target_fn: Option<String>,
552        // --- Match arm parameters ---
553        /// 対象のenum型名
554        enum_name: String,
555        /// 置換対象のパターン (e.g., "PathSegment::Slice { start: _, end: _ }")
556        old_pattern: String,
557        /// 新しいパターン (e.g., "PathSegment::Slice { start, end }")
558        new_pattern: String,
559        /// 新しいbody (e.g., "{ let s = start.unwrap_or(0); ... }")
560        new_body: String,
561    },
562
563    /// 構造体リテラルにフィールドを追加(Cascade用)
564    ///
565    /// AddFieldと組み合わせて使用。フィールド追加後の構造体リテラルを
566    /// 自動的に更新する。
567    AddStructLiteralField {
568        // --- Target struct (3-field) ---
569        /// SymbolId(O(1)確定、"7v2"形式)
570        #[serde(default, skip_serializing_if = "Option::is_none")]
571        symbol_id: Option<String>,
572        /// シンボルパス("tokio::net::TcpStream"形式)
573        #[serde(default, skip_serializing_if = "Option::is_none")]
574        symbol_path: Option<String>,
575        /// 対象の構造体名(診断用、フォールバック検索)
576        #[serde(
577            default,
578            skip_serializing_if = "Option::is_none",
579            alias = "target",
580            alias = "target_name"
581        )]
582        target_struct: Option<String>,
583        // --- Field attributes ---
584        /// 追加するフィールド名
585        field_name: String,
586        /// フィールドの値 (e.g., "None", "Default::default()")
587        value: String,
588    },
589
590    /// 構造体リテラルからフィールドを削除(Cascade用)
591    ///
592    /// RemoveFieldと組み合わせて使用。フィールド削除後の構造体リテラルを
593    /// 自動的に更新する。
594    RemoveStructLiteralField {
595        // --- Target struct (3-field) ---
596        /// SymbolId(O(1)確定、"7v2"形式)
597        #[serde(default, skip_serializing_if = "Option::is_none")]
598        symbol_id: Option<String>,
599        /// シンボルパス("tokio::net::TcpStream"形式)
600        #[serde(default, skip_serializing_if = "Option::is_none")]
601        symbol_path: Option<String>,
602        /// 対象の構造体名(診断用、フォールバック検索)
603        #[serde(
604            default,
605            skip_serializing_if = "Option::is_none",
606            alias = "target",
607            alias = "target_name"
608        )]
609        target_struct: Option<String>,
610        // --- Field attributes ---
611        /// 削除するフィールド名
612        field_name: String,
613    },
614
615    // === 構造体/Enum削除系 ===
616    /// 構造体を削除
617    RemoveStruct {
618        // --- Target struct (3-field) ---
619        /// SymbolId(O(1)確定、"7v2"形式)
620        #[serde(default, skip_serializing_if = "Option::is_none")]
621        symbol_id: Option<String>,
622        /// シンボルパス("tokio::net::TcpStream"形式)
623        #[serde(default, skip_serializing_if = "Option::is_none")]
624        symbol_path: Option<String>,
625        /// 対象の構造体名(診断用、フォールバック検索)
626        #[serde(
627            default,
628            skip_serializing_if = "Option::is_none",
629            alias = "target",
630            alias = "target_name"
631        )]
632        target_struct: Option<String>,
633    },
634
635    /// Enumを削除
636    RemoveEnum {
637        // --- Target enum (3-field) ---
638        /// SymbolId(O(1)確定、"7v2"形式)
639        #[serde(default, skip_serializing_if = "Option::is_none")]
640        symbol_id: Option<String>,
641        /// シンボルパス("tokio::net::TcpStream"形式)
642        #[serde(default, skip_serializing_if = "Option::is_none")]
643        symbol_path: Option<String>,
644        /// 対象のEnum名(診断用、フォールバック検索)
645        #[serde(
646            default,
647            skip_serializing_if = "Option::is_none",
648            alias = "target",
649            alias = "target_name"
650        )]
651        target_enum: Option<String>,
652    },
653
654    // === 複製系 ===
655    /// 関数を複製
656    DuplicateFunction {
657        // --- Target function (3-field) ---
658        /// SymbolId(O(1)確定、"7v2"形式)
659        #[serde(default, skip_serializing_if = "Option::is_none")]
660        symbol_id: Option<String>,
661        /// シンボルパス("tokio::net::TcpStream"形式)
662        #[serde(default, skip_serializing_if = "Option::is_none")]
663        symbol_path: Option<String>,
664        /// 複製元の関数名(診断用、フォールバック検索)
665        #[serde(
666            default,
667            skip_serializing_if = "Option::is_none",
668            alias = "target",
669            alias = "target_name"
670        )]
671        target_fn: Option<String>,
672        // --- Duplicate attributes ---
673        /// 新しい関数名
674        to: String,
675    },
676
677    /// 構造体を複製(関連impl含む)
678    DuplicateStruct {
679        // --- Target struct (3-field) ---
680        /// SymbolId(O(1)確定、"7v2"形式)
681        #[serde(default, skip_serializing_if = "Option::is_none")]
682        symbol_id: Option<String>,
683        /// シンボルパス("tokio::net::TcpStream"形式)
684        #[serde(default, skip_serializing_if = "Option::is_none")]
685        symbol_path: Option<String>,
686        /// 複製元の構造体名(診断用、フォールバック検索)
687        #[serde(
688            default,
689            skip_serializing_if = "Option::is_none",
690            alias = "target",
691            alias = "target_name"
692        )]
693        target_struct: Option<String>,
694        // --- Duplicate attributes ---
695        /// 新しい構造体名
696        to: String,
697        /// impl blockも複製するか
698        #[serde(default = "default_true")]
699        include_impls: bool,
700    },
701
702    /// Enumを複製(関連impl含む)
703    DuplicateEnum {
704        // --- Target enum (3-field) ---
705        /// SymbolId(O(1)確定、"7v2"形式)
706        #[serde(default, skip_serializing_if = "Option::is_none")]
707        symbol_id: Option<String>,
708        /// シンボルパス("tokio::net::TcpStream"形式)
709        #[serde(default, skip_serializing_if = "Option::is_none")]
710        symbol_path: Option<String>,
711        /// 複製元のEnum名(診断用、フォールバック検索)
712        #[serde(
713            default,
714            skip_serializing_if = "Option::is_none",
715            alias = "target",
716            alias = "target_name"
717        )]
718        target_enum: Option<String>,
719        // --- Duplicate attributes ---
720        /// 新しいEnum名
721        to: String,
722        /// impl blockも複製するか
723        #[serde(default = "default_true")]
724        include_impls: bool,
725    },
726
727    /// インラインモジュールを複製
728    DuplicateModTree {
729        // --- Target module (3-field) ---
730        /// SymbolId(O(1)確定、"7v2"形式)
731        #[serde(default, skip_serializing_if = "Option::is_none")]
732        symbol_id: Option<String>,
733        /// シンボルパス("tokio::net::TcpStream"形式)
734        #[serde(default, skip_serializing_if = "Option::is_none")]
735        symbol_path: Option<String>,
736        /// 複製元のモジュール名(診断用、フォールバック検索)
737        #[serde(
738            default,
739            skip_serializing_if = "Option::is_none",
740            alias = "target",
741            alias = "target_name"
742        )]
743        target_mod: Option<String>,
744        // --- Duplicate attributes ---
745        /// 新しいモジュール名
746        to: String,
747    },
748
749    // === 定数/型エイリアス系 ===
750    /// 定数を追加
751    AddConst {
752        /// 追加先モジュールパス(必須: "my_crate", "my_crate::domain"等)
753        symbol_path: String,
754        name: String,
755        ty: String,
756        value: String,
757        #[serde(default)]
758        is_pub: bool,
759    },
760
761    /// 型エイリアスを追加
762    AddTypeAlias {
763        /// 追加先モジュールパス(必須: "my_crate", "my_crate::domain"等)
764        symbol_path: String,
765        name: String,
766        ty: String,
767        #[serde(default)]
768        is_pub: bool,
769    },
770
771    // === Spec系 ===
772    /// Spec TypeAliasを追加 (ドメイン仕様マーカー)
773    AddSpec {
774        // --- Target type (3-field) ---
775        /// SymbolId(O(1)確定、"7v2"形式)
776        #[serde(default, skip_serializing_if = "Option::is_none")]
777        symbol_id: Option<String>,
778        /// シンボルパス("tokio::net::TcpStream"形式)
779        #[serde(default, skip_serializing_if = "Option::is_none")]
780        symbol_path: Option<String>,
781        /// 対象の型名(診断用、フォールバック検索)
782        #[serde(
783            default,
784            skip_serializing_if = "Option::is_none",
785            alias = "target",
786            alias = "target_name"
787        )]
788        target_type: Option<String>,
789
790        // --- Module (3-field) ---
791        /// モジュールのSymbolId
792        #[serde(default, skip_serializing_if = "Option::is_none")]
793        module_id: Option<String>,
794        /// モジュールのシンボルパス (e.g., "crate::domain")
795        #[serde(default, skip_serializing_if = "Option::is_none")]
796        module_path: Option<String>,
797        /// モジュール名(診断用、フォールバック検索)
798        #[serde(default, skip_serializing_if = "Option::is_none")]
799        target_mod: Option<String>,
800
801        /// グループ名 (e.g., "DomainGroup", "ConfigGroup")
802        group: String,
803        /// エイリアス名 (省略時: "{target_type}Spec")
804        #[serde(default)]
805        alias_name: Option<String>,
806        /// 依存関係 (最大3つ)
807        #[serde(default)]
808        relations: Vec<SpecRelation>,
809    },
810
811    // === メソッド追加・削除 ===
812    /// implにメソッドを追加
813    AddMethod {
814        /// SymbolId(O(1)確定、"7v2"形式)
815        #[serde(default, skip_serializing_if = "Option::is_none")]
816        symbol_id: Option<String>,
817        /// シンボルパス("tokio::net::TcpStream"形式)
818        #[serde(default, skip_serializing_if = "Option::is_none")]
819        symbol_path: Option<String>,
820        /// 対象の型名(診断用、フォールバック検索)
821        #[serde(
822            default,
823            skip_serializing_if = "Option::is_none",
824            alias = "target",
825            alias = "target_name"
826        )]
827        target_type: Option<String>,
828        /// メソッド名
829        method_name: String,
830        /// パラメータ (name, type) のペア
831        #[serde(default)]
832        params: Vec<(String, String)>,
833        /// 戻り値の型 (None = unit)
834        #[serde(default)]
835        return_type: Option<String>,
836        /// メソッド本体
837        #[serde(default = "default_method_body")]
838        body: String,
839        /// public かどうか
840        #[serde(default)]
841        is_pub: bool,
842        /// selfパラメータ
843        #[serde(default)]
844        self_param: Option<SelfParam>,
845    },
846
847    /// implからメソッドを削除
848    RemoveMethod {
849        // --- Target impl type (3-field) ---
850        /// SymbolId(O(1)確定、"7v2"形式)
851        #[serde(default, skip_serializing_if = "Option::is_none")]
852        symbol_id: Option<String>,
853        /// シンボルパス("tokio::net::TcpStream"形式)
854        #[serde(default, skip_serializing_if = "Option::is_none")]
855        symbol_path: Option<String>,
856        /// 対象の型名(診断用、フォールバック検索)
857        #[serde(
858            default,
859            skip_serializing_if = "Option::is_none",
860            alias = "target",
861            alias = "target_name"
862        )]
863        target_type: Option<String>,
864        // --- Method attributes ---
865        method_name: String,
866    },
867
868    // === 削除系 ===
869    /// 定数を削除
870    RemoveConst {
871        // --- Target const (3-field) ---
872        /// SymbolId(O(1)確定、"7v2"形式)
873        #[serde(default, skip_serializing_if = "Option::is_none")]
874        symbol_id: Option<String>,
875        /// シンボルパス("tokio::net::TcpStream"形式)
876        #[serde(default, skip_serializing_if = "Option::is_none")]
877        symbol_path: Option<String>,
878        /// 対象の定数名(診断用、フォールバック検索)
879        #[serde(
880            default,
881            skip_serializing_if = "Option::is_none",
882            alias = "target",
883            alias = "target_name"
884        )]
885        target_const: Option<String>,
886    },
887
888    /// 型エイリアスを削除
889    RemoveTypeAlias {
890        // --- Target type alias (3-field) ---
891        /// SymbolId(O(1)確定、"7v2"形式)
892        #[serde(default, skip_serializing_if = "Option::is_none")]
893        symbol_id: Option<String>,
894        /// シンボルパス("tokio::net::TcpStream"形式)
895        #[serde(default, skip_serializing_if = "Option::is_none")]
896        symbol_path: Option<String>,
897        /// 対象の型エイリアス名(診断用、フォールバック検索)
898        #[serde(
899            default,
900            skip_serializing_if = "Option::is_none",
901            alias = "target",
902            alias = "target_name"
903        )]
904        target_type_alias: Option<String>,
905    },
906
907    /// use文を削除
908    RemoveUse {
909        // --- Target use (3-field) ---
910        /// SymbolId(O(1)確定、"7v2"形式)
911        #[serde(default, skip_serializing_if = "Option::is_none")]
912        symbol_id: Option<String>,
913        /// シンボルパス("tokio::net::TcpStream"形式)
914        #[serde(default, skip_serializing_if = "Option::is_none")]
915        symbol_path: Option<String>,
916        /// 対象のuseパス(診断用、フォールバック検索)
917        #[serde(
918            default,
919            skip_serializing_if = "Option::is_none",
920            alias = "target",
921            alias = "target_name"
922        )]
923        target_use: Option<String>,
924    },
925
926    /// Traitを削除
927    RemoveTrait {
928        // --- Target trait (3-field) ---
929        /// SymbolId(O(1)確定、"7v2"形式)
930        #[serde(default, skip_serializing_if = "Option::is_none")]
931        symbol_id: Option<String>,
932        /// シンボルパス("tokio::net::TcpStream"形式)
933        #[serde(default, skip_serializing_if = "Option::is_none")]
934        symbol_path: Option<String>,
935        /// 対象のTrait名(診断用、フォールバック検索)
936        #[serde(
937            default,
938            skip_serializing_if = "Option::is_none",
939            alias = "target",
940            alias = "target_name"
941        )]
942        target_trait: Option<String>,
943    },
944
945    /// impl blockを削除
946    RemoveImpl {
947        // --- Target impl (3-field) ---
948        /// SymbolId(O(1)確定、"7v2"形式)
949        #[serde(default, skip_serializing_if = "Option::is_none")]
950        symbol_id: Option<String>,
951        /// シンボルパス("tokio::net::TcpStream"形式)
952        #[serde(default, skip_serializing_if = "Option::is_none")]
953        symbol_path: Option<String>,
954        /// 対象の型名(診断用、フォールバック検索)
955        #[serde(
956            default,
957            skip_serializing_if = "Option::is_none",
958            alias = "target",
959            alias = "target_name"
960        )]
961        target_type: Option<String>,
962        // --- Impl attributes ---
963        trait_name: Option<String>,
964    },
965
966    // === 追加・削除系 ===
967    /// アイテム追加
968    AddItem {
969        // --- Target (3-field) ---
970        /// SymbolId(O(1)確定、"7v2"形式)
971        #[serde(default, skip_serializing_if = "Option::is_none")]
972        symbol_id: Option<String>,
973        /// シンボルパス("crate::module"形式)
974        #[serde(default, skip_serializing_if = "Option::is_none")]
975        symbol_path: Option<String>,
976        /// モジュール名(診断用、フォールバック検索)
977        #[serde(
978            default,
979            skip_serializing_if = "Option::is_none",
980            alias = "target",
981            alias = "target_name"
982        )]
983        target_mod: Option<String>,
984        // --- Add attributes ---
985        content: String,
986        item_kind: ItemKind,
987    },
988
989    /// アイテム削除
990    RemoveItem {
991        // --- Target (3-field) ---
992        /// SymbolId(O(1)確定、"7v2"形式)
993        #[serde(default, skip_serializing_if = "Option::is_none")]
994        symbol_id: Option<String>,
995        /// シンボルパス("tokio::net::TcpStream"形式)
996        #[serde(default, skip_serializing_if = "Option::is_none")]
997        symbol_path: Option<String>,
998        /// 削除対象の名前(診断用、フォールバック検索)
999        #[serde(
1000            default,
1001            skip_serializing_if = "Option::is_none",
1002            alias = "target",
1003            alias = "target_name"
1004        )]
1005        target_item: Option<String>,
1006        // --- Remove attributes ---
1007        item_kind: ItemKind,
1008    },
1009
1010    /// コードを親パスに追加(存在しなければモジュール自動作成)
1011    ///
1012    /// AddItemの簡易版。item_kindはsynパースで自動判定し、
1013    /// 親パスが存在しない場合は再帰的にモジュールを作成する。
1014    ///
1015    /// ## 自動モジュール作成
1016    ///
1017    /// - **SymbolRegistryあり**: 既存モジュールをスキップし、存在しないモジュールのみ作成
1018    /// - **SymbolRegistryなし**: 全親セグメントに対してCreateModを生成(CreateModは冪等)
1019    ///
1020    /// 例: `parent: "crate::infrastructure::memory"` の場合
1021    /// 1. CreateMod { parent: "crate", mod_name: "infrastructure" }
1022    /// 2. CreateMod { parent: "crate::infrastructure", mod_name: "memory" }
1023    /// 3. AddItem { target: "crate::infrastructure::memory", ... }
1024    ///
1025    /// ## parent と parent_ref
1026    ///
1027    /// どちらか一方を指定。両方指定時は parent_ref を優先。
1028    ///
1029    /// # Example (シンプル形式 - parent)
1030    /// ```json
1031    /// {
1032    ///   "type": "AddCode",
1033    ///   "parent": "crate::usecase",
1034    ///   "code": "pub struct CreateOrderInput { pub user_id: UserId }"
1035    /// }
1036    /// ```
1037    ///
1038    /// # Example (明示的形式 - parent_ref)
1039    /// ```json
1040    /// {
1041    ///   "type": "AddCode",
1042    ///   "parent_ref": { "type": "FilePath", "path": "src/usecase.rs" },
1043    ///   "code": "pub struct CreateOrderInput { ... }"
1044    /// }
1045    /// ```
1046    AddCode {
1047        /// SymbolId直接指定("7v2"形式、O(1)解決)
1048        #[serde(default)]
1049        symbol_id: Option<String>,
1050        /// SymbolPathまたはファイルパス
1051        /// "::"を含めばSymbolPath、それ以外はファイルパスとして解釈
1052        #[serde(default, alias = "parent")]
1053        symbol_path: Option<String>,
1054        /// モジュール名(診断/フォールバック用)
1055        #[serde(default, alias = "target", alias = "target_name")]
1056        target_mod: Option<String>,
1057        /// 追加するRustコード(複数Item可、synでパース)
1058        code: String,
1059    },
1060
1061    // === IDIOM系 (Agent自動適用向け) ===
1062    /// use文の整理・ソート
1063    OrganizeImports {
1064        /// 対象モジュール(None = 全モジュール)"lib::foo::bar" 形式
1065        #[serde(default)]
1066        target_mod: Option<String>,
1067        /// 重複を削除
1068        #[serde(default = "default_true")]
1069        deduplicate: bool,
1070        /// 同一プレフィックスをグループ化
1071        #[serde(default = "default_true")]
1072        merge_groups: bool,
1073    },
1074
1075    /// 同一型のimplブロックをマージ
1076    MergeImplBlocks {
1077        /// 対象モジュール(None = 全モジュール)
1078        #[serde(default)]
1079        target_mod: Option<String>,
1080        /// 対象の型名 (None = 全て)
1081        #[serde(default)]
1082        target_type: Option<String>,
1083        /// inherent implのみ (trait implを除外)
1084        #[serde(default)]
1085        inherent_only: bool,
1086    },
1087
1088    /// forループをイテレータチェーンに変換
1089    LoopToIterator {
1090        /// 対象モジュール(None = 全モジュール)
1091        #[serde(default)]
1092        target_mod: Option<String>,
1093        /// 特定の変数名のループのみ対象
1094        #[serde(default)]
1095        target_var: Option<String>,
1096    },
1097
1098    /// .unwrap()/.expect() を ? 演算子に変換
1099    UnwrapToQuestion {
1100        /// 対象モジュール(None = 全モジュール)
1101        #[serde(default)]
1102        target_mod: Option<String>,
1103        /// 特定の関数のみ対象
1104        #[serde(default)]
1105        target_fn: Option<String>,
1106        /// .expect()も変換対象にする
1107        #[serde(default = "default_true")]
1108        include_expect: bool,
1109    },
1110
1111    /// 重複式を変数に抽出
1112    IntroduceVariable {
1113        /// 対象モジュール(None = 全モジュール)
1114        #[serde(default)]
1115        target_mod: Option<String>,
1116        /// 対象の関数名(None = 全関数)
1117        #[serde(default)]
1118        target_fn: Option<String>,
1119        /// 抽出対象の式(Rustコード文字列, e.g. "a + b * c")
1120        expr: String,
1121        /// 抽出する変数名
1122        var_name: String,
1123    },
1124
1125    /// Builder patternを生成
1126    ///
1127    /// 指定した struct に対して Builder pattern を生成する。
1128    /// 以下のコードが生成される:
1129    /// - `{StructName}Builder` struct (Option-wrapped fields)
1130    /// - `impl {StructName}Builder` with `new()`, setter methods, `build()`
1131    /// - (オプション) `impl {StructName}` with `builder()` method
1132    ///
1133    /// # Example
1134    /// ```json
1135    /// {
1136    ///   "type": "GenerateBuilder",
1137    ///   "struct_name": "Config",
1138    ///   "fields": [
1139    ///     ["host", "String"],
1140    ///     ["port", "u16"],
1141    ///     ["timeout", "Option<u32>"]
1142    ///   ]
1143    /// }
1144    /// ```
1145    GenerateBuilder {
1146        // --- Target struct (3-field) ---
1147        /// SymbolId(O(1)確定、"7v2"形式)
1148        #[serde(default, skip_serializing_if = "Option::is_none")]
1149        symbol_id: Option<String>,
1150        /// シンボルパス("tokio::net::TcpStream"形式)
1151        #[serde(default, skip_serializing_if = "Option::is_none")]
1152        symbol_path: Option<String>,
1153        /// 対象の struct 名(診断用、フォールバック検索)
1154        #[serde(
1155            default,
1156            skip_serializing_if = "Option::is_none",
1157            alias = "target",
1158            alias = "target_name"
1159        )]
1160        target_struct: Option<String>,
1161        // --- Builder attributes ---
1162        /// Builder追加先のモジュールパス(例: "crate::config")
1163        /// symbol_id指定時は省略可(registryから解決)
1164        #[serde(default)]
1165        target_mod: Option<String>,
1166        /// フィールド定義 [(name, type), ...]
1167        fields: Vec<(String, String)>,
1168        /// 元の struct に builder() メソッドを追加するか(デフォルト: true)
1169        #[serde(default = "default_true")]
1170        add_builder_method: bool,
1171    },
1172
1173    // === PureStmt/PureExpr 操作系 ===
1174    /// 式を別の式に置換
1175    ///
1176    /// 対象指定方法(どちらか一方を使用):
1177    /// - `old_expr`: パターンマッチで対象を検索
1178    /// - `symbol_path`: 直接位置指定(例: "crate::fn::$body::0::1")
1179    ReplaceExpr {
1180        /// 対象モジュール(None = 全モジュール)
1181        #[serde(default)]
1182        target_mod: Option<String>,
1183        /// 対象の関数名(None=全関数)
1184        #[serde(default)]
1185        target_fn: Option<String>,
1186        /// 置換元の式(Rustコード文字列)- パターンマッチ方式
1187        old_expr: String,
1188        /// 置換先の式(Rustコード文字列)
1189        new_expr: String,
1190        /// 全ての出現箇所を置換するか(デフォルト: true)
1191        #[serde(default = "default_true")]
1192        replace_all: bool,
1193        /// 直接位置指定(例: "my_crate::my_fn::$body::0::1::2")
1194        /// 指定時は old_expr を無視し、この位置の式を直接置換
1195        #[serde(default)]
1196        symbol_path: Option<String>,
1197    },
1198
1199    /// 文を削除
1200    ///
1201    /// 対象指定方法(どちらか一方を使用):
1202    /// - `pattern`: パターンマッチで対象を検索
1203    /// - `symbol_path`: 直接位置指定(例: "crate::fn::$body::2")
1204    RemoveStatement {
1205        /// 対象モジュール(None = 全モジュール)
1206        #[serde(default)]
1207        target_mod: Option<String>,
1208        /// 対象の関数名(None=全関数)
1209        #[serde(default)]
1210        target_fn: Option<String>,
1211        /// 削除対象の文パターン(Rustコード文字列, e.g. "println!(..)")- パターンマッチ方式
1212        pattern: String,
1213        /// 全ての出現箇所を削除するか(デフォルト: true)
1214        #[serde(default = "default_true")]
1215        remove_all: bool,
1216        /// 直接位置指定(例: "my_crate::my_fn::$body::2")
1217        /// 指定時は pattern を無視し、この位置の文を直接削除
1218        #[serde(default)]
1219        symbol_path: Option<String>,
1220    },
1221
1222    /// 文を挿入
1223    ///
1224    /// 対象指定方法:
1225    /// - `target_fn` + `position`: 従来方式
1226    /// - `symbol_path`: 直接位置指定($body::N の後に挿入)
1227    InsertStatement {
1228        /// 対象モジュール(None = 全モジュール)
1229        #[serde(default)]
1230        target_mod: Option<String>,
1231        /// 対象の関数名
1232        target_fn: String,
1233        /// 挿入する文(Rustコード文字列)
1234        stmt: String,
1235        /// 挿入位置
1236        #[serde(default)]
1237        position: StmtInsertPosition,
1238        /// 参照パターン(BeforePattern/AfterPattern用)
1239        #[serde(default)]
1240        reference_pattern: Option<String>,
1241        /// 直接位置指定(例: "my_crate::my_fn::$body::2")
1242        /// 指定時は position を無視し、この位置の後に挿入
1243        #[serde(default)]
1244        symbol_path: Option<String>,
1245    },
1246
1247    /// 文を別の文に置換
1248    ///
1249    /// 対象指定方法(どちらか一方を使用):
1250    /// - `old_stmt`: パターンマッチで対象を検索
1251    /// - `symbol_path`: 直接位置指定(例: "crate::fn::$body::1")
1252    ReplaceStatement {
1253        /// 対象モジュール(None = 全モジュール)
1254        #[serde(default)]
1255        target_mod: Option<String>,
1256        /// 対象の関数名(None=全関数)
1257        #[serde(default)]
1258        target_fn: Option<String>,
1259        /// 置換元の文パターン(Rustコード文字列)- パターンマッチ方式
1260        old_stmt: String,
1261        /// 置換先の文(Rustコード文字列)
1262        new_stmt: String,
1263        /// 直接位置指定(例: "my_crate::my_fn::$body::1")
1264        /// 指定時は old_stmt を無視し、この位置の文を直接置換
1265        #[serde(default)]
1266        symbol_path: Option<String>,
1267    },
1268
1269    // === 追加 Idiom変換系 ===
1270    /// 代入演算子の簡略化: `x = x + 1` → `x += 1`
1271    AssignOp {
1272        /// 対象モジュール(None = 全モジュール)
1273        #[serde(default)]
1274        target_mod: Option<String>,
1275        /// 特定の関数のみ対象
1276        #[serde(default)]
1277        target_fn: Option<String>,
1278    },
1279
1280    /// bool式の簡略化: `x == true` → `x`, `x == false` → `!x`
1281    BoolSimplify {
1282        /// 対象モジュール(None = 全モジュール)
1283        #[serde(default)]
1284        target_mod: Option<String>,
1285    },
1286
1287    /// Copy型での冗長な.clone()削除
1288    CloneOnCopy {
1289        /// 対象モジュール(None = 全モジュール)
1290        #[serde(default)]
1291        target_mod: Option<String>,
1292    },
1293
1294    /// ネストしたifを&&で統合: `if a { if b { ... } }` → `if a && b { ... }`
1295    CollapsibleIf {
1296        /// 対象モジュール(None = 全モジュール)
1297        #[serde(default)]
1298        target_mod: Option<String>,
1299    },
1300
1301    /// 比較をメソッド呼び出しに変換: `s == ""` → `s.is_empty()`
1302    ComparisonToMethod {
1303        /// 対象モジュール(None = 全モジュール)
1304        #[serde(default)]
1305        target_mod: Option<String>,
1306    },
1307
1308    /// 冗長なクロージャを削除: `|x| f(x)` → `f`
1309    RedundantClosure {
1310        /// 対象モジュール(None = 全モジュール)
1311        #[serde(default)]
1312        target_mod: Option<String>,
1313    },
1314
1315    /// matchをmap()に変換: `match opt { Some(x) => Some(f(x)), None => None }` → `opt.map(f)`
1316    ManualMap {
1317        /// 対象モジュール(None = 全モジュール)
1318        #[serde(default)]
1319        target_mod: Option<String>,
1320    },
1321
1322    /// matchをif letに変換
1323    MatchToIfLet {
1324        /// 対象モジュール(None = 全モジュール)
1325        #[serde(default)]
1326        target_mod: Option<String>,
1327    },
1328
1329    /// .filter().next() を .find() に変換
1330    FilterNext {
1331        /// 対象モジュール(None = 全モジュール)
1332        #[serde(default)]
1333        target_mod: Option<String>,
1334        /// 特定の関数のみ対象
1335        #[serde(default)]
1336        target_fn: Option<String>,
1337    },
1338
1339    /// .map().unwrap_or() を .map_or() に変換
1340    MapUnwrapOr {
1341        /// 対象モジュール(None = 全モジュール)
1342        #[serde(default)]
1343        target_mod: Option<String>,
1344        /// 特定の関数のみ対象
1345        #[serde(default)]
1346        target_fn: Option<String>,
1347    },
1348
1349    // === カスタム ===
1350    /// LLMが理解したが定義済みIntentにマッチしない場合
1351    Custom {
1352        description: String,
1353        /// LLMが推測した変換例(Before → After)
1354        examples: Vec<TransformExample>,
1355    },
1356
1357    // === WASM Plugin ===
1358    /// Execute a WASM plugin by name
1359    #[cfg(feature = "wasm-plugin")]
1360    Plugin {
1361        /// Plugin name (must be registered in MutationRegistry)
1362        name: String,
1363        /// Glob patterns for multi-file targeting (e.g., ["**/*.rs", "src/lib.rs"])
1364        /// If empty, uses all files in project or scope.file_patterns
1365        #[serde(default)]
1366        file_patterns: Vec<String>,
1367    },
1368}
1369
1370// ============================================================================
1371// Supporting Types
1372// ============================================================================
1373
1374/// 文の挿入位置
1375#[cfg_attr(feature = "schemars", derive(JsonSchema))]
1376#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1377#[serde(rename_all = "snake_case")]
1378#[derive(Default)]
1379pub enum StmtInsertPosition {
1380    /// 関数の先頭
1381    Start,
1382    /// 関数の末尾(return文の前)
1383    #[default]
1384    End,
1385    /// 指定した文の前
1386    BeforePattern,
1387    /// 指定した文の後
1388    AfterPattern,
1389}
1390
1391/// 識別子の種類
1392#[cfg_attr(feature = "schemars", derive(JsonSchema))]
1393#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
1394pub enum IdentKind {
1395    /// 変数: let x
1396    Var,
1397    /// フィールド: struct.field
1398    Field,
1399    /// 関数: fn foo()
1400    Fn,
1401    /// 型: struct/enum/type alias
1402    Type,
1403    /// モジュール: mod foo
1404    Module,
1405    /// 種類不明
1406    Any,
1407}
1408
1409/// 可視性
1410#[cfg_attr(feature = "schemars", derive(JsonSchema))]
1411#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
1412pub enum Visibility {
1413    Private,
1414    Pub,
1415    PubCrate,
1416    PubSuper,
1417}
1418
1419/// メソッドのselfパラメータ
1420#[cfg_attr(feature = "schemars", derive(JsonSchema))]
1421#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
1422#[serde(rename_all = "lowercase")]
1423pub enum SelfParam {
1424    /// &self
1425    Ref,
1426    /// &mut self
1427    Mut,
1428    /// self (owned)
1429    Owned,
1430}
1431
1432/// Spec TypeAliasの依存関係
1433#[cfg_attr(feature = "schemars", derive(JsonSchema))]
1434#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1435pub struct SpecRelation {
1436    /// 関係の種類
1437    pub kind: SpecRelationKind,
1438    /// 対象の型名
1439    pub target: String,
1440}
1441
1442/// Spec依存関係の種類
1443#[cfg_attr(feature = "schemars", derive(JsonSchema))]
1444#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1445#[serde(rename_all = "PascalCase")]
1446pub enum SpecRelationKind {
1447    /// AがBに依存する (AはBがないと機能しない)
1448    DependsOn,
1449    /// AがBと関連する (意味的な関係)
1450    RelatedTo,
1451    /// AがBの一部である (集約メンバーシップ)
1452    PartOf,
1453}
1454
1455/// 変換例(Custom用)
1456#[cfg_attr(feature = "schemars", derive(JsonSchema))]
1457#[derive(Debug, Clone, Serialize, Deserialize)]
1458pub struct TransformExample {
1459    pub before: String,
1460    pub after: String,
1461}
1462
1463// ============================================================================
1464// ScopeHint: 並列Discovery用ヒント
1465// ============================================================================
1466
1467/// 検索スコープのヒント
1468///
1469/// 後段のExecutorが並列Discovery戦略を決定するために使用。
1470/// すべてのフィールドは省略可能で、デフォルト値が使用される。
1471#[cfg_attr(feature = "schemars", derive(JsonSchema))]
1472#[derive(Debug, Clone, Serialize, Deserialize)]
1473#[serde(default)]
1474pub struct ScopeHint {
1475    /// ファイルパターン(例: "src/**/*.rs", "crates/project-name/**")
1476    #[serde(skip_serializing_if = "Vec::is_empty")]
1477    pub file_patterns: Vec<String>,
1478
1479    /// シンボルパターン(例: "*Config", "crate::domain::User")
1480    /// "::"を含む → SymbolPath、それ以外 → Globパターン
1481    #[serde(skip_serializing_if = "Vec::is_empty")]
1482    pub symbol_patterns: Vec<String>,
1483
1484    /// 推定影響範囲
1485    #[serde(skip_serializing_if = "is_estimated_scope_unknown")]
1486    pub estimated_scope: EstimatedScope,
1487}
1488
1489fn is_estimated_scope_unknown(scope: &EstimatedScope) -> bool {
1490    matches!(scope, EstimatedScope::Unknown)
1491}
1492
1493/// 推定影響範囲
1494#[cfg_attr(feature = "schemars", derive(JsonSchema))]
1495#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
1496pub enum EstimatedScope {
1497    /// 1ファイルのみ
1498    SingleFile,
1499    /// 2-5ファイル
1500    FewFiles,
1501    /// 6+ファイル
1502    ManyFiles,
1503    /// プロジェクト全体
1504    ProjectWide,
1505    /// 不明
1506    #[default]
1507    Unknown,
1508}
1509
1510impl EstimatedScope {
1511    /// Check if this is the Unknown variant (for serde skip_serializing_if)
1512    pub fn is_unknown(&self) -> bool {
1513        matches!(self, Self::Unknown)
1514    }
1515}
1516
1517impl Default for ScopeHint {
1518    fn default() -> Self {
1519        Self {
1520            file_patterns: vec!["**/*.rs".to_string()],
1521            symbol_patterns: vec![],
1522            estimated_scope: EstimatedScope::Unknown,
1523        }
1524    }
1525}
1526
1527impl ScopeHint {
1528    pub fn new() -> Self {
1529        Self::default()
1530    }
1531
1532    pub fn with_file_patterns(mut self, patterns: Vec<String>) -> Self {
1533        self.file_patterns = patterns;
1534        self
1535    }
1536
1537    pub fn with_symbol_patterns(mut self, patterns: Vec<String>) -> Self {
1538        self.symbol_patterns = patterns;
1539        self
1540    }
1541
1542    pub fn with_estimated_scope(mut self, scope: EstimatedScope) -> Self {
1543        self.estimated_scope = scope;
1544        self
1545    }
1546}
1547
1548// ============================================================================
1549// Constraint: 実行時制約
1550// ============================================================================
1551
1552/// 実行時制約
1553#[cfg_attr(feature = "schemars", derive(JsonSchema))]
1554#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1555pub enum Constraint {
1556    /// コンパイルが通ること(syn parse + cargo check)
1557    MustCompile,
1558
1559    /// pub APIを破壊しない
1560    PreservePublicApi,
1561
1562    /// テストが通ること
1563    MustPassTests,
1564
1565    /// 変更しない(プレビューのみ)
1566    DryRun,
1567
1568    /// 対話的確認が必要
1569    RequireConfirmation,
1570
1571    /// 適用後にcargo checkを実行
1572    CargoCheck,
1573
1574    /// cargo check失敗時に変更をロールバック(CargoCheckと併用)
1575    RollbackOnFailure,
1576}
1577
1578// ============================================================================
1579// ExtractError
1580// ============================================================================
1581
1582/// Intent抽出エラー
1583#[derive(Debug, thiserror::Error)]
1584pub enum ExtractError {
1585    #[error("LLM connection failed: {0}")]
1586    ConnectionFailed(String),
1587
1588    #[error("Failed to parse LLM response: {0}")]
1589    ParseFailed(String),
1590
1591    #[error("Query too ambiguous: {0}")]
1592    AmbiguousQuery(String),
1593
1594    #[error("Unsupported query type: {0}")]
1595    UnsupportedQuery(String),
1596}
1597
1598// ============================================================================
1599// IntentExtractor Trait
1600// ============================================================================
1601
1602/// IntentExtractor: NLクエリ → Goal
1603///
1604/// 責務:ゴール設定のみ。検索・変換は一切行わない。
1605pub trait IntentExtractor: Send + Sync {
1606    /// クエリからGoalを抽出
1607    fn extract(&self, query: &str) -> Result<Goal, ExtractError>;
1608
1609    /// バッチ抽出(複数クエリを並列処理)
1610    fn extract_batch(&self, queries: &[String]) -> Vec<Result<Goal, ExtractError>>;
1611
1612    /// 使用モデル名
1613    fn model_name(&self) -> &str;
1614}
1615
1616// ============================================================================
1617// Goal Builder
1618// ============================================================================
1619
1620impl Goal {
1621    /// 新しいGoalを構築(単一Intent)
1622    pub fn new(query: impl Into<String>, intent: Intent) -> Self {
1623        Self {
1624            query: query.into(),
1625            intents: vec![intent],
1626            scope: ScopeHint::default(),
1627            constraints: vec![Constraint::MustCompile],
1628            conflict_strategy: ConflictStrategy::default(),
1629            confidence: 1.0,
1630        }
1631    }
1632
1633    /// 複数Intentsで構築
1634    pub fn with_intents(query: impl Into<String>, intents: Vec<Intent>) -> Self {
1635        Self {
1636            query: query.into(),
1637            intents,
1638            scope: ScopeHint::default(),
1639            constraints: vec![Constraint::MustCompile],
1640            conflict_strategy: ConflictStrategy::default(),
1641            confidence: 1.0,
1642        }
1643    }
1644
1645    /// コンフリクト解決戦略を設定
1646    pub fn with_conflict_strategy(mut self, strategy: ConflictStrategy) -> Self {
1647        self.conflict_strategy = strategy;
1648        self
1649    }
1650
1651    pub fn with_scope(mut self, scope: ScopeHint) -> Self {
1652        self.scope = scope;
1653        self
1654    }
1655
1656    pub fn with_constraints(mut self, constraints: Vec<Constraint>) -> Self {
1657        self.constraints = constraints;
1658        self
1659    }
1660
1661    pub fn with_confidence(mut self, confidence: f64) -> Self {
1662        self.confidence = confidence;
1663        self
1664    }
1665
1666    /// RenameIdentのGoalを簡易構築
1667    pub fn rename(from: impl Into<String>, to: impl Into<String>) -> Self {
1668        let from_str = from.into();
1669        let to_str = to.into();
1670        let query = format!("{} → {}", from_str, to_str);
1671
1672        Self::new(
1673            &query,
1674            Intent::RenameIdent {
1675                symbol_id: None,
1676                symbol_path: None,
1677                target_ident: Some(from_str.clone()),
1678                to: to_str,
1679                kind: IdentKind::Any,
1680            },
1681        )
1682        .with_scope(ScopeHint::new().with_estimated_scope(EstimatedScope::FewFiles))
1683    }
1684
1685    // Note: to_mutations() and to_mutations_with_discovery() methods are
1686    // implemented in ryo-executor crate where Mutation execution logic resides.
1687}
1688
1689// ============================================================================
1690// Fuzzy JSON Parsing (feature-gated)
1691// ============================================================================
1692
1693#[cfg(feature = "fuzzy-parser")]
1694impl Goal {
1695    /// Parse a Goal from JSON with fuzzy correction for typos
1696    ///
1697    /// This method automatically corrects common typos in Intent type names
1698    /// and field names that may occur when JSON is generated by LLMs.
1699    ///
1700    /// # Example
1701    /// ```ignore
1702    /// let json = r#"{
1703    ///     "query": "Add derives",
1704    ///     "intents": [{"type": "AddDeriv", "taget": "User", "derives": ["Debug"]}]
1705    /// }"#;
1706    /// let (goal, corrections) = Goal::from_json_fuzzy(json)?;
1707    /// // Typos corrected: AddDeriv → AddDerive, taget → target
1708    /// ```
1709    pub fn from_json_fuzzy(
1710        json: &str,
1711    ) -> Result<(Self, Vec<ryo_fuzzy_parser::Correction>), GoalParseError> {
1712        Self::from_json_fuzzy_with_options(json, &ryo_fuzzy_parser::FuzzyOptions::default())
1713    }
1714
1715    /// Parse a Goal from JSON with fuzzy correction using custom options
1716    pub fn from_json_fuzzy_with_options(
1717        json: &str,
1718        options: &ryo_fuzzy_parser::FuzzyOptions,
1719    ) -> Result<(Self, Vec<ryo_fuzzy_parser::Correction>), GoalParseError> {
1720        use crate::intent_schema::{intent_schema, GOAL_FIELDS};
1721        use ryo_fuzzy_parser::{repair_fields_with_list, repair_tagged_enum_array, ObjectSchema};
1722
1723        // Parse JSON first
1724        let mut value: serde_json::Value =
1725            serde_json::from_str(json).map_err(|e| GoalParseError::JsonParse(e.to_string()))?;
1726
1727        let mut all_corrections = Vec::new();
1728
1729        // Repair Goal-level fields
1730        if let Some(obj) = value.as_object_mut() {
1731            let goal_schema = ObjectSchema::new(GOAL_FIELDS);
1732            let corrections = repair_fields_with_list(obj, goal_schema.valid_fields, "$", options);
1733            all_corrections.extend(corrections);
1734
1735            // Repair intents array
1736            if let Some(intents) = obj.get_mut("intents").and_then(|v| v.as_array_mut()) {
1737                let schema = intent_schema();
1738                let corrections = repair_tagged_enum_array(intents, &schema, "$.intents", options);
1739                all_corrections.extend(corrections);
1740            }
1741        }
1742
1743        let goal: Goal =
1744            serde_json::from_value(value).map_err(|e| GoalParseError::JsonParse(e.to_string()))?;
1745
1746        Ok((goal, all_corrections))
1747    }
1748}
1749
1750#[cfg(feature = "fuzzy-parser")]
1751impl Intent {
1752    /// Parse an Intent from JSON with fuzzy correction for typos
1753    ///
1754    /// # Example
1755    /// ```ignore
1756    /// let json = r#"{"type": "AddDeriv", "taget": "User", "derives": ["Debug"]}"#;
1757    /// let (intent, corrections) = Intent::from_json_fuzzy(json)?;
1758    /// ```
1759    pub fn from_json_fuzzy(
1760        json: &str,
1761    ) -> Result<(Self, Vec<ryo_fuzzy_parser::Correction>), GoalParseError> {
1762        Self::from_json_fuzzy_with_options(json, &ryo_fuzzy_parser::FuzzyOptions::default())
1763    }
1764
1765    /// Parse an Intent from JSON with fuzzy correction using custom options
1766    pub fn from_json_fuzzy_with_options(
1767        json: &str,
1768        options: &ryo_fuzzy_parser::FuzzyOptions,
1769    ) -> Result<(Self, Vec<ryo_fuzzy_parser::Correction>), GoalParseError> {
1770        use crate::intent_schema::intent_schema;
1771        use ryo_fuzzy_parser::repair_tagged_enum_json;
1772
1773        let schema = intent_schema();
1774        let result = repair_tagged_enum_json(json, &schema, options)
1775            .map_err(|e| GoalParseError::FuzzyRepair(e.to_string()))?;
1776
1777        let intent: Intent = serde_json::from_value(result.repaired)
1778            .map_err(|e| GoalParseError::JsonParse(e.to_string()))?;
1779
1780        Ok((intent, result.corrections))
1781    }
1782}
1783
1784/// Error type for Goal/Intent parsing
1785#[cfg(feature = "fuzzy-parser")]
1786#[derive(Debug, thiserror::Error)]
1787pub enum GoalParseError {
1788    #[error("JSON parse error: {0}")]
1789    JsonParse(String),
1790
1791    #[error("Fuzzy repair error: {0}")]
1792    FuzzyRepair(String),
1793}
1794// ============================================================================
1795// From<CascadeSpec> for Intent
1796// ============================================================================
1797
1798impl From<ryo_analysis::cascade::CascadeSpec> for Intent {
1799    fn from(spec: ryo_analysis::cascade::CascadeSpec) -> Self {
1800        use ryo_analysis::cascade::CascadeSpec;
1801
1802        match spec {
1803            CascadeSpec::AddMatchArm {
1804                target,
1805                function_name,
1806                enum_name,
1807                pattern,
1808                body,
1809            } => {
1810                // Construct full function path: target (module or module::Type) + function_name
1811                let fn_path = target
1812                    .child(&function_name)
1813                    .map(|p| p.to_string())
1814                    .unwrap_or_else(|_| format!("{}::{}", target, function_name));
1815                Intent::AddMatchArm {
1816                    symbol_id: None,
1817                    symbol_path: Some(fn_path),
1818                    target_fn: None,
1819                    enum_name,
1820                    pattern,
1821                    body,
1822                }
1823            }
1824            CascadeSpec::AddDerive { symbol_id, derives } => Intent::AddDerive {
1825                symbol_id: Some(format!("{:?}", symbol_id)),
1826                symbol_path: None,
1827                target_type: None,
1828                derives,
1829            },
1830            CascadeSpec::ChangeVisibility {
1831                symbol_id,
1832                visibility,
1833                ..
1834            } => {
1835                let vis = match visibility {
1836                    ryo_analysis::cascade::Visibility::Private => Visibility::Private,
1837                    ryo_analysis::cascade::Visibility::Crate => Visibility::PubCrate,
1838                    ryo_analysis::cascade::Visibility::Super => Visibility::PubSuper,
1839                    ryo_analysis::cascade::Visibility::Public => Visibility::Pub,
1840                };
1841                Intent::ChangeVisibility {
1842                    symbol_id: Some(format!("{:?}", symbol_id)),
1843                    symbol_path: None,
1844                    target_item: None,
1845                    to: vis,
1846                }
1847            }
1848            CascadeSpec::AddUse { path, .. } => Intent::Custom {
1849                description: format!("Add use statement: {}", path),
1850                examples: vec![],
1851            },
1852            CascadeSpec::GenerateImpl {
1853                target, trait_name, ..
1854            } => Intent::Custom {
1855                description: format!("Generate impl {} for {}", trait_name, target),
1856                examples: vec![],
1857            },
1858            CascadeSpec::RemoveMatchArm {
1859                target,
1860                function_name,
1861                enum_name,
1862                pattern,
1863            } => {
1864                // Construct full function path: target (module or module::Type) + function_name
1865                let fn_path = target
1866                    .child(&function_name)
1867                    .map(|p| p.to_string())
1868                    .unwrap_or_else(|_| format!("{}::{}", target, function_name));
1869                Intent::RemoveMatchArm {
1870                    symbol_id: None,
1871                    symbol_path: Some(fn_path),
1872                    target_fn: None,
1873                    enum_name,
1874                    pattern,
1875                }
1876            }
1877        }
1878    }
1879}
1880
1881// ============================================================================
1882// Tests
1883// ============================================================================
1884
1885#[cfg(test)]
1886mod tests {
1887    use super::*;
1888
1889    #[test]
1890    fn test_goal_builder() {
1891        let goal = Goal::rename("is_debug", "enable_debug");
1892        assert!(goal.query.contains("is_debug"));
1893
1894        if let Intent::RenameIdent {
1895            target_ident,
1896            to,
1897            kind,
1898            ..
1899        } = &goal.intents[0]
1900        {
1901            assert_eq!(target_ident.as_deref(), Some("is_debug"));
1902            assert_eq!(to, "enable_debug");
1903            assert_eq!(kind, &IdentKind::Any);
1904        } else {
1905            panic!("Expected RenameIdent intent");
1906        }
1907    }
1908
1909    #[test]
1910    fn test_scope_hint_builder() {
1911        let scope = ScopeHint::new()
1912            .with_file_patterns(vec!["src/**/*.rs".to_string()])
1913            .with_symbol_patterns(vec!["*Config".to_string()])
1914            .with_estimated_scope(EstimatedScope::ManyFiles);
1915
1916        assert_eq!(scope.file_patterns, vec!["src/**/*.rs"]);
1917        assert_eq!(scope.symbol_patterns, vec!["*Config"]);
1918        assert_eq!(scope.estimated_scope, EstimatedScope::ManyFiles);
1919    }
1920
1921    #[test]
1922    fn add_variant_rejects_unknown_field() {
1923        // NG-1: variant_fields is not a valid field — should error, not silently ignore
1924        let json = serde_json::json!({
1925            "type": "AddVariant",
1926            "target_enum": "Filter",
1927            "variant_name": "Add",
1928            "variant_fields": [["left", "Box<Filter>"], ["right", "Box<Filter>"]]
1929        });
1930        let result = serde_json::from_value::<Intent>(json);
1931        assert!(
1932            result.is_err(),
1933            "Unknown field 'variant_fields' should cause deserialization error, got: {:?}",
1934            result.unwrap()
1935        );
1936        let err_msg = result.unwrap_err().to_string();
1937        assert!(
1938            err_msg.contains("unknown field"),
1939            "Error should mention 'unknown field', got: {}",
1940            err_msg
1941        );
1942    }
1943
1944    #[test]
1945    fn add_variant_valid_fields_accepted() {
1946        let json = serde_json::json!({
1947            "type": "AddVariant",
1948            "target_enum": "Filter",
1949            "variant_name": "Add",
1950            "variant_type": "tuple:Box<Filter>,Box<Filter>"
1951        });
1952        let result = serde_json::from_value::<Intent>(json);
1953        assert!(
1954            result.is_ok(),
1955            "Valid AddVariant should parse: {:?}",
1956            result.err()
1957        );
1958    }
1959}