sddx-workflow 0.9.1 → 0.10.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/README.md +249 -63
- package/dist/cli.js +370 -129
- package/package.json +1 -1
- package/templates/AGENTS.md +38 -5
- package/templates/CLAUDE.md +76 -5
- package/templates/claude-commands/conventions-sync.md +3 -0
- package/templates/claude-commands/impl-gap.md +10 -0
- package/templates/claude-commands/research.md +7 -0
- package/templates/claude-commands/review.md +1 -1
- package/templates/claude-commands/scan.md +5 -0
- package/templates/claude-commands/spec-amend.md +7 -0
- package/templates/claude-commands/spec-analyze.md +5 -0
- package/templates/claude-commands/spec-clarify.md +5 -0
- package/templates/claude-commands/spec-conflicts.md +3 -0
- package/templates/claude-commands/spec-status.md +3 -0
- package/templates/claude-commands/spec-tasks.md +3 -3
- package/templates/claude-commands/verify.md +5 -0
- package/templates/codex-skills/conventions-sync/SKILL.md +8 -0
- package/templates/codex-skills/impl-gap/SKILL.md +10 -0
- package/templates/codex-skills/research/SKILL.md +10 -0
- package/templates/codex-skills/review/SKILL.md +2 -2
- package/templates/codex-skills/scan/SKILL.md +8 -0
- package/templates/codex-skills/spec-amend/SKILL.md +10 -0
- package/templates/codex-skills/spec-analyze/SKILL.md +8 -0
- package/templates/codex-skills/spec-clarify/SKILL.md +8 -0
- package/templates/codex-skills/spec-conflicts/SKILL.md +8 -0
- package/templates/codex-skills/spec-status/SKILL.md +8 -0
- package/templates/codex-skills/spec-tasks/SKILL.md +3 -3
- package/templates/codex-skills/verify/SKILL.md +8 -0
- package/templates/conventions/base.md +7 -0
- package/templates/copilot-instructions.md +35 -5
- package/templates/copilot-prompts/conventions-sync.prompt.md +8 -0
- package/templates/copilot-prompts/impl-gap.prompt.md +10 -0
- package/templates/copilot-prompts/research.prompt.md +10 -0
- package/templates/copilot-prompts/review.prompt.md +2 -2
- package/templates/copilot-prompts/scan.prompt.md +8 -0
- package/templates/copilot-prompts/spec-amend.prompt.md +10 -0
- package/templates/copilot-prompts/spec-analyze.prompt.md +8 -0
- package/templates/copilot-prompts/spec-clarify.prompt.md +8 -0
- package/templates/copilot-prompts/spec-conflicts.prompt.md +8 -0
- package/templates/copilot-prompts/spec-status.prompt.md +8 -0
- package/templates/copilot-prompts/spec-tasks.prompt.md +3 -3
- package/templates/copilot-prompts/verify.prompt.md +8 -0
- package/templates/cursor-rules/sddx-workflow.mdc +34 -5
- package/templates/gemini-commands/ask.toml +8 -0
- package/templates/gemini-commands/assume.toml +10 -0
- package/templates/gemini-commands/bootstrap.toml +9 -0
- package/templates/gemini-commands/bugfix.toml +10 -0
- package/templates/gemini-commands/conventions-sync.toml +6 -0
- package/templates/gemini-commands/finish.toml +8 -0
- package/templates/gemini-commands/impl-gap.toml +10 -0
- package/templates/gemini-commands/refactor.toml +8 -0
- package/templates/gemini-commands/research.toml +10 -0
- package/templates/gemini-commands/review.toml +8 -0
- package/templates/gemini-commands/scan.toml +6 -0
- package/templates/gemini-commands/spec-amend.toml +10 -0
- package/templates/gemini-commands/spec-analyze.toml +6 -0
- package/templates/gemini-commands/spec-clarify.toml +6 -0
- package/templates/gemini-commands/spec-conflicts.toml +6 -0
- package/templates/gemini-commands/spec-new.toml +7 -0
- package/templates/gemini-commands/spec-plan.toml +7 -0
- package/templates/gemini-commands/spec-status.toml +6 -0
- package/templates/gemini-commands/spec-tasks.toml +8 -0
- package/templates/gemini-commands/verify.toml +6 -0
- package/templates/gemini.md +37 -5
- package/templates/specs/_template/1-requirements.md +19 -0
- package/templates/specs/_template/2-plan.md +14 -0
- package/templates/specs/_template/2a-data-model.md +29 -0
- package/templates/specs/_template/2b-api-contracts.md +27 -0
- package/templates/specs/_template/2c-research.md +25 -0
- package/templates/specs/_template/3-tasks.md +2 -1
- package/templates/specs/_template/amendments.md +15 -0
- package/templates/specs/_template/analysis.md +49 -0
- package/templates/specs/_template/impl-gaps.md +15 -0
- package/templates/specs/_template/verify-report.md +43 -0
- package/templates/windsurf-rules/sddx-workflow.md +34 -5
- package/templates/windsurf-workflows/ask.md +9 -0
- package/templates/windsurf-workflows/assume.md +11 -0
- package/templates/windsurf-workflows/bootstrap.md +10 -0
- package/templates/windsurf-workflows/bugfix.md +11 -0
- package/templates/windsurf-workflows/conventions-sync.md +7 -0
- package/templates/windsurf-workflows/finish.md +9 -0
- package/templates/windsurf-workflows/impl-gap.md +9 -0
- package/templates/windsurf-workflows/refactor.md +9 -0
- package/templates/windsurf-workflows/research.md +9 -0
- package/templates/windsurf-workflows/review.md +9 -0
- package/templates/windsurf-workflows/scan.md +7 -0
- package/templates/windsurf-workflows/spec-amend.md +9 -0
- package/templates/windsurf-workflows/spec-analyze.md +9 -0
- package/templates/windsurf-workflows/spec-clarify.md +9 -0
- package/templates/windsurf-workflows/spec-conflicts.md +7 -0
- package/templates/windsurf-workflows/spec-new.md +10 -0
- package/templates/windsurf-workflows/spec-plan.md +10 -0
- package/templates/windsurf-workflows/spec-status.md +7 -0
- package/templates/windsurf-workflows/spec-tasks.md +11 -0
- package/templates/windsurf-workflows/verify.md +9 -0
- package/templates/workflow.md +319 -25
- package/templates/zed-rules/sddx-workflow.md +33 -5
package/dist/cli.js
CHANGED
|
@@ -40,41 +40,88 @@ function ensureDir(dir) {
|
|
|
40
40
|
import_fs.default.mkdirSync(dir, { recursive: true });
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
+
function displayPath(filePath) {
|
|
44
|
+
const relative = import_path.default.relative(process.cwd(), filePath);
|
|
45
|
+
return relative && !relative.startsWith("..") ? relative : filePath;
|
|
46
|
+
}
|
|
43
47
|
function copyTemplate(src, dest, force) {
|
|
44
|
-
const
|
|
45
|
-
if (
|
|
46
|
-
console.log(` skip ${dest}`);
|
|
48
|
+
const exists2 = import_fs.default.existsSync(dest);
|
|
49
|
+
if (exists2 && !force) {
|
|
50
|
+
console.log(` skip ${displayPath(dest)}`);
|
|
47
51
|
return;
|
|
48
52
|
}
|
|
49
53
|
import_fs.default.copyFileSync(import_path.default.join(TEMPLATES_DIR, src), dest);
|
|
50
|
-
console.log(` ${
|
|
54
|
+
console.log(` ${exists2 ? "overwrite" : "create "} ${displayPath(dest)}`);
|
|
51
55
|
}
|
|
52
56
|
|
|
53
|
-
// src/commands/
|
|
57
|
+
// src/commands/command-names.ts
|
|
58
|
+
var COMMAND_NAMES = [
|
|
59
|
+
"bootstrap",
|
|
60
|
+
"ask",
|
|
61
|
+
"assume",
|
|
62
|
+
"bugfix",
|
|
63
|
+
"refactor",
|
|
64
|
+
"spec-new",
|
|
65
|
+
"spec-plan",
|
|
66
|
+
"spec-tasks",
|
|
67
|
+
"review",
|
|
68
|
+
"finish",
|
|
69
|
+
"spec-amend",
|
|
70
|
+
"impl-gap",
|
|
71
|
+
"research",
|
|
72
|
+
"verify",
|
|
73
|
+
"scan",
|
|
74
|
+
"conventions-sync",
|
|
75
|
+
"spec-status",
|
|
76
|
+
"spec-conflicts",
|
|
77
|
+
"spec-clarify",
|
|
78
|
+
"spec-analyze"
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
// src/providers.ts
|
|
54
82
|
var CORE_FILES = [
|
|
55
83
|
{ src: "workflow.md", dest: ".sdd/workflow.md" },
|
|
56
84
|
{ src: "project-overview.md", dest: ".sdd/project-overview.md" },
|
|
57
85
|
{ src: "conventions/base.md", dest: ".sdd/conventions.md" },
|
|
58
|
-
{ src: "CLAUDE.md", dest: "CLAUDE.md" },
|
|
59
86
|
{ src: "specs/_template/1-requirements.md", dest: "specs/_template/1-requirements.md" },
|
|
60
87
|
{ src: "specs/_template/2-plan.md", dest: "specs/_template/2-plan.md" },
|
|
61
|
-
{ src: "specs/_template/3-tasks.md", dest: "specs/_template/3-tasks.md" }
|
|
88
|
+
{ src: "specs/_template/3-tasks.md", dest: "specs/_template/3-tasks.md" },
|
|
89
|
+
{ src: "specs/_template/amendments.md", dest: "specs/_template/amendments.md" },
|
|
90
|
+
{ src: "specs/_template/impl-gaps.md", dest: "specs/_template/impl-gaps.md" },
|
|
91
|
+
{ src: "specs/_template/verify-report.md", dest: "specs/_template/verify-report.md" },
|
|
92
|
+
{ src: "specs/_template/analysis.md", dest: "specs/_template/analysis.md" },
|
|
93
|
+
{ src: "specs/_template/2a-data-model.md", dest: "specs/_template/2a-data-model.md" },
|
|
94
|
+
{ src: "specs/_template/2b-api-contracts.md", dest: "specs/_template/2b-api-contracts.md" },
|
|
95
|
+
{ src: "specs/_template/2c-research.md", dest: "specs/_template/2c-research.md" }
|
|
62
96
|
];
|
|
97
|
+
var claudeCommandFiles = COMMAND_NAMES.map((name) => ({
|
|
98
|
+
src: `claude-commands/${name}.md`,
|
|
99
|
+
dest: `.claude/commands/${name}.md`
|
|
100
|
+
}));
|
|
101
|
+
var copilotPromptFiles = COMMAND_NAMES.map((name) => ({
|
|
102
|
+
src: `copilot-prompts/${name}.prompt.md`,
|
|
103
|
+
dest: `.github/prompts/${name}.prompt.md`
|
|
104
|
+
}));
|
|
105
|
+
var geminiCommandFiles = COMMAND_NAMES.map((name) => ({
|
|
106
|
+
src: `gemini-commands/${name}.toml`,
|
|
107
|
+
dest: `.gemini/commands/${name}.toml`
|
|
108
|
+
}));
|
|
109
|
+
var windsurfWorkflowFiles = COMMAND_NAMES.map((name) => ({
|
|
110
|
+
src: `windsurf-workflows/${name}.md`,
|
|
111
|
+
dest: `.windsurf/workflows/${name}.md`
|
|
112
|
+
}));
|
|
113
|
+
var codexSkillDirs = COMMAND_NAMES.map((name) => `.agents/skills/${name}`);
|
|
114
|
+
var codexSkillFiles = COMMAND_NAMES.map((name) => ({
|
|
115
|
+
src: `codex-skills/${name}/SKILL.md`,
|
|
116
|
+
dest: `.agents/skills/${name}/SKILL.md`
|
|
117
|
+
}));
|
|
63
118
|
var PROVIDERS = {
|
|
64
119
|
"claude-code": {
|
|
65
120
|
name: "Claude Code",
|
|
66
121
|
dirs: [".claude/commands"],
|
|
67
122
|
files: [
|
|
68
|
-
{ src: "
|
|
69
|
-
|
|
70
|
-
{ src: "claude-commands/assume.md", dest: ".claude/commands/assume.md" },
|
|
71
|
-
{ src: "claude-commands/bugfix.md", dest: ".claude/commands/bugfix.md" },
|
|
72
|
-
{ src: "claude-commands/refactor.md", dest: ".claude/commands/refactor.md" },
|
|
73
|
-
{ src: "claude-commands/spec-new.md", dest: ".claude/commands/spec-new.md" },
|
|
74
|
-
{ src: "claude-commands/spec-plan.md", dest: ".claude/commands/spec-plan.md" },
|
|
75
|
-
{ src: "claude-commands/spec-tasks.md", dest: ".claude/commands/spec-tasks.md" },
|
|
76
|
-
{ src: "claude-commands/review.md", dest: ".claude/commands/review.md" },
|
|
77
|
-
{ src: "claude-commands/finish.md", dest: ".claude/commands/finish.md" }
|
|
123
|
+
{ src: "CLAUDE.md", dest: "CLAUDE.md" },
|
|
124
|
+
...claudeCommandFiles
|
|
78
125
|
]
|
|
79
126
|
},
|
|
80
127
|
cursor: {
|
|
@@ -86,61 +133,34 @@ var PROVIDERS = {
|
|
|
86
133
|
},
|
|
87
134
|
windsurf: {
|
|
88
135
|
name: "Windsurf",
|
|
89
|
-
dirs: [".windsurf/rules"],
|
|
136
|
+
dirs: [".windsurf/rules", ".windsurf/workflows"],
|
|
90
137
|
files: [
|
|
91
|
-
{ src: "windsurf-rules/sddx-workflow.md", dest: ".windsurf/rules/sddx-workflow.md" }
|
|
138
|
+
{ src: "windsurf-rules/sddx-workflow.md", dest: ".windsurf/rules/sddx-workflow.md" },
|
|
139
|
+
...windsurfWorkflowFiles
|
|
92
140
|
]
|
|
93
141
|
},
|
|
94
142
|
copilot: {
|
|
95
143
|
name: "GitHub Copilot",
|
|
96
144
|
dirs: [".github/prompts"],
|
|
97
145
|
files: [
|
|
98
|
-
|
|
99
|
-
{ src: "copilot-prompts/ask.prompt.md", dest: ".github/prompts/ask.prompt.md" },
|
|
100
|
-
{ src: "copilot-prompts/assume.prompt.md", dest: ".github/prompts/assume.prompt.md" },
|
|
101
|
-
{ src: "copilot-prompts/bugfix.prompt.md", dest: ".github/prompts/bugfix.prompt.md" },
|
|
102
|
-
{ src: "copilot-prompts/refactor.prompt.md", dest: ".github/prompts/refactor.prompt.md" },
|
|
103
|
-
{ src: "copilot-prompts/spec-new.prompt.md", dest: ".github/prompts/spec-new.prompt.md" },
|
|
104
|
-
{ src: "copilot-prompts/spec-plan.prompt.md", dest: ".github/prompts/spec-plan.prompt.md" },
|
|
105
|
-
{ src: "copilot-prompts/spec-tasks.prompt.md", dest: ".github/prompts/spec-tasks.prompt.md" },
|
|
106
|
-
{ src: "copilot-prompts/review.prompt.md", dest: ".github/prompts/review.prompt.md" },
|
|
107
|
-
{ src: "copilot-prompts/finish.prompt.md", dest: ".github/prompts/finish.prompt.md" },
|
|
146
|
+
...copilotPromptFiles,
|
|
108
147
|
{ src: "copilot-instructions.md", dest: ".github/copilot-instructions.md" }
|
|
109
148
|
]
|
|
110
149
|
},
|
|
111
150
|
codex: {
|
|
112
151
|
name: "OpenAI Codex",
|
|
113
|
-
dirs:
|
|
114
|
-
".agents/skills/bootstrap",
|
|
115
|
-
".agents/skills/ask",
|
|
116
|
-
".agents/skills/assume",
|
|
117
|
-
".agents/skills/bugfix",
|
|
118
|
-
".agents/skills/refactor",
|
|
119
|
-
".agents/skills/spec-new",
|
|
120
|
-
".agents/skills/spec-plan",
|
|
121
|
-
".agents/skills/spec-tasks",
|
|
122
|
-
".agents/skills/review",
|
|
123
|
-
".agents/skills/finish"
|
|
124
|
-
],
|
|
152
|
+
dirs: codexSkillDirs,
|
|
125
153
|
files: [
|
|
126
154
|
{ src: "AGENTS.md", dest: "AGENTS.md" },
|
|
127
|
-
|
|
128
|
-
{ src: "codex-skills/ask/SKILL.md", dest: ".agents/skills/ask/SKILL.md" },
|
|
129
|
-
{ src: "codex-skills/assume/SKILL.md", dest: ".agents/skills/assume/SKILL.md" },
|
|
130
|
-
{ src: "codex-skills/bugfix/SKILL.md", dest: ".agents/skills/bugfix/SKILL.md" },
|
|
131
|
-
{ src: "codex-skills/refactor/SKILL.md", dest: ".agents/skills/refactor/SKILL.md" },
|
|
132
|
-
{ src: "codex-skills/spec-new/SKILL.md", dest: ".agents/skills/spec-new/SKILL.md" },
|
|
133
|
-
{ src: "codex-skills/spec-plan/SKILL.md", dest: ".agents/skills/spec-plan/SKILL.md" },
|
|
134
|
-
{ src: "codex-skills/spec-tasks/SKILL.md", dest: ".agents/skills/spec-tasks/SKILL.md" },
|
|
135
|
-
{ src: "codex-skills/review/SKILL.md", dest: ".agents/skills/review/SKILL.md" },
|
|
136
|
-
{ src: "codex-skills/finish/SKILL.md", dest: ".agents/skills/finish/SKILL.md" }
|
|
155
|
+
...codexSkillFiles
|
|
137
156
|
]
|
|
138
157
|
},
|
|
139
158
|
gemini: {
|
|
140
159
|
name: "Gemini CLI",
|
|
141
|
-
dirs: [],
|
|
160
|
+
dirs: [".gemini/commands"],
|
|
142
161
|
files: [
|
|
143
|
-
{ src: "gemini.md", dest: "GEMINI.md" }
|
|
162
|
+
{ src: "gemini.md", dest: "GEMINI.md" },
|
|
163
|
+
...geminiCommandFiles
|
|
144
164
|
]
|
|
145
165
|
},
|
|
146
166
|
zed: {
|
|
@@ -152,7 +172,43 @@ var PROVIDERS = {
|
|
|
152
172
|
}
|
|
153
173
|
};
|
|
154
174
|
var ALL_PROVIDER_IDS = Object.keys(PROVIDERS);
|
|
155
|
-
|
|
175
|
+
var COMMAND_PROVIDER_IDS = ["claude-code", "copilot", "codex", "gemini", "windsurf"];
|
|
176
|
+
var WORKFLOW_FILES = [
|
|
177
|
+
{ src: "workflow.md", dest: ".sdd/workflow.md" },
|
|
178
|
+
...Object.values(PROVIDERS).flatMap((provider) => provider.files)
|
|
179
|
+
];
|
|
180
|
+
function parseProviderList(value) {
|
|
181
|
+
const rawIds = value.split(",").map((id) => id.trim()).filter(Boolean);
|
|
182
|
+
if (rawIds.length === 0) {
|
|
183
|
+
throw new Error("Provider list cannot be empty");
|
|
184
|
+
}
|
|
185
|
+
const invalid = rawIds.filter((id) => !ALL_PROVIDER_IDS.includes(id));
|
|
186
|
+
if (invalid.length > 0) {
|
|
187
|
+
throw new Error(`Unknown provider: ${invalid.join(", ")}`);
|
|
188
|
+
}
|
|
189
|
+
return [...new Set(rawIds)];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/commands/init.ts
|
|
193
|
+
async function selectProviders(options) {
|
|
194
|
+
if (options.provider && options.all) {
|
|
195
|
+
console.error("\n error Use either --provider or --all, not both.\n");
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
if (options.provider) {
|
|
199
|
+
try {
|
|
200
|
+
return parseProviderList(options.provider);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error(`
|
|
203
|
+
error ${error.message}`);
|
|
204
|
+
console.error(` valid ${ALL_PROVIDER_IDS.join(", ")}
|
|
205
|
+
`);
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (options.all) {
|
|
210
|
+
return ALL_PROVIDER_IDS;
|
|
211
|
+
}
|
|
156
212
|
if (!process.stdout.isTTY) {
|
|
157
213
|
return ALL_PROVIDER_IDS;
|
|
158
214
|
}
|
|
@@ -167,13 +223,16 @@ async function selectProviders() {
|
|
|
167
223
|
});
|
|
168
224
|
return selected;
|
|
169
225
|
}
|
|
226
|
+
function formatList(items) {
|
|
227
|
+
return items.length > 0 ? items.join(", ") : "none";
|
|
228
|
+
}
|
|
170
229
|
async function initCommand(options) {
|
|
171
230
|
const cwd = process.cwd();
|
|
172
|
-
const { force } = options;
|
|
231
|
+
const { force, existing } = options;
|
|
173
232
|
console.log("");
|
|
174
|
-
console.log(
|
|
233
|
+
console.log(` SDD Workflow \u2014 initializing${existing ? " (existing project mode)" : ""}`);
|
|
175
234
|
console.log("");
|
|
176
|
-
const selectedProviders = await selectProviders();
|
|
235
|
+
const selectedProviders = await selectProviders(options);
|
|
177
236
|
console.log("");
|
|
178
237
|
ensureDir(import_path2.default.join(cwd, ".sdd/domains"));
|
|
179
238
|
ensureDir(import_path2.default.join(cwd, "specs/_template"));
|
|
@@ -193,41 +252,58 @@ async function initCommand(options) {
|
|
|
193
252
|
copyTemplate(file.src, import_path2.default.join(cwd, file.dest), force);
|
|
194
253
|
}
|
|
195
254
|
}
|
|
255
|
+
const providerNames = selectedProviders.map((id) => PROVIDERS[id].name);
|
|
256
|
+
const commandProviders = selectedProviders.filter((id) => COMMAND_PROVIDER_IDS.includes(id));
|
|
257
|
+
const rulesOnly = selectedProviders.filter((id) => !COMMAND_PROVIDER_IDS.includes(id));
|
|
258
|
+
console.log("");
|
|
259
|
+
console.log(" SDD Workflow initialized");
|
|
260
|
+
console.log("");
|
|
261
|
+
console.log(` Providers: ${formatList(providerNames)}`);
|
|
262
|
+
console.log(` Core: .sdd/workflow.md, .sdd/project-overview.md, .sdd/conventions.md, specs/_template/`);
|
|
263
|
+
if (commandProviders.length > 0) {
|
|
264
|
+
const names = commandProviders.map((id) => PROVIDERS[id].name);
|
|
265
|
+
console.log(` Commands: ${formatList(names)}`);
|
|
266
|
+
}
|
|
267
|
+
if (rulesOnly.length > 0) {
|
|
268
|
+
const names = rulesOnly.map((id) => PROVIDERS[id].name);
|
|
269
|
+
console.log(` Rules: ${formatList(names)}`);
|
|
270
|
+
}
|
|
196
271
|
console.log("");
|
|
197
|
-
console.log("
|
|
272
|
+
console.log(" Next steps:");
|
|
198
273
|
console.log("");
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
console.log(" 2. CLAUDE.md was created \u2014 share it with your AI agent as context");
|
|
274
|
+
if (existing) {
|
|
275
|
+
console.log(" 1. Run /scan to discover the codebase (no .sdd/ writes \u2014 produces scan-report.md)");
|
|
276
|
+
console.log(" then /bootstrap --scan to populate .sdd/project-overview.md and .sdd/conventions.md");
|
|
203
277
|
} else {
|
|
204
|
-
console.log("
|
|
278
|
+
console.log(" 1. Run /bootstrap to populate project context (new project)");
|
|
279
|
+
console.log(" or /bootstrap --scan to let the agent analyze the codebase (existing project)");
|
|
280
|
+
}
|
|
281
|
+
const entryMessages = [];
|
|
282
|
+
if (selectedProviders.includes("claude-code")) {
|
|
283
|
+
entryMessages.push(claudeExisted ? "CLAUDE.md already exists \u2014 add a reference to .sdd/ files manually" : "CLAUDE.md was created \u2014 Claude Code will read it automatically");
|
|
205
284
|
}
|
|
206
285
|
if (selectedProviders.includes("gemini")) {
|
|
207
|
-
|
|
208
|
-
console.log(" GEMINI.md was created \u2014 Gemini CLI will read it automatically");
|
|
209
|
-
} else {
|
|
210
|
-
console.log(" GEMINI.md already exists \u2014 add a reference to .sdd/ files manually");
|
|
211
|
-
}
|
|
286
|
+
entryMessages.push(geminiExisted ? "GEMINI.md already exists \u2014 add a reference to .sdd/ files manually" : "GEMINI.md was created \u2014 Gemini CLI will read it automatically");
|
|
212
287
|
}
|
|
213
288
|
if (selectedProviders.includes("codex")) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
289
|
+
entryMessages.push(agentsExisted ? "AGENTS.md already exists \u2014 add a reference to .sdd/ files manually" : "AGENTS.md was created \u2014 Codex will read it automatically");
|
|
290
|
+
}
|
|
291
|
+
let nextStep = 2;
|
|
292
|
+
if (entryMessages.length > 0) {
|
|
293
|
+
console.log(` ${nextStep}. ${entryMessages[0]}`);
|
|
294
|
+
for (const message of entryMessages.slice(1)) {
|
|
295
|
+
console.log(` ${message}`);
|
|
218
296
|
}
|
|
297
|
+
nextStep++;
|
|
219
298
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const names = withCommands.map((id) => PROVIDERS[id].name).join(", ");
|
|
225
|
-
console.log(` 3. Slash commands ready in: ${names}. Type / to see them.`);
|
|
299
|
+
if (commandProviders.length > 0) {
|
|
300
|
+
const names = commandProviders.map((id) => PROVIDERS[id].name).join(", ");
|
|
301
|
+
console.log(` ${nextStep}. Slash commands ready in: ${names}. Type / to see them.`);
|
|
302
|
+
nextStep++;
|
|
226
303
|
}
|
|
227
304
|
if (rulesOnly.length > 0) {
|
|
228
305
|
const names = rulesOnly.map((id) => PROVIDERS[id].name).join(", ");
|
|
229
|
-
|
|
230
|
-
console.log(` ${step} Context rules installed for: ${names}. The agent reads workflow.md on every task.`);
|
|
306
|
+
console.log(` ${nextStep}. Context rules installed for: ${names}. The agent reads workflow.md on every task.`);
|
|
231
307
|
}
|
|
232
308
|
console.log("");
|
|
233
309
|
}
|
|
@@ -265,52 +341,57 @@ function addCommand(type, name) {
|
|
|
265
341
|
// src/commands/update.ts
|
|
266
342
|
var import_fs4 = __toESM(require("fs"));
|
|
267
343
|
var import_path4 = __toESM(require("path"));
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
{ src: "claude-commands/refactor.md", dest: ".claude/commands/refactor.md" },
|
|
275
|
-
{ src: "claude-commands/spec-new.md", dest: ".claude/commands/spec-new.md" },
|
|
276
|
-
{ src: "claude-commands/spec-plan.md", dest: ".claude/commands/spec-plan.md" },
|
|
277
|
-
{ src: "claude-commands/spec-tasks.md", dest: ".claude/commands/spec-tasks.md" },
|
|
278
|
-
{ src: "claude-commands/review.md", dest: ".claude/commands/review.md" },
|
|
279
|
-
{ src: "claude-commands/finish.md", dest: ".claude/commands/finish.md" },
|
|
280
|
-
{ src: "cursor-rules/sddx-workflow.mdc", dest: ".cursor/rules/sddx-workflow.mdc" },
|
|
281
|
-
{ src: "windsurf-rules/sddx-workflow.md", dest: ".windsurf/rules/sddx-workflow.md" },
|
|
282
|
-
{ src: "copilot-prompts/bootstrap.prompt.md", dest: ".github/prompts/bootstrap.prompt.md" },
|
|
283
|
-
{ src: "copilot-prompts/ask.prompt.md", dest: ".github/prompts/ask.prompt.md" },
|
|
284
|
-
{ src: "copilot-prompts/assume.prompt.md", dest: ".github/prompts/assume.prompt.md" },
|
|
285
|
-
{ src: "copilot-prompts/bugfix.prompt.md", dest: ".github/prompts/bugfix.prompt.md" },
|
|
286
|
-
{ src: "copilot-prompts/refactor.prompt.md", dest: ".github/prompts/refactor.prompt.md" },
|
|
287
|
-
{ src: "copilot-prompts/spec-new.prompt.md", dest: ".github/prompts/spec-new.prompt.md" },
|
|
288
|
-
{ src: "copilot-prompts/spec-plan.prompt.md", dest: ".github/prompts/spec-plan.prompt.md" },
|
|
289
|
-
{ src: "copilot-prompts/spec-tasks.prompt.md", dest: ".github/prompts/spec-tasks.prompt.md" },
|
|
290
|
-
{ src: "copilot-prompts/review.prompt.md", dest: ".github/prompts/review.prompt.md" },
|
|
291
|
-
{ src: "copilot-prompts/finish.prompt.md", dest: ".github/prompts/finish.prompt.md" },
|
|
292
|
-
{ src: "copilot-instructions.md", dest: ".github/copilot-instructions.md" },
|
|
293
|
-
{ src: "zed-rules/sddx-workflow.md", dest: ".rules" }
|
|
294
|
-
];
|
|
295
|
-
function updateCommand() {
|
|
344
|
+
function fileChanged(src, dest) {
|
|
345
|
+
const template = import_fs4.default.readFileSync(import_path4.default.join(TEMPLATES_DIR, src), "utf8");
|
|
346
|
+
const current = import_fs4.default.readFileSync(dest, "utf8");
|
|
347
|
+
return template !== current;
|
|
348
|
+
}
|
|
349
|
+
function updateCommand(options = {}) {
|
|
296
350
|
const cwd = process.cwd();
|
|
297
351
|
if (!import_fs4.default.existsSync(import_path4.default.join(cwd, ".sdd"))) {
|
|
298
|
-
console.error("\n error
|
|
352
|
+
console.error("\n error No SDD installation found in this directory.");
|
|
353
|
+
console.error(" next Run `npx sddx-workflow init` or cd into a project that already has .sdd/.\n");
|
|
299
354
|
process.exit(1);
|
|
300
355
|
}
|
|
301
356
|
console.log("");
|
|
302
|
-
console.log(
|
|
303
|
-
console.log(" (project-overview.md, conventions.md,
|
|
357
|
+
console.log(` SDD Workflow \u2014 ${options.check ? "checking" : options.dryRun ? "previewing" : "updating"} workflow files`);
|
|
358
|
+
console.log(" (project-overview.md, conventions.md, and domains are yours \u2014 untouched)");
|
|
359
|
+
console.log(" (only files that already exist are updated \u2014 run `init --force` to add new commands)");
|
|
304
360
|
console.log("");
|
|
305
361
|
let updated = 0;
|
|
362
|
+
let unchanged = 0;
|
|
363
|
+
let missing = 0;
|
|
306
364
|
for (const file of WORKFLOW_FILES) {
|
|
307
365
|
const dest = import_path4.default.join(cwd, file.dest);
|
|
308
|
-
if (!import_fs4.default.existsSync(dest))
|
|
366
|
+
if (!import_fs4.default.existsSync(dest)) {
|
|
367
|
+
missing++;
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (!fileChanged(file.src, dest)) {
|
|
371
|
+
unchanged++;
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (options.check || options.dryRun) {
|
|
375
|
+
console.log(` update ${displayPath(dest)}`);
|
|
376
|
+
updated++;
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
309
379
|
copyTemplate(file.src, dest, true);
|
|
310
380
|
updated++;
|
|
311
381
|
}
|
|
312
382
|
console.log("");
|
|
313
|
-
|
|
383
|
+
if (options.check) {
|
|
384
|
+
console.log(` ${updated === 0 ? "ok" : "outdated"} ${updated} outdated, ${unchanged} current, ${missing} not installed`);
|
|
385
|
+
if (updated > 0) process.exit(1);
|
|
386
|
+
console.log("");
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (options.dryRun) {
|
|
390
|
+
console.log(` Preview. ${updated} file${updated !== 1 ? "s" : ""} would be updated, ${unchanged} current, ${missing} not installed.
|
|
391
|
+
`);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
console.log(` Done. ${updated} file${updated !== 1 ? "s" : ""} updated, ${unchanged} current, ${missing} not installed.
|
|
314
395
|
`);
|
|
315
396
|
}
|
|
316
397
|
|
|
@@ -320,28 +401,97 @@ var import_path5 = __toESM(require("path"));
|
|
|
320
401
|
function isBootstrapped(cwd) {
|
|
321
402
|
const file = import_path5.default.join(cwd, ".sdd/project-overview.md");
|
|
322
403
|
if (!import_fs5.default.existsSync(file)) return false;
|
|
323
|
-
const
|
|
404
|
+
const withoutComments = import_fs5.default.readFileSync(file, "utf8").replace(/<!--[\s\S]*?-->/g, "");
|
|
405
|
+
const lines = withoutComments.split("\n");
|
|
324
406
|
return lines.some((line) => {
|
|
325
407
|
const t = line.trim();
|
|
326
|
-
return t.length > 0 && !t.startsWith("#") && !t.startsWith("
|
|
408
|
+
return t.length > 0 && !t.startsWith("#") && !t.startsWith(">");
|
|
327
409
|
});
|
|
328
410
|
}
|
|
411
|
+
function stripComments(content) {
|
|
412
|
+
return content.replace(/<!--[\s\S]*?-->/g, "");
|
|
413
|
+
}
|
|
414
|
+
function isPlanApproved(specDir) {
|
|
415
|
+
const planFile = import_path5.default.join(specDir, "2-plan.md");
|
|
416
|
+
const tasksFile = import_path5.default.join(specDir, "3-tasks.md");
|
|
417
|
+
if (import_fs5.default.existsSync(planFile)) {
|
|
418
|
+
const plan = stripComments(import_fs5.default.readFileSync(planFile, "utf8"));
|
|
419
|
+
if (/^- \[x\]\s+\*\*Approved\*\*/im.test(plan)) return true;
|
|
420
|
+
}
|
|
421
|
+
if (import_fs5.default.existsSync(tasksFile)) {
|
|
422
|
+
const tasks = import_fs5.default.readFileSync(tasksFile, "utf8");
|
|
423
|
+
const match = tasks.match(/^Plan approved:\s*(.+)$/im);
|
|
424
|
+
return Boolean(match && match[1].trim() && !match[1].includes("<!--"));
|
|
425
|
+
}
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
function countPendingCrs(specDir) {
|
|
429
|
+
const file = import_path5.default.join(specDir, "amendments.md");
|
|
430
|
+
if (!import_fs5.default.existsSync(file)) return 0;
|
|
431
|
+
const content = stripComments(import_fs5.default.readFileSync(file, "utf8"));
|
|
432
|
+
return (content.match(/\*\*Status:\*\*\s*Pending approval/gi) ?? []).length;
|
|
433
|
+
}
|
|
434
|
+
function countUnresolvedGaps(specDir) {
|
|
435
|
+
const file = import_path5.default.join(specDir, "impl-gaps.md");
|
|
436
|
+
if (!import_fs5.default.existsSync(file)) return 0;
|
|
437
|
+
const content = stripComments(import_fs5.default.readFileSync(file, "utf8"));
|
|
438
|
+
const entries = content.split(/^##\s+GAP-\d+/gim).slice(1);
|
|
439
|
+
return entries.filter((entry) => {
|
|
440
|
+
const resolution = entry.match(/\*\*Resolution:\*\*\s*(.*)/i);
|
|
441
|
+
return !resolution || resolution[1].trim().length === 0 || /filled|pending|tbd/i.test(resolution[1]);
|
|
442
|
+
}).length;
|
|
443
|
+
}
|
|
444
|
+
function inferPhase(specDir, planApproved, tasksDone, tasksTotal) {
|
|
445
|
+
const requirementsFile = import_path5.default.join(specDir, "1-requirements.md");
|
|
446
|
+
const planFile = import_path5.default.join(specDir, "2-plan.md");
|
|
447
|
+
const tasksFile = import_path5.default.join(specDir, "3-tasks.md");
|
|
448
|
+
const verifyFile = import_path5.default.join(specDir, "verify-report.md");
|
|
449
|
+
if (!import_fs5.default.existsSync(requirementsFile)) return "missing requirements";
|
|
450
|
+
if (!import_fs5.default.existsSync(planFile)) return "drafting requirements";
|
|
451
|
+
if (!planApproved) return "awaiting plan approval";
|
|
452
|
+
if (!import_fs5.default.existsSync(tasksFile)) return "awaiting tasks";
|
|
453
|
+
if (tasksTotal === 0) return "tasks not planned";
|
|
454
|
+
if (tasksDone < tasksTotal) return "in /spec-tasks";
|
|
455
|
+
if (!import_fs5.default.existsSync(verifyFile)) return "awaiting /verify";
|
|
456
|
+
const verify = import_fs5.default.readFileSync(verifyFile, "utf8");
|
|
457
|
+
return /\bFAIL\b/i.test(verify) ? "verify failed" : "review pending";
|
|
458
|
+
}
|
|
329
459
|
function readSpec(specDir) {
|
|
330
460
|
const name = import_path5.default.basename(specDir);
|
|
331
461
|
const tasksFile = import_path5.default.join(specDir, "3-tasks.md");
|
|
462
|
+
const planApproved = isPlanApproved(specDir);
|
|
463
|
+
const pendingCrs = countPendingCrs(specDir);
|
|
464
|
+
const unresolvedGaps = countUnresolvedGaps(specDir);
|
|
332
465
|
if (!import_fs5.default.existsSync(tasksFile)) {
|
|
333
|
-
return {
|
|
466
|
+
return {
|
|
467
|
+
name,
|
|
468
|
+
phase: inferPhase(specDir, planApproved, 0, 0),
|
|
469
|
+
planApproved,
|
|
470
|
+
tasksDone: 0,
|
|
471
|
+
tasksTotal: 0,
|
|
472
|
+
pendingCrs,
|
|
473
|
+
unresolvedGaps
|
|
474
|
+
};
|
|
334
475
|
}
|
|
335
476
|
const content = import_fs5.default.readFileSync(tasksFile, "utf8");
|
|
336
|
-
const
|
|
337
|
-
const tasksDone = (
|
|
338
|
-
const
|
|
339
|
-
return {
|
|
477
|
+
const taskMatches = [...content.matchAll(/^- \[(x| )\]\s+\*\*(?:T\d+|Task\b)/gim)];
|
|
478
|
+
const tasksDone = taskMatches.filter((match) => match[1].toLowerCase() === "x").length;
|
|
479
|
+
const tasksTotal = taskMatches.length;
|
|
480
|
+
return {
|
|
481
|
+
name,
|
|
482
|
+
phase: inferPhase(specDir, planApproved, tasksDone, tasksTotal),
|
|
483
|
+
planApproved,
|
|
484
|
+
tasksDone,
|
|
485
|
+
tasksTotal,
|
|
486
|
+
pendingCrs,
|
|
487
|
+
unresolvedGaps
|
|
488
|
+
};
|
|
340
489
|
}
|
|
341
490
|
function statusCommand() {
|
|
342
491
|
const cwd = process.cwd();
|
|
343
492
|
if (!import_fs5.default.existsSync(import_path5.default.join(cwd, ".sdd"))) {
|
|
344
|
-
console.error("\n error
|
|
493
|
+
console.error("\n error No SDD installation found in this directory.");
|
|
494
|
+
console.error(" next Run `npx sddx-workflow init` or cd into a project that already has .sdd/.\n");
|
|
345
495
|
process.exit(1);
|
|
346
496
|
}
|
|
347
497
|
console.log("");
|
|
@@ -357,14 +507,103 @@ function statusCommand() {
|
|
|
357
507
|
console.log(` open specs ${specs.length}`);
|
|
358
508
|
for (const spec of specs) {
|
|
359
509
|
const label = spec.name.padEnd(14);
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
510
|
+
const progress = spec.tasksTotal > 0 ? `${spec.tasksDone}/${spec.tasksTotal} tasks` : "no tasks";
|
|
511
|
+
const outstanding = [
|
|
512
|
+
spec.pendingCrs > 0 ? `${spec.pendingCrs} pending CR${spec.pendingCrs === 1 ? "" : "s"}` : "",
|
|
513
|
+
spec.unresolvedGaps > 0 ? `${spec.unresolvedGaps} unresolved gap${spec.unresolvedGaps === 1 ? "" : "s"}` : ""
|
|
514
|
+
].filter(Boolean).join(" \xB7 ");
|
|
515
|
+
const suffix = outstanding ? ` \xB7 ${outstanding}` : "";
|
|
516
|
+
console.log(` ${label} ${spec.phase} \xB7 ${progress}${suffix}`);
|
|
517
|
+
}
|
|
518
|
+
console.log("");
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// src/commands/doctor.ts
|
|
522
|
+
var import_fs6 = __toESM(require("fs"));
|
|
523
|
+
var import_path6 = __toESM(require("path"));
|
|
524
|
+
var OBSOLETE_PATHS = [
|
|
525
|
+
"src/commands/snapshot.ts",
|
|
526
|
+
"templates/claude-commands/spec-restore.md",
|
|
527
|
+
"templates/codex-skills/spec-restore/SKILL.md",
|
|
528
|
+
"templates/copilot-prompts/spec-restore.prompt.md",
|
|
529
|
+
"templates/gemini-commands/spec-restore.toml",
|
|
530
|
+
"templates/windsurf-workflows/spec-restore.md",
|
|
531
|
+
".claude/commands/spec-restore.md",
|
|
532
|
+
".agents/skills/spec-restore/SKILL.md",
|
|
533
|
+
".github/prompts/spec-restore.prompt.md",
|
|
534
|
+
".gemini/commands/spec-restore.toml",
|
|
535
|
+
".windsurf/workflows/spec-restore.md"
|
|
536
|
+
];
|
|
537
|
+
function exists(cwd, relativePath) {
|
|
538
|
+
return import_fs6.default.existsSync(import_path6.default.join(cwd, relativePath));
|
|
539
|
+
}
|
|
540
|
+
function providerHealth(cwd) {
|
|
541
|
+
return Object.entries(PROVIDERS).map(([id, provider]) => ({
|
|
542
|
+
id,
|
|
543
|
+
name: provider.name,
|
|
544
|
+
installed: provider.files.filter((file) => exists(cwd, file.dest)).length,
|
|
545
|
+
missing: provider.files.filter((file) => !exists(cwd, file.dest)).map((file) => file.dest)
|
|
546
|
+
})).filter((provider) => provider.installed > 0);
|
|
547
|
+
}
|
|
548
|
+
function doctorCommand() {
|
|
549
|
+
const cwd = process.cwd();
|
|
550
|
+
const issues = [];
|
|
551
|
+
const warnings = [];
|
|
552
|
+
console.log("");
|
|
553
|
+
console.log(" SDD Workflow doctor");
|
|
554
|
+
console.log("");
|
|
555
|
+
const hasSdd = exists(cwd, ".sdd");
|
|
556
|
+
console.log(` install ${hasSdd ? "found" : "missing"}`);
|
|
557
|
+
if (!hasSdd) {
|
|
558
|
+
console.log("");
|
|
559
|
+
console.log(" error No .sdd/ directory found. Run `npx sddx-workflow init`.");
|
|
560
|
+
console.log("");
|
|
561
|
+
process.exit(1);
|
|
562
|
+
}
|
|
563
|
+
const missingCore = CORE_FILES.map((file) => file.dest).filter((dest) => !exists(cwd, dest));
|
|
564
|
+
console.log(` core files ${missingCore.length === 0 ? "ok" : `${missingCore.length} missing`}`);
|
|
565
|
+
for (const missing of missingCore) {
|
|
566
|
+
warnings.push(`Missing core file: ${missing}`);
|
|
567
|
+
}
|
|
568
|
+
const providers = providerHealth(cwd);
|
|
569
|
+
console.log(` providers ${providers.length > 0 ? providers.map((provider) => provider.name).join(", ") : "none detected"}`);
|
|
570
|
+
if (providers.length === 0) {
|
|
571
|
+
warnings.push("No provider files detected. Run `npx sddx-workflow init --provider <id>` or `npx sddx-workflow init --all`.");
|
|
572
|
+
}
|
|
573
|
+
for (const provider of providers) {
|
|
574
|
+
if (provider.missing.length > 0) {
|
|
575
|
+
warnings.push(`${provider.name} appears partially installed (${provider.missing.length} missing file${provider.missing.length === 1 ? "" : "s"}). Run \`npx sddx-workflow init --force --provider ${provider.id}\` to reinstall it.`);
|
|
366
576
|
}
|
|
367
577
|
}
|
|
578
|
+
const obsolete = OBSOLETE_PATHS.filter((relativePath) => exists(cwd, relativePath));
|
|
579
|
+
console.log(` obsolete ${obsolete.length === 0 ? "none" : `${obsolete.length} found`}`);
|
|
580
|
+
for (const item of obsolete) {
|
|
581
|
+
warnings.push(`Obsolete snapshot/restore file remains: ${item}`);
|
|
582
|
+
}
|
|
583
|
+
console.log("");
|
|
584
|
+
if (issues.length === 0 && warnings.length === 0) {
|
|
585
|
+
console.log(" ok installation looks healthy");
|
|
586
|
+
console.log("");
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
for (const issue of issues) {
|
|
590
|
+
console.log(` error ${issue}`);
|
|
591
|
+
}
|
|
592
|
+
for (const warning of warnings) {
|
|
593
|
+
console.log(` warn ${warning}`);
|
|
594
|
+
}
|
|
595
|
+
console.log("");
|
|
596
|
+
if (issues.length > 0) process.exit(1);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// src/commands/commands.ts
|
|
600
|
+
function commandsCommand() {
|
|
601
|
+
console.log("");
|
|
602
|
+
console.log(" Agent commands");
|
|
603
|
+
console.log("");
|
|
604
|
+
for (const name of COMMAND_NAMES) {
|
|
605
|
+
console.log(` /${name}`);
|
|
606
|
+
}
|
|
368
607
|
console.log("");
|
|
369
608
|
}
|
|
370
609
|
|
|
@@ -373,8 +612,10 @@ var import_module = require("module");
|
|
|
373
612
|
var pkg = (0, import_module.createRequire)(__filename)("../package.json");
|
|
374
613
|
var program = new import_commander.Command();
|
|
375
614
|
program.name("sddx-workflow").description("Spec-Driven Development CLI").version(pkg.version);
|
|
376
|
-
program.command("init").description("Initialize SDD protocol in the current project").option("--force", "Overwrite files that already exist").action(initCommand);
|
|
615
|
+
program.command("init").description("Initialize SDD protocol in the current project").option("--force", "Overwrite files that already exist").option("--existing", "Brownfield mode: prints next-steps that start with /scan and /bootstrap --scan").option("--provider <ids>", "Comma-separated providers to install (claude-code,cursor,windsurf,copilot,codex,gemini,zed)").option("--all", "Install all provider integrations without prompting").action(initCommand);
|
|
377
616
|
program.command("add <type> <name>").description("Add an SDD component to an existing installation").addHelpText("after", "\nExamples:\n $ sddx-workflow add domain auth\n $ sddx-workflow add domain payments").action(addCommand);
|
|
378
|
-
program.command("update").description("Update protocol files to the latest version
|
|
617
|
+
program.command("update").description("Update protocol files to the latest version").option("--dry-run", "Show which installed workflow files would change").option("--check", "Exit non-zero when installed workflow files are outdated").action(updateCommand);
|
|
379
618
|
program.command("status").description("Show bootstrap status and open specs progress").action(statusCommand);
|
|
619
|
+
program.command("doctor").description("Check SDD installation health and provider files").action(doctorCommand);
|
|
620
|
+
program.command("commands").description("List agent commands installed by provider integrations").action(commandsCommand);
|
|
380
621
|
program.parseAsync();
|