dflow-sdd-ddd 0.2.0 → 0.4.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +971 -0
  2. package/README.en.md +345 -0
  3. package/README.md +213 -159
  4. package/docs/evaluating-dflow.en.md +238 -0
  5. package/docs/evaluating-dflow.md +94 -151
  6. package/docs/examples-by-stack.md +516 -0
  7. package/docs/migrating-to-dflow-v1.md +28 -10
  8. package/docs/release-versioning-policy.md +13 -0
  9. package/docs/using-with-claude-code.en.md +210 -0
  10. package/docs/using-with-claude-code.md +108 -124
  11. package/docs/using-with-codex.en.md +248 -0
  12. package/docs/using-with-codex.md +137 -157
  13. package/docs/using-with-gemini-cli.en.md +200 -0
  14. package/docs/using-with-gemini-cli.md +184 -0
  15. package/docs/using-with-github-copilot.en.md +136 -0
  16. package/docs/using-with-github-copilot.md +177 -0
  17. package/docs/why-ddd-for-ai.en.md +37 -0
  18. package/docs/why-ddd-for-ai.md +19 -17
  19. package/lib/init.js +187 -24
  20. package/package.json +1 -1
  21. package/templates/brownfield/scaffolding/AI-AGENT-GUIDE.md +2 -1
  22. package/templates/brownfield/scaffolding/CLAUDE-md-snippet.md +32 -22
  23. package/templates/brownfield/scaffolding/Git-principles-gitflow.md +1 -1
  24. package/templates/brownfield/scaffolding/Git-principles-trunk.md +1 -1
  25. package/templates/brownfield/scaffolding/_conventions.md +1 -1
  26. package/templates/brownfield/scaffolding/_overview.md +40 -29
  27. package/templates/brownfield/templates/CLAUDE.md +26 -18
  28. package/templates/brownfield/templates/context-definition.md +4 -4
  29. package/templates/brownfield/templates/context-map.md +1 -1
  30. package/templates/brownfield/templates/lightweight-spec.md +3 -1
  31. package/templates/brownfield/templates/models.md +1 -1
  32. package/templates/brownfield/templates/phase-spec.md +33 -28
  33. package/templates/brownfield/templates/tech-debt.md +2 -2
  34. package/templates/greenfield/scaffolding/AI-AGENT-GUIDE.md +2 -1
  35. package/templates/greenfield/scaffolding/CLAUDE-md-snippet.md +14 -13
  36. package/templates/greenfield/scaffolding/Git-principles-gitflow.md +2 -2
  37. package/templates/greenfield/scaffolding/Git-principles-trunk.md +2 -2
  38. package/templates/greenfield/scaffolding/_conventions.md +1 -1
  39. package/templates/greenfield/scaffolding/_overview.md +29 -11
  40. package/templates/greenfield/templates/CLAUDE.md +6 -6
  41. package/templates/greenfield/templates/phase-spec.md +24 -21
@@ -0,0 +1,177 @@
1
+ # 在 GitHub Copilot 中使用 Dflow
2
+
3
+ > **繁體中文** | [English](using-with-github-copilot.en.md)
4
+
5
+ 當你的 AI 程式設計助理是 GitHub Copilot(IDE chat + inline completions)時,Dflow 的使用體驗 walk-through。閱讀約需 10 分鐘。
6
+
7
+ 本指南專注於 GitHub Copilot 的具體使用體驗。工具中立的評估流程請見
8
+ [`docs/evaluating-dflow.md`](evaluating-dflow.md)。完整的 Get Started
9
+ 與功能列表請見 [`README.md`](../README.md)。
10
+
11
+ ## 本指南的適用對象
12
+
13
+ 你正在使用或評估以 GitHub Copilot 作為 AI 程式設計助理的 Dflow。
14
+ 本指南說明 `init` 之後 Copilot 看到了什麼、repository shim 的位置、
15
+ 如何從 IDE 呼叫 Dflow workflow,以及幾個值得了解的 Copilot 專屬使用模式
16
+ 與 permission 行為。
17
+
18
+ ## 前置條件
19
+
20
+ - 在你的 IDE(VS Code、JetBrains 或 GitHub.dev)中已啟用 GitHub Copilot。
21
+ - 已具備 Node.js / npm 環境(Dflow 透過 npm 發佈)。以
22
+ `npm install -g dflow-sdd-ddd` 全域安裝,或用 `npx dflow-sdd-ddd init`
23
+ 走免安裝路徑,確保 repository shim 存在。
24
+ - 已具備 Dflow canonical 指南的基本認識:`dflow/specs/shared/AI-AGENT-GUIDE.md`。
25
+
26
+ ## `init` 之後 GitHub Copilot 看到了什麼
27
+
28
+ `init` 為 Copilot 在以下路徑寫入一個小型的工具專屬薄 shim:
29
+
30
+ - `.github/copilot-instructions.md`(注意:位於 `.github/` 目錄內,非專案根目錄)
31
+
32
+ 生成的 shim 範例:
33
+
34
+ ```markdown
35
+ # GitHub Copilot Repository Instructions
36
+
37
+ This project uses Dflow for spec-first AI-assisted development.
38
+
39
+ Before planning or editing code, read and follow:
40
+
41
+ - `dflow/specs/shared/AI-AGENT-GUIDE.md`
42
+ ```
43
+
44
+ 重點說明:
45
+
46
+ - Copilot shim 位於 `.github/copilot-instructions.md`(見 `lib/init.js` mapping)。
47
+ - Copilot shim 不含 Markdown `@` import。它只透過路徑指向 canonical 指南,讀取
48
+ `dflow/specs/shared/AI-AGENT-GUIDE.md` 時需明確開啟該檔案。
49
+
50
+ ## 在 GitHub Copilot 中使用 Dflow Workflow 指令
51
+
52
+ Copilot 是 IDE 優先的助理(chat panel + inline completions),不是 CLI 工具。
53
+ 請把 Dflow workflow 名稱當成普通的對話指示,而非 CLI slash command:
54
+
55
+ - 在 Copilot Chat 中:「Run the Dflow /dflow:new-feature workflow」—— Copilot
56
+ 應讀取 canonical 指南並繼續執行。
57
+ - 在程式碼 comment 或 editor chat 中,以普通文字描述 workflow:
58
+ `Run the Dflow /dflow:new-feature workflow.`
59
+
60
+ 可用的 workflow 入口:
61
+
62
+ | 指令 | 適用情境 |
63
+ |---|---|
64
+ | `/dflow:new-feature` | 需要新增一個使用者可見的功能或業務行為。 |
65
+ | `/dflow:modify-existing` | 需要修改現有行為。 |
66
+ | `/dflow:bug-fix` | 可以用預期行為 vs 實際行為描述的缺陷。 |
67
+ | `/dflow:new-phase` | 進行中的 feature 需要另一個實作 slice。 |
68
+ | `/dflow:finish-feature` | 實作完成後需要進行漂移(drift)收尾。 |
69
+ | `/dflow:verify` | 需要對 spec、領域文件、實作與測試進行一致性檢查。 |
70
+ | `/dflow:pr-review` | 變更已準備好進行 SDD/DDD review。 |
71
+ | `/dflow:report-dflow-feedback` | 你發現了 Dflow 的問題或改進點,想要一份清理過的上游回饋草稿。 |
72
+
73
+ ### 對話範例
74
+
75
+ 典型的 Copilot Chat workflow 如下:
76
+
77
+ ```text
78
+ You: I'd like to use the /dflow:new-feature workflow. We need to add the
79
+ ability for users to export their data as CSV.
80
+
81
+ Copilot: I'll help you structure this as a new feature. Let me start by
82
+ reviewing the AI-AGENT-GUIDE to understand the spec format.
83
+
84
+ Before drafting the feature spec, I need to understand the scope:
85
+
86
+ 1. Should the CSV include all user data fields or a selected subset?
87
+ 2. Which user roles should be able to trigger the export?
88
+ 3. Are there any compliance or performance constraints I should know?
89
+
90
+ You: All fields, only admin users, and exports should complete within 30 seconds
91
+ for datasets up to 1 million rows.
92
+
93
+ Copilot: Got it. I'll create the feature spec at dflow/specs/features/active/
94
+ with these constraints. Let me also outline the implementation phases...
95
+ ```
96
+
97
+ ### 既有的 Repository 指示
98
+
99
+ 如果你的專案中已有 `.github/copilot-instructions.md`,`init` 不會覆蓋它。
100
+ 它改為在 `dflow/specs/shared/` 下寫入 merge snippet,讓你手動審閱後貼入
101
+ 既有檔案。這樣可以避免破壞你已有的自訂 Copilot 指示。
102
+
103
+ 到 `dflow/specs/shared/COPILOT-INSTRUCTIONS-MERGE-SNIPPET.md` 找到 merge
104
+ snippet,再把相關段落貼入你現有的 `.github/copilot-instructions.md`。
105
+
106
+ ### Slash Command Passthrough 說明
107
+
108
+ Copilot 是否會直通(passthrough)原始的 `/dflow:*` slash command,取決於
109
+ IDE 整合方式與 Copilot 版本。若 Copilot 無法識別以 slash 為前綴的 workflow
110
+ 名稱,以普通文字重新送出請求:
111
+
112
+ ```text
113
+ You: Instead of /dflow:new-feature, try: "Please help me start a new Dflow
114
+ feature workflow. Read dflow/specs/shared/AI-AGENT-GUIDE.md first."
115
+ ```
116
+
117
+ ## 與其他 AI 工具的差異
118
+
119
+ canonical 指南(`dflow/specs/shared/AI-AGENT-GUIDE.md`)在各工具之間是相同的。
120
+ 只有根目錄層的 shim 有所不同:
121
+
122
+ | 工具 | 產生的 shim | 載入 canonical 指南的方式 |
123
+ |---|---|---|
124
+ | GitHub Copilot | `.github/copilot-instructions.md` | 直接讀取 repository 指示 |
125
+ | Claude Code | `CLAUDE.md` | `@dflow/specs/shared/AI-AGENT-GUIDE.md` Markdown import |
126
+ | Gemini CLI | `GEMINI.md` | `@dflow/specs/shared/AI-AGENT-GUIDE.md` Markdown import |
127
+ | Codex / Copilot coding agent | `AGENTS.md` | 啟動時直接讀取檔案內容 |
128
+
129
+ - Shim 路徑:Copilot 使用 `.github/copilot-instructions.md`(不是 `AGENTS.md`
130
+ 或 `CLAUDE.md`)。
131
+ - Markdown import:Copilot shim 不含 `@dflow/specs/shared/AI-AGENT-GUIDE.md`
132
+ import。這點與 Claude Code / Gemini CLI 的 shim 透過 `@` import inline 嵌入不同。
133
+ - 工具模型:Copilot 是 IDE-based(chat panel + inline completions);
134
+ Codex / Claude Code 是 CLI-based agent。Copilot 透過編輯器 UI 互動,而非
135
+ command-line session。
136
+ - Workflow 呼叫:使用 CLI agent 時可對 agent process 輸入 `/dflow:*`;使用
137
+ Copilot 時,建議在 Copilot Chat 或 editor comment 中使用普通文字描述。
138
+ - Permission 模型:Copilot 依賴 IDE 的 permission 與 extension sandbox。它可能
139
+ 受 editor-level approvals 管理;CLI 工具通常有明確的 sandbox flags 與獨立的
140
+ permission gates。
141
+
142
+ ## 常見模式與注意事項
143
+
144
+ - 保持 `.github/copilot-instructions.md` 精簡。Canonical Dflow 指南
145
+ (`dflow/specs/shared/AI-AGENT-GUIDE.md`)才是 source of truth。
146
+ - 若 Copilot 看起來只從 shim 文字工作,請明確要求它開啟或讀取
147
+ `dflow/specs/shared/AI-AGENT-GUIDE.md` 後再繼續。
148
+ - Copilot 的 inline completions 可能在未走 Dflow workflow 的情況下直接建議程式碼;
149
+ 需要 spec-driven 輸出時,請明確要求執行 workflow。
150
+ - Copilot Chat context 不一定會在所有 IDE 版本中自動包含 `.github/` 目錄下的
151
+ repository 指示檔;行為因 Copilot / IDE 版本而異(見頁尾說明)。
152
+ - 當 slash-prefixed forms 被 IDE 拒絕時,改用普通文字描述 workflow 名稱。
153
+
154
+ ## 下一步
155
+
156
+ 如果你還沒有執行 `init`:
157
+
158
+ - 執行 `dflow init`(安裝後)或 `npx dflow-sdd-ddd init`,選擇 Copilot 目標
159
+ 以建立 `.github/copilot-instructions.md`。參考
160
+ [評估者指南 Playbook](evaluating-dflow.md#30-分鐘評估-playbook) 在可拋棄的
161
+ 範例專案中試用。
162
+
163
+ 如果你已執行 `init` 且想查看端到端的 workflow 範例:
164
+
165
+ - 閱讀 [`dflow/specs/shared/AI-AGENT-GUIDE.md`](../sdd-ddd-greenfield-skill/scaffolding/AI-AGENT-GUIDE.md)
166
+ (或 brownfield 等效版本)後再開始 workflow。
167
+ - 閱讀 [`tutorial/01-greenfield/`](../tutorial/01-greenfield/walkthrough-00-setup.md),
168
+ 其中有展示 Copilot chat flow 的對話範例。
169
+
170
+ 如果你想了解設計理念:
171
+
172
+ - 閱讀[為什麼 AI 時代 DDD 更重要](why-ddd-for-ai.md)。
173
+
174
+ ---
175
+
176
+ IDE 行為說明:Slash command passthrough 與 `.github/` 指示檔的自動包含行為,
177
+ 會因 Copilot / IDE 版本而異。依賴確切語意前請向 maintainer 確認。
@@ -0,0 +1,37 @@
1
+ # Why DDD Matters More with AI
2
+
3
+ > [繁體中文](why-ddd-for-ai.md) | **English**
4
+
5
+ AI-assisted development changes the failure mode of software design. The team can produce more code faster, but unclear domain meaning is also amplified faster.
6
+
7
+ When a project lacks shared language and explicit boundaries, small inconsistencies spread:
8
+
9
+ - the same concept appears as `Order`, `Booking`, and `Transaction`
10
+ - APIs encode different meanings for similar actions
11
+ - business rules live in handlers, UI code, scripts, and tests
12
+ - nobody can confidently say which behavior is authoritative
13
+
14
+ An AI coding agent does not know the business domain by default. When the prompt is incomplete, it fills the missing parts with plausible logic. That logic may compile, pass superficial tests, and still be wrong. The most dangerous AI mistakes are often not syntax errors; they are reasonable-looking domain mistakes.
15
+
16
+ DDD gives the spec a semantic backbone:
17
+
18
+ | DDD idea | AI-era value |
19
+ |---|---|
20
+ | **Ubiquitous Language** | Keeps names and meanings stable across prompts, specs, code, and reviews. |
21
+ | **Bounded Context** | Defines where a term or rule is valid, and prevents accidental meaning leaks. |
22
+ | **Domain Model** | Gives behavior a clear owner instead of scattering rules across technical layers. |
23
+ | **Domain Rules** | States what is correct, allowed, forbidden, and exceptional before code generation. |
24
+
25
+ The important shift is where design lives. In older workflows, much of the real design could remain implicit in code. With AI, that is too late. The model needs constraints before it generates code.
26
+
27
+ ```text
28
+ Without DDD:
29
+ Prompt -> AI fills gaps -> Code -> Hidden domain drift
30
+
31
+ With DDD:
32
+ Domain meaning -> Structured spec -> AI implementation -> Reviewable code
33
+ ```
34
+
35
+ Code still matters, but it is no longer the first place where meaning should be discovered. For AI collaboration, specs become the pre-generation contract, and DDD supplies the language, boundaries, and rules that make the contract precise.
36
+
37
+ Dflow is built around that idea: spec first, domain meaning explicit, AI constrained before implementation, and drift checked before the work is considered done.
@@ -1,26 +1,28 @@
1
- # Why DDD Matters More with AI
1
+ # 為什麼 AI 時代 DDD 更重要
2
2
 
3
- AI-assisted development changes the failure mode of software design. The team can produce more code faster, but unclear domain meaning is also amplified faster.
3
+ > **繁體中文** | [English](why-ddd-for-ai.en.md)
4
4
 
5
- When a project lacks shared language and explicit boundaries, small inconsistencies spread:
5
+ AI 輔助開發改變了軟體設計的失敗模式。團隊能更快產出更多程式碼,但不清晰的領域語義也會以同樣的速度擴散。
6
6
 
7
- - the same concept appears as `Order`, `Booking`, and `Transaction`
8
- - APIs encode different meanings for similar actions
9
- - business rules live in handlers, UI code, scripts, and tests
10
- - nobody can confidently say which behavior is authoritative
7
+ 當專案缺乏共同語言與明確邊界時,細小的不一致便會蔓延:
11
8
 
12
- An AI coding agent does not know the business domain by default. When the prompt is incomplete, it fills the missing parts with plausible logic. That logic may compile, pass superficial tests, and still be wrong. The most dangerous AI mistakes are often not syntax errors; they are reasonable-looking domain mistakes.
9
+ - 同一個概念以 `Order`、`Booking`、`Transaction` 三種名稱出現
10
+ - API 對相似動作賦予不同語義
11
+ - 業務規則分散在 handler、UI 程式碼、腳本與測試之中
12
+ - 沒有人能確定哪一個行為才是權威的
13
13
 
14
- DDD gives the spec a semantic backbone:
14
+ AI 程式碼代理預設不了解業務領域。當 prompt 資訊不完整時,它會以看似合理的邏輯填補缺漏。這些邏輯可能通過編譯、通過表面測試,卻依然是錯的。AI 最危險的錯誤往往不是語法錯誤,而是外觀合理的領域錯誤。
15
15
 
16
- | DDD idea | AI-era value |
16
+ DDD 為規格提供語義骨幹:
17
+
18
+ | DDD 概念 | AI 時代的價值 |
17
19
  |---|---|
18
- | **Ubiquitous Language** | Keeps names and meanings stable across prompts, specs, code, and reviews. |
19
- | **Bounded Context** | Defines where a term or rule is valid, and prevents accidental meaning leaks. |
20
- | **Domain Model** | Gives behavior a clear owner instead of scattering rules across technical layers. |
21
- | **Domain Rules** | States what is correct, allowed, forbidden, and exceptional before code generation. |
20
+ | **Ubiquitous Language(通用語言)** | 讓名稱與語義在 prompt、規格、程式碼與 review 之間保持穩定。 |
21
+ | **Bounded Context** | 定義術語或規則的有效範圍,防止語義意外洩漏。 |
22
+ | **領域模型(Domain Model)** | 讓行為有明確的所有者,而不是將規則分散在技術層之間。 |
23
+ | **領域規則(Domain Rules)** | 在程式碼生成之前,先明確指出什麼是正確的、允許的、禁止的,以及例外情況。 |
22
24
 
23
- The important shift is where design lives. In older workflows, much of the real design could remain implicit in code. With AI, that is too late. The model needs constraints before it generates code.
25
+ 關鍵的轉變在於設計的位置。舊有的工作流程中,大量真正的設計可以隱含在程式碼裡。引入 AI 之後,這樣做已經太遲。模型需要在生成程式碼之前就取得約束條件。
24
26
 
25
27
  ```text
26
28
  Without DDD:
@@ -30,6 +32,6 @@ With DDD:
30
32
  Domain meaning -> Structured spec -> AI implementation -> Reviewable code
31
33
  ```
32
34
 
33
- Code still matters, but it is no longer the first place where meaning should be discovered. For AI collaboration, specs become the pre-generation contract, and DDD supplies the language, boundaries, and rules that make the contract precise.
35
+ 程式碼仍然重要,但不再是發現語義的第一現場。在 AI 協作模式下,規格成為生成前的契約,DDD 提供語言、邊界與規則,使契約具備精確性。
34
36
 
35
- Dflow is built around that idea: spec first, domain meaning explicit, AI constrained before implementation, and drift checked before the work is considered done.
37
+ Dflow 正是圍繞這個理念建構:規格優先、領域語義顯式化、在實作前約束 AI,並在工作完成前驗證漂移(drift)。
package/lib/init.js CHANGED
@@ -383,7 +383,16 @@ async function detectProjectSignals(cwd) {
383
383
  name.endsWith('.csproj') ||
384
384
  name === 'program.cs' ||
385
385
  name === 'startup.cs' ||
386
- name === 'package.json'
386
+ name === 'package.json' ||
387
+ name === 'pom.xml' ||
388
+ name === 'build.gradle' ||
389
+ name === 'build.gradle.kts' ||
390
+ name === 'pyproject.toml' ||
391
+ name === 'requirements.txt' ||
392
+ name === 'go.mod' ||
393
+ name === 'cargo.toml' ||
394
+ name === 'composer.json' ||
395
+ name === 'gemfile'
387
396
  );
388
397
 
389
398
  const hasWebFormsFiles = Array.from(baseNames).some((name) =>
@@ -412,9 +421,36 @@ async function detectProjectSignals(cwd) {
412
421
  trackHint = 'brownfield';
413
422
  }
414
423
 
424
+ const stackHints = [];
425
+ if (coreSignal || webFormsSignal || csprojFiles.length > 0) {
426
+ stackHints.push('dotnet');
427
+ }
428
+ if (baseNames.has('pom.xml') || baseNames.has('build.gradle') || baseNames.has('build.gradle.kts')) {
429
+ stackHints.push('java');
430
+ }
431
+ if (baseNames.has('package.json')) {
432
+ stackHints.push('nodejs');
433
+ }
434
+ if (baseNames.has('pyproject.toml') || baseNames.has('requirements.txt')) {
435
+ stackHints.push('python');
436
+ }
437
+ if (baseNames.has('go.mod')) {
438
+ stackHints.push('go');
439
+ }
440
+ if (baseNames.has('cargo.toml')) {
441
+ stackHints.push('rust');
442
+ }
443
+ if (baseNames.has('composer.json')) {
444
+ stackHints.push('php');
445
+ }
446
+ if (baseNames.has('gemfile')) {
447
+ stackHints.push('ruby');
448
+ }
449
+
415
450
  return {
416
451
  hasSourceTree: hasSourceTree || relNames.has('src'),
417
- trackHint
452
+ trackHint,
453
+ stackHints
418
454
  };
419
455
  }
420
456
 
@@ -1104,13 +1140,20 @@ async function readPackagedTemplate(edition, sourceRel) {
1104
1140
  }
1105
1141
  }
1106
1142
 
1143
+ const PLACEHOLDER_ALIASES = {
1144
+ '{Framework version}': ['{ASP.NET Core version}', '{ASP.NET WebForms version}', '{.NET Framework version}'],
1145
+ '{ORM / persistence}': ['{ORM / Data Access}'],
1146
+ '{ORM version}': ['{EF Core version}'],
1147
+ '{Mediator}': ['{MediatR version}']
1148
+ };
1149
+
1107
1150
  function buildSubstitutionMap(cwd, answers) {
1108
1151
  const extracted = extractTechStackPlaceholders(answers.techStackSummary);
1109
1152
  const gitSelection = answers.optionalFiles.filter((key) => key === 'git-trunk' || key === 'git-flow');
1110
1153
  const gitStyle = gitSelection.length === 1 ? (gitSelection[0] === 'git-trunk' ? 'trunk' : 'gitflow') : null;
1111
1154
  const systemName = path.basename(cwd);
1112
1155
 
1113
- return new Map([
1156
+ const map = new Map([
1114
1157
  ['{YYYY-MM-DD}', currentLocalDate()],
1115
1158
  ['{System Name}', systemName],
1116
1159
  ['{系統名稱}', systemName],
@@ -1120,15 +1163,26 @@ function buildSubstitutionMap(cwd, answers) {
1120
1163
  ['{migration-context}', answers.migrationContext],
1121
1164
  ['{prose-language}', answers.proseLanguage],
1122
1165
  ['{dflow-version}', pkg.version],
1123
- ['{ASP.NET Core version}', extracted.aspNetCoreVersion || '{ASP.NET Core version}'],
1124
- ['{EF Core version}', extracted.efCoreVersion || '{EF Core version}'],
1125
- ['{MediatR version}', extracted.mediatRVersion || '{MediatR version}'],
1166
+ ['{Language}', extracted.language || '{Language}'],
1167
+ ['{Framework}', extracted.framework || '{Framework}'],
1168
+ ['{Framework version}', extracted.frameworkVersion || '{Framework version}'],
1169
+ ['{ORM / persistence}', extracted.ormPersistence || '{ORM / persistence}'],
1170
+ ['{ORM version}', extracted.ormVersion || '{ORM version}'],
1171
+ ['{Mediator}', extracted.mediator || '{Mediator}'],
1126
1172
  ['{Test framework}', extracted.testFramework || '{Test framework}'],
1127
- ['{ASP.NET WebForms version}', extracted.webFormsVersion || '{ASP.NET WebForms version}'],
1128
- ['{.NET Framework version}', extracted.dotNetFrameworkVersion || '{.NET Framework version}'],
1129
- ['{ORM / Data Access}', extracted.ormDataAccess || '{ORM / Data Access}'],
1130
1173
  ['{gitflow|trunk}', gitStyle || '{gitflow|trunk}']
1131
1174
  ]);
1175
+
1176
+ for (const [canonical, aliases] of Object.entries(PLACEHOLDER_ALIASES)) {
1177
+ const value = map.get(canonical);
1178
+ if (value === undefined) continue;
1179
+ const canonicalResolved = value !== canonical;
1180
+ for (const alias of aliases) {
1181
+ map.set(alias, canonicalResolved ? value : alias);
1182
+ }
1183
+ }
1184
+
1185
+ return map;
1132
1186
  }
1133
1187
 
1134
1188
  function substitutePlaceholders(content, substitution) {
@@ -1139,19 +1193,104 @@ function substitutePlaceholders(content, substitution) {
1139
1193
  return result;
1140
1194
  }
1141
1195
 
1196
+ const LANGUAGE_PATTERNS = [
1197
+ /\bC#\s*[0-9]+(?:\.[0-9]+)?\b/i,
1198
+ /\bC#\b/,
1199
+ /\bTypeScript\s*[0-9]+(?:\.[0-9]+)?\b/i,
1200
+ /\bTypeScript\b/i,
1201
+ /\bJavaScript\b/i,
1202
+ /\bKotlin\s*[0-9]+(?:\.[0-9]+)?\b/i,
1203
+ /\bKotlin\b/i,
1204
+ /\bJava\s*[0-9]+\b/i,
1205
+ /\bJava\b/i,
1206
+ /\bPython\s*[0-9]+(?:\.[0-9]+)?\b/i,
1207
+ /\bPython\b/i,
1208
+ /\bGolang\b/i,
1209
+ /\bGo\s*[0-9]+(?:\.[0-9]+)?\b/i,
1210
+ /\bPHP\s*[0-9]+(?:\.[0-9]+)?\b/i,
1211
+ /\bPHP\b/i,
1212
+ /\bRuby\b/i
1213
+ ];
1214
+
1215
+ const FRAMEWORK_VERSION_PATTERNS = [
1216
+ /\bASP\.?NET\s+Core\s*[0-9]+(?:\.[0-9]+)?\b/i,
1217
+ /\b(?:ASP\.?NET\s+WebForms|WebForms)(?:\s+[0-9]+(?:\.[0-9]+)?)?\b/i,
1218
+ /\b\.NET\s+Framework\s*[0-9]+(?:\.[0-9]+)?\b/i,
1219
+ /\bSpring\s+Boot\s*[0-9]+(?:\.[0-9]+)?\b/i,
1220
+ /\bSpring\s+MVC\b/i,
1221
+ /\bNestJS\s*[0-9]+(?:\.[0-9]+)?\b/i,
1222
+ /\bFastify\s*[0-9]+(?:\.[0-9]+)?\b/i,
1223
+ /\bExpress(?:\.js)?\s*[0-9]+(?:\.[0-9]+)?\b/i,
1224
+ /\bDjango\s*[0-9]+(?:\.[0-9]+)?\b/i,
1225
+ /\bFastAPI\s*[0-9]+(?:\.[0-9]+)?\b/i,
1226
+ /\bFlask\s*[0-9]+(?:\.[0-9]+)?\b/i,
1227
+ /\bGin\s+v?[0-9]+(?:\.[0-9]+)?\b/i,
1228
+ /\bEcho\s+v?[0-9]+(?:\.[0-9]+)?\b/i,
1229
+ /\bLaravel\s*[0-9]+(?:\.[0-9]+)?\b/i
1230
+ ];
1231
+
1232
+ const FRAMEWORK_PATTERNS = [
1233
+ /\bASP\.?NET\s+Core\b/i,
1234
+ /\b(?:ASP\.?NET\s+WebForms|WebForms)\b/i,
1235
+ /\b\.NET\s+Framework\b/i,
1236
+ /\bSpring\s+Boot\b/i,
1237
+ /\bSpring\s+MVC\b/i,
1238
+ /\bNestJS\b/i,
1239
+ /\bFastify\b/i,
1240
+ /\bExpress(?:\.js)?\b/i,
1241
+ /\bDjango\b/i,
1242
+ /\bFastAPI\b/i,
1243
+ /\bFlask\b/i,
1244
+ /\bGin\b/i,
1245
+ /\bEcho\b/i,
1246
+ /\bLaravel\b/i
1247
+ ];
1248
+
1249
+ const ORM_VERSION_PATTERNS = [
1250
+ /\b(?:EF\s+Core|Entity\s+Framework\s+Core)\s*[0-9]+(?:\.[0-9]+)?\b/i,
1251
+ /\b(?:EF6|Entity\s+Framework\s+6)\b/i,
1252
+ /\bHibernate\s*[0-9]+(?:\.[0-9]+)?\b/i,
1253
+ /\bSpring\s+Data\s+JPA\b/i,
1254
+ /\bJPA\s*[0-9]+(?:\.[0-9]+)?\b/i,
1255
+ /\bSQLAlchemy\s*[0-9]+(?:\.[0-9]+)?\b/i,
1256
+ /\bPrisma\s*[0-9]+(?:\.[0-9]+)?\b/i,
1257
+ /\bTypeORM\s*[0-9]+(?:\.[0-9]+)?\b/i,
1258
+ /\bMikro-?ORM\s*[0-9]+(?:\.[0-9]+)?\b/i,
1259
+ /\bGORM\s+v?[0-9]+(?:\.[0-9]+)?\b/i,
1260
+ /\bEloquent\s*[0-9]+(?:\.[0-9]+)?\b/i,
1261
+ /\bDoctrine\s*[0-9]+(?:\.[0-9]+)?\b/i
1262
+ ];
1263
+
1264
+ const ORM_PERSISTENCE_PATTERNS = [
1265
+ /\b(?:EF\s+Core|Entity\s+Framework\s+Core)\b/i,
1266
+ /\b(?:EF6|Entity\s+Framework\s+6|Dapper|ADO\.NET)\b/i,
1267
+ /\bHibernate\b/i,
1268
+ /\bSpring\s+Data\s+JPA\b/i,
1269
+ /\bJPA\b/i,
1270
+ /\bSQLAlchemy\b/i,
1271
+ /\bSQLModel\b/i,
1272
+ /\bPrisma\b/i,
1273
+ /\bTypeORM\b/i,
1274
+ /\bMikro-?ORM\b/i,
1275
+ /\bGORM\b/i,
1276
+ /\bsqlx\b/i,
1277
+ /\bEloquent\b/i,
1278
+ /\bDoctrine\b/i
1279
+ ];
1280
+
1142
1281
  function extractTechStackPlaceholders(text) {
1143
1282
  if (!text || text.toLowerCase() === 'unknown') {
1144
1283
  return {};
1145
1284
  }
1146
1285
 
1147
1286
  return {
1148
- aspNetCoreVersion: firstMatch(text, /\bASP\.?NET\s+Core\s*[0-9]+(?:\.[0-9]+)?\b/i),
1149
- efCoreVersion: firstMatch(text, /\b(?:EF\s+Core|Entity\s+Framework\s+Core)\s*[0-9]+(?:\.[0-9]+)?\b/i),
1150
- mediatRVersion: firstMatch(text, /\bMediatR\s*[0-9]+(?:\.[0-9]+)?\b/i),
1151
- testFramework: extractTestFramework(text),
1152
- webFormsVersion: firstMatch(text, /\b(?:ASP\.?NET\s+WebForms|WebForms)(?:\s*[0-9]+(?:\.[0-9]+)?)?\b/i),
1153
- dotNetFrameworkVersion: firstMatch(text, /\b\.NET\s+Framework\s*[0-9]+(?:\.[0-9]+)?\b/i),
1154
- ormDataAccess: firstMatch(text, /\b(?:EF6|Entity\s+Framework\s+6|Dapper|ADO\.NET)\b/i)
1287
+ language: firstPatternMatch(text, LANGUAGE_PATTERNS),
1288
+ framework: firstPatternMatch(text, FRAMEWORK_PATTERNS),
1289
+ frameworkVersion: firstPatternMatch(text, FRAMEWORK_VERSION_PATTERNS),
1290
+ ormPersistence: firstPatternMatch(text, ORM_PERSISTENCE_PATTERNS),
1291
+ ormVersion: firstPatternMatch(text, ORM_VERSION_PATTERNS),
1292
+ mediator: firstMatch(text, /\bMediatR\s*[0-9]+(?:\.[0-9]+)?\b/i),
1293
+ testFramework: extractTestFramework(text)
1155
1294
  };
1156
1295
  }
1157
1296
 
@@ -1160,15 +1299,38 @@ function firstMatch(text, regex) {
1160
1299
  return match ? match[0] : null;
1161
1300
  }
1162
1301
 
1163
- function extractTestFramework(text) {
1164
- if (/\bxUnit\b/i.test(text)) {
1165
- return 'xUnit';
1166
- }
1167
- if (/\bNUnit\b/i.test(text)) {
1168
- return 'NUnit';
1302
+ function firstPatternMatch(text, patterns) {
1303
+ for (const pattern of patterns) {
1304
+ const match = text.match(pattern);
1305
+ if (match) {
1306
+ return match[0];
1307
+ }
1169
1308
  }
1170
- if (/\bMSTest\b/i.test(text)) {
1171
- return 'MSTest';
1309
+ return null;
1310
+ }
1311
+
1312
+ function extractTestFramework(text) {
1313
+ const patterns = [
1314
+ [/\bxUnit\b/i, 'xUnit'],
1315
+ [/\bNUnit\b/i, 'NUnit'],
1316
+ [/\bMSTest\b/i, 'MSTest'],
1317
+ [/\bJUnit\s*[0-9]+\b/i, null],
1318
+ [/\bJUnit\b/i, 'JUnit'],
1319
+ [/\bVitest\b/i, 'Vitest'],
1320
+ [/\bJest\b/i, 'Jest'],
1321
+ [/\bMocha\b/i, 'Mocha'],
1322
+ [/\bpytest\b/i, 'pytest'],
1323
+ [/\bunittest\b/i, 'unittest'],
1324
+ [/\bgo\s+test\b/i, 'go test'],
1325
+ [/\bPHPUnit\b/i, 'PHPUnit'],
1326
+ [/\bPest\b/i, 'Pest']
1327
+ ];
1328
+
1329
+ for (const [pattern, name] of patterns) {
1330
+ const match = text.match(pattern);
1331
+ if (match) {
1332
+ return name || match[0];
1333
+ }
1172
1334
  }
1173
1335
  return null;
1174
1336
  }
@@ -1386,6 +1548,7 @@ Recommended next steps:
1386
1548
  - For a new feature, use the Dflow new-feature workflow when it becomes available as a CLI command.
1387
1549
  - For brownfield changes, use the Dflow modify-existing workflow when it becomes available as a CLI command.
1388
1550
  - Before generating more specs, make sure dflow/specs/shared/_conventions.md has the correct Prose Language section.
1551
+ - For stack-specific examples (.NET, Java/Spring, Node/TypeScript, Python, Go, PHP/Laravel), see docs/examples-by-stack.md in the Dflow repo.
1389
1552
  `);
1390
1553
  }
1391
1554
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dflow-sdd-ddd",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Spec-first SDD/DDD workflow kit for AI-assisted development",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -70,7 +70,8 @@ Signals:
70
70
  canonical English; compare against `TEMPLATE-LANGUAGE-GLOSSARY.md` if
71
71
  available.
72
72
  - References to a runtime `/dflow:init-project` slash command (V1
73
- replaced it with the shell command `npx dflow-sdd-ddd init`).
73
+ replaced it with the Dflow CLI init command (`dflow init`, or
74
+ `npx dflow-sdd-ddd init` when using the no-install path)).
74
75
  - A root `CLAUDE.md`, `AGENTS.md`, or equivalent that holds the full
75
76
  Dflow workflow text instead of being a thin shim pointing to this
76
77
  file.