@esneiderbravo/speclaw 0.3.4 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +66 -15
  2. package/dist/cli/commands/budget.js +36 -0
  3. package/dist/cli/commands/doctor.js +53 -11
  4. package/dist/cli/commands/init.js +3 -1
  5. package/dist/cli/commands/telemetry.js +16 -0
  6. package/dist/cli/commands/update.js +36 -3
  7. package/dist/cli/index.js +19 -4
  8. package/dist/modules/compass/indexer.js +10 -0
  9. package/dist/modules/compass/map.js +114 -0
  10. package/dist/modules/compass/register.js +30 -64
  11. package/dist/modules/foundation/assets/docs/compass.template.md +3 -0
  12. package/dist/modules/foundation/context-budget.js +58 -0
  13. package/dist/modules/foundation/doctor.js +626 -161
  14. package/dist/modules/foundation/graph.js +7 -4
  15. package/dist/modules/foundation/hooks.js +12 -0
  16. package/dist/modules/foundation/laws.js +1 -0
  17. package/dist/modules/foundation/register-core.js +108 -0
  18. package/dist/modules/foundation/register.js +22 -159
  19. package/dist/modules/foundation/scaffold.js +1 -1
  20. package/dist/modules/lawbook/assets/skills/archive/SKILL.md +1 -31
  21. package/dist/modules/lawbook/assets/skills/archive/steps/01-confirm-done.md +7 -0
  22. package/dist/modules/lawbook/assets/skills/archive/steps/02-reconcile.md +15 -0
  23. package/dist/modules/lawbook/assets/skills/archive/steps/03-validate-and-sync.md +7 -0
  24. package/dist/modules/lawbook/assets/skills/archive/steps/04-archive.md +9 -0
  25. package/dist/modules/lawbook/assets/skills/archive/steps/05-report.md +7 -0
  26. package/dist/modules/lawbook/assets/skills/build/SKILL.md +2 -100
  27. package/dist/modules/lawbook/assets/skills/build/steps/01-load-change.md +7 -0
  28. package/dist/modules/lawbook/assets/skills/build/steps/02-branch.md +6 -0
  29. package/dist/modules/lawbook/assets/skills/build/steps/03-implement.md +11 -0
  30. package/dist/modules/lawbook/assets/skills/build/steps/04-quality-gates.md +10 -0
  31. package/dist/modules/lawbook/assets/skills/build/steps/05-manual-verification.md +22 -0
  32. package/dist/modules/lawbook/assets/skills/build/steps/06-discipline-reports.md +44 -0
  33. package/dist/modules/lawbook/assets/skills/build/steps/07-hand-off.md +9 -0
  34. package/dist/modules/lawbook/assets/skills/draft/SKILL.md +3 -80
  35. package/dist/modules/lawbook/assets/skills/draft/steps/01-ensure-workspace.md +5 -0
  36. package/dist/modules/lawbook/assets/skills/draft/steps/02-understand.md +12 -0
  37. package/dist/modules/lawbook/assets/skills/draft/steps/03-name-capabilities.md +14 -0
  38. package/dist/modules/lawbook/assets/skills/draft/steps/04-write-artifacts.md +41 -0
  39. package/dist/modules/lawbook/assets/skills/draft/steps/05-validate.md +11 -0
  40. package/dist/modules/lawbook/assets/skills/draft/steps/06-hand-off.md +5 -0
  41. package/dist/modules/lawbook/assets/skills/explore/SKILL.md +6 -22
  42. package/dist/modules/lawbook/assets/skills/explore/steps/01-investigate.md +16 -0
  43. package/dist/modules/lawbook/assets/skills/explore/steps/02-summarize.md +7 -0
  44. package/dist/modules/lawbook/assets/skills/sync/SKILL.md +1 -30
  45. package/dist/modules/lawbook/assets/skills/sync/steps/01-confirm.md +5 -0
  46. package/dist/modules/lawbook/assets/skills/sync/steps/02-reconcile.md +16 -0
  47. package/dist/modules/lawbook/assets/skills/sync/steps/03-validate.md +6 -0
  48. package/dist/modules/lawbook/assets/skills/sync/steps/04-promote.md +10 -0
  49. package/dist/modules/lawbook/assets/skills/sync/steps/05-report.md +7 -0
  50. package/dist/modules/lawbook/register.js +17 -36
  51. package/dist/modules/tools/register.js +15 -15
  52. package/dist/server.js +13 -7
  53. package/dist/shared/budget.js +159 -0
  54. package/dist/shared/exposure.js +110 -0
  55. package/dist/shared/manifest.js +11 -2
  56. package/dist/shared/mcp.js +33 -0
  57. package/dist/shared/redact.js +90 -0
  58. package/dist/shared/schema-tokens.js +86 -0
  59. package/dist/shared/tokens.js +41 -0
  60. package/package.json +2 -1
@@ -1,14 +1,17 @@
1
1
  import { underPaths } from "./verify-model.js";
2
2
  /** Build the cross-file dependency graph, restricted to `paths` when given. */
3
- function buildGraph(db, paths) {
3
+ function buildGraph(db, paths, edgeKinds) {
4
+ const kindFilter = edgeKinds && edgeKinds.length > 0
5
+ ? ` AND e.kind IN (${edgeKinds.map(() => "?").join(", ")})`
6
+ : "";
4
7
  const rows = db
5
8
  .prepare(`SELECT DISTINCT sf.path AS src, df.path AS dst
6
9
  FROM edges e
7
10
  JOIN files sf ON sf.id = e.src_file_id
8
11
  JOIN nodes dn ON dn.id = e.dst_node_id
9
12
  JOIN files df ON df.id = dn.file_id
10
- WHERE e.dst_node_id IS NOT NULL AND sf.path <> df.path`)
11
- .all();
13
+ WHERE e.dst_node_id IS NOT NULL AND sf.path <> df.path${kindFilter}`)
14
+ .all(...(edgeKinds && edgeKinds.length > 0 ? edgeKinds : []));
12
15
  const adj = new Map();
13
16
  for (const { src, dst } of rows) {
14
17
  if (!underPaths(src, paths) || !underPaths(dst, paths))
@@ -203,7 +206,7 @@ function reachableFindings(law, rule, adj) {
203
206
  */
204
207
  export function runGraphLaw(db, law, paths) {
205
208
  const rule = law.verification.rule;
206
- const adj = buildGraph(db, paths);
209
+ const adj = buildGraph(db, paths, rule.edgeKinds);
207
210
  const findings = [];
208
211
  const wantReachable = rule.reachable === true && rule.from != null && rule.to != null;
209
212
  const wantCircular = rule.circular === true || (!rule.circular && !wantReachable);
@@ -3,12 +3,24 @@ import path from "node:path";
3
3
  import { agentById } from "../../shared/agents.js";
4
4
  import { sha256 } from "../../shared/install.js";
5
5
  import { globError, hasBackend } from "./laws.js";
6
+ /** Claude Code `${path}` templates — see https://code.claude.com/docs/en/hooks */
7
+ const SPECLAW_HOOK_INPUT = {
8
+ projectPath: "${cwd}",
9
+ event: "${hook_event_name}",
10
+ toolName: "${tool_name}",
11
+ payload: {
12
+ hook_event_name: "${hook_event_name}",
13
+ tool_name: "${tool_name}",
14
+ tool_input: { file_path: "${tool_input.file_path}" },
15
+ },
16
+ };
6
17
  /** The speclaw hook object — its `{type, server}` pair is the merge identity. */
7
18
  const SPECLAW_HOOK = {
8
19
  type: "mcp_tool",
9
20
  server: "speclaw",
10
21
  tool: "speclaw_check",
11
22
  timeout: 5,
23
+ input: SPECLAW_HOOK_INPUT,
12
24
  };
13
25
  /** Tool-name matcher for the file-mutating tools the `path` backend can evaluate. */
14
26
  const MUTATION_MATCHER = "Write|Edit|MultiEdit|NotebookEdit";
@@ -22,6 +22,7 @@ const graphRuleSchema = z.object({
22
22
  reachable: z.boolean().optional(),
23
23
  from: z.string().optional(),
24
24
  to: z.string().optional(),
25
+ edgeKinds: z.array(z.string()).optional(),
25
26
  });
26
27
  const verificationSchema = z.discriminatedUnion("kind", [
27
28
  z.object({ kind: z.literal("path") }),
@@ -0,0 +1,108 @@
1
+ import { z } from "zod";
2
+ import { defineTool, text } from "../../shared/mcp.js";
3
+ import { shouldExpose } from "../../shared/exposure.js";
4
+ import { scaffold } from "./scaffold.js";
5
+ import { checkAction } from "./check.js";
6
+ import { verifyLaws } from "./verify.js";
7
+ import { loadPacks } from "../tools/packs.js";
8
+ import { AGENTS, configureAgent } from "../../shared/agents.js";
9
+ import { emptyReport } from "../../shared/install.js";
10
+ /** Human help text for init_project's questionnaire (not embedded in MCP schemas). */
11
+ const profileFieldHelp = {
12
+ project_name: "Short project name, e.g. the repo name",
13
+ project_description: "One-line description of what the project does",
14
+ organization: "Company/team name",
15
+ stack_summary: "e.g. 'Next.js 15 + TypeScript frontend, FastAPI + PostgreSQL backend'",
16
+ architecture: "e.g. 'hexagonal architecture with bounded contexts'",
17
+ test_commands: "Real commands, e.g. 'pytest backend/tests && npm run test'",
18
+ lint_commands: "Real commands, e.g. 'ruff check . && npm run lint && tsc --noEmit'",
19
+ branch_pattern: "e.g. 'feature/<ticket-id>-<slug>'",
20
+ commit_style: "e.g. 'conventional commits, imperative, English'",
21
+ custom_laws: "Extra markdown for LAWS.md — project-specific binding rules",
22
+ compass_hints: "Markdown bullets with real entrypoints for docs/compass.md",
23
+ base_standards_extra: "Extra cross-cutting rules for base-standards.md",
24
+ modules_table: "Markdown table of modules/bounded contexts",
25
+ layering_rules: "Layers and allowed dependencies for architecture.md",
26
+ backend_layers: "Backend layer table for backend-standards.md",
27
+ frontend_layers: "Frontend layer table for frontend-standards.md",
28
+ versioning_rules: "Versioning/release convention for conventions.md",
29
+ documentation_extra: "Repo-specific docstring notes for documentation.md",
30
+ };
31
+ /** Lean Zod shape for scaffold — no .describe() text (that cost rides in every request). */
32
+ const profileShape = {
33
+ project_name: z.string(),
34
+ project_description: z.string().optional(),
35
+ organization: z.string().optional(),
36
+ stack_summary: z.string().optional(),
37
+ architecture: z.string().optional(),
38
+ test_commands: z.string().optional(),
39
+ lint_commands: z.string().optional(),
40
+ branch_pattern: z.string().optional(),
41
+ commit_style: z.string().optional(),
42
+ custom_laws: z.string().optional(),
43
+ compass_hints: z.string().optional(),
44
+ base_standards_extra: z.string().optional(),
45
+ modules_table: z.string().optional(),
46
+ layering_rules: z.string().optional(),
47
+ backend_layers: z.string().optional(),
48
+ frontend_layers: z.string().optional(),
49
+ versioning_rules: z.string().optional(),
50
+ documentation_extra: z.string().optional(),
51
+ };
52
+ function makeAdd(server, minimal) {
53
+ return (name, description, inputSchema, handler) => {
54
+ if (!shouldExpose(name, minimal))
55
+ return;
56
+ defineTool(server, { name, description, inputSchema, handler });
57
+ };
58
+ }
59
+ /**
60
+ * Foundation tools except `doctor`. Lives in a separate file so budget/doctor
61
+ * measurement can import it without forming a file-level SCC through
62
+ * `register.ts` → `doctor.ts` → `context-budget.ts`.
63
+ */
64
+ export function registerFoundationCore(server, opts = {}) {
65
+ const add = makeAdd(server, Boolean(opts.minimal));
66
+ add("init_project", "Start here to initialize speclaw: returns the analysis questionnaire and packs.", { projectPath: z.string() }, async () => {
67
+ const packs = loadPacks();
68
+ return text({
69
+ instructions: [
70
+ "1. Analyze the repository at projectPath and fill in every profile field below with REAL values from the codebase (read package.json / pyproject.toml / CI configs / README — do not invent).",
71
+ "2. The foundation is a set of GRANULAR standards under docs/standards/ (base, architecture, backend, frontend, testing, conventions, lawbook), bound by LAWS.md and referenced from CLAUDE.md/AGENTS.md. Fill their structured fields from the real repo: modules_table and layering_rules (architecture), backend_layers, frontend_layers, versioning_rules, and any base_standards_extra. Omit a field only when that standard genuinely doesn't apply to this stack.",
72
+ "3. Suggest packs: add stack packs whose 'detect' hints match dependencies you found; offer the rest. Ask the user which packs to install (the lawbook workflow is always installed).",
73
+ "4. Infer the working language and the branch/commit/tracker conventions from the repo itself — the language already used in docstrings, commit messages, branch names, and PR/ticket bodies. Do NOT ask the user or assume English; match what the repo does, and set branch_pattern/commit_style accordingly. speclaw does not prescribe a ticket tool — leave tracker linkage to the team's own convention.",
74
+ "5. Draft any custom_laws (extra binding rules for LAWS.md) from conventions you observed that the standard set doesn't cover.",
75
+ "6. Call the 'scaffold' tool with { projectPath, profile, packs }.",
76
+ "7. Follow the nextSteps returned by scaffold: complete the HTML-comment sections still left in docs/standards/*, then run the lawbook_init and compass_index tools (both built into speclaw — no external installs).",
77
+ ],
78
+ profileFields: profileFieldHelp,
79
+ packs,
80
+ });
81
+ });
82
+ add("scaffold", "Write foundation, lawbook workflow, packs, IDE symlinks, and .mcp.json. Never overwrites.", {
83
+ projectPath: z.string(),
84
+ profile: z.object(profileShape),
85
+ packs: z.array(z.string()),
86
+ agents: z.array(z.string()).optional(),
87
+ }, async ({ projectPath, profile, packs, agents }) => text(scaffold(projectPath, profile, packs, agents ?? [])));
88
+ add("configure_agent", "Add one agent's IDE symlinks and MCP config to an already-scaffolded project.", {
89
+ projectPath: z.string(),
90
+ agent: z.enum(AGENTS.map((a) => a.id)),
91
+ }, async ({ projectPath, agent }) => {
92
+ const report = emptyReport();
93
+ configureAgent(projectPath, agent, report);
94
+ return text(report);
95
+ });
96
+ add("speclaw_check", "Invoked by speclaw's hooks to enforce laws — do not call directly.", {
97
+ projectPath: z.string(),
98
+ event: z.enum(["PreToolUse", "PostToolUse", "Stop", "InstructionsLoaded"]),
99
+ toolName: z.string().optional(),
100
+ payload: z.record(z.unknown()),
101
+ }, async ({ projectPath, event, toolName, payload }) => text(checkAction({ projectPath, event: event, toolName, payload })));
102
+ add("law_verify", "Verify deterministic deps/graph laws and return violations by file.", {
103
+ projectPath: z.string(),
104
+ paths: z.array(z.string()).optional(),
105
+ engines: z.array(z.enum(["deps", "graph"])).optional(),
106
+ lawIds: z.array(z.string()).optional(),
107
+ }, async ({ projectPath, paths, engines, lawIds }) => text(verifyLaws({ projectPath, paths, engines: engines, lawIds })));
108
+ }
@@ -1,163 +1,26 @@
1
1
  import { z } from "zod";
2
- import { text } from "../../shared/mcp.js";
3
- import { scaffold } from "./scaffold.js";
4
- import { doctor } from "./doctor.js";
5
- import { checkAction } from "./check.js";
6
- import { verifyLaws } from "./verify.js";
7
- import { loadPacks } from "../tools/packs.js";
8
- import { AGENTS, configureAgent } from "../../shared/agents.js";
9
- import { emptyReport } from "../../shared/install.js";
10
- const profileShape = {
11
- project_name: z.string().describe("Short project name, e.g. the repo name"),
12
- project_description: z
13
- .string()
14
- .optional()
15
- .describe("One-line description of what the project does"),
16
- organization: z.string().optional().describe("Company/team name"),
17
- stack_summary: z
18
- .string()
19
- .optional()
20
- .describe("e.g. 'Next.js 15 + TypeScript frontend, FastAPI + PostgreSQL backend'"),
21
- architecture: z
22
- .string()
23
- .optional()
24
- .describe("e.g. 'hexagonal architecture with bounded contexts'"),
25
- test_commands: z
26
- .string()
27
- .optional()
28
- .describe("Real commands, e.g. 'pytest backend/tests && npm run test'"),
29
- lint_commands: z
30
- .string()
31
- .optional()
32
- .describe("Real commands, e.g. 'ruff check . && npm run lint && tsc --noEmit'"),
33
- branch_pattern: z.string().optional().describe("e.g. 'feature/<ticket-id>-<slug>'"),
34
- commit_style: z.string().optional().describe("e.g. 'conventional commits, imperative, English'"),
35
- custom_laws: z
36
- .string()
37
- .optional()
38
- .describe("Extra markdown appended to LAWS.md — project-specific binding rules the analysis surfaced"),
39
- compass_hints: z
40
- .string()
41
- .optional()
42
- .describe("Markdown bullets with the repo's real entrypoints and common traces, inserted into docs/compass.md"),
43
- base_standards_extra: z
44
- .string()
45
- .optional()
46
- .describe("Markdown with any project-specific cross-cutting rules, appended to docs/standards/base-standards.md"),
47
- modules_table: z
48
- .string()
49
- .optional()
50
- .describe("Markdown table of the repo's real modules/bounded contexts + one-line responsibility, for docs/standards/architecture.md"),
51
- layering_rules: z
52
- .string()
53
- .optional()
54
- .describe("Markdown describing the layers and their allowed dependencies, for docs/standards/architecture.md"),
55
- backend_layers: z
56
- .string()
57
- .optional()
58
- .describe("Markdown layer table (Layer | File | Responsibility) from the real backend, for docs/standards/backend-standards.md"),
59
- frontend_layers: z
60
- .string()
61
- .optional()
62
- .describe("Markdown layer table from the real frontend, for docs/standards/frontend-standards.md"),
63
- versioning_rules: z
64
- .string()
65
- .optional()
66
- .describe("The repo's versioning/release convention, for docs/standards/conventions.md"),
67
- documentation_extra: z
68
- .string()
69
- .optional()
70
- .describe("Repo-specific docstring notes (keep only the languages used, the enforced linter), appended to docs/standards/documentation.md"),
71
- };
72
- // ─── The foundation module: analyze the repo, then write the constitution ───
73
- /** Register the foundation MCP tools (init_project, scaffold, configure_agent, doctor). */
74
- export function registerFoundation(server) {
75
- server.registerTool("init_project", {
76
- description: "START HERE to initialize speclaw in a project. Returns the analysis questionnaire the agent must answer by reading the target repo, plus the available skill packs. Do NOT guess answers — investigate the codebase (package.json, pyproject.toml, CI config, existing docs) and confirm the pack selection with the user before calling scaffold.",
77
- inputSchema: {
78
- projectPath: z.string().describe("Absolute path to the project to initialize"),
79
- },
80
- }, async () => {
81
- const packs = loadPacks();
82
- return text({
83
- instructions: [
84
- "1. Analyze the repository at projectPath and fill in every profile field below with REAL values from the codebase (read package.json / pyproject.toml / CI configs / README — do not invent).",
85
- "2. The foundation is a set of GRANULAR standards under docs/standards/ (base, architecture, backend, frontend, testing, conventions, lawbook), bound by LAWS.md and referenced from CLAUDE.md/AGENTS.md. Fill their structured fields from the real repo: modules_table and layering_rules (architecture), backend_layers, frontend_layers, versioning_rules, and any base_standards_extra. Omit a field only when that standard genuinely doesn't apply to this stack.",
86
- "3. Suggest packs: add stack packs whose 'detect' hints match dependencies you found; offer the rest. Ask the user which packs to install (the lawbook workflow is always installed).",
87
- "4. Infer the working language and the branch/commit/tracker conventions from the repo itself — the language already used in docstrings, commit messages, branch names, and PR/ticket bodies. Do NOT ask the user or assume English; match what the repo does, and set branch_pattern/commit_style accordingly. speclaw does not prescribe a ticket tool — leave tracker linkage to the team's own convention.",
88
- "5. Draft any custom_laws (extra binding rules for LAWS.md) from conventions you observed that the standard set doesn't cover.",
89
- "6. Call the 'scaffold' tool with { projectPath, profile, packs }.",
90
- "7. Follow the nextSteps returned by scaffold: complete the HTML-comment sections still left in docs/standards/*, then run the lawbook_init and compass_index tools (both built into speclaw — no external installs).",
91
- ],
92
- profileFields: Object.fromEntries(Object.entries(profileShape).map(([key, schema]) => [key, schema.description ?? ""])),
93
- packs,
94
- });
95
- });
96
- server.registerTool("scaffold", {
97
- description: "Write the speclaw setup into a project: the foundation (LAWS.md constitution + granular docs/standards/* + CLAUDE.md + AGENTS.md + docs/compass.md), the lawbook workflow (always), the selected tool packs, multi-IDE symlinks (.claude/.cursor/.codex/.agents), .mcp.json wiring for speclaw, and .gitignore for .speclaw/. Never overwrites existing files. Call init_project first.",
98
- inputSchema: {
99
- projectPath: z.string().describe("Absolute path to the project"),
100
- profile: z.object(profileShape).describe("Project profile gathered by analyzing the repo"),
101
- packs: z.array(z.string()).describe("Optional tool pack names (quality, workflow, agents)"),
102
- agents: z
103
- .array(z.string())
104
- .optional()
105
- .describe(`Agent ids to configure (symlinks + MCP): ${AGENTS.map((a) => a.id).join(", ")}. Usually the CLI handles this; omit to write content only.`),
106
- },
107
- }, async ({ projectPath, profile, packs, agents }) => text(scaffold(projectPath, profile, packs, agents ?? [])));
108
- server.registerTool("configure_agent", {
109
- description: "Configure one agent's integration in an already-scaffolded project: create its IDE symlinks into ai-specs and register the speclaw MCP server in its config. Re-runnable; add agents one at a time.",
110
- inputSchema: {
111
- projectPath: z.string().describe("Absolute path to the project"),
112
- agent: z
113
- .enum(AGENTS.map((a) => a.id))
114
- .describe("Agent id to configure"),
115
- },
116
- }, async ({ projectPath, agent }) => {
117
- const report = emptyReport();
118
- configureAgent(projectPath, agent, report);
2
+ import { defineTool, text } from "../../shared/mcp.js";
3
+ import { shouldExpose } from "../../shared/exposure.js";
4
+ import { registerFoundationCore } from "./register-core.js";
5
+ export { registerFoundationCore } from "./register-core.js";
6
+ const DOCTOR_DESCRIPTION = "Verify the speclaw install; returns a versioned DoctorReport (schemaVersion 1).";
7
+ /** Register foundation MCP tools (core + doctor). */
8
+ export function registerFoundation(server, opts = {}) {
9
+ registerFoundationCore(server, opts);
10
+ const minimal = Boolean(opts.minimal);
11
+ if (!shouldExpose("doctor", minimal))
12
+ return;
13
+ const inputSchema = { projectPath: z.string() };
14
+ const handler = async ({ projectPath }) => {
15
+ // Lazy load: register.ts must stay out of the context-budget → doctor SCC.
16
+ const { doctor } = await import("./doctor.js");
17
+ const report = await doctor(projectPath, { redact: true });
119
18
  return text(report);
120
- });
121
- server.registerTool("speclaw_check", {
122
- // ≤12 words: this is invoked by speclaw's hooks, never called directly.
123
- description: "Invoked by speclaw's hooks to enforce laws — do not call directly.",
124
- inputSchema: {
125
- projectPath: z.string().describe("Absolute path to the project"),
126
- event: z
127
- .enum(["PreToolUse", "PostToolUse", "Stop", "InstructionsLoaded"])
128
- .describe("The hook event that fired"),
129
- toolName: z.string().optional().describe("The tool the agent is invoking, when relevant"),
130
- payload: z.record(z.unknown()).describe("The raw hook event payload from the agent"),
131
- },
132
- }, async ({ projectPath, event, toolName, payload }) => text(checkAction({ projectPath, event: event, toolName, payload })));
133
- server.registerTool("law_verify", {
134
- // ≤30 words: the batch counterpart to speclaw_check, for the Stop hook and CI.
135
- description: "Verify the project's deterministic laws (dependency and graph rules) and return violations by file. Run before claiming an architecture task done.",
136
- inputSchema: {
137
- projectPath: z.string().describe("Absolute path to the project"),
138
- paths: z
139
- .array(z.string())
140
- .optional()
141
- .describe("Restrict to source files under these project-relative paths"),
142
- engines: z
143
- .array(z.enum(["deps", "graph"]))
144
- .optional()
145
- .describe("Which batch engines to run; omit for all"),
146
- lawIds: z.array(z.string()).optional().describe("Restrict to these law ids"),
147
- },
148
- }, async ({ projectPath, paths, engines, lawIds }) => text(verifyLaws({ projectPath, paths, engines: engines, lawIds })));
149
- server.registerTool("doctor", {
150
- description: "Verify a speclaw installation: ai-specs presence, the foundation (LAWS.md + standards + agent contracts), IDE symlinks health, the lawbook/ workflow, the Compass index, and .mcp.json wiring. Returns a checklist with remediation hints.",
151
- inputSchema: { projectPath: z.string().describe("Absolute path to the project") },
152
- }, async ({ projectPath }) => {
153
- const checks = doctor(projectPath);
154
- const failed = checks.filter((c) => !c.ok);
155
- return text({
156
- healthy: failed.length === 0,
157
- checks,
158
- summary: failed.length === 0
159
- ? "Everything is within the law."
160
- : `${failed.length} check(s) failed — see details.`,
161
- });
19
+ };
20
+ defineTool(server, {
21
+ name: "doctor",
22
+ description: DOCTOR_DESCRIPTION,
23
+ inputSchema,
24
+ handler,
162
25
  });
163
26
  }
@@ -159,7 +159,7 @@ export function scaffold(projectPath, profile, packNames, agents = [], opts = {}
159
159
  // Record what was installed so `speclaw update` can re-apply these packs and
160
160
  // gate feature migrations by version, plus the managed-file baselines that let
161
161
  // a later update tell user edits from stale files.
162
- writeManifest(projectPath, pkgVersion(), packNames, record);
162
+ writeManifest(projectPath, pkgVersion(), packNames, record, opts.minimal !== undefined ? { minimal: opts.minimal } : {});
163
163
  report.nextSteps = [
164
164
  "Run the `lawbook_init` tool to set up the spec-driven workflow (creates lawbook/). No external CLI needed — it's built into speclaw.",
165
165
  "Run the `compass_index` tool to build the local code graph (.speclaw/). No install, no LLM — it's built into speclaw. Re-run it after significant edits.",
@@ -14,34 +14,4 @@ reason) while any task is unchecked, while `reports/` holds no discipline report
14
14
  or while the delta specs are not yet synced into the canonical specs. So archive
15
15
  is the last step of a completed change: reconcile, sync, then archive.
16
16
 
17
- ## Steps
18
-
19
- 1. Confirm the change is truly done: every task in `tasks.md` checked, quality
20
- gates green, behavior verified, and the discipline reports written under
21
- `reports/`.
22
-
23
- 2. **Reconciliation review (agent-executed).** Run the reconciliation from the
24
- `sync` skill: reconstruct what was built (branch diff since draft +
25
- `compass_explore` / `compass_impact`) and compare it to the change's delta
26
- specs.
27
- - **If the code drifted past the contracts:** show short insights — a tight
28
- bullet list of what was built outside the delta specs and why it matters
29
- (e.g. "DB path renamed to `data/app.db` + auto-migration — infra behavior
30
- absent from the spec") — and reconcile the delta specs (write the drift
31
- in). Drift left unreconciled cannot be archived: the specs-synced gate will
32
- block it.
33
- - **If nothing drifted:** say so and continue.
34
-
35
- 3. Run `lawbook_validate`, then `lawbook_sync` to promote the delta specs into
36
- `lawbook/specs/`. This is required, not optional: `lawbook_archive` refuses
37
- unless the canonical specs already match the delta specs.
38
-
39
- 4. Run the `lawbook_archive` tool with the change name and today's date
40
- (`YYYY-MM-DD`). It re-checks the gate deterministically and, if it passes,
41
- moves `lawbook/changes/<name>/` to `lawbook/changes/archive/<date>-<name>/`.
42
- If it refuses, resolve the reported blockers (unchecked tasks, missing
43
- reports, unsynced specs) and retry.
44
-
45
- 5. Report the archive path, what you reconciled (or that nothing drifted), and
46
- the promoted specs. Never move the folder by hand — a manual `mv` skips the
47
- gate and hides an incomplete change.
17
+ Read `steps/01-confirm-done.md` and do only what it says.
@@ -0,0 +1,7 @@
1
+ # Confirm the change is done
2
+
3
+ Confirm the change is truly done: every task in `tasks.md` checked, quality
4
+ gates green, behavior verified, and the discipline reports written under
5
+ `reports/`.
6
+
7
+ Next: read `steps/02-reconcile.md` and do only what it says.
@@ -0,0 +1,15 @@
1
+ # Reconciliation review (agent-executed)
2
+
3
+ Run the reconciliation from the `sync` skill: reconstruct what was built
4
+ (branch diff since draft + `compass_explore` / `compass_impact`) and compare it
5
+ to the change's delta specs.
6
+
7
+ - **If the code drifted past the contracts:** show short insights — a tight
8
+ bullet list of what was built outside the delta specs and why it matters
9
+ (e.g. "DB path renamed to `data/app.db` + auto-migration — infra behavior
10
+ absent from the spec") — and reconcile the delta specs (write the drift
11
+ in). Drift left unreconciled cannot be archived: the specs-synced gate will
12
+ block it.
13
+ - **If nothing drifted:** say so and continue.
14
+
15
+ Next: read `steps/03-validate-and-sync.md` and do only what it says.
@@ -0,0 +1,7 @@
1
+ # Validate and sync
2
+
3
+ Run `lawbook_validate`, then `lawbook_sync` to promote the delta specs into
4
+ `lawbook/specs/`. This is required, not optional: `lawbook_archive` refuses
5
+ unless the canonical specs already match the delta specs.
6
+
7
+ Next: read `steps/04-archive.md` and do only what it says.
@@ -0,0 +1,9 @@
1
+ # Archive
2
+
3
+ Run the `lawbook_archive` tool with the change name and today's date
4
+ (`YYYY-MM-DD`). It re-checks the gate deterministically and, if it passes,
5
+ moves `lawbook/changes/<name>/` to `lawbook/changes/archive/<date>-<name>/`.
6
+ If it refuses, resolve the reported blockers (unchecked tasks, missing
7
+ reports, unsynced specs) and retry.
8
+
9
+ Next: read `steps/05-report.md` and do only what it says.
@@ -0,0 +1,7 @@
1
+ # Report
2
+
3
+ Report the archive path, what you reconciled (or that nothing drifted), and
4
+ the promoted specs. Never move the folder by hand — a manual `mv` skips the
5
+ gate and hides an incomplete change.
6
+
7
+ No further steps remain — archive workflow complete.
@@ -8,104 +8,6 @@ description: Implement the tasks of a drafted change, following its spec and the
8
8
  Work through a change's `tasks.md` in order, keeping code, spec, and standards
9
9
  in lockstep.
10
10
 
11
- ## Step 0 Load the change
11
+ Use when the user wants to start or continue implementing a drafted change.
12
12
 
13
- - Read `lawbook/changes/<name>/proposal.md`, `tasks.md`, and the delta specs under
14
- `specs/`. If unsure which change, run `lawbook_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
- **Verification is isolated by construction — it never touches real data.** Run it
47
- against an ephemeral or throwaway store: a temporary copy, an in-memory database
48
- (`:memory:`), a dedicated test store, or inside a transaction that is rolled
49
- back — best of all, verify pure/domain logic with fixtures and no store at all.
50
- Do **not** create, update, or delete the user's real data (a production or
51
- development database, or files holding real data) as a side effect of proving a
52
- change, and do **not** run raw store commands (e.g. direct SQL) against a live
53
- store. Snapshot-and-restore is not a sanctioned method — a stray write slips past
54
- the restore.
55
-
56
- If isolation is genuinely impossible and a real-store write is unavoidable,
57
- **stop and ask first** — state exactly what you will write and to which store —
58
- and proceed only after explicit authorization. A backup is not a substitute for
59
- authorization. Record in the report how verification stayed isolated (or the
60
- authorization you obtained).
61
-
62
- ## Step 5 — Write the discipline reports (mandatory)
63
-
64
- Record the evidence of testing under `lawbook/changes/<name>/reports/`, one file
65
- per discipline the change touched, named for that discipline. The set is **open,
66
- not a fixed list** — `backend.md`, `frontend.md`, and `api.md` are the common
67
- ones, but write `database.md`, `infra.md`, `security.md`, `performance.md`,
68
- `e2e.md`, etc. when the change exercises those concerns, and coin a clear
69
- `<discipline>.md` for anything none of them fit. Omit disciplines the change did
70
- not touch; the archive is blocked until at least one discipline report exists.
71
-
72
- **`api.md` is mandatory whenever the change touches an API surface** — a new or
73
- modified endpoint, its request/response contract, its status codes, or its
74
- auth/permission or ordering guarantees. A `backend.md` unit report does not
75
- substitute for it: the contract is a distinct concern. In `api.md` document the
76
- method and path, the auth/permissions, the response shape and every status code
77
- the change governs (e.g. `200`/`401`/`403`/`404`), any ordering guarantee, and
78
- how the contract was exercised (test client and/or `curl`) — kept isolated from
79
- any live data store per Step 4.
80
-
81
- Each report MUST follow this structure, in order — the fixed shape is what makes
82
- the evidence trustworthy and reproducible, rather than left to improvisation:
83
-
84
- 1. **Title + header** — `# <Discipline> checks — <change> (<date>)`, then a line
85
- `Date · Branch · Environment/cwd` naming where the commands ran.
86
- 2. **Gates & results** — a `| Check | Command | Result |` table: each gate, the
87
- exact command, and its real result with pass/fail counts (e.g. "62 files, 434
88
- passed") and ✅/⚠️/❌. Quote real output — never paraphrase a green you did
89
- not see.
90
- 3. **Tests added / updated** — each new or changed test and what it asserts; note
91
- TDD evidence ("failed before the fix, passes after") where it applies.
92
- 4. **Spec-scenario coverage** — a table mapping each `#### Scenario` in this
93
- change's delta specs to how it was verified (a test id, a gate, or a manual
94
- step). Every scenario must appear.
95
- 5. **Pre-existing / unrelated failures** — any failing check not caused by this
96
- change, with proof it is pre-existing (e.g. it reproduces with the change
97
- stashed) — or state "none".
98
- 6. **Pending manual steps** — anything not automated, stated plainly — or "none".
99
- 7. **Verdict** — one line.
100
-
101
- If a test kind does not yet apply (e.g. no unit runner), the report says so in
102
- place of that evidence and records the gates and manual verification that stood
103
- in.
104
-
105
- ## Step 6 — Hand off
106
-
107
- When every task is checked and gates are green, tell the user the change is
108
- ready to `sync` and `archive`. Keep the delta specs current as you build, but
109
- know that `sync` formally reconciles the delta specs against what was actually
110
- built — so behavior that drifted past the original spec is caught there, not
111
- left to chance.
13
+ Read `steps/01-load-change.md` and do only what it says.
@@ -0,0 +1,7 @@
1
+ # Load the change
2
+
3
+ - Read `lawbook/changes/<name>/proposal.md`, `tasks.md`, and the delta specs under
4
+ `specs/`. If unsure which change, run `lawbook_list`.
5
+ - Read the governing standards in `docs/standards/` for the areas you'll touch.
6
+
7
+ Next: read `steps/02-branch.md` and do only what it says.
@@ -0,0 +1,6 @@
1
+ # Branch first
2
+
3
+ Create the feature branch (the mandatory Step 0 in `tasks.md`), following the
4
+ repo's branch pattern `{{branch_pattern}}`.
5
+
6
+ Next: read `steps/03-implement.md` and do only what it says.
@@ -0,0 +1,11 @@
1
+ # Implement task by task
2
+
3
+ - Use `compass_explore` before editing to see a symbol's callers/callees and
4
+ blast radius; re-run `compass_index` after significant edits to keep the
5
+ graph fresh.
6
+ - Make the smallest correct change; match the surrounding code.
7
+ - The code must satisfy the delta spec exactly. If reality diverges from the
8
+ spec, update the spec in the change (not silently) — the two must agree.
9
+ - Check off each task in `tasks.md` as you complete it.
10
+
11
+ Next: read `steps/04-quality-gates.md` and do only what it says.
@@ -0,0 +1,10 @@
1
+ # Quality gates (mandatory)
2
+
3
+ Run the repo's gates from `docs/standards/testing-standards.md`:
4
+
5
+ - Tests: `{{test_commands}}`
6
+ - Lint / type-check: `{{lint_commands}}`
7
+
8
+ Run them yourself and report real output. A red gate blocks completion.
9
+
10
+ Next: read `steps/05-manual-verification.md` and do only what it says.
@@ -0,0 +1,22 @@
1
+ # Manual verification (mandatory, agent executes)
2
+
3
+ Exercise the behavior (endpoint/UI/CLI) yourself where feasible — do not
4
+ delegate manual testing to the user. Record what you verified.
5
+
6
+ **Verification is isolated by construction — it never touches real data.** Run it
7
+ against an ephemeral or throwaway store: a temporary copy, an in-memory database
8
+ (`:memory:`), a dedicated test store, or inside a transaction that is rolled
9
+ back — best of all, verify pure/domain logic with fixtures and no store at all.
10
+ Do **not** create, update, or delete the user's real data (a production or
11
+ development database, or files holding real data) as a side effect of proving a
12
+ change, and do **not** run raw store commands (e.g. direct SQL) against a live
13
+ store. Snapshot-and-restore is not a sanctioned method — a stray write slips past
14
+ the restore.
15
+
16
+ If isolation is genuinely impossible and a real-store write is unavoidable,
17
+ **stop and ask first** — state exactly what you will write and to which store —
18
+ and proceed only after explicit authorization. A backup is not a substitute for
19
+ authorization. Record in the report how verification stayed isolated (or the
20
+ authorization you obtained).
21
+
22
+ Next: read `steps/06-discipline-reports.md` and do only what it says.