dflow-sdd-ddd 0.4.0 → 0.6.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/lib/init.js CHANGED
@@ -8,6 +8,21 @@ const pkg = require('../package.json');
8
8
  const MIN_NODE_VERSION = '22.0.0';
9
9
  const PACKAGE_ROOT = path.resolve(__dirname, '..');
10
10
  const TEMPLATE_ROOT = path.join(PACKAGE_ROOT, 'templates');
11
+ const COMMAND_REGISTRY_START = '<!-- dflow-command-registry:start -->';
12
+ const COMMAND_REGISTRY_END = '<!-- dflow-command-registry:end -->';
13
+ const EXPECTED_COMMAND_IDS = [
14
+ 'new-feature',
15
+ 'modify-existing',
16
+ 'bug-fix',
17
+ 'new-phase',
18
+ 'finish-feature',
19
+ 'verify',
20
+ 'pr-review',
21
+ 'report-dflow-feedback',
22
+ 'status',
23
+ 'next',
24
+ 'cancel'
25
+ ];
11
26
 
12
27
  const PROSE_LANGUAGE_PATTERN =
13
28
  /^[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}))*$/;
@@ -91,11 +106,6 @@ const AI_AGENT_OPTIONS = [
91
106
  label: 'CLAUDE.md - Claude Code',
92
107
  aliases: ['claude', 'claude.md']
93
108
  },
94
- {
95
- key: 'gemini',
96
- label: 'GEMINI.md - Gemini CLI',
97
- aliases: ['gemini', 'gemini.md']
98
- },
99
109
  {
100
110
  key: 'copilot',
101
111
  label: '.github/copilot-instructions.md - GitHub Copilot',
@@ -251,7 +261,8 @@ async function runConfigureAgents(options = {}) {
251
261
 
252
262
  const plan = await buildConfigureAgentsPlan(cwd, {
253
263
  ...projectContext,
254
- aiAgents
264
+ aiAgents,
265
+ commandAdapters: Boolean(options.commandAdapters)
255
266
  });
256
267
 
257
268
  renderPreview(stdout, plan, []);
@@ -268,7 +279,7 @@ async function runConfigureAgents(options = {}) {
268
279
  result.warnings.push(...collectUnresolvedPlaceholderWarnings(plan, result.created));
269
280
 
270
281
  printResultReport(stdout, result, plan.deferred);
271
- printConfigureAgentsNextSteps(stdout);
282
+ printConfigureAgentsNextSteps(stdout, Boolean(options.commandAdapters));
272
283
  return 0;
273
284
  } catch (error) {
274
285
  if (rl) {
@@ -999,8 +1010,14 @@ async function buildConfigureAgentsPlan(cwd, answers) {
999
1010
  content
1000
1011
  });
1001
1012
 
1013
+ const commandRegistry = answers.commandAdapters ? parseDflowCommandRegistry(content) : [];
1014
+
1002
1015
  for (const agent of answers.aiAgents) {
1003
- await addAiAgentShim(cwd, items, agent, substitution);
1016
+ await addAiAgentShim(cwd, items, agent, substitution, { commandRegistry });
1017
+ }
1018
+
1019
+ if (answers.commandAdapters) {
1020
+ addCommandAdapterItems(items, answers.aiAgents, commandRegistry);
1004
1021
  }
1005
1022
 
1006
1023
  await finalizePlanItems(cwd, items);
@@ -1017,7 +1034,8 @@ async function buildConfigureAgentsPlan(cwd, answers) {
1017
1034
  async function finalizePlanItems(cwd, items) {
1018
1035
  for (const item of items) {
1019
1036
  const absolute = path.join(cwd, item.relativePath);
1020
- item.action = (await pathExists(absolute)) ? 'skip' : 'create';
1037
+ const targetExists = await pathExists(absolute);
1038
+ item.action = targetExists ? (item.overwrite ? 'update' : 'skip') : 'create';
1021
1039
  if (item.action === 'skip') {
1022
1040
  item.notes = item.notes ? `${item.notes}, already exists` : 'already exists';
1023
1041
  }
@@ -1025,15 +1043,23 @@ async function finalizePlanItems(cwd, items) {
1025
1043
  }
1026
1044
  }
1027
1045
 
1028
- async function addAiAgentShim(cwd, items, agent, substitution) {
1046
+ async function addAiAgentShim(cwd, items, agent, substitution, options = {}) {
1029
1047
  const target = getAiAgentTarget(agent);
1030
1048
  const targetPath = path.join(cwd, target.relativePath);
1031
1049
  const targetExists = await pathExists(targetPath);
1032
1050
  const targetConfigured = targetExists && await fileReferencesAiAgentGuide(targetPath);
1033
- const relativePath = targetExists && !targetConfigured ? target.snippetPath : target.relativePath;
1034
- const content = substitutePlaceholders(buildAiAgentShim(target.relativePath), substitution);
1051
+ const commandRegistry = options.commandRegistry || [];
1052
+ const codexCommandAdapterSnippet = target.relativePath === 'AGENTS.md' && commandRegistry.length > 0 && targetExists;
1053
+ const relativePath = codexCommandAdapterSnippet
1054
+ ? (targetConfigured ? 'dflow/specs/shared/AGENTS-md-command-adapters-snippet.md' : target.snippetPath)
1055
+ : (targetExists && !targetConfigured ? target.snippetPath : target.relativePath);
1056
+ const content = substitutePlaceholders(buildAiAgentShim(target.relativePath, options.commandRegistry), substitution);
1035
1057
  let notes = 'selected, tool-specific shim';
1036
- if (targetConfigured) {
1058
+ if (codexCommandAdapterSnippet && targetConfigured) {
1059
+ notes = `selected, ${target.relativePath} already points to AI-AGENT-GUIDE.md; merge this command trigger snippet manually`;
1060
+ } else if (codexCommandAdapterSnippet) {
1061
+ notes = `selected, ${target.relativePath} already exists; merge this command trigger snippet manually`;
1062
+ } else if (targetConfigured) {
1037
1063
  notes = `selected, ${target.relativePath} already points to AI-AGENT-GUIDE.md`;
1038
1064
  } else if (targetExists) {
1039
1065
  notes = `selected, ${target.relativePath} already exists; merge this snippet manually`;
@@ -1043,7 +1069,8 @@ async function addAiAgentShim(cwd, items, agent, substitution) {
1043
1069
  relativePath,
1044
1070
  source: `generated:${agent}-shim`,
1045
1071
  notes,
1046
- content
1072
+ content,
1073
+ overwrite: codexCommandAdapterSnippet
1047
1074
  });
1048
1075
  }
1049
1076
 
@@ -1067,10 +1094,6 @@ function getAiAgentTarget(agent) {
1067
1094
  relativePath: 'CLAUDE.md',
1068
1095
  snippetPath: 'dflow/specs/shared/CLAUDE-md-snippet.md'
1069
1096
  },
1070
- gemini: {
1071
- relativePath: 'GEMINI.md',
1072
- snippetPath: 'dflow/specs/shared/GEMINI-md-snippet.md'
1073
- },
1074
1097
  copilot: {
1075
1098
  relativePath: '.github/copilot-instructions.md',
1076
1099
  snippetPath: 'dflow/specs/shared/copilot-instructions-snippet.md'
@@ -1080,12 +1103,16 @@ function getAiAgentTarget(agent) {
1080
1103
  return targets[agent];
1081
1104
  }
1082
1105
 
1083
- function buildAiAgentShim(targetPath) {
1106
+ function buildAiAgentShim(targetPath, commandRegistry = []) {
1084
1107
  const title = targetPath === '.github/copilot-instructions.md'
1085
1108
  ? 'GitHub Copilot Repository Instructions'
1086
1109
  : `${targetPath} - Dflow Project Instructions`;
1087
1110
 
1088
- const importHint = targetPath === 'CLAUDE.md' || targetPath === 'GEMINI.md'
1111
+ const commandTriggerHint = targetPath === 'AGENTS.md' && commandRegistry.length > 0
1112
+ ? buildCodexCommandTriggerSection(commandRegistry)
1113
+ : '';
1114
+
1115
+ const importHint = targetPath === 'CLAUDE.md'
1089
1116
  ? '\nIf your tool supports Markdown imports, the canonical guide is imported below:\n\n@dflow/specs/shared/AI-AGENT-GUIDE.md\n'
1090
1117
  : '';
1091
1118
 
@@ -1099,10 +1126,200 @@ Before planning or editing code, read and follow:
1099
1126
 
1100
1127
  Keep tool-specific instruction files small. The Dflow guide above is the
1101
1128
  single source of truth for project workflow rules, slash-command behavior,
1102
- spec locations, and SDD/DDD constraints.${importHint}
1129
+ spec locations, and SDD/DDD constraints.${commandTriggerHint}${importHint}
1130
+ `;
1131
+ }
1132
+
1133
+ function addCommandAdapterItems(items, aiAgents, commandRegistry) {
1134
+ if (aiAgents.includes('claude')) {
1135
+ for (const command of commandRegistry) {
1136
+ items.push({
1137
+ relativePath: `.claude/commands/dflow/${command.id}.md`,
1138
+ source: 'generated:claude-command-adapter',
1139
+ notes: 'command adapter, derived from dflow command registry',
1140
+ content: buildThinCommandWrapper(command, `/dflow:${command.id}`),
1141
+ overwrite: true
1142
+ });
1143
+ }
1144
+ }
1145
+
1146
+ if (aiAgents.includes('copilot')) {
1147
+ for (const command of commandRegistry) {
1148
+ items.push({
1149
+ relativePath: `.github/prompts/dflow-${command.id}.prompt.md`,
1150
+ source: 'generated:copilot-command-adapter',
1151
+ notes: 'command adapter, derived from dflow command registry',
1152
+ content: buildThinCommandWrapper(command, `/dflow-${command.id}`),
1153
+ overwrite: true
1154
+ });
1155
+ }
1156
+ }
1157
+ }
1158
+
1159
+ function buildCodexCommandTriggerSection(commandRegistry) {
1160
+ const triggers = commandRegistry
1161
+ .map((command) => {
1162
+ const commandKind = command.scope === 'control' ? 'command' : 'workflow';
1163
+ return `- \`${command.label}\` as text, or "Run the Dflow ${command.label} ${commandKind}."`;
1164
+ })
1165
+ .join('\n');
1166
+
1167
+ return `
1168
+
1169
+ ## Dflow Text Triggers
1170
+
1171
+ Codex does not install Dflow command files. When the developer asks for a
1172
+ canonical Dflow command, treat it as a text trigger, read the canonical guide,
1173
+ and execute the matching workflow or control command.
1174
+
1175
+ If the CLI intercepts a slash-prefixed Dflow name such as \`/dflow:status\` as
1176
+ an unknown command, the developer may resend it without the slash, for example
1177
+ \`dflow:status\`. Treat that as the same Dflow text trigger, read the guide,
1178
+ and execute it.
1179
+
1180
+ Recognized canonical triggers:
1181
+
1182
+ ${triggers}
1183
+ `;
1184
+ }
1185
+
1186
+ function buildThinCommandWrapper(command, displayName = command.label) {
1187
+ const argHint = command.argHint === '-'
1188
+ ? 'Argument hint: none.'
1189
+ : `Argument hint: ${command.argHint}.`;
1190
+
1191
+ return `# ${displayName}
1192
+
1193
+ Execute the canonical \`${command.label}\` Dflow workflow or control command.
1194
+
1195
+ Definition: \`dflow/specs/shared/AI-AGENT-GUIDE.md\`
1196
+
1197
+ ${argHint}
1103
1198
  `;
1104
1199
  }
1105
1200
 
1201
+ function parseDflowCommandRegistry(content) {
1202
+ const start = content.indexOf(COMMAND_REGISTRY_START);
1203
+ const end = content.indexOf(COMMAND_REGISTRY_END);
1204
+
1205
+ if (start === -1 || end === -1 || end <= start) {
1206
+ throw new InitError('Internal error: dflow command registry markers not found in packaged AI-AGENT-GUIDE.md.');
1207
+ }
1208
+
1209
+ const registryContent = content.slice(start + COMMAND_REGISTRY_START.length, end);
1210
+ const tableLines = registryContent
1211
+ .split(/\r?\n/)
1212
+ .map((line) => line.trim())
1213
+ .filter((line) => line.startsWith('|') && line.endsWith('|'));
1214
+
1215
+ if (tableLines.length < 3) {
1216
+ throw new InitError('Internal error: dflow command registry table is empty.');
1217
+ }
1218
+
1219
+ const header = parseMarkdownTableRow(tableLines[0]).map((cell) => cell.toLowerCase());
1220
+ const expectedHeader = ['id', 'label', 'description', 'arg-hint', 'scope'];
1221
+ if (header.length !== expectedHeader.length || !expectedHeader.every((cell, index) => header[index] === cell)) {
1222
+ throw new InitError('Internal error: dflow command registry header must be: id, label, description, arg-hint, scope.');
1223
+ }
1224
+
1225
+ const delimiter = parseMarkdownTableRow(tableLines[1]);
1226
+ if (delimiter.length !== expectedHeader.length || !delimiter.every((cell) => /^:?-{3,}:?$/.test(cell))) {
1227
+ throw new InitError('Internal error: dflow command registry delimiter row is invalid.');
1228
+ }
1229
+
1230
+ const commands = [];
1231
+ const seen = new Set();
1232
+
1233
+ for (const line of tableLines.slice(2)) {
1234
+ const cells = parseMarkdownTableRow(line);
1235
+ if (cells.length !== expectedHeader.length) {
1236
+ throw new InitError(`Internal error: invalid dflow command registry row: ${line}`);
1237
+ }
1238
+
1239
+ const command = {
1240
+ id: stripInlineCode(cells[0]),
1241
+ label: stripInlineCode(cells[1]),
1242
+ description: stripInlineCode(cells[2]),
1243
+ argHint: stripInlineCode(cells[3]),
1244
+ scope: stripInlineCode(cells[4])
1245
+ };
1246
+
1247
+ validateDflowCommandRegistryRow(command, seen);
1248
+ commands.push(command);
1249
+ }
1250
+
1251
+ const actualIds = commands.map((command) => command.id);
1252
+ const missingIds = EXPECTED_COMMAND_IDS.filter((id) => !actualIds.includes(id));
1253
+ const extraIds = actualIds.filter((id) => !EXPECTED_COMMAND_IDS.includes(id));
1254
+ if (missingIds.length > 0 || extraIds.length > 0 || commands.length !== EXPECTED_COMMAND_IDS.length) {
1255
+ throw new InitError(
1256
+ `Internal error: dflow command registry must contain exactly these command ids: ${EXPECTED_COMMAND_IDS.join(', ')}.`
1257
+ );
1258
+ }
1259
+
1260
+ return commands;
1261
+ }
1262
+
1263
+ function parseMarkdownTableRow(line) {
1264
+ const trimmed = line.trim();
1265
+ const inner = trimmed.slice(1, -1);
1266
+ const cells = [];
1267
+ let current = '';
1268
+
1269
+ for (let index = 0; index < inner.length; index += 1) {
1270
+ const char = inner[index];
1271
+ const next = inner[index + 1];
1272
+
1273
+ if (char === '\\' && next === '|') {
1274
+ current += '|';
1275
+ index += 1;
1276
+ continue;
1277
+ }
1278
+
1279
+ if (char === '|') {
1280
+ cells.push(current.trim());
1281
+ current = '';
1282
+ continue;
1283
+ }
1284
+
1285
+ current += char;
1286
+ }
1287
+
1288
+ cells.push(current.trim());
1289
+ return cells;
1290
+ }
1291
+
1292
+ function stripInlineCode(value) {
1293
+ const trimmed = String(value || '').trim();
1294
+ if (trimmed.startsWith('`') && trimmed.endsWith('`') && trimmed.length >= 2) {
1295
+ return trimmed.slice(1, -1);
1296
+ }
1297
+ return trimmed;
1298
+ }
1299
+
1300
+ function validateDflowCommandRegistryRow(command, seen) {
1301
+ if (!/^[a-z][a-z0-9-]*$/.test(command.id)) {
1302
+ throw new InitError(`Internal error: invalid dflow command id: ${command.id}`);
1303
+ }
1304
+ if (seen.has(command.id)) {
1305
+ throw new InitError(`Internal error: duplicate dflow command id: ${command.id}`);
1306
+ }
1307
+ seen.add(command.id);
1308
+
1309
+ if (command.label !== `/dflow:${command.id}`) {
1310
+ throw new InitError(`Internal error: dflow command label must be /dflow:${command.id}.`);
1311
+ }
1312
+ if (!command.description) {
1313
+ throw new InitError(`Internal error: dflow command ${command.id} is missing a description.`);
1314
+ }
1315
+ if (!command.argHint) {
1316
+ throw new InitError(`Internal error: dflow command ${command.id} is missing an arg-hint.`);
1317
+ }
1318
+ if (!command.scope) {
1319
+ throw new InitError(`Internal error: dflow command ${command.id} is missing a scope.`);
1320
+ }
1321
+ }
1322
+
1106
1323
  function buildDeferredItems(edition) {
1107
1324
  const deferred = [...DEFERRED_COMMON];
1108
1325
  if (edition === 'greenfield') {
@@ -1433,6 +1650,7 @@ function renderPreview(stdout, plan, warnings) {
1433
1650
  async function writeFilePlan(cwd, plan) {
1434
1651
  const result = {
1435
1652
  created: [],
1653
+ updated: [],
1436
1654
  skipped: [],
1437
1655
  warnings: []
1438
1656
  };
@@ -1442,6 +1660,13 @@ async function writeFilePlan(cwd, plan) {
1442
1660
 
1443
1661
  try {
1444
1662
  if (await pathExists(targetPath)) {
1663
+ if (item.overwrite) {
1664
+ await fs.mkdir(path.dirname(targetPath), { recursive: true });
1665
+ await fs.writeFile(targetPath, item.content);
1666
+ result.updated.push(item.relativePath);
1667
+ continue;
1668
+ }
1669
+
1445
1670
  result.skipped.push(item.relativePath);
1446
1671
  result.warnings.push(`Skipped existing target: ${item.relativePath}`);
1447
1672
  if (item.relativePath === 'dflow/specs/shared/_conventions.md') {
@@ -1528,6 +1753,9 @@ function printResultReport(stdout, result, deferred) {
1528
1753
  stdout.write('\nCreated:\n');
1529
1754
  printList(stdout, result.created);
1530
1755
 
1756
+ stdout.write('\nUpdated:\n');
1757
+ printList(stdout, result.updated);
1758
+
1531
1759
  stdout.write('\nSkipped:\n');
1532
1760
  printList(stdout, result.skipped);
1533
1761
 
@@ -1552,7 +1780,11 @@ Recommended next steps:
1552
1780
  `);
1553
1781
  }
1554
1782
 
1555
- function printConfigureAgentsNextSteps(stdout) {
1783
+ function printConfigureAgentsNextSteps(stdout, commandAdapters = false) {
1784
+ 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, manually remove stale `.claude/commands/dflow/dflow-*.md` files so Claude Code does not show both old and new command names.\n'
1786
+ : '';
1787
+
1556
1788
  stdout.write(`
1557
1789
  Dflow AI agent configuration complete.
1558
1790
 
@@ -1560,6 +1792,7 @@ Recommended next steps:
1560
1792
  - Keep AI-agent-specific root files small.
1561
1793
  - Put durable workflow changes in dflow/specs/shared/AI-AGENT-GUIDE.md.
1562
1794
  - If a merge snippet was created, review it and merge the pointer into the existing tool instruction file.
1795
+ ${commandAdapterStep}
1563
1796
  `);
1564
1797
  }
1565
1798
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dflow-sdd-ddd",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Spec-first SDD/DDD workflow kit for AI-assisted development",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -31,6 +31,45 @@ are not available in the current AI tool:
31
31
  | `/dflow:verify` | Specs, domain docs, implementation, and tests need consistency checks. |
32
32
  | `/dflow:pr-review` | A change is ready for SDD/DDD review. |
33
33
  | `/dflow:report-dflow-feedback` | You found a Dflow issue or improvement and want a sanitized upstream feedback draft. |
34
+ | `/dflow:status` | You need the current workflow state, current step, completed work, in-progress work, remaining work, pending decision, and next valid action. |
35
+ | `/dflow:next` | An active workflow is waiting at a step gate and the developer confirms continuing to the next step. |
36
+ | `/dflow:cancel` | The developer wants to abort the current workflow and return to free conversation without rollback. |
37
+
38
+ Machine-readable source for rendering tool-specific thin wrappers:
39
+
40
+ <!-- dflow-command-registry:start -->
41
+ | id | label | description | arg-hint | scope |
42
+ |---|---|---|---|---|
43
+ | new-feature | /dflow:new-feature | Start a new user-visible feature or business behavior. | feature request | workflow |
44
+ | modify-existing | /dflow:modify-existing | Change existing behavior. | change request | workflow |
45
+ | bug-fix | /dflow:bug-fix | Investigate a defect described by expected vs actual behavior. | expected vs actual | workflow |
46
+ | new-phase | /dflow:new-phase | Add another implementation slice to an active feature. | feature id or phase goal | workflow |
47
+ | finish-feature | /dflow:finish-feature | Close implementation with drift checks and archived feature state. | feature id | workflow |
48
+ | verify | /dflow:verify | Check specs, domain docs, implementation, and tests for consistency. | area or feature id | workflow |
49
+ | pr-review | /dflow:pr-review | Review a ready change for SDD/DDD alignment. | change or branch | workflow |
50
+ | report-dflow-feedback | /dflow:report-dflow-feedback | Draft sanitized upstream feedback about Dflow. | issue or improvement | workflow |
51
+ | status | /dflow:status | Report current workflow state and next valid action. | - | control |
52
+ | next | /dflow:next | Confirm the active step gate and continue. | - | control |
53
+ | cancel | /dflow:cancel | Abort the active workflow and return to free conversation. | - | control |
54
+ <!-- dflow-command-registry:end -->
55
+
56
+ ## Status / Control Commands
57
+
58
+ `/dflow:status` reports active workflow state. Include these fields: workflow,
59
+ step, completed, in-progress, remaining, pending decision, and next valid action.
60
+ If no workflow is active, say that no workflow is active and list valid flow-entry
61
+ or standalone commands.
62
+
63
+ `/dflow:next` is valid only at a step gate in an active workflow. Treat it as
64
+ developer confirmation equivalent to "OK" or "continue", then move to the next
65
+ workflow step.
66
+
67
+ `/dflow:cancel` aborts the current workflow and returns to free conversation.
68
+ Do not rollback changes, delete artifacts, or rewrite specs merely because the
69
+ workflow was cancelled.
70
+
71
+ When no workflow is active, `/dflow:next` and `/dflow:cancel` must report that
72
+ there is no active workflow to advance or cancel.
34
73
 
35
74
  ## Source of Truth
36
75
 
@@ -85,9 +124,9 @@ review is required.
85
124
  ## Tool-Specific Notes
86
125
 
87
126
  This file is the canonical Dflow guide. Root-level files such as
88
- `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, and `.github/copilot-instructions.md`
127
+ `AGENTS.md`, `CLAUDE.md`, and `.github/copilot-instructions.md`
89
128
  should stay thin and point back here.
90
129
 
91
130
  If a tool does not support Dflow slash commands, treat the command names as
92
- plain workflow names and execute the matching process from the Dflow skill
93
- source in this repository.
131
+ plain workflow names. This guide contains the installed runtime behavior
132
+ contract; execute the workflow semantics defined here directly.
@@ -31,6 +31,45 @@ are not available in the current AI tool:
31
31
  | `/dflow:verify` | Specs, domain docs, implementation, and tests need consistency checks. |
32
32
  | `/dflow:pr-review` | A change is ready for SDD/DDD review. |
33
33
  | `/dflow:report-dflow-feedback` | You found a Dflow issue or improvement and want a sanitized upstream feedback draft. |
34
+ | `/dflow:status` | You need the current workflow state, current step, completed work, in-progress work, remaining work, pending decision, and next valid action. |
35
+ | `/dflow:next` | An active workflow is waiting at a step gate and the developer confirms continuing to the next step. |
36
+ | `/dflow:cancel` | The developer wants to abort the current workflow and return to free conversation without rollback. |
37
+
38
+ Machine-readable source for rendering tool-specific thin wrappers:
39
+
40
+ <!-- dflow-command-registry:start -->
41
+ | id | label | description | arg-hint | scope |
42
+ |---|---|---|---|---|
43
+ | new-feature | /dflow:new-feature | Start a new user-visible feature or business behavior. | feature request | workflow |
44
+ | modify-existing | /dflow:modify-existing | Change existing behavior. | change request | workflow |
45
+ | bug-fix | /dflow:bug-fix | Investigate a defect described by expected vs actual behavior. | expected vs actual | workflow |
46
+ | new-phase | /dflow:new-phase | Add another implementation slice to an active feature. | feature id or phase goal | workflow |
47
+ | finish-feature | /dflow:finish-feature | Close implementation with drift checks and archived feature state. | feature id | workflow |
48
+ | verify | /dflow:verify | Check specs, domain docs, implementation, and tests for consistency. | area or feature id | workflow |
49
+ | pr-review | /dflow:pr-review | Review a ready change for SDD/DDD alignment. | change or branch | workflow |
50
+ | report-dflow-feedback | /dflow:report-dflow-feedback | Draft sanitized upstream feedback about Dflow. | issue or improvement | workflow |
51
+ | status | /dflow:status | Report current workflow state and next valid action. | - | control |
52
+ | next | /dflow:next | Confirm the active step gate and continue. | - | control |
53
+ | cancel | /dflow:cancel | Abort the active workflow and return to free conversation. | - | control |
54
+ <!-- dflow-command-registry:end -->
55
+
56
+ ## Status / Control Commands
57
+
58
+ `/dflow:status` reports active workflow state. Include these fields: workflow,
59
+ step, completed, in-progress, remaining, pending decision, and next valid action.
60
+ If no workflow is active, say that no workflow is active and list valid flow-entry
61
+ or standalone commands.
62
+
63
+ `/dflow:next` is valid only at a step gate in an active workflow. Treat it as
64
+ developer confirmation equivalent to "OK" or "continue", then move to the next
65
+ workflow step.
66
+
67
+ `/dflow:cancel` aborts the current workflow and returns to free conversation.
68
+ Do not rollback changes, delete artifacts, or rewrite specs merely because the
69
+ workflow was cancelled.
70
+
71
+ When no workflow is active, `/dflow:next` and `/dflow:cancel` must report that
72
+ there is no active workflow to advance or cancel.
34
73
 
35
74
  ## Source of Truth
36
75
 
@@ -85,9 +124,9 @@ review is required.
85
124
  ## Tool-Specific Notes
86
125
 
87
126
  This file is the canonical Dflow guide. Root-level files such as
88
- `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, and `.github/copilot-instructions.md`
127
+ `AGENTS.md`, `CLAUDE.md`, and `.github/copilot-instructions.md`
89
128
  should stay thin and point back here.
90
129
 
91
130
  If a tool does not support Dflow slash commands, treat the command names as
92
- plain workflow names and execute the matching process from the Dflow skill
93
- source in this repository.
131
+ plain workflow names. This guide contains the installed runtime behavior
132
+ contract; execute the workflow semantics defined here directly.
@@ -109,6 +109,7 @@ AI 的完整決策樹、Workflow Transparency、Ceremony Scaling 三層判準
109
109
  - `/dflow:finish-feature` — Feature 收尾 + 整合摘要
110
110
  - `/dflow:pr-review` — PR 審查檢查點
111
111
  - `/dflow:report-dflow-feedback` — 草擬給 Dflow upstream 的已清理回饋,不自動送出
112
+ - `/dflow:status` / `/dflow:next` / `/dflow:cancel` — 狀態管理
112
113
 
113
114
  ### Core Principles (Project Reaffirmed)
114
115