dflow-sdd-ddd 0.6.0 → 0.7.0
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/CHANGELOG.md +39 -0
- package/README.en.md +30 -0
- package/README.md +14 -0
- package/bin/dflow.js +7 -3
- package/docs/npm-publish-checklist.md +8 -0
- package/docs/using-with-claude-code.en.md +103 -4
- package/docs/using-with-claude-code.md +81 -3
- package/docs/using-with-codex.en.md +14 -0
- package/docs/using-with-codex.md +10 -0
- package/docs/using-with-github-copilot.en.md +36 -0
- package/docs/using-with-github-copilot.md +26 -0
- package/lib/init.js +232 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,45 @@
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## 0.7.0 — 2026-05-22 — Opt-in Claude skill adapter、自動清理舊 command adapter、發布前 lifecycle 機檢
|
|
10
|
+
|
|
11
|
+
**Proposals**:PROPOSAL-037(generated adapter commit 政策與升級流程)、PROPOSAL-038(選配 skill adapter)、PROPOSAL-035(lifecycle drift forcing-functions)
|
|
12
|
+
|
|
13
|
+
**新功能**:
|
|
14
|
+
|
|
15
|
+
- **`dflow configure-agents --skills`**(PROPOSAL-038):在已初始化的專案投影一個
|
|
16
|
+
edition-neutral 的「thin」skill 到 `.claude/skills/dflow/SKILL.md`,指向專案內既有
|
|
17
|
+
guide(不打包 SKILL.md 模板)。auto-trigger 描述刻意收窄,採 suggest-and-wait
|
|
18
|
+
契約;wrapper 帶 `<!-- dflow-generated: skill-adapter -->` 供覆寫保護。`--skills`
|
|
19
|
+
在未鎖定 Claude 時 warn 並 no-op。Claude skill 與 command adapter(`dflow:<id>`)
|
|
20
|
+
名稱不衝突,可共存(已於 real Claude Code 實測)。Codex 維持 AGENTS.md 文字觸發,
|
|
21
|
+
不提供 user-level skill。
|
|
22
|
+
|
|
23
|
+
**行為改善**:
|
|
24
|
+
|
|
25
|
+
- **`init` 自動移除前一版 stale command adapter**(PROPOSAL-037 Segment B):透過既有
|
|
26
|
+
plan / preview / confirm pipeline,依明確的 `LEGACY_COMMAND_ADAPTERS` 表(目前僅
|
|
27
|
+
v0.5.0)與 CRLF-normalized 精確指紋比對,apply 時再次 re-check;對使用者改過或
|
|
28
|
+
非 Dflow 產生的檔案 warn-but-keep,絕不使用 glob。**這把 0.6.0 需手動刪除舊
|
|
29
|
+
`.claude/commands/dflow/dflow-*.md` 的遷移步驟自動化了**。新生成的 wrapper 帶
|
|
30
|
+
`<!-- dflow-generated: command-adapter -->`。
|
|
31
|
+
|
|
32
|
+
**文件**:
|
|
33
|
+
|
|
34
|
+
- **generated adapter 的版控政策**(PROPOSAL-037 Segment A):README(zh + en)與各工具
|
|
35
|
+
guide 補上建議預設(gitignore + 重新生成)與「追蹤 adapter」的替代做法、條件式
|
|
36
|
+
`.gitignore` 片段與 glob 注意事項、`git rm --cached` 指引,並區分 adapter-refresh
|
|
37
|
+
與 canonical-guide-migration、加入 CLI 版本 pinning 說明。
|
|
38
|
+
|
|
39
|
+
**維護者工具**(不影響套件使用者):
|
|
40
|
+
|
|
41
|
+
- **lifecycle drift 機檢**(PROPOSAL-035):新增 `scripts/check-lifecycle.mjs`(dev-only,
|
|
42
|
+
不隨套件發布),硬斷言「`proposals/` 內無 terminal-status proposal」與「`archive/proposals/`
|
|
43
|
+
內皆為 terminal status」,並對引用已歸檔 proposal 的 active handoff 發出 warning;
|
|
44
|
+
接入 CI 與 npm 發布前 checklist。
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
9
48
|
## 0.6.0 — 2026-05-21 — Command-adapter 命名修正
|
|
10
49
|
|
|
11
50
|
**Proposals**:PROPOSAL-036(修正 command-adapter 命名 + 各工具叫用現實對齊)
|
package/README.en.md
CHANGED
|
@@ -237,6 +237,36 @@ You can run `dflow configure-agents` later to add more tool shims as the team
|
|
|
237
237
|
adopts additional AI coding agents. If you need Claude / Copilot tool-native
|
|
238
238
|
command entries, use `dflow configure-agents --command-adapters`.
|
|
239
239
|
|
|
240
|
+
### Version-Control Policy for Generated Artifacts (recommended default)
|
|
241
|
+
|
|
242
|
+
The command / prompt wrappers produced by `dflow configure-agents
|
|
243
|
+
--command-adapters` are **generated artifacts** projected from the canonical
|
|
244
|
+
guide. Dflow's **recommended default** is to treat them as regenerable output:
|
|
245
|
+
version-control the source, not the generated artifacts.
|
|
246
|
+
|
|
247
|
+
| File | Role | Recommended default |
|
|
248
|
+
|---|---|---|
|
|
249
|
+
| `dflow/` (canonical guide, specs, merge snippet) | source | **version-control** |
|
|
250
|
+
| Thin shims (`CLAUDE.md` / `AGENTS.md` / `.github/copilot-instructions.md`) | source | **version-control** |
|
|
251
|
+
| `.claude/commands/dflow/`, `.github/prompts/dflow-*.prompt.md` | generated | **recommended: do not version-control (gitignore)**; regenerate after clone with `configure-agents --command-adapters` |
|
|
252
|
+
|
|
253
|
+
This is a **recommendation**, not the only valid policy. If your team wants a
|
|
254
|
+
native `/` menu immediately after clone, or your CI / dev environment does not
|
|
255
|
+
install npm, **version-controlling the adapters** is a reasonable choice — the
|
|
256
|
+
trade-off is that when an upgrade renames commands you must re-project and
|
|
257
|
+
commit the removal of the old files. The key rule: **use one consistent policy
|
|
258
|
+
across all tools in a project**, rather than ignoring adapters for one tool and
|
|
259
|
+
tracking them for another.
|
|
260
|
+
|
|
261
|
+
After upgrading Dflow, re-running `dflow configure-agents --command-adapters`
|
|
262
|
+
re-projects adapters from the **new command registry**, but it does **not**
|
|
263
|
+
overwrite an existing `dflow/specs/shared/AI-AGENT-GUIDE.md` (an existing
|
|
264
|
+
canonical guide is kept). "Re-projecting adapters" and "migrating the canonical
|
|
265
|
+
guide" are two different things; re-project with the **same dflow CLI version**
|
|
266
|
+
to avoid a registry / guide version mismatch. Per-tool `.gitignore` snippets,
|
|
267
|
+
glob side effects, the `git rm --cached` switch-over step, and upgrade details
|
|
268
|
+
are covered in the per-tool guides.
|
|
269
|
+
|
|
240
270
|
For tool-specific walk-throughs of what `init` writes and how Dflow's
|
|
241
271
|
workflow commands appear in a given AI tool, see the per-tool guides under
|
|
242
272
|
`docs/`:
|
package/README.md
CHANGED
|
@@ -204,6 +204,20 @@ Dflow 也會為你的 AI 程式設計助理建立或提供可合併的專案指
|
|
|
204
204
|
|
|
205
205
|
之後團隊採用新 AI 程式設計助理時,可隨時跑 `dflow configure-agents` 新增 shim;若需要 Claude / Copilot 的工具原生命令入口,改用 `dflow configure-agents --command-adapters`。
|
|
206
206
|
|
|
207
|
+
### 產生物的版控政策(建議預設)
|
|
208
|
+
|
|
209
|
+
`dflow configure-agents --command-adapters` 產生的命令 / prompt wrapper 是從 canonical guide 投影出來的**衍生物(generated artifact)**。Dflow 的**建議預設**是把它們當成可重生成的產物:版控 source、不版控衍生物。
|
|
210
|
+
|
|
211
|
+
| 檔案 | 角色 | 建議預設 |
|
|
212
|
+
|---|---|---|
|
|
213
|
+
| `dflow/`(canonical guide、規格、merge snippet) | source | **版控** |
|
|
214
|
+
| 薄 shim(`CLAUDE.md` / `AGENTS.md` / `.github/copilot-instructions.md`) | source | **版控** |
|
|
215
|
+
| `.claude/commands/dflow/`、`.github/prompts/dflow-*.prompt.md` | 衍生物 | **建議不版控(gitignore)**,clone 後重跑 `configure-agents --command-adapters` 重生成 |
|
|
216
|
+
|
|
217
|
+
這是**建議**,不是唯一正解。若團隊希望 clone 後立即有原生 `/` 選單、或 CI / 開發環境不裝 npm,**版控 adapter** 也是合理選擇——代價是升級若改了命名,要重投影並 commit 刪除舊檔。關鍵原則:**同一專案對所有工具採一致策略**,別像常見的踩雷一樣一邊 ignore、一邊版控。
|
|
218
|
+
|
|
219
|
+
升級 dflow 後重跑 `dflow configure-agents --command-adapters`,adapter 會用**新版的 command registry** 重投影;但**不會**覆寫已存在的 `dflow/specs/shared/AI-AGENT-GUIDE.md`(canonical guide 已存在則保留)。「重投影 adapter」與「升級 canonical guide」是兩件事;升級時請用**相同的 dflow CLI 版本**重投影,避免 registry 與 guide 版本錯位。各工具的 `.gitignore` 片段、glob 副作用、`git rm --cached` 切換步驟與升級細節見 per-tool 指南。
|
|
220
|
+
|
|
207
221
|
特定工具的 init 寫入內容與 Dflow workflow 命令呈現方式,見 `docs/` 內的 per-tool 指南:
|
|
208
222
|
|
|
209
223
|
- [在 Claude Code 中使用 Dflow](docs/using-with-claude-code.md)
|
package/bin/dflow.js
CHANGED
|
@@ -29,7 +29,7 @@ optional starter files, and AI coding agents before showing a full file preview.
|
|
|
29
29
|
|
|
30
30
|
function printConfigureAgentsHelp() {
|
|
31
31
|
process.stdout.write(`Usage:
|
|
32
|
-
dflow configure-agents [--command-adapters]
|
|
32
|
+
dflow configure-agents [--command-adapters] [--skills]
|
|
33
33
|
|
|
34
34
|
Adds AI agent instruction files to an existing Dflow project.
|
|
35
35
|
The command can create AGENTS.md, CLAUDE.md, and
|
|
@@ -38,6 +38,7 @@ dflow/specs/shared/AI-AGENT-GUIDE.md file.
|
|
|
38
38
|
|
|
39
39
|
Options:
|
|
40
40
|
--command-adapters Also generate tool-native thin wrappers for supported tools.
|
|
41
|
+
--skills Also generate a supported tool's skill adapter (currently Claude Code project skill, restores natural-language auto-trigger).
|
|
41
42
|
`);
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -95,7 +96,9 @@ async function main() {
|
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
const configureOptions = args.slice(1);
|
|
98
|
-
const unsupportedConfigureOptions = configureOptions.filter(
|
|
99
|
+
const unsupportedConfigureOptions = configureOptions.filter(
|
|
100
|
+
(arg) => arg !== '--command-adapters' && arg !== '--skills'
|
|
101
|
+
);
|
|
99
102
|
if (unsupportedConfigureOptions.length > 0) {
|
|
100
103
|
process.stderr.write(`Unsupported configure-agents option: ${unsupportedConfigureOptions.join(' ')}\n`);
|
|
101
104
|
return 1;
|
|
@@ -106,7 +109,8 @@ async function main() {
|
|
|
106
109
|
stdin: process.stdin,
|
|
107
110
|
stdout: process.stdout,
|
|
108
111
|
stderr: process.stderr,
|
|
109
|
-
commandAdapters: configureOptions.includes('--command-adapters')
|
|
112
|
+
commandAdapters: configureOptions.includes('--command-adapters'),
|
|
113
|
+
skills: configureOptions.includes('--skills')
|
|
110
114
|
});
|
|
111
115
|
}
|
|
112
116
|
|
|
@@ -13,6 +13,14 @@ Replace `<version>` with the version being published, for example `0.1.2`.
|
|
|
13
13
|
- [ ] Confirm `README.md` installation instructions match the release.
|
|
14
14
|
- [ ] Confirm Greenfield and Brownfield common flow changes are synchronized.
|
|
15
15
|
- [ ] Confirm generated templates match skill source where applicable.
|
|
16
|
+
- [ ] Run the lifecycle check in the development repo and confirm it is green,
|
|
17
|
+
so every proposal this release covers is terminal (`implemented` /
|
|
18
|
+
`rejected` / `superseded`) and already archived:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
node scripts/check-lifecycle.mjs
|
|
22
|
+
```
|
|
23
|
+
|
|
16
24
|
- [ ] Run:
|
|
17
25
|
|
|
18
26
|
```bash
|
|
@@ -156,10 +156,109 @@ These wrappers use Claude Code's directory namespace names, for example
|
|
|
156
156
|
`/dflow:new-feature`. Their body only points to the canonical
|
|
157
157
|
`/dflow:new-feature` workflow and `dflow/specs/shared/AI-AGENT-GUIDE.md`; it
|
|
158
158
|
does not copy workflow steps. Projects upgraded from Dflow 0.5.0 may still
|
|
159
|
-
have old `.claude/commands/dflow/dflow-*.md` files
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
159
|
+
have old `.claude/commands/dflow/dflow-*.md` files, which make Claude Code show
|
|
160
|
+
both the old `/dflow:dflow-<id>` names and the new `/dflow:<id>` names. When you
|
|
161
|
+
re-run `dflow configure-agents --command-adapters`, Dflow **automatically
|
|
162
|
+
detects and removes** these 0.5.0-generated stale wrappers: the files to remove
|
|
163
|
+
are listed in the confirmation preview (marked `remove`) and deleted only after
|
|
164
|
+
you confirm. Dflow removes only files whose content **exactly matches** the
|
|
165
|
+
0.5.0 generated output; if you have edited the file, or it is one you placed in
|
|
166
|
+
the same namespace yourself, Dflow leaves it in place and prints a warning so
|
|
167
|
+
you can review it manually.
|
|
168
|
+
|
|
169
|
+
### Version Control and Upgrades for Generated Adapters
|
|
170
|
+
|
|
171
|
+
`.claude/commands/dflow/<id>.md` is a **generated artifact** projected from the
|
|
172
|
+
canonical guide. Dflow's **recommended default** is to not version-control it
|
|
173
|
+
and regenerate it after clone with `dflow configure-agents --command-adapters`;
|
|
174
|
+
teams that want a native command menu immediately after clone may instead
|
|
175
|
+
**version-control** it. Use one consistent policy across all tools in a project
|
|
176
|
+
(see the policy overview and ignore-vs-track trade-off in
|
|
177
|
+
[README "Files Created by Init"](../README.en.md#files-created-by-init)).
|
|
178
|
+
|
|
179
|
+
When using the gitignore default, add this to the project `.gitignore` (**only
|
|
180
|
+
if you reserve the `.claude/commands/dflow/` namespace for Dflow**):
|
|
181
|
+
|
|
182
|
+
```gitignore
|
|
183
|
+
.claude/commands/dflow/
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Note: this rule also ignores any custom commands you place in the same
|
|
187
|
+
directory. If the directory is **already version-controlled**, adding the
|
|
188
|
+
ignore rule does not remove it from version control automatically; first run:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
git rm --cached -r .claude/commands/dflow/
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
(`--cached` removes it from version control while keeping the working-tree
|
|
195
|
+
files.)
|
|
196
|
+
|
|
197
|
+
After upgrading Dflow, re-running `dflow configure-agents --command-adapters`
|
|
198
|
+
re-projects adapters from the **new registry**, but an existing
|
|
199
|
+
`dflow/specs/shared/AI-AGENT-GUIDE.md` is **not** overwritten — "re-projecting
|
|
200
|
+
adapters" is not the same as "migrating the canonical guide." Re-project with
|
|
201
|
+
the **same dflow CLI version** to avoid a registry / guide version mismatch.
|
|
202
|
+
|
|
203
|
+
### Optional Skill Adapter (Restore Natural-Language Auto-Trigger)
|
|
204
|
+
|
|
205
|
+
Command adapters give you a `/` menu entry, but they **do not auto-trigger** —
|
|
206
|
+
you have to invoke the command yourself. If you want to restore the "say 'I want
|
|
207
|
+
to add a feature' and it shows up automatically" experience, run this in an
|
|
208
|
+
initialized project:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
dflow configure-agents --skills
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
After you select Claude Code, Dflow generates a thin skill:
|
|
215
|
+
|
|
216
|
+
- `.claude/skills/dflow/SKILL.md`
|
|
217
|
+
|
|
218
|
+
This skill does not copy workflow steps; its body only points to the canonical
|
|
219
|
+
`dflow/specs/shared/AI-AGENT-GUIDE.md`, which carries the real workflow content.
|
|
220
|
+
Its behavior:
|
|
221
|
+
|
|
222
|
+
- **Auto-triggers on** feature / bug-fix workflows, product/domain behavior
|
|
223
|
+
changes, new requirements, and spec-impacting architecture / domain-model
|
|
224
|
+
decisions.
|
|
225
|
+
- **Does not trigger on** pure refactors, infrastructure chores, formatting, or
|
|
226
|
+
general code questions.
|
|
227
|
+
- When engaged by natural language it **does not enter a workflow directly**: it
|
|
228
|
+
judges the intent, **suggests the matching `/dflow:` command, and waits for
|
|
229
|
+
your confirmation** before proceeding.
|
|
230
|
+
|
|
231
|
+
**The four combinations** (command adapters and the skill are each independently
|
|
232
|
+
opt-in):
|
|
233
|
+
|
|
234
|
+
| Installed | Entry behavior |
|
|
235
|
+
|---|---|
|
|
236
|
+
| Neither | Root shim only (CLAUDE.md points to the guide); no `/` menu, no auto-trigger |
|
|
237
|
+
| Command adapters only | `/dflow:*` appears in the `/` menu; no natural-language auto-trigger |
|
|
238
|
+
| Skill only | Natural-language auto-trigger (suggest-and-wait); no `/` menu |
|
|
239
|
+
| Both | `/` menu + natural-language safety net **may coexist** |
|
|
240
|
+
|
|
241
|
+
**Both may coexist with no mutex needed** (validated in a real Claude Code
|
|
242
|
+
environment): the skill name `dflow` does not collide with the command adapters'
|
|
243
|
+
`dflow:<id>` names, explicit commands load their own adapter precisely with no
|
|
244
|
+
double-fire; the skill acts as a natural-language safety net while command
|
|
245
|
+
adapters provide the `/` menu.
|
|
246
|
+
|
|
247
|
+
After installing, verify with `/skills` or by asking "What skills are
|
|
248
|
+
available?". Note: adding a new top-level skills directory may require
|
|
249
|
+
**restarting Claude Code** before it is watched. Also, a personal / enterprise
|
|
250
|
+
skill at `~/.claude/skills/dflow` can **override** the project skill (per the
|
|
251
|
+
Claude docs); use `/skills` to check which one is active.
|
|
252
|
+
|
|
253
|
+
**Version-control policy**: `.claude/skills/dflow/SKILL.md` is a **generated
|
|
254
|
+
artifact**, just like the command adapters, and follows the same default — do
|
|
255
|
+
not version-control it and regenerate it after clone with
|
|
256
|
+
`dflow configure-agents --skills` (`.claude/skills/dflow/` is already in the
|
|
257
|
+
recommended gitignore set); clone-ready teams may version-control it instead.
|
|
258
|
+
Re-running `--skills` is idempotent: an existing marker-stamped skill is
|
|
259
|
+
rewritten cleanly. If `.claude/skills/dflow/SKILL.md` is **not** Dflow-generated
|
|
260
|
+
(no `<!-- dflow-generated: skill-adapter -->` marker), Dflow leaves it unchanged
|
|
261
|
+
and prints a warning asking you to remove or rename it.
|
|
163
262
|
|
|
164
263
|
## Differences vs Other AI Tools
|
|
165
264
|
|
|
@@ -143,9 +143,87 @@ dflow configure-agents --command-adapters
|
|
|
143
143
|
`/dflow:new-feature`。Wrapper 內容只指向 canonical `/dflow:new-feature`
|
|
144
144
|
workflow 與 `dflow/specs/shared/AI-AGENT-GUIDE.md`,不複製 workflow 步驟。
|
|
145
145
|
從 Dflow 0.5.0 升級的專案可能仍保留舊檔
|
|
146
|
-
`.claude/commands/dflow/dflow-*.md
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
`.claude/commands/dflow/dflow-*.md`,會讓 Claude Code 同時顯示舊的
|
|
147
|
+
`/dflow:dflow-<id>` 與新的 `/dflow:<id>`。重跑
|
|
148
|
+
`dflow configure-agents --command-adapters` 時,Dflow 會**自動偵測並清除**這些
|
|
149
|
+
0.5.0 產生的 stale wrapper:待刪檔會列在確認 preview 中(標為 `remove`),由你
|
|
150
|
+
確認後才刪除。Dflow 只會刪除**內容與 0.5.0 產生物完全相符**的檔;若該檔被你改過、
|
|
151
|
+
或是你自己放在同 namespace 的檔,Dflow 不會刪除,只會印出 warning 提示你自行確認。
|
|
152
|
+
|
|
153
|
+
### 產生物的版控政策與升級
|
|
154
|
+
|
|
155
|
+
`.claude/commands/dflow/<id>.md` 是從 canonical guide 投影出來的**衍生物**。Dflow 的
|
|
156
|
+
**建議預設**是不版控、由 clone 後重跑 `dflow configure-agents --command-adapters` 重生成;
|
|
157
|
+
團隊若想 clone 後立即有原生命令選單,也可改為**版控**。重點是同一專案對所有工具採一致策略
|
|
158
|
+
(政策總覽與 ignore-vs-track 取捨見 [README「Init 產生的檔案」](../README.md#init-產生的檔案))。
|
|
159
|
+
|
|
160
|
+
採 gitignore 預設時,在專案 `.gitignore` 加入(**僅在你保留 `.claude/commands/dflow/`
|
|
161
|
+
namespace 給 Dflow 時**):
|
|
162
|
+
|
|
163
|
+
```gitignore
|
|
164
|
+
.claude/commands/dflow/
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
注意:此規則會一併 ignore 你放在同一目錄下的自訂 command。若該目錄**已被版控**,新增 ignore
|
|
168
|
+
規則不會自動把它移出版控,需先:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
git rm --cached -r .claude/commands/dflow/
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
(`--cached` 只移出版控、保留工作目錄檔案。)
|
|
175
|
+
|
|
176
|
+
升級 dflow 後重跑 `dflow configure-agents --command-adapters`,adapter 會用**新版 registry**
|
|
177
|
+
重投影;但既有 `dflow/specs/shared/AI-AGENT-GUIDE.md` **不會**被覆寫——「重投影 adapter」不等於
|
|
178
|
+
「升級 canonical guide」。請以**相同的 dflow CLI 版本**重投影,避免 registry 與 guide 版本錯位。
|
|
179
|
+
|
|
180
|
+
### 選配 Skill Adapter(找回自然語言自動觸發)
|
|
181
|
+
|
|
182
|
+
Command adapter 提供 `/` 選單入口,但**不會自動觸發**——你得主動打命令。若想找回
|
|
183
|
+
「講『我要加一個功能』就自動現身」的體驗,可在已初始化的專案中執行:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
dflow configure-agents --skills
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
選擇 Claude Code 後,Dflow 會產生一份薄 skill:
|
|
190
|
+
|
|
191
|
+
- `.claude/skills/dflow/SKILL.md`
|
|
192
|
+
|
|
193
|
+
這份 skill 不複製 workflow 步驟,body 只指向 canonical
|
|
194
|
+
`dflow/specs/shared/AI-AGENT-GUIDE.md`,由 guide 承載真正的 workflow 內容。
|
|
195
|
+
它的行為:
|
|
196
|
+
|
|
197
|
+
- **自動觸發於** feature / bug-fix workflow、product/domain behavior 變更、新需求、
|
|
198
|
+
spec-impacting 的 architecture / domain-model 決策。
|
|
199
|
+
- **不會觸發於** 純 refactor、infra chore、formatting、一般 code 問題。
|
|
200
|
+
- 由自然語言觸發時,**不會直接進 workflow**:它會判斷意圖、**建議對應的 `/dflow:`
|
|
201
|
+
命令並等待你確認**,再進入流程。
|
|
202
|
+
|
|
203
|
+
**四種組合**(command adapter 與 skill 各自獨立 opt-in):
|
|
204
|
+
|
|
205
|
+
| 安裝組合 | 入口行為 |
|
|
206
|
+
|---|---|
|
|
207
|
+
| 都不裝 | 只有根目錄 shim(CLAUDE.md 指向 guide);無 `/` 選單、無自動觸發 |
|
|
208
|
+
| 只裝 command adapters | `/dflow:*` 出現在 `/` 選單;無自然語言自動觸發 |
|
|
209
|
+
| 只裝 skill | 自然語言自動觸發(suggest-and-wait);無 `/` 選單 |
|
|
210
|
+
| 兩者都裝 | `/` 選單 + 自然語言 safety net **可共存** |
|
|
211
|
+
|
|
212
|
+
**兩者可共存、無需互斥**(已在真實 Claude Code 環境驗證):skill 名稱 `dflow` 與
|
|
213
|
+
command adapter 的 `dflow:<id>` 不撞名,明確命令各自精準載入、不會雙觸發;skill 當
|
|
214
|
+
自然語言 safety net,command adapters 當 `/` 選單。
|
|
215
|
+
|
|
216
|
+
安裝後用 `/skills` 或詢問「What skills are available?」確認 skill 已被探索到。注意:
|
|
217
|
+
新增頂層 skills 目錄可能需**重啟 Claude Code** 才會被 watch 到。另外,位於
|
|
218
|
+
`~/.claude/skills/dflow` 的 personal / enterprise skill 可能會 **override** 專案層級
|
|
219
|
+
skill(依 Claude 官方文件),用 `/skills` 可檢查目前生效的是哪一份。
|
|
220
|
+
|
|
221
|
+
**版控政策**:`.claude/skills/dflow/SKILL.md` 與 command adapter 一樣是**衍生物**,
|
|
222
|
+
沿用相同預設——不版控、由 clone 後重跑 `dflow configure-agents --skills` 重生成
|
|
223
|
+
(`.claude/skills/dflow/` 已列入建議的 gitignore 集合);clone-ready 團隊也可選擇版控。
|
|
224
|
+
重跑 `--skills` 是 idempotent 的:帶 marker 的既有 skill 會被乾淨重寫;若 `.claude/skills/dflow/SKILL.md`
|
|
225
|
+
**不是** Dflow 產生的(無 `<!-- dflow-generated: skill-adapter -->` marker),Dflow 不會覆蓋它,
|
|
226
|
+
只會印出 warning 提示你移除或改名。
|
|
149
227
|
|
|
150
228
|
## 與其他 AI 工具的差異
|
|
151
229
|
|
|
@@ -177,6 +177,20 @@ documentation guidance.
|
|
|
177
177
|
In this mode, the Codex-target merge snippet filename is
|
|
178
178
|
`dflow/specs/shared/AGENTS-md-command-adapters-snippet.md`.
|
|
179
179
|
|
|
180
|
+
### Version-Control Policy for Generated Artifacts (Codex)
|
|
181
|
+
|
|
182
|
+
Codex does not generate command files, so there is **no derived adapter to
|
|
183
|
+
gitignore**. On the Codex side, what you version-control is the `AGENTS.md`
|
|
184
|
+
shim and `dflow/` (the canonical guide and specs); the merge helper
|
|
185
|
+
`dflow/specs/shared/AGENTS-md-command-adapters-snippet.md` is part of `dflow/`
|
|
186
|
+
and is **version-controlled along with `dflow/`**. `--command-adapters` only
|
|
187
|
+
strengthens the text triggers in `AGENTS.md` for Codex; it adds no `.claude/`,
|
|
188
|
+
`.github/`, or `.agents/` command files, so the Claude / Copilot
|
|
189
|
+
"version-control the generated adapter or not" trade-off does not apply on the
|
|
190
|
+
Codex side. The adapter version-control policy for other tools is covered in
|
|
191
|
+
[README "Files Created by Init"](../README.en.md#files-created-by-init) and the
|
|
192
|
+
per-tool guides.
|
|
193
|
+
|
|
180
194
|
## Differences vs Other AI Tools
|
|
181
195
|
|
|
182
196
|
The canonical guide (`dflow/specs/shared/AI-AGENT-GUIDE.md`) is identical
|
package/docs/using-with-codex.md
CHANGED
|
@@ -162,6 +162,16 @@ dflow:new-feature
|
|
|
162
162
|
在這個模式下,Codex 目標的 merge snippet 檔名是
|
|
163
163
|
`dflow/specs/shared/AGENTS-md-command-adapters-snippet.md`。
|
|
164
164
|
|
|
165
|
+
### 產生物的版控政策(Codex)
|
|
166
|
+
|
|
167
|
+
Codex 不產生 command 檔,所以**沒有需要 gitignore 的衍生 adapter**。Codex 端要版控的是
|
|
168
|
+
`AGENTS.md` shim 與 `dflow/`(canonical guide + 規格);其中
|
|
169
|
+
`dflow/specs/shared/AGENTS-md-command-adapters-snippet.md` 這個 merge helper 屬 `dflow/` 的一部分,
|
|
170
|
+
**隨 `dflow/` 一起版控**。`--command-adapters` 對 Codex 只強化 `AGENTS.md` 的文字 trigger,
|
|
171
|
+
不新增任何 `.claude/`、`.github/`、`.agents/` 命令檔,因此 Claude / Copilot 那套「衍生 adapter
|
|
172
|
+
要不要版控」的取捨在 Codex 端不適用。其他工具的 adapter 版控政策見
|
|
173
|
+
[README「Init 產生的檔案」](../README.md#init-產生的檔案) 與各 per-tool 指南。
|
|
174
|
+
|
|
165
175
|
## 與其他 AI 工具的差異
|
|
166
176
|
|
|
167
177
|
canonical 指南(`dflow/specs/shared/AI-AGENT-GUIDE.md`)在各工具之間是相同的。
|
|
@@ -83,6 +83,42 @@ does not copy workflow steps. Copilot's `/` parser behavior differs from
|
|
|
83
83
|
Claude and Codex: the prompt menu entry is `/dflow-<id>`, while chat text may
|
|
84
84
|
still name the canonical `/dflow:<id>` workflow.
|
|
85
85
|
|
|
86
|
+
### Version Control and Upgrades for Generated Adapters
|
|
87
|
+
|
|
88
|
+
`.github/prompts/dflow-<id>.prompt.md` is a **generated artifact** projected
|
|
89
|
+
from the canonical guide. Dflow's **recommended default** is to not
|
|
90
|
+
version-control it and regenerate it after clone with `dflow configure-agents
|
|
91
|
+
--command-adapters`; teams that want a native prompt menu immediately after
|
|
92
|
+
clone may instead **version-control** it. Use one consistent policy across all
|
|
93
|
+
tools in a project (policy overview in
|
|
94
|
+
[README "Files Created by Init"](../README.en.md#files-created-by-init)).
|
|
95
|
+
|
|
96
|
+
When using the gitignore default, add this to the project `.gitignore` (**mind
|
|
97
|
+
the glob side effect**):
|
|
98
|
+
|
|
99
|
+
```gitignore
|
|
100
|
+
.github/prompts/dflow-*.prompt.md
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
This glob also ignores any of your own prompt files named with a `dflow-`
|
|
104
|
+
prefix. If you have custom prompts with the same prefix, use a more specific
|
|
105
|
+
rule or rename your custom files. If these prompts are **already
|
|
106
|
+
version-controlled**, adding the ignore rule does not remove them
|
|
107
|
+
automatically; first run:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
git rm --cached .github/prompts/dflow-*.prompt.md
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
(`--cached` removes them from version control while keeping the working-tree
|
|
114
|
+
files.)
|
|
115
|
+
|
|
116
|
+
After upgrading Dflow, re-running `dflow configure-agents --command-adapters`
|
|
117
|
+
re-projects prompt adapters from the **new registry**, but an existing
|
|
118
|
+
`dflow/specs/shared/AI-AGENT-GUIDE.md` is **not** overwritten — "re-projecting
|
|
119
|
+
adapters" is not the same as "migrating the canonical guide." Re-project with
|
|
120
|
+
the **same dflow CLI version**.
|
|
121
|
+
|
|
86
122
|
### Sample Conversation Flow
|
|
87
123
|
|
|
88
124
|
A typical Copilot Chat workflow looks like this:
|
|
@@ -90,6 +90,32 @@ workflow 與 `dflow/specs/shared/AI-AGENT-GUIDE.md`,不複製 workflow 步驟
|
|
|
90
90
|
Copilot 的 `/` parser 行為與 Claude / Codex 不同:選單入口是 `/dflow-<id>`,
|
|
91
91
|
但在 chat 文字中也可以直接說 canonical `/dflow:<id>`。
|
|
92
92
|
|
|
93
|
+
### 產生物的版控政策與升級
|
|
94
|
+
|
|
95
|
+
`.github/prompts/dflow-<id>.prompt.md` 是從 canonical guide 投影出來的**衍生物**。Dflow 的
|
|
96
|
+
**建議預設**是不版控、由 clone 後重跑 `dflow configure-agents --command-adapters` 重生成;團隊若
|
|
97
|
+
想 clone 後立即有原生 prompt 選單,也可改為**版控**。請對同專案的所有工具採一致策略(政策總覽見
|
|
98
|
+
[README「Init 產生的檔案」](../README.md#init-產生的檔案))。
|
|
99
|
+
|
|
100
|
+
採 gitignore 預設時,在專案 `.gitignore` 加入(**注意 glob 副作用**):
|
|
101
|
+
|
|
102
|
+
```gitignore
|
|
103
|
+
.github/prompts/dflow-*.prompt.md
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
此 glob 會一併 ignore 你自己以 `dflow-` 開頭命名的 prompt 檔。若你有同前綴的自訂 prompt,請改用
|
|
107
|
+
更精確的規則或替自訂檔改名。若這些 prompt **已被版控**,新增 ignore 不會自動移出版控,需先:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
git rm --cached .github/prompts/dflow-*.prompt.md
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
(`--cached` 只移出版控、保留工作目錄檔案。)
|
|
114
|
+
|
|
115
|
+
升級 dflow 後重跑 `dflow configure-agents --command-adapters`,prompt adapter 會用**新版 registry**
|
|
116
|
+
重投影;但既有 `dflow/specs/shared/AI-AGENT-GUIDE.md` **不會**被覆寫——「重投影 adapter」不等於
|
|
117
|
+
「升級 canonical guide」。請以**相同的 dflow CLI 版本**重投影。
|
|
118
|
+
|
|
93
119
|
### 對話範例
|
|
94
120
|
|
|
95
121
|
典型的 Copilot Chat workflow 如下:
|
package/lib/init.js
CHANGED
|
@@ -10,6 +10,8 @@ const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
|
10
10
|
const TEMPLATE_ROOT = path.join(PACKAGE_ROOT, 'templates');
|
|
11
11
|
const COMMAND_REGISTRY_START = '<!-- dflow-command-registry:start -->';
|
|
12
12
|
const COMMAND_REGISTRY_END = '<!-- dflow-command-registry:end -->';
|
|
13
|
+
const COMMAND_ADAPTER_GENERATED_MARKER = '<!-- dflow-generated: command-adapter -->';
|
|
14
|
+
const SKILL_ADAPTER_GENERATED_MARKER = '<!-- dflow-generated: skill-adapter -->';
|
|
13
15
|
const EXPECTED_COMMAND_IDS = [
|
|
14
16
|
'new-feature',
|
|
15
17
|
'modify-existing',
|
|
@@ -23,6 +25,28 @@ const EXPECTED_COMMAND_IDS = [
|
|
|
23
25
|
'next',
|
|
24
26
|
'cancel'
|
|
25
27
|
];
|
|
28
|
+
const LEGACY_COMMAND_ADAPTERS = [
|
|
29
|
+
{
|
|
30
|
+
version: '0.5.0',
|
|
31
|
+
agent: 'claude',
|
|
32
|
+
source: 'generated:legacy-claude-command-adapter-v0.5.0',
|
|
33
|
+
pathPattern: '.claude/commands/dflow/dflow-<id>.md',
|
|
34
|
+
fingerprint: 'v0.5.0 buildThinCommandWrapper',
|
|
35
|
+
commands: [
|
|
36
|
+
{ id: 'new-feature', label: '/dflow:new-feature', argHint: 'feature request' },
|
|
37
|
+
{ id: 'modify-existing', label: '/dflow:modify-existing', argHint: 'change request' },
|
|
38
|
+
{ id: 'bug-fix', label: '/dflow:bug-fix', argHint: 'expected vs actual' },
|
|
39
|
+
{ id: 'new-phase', label: '/dflow:new-phase', argHint: 'feature id or phase goal' },
|
|
40
|
+
{ id: 'finish-feature', label: '/dflow:finish-feature', argHint: 'feature id' },
|
|
41
|
+
{ id: 'verify', label: '/dflow:verify', argHint: 'area or feature id' },
|
|
42
|
+
{ id: 'pr-review', label: '/dflow:pr-review', argHint: 'change or branch' },
|
|
43
|
+
{ id: 'report-dflow-feedback', label: '/dflow:report-dflow-feedback', argHint: 'issue or improvement' },
|
|
44
|
+
{ id: 'status', label: '/dflow:status', argHint: '-' },
|
|
45
|
+
{ id: 'next', label: '/dflow:next', argHint: '-' },
|
|
46
|
+
{ id: 'cancel', label: '/dflow:cancel', argHint: '-' }
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
];
|
|
26
50
|
|
|
27
51
|
const PROSE_LANGUAGE_PATTERN =
|
|
28
52
|
/^[A-Za-z]{2,3}(?:-[A-Za-z]{4})?(?:-(?:[A-Za-z]{2}|[0-9]{3}))?(?:-(?:[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*$/;
|
|
@@ -262,10 +286,12 @@ async function runConfigureAgents(options = {}) {
|
|
|
262
286
|
const plan = await buildConfigureAgentsPlan(cwd, {
|
|
263
287
|
...projectContext,
|
|
264
288
|
aiAgents,
|
|
265
|
-
commandAdapters: Boolean(options.commandAdapters)
|
|
289
|
+
commandAdapters: Boolean(options.commandAdapters),
|
|
290
|
+
skills: Boolean(options.skills)
|
|
266
291
|
});
|
|
267
292
|
|
|
268
|
-
|
|
293
|
+
const warnings = plan.warnings || [];
|
|
294
|
+
renderPreview(stdout, plan, warnings);
|
|
269
295
|
const confirmed = await askConfirmation(rl, 'Create these files? (y/N) ');
|
|
270
296
|
|
|
271
297
|
if (!confirmed) {
|
|
@@ -276,6 +302,7 @@ async function runConfigureAgents(options = {}) {
|
|
|
276
302
|
rl = undefined;
|
|
277
303
|
|
|
278
304
|
const result = await writeFilePlan(cwd, plan);
|
|
305
|
+
result.warnings.unshift(...warnings);
|
|
279
306
|
result.warnings.push(...collectUnresolvedPlaceholderWarnings(plan, result.created));
|
|
280
307
|
|
|
281
308
|
printResultReport(stdout, result, plan.deferred);
|
|
@@ -1000,6 +1027,7 @@ async function buildConfigureAgentsPlan(cwd, answers) {
|
|
|
1000
1027
|
optionalFiles: answers.optionalFiles || []
|
|
1001
1028
|
});
|
|
1002
1029
|
const items = [];
|
|
1030
|
+
const warnings = [];
|
|
1003
1031
|
|
|
1004
1032
|
let content = await readPackagedTemplate(answers.edition, 'scaffolding/AI-AGENT-GUIDE.md');
|
|
1005
1033
|
content = substitutePlaceholders(content, substitution);
|
|
@@ -1022,9 +1050,16 @@ async function buildConfigureAgentsPlan(cwd, answers) {
|
|
|
1022
1050
|
|
|
1023
1051
|
await finalizePlanItems(cwd, items);
|
|
1024
1052
|
|
|
1053
|
+
if (answers.commandAdapters) {
|
|
1054
|
+
await addLegacyCommandAdapterCleanupItems(cwd, items, answers.aiAgents, warnings);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
await addSkillAdapterItems(cwd, items, answers.aiAgents, answers.skills, warnings);
|
|
1058
|
+
|
|
1025
1059
|
return {
|
|
1026
1060
|
items,
|
|
1027
1061
|
deferred: [],
|
|
1062
|
+
warnings,
|
|
1028
1063
|
unresolvedInitPlaceholders: Array.from(substitution.entries())
|
|
1029
1064
|
.filter(([placeholder, value]) => placeholder === value)
|
|
1030
1065
|
.map(([placeholder]) => placeholder)
|
|
@@ -1156,6 +1191,158 @@ function addCommandAdapterItems(items, aiAgents, commandRegistry) {
|
|
|
1156
1191
|
}
|
|
1157
1192
|
}
|
|
1158
1193
|
|
|
1194
|
+
async function addLegacyCommandAdapterCleanupItems(cwd, items, aiAgents, warnings) {
|
|
1195
|
+
for (const legacy of LEGACY_COMMAND_ADAPTERS) {
|
|
1196
|
+
if (!aiAgents.includes(legacy.agent)) {
|
|
1197
|
+
continue;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
for (const command of legacy.commands) {
|
|
1201
|
+
const relativePath = legacy.pathPattern.replace('<id>', command.id);
|
|
1202
|
+
const targetPath = path.join(cwd, relativePath);
|
|
1203
|
+
let stats;
|
|
1204
|
+
|
|
1205
|
+
try {
|
|
1206
|
+
stats = await fs.stat(targetPath);
|
|
1207
|
+
} catch (error) {
|
|
1208
|
+
if (error.code === 'ENOENT') {
|
|
1209
|
+
continue;
|
|
1210
|
+
}
|
|
1211
|
+
throw error;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
if (!stats.isFile()) {
|
|
1215
|
+
warnings.push(`Found legacy Dflow command adapter path but it is not a file; not removed: ${relativePath}`);
|
|
1216
|
+
continue;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
const content = await fs.readFile(targetPath, 'utf8');
|
|
1220
|
+
const expectedContent = buildLegacyCommandAdapterFingerprint(legacy, command);
|
|
1221
|
+
if (normalizeCommandAdapterFingerprint(content) !== normalizeCommandAdapterFingerprint(expectedContent)) {
|
|
1222
|
+
warnings.push(`Found legacy Dflow command adapter with non-generated content; not removed: ${relativePath}. Inspect it manually before deleting.`);
|
|
1223
|
+
continue;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
items.push({
|
|
1227
|
+
relativePath,
|
|
1228
|
+
source: legacy.source,
|
|
1229
|
+
notes: `stale dflow adapter generated by Dflow ${legacy.version}`,
|
|
1230
|
+
action: 'remove',
|
|
1231
|
+
size: Buffer.byteLength(content, 'utf8'),
|
|
1232
|
+
expectedContent
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
function buildDflowSkillAdapter() {
|
|
1239
|
+
return `---
|
|
1240
|
+
name: dflow
|
|
1241
|
+
description: >
|
|
1242
|
+
Dflow SDD/DDD workflow guardian for this project. PRIMARY: the canonical
|
|
1243
|
+
/dflow:* commands (/dflow:new-feature, /dflow:modify-existing, /dflow:bug-fix,
|
|
1244
|
+
/dflow:new-phase, /dflow:finish-feature, /dflow:pr-review, /dflow:verify,
|
|
1245
|
+
/dflow:report-dflow-feedback, /dflow:status, /dflow:next, /dflow:cancel).
|
|
1246
|
+
SECONDARY (auto-trigger safety net) — engage ONLY for: adding or changing
|
|
1247
|
+
product/domain behavior, new requirements, a feature or bug-fix workflow, or
|
|
1248
|
+
spec-impacting architecture/domain-model decisions. Do NOT engage for pure
|
|
1249
|
+
refactors, infrastructure chores, formatting, or general code questions.
|
|
1250
|
+
When engaged by natural language, DO NOT auto-enter a workflow: judge the
|
|
1251
|
+
intent, suggest the matching /dflow: command, and wait for confirmation.
|
|
1252
|
+
---
|
|
1253
|
+
|
|
1254
|
+
${SKILL_ADAPTER_GENERATED_MARKER}
|
|
1255
|
+
|
|
1256
|
+
# Dflow SDD/DDD Workflow Guardian
|
|
1257
|
+
|
|
1258
|
+
This project uses Dflow for spec-first AI-assisted development. The canonical
|
|
1259
|
+
workflow contract, command registry, spec locations, and SDD/DDD rules live in:
|
|
1260
|
+
|
|
1261
|
+
- \`dflow/specs/shared/AI-AGENT-GUIDE.md\`
|
|
1262
|
+
|
|
1263
|
+
When this skill engages, read that guide and follow the matching \`/dflow:\`
|
|
1264
|
+
workflow or control command defined there. Do not duplicate or invent workflow
|
|
1265
|
+
steps here — the guide is the single source of truth.
|
|
1266
|
+
|
|
1267
|
+
If engaged by natural language (not an explicit \`/dflow:\` command): identify
|
|
1268
|
+
which \`/dflow:\` command fits, suggest it, and wait for the developer to confirm
|
|
1269
|
+
before entering any workflow.
|
|
1270
|
+
`;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
async function addSkillAdapterItems(cwd, items, aiAgents, skills, warnings) {
|
|
1274
|
+
if (!skills) {
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
if (!aiAgents.includes('claude')) {
|
|
1279
|
+
warnings.push(
|
|
1280
|
+
'The --skills flag currently supports Claude Code only; no skill adapter was generated because Claude Code was not a selected target.'
|
|
1281
|
+
);
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
const relativePath = '.claude/skills/dflow/SKILL.md';
|
|
1286
|
+
const targetPath = path.join(cwd, relativePath);
|
|
1287
|
+
|
|
1288
|
+
// The thin skill is edition-neutral (it only points to the per-edition guide),
|
|
1289
|
+
// so there is nothing edition-specific to go stale — re-running just rewrites
|
|
1290
|
+
// the same marker-guarded file (idempotent). No LEGACY skill set exists yet
|
|
1291
|
+
// (skills are new in PROPOSAL-038); future skill cleanup would extend the same
|
|
1292
|
+
// LEGACY_* / addLegacyCommandAdapterCleanupItems marker-fingerprint pattern.
|
|
1293
|
+
let existingContent;
|
|
1294
|
+
try {
|
|
1295
|
+
existingContent = await fs.readFile(targetPath, 'utf8');
|
|
1296
|
+
} catch (error) {
|
|
1297
|
+
if (error.code !== 'ENOENT') {
|
|
1298
|
+
throw error;
|
|
1299
|
+
}
|
|
1300
|
+
existingContent = undefined;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
if (existingContent !== undefined && !existingContent.includes(SKILL_ADAPTER_GENERATED_MARKER)) {
|
|
1304
|
+
warnings.push(
|
|
1305
|
+
'Existing .claude/skills/dflow/SKILL.md is not a Dflow-generated skill; left unchanged. Remove or rename it to let Dflow manage this skill.'
|
|
1306
|
+
);
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
items.push({
|
|
1311
|
+
relativePath,
|
|
1312
|
+
source: 'generated:claude-skill-adapter',
|
|
1313
|
+
notes: 'skill adapter, thin skill pointing to AI-AGENT-GUIDE.md',
|
|
1314
|
+
content: buildDflowSkillAdapter(),
|
|
1315
|
+
overwrite: true
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
function buildLegacyCommandAdapterFingerprint(legacy, command) {
|
|
1320
|
+
if (legacy.version === '0.5.0' && legacy.fingerprint === 'v0.5.0 buildThinCommandWrapper') {
|
|
1321
|
+
return buildLegacyV050ThinCommandWrapper(command);
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
throw new InitError(`Internal error: unsupported legacy command adapter fingerprint: ${legacy.version}`);
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
function buildLegacyV050ThinCommandWrapper(command) {
|
|
1328
|
+
const argHint = command.argHint === '-'
|
|
1329
|
+
? 'Argument hint: none.'
|
|
1330
|
+
: `Argument hint: ${command.argHint}.`;
|
|
1331
|
+
|
|
1332
|
+
return `# /dflow-${command.id}
|
|
1333
|
+
|
|
1334
|
+
Execute the canonical \`${command.label}\` Dflow workflow or control command.
|
|
1335
|
+
|
|
1336
|
+
Definition: \`dflow/specs/shared/AI-AGENT-GUIDE.md\`
|
|
1337
|
+
|
|
1338
|
+
${argHint}
|
|
1339
|
+
`;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
function normalizeCommandAdapterFingerprint(content) {
|
|
1343
|
+
return String(content).replace(/\r\n/g, '\n');
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1159
1346
|
function buildCodexCommandTriggerSection(commandRegistry) {
|
|
1160
1347
|
const triggers = commandRegistry
|
|
1161
1348
|
.map((command) => {
|
|
@@ -1190,6 +1377,8 @@ function buildThinCommandWrapper(command, displayName = command.label) {
|
|
|
1190
1377
|
|
|
1191
1378
|
return `# ${displayName}
|
|
1192
1379
|
|
|
1380
|
+
${COMMAND_ADAPTER_GENERATED_MARKER}
|
|
1381
|
+
|
|
1193
1382
|
Execute the canonical \`${command.label}\` Dflow workflow or control command.
|
|
1194
1383
|
|
|
1195
1384
|
Definition: \`dflow/specs/shared/AI-AGENT-GUIDE.md\`
|
|
@@ -1651,6 +1840,7 @@ async function writeFilePlan(cwd, plan) {
|
|
|
1651
1840
|
const result = {
|
|
1652
1841
|
created: [],
|
|
1653
1842
|
updated: [],
|
|
1843
|
+
removed: [],
|
|
1654
1844
|
skipped: [],
|
|
1655
1845
|
warnings: []
|
|
1656
1846
|
};
|
|
@@ -1659,6 +1849,37 @@ async function writeFilePlan(cwd, plan) {
|
|
|
1659
1849
|
const targetPath = path.join(cwd, item.relativePath);
|
|
1660
1850
|
|
|
1661
1851
|
try {
|
|
1852
|
+
if (item.action === 'remove') {
|
|
1853
|
+
let stats;
|
|
1854
|
+
try {
|
|
1855
|
+
stats = await fs.stat(targetPath);
|
|
1856
|
+
} catch (error) {
|
|
1857
|
+
if (error.code === 'ENOENT') {
|
|
1858
|
+
result.skipped.push(item.relativePath);
|
|
1859
|
+
result.warnings.push(`Skipped missing stale adapter: ${item.relativePath}`);
|
|
1860
|
+
continue;
|
|
1861
|
+
}
|
|
1862
|
+
throw error;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
if (!stats.isFile()) {
|
|
1866
|
+
result.skipped.push(item.relativePath);
|
|
1867
|
+
result.warnings.push(`Skipped stale adapter removal because target is not a file: ${item.relativePath}`);
|
|
1868
|
+
continue;
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
const currentContent = await fs.readFile(targetPath, 'utf8');
|
|
1872
|
+
if (normalizeCommandAdapterFingerprint(currentContent) !== normalizeCommandAdapterFingerprint(item.expectedContent || '')) {
|
|
1873
|
+
result.skipped.push(item.relativePath);
|
|
1874
|
+
result.warnings.push(`Skipped stale adapter removal because content changed after preview: ${item.relativePath}`);
|
|
1875
|
+
continue;
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
await fs.unlink(targetPath);
|
|
1879
|
+
result.removed.push(item.relativePath);
|
|
1880
|
+
continue;
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1662
1883
|
if (await pathExists(targetPath)) {
|
|
1663
1884
|
if (item.overwrite) {
|
|
1664
1885
|
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
@@ -1710,9 +1931,11 @@ async function writeFilePlan(cwd, plan) {
|
|
|
1710
1931
|
);
|
|
1711
1932
|
}
|
|
1712
1933
|
|
|
1713
|
-
const message =
|
|
1714
|
-
? `
|
|
1715
|
-
:
|
|
1934
|
+
const message = item.action === 'remove'
|
|
1935
|
+
? `Write failed while removing ${item.relativePath}: ${error.message}`
|
|
1936
|
+
: error.code === 'ENOTDIR' || error.code === 'EEXIST'
|
|
1937
|
+
? `Cannot create parent directory for ${item.relativePath}: a parent path is a file.`
|
|
1938
|
+
: `Write failed while creating ${item.relativePath}: ${error.message}`;
|
|
1716
1939
|
throw new WritePhaseError(message, result);
|
|
1717
1940
|
}
|
|
1718
1941
|
}
|
|
@@ -1756,6 +1979,9 @@ function printResultReport(stdout, result, deferred) {
|
|
|
1756
1979
|
stdout.write('\nUpdated:\n');
|
|
1757
1980
|
printList(stdout, result.updated);
|
|
1758
1981
|
|
|
1982
|
+
stdout.write('\nRemoved:\n');
|
|
1983
|
+
printList(stdout, result.removed);
|
|
1984
|
+
|
|
1759
1985
|
stdout.write('\nSkipped:\n');
|
|
1760
1986
|
printList(stdout, result.skipped);
|
|
1761
1987
|
|
|
@@ -1782,7 +2008,7 @@ Recommended next steps:
|
|
|
1782
2008
|
|
|
1783
2009
|
function printConfigureAgentsNextSteps(stdout, commandAdapters = false) {
|
|
1784
2010
|
const commandAdapterStep = commandAdapters
|
|
1785
|
-
? '- Command adapters use tool-specific invocation names: Claude Code `/dflow:<id>`; GitHub Copilot prompt menu `/dflow-<id>` or canonical `/dflow:<id>` as text; Codex CLI plain text without a slash, such as `dflow:status`. Canonical `/dflow:*` names remain defined in dflow/specs/shared/AI-AGENT-GUIDE.md. If upgrading from Dflow 0.5.0,
|
|
2011
|
+
? '- Command adapters use tool-specific invocation names: Claude Code `/dflow:<id>`; GitHub Copilot prompt menu `/dflow-<id>` or canonical `/dflow:<id>` as text; Codex CLI plain text without a slash, such as `dflow:status`. Canonical `/dflow:*` names remain defined in dflow/specs/shared/AI-AGENT-GUIDE.md. If upgrading from Dflow 0.5.0, stale `.claude/commands/dflow/dflow-*.md` files generated by 0.5.0 are detected and listed for removal in the confirmation preview, so Claude Code does not show both old and new command names; edited or non-Dflow files are kept with a warning.\n'
|
|
1786
2012
|
: '';
|
|
1787
2013
|
|
|
1788
2014
|
stdout.write(`
|