@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.
- package/artifact/chatmodes/Instruction Improve.md +17 -3
- package/artifact/chatmodes/Planning.md +233 -20
- package/artifact/instructions/planning.md +188 -7
- package/artifact/instructions/rules/test/environment-independent-paths.md +206 -0
- package/dist/cli.js +225 -15
- package/dist/utils/pathExpansion.d.ts +24 -0
- package/dist/utils/pathExpansion.d.ts.map +1 -0
- package/dist/utils/pathExpansion.js +67 -0
- package/package.json +1 -1
- package/presets/chatmodes.yaml +8 -51
|
@@ -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
|
|
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:
|
|
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: "
|
|
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:
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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(
|
|
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 =
|
|
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
|
|
10818
|
-
const
|
|
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(
|
|
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:
|
|
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.
|
|
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",
|
package/presets/chatmodes.yaml
CHANGED
|
@@ -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
|
-
|
|
6
|
-
|
|
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
|
-
|
|
32
|
-
|
|
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
|