@trieungoctam/speckit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -0
- package/bin/speckit +4 -0
- package/dist/adapters/antigravity-adapter.d.ts +2 -0
- package/dist/adapters/antigravity-adapter.js +41 -0
- package/dist/adapters/claude-code-adapter.d.ts +2 -0
- package/dist/adapters/claude-code-adapter.js +57 -0
- package/dist/adapters/codex-adapter.d.ts +2 -0
- package/dist/adapters/codex-adapter.js +54 -0
- package/dist/adapters/cursor-adapter.d.ts +2 -0
- package/dist/adapters/cursor-adapter.js +51 -0
- package/dist/adapters/ide-adapter.d.ts +7 -0
- package/dist/adapters/ide-adapter.js +1 -0
- package/dist/adapters/opencode-adapter.d.ts +2 -0
- package/dist/adapters/opencode-adapter.js +58 -0
- package/dist/adapters/tool-checks.d.ts +8 -0
- package/dist/adapters/tool-checks.js +28 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +104 -0
- package/dist/commands/doctor.d.ts +6 -0
- package/dist/commands/doctor.js +50 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.js +17 -0
- package/dist/commands/next.d.ts +4 -0
- package/dist/commands/next.js +17 -0
- package/dist/commands/plan.d.ts +6 -0
- package/dist/commands/plan.js +67 -0
- package/dist/commands/quick.d.ts +6 -0
- package/dist/commands/quick.js +57 -0
- package/dist/commands/review.d.ts +5 -0
- package/dist/commands/review.js +23 -0
- package/dist/commands/run.d.ts +6 -0
- package/dist/commands/run.js +42 -0
- package/dist/commands/shape.d.ts +6 -0
- package/dist/commands/shape.js +33 -0
- package/dist/commands/sync.d.ts +5 -0
- package/dist/commands/sync.js +54 -0
- package/dist/config/adapter-registry.d.ts +5 -0
- package/dist/config/adapter-registry.js +26 -0
- package/dist/core/managed-files.d.ts +14 -0
- package/dist/core/managed-files.js +46 -0
- package/dist/core/policy.d.ts +6 -0
- package/dist/core/policy.js +65 -0
- package/dist/core/scaffold.d.ts +2 -0
- package/dist/core/scaffold.js +71 -0
- package/dist/core/slug.d.ts +2 -0
- package/dist/core/slug.js +21 -0
- package/dist/core/test-detection.d.ts +7 -0
- package/dist/core/test-detection.js +27 -0
- package/docs/adapters.md +27 -0
- package/docs/code-standards.md +9 -0
- package/docs/development-roadmap.md +30 -0
- package/docs/enterprise-rollout.md +27 -0
- package/docs/product-contract.md +27 -0
- package/docs/project-changelog.md +33 -0
- package/docs/release-checklist.md +20 -0
- package/docs/system-architecture.md +27 -0
- package/docs/workflow-model.md +49 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Speckit
|
|
2
|
+
|
|
3
|
+
Speckit is a local-first Agile + TDD workflow compiler for agentic IDEs. It turns rough intent into specs, stories, TDD evidence, and native IDE instruction packs.
|
|
4
|
+
|
|
5
|
+
It is inspired by the operating patterns in ClaudeKit, BMad Method, and Beads Viewer, but it does not fork their runtime. Speckit owns the workflow contract and generates adapters for the tools you already use.
|
|
6
|
+
|
|
7
|
+
## Quickstart
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx @trieungoctam/speckit@latest init --ide all
|
|
11
|
+
npx @trieungoctam/speckit@latest quick "Add checkout validation"
|
|
12
|
+
npx @trieungoctam/speckit@latest review
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or install globally:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm i -g @trieungoctam/speckit
|
|
19
|
+
speckit init --ide cursor
|
|
20
|
+
speckit doctor
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Core Flow
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
intent
|
|
27
|
+
-> shape
|
|
28
|
+
-> plan
|
|
29
|
+
-> story with acceptance criteria
|
|
30
|
+
-> red test evidence
|
|
31
|
+
-> minimal implementation
|
|
32
|
+
-> green test evidence
|
|
33
|
+
-> refactor validation
|
|
34
|
+
-> review
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For implementation stories, red-green-refactor evidence is mandatory. A story is not review-ready until the evidence file records test intent, red result, green result, and refactor validation.
|
|
38
|
+
|
|
39
|
+
## Commands
|
|
40
|
+
|
|
41
|
+
| Command | Purpose |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| `speckit init` | Create `.speckit/` core rules and all IDE adapters. |
|
|
44
|
+
| `speckit init --ide <name>` | Generate one adapter: `claude-code`, `codex`, `antigravity`, `opencode`, or `cursor`. |
|
|
45
|
+
| `speckit doctor` | Report required tools, optional integrations, test commands, and adapter readiness. |
|
|
46
|
+
| `speckit shape "<intent>"` | Create a short spec contract. |
|
|
47
|
+
| `speckit quick "<intent>"` | Create one story plus matching TDD evidence file. |
|
|
48
|
+
| `speckit plan "<intent>"` | Create PRD, architecture, story, and evidence skeletons. |
|
|
49
|
+
| `speckit next` | Safely wraps `bv --robot-next --format json`. |
|
|
50
|
+
| `speckit sync` | Export Speckit stories as beads-compatible JSONL metadata. |
|
|
51
|
+
| `speckit run <story>` | Print the TDD execution handoff for a story. |
|
|
52
|
+
| `speckit review` | Print the review checklist and current diff summary. |
|
|
53
|
+
|
|
54
|
+
## Supported IDEs
|
|
55
|
+
|
|
56
|
+
Speckit generates native instruction/config files for:
|
|
57
|
+
|
|
58
|
+
- Claude Code
|
|
59
|
+
- Codex
|
|
60
|
+
- Antigravity
|
|
61
|
+
- OpenCode
|
|
62
|
+
- Cursor
|
|
63
|
+
|
|
64
|
+
See `docs/adapters.md` for exact output paths.
|
|
65
|
+
|
|
66
|
+
## Validation
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm run build
|
|
70
|
+
npm test
|
|
71
|
+
```
|
package/bin/speckit
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { markdown } from "../core/managed-files.js";
|
|
2
|
+
export const antigravityAdapter = {
|
|
3
|
+
name: "antigravity",
|
|
4
|
+
displayName: "Antigravity",
|
|
5
|
+
outputPaths: [
|
|
6
|
+
".agents/rules/speckit-agile.md",
|
|
7
|
+
".agents/rules/speckit-tdd.md",
|
|
8
|
+
".agents/rules/speckit-enterprise-safety.md",
|
|
9
|
+
".agents/workflows/speckit-plan.md",
|
|
10
|
+
".agents/workflows/speckit-tdd-run.md",
|
|
11
|
+
".agents/workflows/speckit-review.md",
|
|
12
|
+
],
|
|
13
|
+
render() {
|
|
14
|
+
return [
|
|
15
|
+
rule("agile", "Follow Speckit Agile: shape intent, create stories with ACs, preserve dependencies, and emit artifacts for review."),
|
|
16
|
+
rule("tdd", "For code changes, use TDD: record red evidence, green evidence, and refactor validation."),
|
|
17
|
+
rule("enterprise-safety", "Require human approval for destructive commands, production changes, secrets access, and deployment."),
|
|
18
|
+
workflow("plan", "Create a Speckit plan artifact with PRD, architecture, stories, risks, and dependencies."),
|
|
19
|
+
workflow("tdd-run", "Run a story with TDD. Emit artifact sections: Test Intent, Red, Green, Refactor."),
|
|
20
|
+
workflow("review", "Review the produced artifacts and code diff. Flag AC gaps, TDD gaps, security issues, and docs needs."),
|
|
21
|
+
];
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
function rule(name, body) {
|
|
25
|
+
return {
|
|
26
|
+
path: `.agents/rules/speckit-${name}.md`,
|
|
27
|
+
content: markdown(`# Speckit ${name}
|
|
28
|
+
|
|
29
|
+
${body}
|
|
30
|
+
`),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function workflow(name, body) {
|
|
34
|
+
return {
|
|
35
|
+
path: `.agents/workflows/speckit-${name}.md`,
|
|
36
|
+
content: markdown(`# /speckit-${name}
|
|
37
|
+
|
|
38
|
+
${body}
|
|
39
|
+
`),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { json, markdown } from "../core/managed-files.js";
|
|
2
|
+
const skillIntro = `Follow Speckit: Agile story flow plus mandatory TDD evidence. Use project-local context first.`;
|
|
3
|
+
export const claudeCodeAdapter = {
|
|
4
|
+
name: "claude-code",
|
|
5
|
+
displayName: "Claude Code",
|
|
6
|
+
outputPaths: [
|
|
7
|
+
"CLAUDE.md",
|
|
8
|
+
".claude/settings.json",
|
|
9
|
+
".claude/skills/speckit-plan/SKILL.md",
|
|
10
|
+
".claude/skills/speckit-tdd/SKILL.md",
|
|
11
|
+
".claude/skills/speckit-review/SKILL.md",
|
|
12
|
+
],
|
|
13
|
+
render() {
|
|
14
|
+
return [
|
|
15
|
+
{
|
|
16
|
+
path: "CLAUDE.md",
|
|
17
|
+
content: markdown(`# Speckit Project Instructions
|
|
18
|
+
|
|
19
|
+
Use Speckit for Agile + TDD work.
|
|
20
|
+
|
|
21
|
+
- Start from \`.speckit/rules/agile-policy.md\`.
|
|
22
|
+
- For implementation, follow \`.speckit/rules/tdd-policy.md\`.
|
|
23
|
+
- For safety, follow \`.speckit/rules/enterprise-safety.md\`.
|
|
24
|
+
- Prefer \`speckit quick\`, \`speckit plan\`, \`speckit run\`, and \`speckit review\` over ad hoc prompts.
|
|
25
|
+
`),
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
path: ".claude/settings.json",
|
|
29
|
+
content: json({
|
|
30
|
+
permissions: {
|
|
31
|
+
defaultMode: "ask",
|
|
32
|
+
},
|
|
33
|
+
includeCoAuthoredBy: false,
|
|
34
|
+
}),
|
|
35
|
+
},
|
|
36
|
+
skill("speckit-plan", "Create or update a Speckit Agile plan.", "Read `.speckit/workflows/shape.md`, then create PRD, architecture, and story artifacts with acceptance criteria."),
|
|
37
|
+
skill("speckit-tdd", "Execute a Speckit story with TDD.", "Read `.speckit/workflows/tdd-run.md`. Do not implement before test intent and red evidence are recorded."),
|
|
38
|
+
skill("speckit-review", "Review a Speckit change.", "Read `.speckit/workflows/review.md`. Prioritize bugs, TDD evidence gaps, security, and missing docs."),
|
|
39
|
+
];
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
function skill(name, description, body) {
|
|
43
|
+
return {
|
|
44
|
+
path: `.claude/skills/${name}/SKILL.md`,
|
|
45
|
+
content: markdown(`---
|
|
46
|
+
name: ${name}
|
|
47
|
+
description: "${description}"
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
# ${name}
|
|
51
|
+
|
|
52
|
+
${skillIntro}
|
|
53
|
+
|
|
54
|
+
${body}
|
|
55
|
+
`),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { markdown, text } from "../core/managed-files.js";
|
|
2
|
+
export const codexAdapter = {
|
|
3
|
+
name: "codex",
|
|
4
|
+
displayName: "Codex",
|
|
5
|
+
outputPaths: [
|
|
6
|
+
"AGENTS.md",
|
|
7
|
+
".codex/config.toml",
|
|
8
|
+
".speckit/codex-prompts/plan.md",
|
|
9
|
+
".speckit/codex-prompts/tdd-run.md",
|
|
10
|
+
".speckit/codex-prompts/review.md",
|
|
11
|
+
],
|
|
12
|
+
render() {
|
|
13
|
+
return [
|
|
14
|
+
{
|
|
15
|
+
path: "AGENTS.md",
|
|
16
|
+
content: markdown(`# Speckit Agent Instructions
|
|
17
|
+
|
|
18
|
+
This repository uses Speckit: Agile + TDD for agentic development.
|
|
19
|
+
|
|
20
|
+
Rules:
|
|
21
|
+
- Shape intent before implementation.
|
|
22
|
+
- For code stories, write test intent and capture red/green/refactor evidence.
|
|
23
|
+
- Never call bare \`bv\`; use \`bv --robot-*\`.
|
|
24
|
+
- Do not weaken approval or sandbox settings.
|
|
25
|
+
- Keep changes scoped to the story and update docs when behavior changes.
|
|
26
|
+
`),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
path: ".codex/config.toml",
|
|
30
|
+
content: text(`model = "gpt-5.4"
|
|
31
|
+
approval_policy = "on-request"
|
|
32
|
+
sandbox_mode = "workspace-write"
|
|
33
|
+
|
|
34
|
+
[tools]
|
|
35
|
+
web_search = true
|
|
36
|
+
|
|
37
|
+
[features]
|
|
38
|
+
child_agents_md = true`),
|
|
39
|
+
},
|
|
40
|
+
prompt("plan", "Create a Speckit plan from the user intent. Include PRD, architecture, stories, ACs, TDD checklist, and risks."),
|
|
41
|
+
prompt("tdd-run", "Execute the selected Speckit story using red-green-refactor. Record command evidence before marking done."),
|
|
42
|
+
prompt("review", "Review the current diff against Speckit ACs, TDD evidence, security, and docs impact."),
|
|
43
|
+
];
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
function prompt(name, body) {
|
|
47
|
+
return {
|
|
48
|
+
path: `.speckit/codex-prompts/${name}.md`,
|
|
49
|
+
content: markdown(`# Codex Prompt: ${name}
|
|
50
|
+
|
|
51
|
+
${body}
|
|
52
|
+
`),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { json, markdown } from "../core/managed-files.js";
|
|
2
|
+
export const cursorAdapter = {
|
|
3
|
+
name: "cursor",
|
|
4
|
+
displayName: "Cursor",
|
|
5
|
+
outputPaths: [
|
|
6
|
+
".cursor/rules/speckit-agile.mdc",
|
|
7
|
+
".cursor/rules/speckit-tdd.mdc",
|
|
8
|
+
".cursor/rules/speckit-review.mdc",
|
|
9
|
+
".cursor/rules/speckit-enterprise-safety.mdc",
|
|
10
|
+
".cursor/mcp.json",
|
|
11
|
+
"AGENTS.md",
|
|
12
|
+
],
|
|
13
|
+
render() {
|
|
14
|
+
return [
|
|
15
|
+
rule("agile", "alwaysApply: true", "Use Speckit Agile flow. Shape intent, create stories with ACs, and keep work scoped."),
|
|
16
|
+
rule("tdd", "description: Apply when implementing or modifying code", "Use red-green-refactor. Record TDD evidence before review-ready."),
|
|
17
|
+
rule("review", "description: Apply when reviewing code or preparing a PR", "Review AC coverage, TDD evidence, security, and docs impact."),
|
|
18
|
+
rule("enterprise-safety", "alwaysApply: true", "Do not expose secrets. Ask before destructive commands, deployment, or production changes."),
|
|
19
|
+
{
|
|
20
|
+
path: ".cursor/mcp.json",
|
|
21
|
+
content: json({
|
|
22
|
+
mcpServers: {},
|
|
23
|
+
}),
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
path: "AGENTS.md",
|
|
27
|
+
content: markdown(`# Speckit Agent Instructions
|
|
28
|
+
|
|
29
|
+
Use Speckit for Agile + TDD work. Cursor-specific rules live in \`.cursor/rules/*.mdc\`; this file is the cross-tool fallback.
|
|
30
|
+
|
|
31
|
+
- Follow \`.speckit/rules/agile-policy.md\`.
|
|
32
|
+
- Follow \`.speckit/rules/tdd-policy.md\` for code stories.
|
|
33
|
+
- Never run bare \`bv\`; use robot commands only.
|
|
34
|
+
`),
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
function rule(name, frontmatter, body) {
|
|
40
|
+
return {
|
|
41
|
+
path: `.cursor/rules/speckit-${name}.mdc`,
|
|
42
|
+
content: markdown(`---
|
|
43
|
+
${frontmatter}
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
# Speckit ${name}
|
|
47
|
+
|
|
48
|
+
${body}
|
|
49
|
+
`),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { json, markdown } from "../core/managed-files.js";
|
|
2
|
+
export const opencodeAdapter = {
|
|
3
|
+
name: "opencode",
|
|
4
|
+
displayName: "OpenCode",
|
|
5
|
+
outputPaths: [
|
|
6
|
+
"opencode.json",
|
|
7
|
+
".opencode/agent/speckit-planner.md",
|
|
8
|
+
".opencode/agent/speckit-tdd-developer.md",
|
|
9
|
+
".opencode/agent/speckit-reviewer.md",
|
|
10
|
+
".opencode/agent/speckit-docs.md",
|
|
11
|
+
],
|
|
12
|
+
render() {
|
|
13
|
+
return [
|
|
14
|
+
{
|
|
15
|
+
path: "opencode.json",
|
|
16
|
+
content: json({
|
|
17
|
+
$schema: "https://opencode.ai/config.json",
|
|
18
|
+
instructions: ["AGENTS.md", ".speckit/rules/*.md"],
|
|
19
|
+
permission: {
|
|
20
|
+
edit: "ask",
|
|
21
|
+
bash: "ask",
|
|
22
|
+
},
|
|
23
|
+
}),
|
|
24
|
+
},
|
|
25
|
+
agent("planner", "Plan Speckit Agile work without editing files.", "primary", {
|
|
26
|
+
edit: "deny",
|
|
27
|
+
bash: { "*": "ask", "git status*": "allow", "git diff*": "allow" },
|
|
28
|
+
}),
|
|
29
|
+
agent("tdd-developer", "Implement Speckit stories with TDD evidence.", "primary", {
|
|
30
|
+
edit: "ask",
|
|
31
|
+
bash: "ask",
|
|
32
|
+
}),
|
|
33
|
+
agent("reviewer", "Review changes without editing files.", "subagent", {
|
|
34
|
+
edit: "deny",
|
|
35
|
+
bash: { "*": "ask", "git diff*": "allow", "git log*": "allow", "npm test*": "allow" },
|
|
36
|
+
}),
|
|
37
|
+
agent("docs", "Update Speckit docs after approved changes.", "subagent", {
|
|
38
|
+
edit: "ask",
|
|
39
|
+
bash: "deny",
|
|
40
|
+
}),
|
|
41
|
+
];
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
function agent(name, description, mode, permission) {
|
|
45
|
+
return {
|
|
46
|
+
path: `.opencode/agent/speckit-${name}.md`,
|
|
47
|
+
content: markdown(`---
|
|
48
|
+
description: "${description}"
|
|
49
|
+
mode: ${mode}
|
|
50
|
+
permission: ${JSON.stringify(permission)}
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
# speckit-${name}
|
|
54
|
+
|
|
55
|
+
Follow \`.speckit/rules/agile-policy.md\`, \`.speckit/rules/tdd-policy.md\`, and \`.speckit/rules/enterprise-safety.md\`.
|
|
56
|
+
`),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
const tools = [
|
|
3
|
+
["git", true, "Install Git and run Speckit inside a repository."],
|
|
4
|
+
["node", true, "Install Node.js 20 or newer."],
|
|
5
|
+
["ck", false, "Recommended for ClaudeKit-style skills and workflows."],
|
|
6
|
+
["br", false, "Recommended beads CLI. Use bd only for older projects."],
|
|
7
|
+
["bd", false, "Legacy beads CLI fallback."],
|
|
8
|
+
["bv", false, "Recommended for Beads Viewer robot commands."],
|
|
9
|
+
["claude", false, "Claude Code adapter runtime."],
|
|
10
|
+
["codex", false, "Codex adapter runtime."],
|
|
11
|
+
["opencode", false, "OpenCode adapter runtime."],
|
|
12
|
+
["cursor", false, "Cursor adapter runtime."],
|
|
13
|
+
];
|
|
14
|
+
export function checkTools() {
|
|
15
|
+
return tools.map(([name, required, hint]) => ({
|
|
16
|
+
name,
|
|
17
|
+
required,
|
|
18
|
+
available: commandExists(name),
|
|
19
|
+
hint,
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
export function commandExists(command) {
|
|
23
|
+
const result = spawnSync(command, ["--version"], {
|
|
24
|
+
encoding: "utf8",
|
|
25
|
+
stdio: "ignore",
|
|
26
|
+
});
|
|
27
|
+
return result.status === 0;
|
|
28
|
+
}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function main(argv?: string[], root?: string): Promise<number>;
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { initCommand } from "./commands/init.js";
|
|
2
|
+
import { doctorCommand } from "./commands/doctor.js";
|
|
3
|
+
import { shapeCommand } from "./commands/shape.js";
|
|
4
|
+
import { quickCommand } from "./commands/quick.js";
|
|
5
|
+
import { planCommand } from "./commands/plan.js";
|
|
6
|
+
import { nextCommand } from "./commands/next.js";
|
|
7
|
+
import { syncCommand } from "./commands/sync.js";
|
|
8
|
+
import { runCommand } from "./commands/run.js";
|
|
9
|
+
import { reviewCommand } from "./commands/review.js";
|
|
10
|
+
export async function main(argv = process.argv.slice(2), root = process.cwd()) {
|
|
11
|
+
const parsed = parseArgs(argv);
|
|
12
|
+
try {
|
|
13
|
+
switch (parsed.command) {
|
|
14
|
+
case "init":
|
|
15
|
+
return initCommand({
|
|
16
|
+
root,
|
|
17
|
+
ide: value(parsed, "ide"),
|
|
18
|
+
force: has(parsed, "force"),
|
|
19
|
+
});
|
|
20
|
+
case "doctor":
|
|
21
|
+
return doctorCommand({ root, json: has(parsed, "json") });
|
|
22
|
+
case "shape":
|
|
23
|
+
return shapeCommand({ root, intent: requiredIntent(parsed) });
|
|
24
|
+
case "quick":
|
|
25
|
+
return quickCommand({ root, intent: requiredIntent(parsed) });
|
|
26
|
+
case "plan":
|
|
27
|
+
return planCommand({ root, intent: requiredIntent(parsed) });
|
|
28
|
+
case "next":
|
|
29
|
+
return nextCommand();
|
|
30
|
+
case "sync":
|
|
31
|
+
return syncCommand({ root });
|
|
32
|
+
case "run":
|
|
33
|
+
return runCommand({ root, target: requiredIntent(parsed) });
|
|
34
|
+
case "review":
|
|
35
|
+
return reviewCommand({ root });
|
|
36
|
+
case "help":
|
|
37
|
+
case "":
|
|
38
|
+
printHelp();
|
|
39
|
+
return 0;
|
|
40
|
+
default:
|
|
41
|
+
console.error(`Unknown command: ${parsed.command}`);
|
|
42
|
+
printHelp();
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
48
|
+
return 1;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function parseArgs(argv) {
|
|
52
|
+
const [command = "", ...rest] = argv;
|
|
53
|
+
const args = [];
|
|
54
|
+
const flags = new Map();
|
|
55
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
56
|
+
const token = rest[index];
|
|
57
|
+
if (!token.startsWith("--")) {
|
|
58
|
+
args.push(token);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const name = token.slice(2);
|
|
62
|
+
const next = rest[index + 1];
|
|
63
|
+
if (next && !next.startsWith("--")) {
|
|
64
|
+
flags.set(name, next);
|
|
65
|
+
index += 1;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
flags.set(name, true);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return { command, args, flags };
|
|
72
|
+
}
|
|
73
|
+
function requiredIntent(parsed) {
|
|
74
|
+
const intent = parsed.args.join(" ").trim();
|
|
75
|
+
if (!intent)
|
|
76
|
+
throw new Error(`Command "${parsed.command}" requires a target or intent.`);
|
|
77
|
+
return intent;
|
|
78
|
+
}
|
|
79
|
+
function value(parsed, name) {
|
|
80
|
+
const found = parsed.flags.get(name);
|
|
81
|
+
return typeof found === "string" ? found : undefined;
|
|
82
|
+
}
|
|
83
|
+
function has(parsed, name) {
|
|
84
|
+
return parsed.flags.has(name);
|
|
85
|
+
}
|
|
86
|
+
function printHelp() {
|
|
87
|
+
console.log(`Speckit - Agile + TDD workflow compiler for agentic IDEs
|
|
88
|
+
|
|
89
|
+
Usage:
|
|
90
|
+
speckit init [--ide all|claude-code|codex|antigravity|opencode|cursor] [--force]
|
|
91
|
+
speckit doctor [--json]
|
|
92
|
+
speckit shape "<intent>"
|
|
93
|
+
speckit quick "<intent>"
|
|
94
|
+
speckit plan "<intent>"
|
|
95
|
+
speckit next
|
|
96
|
+
speckit sync
|
|
97
|
+
speckit run <story-path-or-id>
|
|
98
|
+
speckit review
|
|
99
|
+
`);
|
|
100
|
+
}
|
|
101
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
102
|
+
const exitCode = await main();
|
|
103
|
+
process.exitCode = exitCode;
|
|
104
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { getAdapters } from "../config/adapter-registry.js";
|
|
4
|
+
import { checkTools } from "../adapters/tool-checks.js";
|
|
5
|
+
import { detectTestCommands } from "../core/test-detection.js";
|
|
6
|
+
export async function doctorCommand(options) {
|
|
7
|
+
const stdout = options.stdout ?? console;
|
|
8
|
+
const tools = checkTools();
|
|
9
|
+
const tests = await detectTestCommands(options.root);
|
|
10
|
+
const adapters = await Promise.all(getAdapters("all").map(async (adapter) => ({
|
|
11
|
+
name: adapter.name,
|
|
12
|
+
...(await adapterStatus(options.root, adapter.outputPaths)),
|
|
13
|
+
})));
|
|
14
|
+
const report = {
|
|
15
|
+
node: process.versions.node,
|
|
16
|
+
tools,
|
|
17
|
+
tests,
|
|
18
|
+
adapters,
|
|
19
|
+
status: tools.filter((tool) => tool.required && !tool.available).length === 0 ? "ok" : "needs-attention",
|
|
20
|
+
};
|
|
21
|
+
if (options.json) {
|
|
22
|
+
stdout.log(JSON.stringify(report, null, 2));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
stdout.log(`Speckit doctor: ${report.status}`);
|
|
26
|
+
stdout.log(`Node: ${report.node}`);
|
|
27
|
+
stdout.log(`Tools: ${tools.map((tool) => `${tool.name}=${tool.available ? "ok" : "missing"}`).join(", ")}`);
|
|
28
|
+
stdout.log(`Tests: ${tests[0]?.command ?? "not detected"}`);
|
|
29
|
+
for (const adapter of adapters) {
|
|
30
|
+
stdout.log(`Adapter ${adapter.name}: ${adapter.present}/${adapter.total} files present`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return report.status === "ok" ? 0 : 1;
|
|
34
|
+
}
|
|
35
|
+
async function adapterStatus(root, paths) {
|
|
36
|
+
const missing = [];
|
|
37
|
+
for (const path of paths) {
|
|
38
|
+
try {
|
|
39
|
+
await access(join(root, path));
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
missing.push(path);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
present: paths.length - missing.length,
|
|
47
|
+
total: paths.length,
|
|
48
|
+
missing,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { coreFiles } from "../core/scaffold.js";
|
|
2
|
+
import { writeManagedFiles } from "../core/managed-files.js";
|
|
3
|
+
import { getAdapters } from "../config/adapter-registry.js";
|
|
4
|
+
export async function initCommand(options) {
|
|
5
|
+
const stdout = options.stdout ?? console;
|
|
6
|
+
const selectedIde = options.ide ?? "all";
|
|
7
|
+
const adapterFiles = getAdapters(selectedIde).flatMap((adapter) => adapter.render());
|
|
8
|
+
const results = await writeManagedFiles(options.root, [...coreFiles(), ...adapterFiles], options.force ?? false);
|
|
9
|
+
const created = results.filter((result) => result.status === "created").length;
|
|
10
|
+
const updated = results.filter((result) => result.status === "updated").length;
|
|
11
|
+
const skipped = results.filter((result) => result.status === "skipped");
|
|
12
|
+
stdout.log(`Speckit initialized: ${created} created, ${updated} updated, ${skipped.length} skipped.`);
|
|
13
|
+
for (const result of skipped) {
|
|
14
|
+
stdout.log(`skipped ${result.path}: ${result.reason}`);
|
|
15
|
+
}
|
|
16
|
+
return skipped.length > 0 ? 2 : 0;
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { commandExists } from "../adapters/tool-checks.js";
|
|
3
|
+
export async function nextCommand(options = {}) {
|
|
4
|
+
const stdout = options.stdout ?? console;
|
|
5
|
+
if (!commandExists("bv")) {
|
|
6
|
+
stdout.error("bv is not installed. Install Beads Viewer, then run: bv --robot-next --format json");
|
|
7
|
+
return 1;
|
|
8
|
+
}
|
|
9
|
+
const result = spawnSync("bv", ["--robot-next", "--format", "json"], {
|
|
10
|
+
encoding: "utf8",
|
|
11
|
+
});
|
|
12
|
+
if (result.stdout)
|
|
13
|
+
stdout.log(result.stdout.trim());
|
|
14
|
+
if (result.stderr)
|
|
15
|
+
stdout.error(result.stderr.trim());
|
|
16
|
+
return result.status ?? 1;
|
|
17
|
+
}
|