@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,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.
|