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.
- package/dist/index.js +456 -428
- 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
|
|
55
|
-
import { join
|
|
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 =
|
|
106
|
-
|
|
107
|
-
const skillPath =
|
|
108
|
-
|
|
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 =
|
|
120
|
-
|
|
121
|
-
const skillPath =
|
|
122
|
-
|
|
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 =
|
|
134
|
-
|
|
135
|
-
const skillPath =
|
|
136
|
-
|
|
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 =
|
|
149
|
-
const globalSkillPath =
|
|
150
|
-
const legacySkillPath =
|
|
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 (
|
|
457
|
+
if (existsSync2(skillPath)) {
|
|
153
458
|
rmSync(skillPath);
|
|
154
459
|
uninstalled = true;
|
|
155
460
|
}
|
|
156
|
-
if (
|
|
461
|
+
if (existsSync2(globalSkillPath)) {
|
|
157
462
|
rmSync(globalSkillPath);
|
|
158
463
|
uninstalled = true;
|
|
159
464
|
}
|
|
160
|
-
if (
|
|
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 =
|
|
173
|
-
const globalSkillPath =
|
|
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 (
|
|
481
|
+
if (existsSync2(skillPath)) {
|
|
177
482
|
rmSync(skillPath);
|
|
178
483
|
try {
|
|
179
|
-
const skillDir =
|
|
180
|
-
const skillsDir =
|
|
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 =
|
|
200
|
-
const legacyCommandPath =
|
|
201
|
-
const legacyGlobalSkillPath =
|
|
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 (
|
|
508
|
+
if (existsSync2(projectSkillPath)) {
|
|
204
509
|
rmSync(projectSkillPath);
|
|
205
510
|
try {
|
|
206
|
-
const skillDir =
|
|
207
|
-
const skillsDir =
|
|
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 (
|
|
522
|
+
if (existsSync2(legacyCommandPath)) {
|
|
218
523
|
rmSync(legacyCommandPath);
|
|
219
524
|
uninstalled = true;
|
|
220
525
|
}
|
|
221
|
-
if (
|
|
526
|
+
if (existsSync2(legacyGlobalSkillPath)) {
|
|
222
527
|
rmSync(legacyGlobalSkillPath);
|
|
223
528
|
try {
|
|
224
|
-
const skillDir =
|
|
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 =
|
|
251
|
-
const claudeGlobalPath =
|
|
252
|
-
const claudeLegacySkillPath =
|
|
253
|
-
const claudeInstalled =
|
|
254
|
-
const codexProjectPath =
|
|
255
|
-
const codexGlobalPath =
|
|
256
|
-
const codexInstalled =
|
|
257
|
-
const codexLocation =
|
|
258
|
-
const geminiProjectPath =
|
|
259
|
-
const geminiLegacyCommandPath =
|
|
260
|
-
const geminiLegacyGlobalPath =
|
|
261
|
-
const geminiInstalled =
|
|
262
|
-
const geminiLocation =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
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
|
-
|
|
681
|
+
## Loop
|
|
514
682
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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
|
-
|
|
522
|
-
path: .spets/outputs # Relative to project root, or absolute path
|
|
688
|
+
## Rules
|
|
523
689
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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
|
-
|
|
534
|
-
enabled: true # Prompt to save knowledge after workflows
|
|
535
|
-
inject: true # Use saved knowledge in future workflows
|
|
695
|
+
## Forbidden
|
|
536
696
|
|
|
537
|
-
|
|
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
|
-
|
|
548
|
-
|
|
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
|
|
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
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
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
|