@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.
Files changed (62) hide show
  1. package/ATTRIBUTION.md +34 -0
  2. package/LICENSE +21 -0
  3. package/README.md +134 -0
  4. package/dist/cli/commands/agent.js +42 -0
  5. package/dist/cli/commands/doctor.js +21 -0
  6. package/dist/cli/commands/index-build.js +24 -0
  7. package/dist/cli/commands/init.js +121 -0
  8. package/dist/cli/commands/query.js +69 -0
  9. package/dist/cli/commands/spec.js +71 -0
  10. package/dist/cli/index.js +77 -0
  11. package/dist/cli/lib/args.js +44 -0
  12. package/dist/cli/lib/ui.js +89 -0
  13. package/dist/modules/compass/db.js +84 -0
  14. package/dist/modules/compass/embedder.js +87 -0
  15. package/dist/modules/compass/extract.js +91 -0
  16. package/dist/modules/compass/indexer.js +158 -0
  17. package/dist/modules/compass/languages.js +67 -0
  18. package/dist/modules/compass/parser.js +37 -0
  19. package/dist/modules/compass/query.js +260 -0
  20. package/dist/modules/compass/register.js +72 -0
  21. package/dist/modules/compass/watcher.js +95 -0
  22. package/dist/modules/foundation/assets/AGENTS.template.md +61 -0
  23. package/dist/modules/foundation/assets/CLAUDE.template.md +72 -0
  24. package/dist/modules/foundation/assets/LAWS.template.md +39 -0
  25. package/dist/modules/foundation/assets/docs/compass.template.md +43 -0
  26. package/dist/modules/foundation/assets/docs/standards/architecture.template.md +36 -0
  27. package/dist/modules/foundation/assets/docs/standards/backend-standards.template.md +50 -0
  28. package/dist/modules/foundation/assets/docs/standards/base-standards.template.md +47 -0
  29. package/dist/modules/foundation/assets/docs/standards/conventions.template.md +31 -0
  30. package/dist/modules/foundation/assets/docs/standards/documentation.template.md +50 -0
  31. package/dist/modules/foundation/assets/docs/standards/frontend-standards.template.md +46 -0
  32. package/dist/modules/foundation/assets/docs/standards/spec-workflow.template.md +46 -0
  33. package/dist/modules/foundation/assets/docs/standards/testing-standards.template.md +34 -0
  34. package/dist/modules/foundation/doctor.js +107 -0
  35. package/dist/modules/foundation/register.js +86 -0
  36. package/dist/modules/foundation/scaffold.js +103 -0
  37. package/dist/modules/spec/assets/commands/archive.md +10 -0
  38. package/dist/modules/spec/assets/commands/build.md +11 -0
  39. package/dist/modules/spec/assets/commands/draft.md +12 -0
  40. package/dist/modules/spec/assets/commands/explore.md +10 -0
  41. package/dist/modules/spec/assets/commands/sync.md +9 -0
  42. package/dist/modules/spec/assets/rules/spec-tasks-mandatory-steps.md +37 -0
  43. package/dist/modules/spec/assets/skills/archive/SKILL.md +22 -0
  44. package/dist/modules/spec/assets/skills/build/SKILL.md +49 -0
  45. package/dist/modules/spec/assets/skills/draft/SKILL.md +64 -0
  46. package/dist/modules/spec/assets/skills/explore/SKILL.md +28 -0
  47. package/dist/modules/spec/assets/skills/sync/SKILL.md +21 -0
  48. package/dist/modules/spec/engine.js +227 -0
  49. package/dist/modules/spec/register.js +53 -0
  50. package/dist/modules/tools/assets/packs/agents/backend-developer.md +61 -0
  51. package/dist/modules/tools/assets/packs/agents/frontend-developer.md +62 -0
  52. package/dist/modules/tools/assets/packs/agents/product-strategy-analyst.md +56 -0
  53. package/dist/modules/tools/assets/packs.json +6 -0
  54. package/dist/modules/tools/packs.js +44 -0
  55. package/dist/modules/tools/register.js +27 -0
  56. package/dist/server.js +21 -0
  57. package/dist/shared/agents.js +94 -0
  58. package/dist/shared/install.js +66 -0
  59. package/dist/shared/mcp.js +16 -0
  60. package/dist/shared/paths.js +10 -0
  61. package/dist/shared/render.js +21 -0
  62. package/package.json +49 -0
@@ -0,0 +1,36 @@
1
+ # Architecture — {{project_name}}
2
+
3
+ The structural law of the project. Every change respects these boundaries —
4
+ see [`../../LAWS.md`](../../LAWS.md). Use `compass_explore` to navigate the
5
+ real code before editing.
6
+
7
+ - **Overall shape**: {{architecture}}
8
+ - **Stack**: {{stack_summary}}
9
+
10
+ ## Modules / bounded contexts
11
+
12
+ <!-- speclaw init: list the real modules/bounded contexts of this repo, each
13
+ with a one-line responsibility. Derive them from the actual directory layout
14
+ (e.g. src/modules/*, apps/*), never invent them. Example:
15
+
16
+ | Module | Responsibility |
17
+ |--------|----------------|
18
+ | planning | assignment engine, coverage, scheduling |
19
+ | workforce | employees, skills, availability |
20
+ -->
21
+ {{modules_table}}
22
+
23
+ ## Layering
24
+
25
+ <!-- speclaw init: describe the layers and their allowed dependencies, per the
26
+ repo's real architecture. Keep the "strictly enforced" tone. Example for a
27
+ hexagonal backend: Routes -> Controller -> Manager -> Domain, with the rules
28
+ about what each layer may and may not do. -->
29
+ {{layering_rules}}
30
+
31
+ ## Cross-boundary rules
32
+
33
+ - Dependencies point inward: outer layers depend on inner, never the reverse.
34
+ - Business logic never leaks into transport (HTTP handlers) or persistence.
35
+ - A change that crosses a module boundary needs a spec change describing
36
+ the new contract.
@@ -0,0 +1,50 @@
1
+ # Backend Standards — {{project_name}}
2
+
3
+ Rules for backend code. A law of the project — see [`../../LAWS.md`](../../LAWS.md).
4
+ Architecture and layer boundaries: [`architecture.md`](architecture.md).
5
+
6
+ ## Layering — strictly enforced
7
+
8
+ <!-- speclaw init: fill the layer table from the repo's real backend structure.
9
+ Example (FastAPI):
10
+
11
+ | Layer | File | Responsibility |
12
+ |-------|------|----------------|
13
+ | Routes | router.py | HTTP + schema validation. No business logic. |
14
+ | Controller | controller.py | Business logic, use-case orchestration. |
15
+ | Manager | manager.py | Data access, transactions. |
16
+ | Domain | domain/ | Pure entities and rules. No I/O. |
17
+ -->
18
+ {{backend_layers}}
19
+
20
+ - Never write business logic in the transport/routes layer.
21
+ - Never write data-access code in the business layer.
22
+
23
+ ## Formatting & linting
24
+
25
+ - Use the repo's configured linter/formatter. Run before committing.
26
+ - Lint / type-check command: `{{lint_commands}}`
27
+
28
+ ## Docstrings — mandatory
29
+
30
+ Required on every public module, class, and function/method, in the repo's
31
+ documented style (e.g. Google-style `Args:`/`Returns:`/`Raises:` for Python).
32
+ Write them as you code, not afterward. Dunder and test functions are exempt.
33
+ Docstrings describe what and why — never ticket text.
34
+
35
+ ## Typing
36
+
37
+ - Full signatures (arguments + return types) on public functions.
38
+ - Code must pass the repo's type-checker in strict mode.
39
+
40
+ ## Tests
41
+
42
+ - New behavior ships with tests. Tests mock external systems (DB, network) via
43
+ the project's fixtures — never hit a real database in unit tests.
44
+ - Test command: `{{test_commands}}`
45
+
46
+ ## Migrations
47
+
48
+ - Use the migration tool's CLI; never handcraft revision identifiers.
49
+ - Keep a single linear head; rebase when the base branch moved.
50
+ - Schema changes must match what the spec proposal declared.
@@ -0,0 +1,47 @@
1
+ # Base Standards — {{project_name}}
2
+
3
+ Cross-cutting rules that apply to **all** code in this repository, regardless
4
+ of layer or language. This is a law of the project — see [`../../LAWS.md`](../../LAWS.md).
5
+
6
+ ## Languages
7
+
8
+ - **Code, identifiers, comments, docstrings, commit messages, PR titles/bodies,
9
+ and technical docs**: the repo's artifact language (default English).
10
+ - **User-facing product copy**: as the product requires.
11
+ - **Agent ↔ human communication** (review comments, thread replies, ticket
12
+ bodies): the team's working language ({{team_language}}). Technical terms
13
+ stay in English within that prose — don't force-translate them.
14
+
15
+ ## Commits & branches
16
+
17
+ - **Branch naming**: `{{branch_pattern}}`
18
+ - **Commit style**: {{commit_style}}
19
+ - One focused change per branch; the smallest correct diff. No drive-by
20
+ refactors mixed into a feature branch.
21
+
22
+ ## Comments & documentation
23
+
24
+ - Comments state **constraints the code cannot express** (invariants, tricky
25
+ edge cases, "why"). They never narrate history, restate the next line, or
26
+ address the reviewer.
27
+ - **Never** put ticket IDs, ticket text, or changelog narration
28
+ ("added for {{ticket_prefix}}-123", "fixed as part of…") in code or
29
+ docstrings. Traceability lives in the branch name, PR, and git history.
30
+
31
+ ## Dependencies
32
+
33
+ - Any new dependency must be justified in the PR description. Unannounced
34
+ dependencies are a blocking finding.
35
+ - Prefer the standard library and existing project utilities before adding a
36
+ package.
37
+
38
+ ## Engineering principles
39
+
40
+ - Code reads like its neighbors (naming, structure, idioms).
41
+ - Report outcomes faithfully: if a gate fails, say so with the output; never
42
+ claim a success you did not observe.
43
+ - Ask before irreversible or outward-facing actions (destructive commands,
44
+ publishing reviews/tickets/comments).
45
+
46
+ <!-- speclaw init: add any project-specific base rules the analysis surfaced. -->
47
+ {{base_standards_extra}}
@@ -0,0 +1,31 @@
1
+ # Conventions — {{project_name}}
2
+
3
+ Naming, branching, PR, and tracking conventions. A law of the project — see
4
+ [`../../LAWS.md`](../../LAWS.md).
5
+
6
+ ## Branches
7
+
8
+ - Pattern: `{{branch_pattern}}`
9
+ - Branches without a tracker ticket reference must not be merged (except
10
+ explicitly scoped chores).
11
+
12
+ ## Pull requests
13
+
14
+ - Title and body in the repo's artifact language, following the PR template.
15
+ - The body references the tracker ticket ({{ticket_prefix}}-N) so it links.
16
+ - The ticket lives in the PR title/body only — never in the code.
17
+ - CI must be green before requesting review.
18
+
19
+ ## Tracker
20
+
21
+ - Ticket prefix: `{{ticket_prefix}}`
22
+ - New behavior, endpoints, schema changes, or UI flows get a ticket and a
23
+ spec change; one-line fixes need not.
24
+ - Ticket ↔ PR traceability is mandatory: closing a ticket attaches its PR
25
+ (see the `close-ticket` skill).
26
+
27
+ ## Versioning & releases
28
+
29
+ <!-- speclaw init: describe the repo's versioning/release convention if it has
30
+ one (semver tags, changelog, release branches). Otherwise leave a short note. -->
31
+ {{versioning_rules}}
@@ -0,0 +1,50 @@
1
+ # Documentation Standard — {{project_name}}
2
+
3
+ The docstring/API-comment law of the project — see [`../../LAWS.md`](../../LAWS.md).
4
+ One consistent convention per language, so every public API reads the same way.
5
+ Comment *philosophy* (what a comment is for) lives in
6
+ [`base-standards.md`](base-standards.md); this file defines the *format*.
7
+
8
+ ## Convention per language
9
+
10
+ Defaults below are the idiomatic choice per language. During standardization,
11
+ confirm each against what this repo actually uses and adjust if the team
12
+ follows a different one (e.g. NumPy-style Python).
13
+
14
+ | Language | Convention | Required sections |
15
+ |----------|------------|-------------------|
16
+ | Python | Google-style (PEP 257 base) | `Args:` · `Returns:` · `Raises:` |
17
+ | TypeScript / JavaScript | TSDoc (superset of JSDoc) | `@param` · `@returns` · `@throws` · `@remarks` |
18
+ | Java | Javadoc | `@param` · `@return` · `@throws` |
19
+ | Kotlin | KDoc | `@param` · `@return` · `@throws` |
20
+ | Go | godoc | full-sentence comment beginning with the identifier name; no tags |
21
+ | Rust | rustdoc (`///`, markdown) | `# Examples` · `# Errors` · `# Panics` (when applicable) |
22
+ | C# | XML doc comments | `<summary>` · `<param>` · `<returns>` |
23
+ | Ruby | YARD | `@param` · `@return` |
24
+ | PHP | PHPDoc | `@param` · `@return` · `@throws` |
25
+ | Swift | Swift markup | `- Parameters:` · `- Returns:` · `- Throws:` |
26
+
27
+ <!-- speclaw init: keep only the rows for languages this repo uses, and confirm
28
+ the chosen convention matches the codebase. Add the actual style config file if
29
+ the repo enforces one (e.g. a docstring linter). -->
30
+ {{documentation_extra}}
31
+
32
+ ## Rules
33
+
34
+ - **Required on every public API**: exported/public modules, classes,
35
+ functions, and methods. Internal helpers get a docstring when their intent
36
+ isn't obvious from the name.
37
+ - **Write them as you code**, not afterward — a new or changed public symbol is
38
+ not done until it is documented.
39
+ - **Describe intent and contract** (what and why, inputs/outputs, errors), not
40
+ a restatement of the syntax. No ticket text, no changelog narration.
41
+ - **Exempt**: trivial dunders/accessors and test functions, unless they carry
42
+ non-obvious behavior.
43
+ - **One style per language** — never mix conventions within the same language
44
+ in the repo.
45
+
46
+ ## Enforcement
47
+
48
+ Prefer a docstring linter in the quality gates when the language has one
49
+ (e.g. `ruff`'s pydocstyle rules for Python, `eslint-plugin-jsdoc` for TS/JS).
50
+ List the concrete command in [`testing-standards.md`](testing-standards.md).
@@ -0,0 +1,46 @@
1
+ # Frontend Standards — {{project_name}}
2
+
3
+ Rules for frontend code. A law of the project — see [`../../LAWS.md`](../../LAWS.md).
4
+ Architecture and layer boundaries: [`architecture.md`](architecture.md).
5
+
6
+ ## Layering — strictly enforced
7
+
8
+ <!-- speclaw init: fill from the repo's real frontend structure. Example
9
+ (hexagonal / vertical slices):
10
+
11
+ | Layer | Location | Responsibility |
12
+ |-------|----------|----------------|
13
+ | Domain | domain/ | Pure entities, ports. No framework, no I/O. |
14
+ | Application | application/use-cases/ | Orchestrate domain via ports. |
15
+ | Infrastructure | infrastructure/ | HTTP, storage, providers. |
16
+ | Presentation | presentation/ | Screens/components. No business logic, no I/O. |
17
+ -->
18
+ {{frontend_layers}}
19
+
20
+ - Route entry files stay thin — they delegate to a screen/component.
21
+ - Presentation never calls the network or storage directly; it goes through
22
+ the application layer.
23
+
24
+ ## Rendering & component boundaries
25
+
26
+ - Respect the framework's server/client boundary rules.
27
+ - Keep side effects and data fetching at the correct layer, not inside
28
+ presentational components.
29
+
30
+ ## State & data
31
+
32
+ - Data access goes through typed clients/repositories, not ad-hoc fetches
33
+ scattered in components.
34
+ - Follow the repo's BFF / API-access rule (browser calls same-origin routes
35
+ when the project uses a BFF).
36
+
37
+ ## i18n & UI
38
+
39
+ - User-visible copy goes through the i18n layer; keep keys and types in sync.
40
+ - Use the project's design system components and tokens — no hardcoded colors
41
+ when a token exists.
42
+
43
+ ## Formatting, linting, docs
44
+
45
+ - TSDoc on exported functions and components (no ticket text).
46
+ - Lint / type-check command: `{{lint_commands}}`
@@ -0,0 +1,46 @@
1
+ # Spec Workflow — {{project_name}}
2
+
3
+ The process law of the project — see [`../../LAWS.md`](../../LAWS.md). This
4
+ repo is spec-driven through speclaw's **spec** module (no external CLI; the
5
+ mechanical steps are speclaw MCP tools).
6
+
7
+ ## The loop
8
+
9
+ No non-trivial change lands without a spec change:
10
+
11
+ 1. **explore** — think an idea through before committing (writes nothing).
12
+ 2. **draft** — create `spec/changes/<name>/`: `proposal.md`, delta specs under
13
+ `specs/<capability>/spec.md`, optional `design.md`, and `tasks.md`.
14
+ 3. **build** — implement the tasks in order, keeping code and spec in
15
+ agreement.
16
+ 4. **sync** — promote the change's delta specs into the canonical
17
+ `spec/specs/` (`spec_sync`).
18
+ 5. **archive** — finalize: sync + move the change to `spec/changes/archive/`
19
+ (`spec_archive`), **within the same PR** — never a post-merge chore.
20
+
21
+ ## Mandatory task steps
22
+
23
+ `tasks.md` MUST include the steps defined in `spec/config.yaml` and the
24
+ `spec-tasks-mandatory-steps` rule: feature branch first, tests reviewed and
25
+ run, manual verification executed by the agent, docs updated, archive within
26
+ the PR. The agent performs the manual testing itself — never delegates it.
27
+
28
+ ## Delta specs
29
+
30
+ - Normative requirements use SHALL/MUST.
31
+ - Requirement headers use `### Requirement:`.
32
+ - Scenario headers use exactly `#### Scenario:`.
33
+ - Acceptance criteria are testable without production integrations.
34
+ - The implemented code must match what the delta spec promises. Validate with
35
+ the `spec_validate` tool before syncing or archiving.
36
+
37
+ ## Archiving discipline
38
+
39
+ Always archive with the `archive` command / `spec_archive` tool, never a manual
40
+ `mv` — the tool performs the spec promotion and validation a manual move skips.
41
+
42
+ ## Amendments to the law
43
+
44
+ The standards in `docs/standards/` are amended like code: through a spec change
45
+ reviewed by a human. An agent may propose an amendment; it may never silently
46
+ ignore a standard.
@@ -0,0 +1,34 @@
1
+ # Testing & Quality Gates — {{project_name}}
2
+
3
+ The quality law of the project — see [`../../LAWS.md`](../../LAWS.md). These
4
+ gates are non-negotiable: an agent runs them itself and reports real output
5
+ before declaring anything done.
6
+
7
+ ## Gates
8
+
9
+ - **Tests**: `{{test_commands}}`
10
+ - **Lint / type-check**: `{{lint_commands}}`
11
+
12
+ A red gate blocks the task. Fix it or report it — never work around it by
13
+ suppressing a linter or deleting a test.
14
+
15
+ ## What must be tested
16
+
17
+ - Every new behavior ships with tests covering the happy path and at least one
18
+ edge/error case.
19
+ - Bug fixes ship with a regression test that fails before the fix.
20
+ - Permission/authorization paths are tested when the change touches them.
21
+
22
+ ## Test hygiene
23
+
24
+ - Unit tests are deterministic and isolated — mock external systems via the
25
+ project's fixtures; never depend on a live database or network.
26
+ - A test "fixed" by weakening its assertion is not fixed.
27
+
28
+ ## Manual & end-to-end verification
29
+
30
+ - When a change affects runtime behavior and it's feasible, verify it works by
31
+ exercising the endpoint/UI — don't assume green CI covers everything.
32
+ - The mandatory spec task steps
33
+ ([`spec-workflow.md`](spec-workflow.md)) define which manual checks
34
+ the agent must execute itself.
@@ -0,0 +1,107 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { AGENTS, detectConfiguredAgents } from "../../shared/agents.js";
4
+ /**
5
+ * Run the speclaw installation health checks against a project: ai-specs and
6
+ * LAWS.md presence, agent contracts, the docs/standards set, per-agent IDE
7
+ * symlink health, the spec/ workflow, the Compass index, and .mcp.json wiring.
8
+ *
9
+ * @param projectPath - Absolute path to the project root.
10
+ * @returns One {@link Check} per verified item, each carrying a remediation hint.
11
+ */
12
+ export function doctor(projectPath) {
13
+ const checks = [];
14
+ const has = (rel) => fs.existsSync(path.join(projectPath, rel));
15
+ checks.push({
16
+ name: "ai-specs directory",
17
+ ok: has("ai-specs"),
18
+ detail: has("ai-specs")
19
+ ? "present"
20
+ : "missing — run the scaffold tool first",
21
+ });
22
+ checks.push({
23
+ name: "LAWS.md constitution",
24
+ ok: has("LAWS.md"),
25
+ detail: has("LAWS.md") ? "present" : "missing — the project has no law",
26
+ });
27
+ for (const entry of ["CLAUDE.md", "AGENTS.md", "docs/compass.md"]) {
28
+ checks.push({
29
+ name: `${entry} agent contract`,
30
+ ok: has(entry),
31
+ detail: has(entry) ? "present" : "missing — scaffold writes it",
32
+ });
33
+ }
34
+ const standards = [
35
+ "base-standards",
36
+ "architecture",
37
+ "backend-standards",
38
+ "frontend-standards",
39
+ "testing-standards",
40
+ "documentation",
41
+ "conventions",
42
+ "spec-workflow",
43
+ ];
44
+ const missingStandards = standards.filter((s) => !has(path.join("docs/standards", `${s}.md`)));
45
+ checks.push({
46
+ name: "docs/standards/*",
47
+ ok: missingStandards.length === 0,
48
+ detail: missingStandards.length === 0
49
+ ? `all ${standards.length} standards present`
50
+ : `missing: ${missingStandards.join(", ")}`,
51
+ });
52
+ // Only check the agents the user actually configured — selection is opt-in.
53
+ const configured = AGENTS.filter((a) => detectConfiguredAgents(projectPath).includes(a.id));
54
+ if (configured.length === 0) {
55
+ checks.push({
56
+ name: "agents",
57
+ ok: false,
58
+ detail: "none configured — run `speclaw init` or `speclaw agent add <id>`",
59
+ });
60
+ }
61
+ for (const agent of configured) {
62
+ for (const target of agent.linkTargets) {
63
+ // only demand links for content that actually exists in ai-specs/
64
+ if (!has(path.join("ai-specs", target)))
65
+ continue;
66
+ const ideDir = agent.ideDir;
67
+ const linkPath = path.join(projectPath, ideDir, target);
68
+ let ok = false;
69
+ let detail = "missing";
70
+ try {
71
+ const stat = fs.lstatSync(linkPath);
72
+ if (stat.isSymbolicLink()) {
73
+ ok = fs.existsSync(linkPath); // broken symlink -> false
74
+ detail = ok ? `-> ${fs.readlinkSync(linkPath)}` : "broken symlink";
75
+ }
76
+ else {
77
+ ok = true;
78
+ detail = "real directory (not a symlink — consider migrating to ai-specs)";
79
+ }
80
+ }
81
+ catch {
82
+ // stays missing
83
+ }
84
+ checks.push({ name: `${ideDir}/${target}`, ok, detail });
85
+ }
86
+ }
87
+ checks.push({
88
+ name: "spec workflow",
89
+ ok: has("spec"),
90
+ detail: has("spec")
91
+ ? "spec/ present"
92
+ : "missing — run the `spec_init` tool",
93
+ });
94
+ checks.push({
95
+ name: "Compass index",
96
+ ok: has(".speclaw/index.db"),
97
+ detail: has(".speclaw/index.db")
98
+ ? ".speclaw/index.db present"
99
+ : "missing — run the `compass_index` tool",
100
+ });
101
+ checks.push({
102
+ name: ".mcp.json wiring",
103
+ ok: has(".mcp.json"),
104
+ detail: has(".mcp.json") ? "present" : "missing — scaffold writes it",
105
+ });
106
+ return checks;
107
+ }
@@ -0,0 +1,86 @@
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 { loadPacks } from "../tools/packs.js";
6
+ import { AGENTS, configureAgent } from "../../shared/agents.js";
7
+ import { emptyReport } from "../../shared/install.js";
8
+ const profileShape = {
9
+ project_name: z.string().describe("Short project name, e.g. the repo name"),
10
+ project_description: z.string().optional().describe("One-line description of what the project does"),
11
+ organization: z.string().optional().describe("Company/team name"),
12
+ stack_summary: z.string().optional().describe("e.g. 'Next.js 15 + TypeScript frontend, FastAPI + PostgreSQL backend'"),
13
+ architecture: z.string().optional().describe("e.g. 'hexagonal architecture with bounded contexts'"),
14
+ test_commands: z.string().optional().describe("Real commands, e.g. 'pytest backend/tests && npm run test'"),
15
+ lint_commands: z.string().optional().describe("Real commands, e.g. 'ruff check . && npm run lint && tsc --noEmit'"),
16
+ branch_pattern: z.string().optional().describe("e.g. 'feature/<ticket-id>-<slug>'"),
17
+ commit_style: z.string().optional().describe("e.g. 'conventional commits, imperative, English'"),
18
+ custom_laws: z.string().optional().describe("Extra markdown appended to LAWS.md — project-specific binding rules the analysis surfaced"),
19
+ ticket_prefix: z.string().optional().describe("Ticket prefix in the team's tracker, e.g. 'FAR'"),
20
+ team_language: z.string().optional().describe("The team's working language for posted communication (reviews, replies, tickets), e.g. 'Spanish'. Repo artifacts stay in the repo's own language. Defaults to English."),
21
+ compass_hints: z.string().optional().describe("Markdown bullets with the repo's real entrypoints and common traces, inserted into docs/compass.md"),
22
+ base_standards_extra: z.string().optional().describe("Markdown with any project-specific cross-cutting rules, appended to docs/standards/base-standards.md"),
23
+ modules_table: z.string().optional().describe("Markdown table of the repo's real modules/bounded contexts + one-line responsibility, for docs/standards/architecture.md"),
24
+ layering_rules: z.string().optional().describe("Markdown describing the layers and their allowed dependencies, for docs/standards/architecture.md"),
25
+ backend_layers: z.string().optional().describe("Markdown layer table (Layer | File | Responsibility) from the real backend, for docs/standards/backend-standards.md"),
26
+ frontend_layers: z.string().optional().describe("Markdown layer table from the real frontend, for docs/standards/frontend-standards.md"),
27
+ versioning_rules: z.string().optional().describe("The repo's versioning/release convention, for docs/standards/conventions.md"),
28
+ documentation_extra: z.string().optional().describe("Repo-specific docstring notes (keep only the languages used, the enforced linter), appended to docs/standards/documentation.md"),
29
+ };
30
+ // ─── The foundation module: analyze the repo, then write the constitution ───
31
+ /** Register the foundation MCP tools (init_project, scaffold, configure_agent, doctor). */
32
+ export function registerFoundation(server) {
33
+ server.registerTool("init_project", {
34
+ 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.",
35
+ inputSchema: { projectPath: z.string().describe("Absolute path to the project to initialize") },
36
+ }, async () => {
37
+ const packs = loadPacks();
38
+ return text({
39
+ instructions: [
40
+ "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).",
41
+ "2. The foundation is a set of GRANULAR standards under docs/standards/ (base, architecture, backend, frontend, testing, conventions, spec-workflow), 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.",
42
+ "3. Suggest packs: add stack packs whose 'detect' hints match dependencies you found; offer the rest. Ask the user which packs to install (the spec workflow is always installed).",
43
+ "4. If the 'workflow' pack is selected, ask the user for their tracker's ticket prefix and team working language.",
44
+ "5. Draft any custom_laws (extra binding rules for LAWS.md) from conventions you observed that the standard set doesn't cover.",
45
+ "6. Call the 'scaffold' tool with { projectPath, profile, packs }.",
46
+ "7. Follow the nextSteps returned by scaffold: complete the HTML-comment sections still left in docs/standards/*, then run the spec_init and compass_index tools (both built into speclaw — no external installs).",
47
+ ],
48
+ profileFields: Object.fromEntries(Object.entries(profileShape).map(([key, schema]) => [key, schema.description ?? ""])),
49
+ packs,
50
+ });
51
+ });
52
+ server.registerTool("scaffold", {
53
+ description: "Write the speclaw setup into a project: the foundation (LAWS.md constitution + granular docs/standards/* + CLAUDE.md + AGENTS.md + docs/compass.md), the spec 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.",
54
+ inputSchema: {
55
+ projectPath: z.string().describe("Absolute path to the project"),
56
+ profile: z.object(profileShape).describe("Project profile gathered by analyzing the repo"),
57
+ packs: z.array(z.string()).describe("Optional tool pack names (quality, workflow, agents)"),
58
+ agents: z.array(z.string()).optional().describe(`Agent ids to configure (symlinks + MCP): ${AGENTS.map((a) => a.id).join(", ")}. Usually the CLI handles this; omit to write content only.`),
59
+ },
60
+ }, async ({ projectPath, profile, packs, agents }) => text(scaffold(projectPath, profile, packs, agents ?? [])));
61
+ server.registerTool("configure_agent", {
62
+ 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.",
63
+ inputSchema: {
64
+ projectPath: z.string().describe("Absolute path to the project"),
65
+ agent: z.enum(AGENTS.map((a) => a.id)).describe("Agent id to configure"),
66
+ },
67
+ }, async ({ projectPath, agent }) => {
68
+ const report = emptyReport();
69
+ configureAgent(projectPath, agent, report);
70
+ return text(report);
71
+ });
72
+ server.registerTool("doctor", {
73
+ description: "Verify a speclaw installation: ai-specs presence, the foundation (LAWS.md + standards + agent contracts), IDE symlinks health, the spec/ workflow, the Compass index, and .mcp.json wiring. Returns a checklist with remediation hints.",
74
+ inputSchema: { projectPath: z.string().describe("Absolute path to the project") },
75
+ }, async ({ projectPath }) => {
76
+ const checks = doctor(projectPath);
77
+ const failed = checks.filter((c) => !c.ok);
78
+ return text({
79
+ healthy: failed.length === 0,
80
+ checks,
81
+ summary: failed.length === 0
82
+ ? "Everything is within the law."
83
+ : `${failed.length} check(s) failed — see details.`,
84
+ });
85
+ });
86
+ }
@@ -0,0 +1,103 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { assetsDir } from "../../shared/paths.js";
4
+ import { render } from "../../shared/render.js";
5
+ import { emptyReport, ensureGitignore } from "../../shared/install.js";
6
+ import { configureAgent } from "../../shared/agents.js";
7
+ import { installWorkflow } from "../spec/register.js";
8
+ import { installPack, loadPacks } from "../tools/packs.js";
9
+ const ASSETS = assetsDir(import.meta.url);
10
+ // Every {{var}} the foundation templates may reference. Ones the agent didn't
11
+ // provide default to empty so a bare `scaffold` never leaves a raw {{tag}}.
12
+ const FOUNDATION_DEFAULTS = {
13
+ custom_laws: "",
14
+ compass_hints: "",
15
+ base_standards_extra: "",
16
+ modules_table: "",
17
+ layering_rules: "",
18
+ backend_layers: "",
19
+ frontend_layers: "",
20
+ versioning_rules: "",
21
+ documentation_extra: "",
22
+ team_language: "English",
23
+ };
24
+ /**
25
+ * Render the foundation: walk the module's assets/, mirror its structure into
26
+ * the project, stripping the `.template` marker (foo.template.md -> foo.md).
27
+ * Directory-driven — adding a standard is dropping a file, no code change.
28
+ *
29
+ * @param projectPath - Absolute path to the project root.
30
+ * @param vars - Template variables substituted into each `.template.md`.
31
+ * @param report - Mutated in place with written/skipped paths and any
32
+ * unresolved template variables. Existing destination files are skipped.
33
+ */
34
+ function renderFoundation(projectPath, vars, report) {
35
+ const walk = (relDir) => {
36
+ for (const entry of fs.readdirSync(path.join(ASSETS, relDir), { withFileTypes: true })) {
37
+ const rel = path.join(relDir, entry.name);
38
+ if (entry.isDirectory()) {
39
+ walk(rel);
40
+ continue;
41
+ }
42
+ if (!entry.name.endsWith(".template.md"))
43
+ continue;
44
+ const destPath = path.join(projectPath, rel.replace(/\.template\.md$/, ".md"));
45
+ if (fs.existsSync(destPath)) {
46
+ report.skipped.push(destPath);
47
+ continue;
48
+ }
49
+ const { output, unresolved } = render(fs.readFileSync(path.join(ASSETS, rel), "utf8"), vars);
50
+ unresolved.forEach((v) => {
51
+ if (!report.unresolvedVars.includes(v))
52
+ report.unresolvedVars.push(v);
53
+ });
54
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
55
+ fs.writeFileSync(destPath, output);
56
+ report.written.push(destPath);
57
+ }
58
+ };
59
+ walk(".");
60
+ }
61
+ /**
62
+ * Write the speclaw content into a project: the foundation (constitution +
63
+ * standards), the spec workflow (always), the selected tool packs, .gitignore,
64
+ * and — for each agent id passed — that agent's symlinks + MCP config. Agent
65
+ * selection is what the CLI drives interactively; omit it to write content only.
66
+ * Never overwrites existing files.
67
+ *
68
+ * @param projectPath - Absolute path to an existing project root.
69
+ * @param profile - Project identity and conventions for template rendering.
70
+ * @param packNames - Tool pack names to install (the spec workflow is always installed).
71
+ * @param agents - Agent ids to configure with symlinks + MCP; empty writes content only.
72
+ * @returns The install report augmented with the ordered next steps to run.
73
+ * @throws If `projectPath` does not exist, or any pack name is unknown.
74
+ */
75
+ export function scaffold(projectPath, profile, packNames, agents = []) {
76
+ if (!fs.existsSync(projectPath)) {
77
+ throw new Error(`projectPath does not exist: ${projectPath}`);
78
+ }
79
+ const packs = loadPacks();
80
+ const unknown = packNames.filter((n) => !packs[n]);
81
+ if (unknown.length)
82
+ throw new Error(`Unknown packs: ${unknown.join(", ")}`);
83
+ const report = { ...emptyReport(), nextSteps: [] };
84
+ const vars = { ...FOUNDATION_DEFAULTS, ...profile };
85
+ renderFoundation(projectPath, vars, report);
86
+ installWorkflow(projectPath, vars, report); // spec module — always
87
+ for (const name of packNames)
88
+ installPack(projectPath, name, vars, report); // tools module
89
+ ensureGitignore(projectPath, ".speclaw/", "speclaw local code Compass (never commit)", report);
90
+ for (const id of agents)
91
+ configureAgent(projectPath, id, report); // only the chosen agents
92
+ report.nextSteps = [
93
+ "Run the `spec_init` tool to set up the spec-driven workflow (creates spec/). No external CLI needed — it's built into speclaw.",
94
+ "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.",
95
+ "Analyze the repo's real entrypoints and core flows, then fill in the 'Project-specific starting points' section of docs/compass.md (or pass compass_hints in the profile) so agents know where to start querying.",
96
+ "Fill in the per-standard sections left as HTML comments in docs/standards/* (architecture module table, backend/frontend layer tables, versioning) by analyzing the real repo. These are the granular laws CLAUDE.md, AGENTS.md and the dev agents reference.",
97
+ "Read the generated LAWS.md, CLAUDE.md, AGENTS.md and docs/standards/* with the user and refine any standard that does not match how the team actually works.",
98
+ report.unresolvedVars.length
99
+ ? `Fill in the unresolved template variables (${report.unresolvedVars.join(", ")}) by editing the affected files or re-running scaffold with a more complete profile.`
100
+ : "",
101
+ ].filter(Boolean);
102
+ return report;
103
+ }
@@ -0,0 +1,10 @@
1
+ ---
2
+ description: Finalize a completed change — sync specs into canonical, then archive it.
3
+ ---
4
+
5
+ Archive the completed change: $ARGUMENTS
6
+
7
+ Follow the `archive` skill: confirm every task is done and gates are green,
8
+ run `spec_validate`, then `spec_archive` with today's date (YYYY-MM-DD). It
9
+ syncs the specs and moves the change to `spec/changes/archive/`. Never move
10
+ the folder by hand.