@aramassa/ai-rules 0.1.1-npmjs.20250910072942 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,22 @@
1
1
  ---
2
- description: "Chatmode for improving the project instructions through retrospective analysis.\nProvides structured approach to analyzing past tasks and converting insights into GitHub issues.\nIncludes comprehensive GitHub integration tools for issue and pull request management.\n"
2
+ description: "Chatmode for improving the project instructions through retrospective analysis.\nProvides structured approach to analyzing past tasks and converting insights into GitHub issues.\nIncludes comprehensive GitHub integration tools for issue and pull request management"
3
3
  type: chatmode
4
4
  chatmode: instruction-improve
5
- tools: ["changes", "create_issue", "get_pull_request", "get_pull_request_comments", "get_pull_request_diff", "get_pull_request_files", "list_issues", "get_issue", "get_issue_comments", "search_issues", "update_issue", "add_issue_comment", "assign_copilot_to_issue"]
5
+ tools:
6
+ - changes
7
+ - create_issue
8
+ - get_pull_request
9
+ - get_pull_request_comments
10
+ - get_pull_request_diff
11
+ - get_pull_request_files
12
+ - list_issues
13
+ - get_issue
14
+ - get_issue_comments
15
+ - search_issues
16
+ - update_issue
17
+ - add_issue_comment
18
+ - assign_copilot_to_issue
19
+ - search_pull_requests
6
20
  ---
7
21
 
8
22
  # Improve Instruction
@@ -133,7 +147,7 @@ get_issue(owner="{OWNER}", repo="{REPO}", issue_number={ISSUE_NUMBER})
133
147
  #### 新規Issue作成
134
148
  ```
135
149
  create_issue(
136
- owner="{OWNER}",
150
+ owner="{OWNER}",
137
151
  repo="{REPO}",
138
152
  title="[Instruction] 改善内容のタイトル",
139
153
  body="詳細な改善内容...",
@@ -1,38 +1,95 @@
1
1
  ---
2
- description: "Chatmode for supporting todo_plans creation with structured planning process.\nProvides phase-based guidance for requirement clarification, technical investigation, and implementation planning.\n"
2
+ description: "Enhanced chatmode for comprehensive todo_plans creation with 4-phase structured planning process.\nProvides interactive phase-based guidance for requirement clarification, technical investigation, implementation planning, and quality assurance.\n"
3
3
  type: chatmode
4
4
  chatmode: planning
5
- tools: ["changes", "searchResults", "editFiles", "search", "add_issue_comment", "add_sub_issue", "create_issue", "get_code_scanning_alert", "get_discussion", "get_discussion_comments", "get_issue_comments", "get_pull_request", "get_pull_request_diff", "get_pull_request_files", "list_commits", "list_issues", "list_pull_requests", "search_code", "search_issues", "search_pull_requests", "search_repositories", "update_issue", "update_pull_request"]
5
+ tools:
6
+ - "changes"
7
+ - "searchResults"
8
+ - "editFiles"
9
+ - "search"
10
+ - "add_issue_comment"
11
+ - "add_sub_issue"
12
+ - "create_issue"
13
+ - "get_code_scanning_alert"
14
+ - "get_discussion"
15
+ - "get_discussion_comments"
16
+ - "get_issue_comments"
17
+ - "get_pull_request"
18
+ - "get_pull_request_diff"
19
+ - "get_pull_request_files"
20
+ - "list_commits"
21
+ - "list_issues"
22
+ - "list_pull_requests"
23
+ - "search_code"
24
+ - "search_issues"
25
+ - "search_pull_requests"
26
+ - "search_repositories"
27
+ - "update_issue"
28
+ - "update_pull_request"
29
+ - "assign_copilot_to_issue"
30
+ - "create_pull_request_with_copilot"
6
31
  ---
7
32
 
8
- # Planning Instructions
33
+ # Enhanced Planning Instructions
9
34
 
10
35
  todo_plans作成を段階的に支援し、一貫性のあるプランニングプロセスを実現します。
11
36
 
12
- ## プランニングプロセス
37
+ ## インタラクティブ・プランニングプロセス
38
+
39
+ 私と一緒に以下の4段階でプランを作成していきましょう。各段階で質問形式のガイダンスを提供し、漏れのない計画策定をサポートします。
13
40
 
14
41
  ### Phase 1: 要件明確化
15
42
 
16
- まず、以下の項目を明確にしましょう:
43
+ 私と一緒に以下を明確にしていきましょう:
44
+
45
+ #### 📋 ガイダンス質問
46
+
47
+ **1. 何を実現したいですか?**
48
+ - ユーザーの具体的なニーズは何ですか?
49
+ - どのような価値を提供しますか?
50
+ - 現在直面している課題や問題点は何ですか?
51
+
52
+ **2. 成功の基準は何ですか?**
53
+ - どうなれば「完了」と言えますか?
54
+ - 測定可能な指標はありますか?
55
+ - 完成時にどのような状態になっているべきですか?
56
+
57
+ **3. 制約はありますか?**
58
+ - 技術的な制約(互換性、パフォーマンス等)
59
+ - 時間的な制約(デッドライン、リリース予定等)
60
+ - リソースの制約(工数、予算等)
61
+ - プロジェクト固有の制約(コーディング規約、アーキテクチャ等)
62
+
63
+ #### ✅ Phase 1 チェックリスト
17
64
 
18
- - **背景状況の整理**
19
- - 現在直面している課題や問題点
20
- - 改善したい機能や追加したい機能
21
- - なぜこの改修が必要なのか
65
+ - [ ] 背景状況と課題が具体的に整理されている
66
+ - [ ] 解決したい課題が明確に特定されている
67
+ - [ ] 成功条件が検証可能な形で定義されている
68
+ - [ ] 技術的・時間的・リソース制約が把握されている
69
+ - [ ] ステークホルダーの期待値が明確になっている
22
70
 
23
- - **解決したい課題の特定**
24
- - 具体的に何を実現したいか
25
- - どのような価値を提供するか
26
- - 現状との違いを明確化
71
+ ### Phase 2: 技術調査・分析
27
72
 
28
- - **成功条件の定義**
29
- - 完成時にどのような状態になっているべきか
30
- - 何をもって成功とするか
31
- - 検証可能な基準の設定
73
+ 技術面での詳細調査を行います:
32
74
 
33
- ### Phase 2: 技術調査
75
+ #### 📋 ガイダンス質問
34
76
 
35
- 次に、技術面での調査を行います:
77
+ **1. 既存システムへの影響はどの程度ですか?**
78
+ - 関連するファイルやディレクトリはどこですか?
79
+ - 既存機能への影響度はどの程度ですか?
80
+ - 破壊的変更が発生する可能性はありますか?
81
+
82
+ **2. 技術的な実装アプローチは何が考えられますか?**
83
+ - 複数の実装方法のうち、どれが最適ですか?
84
+ - 既存のパターンや類似機能を参考にできますか?
85
+ - 新しい技術やライブラリの導入が必要ですか?
86
+
87
+ **3. リスクと課題は何が予想されますか?**
88
+ - 技術的なリスクはどのようなものがありますか?
89
+ - パフォーマンスやセキュリティへの影響はありますか?
90
+ - 他の開発作業との競合はありませんか?
91
+
92
+ #### 🔍 調査項目
36
93
 
37
94
  - **既存コードベースの影響範囲分析**
38
95
  - 関連するファイルやディレクトリの特定
@@ -49,10 +106,38 @@ todo_plans作成を段階的に支援し、一貫性のあるプランニング
49
106
  - 同様の機能実装がある場合の参照
50
107
  - ベストプラクティスの活用
51
108
 
109
+ #### ✅ Phase 2 チェックリスト
110
+
111
+ - [ ] 影響を受けるファイル・ディレクトリが特定されている
112
+ - [ ] 既存機能への影響度が評価されている
113
+ - [ ] 複数の実装アプローチが検討されている
114
+ - [ ] 技術的リスクが洗い出されている
115
+ - [ ] 参考にできる既存パターンが調査されている
116
+ - [ ] 新規依存関係の必要性が判断されている
117
+
52
118
  ### Phase 3: 実装計画策定
53
119
 
54
120
  具体的な実装計画を立てます:
55
121
 
122
+ #### 📋 ガイダンス質問
123
+
124
+ **1. 実装する順序はどのように決めますか?**
125
+ - 依存関係を考慮した最適な順序は何ですか?
126
+ - 段階的リリース(フェーズ分け)は可能ですか?
127
+ - リスクを最小化する実装順序は何ですか?
128
+
129
+ **2. どのファイルを修正・新規作成しますか?**
130
+ - 新規作成が必要なファイルは何ですか?
131
+ - 修正が必要な既存ファイルは何ですか?
132
+ - 影響を受ける可能性があるファイルは何ですか?
133
+
134
+ **3. テスト戦略はどのように設計しますか?**
135
+ - どのような種類のテストが必要ですか?
136
+ - テストデータやフィクスチャは何が必要ですか?
137
+ - 既存テストへの影響はありますか?
138
+
139
+ #### 🎯 計画項目
140
+
56
141
  - **改修対象ファイルの特定**
57
142
  - 新規作成が必要なファイル
58
143
  - 修正が必要な既存ファイル
@@ -68,8 +153,80 @@ todo_plans作成を段階的に支援し、一貫性のあるプランニング
68
153
  - 対策・回避策の検討
69
154
  - ロールバック計画の準備
70
155
 
156
+ #### ✅ Phase 3 チェックリスト
157
+
158
+ - [ ] 実装対象ファイルが具体的にリストアップされている
159
+ - [ ] 実装順序が論理的に決定されている
160
+ - [ ] テスト戦略が明確に定義されている
161
+ - [ ] 潜在的リスクと対策が検討されている
162
+ - [ ] 工数見積もりが妥当である
163
+ - [ ] ロールバック計画が準備されている
164
+
165
+ ### Phase 4: 品質保証・レビュー
166
+
167
+ プランの妥当性と品質を確保します:
168
+
169
+ #### 📋 ガイダンス質問
170
+
171
+ **1. プランの完全性は確保されていますか?**
172
+ - すべての要件がカバーされていますか?
173
+ - 見落としている重要な観点はありませんか?
174
+ - 実装手順に漏れはありませんか?
175
+
176
+ **2. 実現可能性は検証されていますか?**
177
+ - 技術的に実装可能ですか?
178
+ - 必要なリソースは現実的ですか?
179
+ - タイムラインは妥当ですか?
180
+
181
+ **3. レビュー観点は明確ですか?**
182
+ - どの観点でレビューを受けるべきですか?
183
+ - 誰にレビューを依頼すべきですか?
184
+ - レビューのタイミングはいつが適切ですか?
185
+
186
+ #### 🔍 品質チェック項目
187
+
188
+ **完全性チェック**
189
+ - [ ] 要件が具体的に定義されている
190
+ - [ ] 実装手順が詳細に記載されている
191
+ - [ ] テスト計画が含まれている
192
+ - [ ] リスクが特定されている
193
+ - [ ] 成功基準が明確である
194
+
195
+ **実現可能性チェック**
196
+ - [ ] 技術的に実装可能である
197
+ - [ ] 必要なリソースが明確である
198
+ - [ ] 依存関係が整理されている
199
+ - [ ] タイムラインが現実的である
200
+ - [ ] 制約条件が考慮されている
201
+
202
+ **品質保証チェック**
203
+ - [ ] コーディング規約への準拠計画がある
204
+ - [ ] テストカバレッジ基準が設定されている
205
+ - [ ] ドキュメント更新計画がある
206
+ - [ ] レビュープロセスが定義されている
207
+
208
+ #### ✅ Phase 4 チェックリスト
209
+
210
+ - [ ] プラン全体の完全性が確認されている
211
+ - [ ] 実現可能性が技術的に検証されている
212
+ - [ ] 品質基準が明確に定義されている
213
+ - [ ] レビュー観点と方法が決定されている
214
+ - [ ] リリース準備とデプロイ計画がある
215
+
71
216
  ## プロジェクト固有ガイドライン
72
217
 
218
+ ### ai-rules プロジェクト固有の考慮事項
219
+
220
+ #### アーキテクチャ原則
221
+ - **モノレポ構造**: packages/core, packages/extract の責務分離
222
+ - **CLIインターフェースの一貫性**: コマンドオプションの統一
223
+ - **後方互換性の維持**: 既存ユーザーへの影響最小化
224
+
225
+ #### 開発プロセス
226
+ - **skel/ ディレクトリとの整合性**: 構造変更時の同期必須
227
+ - **テストファーストアプローチ**: TDDによる品質確保
228
+ - **ドキュメントの自動生成対応**: docs-ai/ での記録
229
+
73
230
  ### monorepoワークスペース考慮事項
74
231
 
75
232
  - **core/commonパッケージの依存ルール**
@@ -94,6 +251,8 @@ todo_plans作成を段階的に支援し、一貫性のあるプランニング
94
251
 
95
252
  todo_plans作成時に必ず確認する項目:
96
253
 
254
+ ### 📋 必須確認項目
255
+
97
256
  - [ ] **skel/構造との整合性確認**
98
257
  - スケルトンファイルの更新が必要か
99
258
  - 構造変更がskel/と一致しているか
@@ -117,11 +276,45 @@ todo_plans作成時に必ず確認する項目:
117
276
  - package.jsonの更新内容
118
277
  - .npmignoreの確認
119
278
 
120
- ## テンプレート活用ガイド
279
+ ## プランニングパターン・テンプレート
280
+
281
+ 用途に応じて以下のテンプレートを活用してください:
282
+
283
+ ### 🎯 機能実装パターン
284
+ 新機能追加時のプランニングアプローチ
285
+ - Phase 1: ユーザーストーリーと受け入れ基準
286
+ - Phase 2: システムアーキテクチャ設計
287
+ - Phase 3: インクリメンタル実装計画
288
+ - Phase 4: 統合テストと品質保証
289
+
290
+ ### 🔧 リファクタリングパターン
291
+ 既存コードの改善・整理時のアプローチ
292
+ - Phase 1: 改善目標と品質基準
293
+ - Phase 2: 影響範囲とリスク分析
294
+ - Phase 3: 段階的リファクタリング計画
295
+ - Phase 4: 回帰テストと検証
296
+
297
+ ### 🐛 バグ修正パターン
298
+ 問題解決時のプランニングアプローチ
299
+ - Phase 1: 問題の根本原因特定
300
+ - Phase 2: 修正方法の検討と影響分析
301
+ - Phase 3: 修正実装と検証計画
302
+ - Phase 4: 再発防止策の検討
303
+
304
+ ### 🏗️ アーキテクチャ変更パターン
305
+ 大規模な構造変更時のアプローチ
306
+ - Phase 1: 変更の必要性と目標設定
307
+ - Phase 2: 移行戦略とリスク評価
308
+ - Phase 3: 段階的移行計画
309
+ - Phase 4: 安定性確認と運用計画
310
+
311
+ ## 標準テンプレート構造
121
312
 
122
313
  todo_plans作成時は以下のテンプレート構造を使用してください:
123
314
 
124
315
  ```markdown
316
+ # [機能名] 実装計画
317
+
125
318
  ## 背景
126
319
 
127
320
  [現状の課題・改善したい点を具体的に記載]
@@ -130,20 +323,40 @@ todo_plans作成時は以下のテンプレート構造を使用してくださ
130
323
 
131
324
  [実現したい機能・改善内容を段階的に列挙]
132
325
 
326
+ ## 要件分析
327
+ - [ ] ユーザーストーリー明確化
328
+ - [ ] 受け入れ基準定義
329
+ - [ ] 技術的制約確認
330
+
133
331
  ## 実装後の挙動に関する説明
134
332
 
135
333
  出力があるのであれば、それがどのような出力になるか、具体例を表示する。
136
334
 
335
+ ## アーキテクチャ設計
336
+ - [ ] システム影響範囲特定
337
+ - [ ] インターフェース設計
338
+ - [ ] データモデル設計
339
+
137
340
  ## 改修内容
138
341
 
139
342
  ### 改修対象ファイル一覧
140
343
 
141
344
  [影響を受けるファイルを漏れなくリストアップ]
142
345
 
346
+ ### 実装計画
347
+ - [ ] タスク分解
348
+ - [ ] 依存関係整理
349
+ - [ ] 実装順序決定
350
+
143
351
  ### ファイルごと改修内容
144
352
 
145
353
  [各ファイルの具体的な変更内容。具体的なコードは書かない。どう修正するかを説明する]
146
354
 
355
+ ### 品質保証
356
+ - [ ] テスト戦略
357
+ - [ ] レビュー計画
358
+ - [ ] デプロイメント計画
359
+
147
360
  ### テスト時の確認事項
148
361
 
149
362
  [品質確保のためのチェック項目]
@@ -1,19 +1,200 @@
1
1
  ---
2
2
  type: planning
3
+ category: methodology
4
+ focus: structured-process
3
5
  ---
4
6
 
5
- ## コミュニケーションのルール
7
+ # プランニング手法とベストプラクティス
6
8
 
7
- ### パスの指定について
9
+ ## 段階的プランニング手法
8
10
 
11
+ ### 4段階プランニングプロセス
12
+
13
+ 効果的なプランニングのために、以下の4段階のプロセスを順次実行します:
14
+
15
+ #### Phase 1: 要件明確化
16
+ **目的**: プロジェクトの目標と制約を明確化
17
+ **手法**:
18
+ - 5W1H分析(Why, What, Who, When, Where, How)
19
+ - ステークホルダー分析
20
+ - 制約条件の洗い出し
21
+ - 成功基準の定義
22
+
23
+ **品質チェック**:
24
+ - [ ] 背景と課題が具体的に説明されている
25
+ - [ ] 成功基準が測定可能である
26
+ - [ ] 制約条件が網羅されている
27
+
28
+ #### Phase 2: 技術調査・分析
29
+ **目的**: 実装可能性と技術的リスクの評価
30
+ **手法**:
31
+ - システム影響分析(Impact Analysis)
32
+ - 技術スタック調査
33
+ - アーキテクチャ分析
34
+ - 既存パターンの調査
35
+
36
+ **品質チェック**:
37
+ - [ ] 影響範囲が特定されている
38
+ - [ ] 技術的選択肢が比較検討されている
39
+ - [ ] リスクが定量的に評価されている
40
+
41
+ #### Phase 3: 実装計画策定
42
+ **目的**: 具体的で実行可能な計画の作成
43
+ **手法**:
44
+ - WBS(Work Breakdown Structure)
45
+ - 依存関係分析
46
+ - リソース見積もり
47
+ - マイルストーン設定
48
+
49
+ **品質チェック**:
50
+ - [ ] タスクが適切に分解されている
51
+ - [ ] 実装順序が論理的である
52
+ - [ ] 工数見積もりが現実的である
53
+
54
+ #### Phase 4: 品質保証・レビュー
55
+ **目的**: プランの妥当性と品質の確保
56
+ **手法**:
57
+ - レビューチェックリスト
58
+ - リスクアセスメント
59
+ - 品質ゲート設定
60
+ - 承認プロセス
61
+
62
+ **品質チェック**:
63
+ - [ ] プラン全体の一貫性が確保されている
64
+ - [ ] リスクが適切に管理されている
65
+ - [ ] レビュー観点が明確である
66
+
67
+ ## プランニング品質チェック項目
68
+
69
+ ### 完全性チェックリスト
70
+ - [ ] **要件定義の完全性**
71
+ - すべてのステークホルダーが特定されている
72
+ - 機能要件・非機能要件が明確である
73
+ - 制約条件が網羅されている
74
+ - 前提条件が明記されている
75
+
76
+ - [ ] **実装計画の詳細度**
77
+ - タスクが実行可能なレベルまで分解されている
78
+ - 各タスクの成果物が明確である
79
+ - 依存関係が正確に把握されている
80
+ - 工数見積もりが根拠とともに示されている
81
+
82
+ - [ ] **テスト戦略の包括性**
83
+ - テストケースの方針が明確である
84
+ - テストデータの準備方法が決まっている
85
+ - テスト環境の準備が考慮されている
86
+ - 受け入れ基準が検証可能である
87
+
88
+ ### 実現可能性チェックリスト
89
+ - [ ] **技術的実現可能性**
90
+ - 選択技術の成熟度と安定性
91
+ - チームの技術スキルとの適合性
92
+ - 既存システムとの整合性
93
+ - パフォーマンス・スケーラビリティ要件への対応
94
+
95
+ - [ ] **リソース実現可能性**
96
+ - 必要な人的リソースの確保可能性
97
+ - 予算・コスト制約への適合
98
+ - 時間制約との整合性
99
+ - 外部依存の調整可能性
100
+
101
+ - [ ] **運用実現可能性**
102
+ - 運用・保守体制での対応可能性
103
+ - 既存運用プロセスとの整合性
104
+ - 監視・アラート体制の確立可能性
105
+ - 障害対応・復旧手順の明確性
106
+
107
+ ### リスク評価・管理チェックリスト
108
+ - [ ] **リスク特定の網羅性**
109
+ - 技術リスク(性能、互換性、セキュリティ等)
110
+ - プロジェクトリスク(スケジュール、リソース、スコープ等)
111
+ - ビジネスリスク(市場、法規制、競合等)
112
+ - 運用リスク(障害、保守、スケーラビリティ等)
113
+
114
+ - [ ] **リスク対策の具体性**
115
+ - 各リスクの発生確率と影響度の評価
116
+ - 具体的な予防策・軽減策
117
+ - 事象発生時の対応策・復旧策
118
+ - リスク監視の方法・頻度
119
+
120
+ ## プロジェクトパターン別ベストプラクティス
121
+
122
+ ### CLI機能拡張パターン
123
+ **特徴**: コマンドライン引数、オプション、出力フォーマットの拡張
124
+ **重点確認項目**:
125
+ - [ ] 既存オプションとの互換性維持
126
+ - [ ] ヘルプメッセージの一貫性
127
+ - [ ] エラーメッセージの適切性
128
+ - [ ] 出力フォーマットの統一性
129
+
130
+ ### ライブラリ機能追加パターン
131
+ **特徴**: APIの拡張、新規モジュールの追加
132
+ **重点確認項目**:
133
+ - [ ] API設計の一貫性
134
+ - [ ] 後方互換性の保証
135
+ - [ ] TypeScript型定義の整合性
136
+ - [ ] パッケージ依存関係の管理
137
+
138
+ ### リファクタリングパターン
139
+ **特徴**: コード品質改善、構造最適化
140
+ **重点確認項目**:
141
+ - [ ] 外部インターフェースの不変性
142
+ - [ ] 既存テストの継続実行可能性
143
+ - [ ] パフォーマンスへの影響評価
144
+ - [ ] 段階的移行の安全性
145
+
146
+ ### バグ修正パターン
147
+ **特徴**: 問題解決、品質向上
148
+ **重点確認項目**:
149
+ - [ ] 根本原因の特定と対策
150
+ - [ ] 再現手順と修正検証
151
+ - [ ] 再発防止策の確立
152
+ - [ ] 関連する潜在問題の調査
153
+
154
+ ## ドキュメント作成計画ガイドライン
155
+
156
+ ### 事前に計画すべきドキュメント
157
+ - [ ] **技術仕様書**: システムの設計・アーキテクチャを記録
158
+ - [ ] **ユーザーマニュアル**: エンドユーザー向けの操作ガイド
159
+ - [ ] **API ドキュメント**: 開発者向けのAPIリファレンス
160
+ - [ ] **運用ドキュメント**: 保守・運用担当者向けの手順書
161
+ - [ ] **テストドキュメント**: テスト仕様・結果・レポート
162
+
163
+ ### ドキュメント更新計画
164
+ - **作成タイミング**: いつ作成・更新するか
165
+ - **責任者**: 誰が作成・レビューするか
166
+ - **フォーマット**: どの形式で作成するか
167
+ - **配布・共有**: どこに配置・共有するか
168
+ - **メンテナンス**: どのように最新性を保つか
169
+
170
+ ## プランニングワークフロー管理
171
+
172
+ ### todo_plans → change_plans → GitHub Issues の流れ
173
+
174
+ #### 1. todo_plans段階
175
+ - **目的**: アイデアの整理と初期プランニング
176
+ - **期間**: プロジェクト開始前の計画策定期間
177
+ - **成果物**: 詳細な実装計画書
178
+ - **品質基準**: 4段階プランニングプロセスの完了
179
+
180
+ #### 2. change_plans段階
181
+ - **目的**: 実装中の進捗管理と計画調整
182
+ - **期間**: 実装開始から完了まで
183
+ - **成果物**: 実装実績と学習内容の記録
184
+ - **品質基準**: 計画の実行状況と乖離の記録
185
+
186
+ #### 3. GitHub Issues段階
187
+ - **目的**: チーム内での課題管理と進捗共有
188
+ - **期間**: 公式な課題管理が必要な期間
189
+ - **成果物**: Issue、Pull Request、レビュー記録
190
+ - **品質基準**: ステークホルダーとの情報共有
191
+
192
+ ### コミュニケーションのルール
193
+
194
+ #### パスの指定について
9
195
  相対パスはプロジェクトのルートディレクトリを基準に考えること。
10
196
  絶対パスはシステムのルートディレクトリを基準に考えること。
11
197
 
12
- 例:
13
- - `docs/README.md` は、プロジェクトのルートディレクトリからの相対パス指定
14
- - `./docs/README.md` は、プロジェクトのルートディレクトリからの相対パス指定
15
- - `/docs/README.md` は、システムのルートディレクトリからのパス指定
16
-
17
198
  ### 根源的なルール
18
199
 
19
200
  - 機能追加のために不明な点があれば、追加で確認する
@@ -0,0 +1,206 @@
1
+ ---
2
+ type: test
3
+ category: best-practices
4
+ focus: environment-independence
5
+ ---
6
+
7
+ # 環境非依存テスト実行のためのパス指定ルール
8
+
9
+ ## 概要
10
+
11
+ テスト実行は、ローカル環境でもCI環境でもどちらでもパスするように気をつける必要があります。パス指定などは絶対パス指定にせず、環境に依存しない書き方をすることが重要です。
12
+
13
+ ## 背景・理由
14
+
15
+ コーディングするのはAIだけではありません。手動で修正した内容が問題ないかの確認を実施する際に、ローカル環境でテストがこけてしまうと困ります。CI環境とローカル環境の差異によるテスト失敗を防ぐため、環境に依存しないテストコードの作成が必要です。
16
+
17
+ ## 基本原則
18
+
19
+ ### 1. 絶対パスの使用を避ける
20
+
21
+ - **禁止**: `/home/user/project/test-data/sample.txt` のような絶対パス指定
22
+ - **推奨**: 相対パス や Node.js の `path` モジュールを使った動的パス生成
23
+
24
+ ### 2. 一時ディレクトリの適切な使用
25
+
26
+ - **推奨**: `os.tmpdir()` と `mkdtemp()` の組み合わせを使用
27
+ - **理由**: OS固有の一時ディレクトリ場所を自動で取得し、ユニークなディレクトリを作成
28
+
29
+ ### 3. プラットフォーム依存機能の適切な処理
30
+
31
+ - **推奨**: テスト前後で環境変数やプラットフォーム情報を保存・復元
32
+ - **推奨**: プラットフォーム固有の機能は条件分岐で対応
33
+
34
+ ## 実装パターン
35
+
36
+ ### パターン 1: 一時ディレクトリの使用
37
+
38
+ ```typescript
39
+ import { mkdtemp, rm } from "fs/promises";
40
+ import path from "path";
41
+ import os from "os";
42
+
43
+ describe("Example Test", () => {
44
+ let testTempDir: string;
45
+
46
+ beforeEach(async () => {
47
+ // 環境に依存しない一時ディレクトリの作成
48
+ testTempDir = await mkdtemp(path.join(os.tmpdir(), "test-prefix-"));
49
+ });
50
+
51
+ afterEach(async () => {
52
+ // テスト後のクリーンアップ
53
+ try {
54
+ await rm(testTempDir, { recursive: true, force: true });
55
+ } catch {
56
+ // エラーは無視(クリーンアップエラーは致命的ではない)
57
+ }
58
+ });
59
+
60
+ it("should work with temporary files", async () => {
61
+ const testFile = path.join(testTempDir, "test.txt");
62
+ // テスト実装...
63
+ });
64
+ });
65
+ ```
66
+
67
+ ### パターン 2: 環境変数の保存・復元
68
+
69
+ ```typescript
70
+ describe("Environment Test", () => {
71
+ let originalEnv: NodeJS.ProcessEnv;
72
+ let originalPlatform: string;
73
+
74
+ beforeEach(() => {
75
+ // 元の環境を保存
76
+ originalEnv = { ...process.env };
77
+ originalPlatform = process.platform;
78
+ });
79
+
80
+ afterEach(() => {
81
+ // 環境を復元
82
+ process.env = originalEnv;
83
+ Object.defineProperty(process, 'platform', {
84
+ value: originalPlatform
85
+ });
86
+ });
87
+
88
+ it("should handle platform differences", () => {
89
+ // プラットフォーム固有のテスト...
90
+ });
91
+ });
92
+ ```
93
+
94
+ ### パターン 3: 相対パスの適切な解決
95
+
96
+ ```typescript
97
+ import path from "path";
98
+
99
+ describe("Path Resolution Test", () => {
100
+ it("should resolve paths correctly", () => {
101
+ // ❌ 絶対パス(環境依存)
102
+ // const filePath = "/home/user/project/src/cli.ts";
103
+
104
+ // ✅ 相対パス(環境非依存)
105
+ const filePath = path.resolve("src/cli.ts");
106
+
107
+ // ✅ パッケージルートからの相対パス
108
+ const packageRoot = findPackageRoot();
109
+ const configPath = path.join(packageRoot, "config/settings.json");
110
+ });
111
+ });
112
+ ```
113
+
114
+ ### パターン 4: CLI実行時のパス指定
115
+
116
+ ```typescript
117
+ import { execSync } from "child_process";
118
+ import path from "path";
119
+
120
+ describe("CLI Test", () => {
121
+ it("should execute CLI with proper paths", () => {
122
+ const testSrcDir = path.join(testTempDir, "test-src");
123
+
124
+ // ❌ 絶対パスでCLIファイル指定
125
+ // execSync(`/home/user/project/dist/cli.js extract --src ${testSrcDir}`);
126
+
127
+ // ✅ 相対パスでCLIファイル指定
128
+ execSync(`npx tsx src/cli.ts extract --src ${testSrcDir}`, {
129
+ cwd: process.cwd(), // 現在の作業ディレクトリを明示
130
+ stdio: "pipe"
131
+ });
132
+ });
133
+ });
134
+ ```
135
+
136
+ ## 避けるべきパターン
137
+
138
+ ### ❌ 絶対パスの直接指定
139
+
140
+ ```typescript
141
+ // 悪い例: ユーザーディレクトリやシステムパスの直接指定
142
+ const testFile = "/home/runner/project/test/data.txt";
143
+ const configPath = "C:\\Users\\user\\project\\config.json";
144
+ ```
145
+
146
+ ### ❌ プラットフォーム固有パスの固定
147
+
148
+ ```typescript
149
+ // 悪い例: パス区切り文字の直接使用
150
+ const filePath = "src/utils/helper.js"; // Windowsでは問題になる可能性
151
+ // 良い例: path.join() の使用
152
+ const filePath = path.join("src", "utils", "helper.js");
153
+ ```
154
+
155
+ ### ❌ 環境変数の復元なし
156
+
157
+ ```typescript
158
+ // 悪い例: テスト後に環境を復元しない
159
+ it("should test with modified env", () => {
160
+ process.env.TEST_VAR = "test-value";
161
+ // テスト後も環境変数が残り、他のテストに影響する
162
+ });
163
+ ```
164
+
165
+ ## クリーンアップのベストプラクティス
166
+
167
+ ### 確実なリソース解放
168
+
169
+ ```typescript
170
+ afterEach(async () => {
171
+ // ファイルシステムリソースの解放
172
+ try {
173
+ await rm(testTempDir, { recursive: true, force: true });
174
+ } catch {
175
+ // クリーンアップエラーは無視(テストの失敗より重要ではない)
176
+ }
177
+ });
178
+ ```
179
+
180
+ ### プロセス状態の復元
181
+
182
+ ```typescript
183
+ afterEach(() => {
184
+ // プロセス状態を確実に復元
185
+ process.env = originalEnv;
186
+ process.chdir(originalCwd);
187
+ });
188
+ ```
189
+
190
+ ## CI/ローカル共通の注意点
191
+
192
+ 1. **タイムアウト設定**: CI環境では処理が遅い場合があるため、適切なタイムアウト値を設定
193
+ 2. **並列実行対応**: 複数のテストが同時実行されても競合しないよう、ユニークな識別子を使用
194
+ 3. **権限問題の回避**: 読み取り専用ファイルや権限制限のあるディレクトリを避ける
195
+ 4. **パスの長さ制限**: Windows環境での長いパス名制限を考慮
196
+
197
+ ## 検証方法
198
+
199
+ テストが環境非依存であることを確認する方法:
200
+
201
+ 1. **ローカルでの実行**: `npm test`
202
+ 2. **異なるワーキングディレクトリからの実行**: 別のディレクトリからテストを実行
203
+ 3. **CI環境での実行**: GitHub Actions や他のCI環境での動作確認
204
+ 4. **異なるOS環境**: 可能であれば Windows/Linux/macOS での動作確認
205
+
206
+ このルールに従うことで、テストはどの環境でも一貫して動作し、開発者の生産性向上とCI/CDパイプラインの安定性を実現できます。
package/dist/cli.js CHANGED
@@ -10,6 +10,7 @@ import require$$3 from 'node:fs';
10
10
  import require$$4 from 'node:process';
11
11
  import { fileURLToPath } from 'url';
12
12
  import { createHash } from 'crypto';
13
+ import { homedir } from 'os';
13
14
 
14
15
  /******************************************************************************
15
16
  Copyright (c) Microsoft Corporation.
@@ -10042,6 +10043,53 @@ class VariableResolver {
10042
10043
  }
10043
10044
  }
10044
10045
 
10046
+ /**
10047
+ * Path expansion utility that supports tilde (~) and environment variable expansion
10048
+ *
10049
+ * Features:
10050
+ * - Tilde expansion: ~/path -> /Users/username/path (Unix-like systems only)
10051
+ * - Environment variable expansion: $HOME/path -> /Users/username/path
10052
+ * - Environment variable expansion: ${VAR}/path -> /value/path
10053
+ * - Complex combinations: ~/projects/${PROJECT_NAME}/file.md
10054
+ *
10055
+ * @param inputPath - Path to expand and resolve
10056
+ * @returns Expanded and resolved absolute path
10057
+ * @throws Error if required environment variables are missing
10058
+ */
10059
+ function resolvePath(inputPath) {
10060
+ if (!inputPath || typeof inputPath !== 'string') {
10061
+ throw new Error('Path must be a non-empty string');
10062
+ }
10063
+ let expandedPath = inputPath;
10064
+ // 1. Handle tilde expansion first (Unix-like systems only)
10065
+ if (expandedPath.startsWith('~/')) {
10066
+ // Check if we're on Windows
10067
+ if (process.platform === 'win32') {
10068
+ throw new Error('Tilde (~) expansion is not supported on Windows. Use %USERPROFILE% or environment variables instead.');
10069
+ }
10070
+ const homeDir = homedir();
10071
+ expandedPath = path.join(homeDir, expandedPath.slice(2));
10072
+ }
10073
+ else if (expandedPath === '~') {
10074
+ if (process.platform === 'win32') {
10075
+ throw new Error('Tilde (~) expansion is not supported on Windows. Use %USERPROFILE% or environment variables instead.');
10076
+ }
10077
+ expandedPath = homedir();
10078
+ }
10079
+ // 2. Handle environment variable expansion
10080
+ // Support both $VAR and ${VAR} syntax
10081
+ expandedPath = expandedPath.replace(/\$\{([^}]+)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g, (match, bracedVar, simpleVar) => {
10082
+ const varName = bracedVar || simpleVar;
10083
+ const envValue = process.env[varName];
10084
+ if (envValue === undefined) {
10085
+ throw new Error(`Environment variable '${varName}' is not defined`);
10086
+ }
10087
+ return envValue;
10088
+ });
10089
+ // 3. Resolve to absolute path
10090
+ return path.resolve(expandedPath);
10091
+ }
10092
+
10045
10093
  const EXCLUDED_STATS_ATTRS = new Set(["human-instruction", "title"]);
10046
10094
  const MAX_IMPORT_DEPTH = 10;
10047
10095
  /**
@@ -10094,7 +10142,7 @@ class ContentTracker {
10094
10142
  * Checks if the same content has already been written to the file
10095
10143
  */
10096
10144
  hasContent(outputFile, content) {
10097
- const resolvedFile = path.resolve(outputFile);
10145
+ const resolvedFile = resolvePath(outputFile);
10098
10146
  const contentHash = this.generateContentHash(content);
10099
10147
  const fileHashes = this.fileContentHashes.get(resolvedFile);
10100
10148
  return fileHashes ? fileHashes.has(contentHash) : false;
@@ -10103,7 +10151,7 @@ class ContentTracker {
10103
10151
  * Records that content has been written to a file
10104
10152
  */
10105
10153
  addContent(outputFile, content) {
10106
- const resolvedFile = path.resolve(outputFile);
10154
+ const resolvedFile = resolvePath(outputFile);
10107
10155
  const contentHash = this.generateContentHash(content);
10108
10156
  if (!this.fileContentHashes.has(resolvedFile)) {
10109
10157
  this.fileContentHashes.set(resolvedFile, new Set());
@@ -10114,7 +10162,7 @@ class ContentTracker {
10114
10162
  * Checks if a file has been written to (for auto-append logic)
10115
10163
  */
10116
10164
  hasFile(outputFile) {
10117
- const resolvedFile = path.resolve(outputFile);
10165
+ const resolvedFile = resolvePath(outputFile);
10118
10166
  return this.fileContentHashes.has(resolvedFile);
10119
10167
  }
10120
10168
  /**
@@ -10160,8 +10208,56 @@ function resolveDefaultSrcDir(providedSrc) {
10160
10208
  return path.join(packageRoot, 'artifact');
10161
10209
  }
10162
10210
  /**
10163
- * Resolves recipe path, supporting package presets with colon syntax
10211
+ * Resolves output path with baseDir support following priority order:
10212
+ * 1. CLI baseDir option (highest priority)
10213
+ * 2. Import-level baseDir (new priority level)
10214
+ * 3. Recipe config.baseDir
10215
+ * 4. Existing behavior (relative to recipe file or current directory)
10164
10216
  */
10217
+ function resolveOutputPath(itemOut, cliBaseDir, importBaseDir, recipeBaseDir, recipePath) {
10218
+ // Check if the itemOut contains environment variables or tilde - if so, try to expand
10219
+ let processedItemOut = itemOut;
10220
+ if (itemOut.includes('$') || itemOut.startsWith('~')) {
10221
+ try {
10222
+ processedItemOut = resolvePath(itemOut);
10223
+ // If expansion resulted in an absolute path, use it as-is (ignore baseDir)
10224
+ if (path.isAbsolute(processedItemOut)) {
10225
+ return processedItemOut;
10226
+ }
10227
+ }
10228
+ catch (error) {
10229
+ // If expansion fails, continue with original itemOut
10230
+ processedItemOut = itemOut;
10231
+ }
10232
+ }
10233
+ // If output path is already absolute, use it as-is (ignore baseDir)
10234
+ if (path.isAbsolute(processedItemOut)) {
10235
+ return processedItemOut;
10236
+ }
10237
+ // Determine effective baseDir with priority: CLI > Import > Recipe > undefined
10238
+ const effectiveBaseDir = cliBaseDir || importBaseDir || recipeBaseDir;
10239
+ if (effectiveBaseDir) {
10240
+ // Use baseDir + relative path
10241
+ const expandedBaseDir = resolvePath(effectiveBaseDir);
10242
+ return path.resolve(expandedBaseDir, processedItemOut);
10243
+ }
10244
+ // Existing behavior: resolve relative to recipe file directory or current directory
10245
+ // For preset files (located in presets/ directory), resolve relative to project root instead
10246
+ let baseDirectory = '.';
10247
+ if (recipePath) {
10248
+ const packageRoot = findPackageRoot();
10249
+ const presetsDir = path.join(packageRoot, 'presets');
10250
+ // If the recipe is in the presets directory, use project root as base
10251
+ if (path.dirname(recipePath) === presetsDir) {
10252
+ baseDirectory = process.cwd();
10253
+ }
10254
+ else {
10255
+ // For regular recipe files, use recipe file directory as base
10256
+ baseDirectory = path.dirname(recipePath);
10257
+ }
10258
+ }
10259
+ return path.resolve(baseDirectory, processedItemOut);
10260
+ }
10165
10261
  function resolveRecipePath(recipePath) {
10166
10262
  // If recipe starts with ':', resolve to package preset
10167
10263
  if (recipePath.startsWith(':')) {
@@ -10260,11 +10356,26 @@ async function expandRecipeImports(items, currentPath, debugLogger, visited = ne
10260
10356
  throw new Error(`Invalid imported recipe file '${importPath}': 'recipe' array not found`);
10261
10357
  }
10262
10358
  debugLogger?.log(`Import contains ${importData.recipe.length} items`);
10359
+ // Check if this import item has an import-level baseDir
10360
+ const importLevelBaseDir = item.baseDir;
10361
+ if (importLevelBaseDir) {
10362
+ debugLogger?.log(`Import has baseDir: ${importLevelBaseDir}`);
10363
+ }
10263
10364
  // Recursively expand imports in the imported recipe
10264
10365
  debugLogger?.time(`Expanding nested imports in: ${resolvedImportPath}`);
10265
10366
  const expandedImported = await expandRecipeImports(importData.recipe, resolvedImportPath, debugLogger, newVisited, imported, depth + 1);
10266
10367
  debugLogger?.timeEnd(`Expanding nested imports in: ${resolvedImportPath}`);
10267
10368
  debugLogger?.log(`Nested expansion yielded ${expandedImported.length} items`);
10369
+ // If import has baseDir, tag all expanded items with it
10370
+ if (importLevelBaseDir) {
10371
+ expandedImported.forEach(expandedItem => {
10372
+ // Only set _importBaseDir if not already set (to preserve nested import baseDir priority)
10373
+ if (!expandedItem._importBaseDir) {
10374
+ expandedItem._importBaseDir = importLevelBaseDir;
10375
+ debugLogger?.log(`Tagged item '${expandedItem.title || expandedItem.out || 'untitled'}' with import baseDir: ${importLevelBaseDir}`);
10376
+ }
10377
+ });
10378
+ }
10268
10379
  // Add all expanded items from the import
10269
10380
  expanded.push(...expandedImported);
10270
10381
  }
@@ -10320,6 +10431,7 @@ function setupProgram() {
10320
10431
  .option('--title <title>', 'Title for the output')
10321
10432
  .option('--mode <mode>', 'Write mode: append, prepend, overwrite', 'overwrite')
10322
10433
  .option('--recipe <path>', 'Recipe file path or package preset (e.g., :typescript). Can be specified multiple times or comma-separated.', collectRecipeOptions, [])
10434
+ .option('--base-dir <path>', 'Base directory for output files (supports ~ and environment variable expansion)')
10323
10435
  .option('--vars <variables>', 'Template variables in key=value format (comma-separated)')
10324
10436
  .option('--env-file <path>', 'Path to .env file for template variables')
10325
10437
  .option('--debug', 'Enable debug logging')
@@ -10458,7 +10570,7 @@ async function processContentWithMode(outFile, newContent, mode, debugLogger) {
10458
10570
  return newContent;
10459
10571
  }
10460
10572
  try {
10461
- const existing = await fs.readFile(path.resolve(outFile), "utf-8");
10573
+ const existing = await fs.readFile(resolvePath(outFile), "utf-8");
10462
10574
  debugLogger?.log(`Existing file found with ${existing.length} characters`);
10463
10575
  if (mode === WriteMode.APPEND) {
10464
10576
  debugLogger?.log('Appending new content to existing content');
@@ -10577,7 +10689,7 @@ async function processSingle(options, debugLogger) {
10577
10689
  debugLogger.time('Content mode processing');
10578
10690
  const finalContent = await processContentWithMode(outFile, contentWithTitle, mode, debugLogger);
10579
10691
  debugLogger.timeEnd('Content mode processing');
10580
- const resolved = path.resolve(outFile);
10692
+ const resolved = resolvePath(outFile);
10581
10693
  const writer = new MarkdownWriter();
10582
10694
  debugLogger.log(`Writing to output file: ${resolved}`);
10583
10695
  // Build front matter
@@ -10740,15 +10852,20 @@ async function handleExtractCommand(options) {
10740
10852
  await validateRecipeFilesExist(options.recipe);
10741
10853
  if (options.recipe.length === 1) {
10742
10854
  // Single recipe - maintain backward compatibility
10743
- await processRecipe(options.recipe[0], extractOptions, new ContentTracker(), debugLogger);
10855
+ await processRecipe(options.recipe[0], extractOptions, new ContentTracker(), debugLogger, options.baseDir);
10744
10856
  }
10745
10857
  else {
10746
10858
  // Multiple recipes - process in order with auto-append
10747
- await processMultipleRecipes(options.recipe, extractOptions, debugLogger);
10859
+ await processMultipleRecipes(options.recipe, extractOptions, debugLogger, options.baseDir);
10748
10860
  }
10749
10861
  }
10750
10862
  else {
10751
10863
  debugLogger.log('Processing single extraction without recipe');
10864
+ // For single extraction, apply baseDir to outFile if provided
10865
+ if (options.baseDir) {
10866
+ extractOptions.outFile = resolveOutputPath(extractOptions.outFile, options.baseDir, undefined, undefined, undefined);
10867
+ debugLogger.log(`Applied CLI baseDir to output file: ${extractOptions.outFile}`);
10868
+ }
10752
10869
  await processSingle(extractOptions, debugLogger);
10753
10870
  }
10754
10871
  }
@@ -10765,13 +10882,13 @@ async function handleStatsCommand(options) {
10765
10882
  /**
10766
10883
  * Processes multiple recipe files in the specified order
10767
10884
  */
10768
- async function processMultipleRecipes(recipePaths, baseOptions, debugLogger) {
10885
+ async function processMultipleRecipes(recipePaths, baseOptions, debugLogger, cliBaseDir) {
10769
10886
  const contentTracker = new ContentTracker();
10770
10887
  debugLogger.log(`Processing ${recipePaths.length} recipes in order`);
10771
10888
  for (const recipePath of recipePaths) {
10772
10889
  try {
10773
10890
  debugLogger.log(`Starting processing of recipe: ${recipePath}`);
10774
- await processRecipe(recipePath, baseOptions, contentTracker, debugLogger);
10891
+ await processRecipe(recipePath, baseOptions, contentTracker, debugLogger, cliBaseDir);
10775
10892
  debugLogger.log(`Completed processing of recipe: ${recipePath}`);
10776
10893
  }
10777
10894
  catch (error) {
@@ -10784,10 +10901,91 @@ async function processMultipleRecipes(recipePaths, baseOptions, debugLogger) {
10784
10901
  }
10785
10902
  }
10786
10903
  }
10904
+ /**
10905
+ * Reads template files and extracts frontmatter for inheritance
10906
+ */
10907
+ async function loadTemplateFrontmatter(srcDir, types, languages, attrFilters, debugLogger) {
10908
+ debugLogger?.log('Loading template frontmatter for inheritance');
10909
+ const templateFiles = await loadAndFilterFiles(srcDir, types, languages, attrFilters, debugLogger);
10910
+ debugLogger?.log(`Found ${templateFiles.length} template files for frontmatter inheritance`);
10911
+ return templateFiles.map(file => file.attrs);
10912
+ }
10913
+ /**
10914
+ * Merges frontmatter values from multiple templates according to merge rules:
10915
+ * - Strings: concatenate with newlines
10916
+ * - Arrays: combine all elements
10917
+ * - Objects: later values overwrite
10918
+ */
10919
+ function mergeTemplateFrontmatterValues(templateFrontmatters, fieldName) {
10920
+ const values = templateFrontmatters
10921
+ .map(fm => fm[fieldName])
10922
+ .filter(val => val !== undefined && val !== null);
10923
+ if (values.length === 0) {
10924
+ return undefined;
10925
+ }
10926
+ if (values.length === 1) {
10927
+ return values[0];
10928
+ }
10929
+ // Check if all values are strings
10930
+ if (values.every(val => typeof val === 'string')) {
10931
+ return values.join('\n');
10932
+ }
10933
+ // Check if all values are arrays
10934
+ if (values.every(val => Array.isArray(val))) {
10935
+ return values.flat();
10936
+ }
10937
+ // For objects and mixed types, use the last value
10938
+ return values[values.length - 1];
10939
+ }
10940
+ /**
10941
+ * Processes @ syntax in frontmatter to inherit from template files
10942
+ */
10943
+ async function processFrontmatterInheritance(frontmatter, srcDir, types, languages, attrFilters, debugLogger) {
10944
+ if (!frontmatter || typeof frontmatter !== 'object') {
10945
+ return {};
10946
+ }
10947
+ const result = {};
10948
+ const inheritanceFields = [];
10949
+ // Separate inheritance fields (@ syntax) from regular fields
10950
+ for (const [key, value] of Object.entries(frontmatter)) {
10951
+ if (key.startsWith('@') && value === true) {
10952
+ inheritanceFields.push(key.slice(1)); // Remove @ prefix
10953
+ debugLogger?.log(`Found inheritance field: ${key} -> ${key.slice(1)}`);
10954
+ }
10955
+ else {
10956
+ result[key] = value;
10957
+ }
10958
+ }
10959
+ // If no inheritance fields, return as-is
10960
+ if (inheritanceFields.length === 0) {
10961
+ debugLogger?.log('No frontmatter inheritance fields found');
10962
+ return result;
10963
+ }
10964
+ debugLogger?.log(`Processing ${inheritanceFields.length} inheritance fields:`, inheritanceFields);
10965
+ // Load template frontmatters
10966
+ const templateFrontmatters = await loadTemplateFrontmatter(srcDir, types, languages, attrFilters, debugLogger);
10967
+ if (templateFrontmatters.length === 0) {
10968
+ debugLogger?.log('No template files found for inheritance');
10969
+ return result;
10970
+ }
10971
+ // Process each inheritance field
10972
+ for (const fieldName of inheritanceFields) {
10973
+ const mergedValue = mergeTemplateFrontmatterValues(templateFrontmatters, fieldName);
10974
+ if (mergedValue !== undefined) {
10975
+ result[fieldName] = mergedValue;
10976
+ debugLogger?.log(`Inherited field ${fieldName}:`, mergedValue);
10977
+ }
10978
+ else {
10979
+ debugLogger?.log(`No value found for inherited field ${fieldName} in templates`);
10980
+ }
10981
+ }
10982
+ debugLogger?.log('Frontmatter after inheritance processing:', Object.keys(result));
10983
+ return result;
10984
+ }
10787
10985
  /**
10788
10986
  * Processes a recipe file with multiple extract operations
10789
10987
  */
10790
- async function processRecipe(recipePath, baseOptions, contentTracker, debugLogger) {
10988
+ async function processRecipe(recipePath, baseOptions, contentTracker, debugLogger, cliBaseDir) {
10791
10989
  const resolvedPath = resolveRecipePath(recipePath);
10792
10990
  debugLogger?.log(`Processing recipe at path: ${resolvedPath}`);
10793
10991
  try {
@@ -10799,6 +10997,9 @@ async function processRecipe(recipePath, baseOptions, contentTracker, debugLogge
10799
10997
  throw new Error("Invalid recipe file: 'recipe' array not found");
10800
10998
  }
10801
10999
  debugLogger?.log(`Recipe contains ${data.recipe.length} items`);
11000
+ // Read recipe config for baseDir
11001
+ const recipeBaseDir = data.config?.baseDir;
11002
+ debugLogger?.log('Recipe config:', { baseDir: recipeBaseDir });
10802
11003
  // Expand any imports in the recipe
10803
11004
  debugLogger?.time('Recipe import expansion');
10804
11005
  const expandedRecipe = await expandRecipeImports(data.recipe, resolvedPath, debugLogger);
@@ -10814,8 +11015,13 @@ async function processRecipe(recipePath, baseOptions, contentTracker, debugLogge
10814
11015
  language: item.language,
10815
11016
  mode: item.mode
10816
11017
  });
10817
- const outputFile = item.out || baseOptions.outFile;
10818
- const resolvedOutputFile = path.resolve(outputFile);
11018
+ const itemOut = item.out || baseOptions.outFile;
11019
+ const outputFile = resolveOutputPath(itemOut, cliBaseDir, item._importBaseDir, recipeBaseDir, resolvedPath);
11020
+ debugLogger?.log(`Resolved output path: ${itemOut} -> ${outputFile}`, {
11021
+ cliBaseDir,
11022
+ importBaseDir: item._importBaseDir,
11023
+ recipeBaseDir
11024
+ });
10819
11025
  // Generate the content that would be written to check for duplicates
10820
11026
  const { srcDir, types, languages, attrFilters, title, attr, vars, envFile } = baseOptions;
10821
11027
  const itemTypes = item.type ? parseCommaSeparated(item.type) : types;
@@ -10867,7 +11073,7 @@ async function processRecipe(recipePath, baseOptions, contentTracker, debugLogge
10867
11073
  effectiveMode = parseWriteMode(item.mode);
10868
11074
  debugLogger?.log(`Using explicit mode from recipe item: ${effectiveMode}`);
10869
11075
  }
10870
- else if (localTracker.hasFile(resolvedOutputFile)) {
11076
+ else if (localTracker.hasFile(outputFile)) {
10871
11077
  // Auto-append for duplicate output files when no explicit mode
10872
11078
  effectiveMode = WriteMode.APPEND;
10873
11079
  debugLogger?.log(`Auto-appending due to existing output file: ${effectiveMode}`);
@@ -10875,6 +11081,10 @@ async function processRecipe(recipePath, baseOptions, contentTracker, debugLogge
10875
11081
  else {
10876
11082
  debugLogger?.log(`Using base mode: ${effectiveMode}`);
10877
11083
  }
11084
+ // Process frontmatter inheritance (@ syntax)
11085
+ debugLogger?.time(`Frontmatter inheritance processing for item ${index + 1}`);
11086
+ const processedFrontmatter = await processFrontmatterInheritance(item.frontmatter, baseOptions.srcDir, itemTypes, itemLanguages, combinedAttrFilters, debugLogger);
11087
+ debugLogger?.timeEnd(`Frontmatter inheritance processing for item ${index + 1}`);
10878
11088
  const options = {
10879
11089
  srcDir: baseOptions.srcDir,
10880
11090
  outFile: outputFile,
@@ -10883,7 +11093,7 @@ async function processRecipe(recipePath, baseOptions, contentTracker, debugLogge
10883
11093
  attrFilters: combinedAttrFilters,
10884
11094
  title: itemTitle,
10885
11095
  mode: effectiveMode,
10886
- attr: item.frontmatter,
11096
+ attr: processedFrontmatter,
10887
11097
  debug: baseOptions.debug,
10888
11098
  vars: itemVars,
10889
11099
  envFile: envFile,
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Path expansion utility that supports tilde (~) and environment variable expansion
3
+ *
4
+ * Features:
5
+ * - Tilde expansion: ~/path -> /Users/username/path (Unix-like systems only)
6
+ * - Environment variable expansion: $HOME/path -> /Users/username/path
7
+ * - Environment variable expansion: ${VAR}/path -> /value/path
8
+ * - Complex combinations: ~/projects/${PROJECT_NAME}/file.md
9
+ *
10
+ * @param inputPath - Path to expand and resolve
11
+ * @returns Expanded and resolved absolute path
12
+ * @throws Error if required environment variables are missing
13
+ */
14
+ export declare function resolvePath(inputPath: string): string;
15
+ /**
16
+ * Safely resolve a path with environment variable expansion, returning the original path
17
+ * if expansion fails (useful for optional environment variables)
18
+ *
19
+ * @param inputPath - Path to expand and resolve
20
+ * @param fallbackToOriginal - If true, return path.resolve(inputPath) when expansion fails
21
+ * @returns Expanded and resolved absolute path, or resolved original path on failure
22
+ */
23
+ export declare function resolvePathSafe(inputPath: string, fallbackToOriginal?: boolean): string;
24
+ //# sourceMappingURL=pathExpansion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pathExpansion.d.ts","sourceRoot":"","sources":["../../src/utils/pathExpansion.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAuCrD;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,kBAAkB,GAAE,OAAc,GAAG,MAAM,CAS7F"}
@@ -0,0 +1,67 @@
1
+ import { homedir } from "os";
2
+ import path from "path";
3
+ /**
4
+ * Path expansion utility that supports tilde (~) and environment variable expansion
5
+ *
6
+ * Features:
7
+ * - Tilde expansion: ~/path -> /Users/username/path (Unix-like systems only)
8
+ * - Environment variable expansion: $HOME/path -> /Users/username/path
9
+ * - Environment variable expansion: ${VAR}/path -> /value/path
10
+ * - Complex combinations: ~/projects/${PROJECT_NAME}/file.md
11
+ *
12
+ * @param inputPath - Path to expand and resolve
13
+ * @returns Expanded and resolved absolute path
14
+ * @throws Error if required environment variables are missing
15
+ */
16
+ export function resolvePath(inputPath) {
17
+ if (!inputPath || typeof inputPath !== 'string') {
18
+ throw new Error('Path must be a non-empty string');
19
+ }
20
+ let expandedPath = inputPath;
21
+ // 1. Handle tilde expansion first (Unix-like systems only)
22
+ if (expandedPath.startsWith('~/')) {
23
+ // Check if we're on Windows
24
+ if (process.platform === 'win32') {
25
+ throw new Error('Tilde (~) expansion is not supported on Windows. Use %USERPROFILE% or environment variables instead.');
26
+ }
27
+ const homeDir = homedir();
28
+ expandedPath = path.join(homeDir, expandedPath.slice(2));
29
+ }
30
+ else if (expandedPath === '~') {
31
+ if (process.platform === 'win32') {
32
+ throw new Error('Tilde (~) expansion is not supported on Windows. Use %USERPROFILE% or environment variables instead.');
33
+ }
34
+ expandedPath = homedir();
35
+ }
36
+ // 2. Handle environment variable expansion
37
+ // Support both $VAR and ${VAR} syntax
38
+ expandedPath = expandedPath.replace(/\$\{([^}]+)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g, (match, bracedVar, simpleVar) => {
39
+ const varName = bracedVar || simpleVar;
40
+ const envValue = process.env[varName];
41
+ if (envValue === undefined) {
42
+ throw new Error(`Environment variable '${varName}' is not defined`);
43
+ }
44
+ return envValue;
45
+ });
46
+ // 3. Resolve to absolute path
47
+ return path.resolve(expandedPath);
48
+ }
49
+ /**
50
+ * Safely resolve a path with environment variable expansion, returning the original path
51
+ * if expansion fails (useful for optional environment variables)
52
+ *
53
+ * @param inputPath - Path to expand and resolve
54
+ * @param fallbackToOriginal - If true, return path.resolve(inputPath) when expansion fails
55
+ * @returns Expanded and resolved absolute path, or resolved original path on failure
56
+ */
57
+ export function resolvePathSafe(inputPath, fallbackToOriginal = true) {
58
+ try {
59
+ return resolvePath(inputPath);
60
+ }
61
+ catch (error) {
62
+ if (fallbackToOriginal) {
63
+ return path.resolve(inputPath);
64
+ }
65
+ throw error;
66
+ }
67
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aramassa/ai-rules",
3
- "version": "0.1.1-npmjs.20250910072942",
3
+ "version": "0.1.2",
4
4
  "description": "This repository collects guidelines and instructions for developing AI agents. It contains documents covering communication rules, coding standards, testing strategies, and general operational practices.",
5
5
  "workspaces": [
6
6
  "packages/extract",
@@ -1,25 +1,11 @@
1
+ config:
2
+ baseDir: .github/chatmodes
1
3
  recipe:
2
4
  - title: Instruction Improve Chatmode
3
5
  frontmatter:
4
- description: |
5
- Chatmode for improving the project instructions through retrospective analysis.
6
- Provides structured approach to analyzing past tasks and converting insights into GitHub issues.
7
- Includes comprehensive GitHub integration tools for issue and pull request management.
8
- tools:
9
- [
10
- "changes",
11
- "create_issue",
12
- "list_issues",
13
- "get_issue",
14
- "get_pull_request",
15
- "create_pull_request",
16
- "list_pull_requests",
17
- "get_pull_request_comments",
18
- "get_pull_request_reviews",
19
- "search_issues",
20
- "search_pull_requests",
21
- ]
22
- out: ./.github/chatmodes/Instruction Improve.chatmode.md
6
+ "@description": true
7
+ "@tools": true
8
+ out: Instruction Improve.chatmode.md
23
9
  type: chatmode
24
10
  filters:
25
11
  chatmode: instruction-improve
@@ -27,38 +13,9 @@ recipe:
27
13
  INSTRUCTION_GITHUB_REPO: https://github.com/Aramassa/ai-rules
28
14
  - title: Planning Chatmode
29
15
  frontmatter:
30
- description: |
31
- Chatmode for supporting todo_plans creation with structured planning process.
32
- Provides phase-based guidance for requirement clarification, technical investigation, and implementation planning.
33
- tools:
34
- [
35
- "changes",
36
- "searchResults",
37
- "editFiles",
38
- "search",
39
- "add_issue_comment",
40
- "add_sub_issue",
41
- "assign_copilot_to_issue",
42
- "create_issue",
43
- "create_pull_request_with_copilot",
44
- "get_code_scanning_alert",
45
- "get_discussion",
46
- "get_discussion_comments",
47
- "get_issue_comments",
48
- "get_pull_request",
49
- "get_pull_request_diff",
50
- "get_pull_request_files",
51
- "list_commits",
52
- "list_issues",
53
- "list_pull_requests",
54
- "search_code",
55
- "search_issues",
56
- "search_pull_requests",
57
- "search_repositories",
58
- "update_issue",
59
- "update_pull_request",
60
- ]
61
- out: ./.github/chatmodes/Planning.chatmode.md
16
+ "@description": true
17
+ "@tools": true
18
+ out: Planning.chatmode.md
62
19
  type: chatmode
63
20
  filters:
64
21
  chatmode: planning