spets 0.2.6 → 0.2.7

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 (2) hide show
  1. package/dist/index.js +456 -428
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -51,14 +51,319 @@ import { dirname as dirname4, join as join13 } from "path";
51
51
  import { fileURLToPath as fileURLToPath3 } from "url";
52
52
 
53
53
  // src/commands/init.ts
54
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, cpSync, readFileSync } from "fs";
55
- import { join as join2, dirname } from "path";
54
+ import { existsSync, mkdirSync, writeFileSync, cpSync, readFileSync } from "fs";
55
+ import { join, dirname } from "path";
56
56
  import { fileURLToPath } from "url";
57
57
  import { select } from "@inquirer/prompts";
58
+ var __dirname = dirname(fileURLToPath(import.meta.url));
59
+ async function initCommand(options) {
60
+ const cwd = process.cwd();
61
+ const spetsDir = getSpetsDir(cwd);
62
+ if (spetsExists(cwd) && !options.force) {
63
+ console.error("\u274C Spets already initialized. Use --force to overwrite.");
64
+ process.exit(1);
65
+ }
66
+ if (options.interactive) {
67
+ await runInteractiveSetup(cwd, spetsDir, options);
68
+ return;
69
+ }
70
+ mkdirSync(spetsDir, { recursive: true });
71
+ mkdirSync(join(spetsDir, "steps"), { recursive: true });
72
+ mkdirSync(join(spetsDir, "outputs"), { recursive: true });
73
+ mkdirSync(join(spetsDir, "hooks"), { recursive: true });
74
+ mkdirSync(join(spetsDir, "knowledge"), { recursive: true });
75
+ const knowledgeGuideTemplate = join(__dirname, "..", "templates", "knowledge", "guide.md");
76
+ if (existsSync(knowledgeGuideTemplate)) {
77
+ const guideDest = join(spetsDir, "knowledge", "guide.md");
78
+ if (!existsSync(guideDest)) {
79
+ cpSync(knowledgeGuideTemplate, guideDest);
80
+ }
81
+ }
82
+ const configPath = join(spetsDir, "config.yml");
83
+ if (!existsSync(configPath)) {
84
+ writeFileSync(configPath, getDefaultConfig());
85
+ }
86
+ createDefaultSteps(spetsDir, options.force);
87
+ printEnhancedInitOutput(options);
88
+ }
89
+ function printEnhancedInitOutput(options) {
90
+ console.log("");
91
+ console.log("\u2501".repeat(60));
92
+ console.log("\u2705 Spets initialized successfully!");
93
+ console.log("\u2501".repeat(60));
94
+ console.log("");
95
+ console.log("\u{1F4C1} Created .spets/ \u2014 Your workflow configuration folder");
96
+ console.log("");
97
+ console.log(" \u{1F4C4} config.yml");
98
+ console.log(" \u21B3 Main configuration file. Start here!");
99
+ console.log(" \u21B3 Configure workflow steps, output paths, hooks, and more");
100
+ console.log(" \u21B3 Each option is documented with comments");
101
+ console.log("");
102
+ console.log(" \u{1F4C2} steps/");
103
+ console.log(" \u21B3 01-plan/template.md \u2014 Template for planning documents");
104
+ console.log(" \u21B3 02-implement/template.md \u2014 Template for implementation docs");
105
+ console.log(" \u21B3 Edit these to customize AI output format");
106
+ console.log(" \u21B3 Add/remove steps by editing config.yml AND creating folders");
107
+ console.log("");
108
+ console.log(" \u{1F4C2} hooks/");
109
+ console.log(" \u21B3 Hooks run at workflow events (preStep, postStep, onApprove...)");
110
+ console.log("");
111
+ console.log(" \u{1F4C2} knowledge/");
112
+ console.log(" \u21B3 guide.md \u2014 How to capture learnings from workflows");
113
+ console.log(" \u21B3 Add .md files here to teach future workflows");
114
+ console.log("");
115
+ console.log(" \u{1F4C2} outputs/");
116
+ console.log(" \u21B3 Where generated documents are saved (don't edit)");
117
+ console.log("");
118
+ console.log("\u2501".repeat(60));
119
+ console.log("\u{1F680} Next Steps");
120
+ console.log("\u2501".repeat(60));
121
+ console.log("");
122
+ console.log(" 1. Open .spets/config.yml and review the options");
123
+ console.log(" (Everything is documented with comments!)");
124
+ console.log("");
125
+ console.log(" 2. Optionally customize templates in .spets/steps/");
126
+ console.log(" (These control what the AI outputs look like)");
127
+ console.log("");
128
+ console.log(" 3. Install a plugin: spets plugin install claude");
129
+ console.log(" (Also available: codex, gemini)");
130
+ console.log("");
131
+ console.log('\u{1F4A1} Tip: Run "spets --help" to see all available commands');
132
+ console.log("");
133
+ }
134
+ function getDefaultConfig(agent = "claude") {
135
+ return `# \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
136
+ # \u2551 SPETS CONFIGURATION \u2551
137
+ # \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
138
+ # \u2551 This file controls your Spec-Driven Development workflow. \u2551
139
+ # \u2551 Each section is documented - read the comments! \u2551
140
+ # \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
141
+
142
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
143
+ # WORKFLOW STEPS
144
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
145
+ # Define the steps in your workflow. Each step:
146
+ # 1. Has a folder in .spets/steps/<step-name>/
147
+ # 2. Contains a template.md that controls AI output format
148
+ # 3. Runs through 5 phases: Explore \u2192 Clarify \u2192 Execute \u2192 Verify \u2192 Review
149
+ #
150
+ # To add a new step:
151
+ # 1. Add the name here (e.g., "03-test")
152
+ # 2. Create folder: .spets/steps/03-test/
153
+ # 3. Create template: .spets/steps/03-test/template.md
154
+ #
155
+ # To remove a step: Just delete it from this list
156
+ # To reorder: Change the order here
157
+
158
+ steps:
159
+ - 01-plan # Planning phase - analyzes codebase, creates implementation plan
160
+ - 02-implement # Implementation phase - executes the plan, writes code
161
+
162
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
163
+ # AI AGENT
164
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
165
+ # Which AI CLI to use for generating documents.
166
+ # Options: claude, codex, gemini
167
+
168
+ agent: ${agent}
169
+
170
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
171
+ # OUTPUT CONFIGURATION
172
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
173
+ # Where generated documents are saved.
174
+ # Each workflow creates a folder: <path>/<task-id>/
175
+
176
+ output:
177
+ path: .spets/outputs # Relative to project root, or absolute path
178
+
179
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
180
+ # KNOWLEDGE BASE
181
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
182
+ # The knowledge base captures learnings from completed workflows.
183
+ # Add .md files to .spets/knowledge/ to teach future workflows.
184
+ #
185
+ # enabled: Save knowledge suggestions after each workflow
186
+ # inject: Include knowledge files in the explore phase prompt
187
+
188
+ knowledge:
189
+ enabled: true # Prompt to save knowledge after workflows
190
+ inject: true # Use saved knowledge in future workflows
191
+
192
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
193
+ # AGENT TEAMS (optional, Claude Code only)
194
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
195
+ # Requires: CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
196
+ # When enabled, explore phase uses parallel teammates for analysis
197
+ # and verify phase uses an independent reviewer agent.
198
+ #
199
+ # team:
200
+ # enabled: true
201
+
202
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
203
+ # HOOKS (optional)
204
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
205
+ # Shell scripts that run at specific workflow events.
206
+ # Paths are relative to project root.
207
+ #
208
+ # Available hooks:
209
+ # preStep - Before each step starts
210
+ # postStep - After each step completes
211
+ # onApprove - When a document is approved
212
+ # onReject - When a workflow is rejected/stopped
213
+ # onComplete - When the entire workflow finishes
214
+ #
215
+ # Environment variables available in hooks:
216
+ # SPETS_TASK_ID, SPETS_STEP_NAME, SPETS_STEP_INDEX,
217
+ # SPETS_OUTPUT_PATH, SPETS_BRANCH, SPETS_CWD
218
+ #
219
+ # hooks:
220
+ # preStep: "./hooks/pre-step.sh"
221
+ # postStep: "./hooks/post-step.sh"
222
+ # onApprove: "./hooks/on-approve.sh"
223
+ # onReject: "./hooks/on-reject.sh"
224
+ # onComplete: "./hooks/on-complete.sh"
225
+ `;
226
+ }
227
+ async function runInteractiveSetup(cwd, spetsDir, options) {
228
+ console.log("");
229
+ console.log("\u2501".repeat(60));
230
+ console.log("\u{1F9D9} Spets Interactive Setup");
231
+ console.log("\u2501".repeat(60));
232
+ console.log("");
233
+ console.log("Let's configure spets for your project.");
234
+ console.log("");
235
+ const agent = await select({
236
+ message: "Which AI CLI do you want to use?",
237
+ choices: [
238
+ { value: "claude", name: "Claude (Anthropic) \u2014 Recommended" },
239
+ { value: "codex", name: "Codex (OpenAI)" },
240
+ { value: "gemini", name: "Gemini (Google)" }
241
+ ]
242
+ });
243
+ mkdirSync(spetsDir, { recursive: true });
244
+ mkdirSync(join(spetsDir, "steps"), { recursive: true });
245
+ mkdirSync(join(spetsDir, "outputs"), { recursive: true });
246
+ mkdirSync(join(spetsDir, "hooks"), { recursive: true });
247
+ mkdirSync(join(spetsDir, "knowledge"), { recursive: true });
248
+ const knowledgeGuideTemplate = join(__dirname, "..", "templates", "knowledge", "guide.md");
249
+ const guideDest = join(spetsDir, "knowledge", "guide.md");
250
+ if (existsSync(knowledgeGuideTemplate) && !existsSync(guideDest)) {
251
+ cpSync(knowledgeGuideTemplate, guideDest);
252
+ }
253
+ const configPath = join(spetsDir, "config.yml");
254
+ if (!existsSync(configPath)) {
255
+ writeFileSync(configPath, getDefaultConfig(agent));
256
+ }
257
+ createDefaultSteps(spetsDir, options.force);
258
+ printEnhancedInitOutput(options);
259
+ }
260
+ function createDefaultSteps(spetsDir, force) {
261
+ const planDir = join(spetsDir, "steps", "01-plan");
262
+ mkdirSync(planDir, { recursive: true });
263
+ const planTemplatePath = join(planDir, "template.md");
264
+ if (force || !existsSync(planTemplatePath)) {
265
+ writeFileSync(planTemplatePath, getPlanTemplate());
266
+ }
267
+ const implementDir = join(spetsDir, "steps", "02-implement");
268
+ mkdirSync(implementDir, { recursive: true });
269
+ const implementTemplatePath = join(implementDir, "template.md");
270
+ if (force || !existsSync(implementTemplatePath)) {
271
+ writeFileSync(implementTemplatePath, getImplementTemplate());
272
+ }
273
+ }
274
+ function getPlanTemplate() {
275
+ const fullTemplate = readFileSync(join(__dirname, "..", "templates", "steps", "01-plan", "template.md"), "utf-8");
276
+ return fullTemplate;
277
+ }
278
+ function getImplementTemplate() {
279
+ const fullTemplate = readFileSync(join(__dirname, "..", "templates", "steps", "02-implement", "template.md"), "utf-8");
280
+ return fullTemplate;
281
+ }
282
+
283
+ // src/commands/status.ts
284
+ async function statusCommand(options) {
285
+ const cwd = process.cwd();
286
+ if (!spetsExists(cwd)) {
287
+ console.error('Spets not initialized. Run "spets init" first.');
288
+ process.exit(1);
289
+ }
290
+ const config = loadConfig(cwd);
291
+ if (options.task) {
292
+ showTaskStatus(options.task, config, cwd);
293
+ } else {
294
+ showAllTasks(config, cwd);
295
+ }
296
+ }
297
+ function showTaskStatus(taskId, config, cwd) {
298
+ const state = getWorkflowState(taskId, config, cwd);
299
+ const meta = loadTaskMetadata(taskId, cwd);
300
+ if (!state) {
301
+ console.error(`Task '${taskId}' not found.`);
302
+ process.exit(1);
303
+ }
304
+ console.log(`Task: ${taskId}`);
305
+ console.log(`Query: ${meta?.userQuery || state.userQuery || "(unknown)"}`);
306
+ console.log(`Status: ${formatStatus(state.status)}`);
307
+ console.log(`Current Step: ${state.currentStepName} (${state.currentStepIndex + 1}/${config.steps.length})`);
308
+ console.log("");
309
+ console.log("Steps:");
310
+ for (let i = 0; i < config.steps.length; i++) {
311
+ const stepName = config.steps[i];
312
+ const output = state.outputs.get(stepName);
313
+ const isCurrent = i === state.currentStepIndex;
314
+ const marker = isCurrent ? "\u2192" : " ";
315
+ const status = output ? formatDocStatus(output.status) : "\u25CB";
316
+ console.log(` ${marker} ${i + 1}. ${stepName} ${status}`);
317
+ }
318
+ }
319
+ function showAllTasks(config, cwd) {
320
+ const tasks = listTasks(cwd);
321
+ if (tasks.length === 0) {
322
+ console.log("No tasks found.");
323
+ console.log("");
324
+ console.log('Start a new task with: spets orchestrate init "your task description"');
325
+ return;
326
+ }
327
+ console.log("Tasks:");
328
+ console.log("");
329
+ for (const taskId of tasks.slice(0, 10)) {
330
+ const state = getWorkflowState(taskId, config, cwd);
331
+ const meta = loadTaskMetadata(taskId, cwd);
332
+ if (!state) continue;
333
+ const query = meta?.userQuery || state.userQuery || "(no query)";
334
+ const truncatedQuery = query.length > 50 ? query.substring(0, 47) + "..." : query;
335
+ const progress = `${state.currentStepIndex + 1}/${config.steps.length}`;
336
+ console.log(` ${taskId} ${formatStatus(state.status)} [${progress}]`);
337
+ console.log(` ${truncatedQuery}`);
338
+ console.log("");
339
+ }
340
+ if (tasks.length > 10) {
341
+ console.log(` ... and ${tasks.length - 10} more tasks`);
342
+ }
343
+ console.log("");
344
+ console.log('Use "spets status -t <taskId>" for details');
345
+ }
346
+ function formatStatus(status) {
347
+ const icons = {
348
+ in_progress: "\u{1F504} in_progress",
349
+ completed: "\u2705 completed",
350
+ paused: "\u23F8\uFE0F paused",
351
+ rejected: "\u274C rejected"
352
+ };
353
+ return icons[status] || status;
354
+ }
355
+ function formatDocStatus(status) {
356
+ const icons = {
357
+ draft: "\u25D0",
358
+ approved: "\u25CF",
359
+ rejected: "\u2717"
360
+ };
361
+ return icons[status] || "\u25CB";
362
+ }
58
363
 
59
364
  // src/commands/plugin.ts
60
- import { existsSync, mkdirSync, writeFileSync, rmSync, readdirSync } from "fs";
61
- import { join } from "path";
365
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, rmSync, readdirSync } from "fs";
366
+ import { join as join2 } from "path";
62
367
  import { homedir } from "os";
63
368
  async function pluginCommand(action, name) {
64
369
  switch (action) {
@@ -102,10 +407,10 @@ async function installPlugin(name) {
102
407
  installer();
103
408
  }
104
409
  function installClaudePlugin() {
105
- const commandsDir = join(process.cwd(), ".claude", "commands");
106
- mkdirSync(commandsDir, { recursive: true });
107
- const skillPath = join(commandsDir, "spets.md");
108
- writeFileSync(skillPath, getClaudeSkillContent());
410
+ const commandsDir = join2(process.cwd(), ".claude", "commands");
411
+ mkdirSync2(commandsDir, { recursive: true });
412
+ const skillPath = join2(commandsDir, "spets.md");
413
+ writeFileSync2(skillPath, getClaudeSkillContent());
109
414
  console.log("Installed Claude Code plugin.");
110
415
  console.log(`Location: ${skillPath}`);
111
416
  console.log("");
@@ -116,10 +421,10 @@ function installClaudePlugin() {
116
421
  console.log("No additional Claude processes are spawned.");
117
422
  }
118
423
  function installCodexPlugin() {
119
- const spetsDir = join(process.cwd(), ".codex", "skills", "spets");
120
- mkdirSync(spetsDir, { recursive: true });
121
- const skillPath = join(spetsDir, "SKILL.md");
122
- writeFileSync(skillPath, getCodexSkillContent());
424
+ const spetsDir = join2(process.cwd(), ".codex", "skills", "spets");
425
+ mkdirSync2(spetsDir, { recursive: true });
426
+ const skillPath = join2(spetsDir, "SKILL.md");
427
+ writeFileSync2(skillPath, getCodexSkillContent());
123
428
  console.log("Installed Codex CLI plugin.");
124
429
  console.log(`Location: ${skillPath}`);
125
430
  console.log("");
@@ -130,10 +435,10 @@ function installCodexPlugin() {
130
435
  console.log("No additional Codex processes are spawned.");
131
436
  }
132
437
  function installGeminiPlugin() {
133
- const spetsDir = join(process.cwd(), ".gemini", "skills", "spets");
134
- mkdirSync(spetsDir, { recursive: true });
135
- const skillPath = join(spetsDir, "SKILL.md");
136
- writeFileSync(skillPath, getGeminiSkillContent());
438
+ const spetsDir = join2(process.cwd(), ".gemini", "skills", "spets");
439
+ mkdirSync2(spetsDir, { recursive: true });
440
+ const skillPath = join2(spetsDir, "SKILL.md");
441
+ writeFileSync2(skillPath, getGeminiSkillContent());
137
442
  console.log("Installed Gemini CLI plugin.");
138
443
  console.log(`Location: ${skillPath}`);
139
444
  console.log("");
@@ -145,19 +450,19 @@ function installGeminiPlugin() {
145
450
  }
146
451
  async function uninstallPlugin(name) {
147
452
  if (name === "claude") {
148
- const skillPath = join(process.cwd(), ".claude", "commands", "spets.md");
149
- const globalSkillPath = join(homedir(), ".claude", "commands", "spets.md");
150
- const legacySkillPath = join(homedir(), ".claude", "commands", "sdd-do.md");
453
+ const skillPath = join2(process.cwd(), ".claude", "commands", "spets.md");
454
+ const globalSkillPath = join2(homedir(), ".claude", "commands", "spets.md");
455
+ const legacySkillPath = join2(homedir(), ".claude", "commands", "sdd-do.md");
151
456
  let uninstalled = false;
152
- if (existsSync(skillPath)) {
457
+ if (existsSync2(skillPath)) {
153
458
  rmSync(skillPath);
154
459
  uninstalled = true;
155
460
  }
156
- if (existsSync(globalSkillPath)) {
461
+ if (existsSync2(globalSkillPath)) {
157
462
  rmSync(globalSkillPath);
158
463
  uninstalled = true;
159
464
  }
160
- if (existsSync(legacySkillPath)) {
465
+ if (existsSync2(legacySkillPath)) {
161
466
  rmSync(legacySkillPath);
162
467
  uninstalled = true;
163
468
  }
@@ -169,15 +474,15 @@ async function uninstallPlugin(name) {
169
474
  return;
170
475
  }
171
476
  if (name === "codex") {
172
- const projectSkillPath = join(process.cwd(), ".codex", "skills", "spets", "SKILL.md");
173
- const globalSkillPath = join(homedir(), ".codex", "skills", "spets", "SKILL.md");
477
+ const projectSkillPath = join2(process.cwd(), ".codex", "skills", "spets", "SKILL.md");
478
+ const globalSkillPath = join2(homedir(), ".codex", "skills", "spets", "SKILL.md");
174
479
  let uninstalled = false;
175
480
  for (const skillPath of [projectSkillPath, globalSkillPath]) {
176
- if (existsSync(skillPath)) {
481
+ if (existsSync2(skillPath)) {
177
482
  rmSync(skillPath);
178
483
  try {
179
- const skillDir = join(skillPath, "..");
180
- const skillsDir = join(skillDir, "..");
484
+ const skillDir = join2(skillPath, "..");
485
+ const skillsDir = join2(skillDir, "..");
181
486
  rmSync(skillDir, { recursive: true });
182
487
  const remaining = readdirSync(skillsDir);
183
488
  if (remaining.length === 0) {
@@ -196,15 +501,15 @@ async function uninstallPlugin(name) {
196
501
  return;
197
502
  }
198
503
  if (name === "gemini") {
199
- const projectSkillPath = join(process.cwd(), ".gemini", "skills", "spets", "SKILL.md");
200
- const legacyCommandPath = join(process.cwd(), ".gemini", "commands", "spets.md");
201
- const legacyGlobalSkillPath = join(homedir(), ".gemini", "skills", "spets", "SKILL.md");
504
+ const projectSkillPath = join2(process.cwd(), ".gemini", "skills", "spets", "SKILL.md");
505
+ const legacyCommandPath = join2(process.cwd(), ".gemini", "commands", "spets.md");
506
+ const legacyGlobalSkillPath = join2(homedir(), ".gemini", "skills", "spets", "SKILL.md");
202
507
  let uninstalled = false;
203
- if (existsSync(projectSkillPath)) {
508
+ if (existsSync2(projectSkillPath)) {
204
509
  rmSync(projectSkillPath);
205
510
  try {
206
- const skillDir = join(projectSkillPath, "..");
207
- const skillsDir = join(skillDir, "..");
511
+ const skillDir = join2(projectSkillPath, "..");
512
+ const skillsDir = join2(skillDir, "..");
208
513
  rmSync(skillDir, { recursive: true });
209
514
  const remaining = readdirSync(skillsDir);
210
515
  if (remaining.length === 0) {
@@ -214,14 +519,14 @@ async function uninstallPlugin(name) {
214
519
  }
215
520
  uninstalled = true;
216
521
  }
217
- if (existsSync(legacyCommandPath)) {
522
+ if (existsSync2(legacyCommandPath)) {
218
523
  rmSync(legacyCommandPath);
219
524
  uninstalled = true;
220
525
  }
221
- if (existsSync(legacyGlobalSkillPath)) {
526
+ if (existsSync2(legacyGlobalSkillPath)) {
222
527
  rmSync(legacyGlobalSkillPath);
223
528
  try {
224
- const skillDir = join(legacyGlobalSkillPath, "..");
529
+ const skillDir = join2(legacyGlobalSkillPath, "..");
225
530
  const remaining = readdirSync(skillDir);
226
531
  if (remaining.length === 0) {
227
532
  rmSync(skillDir, { recursive: true });
@@ -247,22 +552,22 @@ async function listPlugins() {
247
552
  console.log(" codex - Codex CLI $spets skill (project-level)");
248
553
  console.log(" gemini - Gemini CLI spets skill (project-level)");
249
554
  console.log("");
250
- const claudeProjectPath = join(process.cwd(), ".claude", "commands", "spets.md");
251
- const claudeGlobalPath = join(homedir(), ".claude", "commands", "spets.md");
252
- const claudeLegacySkillPath = join(homedir(), ".claude", "commands", "sdd-do.md");
253
- const claudeInstalled = existsSync(claudeProjectPath) || existsSync(claudeGlobalPath) || existsSync(claudeLegacySkillPath);
254
- const codexProjectPath = join(process.cwd(), ".codex", "skills", "spets", "SKILL.md");
255
- const codexGlobalPath = join(homedir(), ".codex", "skills", "spets", "SKILL.md");
256
- const codexInstalled = existsSync(codexProjectPath) || existsSync(codexGlobalPath);
257
- const codexLocation = existsSync(codexProjectPath) ? "project" : existsSync(codexGlobalPath) ? "global" : null;
258
- const geminiProjectPath = join(process.cwd(), ".gemini", "skills", "spets", "SKILL.md");
259
- const geminiLegacyCommandPath = join(process.cwd(), ".gemini", "commands", "spets.md");
260
- const geminiLegacyGlobalPath = join(homedir(), ".gemini", "skills", "spets", "SKILL.md");
261
- const geminiInstalled = existsSync(geminiProjectPath) || existsSync(geminiLegacyCommandPath) || existsSync(geminiLegacyGlobalPath);
262
- const geminiLocation = existsSync(geminiProjectPath) ? "project" : existsSync(geminiLegacyCommandPath) ? "project" : existsSync(geminiLegacyGlobalPath) ? "global" : null;
555
+ const claudeProjectPath = join2(process.cwd(), ".claude", "commands", "spets.md");
556
+ const claudeGlobalPath = join2(homedir(), ".claude", "commands", "spets.md");
557
+ const claudeLegacySkillPath = join2(homedir(), ".claude", "commands", "sdd-do.md");
558
+ const claudeInstalled = existsSync2(claudeProjectPath) || existsSync2(claudeGlobalPath) || existsSync2(claudeLegacySkillPath);
559
+ const codexProjectPath = join2(process.cwd(), ".codex", "skills", "spets", "SKILL.md");
560
+ const codexGlobalPath = join2(homedir(), ".codex", "skills", "spets", "SKILL.md");
561
+ const codexInstalled = existsSync2(codexProjectPath) || existsSync2(codexGlobalPath);
562
+ const codexLocation = existsSync2(codexProjectPath) ? "project" : existsSync2(codexGlobalPath) ? "global" : null;
563
+ const geminiProjectPath = join2(process.cwd(), ".gemini", "skills", "spets", "SKILL.md");
564
+ const geminiLegacyCommandPath = join2(process.cwd(), ".gemini", "commands", "spets.md");
565
+ const geminiLegacyGlobalPath = join2(homedir(), ".gemini", "skills", "spets", "SKILL.md");
566
+ const geminiInstalled = existsSync2(geminiProjectPath) || existsSync2(geminiLegacyCommandPath) || existsSync2(geminiLegacyGlobalPath);
567
+ const geminiLocation = existsSync2(geminiProjectPath) ? "project" : existsSync2(geminiLegacyCommandPath) ? "project" : existsSync2(geminiLegacyGlobalPath) ? "global" : null;
263
568
  console.log("Installed:");
264
569
  const installed = [];
265
- const claudeLocation = existsSync(claudeProjectPath) ? "project" : existsSync(claudeGlobalPath) ? "global" : "legacy";
570
+ const claudeLocation = existsSync2(claudeProjectPath) ? "project" : existsSync2(claudeGlobalPath) ? "global" : "legacy";
266
571
  if (claudeInstalled) installed.push(`claude (${claudeLocation})`);
267
572
  if (codexInstalled) installed.push(`codex (${codexLocation})`);
268
573
  if (geminiInstalled) installed.push(`gemini (${geminiLocation})`);
@@ -361,356 +666,40 @@ description: SDD workflow executor - orchestrator-controlled spec-driven develop
361
666
 
362
667
  # Spets Executor
363
668
 
364
- You execute spets workflow commands. Follow orchestrator instructions exactly.
365
-
366
- ## Startup
367
-
368
- IF \`$ARGUMENTS\` starts with "resume":
369
- Extract optional task ID from arguments (e.g. "resume abc-123" or "resume --task abc-123")
370
- RUN \`npx spets orchestrate resume <taskId>\` (omit taskId if none provided)
371
- ELSE IF \`$ARGUMENTS\` starts with "list":
372
- RUN \`npx spets orchestrate list\`
373
- ELSE:
374
- RUN \`npx spets orchestrate init "$ARGUMENTS"\`
375
-
376
- ## Loop
377
-
378
- 1. Parse the JSON response
379
- 2. Read the \`instructions\` field
380
- 3. Follow the instructions exactly
381
- 4. Repeat until the response says to stop
382
-
383
- ## Rules
384
-
385
- - NEVER skip or modify orchestrator commands
386
- - NEVER improvise steps \u2014 only do what instructions say
387
- - Always pass JSON output as a single-quoted string argument
388
- - Minify JSON when passing as command arguments
389
-
390
- ## Forbidden
391
-
392
- Planning mode, task tracking tools
393
-
394
- $ARGUMENTS
395
- description: Task description for the workflow (or "resume" to list/continue previous workflows)
396
- `;
397
- }
398
-
399
- // src/commands/init.ts
400
- var __dirname = dirname(fileURLToPath(import.meta.url));
401
- async function initCommand(options) {
402
- const cwd = process.cwd();
403
- const spetsDir = getSpetsDir(cwd);
404
- if (spetsExists(cwd) && !options.force) {
405
- console.error("\u274C Spets already initialized. Use --force to overwrite.");
406
- process.exit(1);
407
- }
408
- if (options.interactive) {
409
- await runInteractiveSetup(cwd, spetsDir, options);
410
- return;
411
- }
412
- mkdirSync2(spetsDir, { recursive: true });
413
- mkdirSync2(join2(spetsDir, "steps"), { recursive: true });
414
- mkdirSync2(join2(spetsDir, "outputs"), { recursive: true });
415
- mkdirSync2(join2(spetsDir, "hooks"), { recursive: true });
416
- mkdirSync2(join2(spetsDir, "knowledge"), { recursive: true });
417
- const knowledgeGuideTemplate = join2(__dirname, "..", "templates", "knowledge", "guide.md");
418
- if (existsSync2(knowledgeGuideTemplate)) {
419
- const guideDest = join2(spetsDir, "knowledge", "guide.md");
420
- if (!existsSync2(guideDest)) {
421
- cpSync(knowledgeGuideTemplate, guideDest);
422
- }
423
- }
424
- const configPath = join2(spetsDir, "config.yml");
425
- if (!existsSync2(configPath)) {
426
- writeFileSync2(configPath, getDefaultConfig());
427
- }
428
- createDefaultSteps(spetsDir);
429
- createClaudeCommand(cwd);
430
- printEnhancedInitOutput(options);
431
- }
432
- function printEnhancedInitOutput(options) {
433
- console.log("");
434
- console.log("\u2501".repeat(60));
435
- console.log("\u2705 Spets initialized successfully!");
436
- console.log("\u2501".repeat(60));
437
- console.log("");
438
- console.log("\u{1F4C1} Created .spets/ \u2014 Your workflow configuration folder");
439
- console.log("");
440
- console.log(" \u{1F4C4} config.yml");
441
- console.log(" \u21B3 Main configuration file. Start here!");
442
- console.log(" \u21B3 Configure workflow steps, output paths, hooks, and more");
443
- console.log(" \u21B3 Each option is documented with comments");
444
- console.log("");
445
- console.log(" \u{1F4C2} steps/");
446
- console.log(" \u21B3 01-plan/template.md \u2014 Template for planning documents");
447
- console.log(" \u21B3 02-implement/template.md \u2014 Template for implementation docs");
448
- console.log(" \u21B3 Edit these to customize AI output format");
449
- console.log(" \u21B3 Add/remove steps by editing config.yml AND creating folders");
450
- console.log("");
451
- console.log(" \u{1F4C2} hooks/");
452
- console.log(" \u21B3 Hooks run at workflow events (preStep, postStep, onApprove...)");
453
- console.log("");
454
- console.log(" \u{1F4C2} knowledge/");
455
- console.log(" \u21B3 guide.md \u2014 How to capture learnings from workflows");
456
- console.log(" \u21B3 Add .md files here to teach future workflows");
457
- console.log("");
458
- console.log(" \u{1F4C2} outputs/");
459
- console.log(" \u21B3 Where generated documents are saved (don't edit)");
460
- console.log("");
461
- console.log("\u{1F4C4} .claude/commands/spets.md \u2014 Claude Code integration");
462
- console.log(" \u21B3 Use /spets in Claude Code to run workflows");
463
- console.log("");
464
- console.log("\u2501".repeat(60));
465
- console.log("\u{1F680} Next Steps");
466
- console.log("\u2501".repeat(60));
467
- console.log("");
468
- console.log(" 1. Open .spets/config.yml and review the options");
469
- console.log(" (Everything is documented with comments!)");
470
- console.log("");
471
- console.log(" 2. Optionally customize templates in .spets/steps/");
472
- console.log(" (These control what the AI outputs look like)");
473
- console.log("");
474
- console.log(" 3. Use /spets in Claude Code to run your first workflow");
475
- console.log("");
476
- console.log('\u{1F4A1} Tip: Run "spets --help" to see all available commands');
477
- console.log("");
478
- }
479
- function getDefaultConfig(agent = "claude") {
480
- return `# \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
481
- # \u2551 SPETS CONFIGURATION \u2551
482
- # \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
483
- # \u2551 This file controls your Spec-Driven Development workflow. \u2551
484
- # \u2551 Each section is documented - read the comments! \u2551
485
- # \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
486
-
487
- # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
488
- # WORKFLOW STEPS
489
- # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
490
- # Define the steps in your workflow. Each step:
491
- # 1. Has a folder in .spets/steps/<step-name>/
492
- # 2. Contains a template.md that controls AI output format
493
- # 3. Runs through 5 phases: Explore \u2192 Clarify \u2192 Execute \u2192 Verify \u2192 Review
494
- #
495
- # To add a new step:
496
- # 1. Add the name here (e.g., "03-test")
497
- # 2. Create folder: .spets/steps/03-test/
498
- # 3. Create template: .spets/steps/03-test/template.md
499
- #
500
- # To remove a step: Just delete it from this list
501
- # To reorder: Change the order here
669
+ You execute spets workflow commands. Follow orchestrator instructions exactly.
502
670
 
503
- steps:
504
- - 01-plan # Planning phase - analyzes codebase, creates implementation plan
505
- - 02-implement # Implementation phase - executes the plan, writes code
671
+ ## Startup
506
672
 
507
- # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
508
- # AI AGENT
509
- # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
510
- # Which AI CLI to use for generating documents.
511
- # Options: claude, codex, gemini
673
+ IF \`$ARGUMENTS\` starts with "resume":
674
+ Extract optional task ID from arguments (e.g. "resume abc-123" or "resume --task abc-123")
675
+ RUN \`npx spets orchestrate resume <taskId>\` (omit taskId if none provided)
676
+ ELSE IF \`$ARGUMENTS\` starts with "list":
677
+ RUN \`npx spets orchestrate list\`
678
+ ELSE:
679
+ RUN \`npx spets orchestrate init "$ARGUMENTS"\`
512
680
 
513
- agent: ${agent}
681
+ ## Loop
514
682
 
515
- # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
516
- # OUTPUT CONFIGURATION
517
- # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
518
- # Where generated documents are saved.
519
- # Each workflow creates a folder: <path>/<task-id>/
683
+ 1. Parse the JSON response
684
+ 2. Read the \`instructions\` field
685
+ 3. Follow the instructions exactly
686
+ 4. Repeat until the response says to stop
520
687
 
521
- output:
522
- path: .spets/outputs # Relative to project root, or absolute path
688
+ ## Rules
523
689
 
524
- # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
525
- # KNOWLEDGE BASE
526
- # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
527
- # The knowledge base captures learnings from completed workflows.
528
- # Add .md files to .spets/knowledge/ to teach future workflows.
529
- #
530
- # enabled: Save knowledge suggestions after each workflow
531
- # inject: Include knowledge files in the explore phase prompt
690
+ - NEVER skip or modify orchestrator commands
691
+ - NEVER improvise steps \u2014 only do what instructions say
692
+ - Always pass JSON output as a single-quoted string argument
693
+ - Minify JSON when passing as command arguments
532
694
 
533
- knowledge:
534
- enabled: true # Prompt to save knowledge after workflows
535
- inject: true # Use saved knowledge in future workflows
695
+ ## Forbidden
536
696
 
537
- # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
538
- # AGENT TEAMS (optional, Claude Code only)
539
- # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
540
- # Requires: CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
541
- # When enabled, explore phase uses parallel teammates for analysis
542
- # and verify phase uses an independent reviewer agent.
543
- #
544
- # team:
545
- # enabled: true
697
+ Planning mode, task tracking tools
546
698
 
547
- # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
548
- # HOOKS (optional)
549
- # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
550
- # Shell scripts that run at specific workflow events.
551
- # Paths are relative to project root.
552
- #
553
- # Available hooks:
554
- # preStep - Before each step starts
555
- # postStep - After each step completes
556
- # onApprove - When a document is approved
557
- # onReject - When a workflow is rejected/stopped
558
- # onComplete - When the entire workflow finishes
559
- #
560
- # Environment variables available in hooks:
561
- # SPETS_TASK_ID, SPETS_STEP_NAME, SPETS_STEP_INDEX,
562
- # SPETS_OUTPUT_PATH, SPETS_BRANCH, SPETS_CWD
563
- #
564
- # hooks:
565
- # preStep: "./hooks/pre-step.sh"
566
- # postStep: "./hooks/post-step.sh"
567
- # onApprove: "./hooks/on-approve.sh"
568
- # onReject: "./hooks/on-reject.sh"
569
- # onComplete: "./hooks/on-complete.sh"
699
+ $ARGUMENTS
700
+ description: Task description for the workflow (or "resume" to list/continue previous workflows)
570
701
  `;
571
702
  }
572
- async function runInteractiveSetup(cwd, spetsDir, options) {
573
- console.log("");
574
- console.log("\u2501".repeat(60));
575
- console.log("\u{1F9D9} Spets Interactive Setup");
576
- console.log("\u2501".repeat(60));
577
- console.log("");
578
- console.log("Let's configure spets for your project.");
579
- console.log("");
580
- const agent = await select({
581
- message: "Which AI CLI do you want to use?",
582
- choices: [
583
- { value: "claude", name: "Claude (Anthropic) \u2014 Recommended" },
584
- { value: "codex", name: "Codex (OpenAI)" },
585
- { value: "gemini", name: "Gemini (Google)" }
586
- ]
587
- });
588
- mkdirSync2(spetsDir, { recursive: true });
589
- mkdirSync2(join2(spetsDir, "steps"), { recursive: true });
590
- mkdirSync2(join2(spetsDir, "outputs"), { recursive: true });
591
- mkdirSync2(join2(spetsDir, "hooks"), { recursive: true });
592
- mkdirSync2(join2(spetsDir, "knowledge"), { recursive: true });
593
- const knowledgeGuideTemplate = join2(__dirname, "..", "templates", "knowledge", "guide.md");
594
- const guideDest = join2(spetsDir, "knowledge", "guide.md");
595
- if (existsSync2(knowledgeGuideTemplate) && !existsSync2(guideDest)) {
596
- cpSync(knowledgeGuideTemplate, guideDest);
597
- }
598
- const configPath = join2(spetsDir, "config.yml");
599
- if (!existsSync2(configPath)) {
600
- writeFileSync2(configPath, getDefaultConfig(agent));
601
- }
602
- createDefaultSteps(spetsDir);
603
- createClaudeCommand(cwd);
604
- printEnhancedInitOutput(options);
605
- }
606
- function createDefaultSteps(spetsDir) {
607
- const planDir = join2(spetsDir, "steps", "01-plan");
608
- mkdirSync2(planDir, { recursive: true });
609
- const planTemplatePath = join2(planDir, "template.md");
610
- if (!existsSync2(planTemplatePath)) {
611
- writeFileSync2(planTemplatePath, getPlanTemplate());
612
- }
613
- const implementDir = join2(spetsDir, "steps", "02-implement");
614
- mkdirSync2(implementDir, { recursive: true });
615
- const implementTemplatePath = join2(implementDir, "template.md");
616
- if (!existsSync2(implementTemplatePath)) {
617
- writeFileSync2(implementTemplatePath, getImplementTemplate());
618
- }
619
- }
620
- function getPlanTemplate() {
621
- const fullTemplate = readFileSync(join2(__dirname, "..", "templates", "steps", "01-plan", "template.md"), "utf-8");
622
- return fullTemplate;
623
- }
624
- function getImplementTemplate() {
625
- const fullTemplate = readFileSync(join2(__dirname, "..", "templates", "steps", "02-implement", "template.md"), "utf-8");
626
- return fullTemplate;
627
- }
628
- function createClaudeCommand(cwd) {
629
- const commandDir = join2(cwd, ".claude", "commands");
630
- mkdirSync2(commandDir, { recursive: true });
631
- writeFileSync2(join2(commandDir, "spets.md"), getClaudeSkillContent());
632
- }
633
-
634
- // src/commands/status.ts
635
- async function statusCommand(options) {
636
- const cwd = process.cwd();
637
- if (!spetsExists(cwd)) {
638
- console.error('Spets not initialized. Run "spets init" first.');
639
- process.exit(1);
640
- }
641
- const config = loadConfig(cwd);
642
- if (options.task) {
643
- showTaskStatus(options.task, config, cwd);
644
- } else {
645
- showAllTasks(config, cwd);
646
- }
647
- }
648
- function showTaskStatus(taskId, config, cwd) {
649
- const state = getWorkflowState(taskId, config, cwd);
650
- const meta = loadTaskMetadata(taskId, cwd);
651
- if (!state) {
652
- console.error(`Task '${taskId}' not found.`);
653
- process.exit(1);
654
- }
655
- console.log(`Task: ${taskId}`);
656
- console.log(`Query: ${meta?.userQuery || state.userQuery || "(unknown)"}`);
657
- console.log(`Status: ${formatStatus(state.status)}`);
658
- console.log(`Current Step: ${state.currentStepName} (${state.currentStepIndex + 1}/${config.steps.length})`);
659
- console.log("");
660
- console.log("Steps:");
661
- for (let i = 0; i < config.steps.length; i++) {
662
- const stepName = config.steps[i];
663
- const output = state.outputs.get(stepName);
664
- const isCurrent = i === state.currentStepIndex;
665
- const marker = isCurrent ? "\u2192" : " ";
666
- const status = output ? formatDocStatus(output.status) : "\u25CB";
667
- console.log(` ${marker} ${i + 1}. ${stepName} ${status}`);
668
- }
669
- }
670
- function showAllTasks(config, cwd) {
671
- const tasks = listTasks(cwd);
672
- if (tasks.length === 0) {
673
- console.log("No tasks found.");
674
- console.log("");
675
- console.log('Start a new task with: spets orchestrate init "your task description"');
676
- return;
677
- }
678
- console.log("Tasks:");
679
- console.log("");
680
- for (const taskId of tasks.slice(0, 10)) {
681
- const state = getWorkflowState(taskId, config, cwd);
682
- const meta = loadTaskMetadata(taskId, cwd);
683
- if (!state) continue;
684
- const query = meta?.userQuery || state.userQuery || "(no query)";
685
- const truncatedQuery = query.length > 50 ? query.substring(0, 47) + "..." : query;
686
- const progress = `${state.currentStepIndex + 1}/${config.steps.length}`;
687
- console.log(` ${taskId} ${formatStatus(state.status)} [${progress}]`);
688
- console.log(` ${truncatedQuery}`);
689
- console.log("");
690
- }
691
- if (tasks.length > 10) {
692
- console.log(` ... and ${tasks.length - 10} more tasks`);
693
- }
694
- console.log("");
695
- console.log('Use "spets status -t <taskId>" for details');
696
- }
697
- function formatStatus(status) {
698
- const icons = {
699
- in_progress: "\u{1F504} in_progress",
700
- completed: "\u2705 completed",
701
- paused: "\u23F8\uFE0F paused",
702
- rejected: "\u274C rejected"
703
- };
704
- return icons[status] || status;
705
- }
706
- function formatDocStatus(status) {
707
- const icons = {
708
- draft: "\u25D0",
709
- approved: "\u25CF",
710
- rejected: "\u2717"
711
- };
712
- return icons[status] || "\u25CB";
713
- }
714
703
 
715
704
  // src/orchestrator/index.ts
716
705
  import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, existsSync as existsSync11, readdirSync as readdirSync2 } from "fs";
@@ -1169,6 +1158,9 @@ function buildExecutePrompt(params) {
1169
1158
  parts.push("- **Use your full capabilities.** Whatever this output requires \u2014 do it.");
1170
1159
  parts.push("- **Be context-efficient.** Use the explored context above as your primary reference. Use Grep to locate code before reading. Never re-read a file you have already read in this session.");
1171
1160
  parts.push("");
1161
+ if (config.team?.enabled) {
1162
+ parts.push(buildExecuteTeamSection());
1163
+ }
1172
1164
  parts.push("## Output");
1173
1165
  parts.push("");
1174
1166
  parts.push(`Produce the output and save to: \`${outputPath}\``);
@@ -1187,21 +1179,50 @@ function buildExecutePrompt(params) {
1187
1179
  outputPath
1188
1180
  };
1189
1181
  }
1182
+ function buildExecuteTeamSection() {
1183
+ return `## Agent Team Instructions
1184
+
1185
+ **You have access to Agent Teams (TeammateTool).** Use them to produce higher-quality output.
1186
+
1187
+ Spawn an agent team with 3 teammates:
1188
+
1189
+ 1. **context-verifier** \u2014 Re-check that the explored context is still current.
1190
+ - Verify referenced files still exist and snippets are accurate
1191
+ - Flag any stale context that needs updating
1192
+ - Report: list of confirmed/stale context items
1193
+
1194
+ 2. **drafter** \u2014 Write the output document following the template.
1195
+ - Use the explored context, user answers, and template
1196
+ - Produce the complete document with required frontmatter
1197
+ - Report: the full draft document
1198
+
1199
+ 3. **quality-checker** \u2014 Review the draft for completeness and correctness.
1200
+ - Check for placeholders, TODOs, or missing sections
1201
+ - Verify claims against the codebase
1202
+ - Report: list of issues with suggestions
1203
+
1204
+ **Workflow:**
1205
+ - Run **context-verifier** and **drafter** in parallel
1206
+ - Once drafter completes, send draft to **quality-checker** for review
1207
+ - As the lead, synthesize: apply quality-checker fixes, incorporate context-verifier updates
1208
+ - Produce the final output
1209
+
1210
+ **Fallback:** If TeammateTool is not available (Agent Teams not enabled),
1211
+ perform all work yourself. The output format is the same either way.
1212
+ `;
1213
+ }
1190
1214
 
1191
1215
  // src/core/prompts/verify.ts
1192
1216
  import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
1193
1217
  import { join as join8 } from "path";
1194
- function buildVerifyPrompt(params) {
1218
+ function buildVerifySharedContext(params) {
1195
1219
  const cwd = params.cwd || process.cwd();
1196
1220
  const stepsDir = getStepsDir(cwd);
1197
1221
  const templatePath = join8(stepsDir, params.step, "template.md");
1198
1222
  const template = existsSync9(templatePath) ? readFileSync8(templatePath, "utf-8") : "";
1199
1223
  const document = existsSync9(params.documentPath) ? readFileSync8(params.documentPath, "utf-8") : "";
1224
+ const config = loadConfig(cwd);
1200
1225
  const parts = [];
1201
- parts.push("# Verify Phase");
1202
- parts.push("");
1203
- parts.push("Verify that this output is accurate, complete, and consistent.");
1204
- parts.push("");
1205
1226
  parts.push(`**Attempt ${params.verifyAttempts} of 3**`);
1206
1227
  parts.push("");
1207
1228
  parts.push("## Task Information");
@@ -1221,7 +1242,6 @@ function buildVerifyPrompt(params) {
1221
1242
  parts.push(template);
1222
1243
  parts.push("");
1223
1244
  }
1224
- const config = loadConfig(cwd);
1225
1245
  if (config.knowledge?.inject !== false) {
1226
1246
  const knowledgeSection = buildKnowledgeSummarySection(cwd);
1227
1247
  if (knowledgeSection) {
@@ -1235,6 +1255,16 @@ function buildVerifyPrompt(params) {
1235
1255
  parts.push("");
1236
1256
  }
1237
1257
  }
1258
+ return { template, document, sharedContext: parts.join("\n"), config };
1259
+ }
1260
+ function buildVerifyPrompt(params) {
1261
+ const { sharedContext, config } = buildVerifySharedContext(params);
1262
+ const parts = [];
1263
+ parts.push("# Verify Phase");
1264
+ parts.push("");
1265
+ parts.push("Verify that this output is accurate, complete, and consistent.");
1266
+ parts.push("");
1267
+ parts.push(sharedContext);
1238
1268
  parts.push("## Verification Criteria");
1239
1269
  parts.push("");
1240
1270
  parts.push("1. **Accuracy** (0-100)");
@@ -1291,39 +1321,37 @@ function buildVerifyPrompt(params) {
1291
1321
  return parts.join("\n");
1292
1322
  }
1293
1323
  function buildVerifyTeamSection() {
1294
- const lines = [];
1295
- lines.push("## Agent Team Instructions");
1296
- lines.push("");
1297
- lines.push("**You have access to Agent Teams (TeammateTool).** Use them for independent verification.");
1298
- lines.push("");
1299
- lines.push("Spawn an agent team with 2 teammates:");
1300
- lines.push("");
1301
- lines.push("1. **accuracy-verifier** \u2014 Verify that claims in the output are true.");
1302
- lines.push(" - Check referenced files exist, described changes are real");
1303
- lines.push(" - Verify cited patterns match the codebase");
1304
- lines.push(" - Score: accuracy");
1305
- lines.push(" - List any issues found with severity and suggestion");
1306
- lines.push("");
1307
- lines.push("2. **completeness-verifier** \u2014 Verify completeness and consistency.");
1308
- lines.push(" - Check the output covers what the template asks for");
1309
- lines.push(" - Verify no placeholders, TODOs, or missing sections");
1310
- lines.push(" - Check internal consistency, no contradictions");
1311
- lines.push(" - Score: completeness, consistency");
1312
- lines.push(" - List any issues found with severity and suggestion");
1313
- lines.push("");
1314
- lines.push("**Key principle:** Writer \u2260 Reviewer. The teammates provide independent review");
1315
- lines.push("of a document they did not write, reducing blind spots.");
1316
- lines.push("");
1317
- lines.push("**As the lead agent**, you should:");
1318
- lines.push("- Send each teammate the document and their specific evaluation criteria");
1319
- lines.push("- Wait for both teammates to complete");
1320
- lines.push("- Combine their scores and issues into the standard VerifyOutput JSON");
1321
- lines.push("- Determine the final passed/failed status based on pass criteria");
1322
- lines.push("");
1323
- lines.push("**Fallback:** If TeammateTool is not available (Agent Teams not enabled),");
1324
- lines.push("perform all verification yourself. The output format is the same either way.");
1325
- lines.push("");
1326
- return lines.join("\n");
1324
+ return `## Agent Team Instructions
1325
+
1326
+ **You have access to Agent Teams (TeammateTool).** Use them for independent verification.
1327
+
1328
+ Spawn an agent team with 2 teammates:
1329
+
1330
+ 1. **accuracy-verifier** \u2014 Verify that claims in the output are true.
1331
+ - Check referenced files exist, described changes are real
1332
+ - Verify cited patterns match the codebase
1333
+ - Score: accuracy
1334
+ - List any issues found with severity and suggestion
1335
+
1336
+ 2. **completeness-verifier** \u2014 Verify completeness and consistency.
1337
+ - Check the output covers what the template asks for
1338
+ - Verify no placeholders, TODOs, or missing sections
1339
+ - Check internal consistency, no contradictions
1340
+ - Score: completeness, consistency
1341
+ - List any issues found with severity and suggestion
1342
+
1343
+ **Key principle:** Writer \u2260 Reviewer. The teammates provide independent review
1344
+ of a document they did not write, reducing blind spots.
1345
+
1346
+ **As the lead agent**, you should:
1347
+ - Send each teammate the document and their specific evaluation criteria
1348
+ - Wait for both teammates to complete
1349
+ - Combine their scores and issues into the standard VerifyOutput JSON
1350
+ - Determine the final passed/failed status based on pass criteria
1351
+
1352
+ **Fallback:** If TeammateTool is not available (Agent Teams not enabled),
1353
+ perform all verification yourself. The output format is the same either way.
1354
+ `;
1327
1355
  }
1328
1356
 
1329
1357
  // src/core/prompts/knowledge.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spets",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "Spec Driven Development Execution Framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",