@abranjith/spec-lite 0.0.1
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/LICENSE +21 -0
- package/README.md +244 -0
- package/dist/index.js +903 -0
- package/dist/index.js.map +1 -0
- package/dist/stacks/dotnet.md +84 -0
- package/dist/stacks/java.md +76 -0
- package/dist/stacks/python.md +76 -0
- package/dist/stacks/react.md +79 -0
- package/dist/stacks/typescript.md +67 -0
- package/package.json +60 -0
- package/prompts/brainstorm.md +265 -0
- package/prompts/code_review.md +181 -0
- package/prompts/devops.md +227 -0
- package/prompts/feature.md +294 -0
- package/prompts/fix.md +195 -0
- package/prompts/implement.md +210 -0
- package/prompts/integration_tests.md +216 -0
- package/prompts/memorize.md +369 -0
- package/prompts/orchestrator.md +371 -0
- package/prompts/performance_review.md +202 -0
- package/prompts/planner.md +301 -0
- package/prompts/readme.md +232 -0
- package/prompts/security_audit.md +212 -0
- package/prompts/spec_help.md +230 -0
- package/prompts/technical_docs.md +219 -0
- package/prompts/unit_tests.md +339 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,903 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/commands/init.ts
|
|
7
|
+
import path6 from "path";
|
|
8
|
+
import fs6 from "fs-extra";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import inquirer from "inquirer";
|
|
11
|
+
|
|
12
|
+
// src/providers/copilot.ts
|
|
13
|
+
import path from "path";
|
|
14
|
+
import fs from "fs-extra";
|
|
15
|
+
var CopilotProvider = class {
|
|
16
|
+
name = "GitHub Copilot";
|
|
17
|
+
alias = "copilot";
|
|
18
|
+
description = "GitHub Copilot (VS Code, JetBrains, Neovim)";
|
|
19
|
+
getTargetPath(promptName) {
|
|
20
|
+
return path.join(".github", "copilot", `${promptName}.prompt.md`);
|
|
21
|
+
}
|
|
22
|
+
transformPrompt(content, meta) {
|
|
23
|
+
const header = [
|
|
24
|
+
`<!-- spec-lite | ${meta.name} | DO NOT EDIT below the project-context block \u2014 managed by spec-lite -->`,
|
|
25
|
+
`<!-- To update: run "spec-lite update" \u2014 your Project Context edits will be preserved -->`,
|
|
26
|
+
""
|
|
27
|
+
].join("\n");
|
|
28
|
+
return header + content;
|
|
29
|
+
}
|
|
30
|
+
async detectExisting(workspaceRoot) {
|
|
31
|
+
const existing = [];
|
|
32
|
+
const copilotDir = path.join(workspaceRoot, ".github", "copilot");
|
|
33
|
+
if (await fs.pathExists(copilotDir)) {
|
|
34
|
+
const files = await fs.readdir(copilotDir);
|
|
35
|
+
for (const f of files) {
|
|
36
|
+
if (f.endsWith(".prompt.md") || f.endsWith(".md")) {
|
|
37
|
+
existing.push(path.join(".github", "copilot", f));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const mainFile = path.join(
|
|
42
|
+
workspaceRoot,
|
|
43
|
+
".github",
|
|
44
|
+
"copilot-instructions.md"
|
|
45
|
+
);
|
|
46
|
+
if (await fs.pathExists(mainFile)) {
|
|
47
|
+
existing.push(".github/copilot-instructions.md");
|
|
48
|
+
}
|
|
49
|
+
return existing;
|
|
50
|
+
}
|
|
51
|
+
getPostInitMessage() {
|
|
52
|
+
return [
|
|
53
|
+
"",
|
|
54
|
+
"\u{1F4CB} GitHub Copilot setup complete!",
|
|
55
|
+
"",
|
|
56
|
+
" Your sub-agent prompts are in .github/copilot/",
|
|
57
|
+
"",
|
|
58
|
+
" How to use:",
|
|
59
|
+
" 1. Open GitHub Copilot Chat in VS Code",
|
|
60
|
+
" 2. Sub-agents are available as slash commands \u2014 type /prompt_name",
|
|
61
|
+
" (e.g., /brainstorm, /planner, /feature)",
|
|
62
|
+
" 3. Files ending in .prompt.md are natively recognized by Copilot",
|
|
63
|
+
" 4. Customize the Project Context block in each file for your project",
|
|
64
|
+
""
|
|
65
|
+
].join("\n");
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// src/providers/claude-code.ts
|
|
70
|
+
import path2 from "path";
|
|
71
|
+
import fs2 from "fs-extra";
|
|
72
|
+
var ClaudeCodeProvider = class {
|
|
73
|
+
name = "Claude Code";
|
|
74
|
+
alias = "claude-code";
|
|
75
|
+
description = "Claude Code (Anthropic's coding agent)";
|
|
76
|
+
getTargetPath(promptName) {
|
|
77
|
+
return path2.join(".claude", "prompts", `${promptName}.md`);
|
|
78
|
+
}
|
|
79
|
+
transformPrompt(content, meta) {
|
|
80
|
+
const header = [
|
|
81
|
+
`<!-- spec-lite | ${meta.name} | DO NOT EDIT below the project-context block \u2014 managed by spec-lite -->`,
|
|
82
|
+
`<!-- To update: run "spec-lite update" \u2014 your Project Context edits will be preserved -->`,
|
|
83
|
+
""
|
|
84
|
+
].join("\n");
|
|
85
|
+
return header + content;
|
|
86
|
+
}
|
|
87
|
+
async detectExisting(workspaceRoot) {
|
|
88
|
+
const existing = [];
|
|
89
|
+
const claudePromptsDir = path2.join(
|
|
90
|
+
workspaceRoot,
|
|
91
|
+
".claude",
|
|
92
|
+
"prompts"
|
|
93
|
+
);
|
|
94
|
+
if (await fs2.pathExists(claudePromptsDir)) {
|
|
95
|
+
const files = await fs2.readdir(claudePromptsDir);
|
|
96
|
+
for (const f of files) {
|
|
97
|
+
if (f.endsWith(".md")) {
|
|
98
|
+
existing.push(path2.join(".claude", "prompts", f));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const rootFile = path2.join(workspaceRoot, "CLAUDE.md");
|
|
103
|
+
if (await fs2.pathExists(rootFile)) {
|
|
104
|
+
existing.push("CLAUDE.md");
|
|
105
|
+
}
|
|
106
|
+
return existing;
|
|
107
|
+
}
|
|
108
|
+
getPostInitMessage() {
|
|
109
|
+
return [
|
|
110
|
+
"",
|
|
111
|
+
"\u{1F4CB} Claude Code setup complete!",
|
|
112
|
+
"",
|
|
113
|
+
" Your sub-agent prompts are in .claude/prompts/",
|
|
114
|
+
" A root CLAUDE.md has been created with references to the sub-agents.",
|
|
115
|
+
"",
|
|
116
|
+
" How to use:",
|
|
117
|
+
" 1. Claude Code automatically reads CLAUDE.md for project context",
|
|
118
|
+
' 2. Reference specific sub-agents: "Use the planner from .claude/prompts/planner.md"',
|
|
119
|
+
" 3. Customize the Project Context block in each file for your project",
|
|
120
|
+
""
|
|
121
|
+
].join("\n");
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
function generateClaudeRootMd(installedPrompts) {
|
|
125
|
+
const lines = [
|
|
126
|
+
"<!-- spec-lite managed \u2014 regenerated on spec-lite init/update -->",
|
|
127
|
+
"",
|
|
128
|
+
"# Project Instructions",
|
|
129
|
+
"",
|
|
130
|
+
"This project uses [spec-lite](https://github.com/ranjithab/spec-lite) sub-agent prompts",
|
|
131
|
+
"for structured software engineering workflows.",
|
|
132
|
+
"",
|
|
133
|
+
"## Available Sub-Agents",
|
|
134
|
+
"",
|
|
135
|
+
"The following specialist sub-agents are available in `.claude/prompts/`:",
|
|
136
|
+
""
|
|
137
|
+
];
|
|
138
|
+
for (const name of installedPrompts) {
|
|
139
|
+
lines.push(`- [${name}](.claude/prompts/${name}.md)`);
|
|
140
|
+
}
|
|
141
|
+
lines.push(
|
|
142
|
+
"",
|
|
143
|
+
"## Usage",
|
|
144
|
+
"",
|
|
145
|
+
"To use a sub-agent, reference its prompt file in your conversation:",
|
|
146
|
+
"",
|
|
147
|
+
"```text",
|
|
148
|
+
"Use the planner from .claude/prompts/planner.md to create a technical plan for this project.",
|
|
149
|
+
"```",
|
|
150
|
+
"",
|
|
151
|
+
"## Output Directory",
|
|
152
|
+
"",
|
|
153
|
+
"Sub-agent outputs are written to the `.spec/` directory:",
|
|
154
|
+
"",
|
|
155
|
+
"```text",
|
|
156
|
+
".spec/",
|
|
157
|
+
"\u251C\u2500\u2500 brainstorm.md",
|
|
158
|
+
"\u251C\u2500\u2500 plan.md # Default plan (simple projects)",
|
|
159
|
+
"\u251C\u2500\u2500 plan_<name>.md # Named plans (complex projects)",
|
|
160
|
+
"\u251C\u2500\u2500 TODO.md",
|
|
161
|
+
"\u251C\u2500\u2500 features/",
|
|
162
|
+
"\u2514\u2500\u2500 reviews/",
|
|
163
|
+
"```",
|
|
164
|
+
""
|
|
165
|
+
);
|
|
166
|
+
return lines.join("\n");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/providers/generic.ts
|
|
170
|
+
import path3 from "path";
|
|
171
|
+
import fs3 from "fs-extra";
|
|
172
|
+
var GenericProvider = class {
|
|
173
|
+
name = "Generic";
|
|
174
|
+
alias = "generic";
|
|
175
|
+
description = "Raw prompts in .spec-lite/prompts/ (copy-paste into any LLM)";
|
|
176
|
+
getTargetPath(promptName) {
|
|
177
|
+
return path3.join(".spec-lite", "prompts", `${promptName}.md`);
|
|
178
|
+
}
|
|
179
|
+
transformPrompt(content, meta) {
|
|
180
|
+
const header = [
|
|
181
|
+
`<!-- spec-lite | ${meta.name} | managed by spec-lite -->`,
|
|
182
|
+
`<!-- To update: run "spec-lite update" -->`,
|
|
183
|
+
""
|
|
184
|
+
].join("\n");
|
|
185
|
+
return header + content;
|
|
186
|
+
}
|
|
187
|
+
async detectExisting(workspaceRoot) {
|
|
188
|
+
const existing = [];
|
|
189
|
+
const dir = path3.join(workspaceRoot, ".spec-lite", "prompts");
|
|
190
|
+
if (await fs3.pathExists(dir)) {
|
|
191
|
+
const files = await fs3.readdir(dir);
|
|
192
|
+
for (const f of files) {
|
|
193
|
+
if (f.endsWith(".md")) {
|
|
194
|
+
existing.push(path3.join(".spec-lite", "prompts", f));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return existing;
|
|
199
|
+
}
|
|
200
|
+
getPostInitMessage() {
|
|
201
|
+
return [
|
|
202
|
+
"",
|
|
203
|
+
"\u{1F4CB} Generic setup complete!",
|
|
204
|
+
"",
|
|
205
|
+
" Your sub-agent prompts are in .spec-lite/prompts/",
|
|
206
|
+
"",
|
|
207
|
+
" How to use:",
|
|
208
|
+
" 1. Open any prompt file and copy its content",
|
|
209
|
+
" 2. Paste into your LLM of choice (ChatGPT, Claude, Gemini, etc.)",
|
|
210
|
+
" 3. Fill in the Project Context section for your project",
|
|
211
|
+
" 4. Start the conversation with your requirements",
|
|
212
|
+
""
|
|
213
|
+
].join("\n");
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// src/providers/index.ts
|
|
218
|
+
var providers = [
|
|
219
|
+
new CopilotProvider(),
|
|
220
|
+
new ClaudeCodeProvider(),
|
|
221
|
+
new GenericProvider()
|
|
222
|
+
];
|
|
223
|
+
function getProvider(alias) {
|
|
224
|
+
return providers.find((p) => p.alias === alias);
|
|
225
|
+
}
|
|
226
|
+
function getAllProviders() {
|
|
227
|
+
return [...providers];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// src/utils/prompts.ts
|
|
231
|
+
import path4 from "path";
|
|
232
|
+
import fs4 from "fs-extra";
|
|
233
|
+
import { fileURLToPath } from "url";
|
|
234
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
235
|
+
var __dirname = path4.dirname(__filename);
|
|
236
|
+
function getPromptsDir() {
|
|
237
|
+
return path4.resolve(__dirname, "..", "prompts");
|
|
238
|
+
}
|
|
239
|
+
var PROMPT_CATALOG = {
|
|
240
|
+
spec_help: {
|
|
241
|
+
title: "Spec Help",
|
|
242
|
+
description: "Lists available sub-agents, their purpose, inputs, and outputs",
|
|
243
|
+
output: "(interactive guide)"
|
|
244
|
+
},
|
|
245
|
+
brainstorm: {
|
|
246
|
+
title: "Brainstorm",
|
|
247
|
+
description: "Refines a vague idea into a clear, actionable vision",
|
|
248
|
+
output: ".spec/brainstorm.md"
|
|
249
|
+
},
|
|
250
|
+
planner: {
|
|
251
|
+
title: "Planner",
|
|
252
|
+
description: "Creates a detailed technical blueprint from requirements",
|
|
253
|
+
output: ".spec/plan.md or .spec/plan_<name>.md"
|
|
254
|
+
},
|
|
255
|
+
feature: {
|
|
256
|
+
title: "Feature",
|
|
257
|
+
description: "Breaks one feature into granular, verifiable vertical slices",
|
|
258
|
+
output: ".spec/features/feature_<name>.md"
|
|
259
|
+
},
|
|
260
|
+
implement: {
|
|
261
|
+
title: "Implement",
|
|
262
|
+
description: "Picks up a feature spec and executes its tasks with code",
|
|
263
|
+
output: "Working code + updated feature spec"
|
|
264
|
+
},
|
|
265
|
+
code_review: {
|
|
266
|
+
title: "Code Review",
|
|
267
|
+
description: "Reviews code for correctness, architecture, and readability",
|
|
268
|
+
output: ".spec/reviews/code_review_<name>.md"
|
|
269
|
+
},
|
|
270
|
+
security_audit: {
|
|
271
|
+
title: "Security Audit",
|
|
272
|
+
description: "Scans for vulnerabilities, misconfigurations, and security risks",
|
|
273
|
+
output: ".spec/reviews/security_audit_<scope>.md"
|
|
274
|
+
},
|
|
275
|
+
performance_review: {
|
|
276
|
+
title: "Performance Review",
|
|
277
|
+
description: "Identifies bottlenecks and optimization opportunities",
|
|
278
|
+
output: ".spec/reviews/performance_review_<scope>.md"
|
|
279
|
+
},
|
|
280
|
+
integration_tests: {
|
|
281
|
+
title: "Integration Tests",
|
|
282
|
+
description: "Writes traceable integration test scenarios from feature specs",
|
|
283
|
+
output: "tests/"
|
|
284
|
+
},
|
|
285
|
+
unit_tests: {
|
|
286
|
+
title: "Unit Tests",
|
|
287
|
+
description: "Generates comprehensive unit tests with edge-case coverage and smart coverage exclusions",
|
|
288
|
+
output: ".spec/features/unit_tests_<name>.md"
|
|
289
|
+
},
|
|
290
|
+
devops: {
|
|
291
|
+
title: "DevOps",
|
|
292
|
+
description: "Sets up Docker, CI/CD, environments, and deployment",
|
|
293
|
+
output: "Project infrastructure files"
|
|
294
|
+
},
|
|
295
|
+
fix: {
|
|
296
|
+
title: "Fix & Refactor",
|
|
297
|
+
description: "Debugs issues or restructures code safely",
|
|
298
|
+
output: "Targeted fixes with verification"
|
|
299
|
+
},
|
|
300
|
+
memorize: {
|
|
301
|
+
title: "Memorize",
|
|
302
|
+
description: "Stores standing instructions that all sub-agents enforce. Use `/memorize bootstrap` to auto-generate from project analysis.",
|
|
303
|
+
output: ".spec/memory.md"
|
|
304
|
+
},
|
|
305
|
+
technical_docs: {
|
|
306
|
+
title: "Technical Docs",
|
|
307
|
+
description: "Creates deep architecture documentation for developers",
|
|
308
|
+
output: "docs/technical_architecture.md"
|
|
309
|
+
},
|
|
310
|
+
readme: {
|
|
311
|
+
title: "README",
|
|
312
|
+
description: "Writes the project README and optional user guide",
|
|
313
|
+
output: "README.md + docs/user_guide.md"
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
var SKIP_FILES = /* @__PURE__ */ new Set(["orchestrator"]);
|
|
317
|
+
async function loadPrompts(exclude = []) {
|
|
318
|
+
const promptsDir = getPromptsDir();
|
|
319
|
+
const excludeSet = /* @__PURE__ */ new Set([...exclude, ...SKIP_FILES]);
|
|
320
|
+
const files = await fs4.readdir(promptsDir);
|
|
321
|
+
const prompts = [];
|
|
322
|
+
for (const file of files) {
|
|
323
|
+
if (!file.endsWith(".md")) continue;
|
|
324
|
+
const name = file.replace(".md", "");
|
|
325
|
+
if (excludeSet.has(name)) continue;
|
|
326
|
+
const filePath = path4.join(promptsDir, file);
|
|
327
|
+
const content = await fs4.readFile(filePath, "utf-8");
|
|
328
|
+
const catalog = PROMPT_CATALOG[name];
|
|
329
|
+
prompts.push({
|
|
330
|
+
name,
|
|
331
|
+
filePath,
|
|
332
|
+
content,
|
|
333
|
+
title: catalog?.title ?? name,
|
|
334
|
+
description: catalog?.description ?? ""
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
return prompts;
|
|
338
|
+
}
|
|
339
|
+
function getPromptCatalog() {
|
|
340
|
+
return PROMPT_CATALOG;
|
|
341
|
+
}
|
|
342
|
+
var CONTEXT_START_MARKER = "<!-- project-context-start -->";
|
|
343
|
+
var CONTEXT_END_MARKER = "<!-- project-context-end -->";
|
|
344
|
+
function extractProjectContext(content) {
|
|
345
|
+
const startIdx = content.indexOf(CONTEXT_START_MARKER);
|
|
346
|
+
const endIdx = content.indexOf(CONTEXT_END_MARKER);
|
|
347
|
+
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) return null;
|
|
348
|
+
return content.substring(
|
|
349
|
+
startIdx + CONTEXT_START_MARKER.length,
|
|
350
|
+
endIdx
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
function replaceProjectContext(content, newContext) {
|
|
354
|
+
const startIdx = content.indexOf(CONTEXT_START_MARKER);
|
|
355
|
+
const endIdx = content.indexOf(CONTEXT_END_MARKER);
|
|
356
|
+
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) return content;
|
|
357
|
+
return content.substring(0, startIdx + CONTEXT_START_MARKER.length) + newContext + content.substring(endIdx);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// src/utils/stacks.ts
|
|
361
|
+
import path5 from "path";
|
|
362
|
+
import fs5 from "fs-extra";
|
|
363
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
364
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
365
|
+
var __dirname2 = path5.dirname(__filename2);
|
|
366
|
+
function getStacksDir() {
|
|
367
|
+
return path5.resolve(__dirname2, "stacks");
|
|
368
|
+
}
|
|
369
|
+
var LANGUAGE_MAP = {
|
|
370
|
+
typescript: "typescript.md",
|
|
371
|
+
ts: "typescript.md",
|
|
372
|
+
javascript: "typescript.md",
|
|
373
|
+
js: "typescript.md",
|
|
374
|
+
"node.js": "typescript.md",
|
|
375
|
+
node: "typescript.md",
|
|
376
|
+
react: "react.md",
|
|
377
|
+
"react.js": "react.md",
|
|
378
|
+
"next.js": "react.md",
|
|
379
|
+
nextjs: "react.md",
|
|
380
|
+
python: "python.md",
|
|
381
|
+
py: "python.md",
|
|
382
|
+
"c#": "dotnet.md",
|
|
383
|
+
csharp: "dotnet.md",
|
|
384
|
+
".net": "dotnet.md",
|
|
385
|
+
dotnet: "dotnet.md",
|
|
386
|
+
java: "java.md",
|
|
387
|
+
"spring": "java.md",
|
|
388
|
+
"spring boot": "java.md",
|
|
389
|
+
"spring-boot": "java.md"
|
|
390
|
+
};
|
|
391
|
+
function getStackSnippet(language) {
|
|
392
|
+
const key = language.toLowerCase().trim();
|
|
393
|
+
const filename = LANGUAGE_MAP[key];
|
|
394
|
+
if (!filename) return null;
|
|
395
|
+
const filePath = path5.join(getStacksDir(), filename);
|
|
396
|
+
if (!fs5.pathExistsSync(filePath)) return null;
|
|
397
|
+
return fs5.readFileSync(filePath, "utf-8");
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// src/commands/init.ts
|
|
401
|
+
var LANGUAGE_CHOICES = [
|
|
402
|
+
{ name: "TypeScript", value: "TypeScript" },
|
|
403
|
+
{ name: "Python", value: "Python" },
|
|
404
|
+
{ name: "Java", value: "Java" },
|
|
405
|
+
{ name: "C# / .NET", value: "C#" },
|
|
406
|
+
{ name: "Go", value: "Go" },
|
|
407
|
+
{ name: "Rust", value: "Rust" },
|
|
408
|
+
{ name: "Other (specify below)", value: "__other__" }
|
|
409
|
+
];
|
|
410
|
+
var ARCHITECTURE_CHOICES = [
|
|
411
|
+
{ name: "Monolith", value: "Monolith" },
|
|
412
|
+
{ name: "Microservices", value: "Microservices" },
|
|
413
|
+
{ name: "Serverless", value: "Serverless" },
|
|
414
|
+
{ name: "Monorepo", value: "Monorepo" },
|
|
415
|
+
{ name: "Other (specify below)", value: "__other__" }
|
|
416
|
+
];
|
|
417
|
+
async function collectProjectProfile() {
|
|
418
|
+
console.log(
|
|
419
|
+
chalk.cyan(
|
|
420
|
+
"\n \u{1F4CB} Project Profile \u2014 a few questions to personalize your setup:\n"
|
|
421
|
+
)
|
|
422
|
+
);
|
|
423
|
+
const answers = await inquirer.prompt([
|
|
424
|
+
{
|
|
425
|
+
type: "list",
|
|
426
|
+
name: "language",
|
|
427
|
+
message: "Primary programming language?",
|
|
428
|
+
choices: LANGUAGE_CHOICES
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
type: "input",
|
|
432
|
+
name: "languageOther",
|
|
433
|
+
message: "Specify your primary language:",
|
|
434
|
+
when: (prev) => prev.language === "__other__",
|
|
435
|
+
validate: (input) => input.trim() ? true : "Please enter a language."
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
type: "input",
|
|
439
|
+
name: "frameworks",
|
|
440
|
+
message: 'Framework(s) in use? (e.g., "Express + React", "FastAPI", "ASP.NET Core")',
|
|
441
|
+
default: "None / not sure yet"
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
type: "input",
|
|
445
|
+
name: "testFramework",
|
|
446
|
+
message: 'Testing framework? (e.g., "Jest", "Vitest", "pytest", "xUnit")',
|
|
447
|
+
default: "Not decided yet"
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
type: "list",
|
|
451
|
+
name: "architecture",
|
|
452
|
+
message: "Architectural pattern?",
|
|
453
|
+
choices: ARCHITECTURE_CHOICES
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
type: "input",
|
|
457
|
+
name: "architectureOther",
|
|
458
|
+
message: "Specify your architectural pattern:",
|
|
459
|
+
when: (prev) => prev.architecture === "__other__",
|
|
460
|
+
validate: (input) => input.trim() ? true : "Please enter a pattern."
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
type: "input",
|
|
464
|
+
name: "conventions",
|
|
465
|
+
message: 'Any specific coding conventions? (e.g., "Airbnb style guide", "PEP 8") \u2014 leave blank if none',
|
|
466
|
+
default: ""
|
|
467
|
+
}
|
|
468
|
+
]);
|
|
469
|
+
return {
|
|
470
|
+
language: answers.language === "__other__" ? answers.languageOther.trim() : answers.language,
|
|
471
|
+
frameworks: answers.frameworks.trim(),
|
|
472
|
+
testFramework: answers.testFramework.trim(),
|
|
473
|
+
architecture: answers.architecture === "__other__" ? answers.architectureOther.trim() : answers.architecture,
|
|
474
|
+
conventions: answers.conventions.trim()
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
function buildProjectContextBlock(profile) {
|
|
478
|
+
const lines = [
|
|
479
|
+
"",
|
|
480
|
+
"## Project Context (Customize per project)",
|
|
481
|
+
"",
|
|
482
|
+
"> Auto-populated by spec-lite init. Edit these values as your project evolves.",
|
|
483
|
+
"",
|
|
484
|
+
`- **Language(s)**: ${profile.language}`,
|
|
485
|
+
`- **Framework(s)**: ${profile.frameworks}`,
|
|
486
|
+
`- **Test Framework**: ${profile.testFramework}`,
|
|
487
|
+
`- **Architecture**: ${profile.architecture}`
|
|
488
|
+
];
|
|
489
|
+
if (profile.conventions) {
|
|
490
|
+
lines.push(`- **Conventions**: ${profile.conventions}`);
|
|
491
|
+
}
|
|
492
|
+
lines.push("");
|
|
493
|
+
return lines.join("\n");
|
|
494
|
+
}
|
|
495
|
+
async function initCommand(options) {
|
|
496
|
+
const cwd = process.cwd();
|
|
497
|
+
console.log(chalk.bold("\n\u26A1 spec-lite init\n"));
|
|
498
|
+
let providerAlias = options.ai;
|
|
499
|
+
if (!providerAlias) {
|
|
500
|
+
const providers2 = getAllProviders();
|
|
501
|
+
const answer = await inquirer.prompt([
|
|
502
|
+
{
|
|
503
|
+
type: "list",
|
|
504
|
+
name: "provider",
|
|
505
|
+
message: "Which AI coding assistant are you using?",
|
|
506
|
+
choices: providers2.map((p) => ({
|
|
507
|
+
name: `${p.name} \u2014 ${p.description}`,
|
|
508
|
+
value: p.alias
|
|
509
|
+
}))
|
|
510
|
+
}
|
|
511
|
+
]);
|
|
512
|
+
providerAlias = answer.provider;
|
|
513
|
+
}
|
|
514
|
+
const provider = getProvider(providerAlias);
|
|
515
|
+
if (!provider) {
|
|
516
|
+
console.error(
|
|
517
|
+
chalk.red(
|
|
518
|
+
`Unknown provider: "${providerAlias}". Available: ${getAllProviders().map((p) => p.alias).join(", ")}`
|
|
519
|
+
)
|
|
520
|
+
);
|
|
521
|
+
process.exit(1);
|
|
522
|
+
}
|
|
523
|
+
console.log(chalk.cyan(` Provider: ${provider.name}`));
|
|
524
|
+
let projectProfile;
|
|
525
|
+
if (!options.skipProfile) {
|
|
526
|
+
projectProfile = await collectProjectProfile();
|
|
527
|
+
console.log(chalk.green(" \u2713 Project profile collected"));
|
|
528
|
+
} else {
|
|
529
|
+
console.log(chalk.dim(" Skipping project profile questionnaire."));
|
|
530
|
+
}
|
|
531
|
+
const exclude = options.exclude ? options.exclude.split(/[,\s]+/).map((s) => s.trim()).filter(Boolean) : [];
|
|
532
|
+
if (exclude.length > 0) {
|
|
533
|
+
console.log(chalk.dim(` Excluding: ${exclude.join(", ")}`));
|
|
534
|
+
}
|
|
535
|
+
const existingFiles = await provider.detectExisting(cwd);
|
|
536
|
+
if (existingFiles.length > 0 && !options.force) {
|
|
537
|
+
console.log(
|
|
538
|
+
chalk.yellow(
|
|
539
|
+
`
|
|
540
|
+
Found existing instruction files:
|
|
541
|
+
${existingFiles.map((f) => ` - ${f}`).join("\n")}`
|
|
542
|
+
)
|
|
543
|
+
);
|
|
544
|
+
const answer = await inquirer.prompt([
|
|
545
|
+
{
|
|
546
|
+
type: "list",
|
|
547
|
+
name: "action",
|
|
548
|
+
message: "How should we handle existing files?",
|
|
549
|
+
choices: [
|
|
550
|
+
{
|
|
551
|
+
name: "Overwrite \u2014 replace all existing files",
|
|
552
|
+
value: "overwrite"
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
name: "Skip \u2014 only write files that don't exist yet",
|
|
556
|
+
value: "skip"
|
|
557
|
+
},
|
|
558
|
+
{ name: "Abort \u2014 cancel initialization", value: "abort" }
|
|
559
|
+
]
|
|
560
|
+
}
|
|
561
|
+
]);
|
|
562
|
+
if (answer.action === "abort") {
|
|
563
|
+
console.log(chalk.dim(" Aborted."));
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
if (answer.action === "skip") {
|
|
567
|
+
console.log(chalk.dim(" Skipping existing files."));
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
const contextBlock = projectProfile ? buildProjectContextBlock(projectProfile) : null;
|
|
571
|
+
const prompts = await loadPrompts(exclude);
|
|
572
|
+
let written = 0;
|
|
573
|
+
let skipped = 0;
|
|
574
|
+
const installedPrompts = [];
|
|
575
|
+
for (const prompt of prompts) {
|
|
576
|
+
const targetRelPath = provider.getTargetPath(prompt.name);
|
|
577
|
+
const targetAbsPath = path6.join(cwd, targetRelPath);
|
|
578
|
+
if (!options.force && existingFiles.includes(targetRelPath) && existingFiles.length > 0) {
|
|
579
|
+
const answer = await inquirer.prompt([
|
|
580
|
+
{
|
|
581
|
+
type: "list",
|
|
582
|
+
name: "action",
|
|
583
|
+
message: `How should we handle existing files?`,
|
|
584
|
+
choices: [
|
|
585
|
+
{ name: "Overwrite", value: "overwrite" },
|
|
586
|
+
{ name: "Skip", value: "skip" }
|
|
587
|
+
]
|
|
588
|
+
}
|
|
589
|
+
]);
|
|
590
|
+
if (answer.action === "skip") {
|
|
591
|
+
skipped++;
|
|
592
|
+
installedPrompts.push(prompt.name);
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
let content = prompt.content;
|
|
597
|
+
if (contextBlock) {
|
|
598
|
+
content = replaceProjectContext(content, contextBlock);
|
|
599
|
+
}
|
|
600
|
+
const transformed = provider.transformPrompt(content, {
|
|
601
|
+
name: prompt.name,
|
|
602
|
+
title: prompt.title,
|
|
603
|
+
description: prompt.description
|
|
604
|
+
});
|
|
605
|
+
await fs6.ensureDir(path6.dirname(targetAbsPath));
|
|
606
|
+
await fs6.writeFile(targetAbsPath, transformed, "utf-8");
|
|
607
|
+
written++;
|
|
608
|
+
installedPrompts.push(prompt.name);
|
|
609
|
+
console.log(chalk.green(` \u2713 ${targetRelPath}`));
|
|
610
|
+
}
|
|
611
|
+
if (provider.alias === "claude-code") {
|
|
612
|
+
const claudeMdPath = path6.join(cwd, "CLAUDE.md");
|
|
613
|
+
const claudeMdContent = generateClaudeRootMd(installedPrompts);
|
|
614
|
+
await fs6.writeFile(claudeMdPath, claudeMdContent, "utf-8");
|
|
615
|
+
console.log(chalk.green(` \u2713 CLAUDE.md`));
|
|
616
|
+
written++;
|
|
617
|
+
}
|
|
618
|
+
const specDirs = [
|
|
619
|
+
".spec",
|
|
620
|
+
path6.join(".spec", "features"),
|
|
621
|
+
path6.join(".spec", "reviews")
|
|
622
|
+
];
|
|
623
|
+
for (const dir of specDirs) {
|
|
624
|
+
const absDir = path6.join(cwd, dir);
|
|
625
|
+
if (!await fs6.pathExists(absDir)) {
|
|
626
|
+
await fs6.ensureDir(absDir);
|
|
627
|
+
console.log(chalk.green(` \u2713 ${dir}/`));
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
const todoPath = path6.join(cwd, ".spec", "TODO.md");
|
|
631
|
+
if (!await fs6.pathExists(todoPath)) {
|
|
632
|
+
const todoContent = [
|
|
633
|
+
"# TODO \u2014 Enhancements & Ideas",
|
|
634
|
+
"",
|
|
635
|
+
"> Discovered by sub-agents during planning and development.",
|
|
636
|
+
"> Items here are out-of-scope for their current task but worth tracking.",
|
|
637
|
+
"",
|
|
638
|
+
"## General",
|
|
639
|
+
"",
|
|
640
|
+
"## General / Caching",
|
|
641
|
+
"",
|
|
642
|
+
"## UI",
|
|
643
|
+
"",
|
|
644
|
+
"## Performance",
|
|
645
|
+
"",
|
|
646
|
+
"## Security",
|
|
647
|
+
"",
|
|
648
|
+
"## DX (Developer Experience)",
|
|
649
|
+
""
|
|
650
|
+
].join("\n");
|
|
651
|
+
await fs6.writeFile(todoPath, todoContent, "utf-8");
|
|
652
|
+
console.log(chalk.green(` \u2713 .spec/TODO.md`));
|
|
653
|
+
}
|
|
654
|
+
if (projectProfile) {
|
|
655
|
+
const snippet = getStackSnippet(projectProfile.language);
|
|
656
|
+
if (snippet) {
|
|
657
|
+
const stacksTargetDir = path6.join(cwd, ".spec-lite", "stacks");
|
|
658
|
+
await fs6.ensureDir(stacksTargetDir);
|
|
659
|
+
const snippetFileName = `${projectProfile.language.toLowerCase().replace(/[^a-z0-9]/g, "-")}.md`;
|
|
660
|
+
const snippetPath = path6.join(stacksTargetDir, snippetFileName);
|
|
661
|
+
if (await fs6.pathExists(snippetPath) && !options.force) {
|
|
662
|
+
console.log(
|
|
663
|
+
chalk.dim(` \u2013 .spec-lite/stacks/${snippetFileName} already exists (kept your edits)`)
|
|
664
|
+
);
|
|
665
|
+
} else {
|
|
666
|
+
await fs6.writeFile(snippetPath, snippet, "utf-8");
|
|
667
|
+
console.log(
|
|
668
|
+
chalk.green(` \u2713 .spec-lite/stacks/${snippetFileName}`)
|
|
669
|
+
);
|
|
670
|
+
console.log(
|
|
671
|
+
chalk.dim(" \u21B3 Edit this file to customize defaults before running /memorize bootstrap")
|
|
672
|
+
);
|
|
673
|
+
written++;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
const pkg2 = await loadPackageVersion();
|
|
678
|
+
const config = {
|
|
679
|
+
version: pkg2,
|
|
680
|
+
provider: provider.alias,
|
|
681
|
+
installedPrompts,
|
|
682
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
683
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
684
|
+
...projectProfile ? { projectProfile } : {}
|
|
685
|
+
};
|
|
686
|
+
const configPath = path6.join(cwd, ".spec-lite.json");
|
|
687
|
+
await fs6.writeJson(configPath, config, { spaces: 2 });
|
|
688
|
+
console.log(chalk.green(` \u2713 .spec-lite.json`));
|
|
689
|
+
console.log(
|
|
690
|
+
chalk.bold(
|
|
691
|
+
`
|
|
692
|
+
Done! ${written} files written, ${skipped} skipped.`
|
|
693
|
+
)
|
|
694
|
+
);
|
|
695
|
+
console.log(provider.getPostInitMessage());
|
|
696
|
+
if (projectProfile) {
|
|
697
|
+
console.log(
|
|
698
|
+
chalk.cyan(
|
|
699
|
+
"\n \u{1F4CC} Next step: Run "
|
|
700
|
+
) + chalk.bold("/memorize bootstrap") + chalk.cyan(
|
|
701
|
+
" in your AI assistant to auto-generate\n coding standards, architecture guidelines, and best practices\n for your project based on the profile you just provided."
|
|
702
|
+
)
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
async function loadPackageVersion() {
|
|
707
|
+
try {
|
|
708
|
+
const { createRequire: createRequire2 } = await import("module");
|
|
709
|
+
const require3 = createRequire2(import.meta.url);
|
|
710
|
+
const pkg2 = require3("../../package.json");
|
|
711
|
+
return pkg2.version;
|
|
712
|
+
} catch {
|
|
713
|
+
return "1.0.0";
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// src/commands/update.ts
|
|
718
|
+
import path7 from "path";
|
|
719
|
+
import fs7 from "fs-extra";
|
|
720
|
+
import chalk2 from "chalk";
|
|
721
|
+
async function updateCommand(options) {
|
|
722
|
+
const cwd = process.cwd();
|
|
723
|
+
console.log(chalk2.bold("\n\u26A1 spec-lite update\n"));
|
|
724
|
+
const configPath = path7.join(cwd, ".spec-lite.json");
|
|
725
|
+
if (!await fs7.pathExists(configPath)) {
|
|
726
|
+
console.error(
|
|
727
|
+
chalk2.red(
|
|
728
|
+
' No .spec-lite.json found. Run "spec-lite init" first.'
|
|
729
|
+
)
|
|
730
|
+
);
|
|
731
|
+
process.exit(1);
|
|
732
|
+
}
|
|
733
|
+
const config = await fs7.readJson(configPath);
|
|
734
|
+
const provider = getProvider(config.provider);
|
|
735
|
+
if (!provider) {
|
|
736
|
+
console.error(
|
|
737
|
+
chalk2.red(
|
|
738
|
+
` Unknown provider "${config.provider}" in .spec-lite.json. Re-run "spec-lite init".`
|
|
739
|
+
)
|
|
740
|
+
);
|
|
741
|
+
process.exit(1);
|
|
742
|
+
}
|
|
743
|
+
console.log(chalk2.cyan(` Provider: ${provider.name}`));
|
|
744
|
+
console.log(
|
|
745
|
+
chalk2.dim(` Installed: ${config.installedPrompts.length} prompts`)
|
|
746
|
+
);
|
|
747
|
+
const allPrompts = await loadPrompts();
|
|
748
|
+
const installedSet = new Set(config.installedPrompts);
|
|
749
|
+
const prompts = allPrompts.filter((p) => installedSet.has(p.name));
|
|
750
|
+
let updated = 0;
|
|
751
|
+
let preserved = 0;
|
|
752
|
+
let unchanged = 0;
|
|
753
|
+
let migrated = 0;
|
|
754
|
+
for (const prompt of prompts) {
|
|
755
|
+
const targetRelPath = provider.getTargetPath(prompt.name);
|
|
756
|
+
const targetAbsPath = path7.join(cwd, targetRelPath);
|
|
757
|
+
if (provider.alias === "copilot") {
|
|
758
|
+
const oldRelPath = path7.join(".github", "copilot", `${prompt.name}.md`);
|
|
759
|
+
const oldAbsPath = path7.join(cwd, oldRelPath);
|
|
760
|
+
if (targetRelPath !== oldRelPath && await fs7.pathExists(oldAbsPath) && !await fs7.pathExists(targetAbsPath)) {
|
|
761
|
+
await fs7.ensureDir(path7.dirname(targetAbsPath));
|
|
762
|
+
await fs7.rename(oldAbsPath, targetAbsPath);
|
|
763
|
+
console.log(
|
|
764
|
+
chalk2.cyan(
|
|
765
|
+
` \u2197 ${oldRelPath} \u2192 ${targetRelPath} (migrated to .prompt.md)`
|
|
766
|
+
)
|
|
767
|
+
);
|
|
768
|
+
migrated++;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
const newContent = provider.transformPrompt(prompt.content, {
|
|
772
|
+
name: prompt.name,
|
|
773
|
+
title: prompt.title,
|
|
774
|
+
description: prompt.description
|
|
775
|
+
});
|
|
776
|
+
if (!await fs7.pathExists(targetAbsPath)) {
|
|
777
|
+
await fs7.ensureDir(path7.dirname(targetAbsPath));
|
|
778
|
+
await fs7.writeFile(targetAbsPath, newContent, "utf-8");
|
|
779
|
+
console.log(chalk2.green(` \u2713 ${targetRelPath} (restored)`));
|
|
780
|
+
updated++;
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
const currentContent = await fs7.readFile(targetAbsPath, "utf-8");
|
|
784
|
+
if (currentContent === newContent) {
|
|
785
|
+
unchanged++;
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
if (!options.force) {
|
|
789
|
+
const userContext = extractProjectContext(currentContent);
|
|
790
|
+
if (userContext) {
|
|
791
|
+
const mergedContent = replaceProjectContext(newContent, userContext);
|
|
792
|
+
await fs7.writeFile(targetAbsPath, mergedContent, "utf-8");
|
|
793
|
+
console.log(
|
|
794
|
+
chalk2.green(
|
|
795
|
+
` \u2713 ${targetRelPath} (updated, Project Context preserved)`
|
|
796
|
+
)
|
|
797
|
+
);
|
|
798
|
+
preserved++;
|
|
799
|
+
updated++;
|
|
800
|
+
continue;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
await fs7.writeFile(targetAbsPath, newContent, "utf-8");
|
|
804
|
+
console.log(chalk2.green(` \u2713 ${targetRelPath} (updated)`));
|
|
805
|
+
updated++;
|
|
806
|
+
}
|
|
807
|
+
if (provider.alias === "claude-code") {
|
|
808
|
+
const claudeMdPath = path7.join(cwd, "CLAUDE.md");
|
|
809
|
+
const claudeMdContent = generateClaudeRootMd(config.installedPrompts);
|
|
810
|
+
await fs7.writeFile(claudeMdPath, claudeMdContent, "utf-8");
|
|
811
|
+
console.log(chalk2.green(` \u2713 CLAUDE.md (regenerated)`));
|
|
812
|
+
}
|
|
813
|
+
config.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
814
|
+
try {
|
|
815
|
+
const { createRequire: createRequire2 } = await import("module");
|
|
816
|
+
const require3 = createRequire2(import.meta.url);
|
|
817
|
+
const pkg2 = require3("../../package.json");
|
|
818
|
+
config.version = pkg2.version;
|
|
819
|
+
} catch {
|
|
820
|
+
}
|
|
821
|
+
await fs7.writeJson(configPath, config, { spaces: 2 });
|
|
822
|
+
console.log(
|
|
823
|
+
chalk2.bold(
|
|
824
|
+
`
|
|
825
|
+
Done! ${updated} updated, ${unchanged} unchanged, ${preserved} with preserved edits${migrated > 0 ? `, ${migrated} migrated` : ""}.`
|
|
826
|
+
)
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// src/commands/list.ts
|
|
831
|
+
import chalk3 from "chalk";
|
|
832
|
+
async function listCommand() {
|
|
833
|
+
const catalog = getPromptCatalog();
|
|
834
|
+
console.log(chalk3.bold("\n\u26A1 spec-lite \u2014 Available Sub-Agents\n"));
|
|
835
|
+
console.log(
|
|
836
|
+
chalk3.dim(
|
|
837
|
+
" Each sub-agent is a specialist prompt for one phase of the development lifecycle.\n"
|
|
838
|
+
)
|
|
839
|
+
);
|
|
840
|
+
const entries = Object.entries(catalog);
|
|
841
|
+
const maxName = Math.max(...entries.map(([name]) => name.length), 4);
|
|
842
|
+
const maxTitle = Math.max(
|
|
843
|
+
...entries.map(([, v]) => v.title.length),
|
|
844
|
+
5
|
|
845
|
+
);
|
|
846
|
+
const header = ` ${"Name".padEnd(maxName + 2)}${"Title".padEnd(maxTitle + 2)}${"Description"}`;
|
|
847
|
+
console.log(chalk3.cyan(header));
|
|
848
|
+
console.log(chalk3.dim(` ${"\u2500".repeat(header.trim().length + 10)}`));
|
|
849
|
+
for (const [name, meta] of entries) {
|
|
850
|
+
const nameCol = chalk3.green(name.padEnd(maxName + 2));
|
|
851
|
+
const titleCol = chalk3.white(meta.title.padEnd(maxTitle + 2));
|
|
852
|
+
const descCol = chalk3.dim(meta.description);
|
|
853
|
+
console.log(` ${nameCol}${titleCol}${descCol}`);
|
|
854
|
+
if (meta.output) {
|
|
855
|
+
console.log(
|
|
856
|
+
` ${"".padEnd(maxName + 2)}${"".padEnd(maxTitle + 2)}${chalk3.dim(`\u2192 ${meta.output}`)}`
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
console.log(
|
|
861
|
+
chalk3.dim(
|
|
862
|
+
`
|
|
863
|
+
${entries.length} sub-agents available. Run "spec-lite init" to install them.
|
|
864
|
+
`
|
|
865
|
+
)
|
|
866
|
+
);
|
|
867
|
+
console.log(chalk3.bold(" Recommended Pipeline:\n"));
|
|
868
|
+
console.log(
|
|
869
|
+
chalk3.dim(
|
|
870
|
+
" Brainstorm \u2192 Planner \u2192 Feature (\xD7N) \u2192 Reviews \u2192 Tests \u2192 DevOps \u2192 Docs"
|
|
871
|
+
)
|
|
872
|
+
);
|
|
873
|
+
console.log(
|
|
874
|
+
chalk3.dim(
|
|
875
|
+
" (Not every project needs every sub-agent. Start with Planner if you have clear requirements.)\n"
|
|
876
|
+
)
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// src/index.ts
|
|
881
|
+
import { createRequire } from "module";
|
|
882
|
+
var require2 = createRequire(import.meta.url);
|
|
883
|
+
var pkg = require2("../package.json");
|
|
884
|
+
var program = new Command();
|
|
885
|
+
program.name("spec-lite").description(
|
|
886
|
+
"Install structured AI sub-agent prompts into your workspace for any AI coding assistant"
|
|
887
|
+
).version(pkg.version);
|
|
888
|
+
program.command("init").description("Initialize spec-lite sub-agent prompts in your workspace").option(
|
|
889
|
+
"--ai <provider>",
|
|
890
|
+
"AI provider to configure for (copilot, claude-code, generic)"
|
|
891
|
+
).option(
|
|
892
|
+
"--exclude <prompts>",
|
|
893
|
+
"Comma-separated list of prompts to exclude (e.g., brainstorm,readme)"
|
|
894
|
+
).option("--force", "Overwrite existing files without prompting", false).option(
|
|
895
|
+
"--skip-profile",
|
|
896
|
+
"Skip the project profile questionnaire (for CI/scripting)"
|
|
897
|
+
).action(initCommand);
|
|
898
|
+
program.command("update").description(
|
|
899
|
+
"Update spec-lite prompts to the latest version, preserving your Project Context edits"
|
|
900
|
+
).option("--force", "Overwrite all files including user-modified ones", false).action(updateCommand);
|
|
901
|
+
program.command("list").description("List all available spec-lite sub-agents and their purpose").action(listCommand);
|
|
902
|
+
program.parse();
|
|
903
|
+
//# sourceMappingURL=index.js.map
|