@esneiderbravo/speclaw 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/ATTRIBUTION.md +34 -0
- package/LICENSE +21 -0
- package/README.md +134 -0
- package/dist/cli/commands/agent.js +42 -0
- package/dist/cli/commands/doctor.js +21 -0
- package/dist/cli/commands/index-build.js +24 -0
- package/dist/cli/commands/init.js +121 -0
- package/dist/cli/commands/query.js +69 -0
- package/dist/cli/commands/spec.js +71 -0
- package/dist/cli/index.js +77 -0
- package/dist/cli/lib/args.js +44 -0
- package/dist/cli/lib/ui.js +89 -0
- package/dist/modules/compass/db.js +84 -0
- package/dist/modules/compass/embedder.js +87 -0
- package/dist/modules/compass/extract.js +91 -0
- package/dist/modules/compass/indexer.js +158 -0
- package/dist/modules/compass/languages.js +67 -0
- package/dist/modules/compass/parser.js +37 -0
- package/dist/modules/compass/query.js +260 -0
- package/dist/modules/compass/register.js +72 -0
- package/dist/modules/compass/watcher.js +95 -0
- package/dist/modules/foundation/assets/AGENTS.template.md +61 -0
- package/dist/modules/foundation/assets/CLAUDE.template.md +72 -0
- package/dist/modules/foundation/assets/LAWS.template.md +39 -0
- package/dist/modules/foundation/assets/docs/compass.template.md +43 -0
- package/dist/modules/foundation/assets/docs/standards/architecture.template.md +36 -0
- package/dist/modules/foundation/assets/docs/standards/backend-standards.template.md +50 -0
- package/dist/modules/foundation/assets/docs/standards/base-standards.template.md +47 -0
- package/dist/modules/foundation/assets/docs/standards/conventions.template.md +31 -0
- package/dist/modules/foundation/assets/docs/standards/documentation.template.md +50 -0
- package/dist/modules/foundation/assets/docs/standards/frontend-standards.template.md +46 -0
- package/dist/modules/foundation/assets/docs/standards/spec-workflow.template.md +46 -0
- package/dist/modules/foundation/assets/docs/standards/testing-standards.template.md +34 -0
- package/dist/modules/foundation/doctor.js +107 -0
- package/dist/modules/foundation/register.js +86 -0
- package/dist/modules/foundation/scaffold.js +103 -0
- package/dist/modules/spec/assets/commands/archive.md +10 -0
- package/dist/modules/spec/assets/commands/build.md +11 -0
- package/dist/modules/spec/assets/commands/draft.md +12 -0
- package/dist/modules/spec/assets/commands/explore.md +10 -0
- package/dist/modules/spec/assets/commands/sync.md +9 -0
- package/dist/modules/spec/assets/rules/spec-tasks-mandatory-steps.md +37 -0
- package/dist/modules/spec/assets/skills/archive/SKILL.md +22 -0
- package/dist/modules/spec/assets/skills/build/SKILL.md +49 -0
- package/dist/modules/spec/assets/skills/draft/SKILL.md +64 -0
- package/dist/modules/spec/assets/skills/explore/SKILL.md +28 -0
- package/dist/modules/spec/assets/skills/sync/SKILL.md +21 -0
- package/dist/modules/spec/engine.js +227 -0
- package/dist/modules/spec/register.js +53 -0
- package/dist/modules/tools/assets/packs/agents/backend-developer.md +61 -0
- package/dist/modules/tools/assets/packs/agents/frontend-developer.md +62 -0
- package/dist/modules/tools/assets/packs/agents/product-strategy-analyst.md +56 -0
- package/dist/modules/tools/assets/packs.json +6 -0
- package/dist/modules/tools/packs.js +44 -0
- package/dist/modules/tools/register.js +27 -0
- package/dist/server.js +21 -0
- package/dist/shared/agents.js +94 -0
- package/dist/shared/install.js +66 -0
- package/dist/shared/mcp.js +16 -0
- package/dist/shared/paths.js +10 -0
- package/dist/shared/render.js +21 -0
- package/package.json +49 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Implement a drafted change, following its spec and the project's standards.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Implement the change: $ARGUMENTS
|
|
6
|
+
|
|
7
|
+
Follow the `build` skill: read the change's `proposal.md`, `tasks.md`, and delta
|
|
8
|
+
specs; create the feature branch first; work the tasks in order using
|
|
9
|
+
`compass_explore` before editing; keep code and spec in agreement; run the
|
|
10
|
+
quality gates (`{{test_commands}}`, `{{lint_commands}}`) and manual
|
|
11
|
+
verification yourself; check off each task.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Draft a new spec-driven change (proposal, delta specs, tasks) before coding.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Draft a new change under `spec/changes/<name>/` for: $ARGUMENTS
|
|
6
|
+
|
|
7
|
+
Follow the `draft` skill: ensure `spec/` exists (`spec_init`), investigate the
|
|
8
|
+
code with `compass_explore`/`compass_recall`, read the governing
|
|
9
|
+
`docs/standards/`, then write `proposal.md`, `specs/<capability>/spec.md`
|
|
10
|
+
(normative `SHALL`/`MUST` + `#### Scenario:`), optional `design.md`, and
|
|
11
|
+
`tasks.md` (with the mandatory steps from `spec/config.yaml`). Finish by
|
|
12
|
+
running `spec_validate` and fixing every issue.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Enter explore mode — a thinking partner before or during a change.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Explore: $ARGUMENTS
|
|
6
|
+
|
|
7
|
+
Follow the `explore` skill: use `compass_recall`/`compass_explore` to understand
|
|
8
|
+
the code, ask sharp questions, check the relevant `docs/standards/`, weigh
|
|
9
|
+
approaches with trade-offs, and give a recommendation. Write nothing to
|
|
10
|
+
`spec/`; when the direction is clear, offer to `draft` the change.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Promote a change's delta specs into the canonical specs, without archiving.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Sync the change's specs into canonical: $ARGUMENTS
|
|
6
|
+
|
|
7
|
+
Follow the `sync` skill: validate the change (`spec_validate`), then run
|
|
8
|
+
`spec_sync` to promote each delta spec into `spec/specs/`. Report what was
|
|
9
|
+
promoted; leave the change active.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Enforce the mandatory steps from spec/config.yaml when creating tasks.md, and ensure the agent executes all manual verification itself.
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Spec Tasks: Mandatory Steps
|
|
7
|
+
|
|
8
|
+
When creating or updating a `tasks.md` inside a `spec/changes/<name>/`, you MUST:
|
|
9
|
+
|
|
10
|
+
## 1. Read spec/config.yaml first
|
|
11
|
+
|
|
12
|
+
Before writing tasks, read `spec/config.yaml` for the project's mandatory task
|
|
13
|
+
steps, branch convention, and testing/documentation requirements.
|
|
14
|
+
|
|
15
|
+
## 2. Include the mandatory steps, in order
|
|
16
|
+
|
|
17
|
+
- **Step 0 — Create the feature branch (must be first).** Follow the repo's
|
|
18
|
+
branch pattern.
|
|
19
|
+
- Review and update the affected tests.
|
|
20
|
+
- Run the quality gates and verify they pass
|
|
21
|
+
(see `docs/standards/testing-standards.md`).
|
|
22
|
+
- Perform manual verification of the behavior — **the agent executes this
|
|
23
|
+
itself, never the user.**
|
|
24
|
+
- Update the technical documentation the change touches.
|
|
25
|
+
- Archive the change within the same PR (the `archive` command / `spec_archive`
|
|
26
|
+
tool).
|
|
27
|
+
|
|
28
|
+
## 3. Manual verification — the agent must execute it
|
|
29
|
+
|
|
30
|
+
The coding agent performs all manual testing itself (exercise the endpoint, UI,
|
|
31
|
+
or CLI). Never delegate it to the user. A task that requires verification is
|
|
32
|
+
not complete until the agent has verified it.
|
|
33
|
+
|
|
34
|
+
## 4. Archiving is part of the change
|
|
35
|
+
|
|
36
|
+
A change is not done until it is archived with `spec_archive` (never a manual
|
|
37
|
+
`mv`). The archive lands in the same PR that implements the change.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: archive
|
|
3
|
+
description: Finalize a completed change — sync its specs into canonical, then move it to the archive. Use when a change is done and merged-ready: "archive the change", "finalize X", "close out the change". Part of speclaw's spec module (draft → build → sync → archive). Archiving belongs in the same PR that implements the change.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# archive — Finalize and archive a change
|
|
7
|
+
|
|
8
|
+
Close out a completed change: its delta specs become canonical and the change
|
|
9
|
+
folder moves to `spec/changes/archive/`. This is part of the PR that
|
|
10
|
+
implements the change, not a post-merge chore.
|
|
11
|
+
|
|
12
|
+
## Steps
|
|
13
|
+
|
|
14
|
+
1. Confirm the change is truly done: every task in `tasks.md` checked, quality
|
|
15
|
+
gates green, behavior verified.
|
|
16
|
+
2. Run `spec_validate` for the change; resolve any issues first.
|
|
17
|
+
3. Run the `spec_archive` tool with the change name and today's date
|
|
18
|
+
(`YYYY-MM-DD`). It syncs the delta specs into `spec/specs/` and then moves
|
|
19
|
+
`spec/changes/<name>/` to `spec/changes/archive/<date>-<name>/`.
|
|
20
|
+
4. Report the archive path and the promoted specs. Never move the folder by
|
|
21
|
+
hand — always use `spec_archive`, which performs the sync and validation a
|
|
22
|
+
manual move would skip.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: build
|
|
3
|
+
description: Implement the tasks of a drafted change, following its spec and the project's standards. Use when the user wants to start or continue implementing a change: "build X", "implement the change", "work through the tasks", "continue X". Part of speclaw's spec module (draft → build → sync → archive).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# build — Implement a change
|
|
7
|
+
|
|
8
|
+
Work through a change's `tasks.md` in order, keeping code, spec, and standards
|
|
9
|
+
in lockstep.
|
|
10
|
+
|
|
11
|
+
## Step 0 — Load the change
|
|
12
|
+
|
|
13
|
+
- Read `spec/changes/<name>/proposal.md`, `tasks.md`, and the delta specs under
|
|
14
|
+
`specs/`. If unsure which change, run `spec_list`.
|
|
15
|
+
- Read the governing standards in `docs/standards/` for the areas you'll touch.
|
|
16
|
+
|
|
17
|
+
## Step 1 — Branch first
|
|
18
|
+
|
|
19
|
+
Create the feature branch (the mandatory Step 0 in `tasks.md`), following the
|
|
20
|
+
repo's branch pattern `{{branch_pattern}}`.
|
|
21
|
+
|
|
22
|
+
## Step 2 — Implement task by task
|
|
23
|
+
|
|
24
|
+
- Use `compass_explore` before editing to see a symbol's callers/callees and
|
|
25
|
+
blast radius; re-run `compass_index` after significant edits to keep the
|
|
26
|
+
graph fresh.
|
|
27
|
+
- Make the smallest correct change; match the surrounding code.
|
|
28
|
+
- The code must satisfy the delta spec exactly. If reality diverges from the
|
|
29
|
+
spec, update the spec in the change (not silently) — the two must agree.
|
|
30
|
+
- Check off each task in `tasks.md` as you complete it.
|
|
31
|
+
|
|
32
|
+
## Step 3 — Quality gates (mandatory)
|
|
33
|
+
|
|
34
|
+
Run the repo's gates from `docs/standards/testing-standards.md`:
|
|
35
|
+
|
|
36
|
+
- Tests: `{{test_commands}}`
|
|
37
|
+
- Lint / type-check: `{{lint_commands}}`
|
|
38
|
+
|
|
39
|
+
Run them yourself and report real output. A red gate blocks completion.
|
|
40
|
+
|
|
41
|
+
## Step 4 — Manual verification (mandatory, agent executes)
|
|
42
|
+
|
|
43
|
+
Exercise the behavior (endpoint/UI/CLI) yourself where feasible — do not
|
|
44
|
+
delegate manual testing to the user. Record what you verified.
|
|
45
|
+
|
|
46
|
+
## Step 5 — Hand off
|
|
47
|
+
|
|
48
|
+
When every task is checked and gates are green, tell the user the change is
|
|
49
|
+
ready to `sync` and `archive`.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: draft
|
|
3
|
+
description: Draft a new spec-driven change — proposal, delta specs, and tasks — before writing any code. Use when the user wants to start, plan, or propose a new feature, fix, or refactor: "draft a change for X", "propose X", "let's plan X", "spec out X", "new change". Part of speclaw's spec module (draft → build → sync → archive).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# draft — Draft a new change
|
|
7
|
+
|
|
8
|
+
Turn a request into a complete, reviewable change under `spec/changes/<name>/`
|
|
9
|
+
before any implementation. This is speclaw's own spec-driven workflow — no
|
|
10
|
+
external CLI; the mechanical steps are speclaw MCP tools.
|
|
11
|
+
|
|
12
|
+
## Step 0 — Ensure the workspace exists
|
|
13
|
+
|
|
14
|
+
If `spec/` is missing, run the `spec_init` tool once to create it.
|
|
15
|
+
|
|
16
|
+
## Step 1 — Understand the request and the code
|
|
17
|
+
|
|
18
|
+
- Clarify what the user wants (feature / fix / refactor) and confirm scope.
|
|
19
|
+
- Use `compass_explore` and `compass_recall` (speclaw's code index) BEFORE
|
|
20
|
+
grep/read to locate the real code the change touches and its blast radius.
|
|
21
|
+
If the index is stale or missing, run `compass_index` first.
|
|
22
|
+
- Read the governing standards in `docs/standards/` (architecture, backend,
|
|
23
|
+
frontend, testing) so the change complies with the project's law.
|
|
24
|
+
|
|
25
|
+
## Step 2 — Pick a change name
|
|
26
|
+
|
|
27
|
+
Kebab-case, action-oriented (e.g. `add-login`, `fix-shift-overlap`). This is
|
|
28
|
+
the folder under `spec/changes/`.
|
|
29
|
+
|
|
30
|
+
## Step 3 — Write the artifacts
|
|
31
|
+
|
|
32
|
+
Create under `spec/changes/<name>/`:
|
|
33
|
+
|
|
34
|
+
- **proposal.md** — the why, the what, non-goals, and whether migrations are
|
|
35
|
+
needed. Reference the tracker ticket ({{ticket_prefix}}-N) if there is one.
|
|
36
|
+
- **specs/<capability>/spec.md** — the delta spec for each affected capability.
|
|
37
|
+
Use normative language and testable scenarios:
|
|
38
|
+
```markdown
|
|
39
|
+
# <Capability>
|
|
40
|
+
|
|
41
|
+
### Requirement: <name>
|
|
42
|
+
The system SHALL <requirement>.
|
|
43
|
+
|
|
44
|
+
#### Scenario: <name>
|
|
45
|
+
- Given <context>
|
|
46
|
+
- When <action>
|
|
47
|
+
- Then <observable outcome>
|
|
48
|
+
```
|
|
49
|
+
- **design.md** (optional) — for non-trivial changes: approach, alternatives,
|
|
50
|
+
trade-offs.
|
|
51
|
+
- **tasks.md** — ordered, checkable steps. MUST include the mandatory steps
|
|
52
|
+
from `spec/config.yaml` (feature branch first; tests reviewed and run;
|
|
53
|
+
manual verification executed by the agent; docs updated; archive within the
|
|
54
|
+
PR).
|
|
55
|
+
|
|
56
|
+
## Step 4 — Validate
|
|
57
|
+
|
|
58
|
+
Run the `spec_validate` tool for the change and fix every issue it reports
|
|
59
|
+
(missing artifacts, non-normative specs, missing scenarios) before handing off
|
|
60
|
+
to implementation.
|
|
61
|
+
|
|
62
|
+
## Step 5 — Hand off
|
|
63
|
+
|
|
64
|
+
Summarize the change and tell the user it's ready to `build`.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: explore
|
|
3
|
+
description: Enter explore mode — a thinking partner for investigating an idea, a problem, or the codebase before or during a change. Use when the user wants to think something through, understand how something works, or clarify requirements before committing to a spec: "help me think through X", "how does X work", "explore X", "I'm not sure how to approach X". Part of speclaw's spec module.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# explore — Think it through
|
|
7
|
+
|
|
8
|
+
A low-commitment mode for investigating ideas and the codebase before (or
|
|
9
|
+
during) a change. Nothing is written to `spec/` here — the output is shared
|
|
10
|
+
understanding and a recommended direction.
|
|
11
|
+
|
|
12
|
+
## How to explore
|
|
13
|
+
|
|
14
|
+
- **Understand the code first.** Use `compass_recall` to find relevant code by
|
|
15
|
+
meaning and `compass_explore` to read a symbol's source plus its callers and
|
|
16
|
+
callees — before grep/read. Run `compass_index` if the graph is missing.
|
|
17
|
+
- **Ask sharp questions** to surface hidden assumptions, constraints, and edge
|
|
18
|
+
cases. Confirm scope and non-goals.
|
|
19
|
+
- **Check the law.** Read the relevant `docs/standards/` so any direction you
|
|
20
|
+
propose already fits the project's architecture and conventions.
|
|
21
|
+
- **Weigh approaches.** Lay out the viable options with trade-offs and give a
|
|
22
|
+
recommendation, not an exhaustive survey.
|
|
23
|
+
|
|
24
|
+
## Output
|
|
25
|
+
|
|
26
|
+
A concise summary: the problem as understood, the constraints, the recommended
|
|
27
|
+
approach, and the open questions. When the direction is clear, offer to `draft`
|
|
28
|
+
the change.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sync
|
|
3
|
+
description: Promote a change's delta specs into the canonical specs, without archiving. Use when the user wants to update the source-of-truth specs from a change: "sync the specs", "update the canonical specs", "promote the specs". Part of speclaw's spec module (draft → build → sync → archive).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# sync — Promote delta specs to canonical
|
|
7
|
+
|
|
8
|
+
Update the project's canonical specifications (`spec/specs/`) with a change's
|
|
9
|
+
delta specs, without archiving the change. Use this when the specs should
|
|
10
|
+
become the source of truth but the change isn't finished (e.g. multi-PR work).
|
|
11
|
+
|
|
12
|
+
## Steps
|
|
13
|
+
|
|
14
|
+
1. Confirm which change to sync (run `spec_list` if unsure).
|
|
15
|
+
2. Run `spec_validate` for the change; do not sync a change whose specs are
|
|
16
|
+
invalid.
|
|
17
|
+
3. Run the `spec_sync` tool for the change. It copies each
|
|
18
|
+
`spec/changes/<name>/specs/<capability>/spec.md` over the canonical
|
|
19
|
+
`spec/specs/<capability>/spec.md` and reports what it promoted.
|
|
20
|
+
4. Report the promoted files to the user. The change stays active — `archive`
|
|
21
|
+
it when it's fully done.
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
// speclaw's own spec-driven workflow engine. Inspired by OpenSpec's model
|
|
4
|
+
// (proposals, delta specs, changes, archive) but implemented from scratch and
|
|
5
|
+
// deliberately simpler: a change's specs/ holds the full intended spec for each
|
|
6
|
+
// affected capability, and sync promotes those into the canonical specs/.
|
|
7
|
+
const SPEC_DIR = "spec";
|
|
8
|
+
function specRoot(projectPath) {
|
|
9
|
+
return path.join(projectPath, SPEC_DIR);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Report whether the spec workspace (spec/) has been initialized for a project.
|
|
13
|
+
*
|
|
14
|
+
* @param projectPath - Absolute path to the project root.
|
|
15
|
+
* @returns True if the spec/ directory exists.
|
|
16
|
+
*/
|
|
17
|
+
export function specExists(projectPath) {
|
|
18
|
+
return fs.existsSync(specRoot(projectPath));
|
|
19
|
+
}
|
|
20
|
+
const CONFIG_YAML = `# speclaw spec module configuration
|
|
21
|
+
# The spec-driven workflow: draft -> build -> sync -> archive (explore anytime).
|
|
22
|
+
|
|
23
|
+
# Mandatory steps every change's tasks.md must include, in order.
|
|
24
|
+
mandatory_task_steps:
|
|
25
|
+
- "Step 0: Create the feature branch (must be first)."
|
|
26
|
+
- "Review and update the affected tests."
|
|
27
|
+
- "Run the quality gates and verify they pass (see docs/standards/testing-standards.md)."
|
|
28
|
+
- "Perform manual verification of the behavior — the agent executes this itself, never the user."
|
|
29
|
+
- "Update the technical documentation touched by the change."
|
|
30
|
+
- "Archive the change within the same PR (spec:archive)."
|
|
31
|
+
|
|
32
|
+
# A change is required for new behavior, endpoints, schema changes, or UI flows;
|
|
33
|
+
# one-line fixes need not have one.
|
|
34
|
+
`;
|
|
35
|
+
const README_MD = `# spec/ — the spec-driven workflow (speclaw)
|
|
36
|
+
|
|
37
|
+
This directory is managed by speclaw's **spec** module.
|
|
38
|
+
|
|
39
|
+
- \`specs/\` — the canonical specifications (the current source of truth).
|
|
40
|
+
- \`changes/<name>/\` — an in-flight change: \`proposal.md\`, \`tasks.md\`,
|
|
41
|
+
optional \`design.md\`, and \`specs/<capability>/spec.md\` delta specs.
|
|
42
|
+
- \`changes/archive/\` — completed, archived changes.
|
|
43
|
+
- \`config.yaml\` — mandatory task steps and workflow rules.
|
|
44
|
+
|
|
45
|
+
## Workflow
|
|
46
|
+
|
|
47
|
+
1. \`spec:draft\` — describe the change; generates proposal, delta specs, tasks.
|
|
48
|
+
2. \`spec:build\` — implement the tasks.
|
|
49
|
+
3. \`spec:sync\` — promote the change's delta specs into \`specs/\`.
|
|
50
|
+
4. \`spec:archive\` — sync + move the change to \`changes/archive/\`.
|
|
51
|
+
5. \`spec:explore\` — think through an idea before or during a change.
|
|
52
|
+
`;
|
|
53
|
+
/**
|
|
54
|
+
* Initialize the spec/ workspace, creating the specs/, changes/, and archive
|
|
55
|
+
* directories plus config.yaml and README. Idempotent — existing files and
|
|
56
|
+
* directories are left untouched.
|
|
57
|
+
*
|
|
58
|
+
* @param projectPath - Absolute path to the project root.
|
|
59
|
+
* @returns The paths created and whether the workspace already existed.
|
|
60
|
+
*/
|
|
61
|
+
export function specInit(projectPath) {
|
|
62
|
+
const root = specRoot(projectPath);
|
|
63
|
+
const created = [];
|
|
64
|
+
const alreadyExisted = fs.existsSync(root);
|
|
65
|
+
const ensure = (rel, content) => {
|
|
66
|
+
const abs = path.join(root, rel);
|
|
67
|
+
if (content === undefined) {
|
|
68
|
+
if (!fs.existsSync(abs)) {
|
|
69
|
+
fs.mkdirSync(abs, { recursive: true });
|
|
70
|
+
created.push(rel + "/");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else if (!fs.existsSync(abs)) {
|
|
74
|
+
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
75
|
+
fs.writeFileSync(abs, content);
|
|
76
|
+
created.push(rel);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
ensure("specs");
|
|
80
|
+
ensure("changes");
|
|
81
|
+
ensure("changes/archive");
|
|
82
|
+
ensure("config.yaml", CONFIG_YAML);
|
|
83
|
+
ensure("README.md", README_MD);
|
|
84
|
+
return { created, alreadyExisted };
|
|
85
|
+
}
|
|
86
|
+
/** Recursively collect every .md file under a change's specs/ directory. */
|
|
87
|
+
function deltaSpecFiles(changeDir) {
|
|
88
|
+
const specsDir = path.join(changeDir, "specs");
|
|
89
|
+
if (!fs.existsSync(specsDir))
|
|
90
|
+
return [];
|
|
91
|
+
const out = [];
|
|
92
|
+
const walk = (dir) => {
|
|
93
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
94
|
+
const full = path.join(dir, e.name);
|
|
95
|
+
if (e.isDirectory())
|
|
96
|
+
walk(full);
|
|
97
|
+
else if (e.name.endsWith(".md"))
|
|
98
|
+
out.push(full);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
walk(specsDir);
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Validate a change's artifacts: proposal.md and tasks.md must be present, and
|
|
106
|
+
* each delta spec must use normative language (SHALL/MUST), a "### Requirement:"
|
|
107
|
+
* header, and a "#### Scenario:" acceptance criterion.
|
|
108
|
+
*
|
|
109
|
+
* @param projectPath - Absolute path to the project root.
|
|
110
|
+
* @param change - Change name (folder under spec/changes/).
|
|
111
|
+
* @returns The validation verdict and the list of issues to fix; never throws
|
|
112
|
+
* for a missing change — it is reported as an issue with `valid: false`.
|
|
113
|
+
*/
|
|
114
|
+
export function specValidate(projectPath, change) {
|
|
115
|
+
const changeDir = path.join(specRoot(projectPath), "changes", change);
|
|
116
|
+
const issues = [];
|
|
117
|
+
if (!fs.existsSync(changeDir)) {
|
|
118
|
+
return { change, valid: false, issues: [`change "${change}" not found under spec/changes/`], deltaSpecs: [] };
|
|
119
|
+
}
|
|
120
|
+
if (!fs.existsSync(path.join(changeDir, "proposal.md")))
|
|
121
|
+
issues.push("missing proposal.md");
|
|
122
|
+
const tasksPath = path.join(changeDir, "tasks.md");
|
|
123
|
+
if (!fs.existsSync(tasksPath))
|
|
124
|
+
issues.push("missing tasks.md");
|
|
125
|
+
const deltas = deltaSpecFiles(changeDir);
|
|
126
|
+
if (deltas.length === 0)
|
|
127
|
+
issues.push("no delta specs under specs/ (a change should specify what it changes)");
|
|
128
|
+
for (const file of deltas) {
|
|
129
|
+
const rel = path.relative(changeDir, file);
|
|
130
|
+
const content = fs.readFileSync(file, "utf8");
|
|
131
|
+
if (!/\b(SHALL|MUST)\b/.test(content)) {
|
|
132
|
+
issues.push(`${rel}: no normative requirement (use SHALL/MUST)`);
|
|
133
|
+
}
|
|
134
|
+
if (!/^####\s+Scenario:/m.test(content)) {
|
|
135
|
+
issues.push(`${rel}: no "#### Scenario:" acceptance criteria`);
|
|
136
|
+
}
|
|
137
|
+
if (!/^###\s+Requirement:/m.test(content)) {
|
|
138
|
+
issues.push(`${rel}: no "### Requirement:" header`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return { change, valid: issues.length === 0, issues, deltaSpecs: deltas.map((f) => path.relative(projectPath, f)) };
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Promote a change's delta specs into the canonical specs/, overwriting the
|
|
145
|
+
* file for each affected capability.
|
|
146
|
+
*
|
|
147
|
+
* @param projectPath - Absolute path to the project root.
|
|
148
|
+
* @param change - Change name (folder under spec/changes/).
|
|
149
|
+
* @returns The change name and the list of promoted spec paths.
|
|
150
|
+
* @throws If the change directory does not exist.
|
|
151
|
+
*/
|
|
152
|
+
export function specSync(projectPath, change) {
|
|
153
|
+
const root = specRoot(projectPath);
|
|
154
|
+
const changeDir = path.join(root, "changes", change);
|
|
155
|
+
if (!fs.existsSync(changeDir))
|
|
156
|
+
throw new Error(`change "${change}" not found`);
|
|
157
|
+
const changeSpecs = path.join(changeDir, "specs");
|
|
158
|
+
const promoted = [];
|
|
159
|
+
if (!fs.existsSync(changeSpecs))
|
|
160
|
+
return { change, promoted };
|
|
161
|
+
const walk = (dir) => {
|
|
162
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
163
|
+
const full = path.join(dir, e.name);
|
|
164
|
+
if (e.isDirectory())
|
|
165
|
+
walk(full);
|
|
166
|
+
else if (e.name.endsWith(".md")) {
|
|
167
|
+
const rel = path.relative(changeSpecs, full);
|
|
168
|
+
const dest = path.join(root, "specs", rel);
|
|
169
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
170
|
+
fs.copyFileSync(full, dest);
|
|
171
|
+
promoted.push(path.join("spec/specs", rel));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
walk(changeSpecs);
|
|
176
|
+
return { change, promoted };
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Finalize a change: promote its delta specs (via {@link specSync}), then move
|
|
180
|
+
* it to changes/archive/<date>-<name>/.
|
|
181
|
+
*
|
|
182
|
+
* @param projectPath - Absolute path to the project root.
|
|
183
|
+
* @param change - Change name (folder under spec/changes/).
|
|
184
|
+
* @param date - Archive date prefix, formatted YYYY-MM-DD.
|
|
185
|
+
* @returns The promoted specs and the archive destination path.
|
|
186
|
+
* @throws If the change does not exist, or the archive target already exists.
|
|
187
|
+
*/
|
|
188
|
+
export function specArchive(projectPath, change, date) {
|
|
189
|
+
const root = specRoot(projectPath);
|
|
190
|
+
const changeDir = path.join(root, "changes", change);
|
|
191
|
+
if (!fs.existsSync(changeDir))
|
|
192
|
+
throw new Error(`change "${change}" not found`);
|
|
193
|
+
const { promoted } = specSync(projectPath, change);
|
|
194
|
+
const archiveDir = path.join(root, "changes", "archive", `${date}-${change}`);
|
|
195
|
+
fs.mkdirSync(path.dirname(archiveDir), { recursive: true });
|
|
196
|
+
if (fs.existsSync(archiveDir))
|
|
197
|
+
throw new Error(`archive target already exists: ${archiveDir}`);
|
|
198
|
+
fs.renameSync(changeDir, archiveDir);
|
|
199
|
+
return { change, promoted, archivedTo: path.relative(projectPath, archiveDir) };
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* List the spec workspace: active changes, archived changes, and canonical
|
|
203
|
+
* capabilities.
|
|
204
|
+
*
|
|
205
|
+
* @param projectPath - Absolute path to the project root.
|
|
206
|
+
* @returns The workspace snapshot; `initialized` is false when spec/ is absent.
|
|
207
|
+
*/
|
|
208
|
+
export function specList(projectPath) {
|
|
209
|
+
const root = specRoot(projectPath);
|
|
210
|
+
if (!fs.existsSync(root)) {
|
|
211
|
+
return { initialized: false, activeChanges: [], archivedChanges: [], capabilities: [] };
|
|
212
|
+
}
|
|
213
|
+
const dirsIn = (rel) => {
|
|
214
|
+
const abs = path.join(root, rel);
|
|
215
|
+
if (!fs.existsSync(abs))
|
|
216
|
+
return [];
|
|
217
|
+
return fs.readdirSync(abs, { withFileTypes: true })
|
|
218
|
+
.filter((e) => e.isDirectory() && e.name !== "archive")
|
|
219
|
+
.map((e) => e.name);
|
|
220
|
+
};
|
|
221
|
+
return {
|
|
222
|
+
initialized: true,
|
|
223
|
+
activeChanges: dirsIn("changes"),
|
|
224
|
+
archivedChanges: dirsIn("changes/archive"),
|
|
225
|
+
capabilities: dirsIn("specs"),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { text } from "../../shared/mcp.js";
|
|
4
|
+
import { assetsDir } from "../../shared/paths.js";
|
|
5
|
+
import { copyRendered } from "../../shared/install.js";
|
|
6
|
+
import { specInit, specValidate, specSync, specArchive, specList } from "./engine.js";
|
|
7
|
+
const ASSETS = assetsDir(import.meta.url);
|
|
8
|
+
/**
|
|
9
|
+
* Install the spec module's workflow interface into a project's ai-specs/:
|
|
10
|
+
* the draft/build/sync/archive/explore skills, the /spec commands, and the
|
|
11
|
+
* mandatory-task-steps rule. Always installed — it's the core workflow.
|
|
12
|
+
*/
|
|
13
|
+
export function installWorkflow(projectPath, vars, report) {
|
|
14
|
+
const aiSpecs = path.join(projectPath, "ai-specs");
|
|
15
|
+
copyRendered(path.join(ASSETS, "skills"), path.join(aiSpecs, "skills"), vars, report);
|
|
16
|
+
copyRendered(path.join(ASSETS, "commands"), path.join(aiSpecs, "commands", "spec"), vars, report);
|
|
17
|
+
copyRendered(path.join(ASSETS, "rules"), path.join(aiSpecs, "rules"), vars, report);
|
|
18
|
+
}
|
|
19
|
+
// ─── The spec module: speclaw's own spec-driven workflow (no external OpenSpec) ───
|
|
20
|
+
// Mechanical operations behind the draft/build/sync/archive/explore commands.
|
|
21
|
+
/** Register the spec workflow MCP tools (init, list, validate, sync, archive). */
|
|
22
|
+
export function registerSpec(server) {
|
|
23
|
+
server.registerTool("spec_init", {
|
|
24
|
+
description: "Initialize speclaw's spec-driven workflow in a project: creates spec/ (specs/, changes/, changes/archive/, config.yaml, README). Idempotent — never overwrites existing files.",
|
|
25
|
+
inputSchema: { projectPath: z.string().describe("Absolute path to the project") },
|
|
26
|
+
}, async ({ projectPath }) => text(specInit(projectPath)));
|
|
27
|
+
server.registerTool("spec_list", {
|
|
28
|
+
description: "List the spec workspace: active changes, archived changes, and canonical capabilities under spec/.",
|
|
29
|
+
inputSchema: { projectPath: z.string().describe("Absolute path to the project") },
|
|
30
|
+
}, async ({ projectPath }) => text(specList(projectPath)));
|
|
31
|
+
server.registerTool("spec_validate", {
|
|
32
|
+
description: "Validate a change's artifacts: proposal.md and tasks.md present, and delta specs use normative language (SHALL/MUST), '### Requirement:' headers, and '#### Scenario:' acceptance criteria. Returns the issues to fix. Used by the draft/build commands before proceeding.",
|
|
33
|
+
inputSchema: {
|
|
34
|
+
projectPath: z.string().describe("Absolute path to the project"),
|
|
35
|
+
change: z.string().describe("Change name (folder under spec/changes/)"),
|
|
36
|
+
},
|
|
37
|
+
}, async ({ projectPath, change }) => text(specValidate(projectPath, change)));
|
|
38
|
+
server.registerTool("spec_sync", {
|
|
39
|
+
description: "Promote a change's delta specs into the canonical spec/specs/ (per capability), without archiving. Backs the `sync` command.",
|
|
40
|
+
inputSchema: {
|
|
41
|
+
projectPath: z.string().describe("Absolute path to the project"),
|
|
42
|
+
change: z.string().describe("Change name (folder under spec/changes/)"),
|
|
43
|
+
},
|
|
44
|
+
}, async ({ projectPath, change }) => text(specSync(projectPath, change)));
|
|
45
|
+
server.registerTool("spec_archive", {
|
|
46
|
+
description: "Finalize a change: sync its delta specs into spec/specs/, then move it to spec/changes/archive/<date>-<name>/. Backs the `archive` command. Pass today's date as YYYY-MM-DD.",
|
|
47
|
+
inputSchema: {
|
|
48
|
+
projectPath: z.string().describe("Absolute path to the project"),
|
|
49
|
+
change: z.string().describe("Change name (folder under spec/changes/)"),
|
|
50
|
+
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).describe("Today's date, YYYY-MM-DD"),
|
|
51
|
+
},
|
|
52
|
+
}, async ({ projectPath, change, date }) => text(specArchive(projectPath, change, date)));
|
|
53
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-developer
|
|
3
|
+
description: Use this agent for backend planning, reviews, and refactors in this repository. Focus on the backend source and test trees, migration wiring, and the service/workflow boundaries documented in `AGENTS.md` and `LAWS.md`.
|
|
4
|
+
model: sonnet
|
|
5
|
+
color: red
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are a senior backend architect for this project. The stack, architecture,
|
|
9
|
+
and layers are defined in the standards below — read them first; they are the
|
|
10
|
+
source of truth for how this repo is built.
|
|
11
|
+
|
|
12
|
+
## Goal
|
|
13
|
+
|
|
14
|
+
Create a concrete implementation plan only (do not implement code directly).
|
|
15
|
+
|
|
16
|
+
Output path:
|
|
17
|
+
|
|
18
|
+
- `docs/agent_outputs/{feature_name}/backend.md`
|
|
19
|
+
|
|
20
|
+
## Standards to apply (read before planning)
|
|
21
|
+
|
|
22
|
+
- [`docs/standards/backend-standards.md`](../../docs/standards/backend-standards.md) — layers, docstrings, typing, tests, migrations
|
|
23
|
+
- [`docs/standards/architecture.md`](../../docs/standards/architecture.md) — modules and layer boundaries
|
|
24
|
+
- [`docs/standards/testing-standards.md`](../../docs/standards/testing-standards.md) — quality gates
|
|
25
|
+
- [`docs/standards/spec-workflow.md`](../../docs/standards/spec-workflow.md) — spec-driven workflow
|
|
26
|
+
- [`docs/standards/base-standards.md`](../../docs/standards/base-standards.md) — cross-cutting rules
|
|
27
|
+
|
|
28
|
+
Use `compass_explore` to locate the real entrypoints, routers, schemas,
|
|
29
|
+
config, and DB wiring. Repo entry points: `AGENTS.md` and `LAWS.md`.
|
|
30
|
+
|
|
31
|
+
## Planning rules
|
|
32
|
+
|
|
33
|
+
1. Plan with small, ordered steps and explicit file paths.
|
|
34
|
+
2. Include API contract impacts, validation impacts, and test updates.
|
|
35
|
+
3. If config changes are involved, include synchronized updates for the env example file, the backend README, and the migration wiring (when DB settings are affected).
|
|
36
|
+
4. For migration changes, require the migration tool's CLI; never handcraft revision identifiers.
|
|
37
|
+
5. Include the repo's real verification commands in the plan — the quality
|
|
38
|
+
gates defined in [`docs/standards/testing-standards.md`](../../docs/standards/testing-standards.md).
|
|
39
|
+
|
|
40
|
+
## Review focus
|
|
41
|
+
|
|
42
|
+
- Route -> schema consistency
|
|
43
|
+
- Error handling and HTTP semantics
|
|
44
|
+
- Config safety and environment defaults
|
|
45
|
+
- DB session and migration compatibility
|
|
46
|
+
- Tests for new behavior and regressions
|
|
47
|
+
|
|
48
|
+
## Output format requirements
|
|
49
|
+
|
|
50
|
+
- Include a brief context section
|
|
51
|
+
- Include a numbered implementation sequence
|
|
52
|
+
- Include a testing and verification section
|
|
53
|
+
- End with: created plan file path
|
|
54
|
+
|
|
55
|
+
Example closing line:
|
|
56
|
+
`I've created a plan at docs/agent_outputs/{feature_name}/backend.md.`
|
|
57
|
+
|
|
58
|
+
## Hard constraints
|
|
59
|
+
|
|
60
|
+
- Do not run implementation edits while acting as this planning agent.
|
|
61
|
+
- If context is missing, request it explicitly before finalizing the plan.
|