sdd-mcp-server 5.3.0 → 5.5.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/CHANGELOG.md +49 -0
- package/README.md +85 -13
- package/agents/security-auditor.md +2 -2
- package/dist/__tests__/helpers/message-clarity.d.ts +15 -0
- package/dist/__tests__/helpers/message-clarity.js +26 -0
- package/dist/__tests__/helpers/message-clarity.js.map +1 -0
- package/dist/__tests__/helpers/message-extractor.d.ts +22 -0
- package/dist/__tests__/helpers/message-extractor.js +135 -0
- package/dist/__tests__/helpers/message-extractor.js.map +1 -0
- package/dist/__tests__/unit/jev/helpers.d.ts +84 -0
- package/dist/__tests__/unit/jev/helpers.js +188 -0
- package/dist/__tests__/unit/jev/helpers.js.map +1 -0
- package/dist/adapters/cli/SDDToolAdapter.d.ts +5 -1
- package/dist/adapters/cli/SDDToolAdapter.js +22 -3
- package/dist/adapters/cli/SDDToolAdapter.js.map +1 -1
- package/dist/application/services/JevAdvisoryService.d.ts +53 -0
- package/dist/application/services/JevAdvisoryService.js +310 -0
- package/dist/application/services/JevAdvisoryService.js.map +1 -0
- package/dist/application/services/JevBaseRef.d.ts +21 -0
- package/dist/application/services/JevBaseRef.js +46 -0
- package/dist/application/services/JevBaseRef.js.map +1 -0
- package/dist/application/services/JevChangeManifest.d.ts +33 -0
- package/dist/application/services/JevChangeManifest.js +208 -0
- package/dist/application/services/JevChangeManifest.js.map +1 -0
- package/dist/application/services/JevCheckTypes.d.ts +74 -0
- package/dist/application/services/JevCheckTypes.js +2 -0
- package/dist/application/services/JevCheckTypes.js.map +1 -0
- package/dist/application/services/JevCodeExcerpts.d.ts +23 -0
- package/dist/application/services/JevCodeExcerpts.js +102 -0
- package/dist/application/services/JevCodeExcerpts.js.map +1 -0
- package/dist/application/services/JevCommitLookup.d.ts +17 -0
- package/dist/application/services/JevCommitLookup.js +72 -0
- package/dist/application/services/JevCommitLookup.js.map +1 -0
- package/dist/application/services/JevExchange.d.ts +48 -0
- package/dist/application/services/JevExchange.js +119 -0
- package/dist/application/services/JevExchange.js.map +1 -0
- package/dist/application/services/JevQuestionCatalog.d.ts +28 -0
- package/dist/application/services/JevQuestionCatalog.js +194 -0
- package/dist/application/services/JevQuestionCatalog.js.map +1 -0
- package/dist/application/services/JevRedaction.d.ts +12 -0
- package/dist/application/services/JevRedaction.js +56 -0
- package/dist/application/services/JevRedaction.js.map +1 -0
- package/dist/application/services/JevReport.d.ts +41 -0
- package/dist/application/services/JevReport.js +101 -0
- package/dist/application/services/JevReport.js.map +1 -0
- package/dist/application/services/JevResponseValidator.d.ts +34 -0
- package/dist/application/services/JevResponseValidator.js +98 -0
- package/dist/application/services/JevResponseValidator.js.map +1 -0
- package/dist/application/services/JevResults.d.ts +3 -0
- package/dist/application/services/JevResults.js +7 -0
- package/dist/application/services/JevResults.js.map +1 -0
- package/dist/application/services/JevReviewCheck.d.ts +20 -0
- package/dist/application/services/JevReviewCheck.js +120 -0
- package/dist/application/services/JevReviewCheck.js.map +1 -0
- package/dist/application/services/JevSettings.d.ts +18 -0
- package/dist/application/services/JevSettings.js +72 -0
- package/dist/application/services/JevSettings.js.map +1 -0
- package/dist/application/services/JevSidecarStore.d.ts +68 -0
- package/dist/application/services/JevSidecarStore.js +201 -0
- package/dist/application/services/JevSidecarStore.js.map +1 -0
- package/dist/application/services/JevSpecState.d.ts +38 -0
- package/dist/application/services/JevSpecState.js +109 -0
- package/dist/application/services/JevSpecState.js.map +1 -0
- package/dist/application/services/JevTestcaseState.d.ts +22 -0
- package/dist/application/services/JevTestcaseState.js +85 -0
- package/dist/application/services/JevTestcaseState.js.map +1 -0
- package/dist/application/services/RedEvidenceClassifier.d.ts +10 -0
- package/dist/application/services/RedEvidenceClassifier.js +32 -0
- package/dist/application/services/RedEvidenceClassifier.js.map +1 -0
- package/dist/application/services/WorkflowEngineService.d.ts +28 -0
- package/dist/application/services/WorkflowEngineService.js +111 -63
- package/dist/application/services/WorkflowEngineService.js.map +1 -1
- package/dist/application/services/WorkflowValidationService.d.ts +16 -0
- package/dist/application/services/WorkflowValidationService.js +39 -34
- package/dist/application/services/WorkflowValidationService.js.map +1 -1
- package/dist/cli/install-skills.js +0 -0
- package/dist/cli/install-target.d.ts +8 -6
- package/dist/cli/install-target.js +11 -3
- package/dist/cli/install-target.js.map +1 -1
- package/dist/cli/tool-support/root-guidance.js +1 -0
- package/dist/cli/tool-support/root-guidance.js.map +1 -1
- package/dist/cli/tool-support/target-installer.d.ts +1 -0
- package/dist/cli/tool-support/target-installer.js +9 -5
- package/dist/cli/tool-support/target-installer.js.map +1 -1
- package/dist/domain/ports.d.ts +34 -0
- package/dist/domain/ports.js +11 -1
- package/dist/domain/ports.js.map +1 -1
- package/dist/infrastructure/adapters/FetchJevHttpAdapter.d.ts +4 -0
- package/dist/infrastructure/adapters/FetchJevHttpAdapter.js +73 -0
- package/dist/infrastructure/adapters/FetchJevHttpAdapter.js.map +1 -0
- package/dist/infrastructure/adapters/NodeGitAdapter.d.ts +5 -0
- package/dist/infrastructure/adapters/NodeGitAdapter.js +31 -0
- package/dist/infrastructure/adapters/NodeGitAdapter.js.map +1 -0
- package/dist/infrastructure/di/container.js +10 -0
- package/dist/infrastructure/di/container.js.map +1 -1
- package/dist/infrastructure/di/types.d.ts +4 -0
- package/dist/infrastructure/di/types.js +4 -0
- package/dist/infrastructure/di/types.js.map +1 -1
- package/dist/infrastructure/mcp/sddToolDefinitions.d.ts +1 -1
- package/dist/infrastructure/mcp/sddToolDefinitions.js +72 -1
- package/dist/infrastructure/mcp/sddToolDefinitions.js.map +1 -1
- package/dist/shared/version.d.ts +1 -1
- package/dist/shared/version.js +1 -1
- package/package.json +1 -1
- package/skills/output-clarity-ladder/SKILL.md +43 -0
- package/skills/output-clarity-ladder/references/ja.md +30 -0
- package/skills/output-clarity-ladder/references/zh-TW.md +30 -0
- package/skills/sdd-commit/SKILL.md +4 -3
- package/skills/sdd-design/SKILL.md +1 -1
- package/skills/sdd-implement/SKILL.md +4 -4
- package/skills/sdd-requirements/SKILL.md +6 -6
- package/skills/sdd-review/SKILL.md +3 -2
- package/skills/sdd-security-check/SKILL.md +3 -2
- package/skills/sdd-tasks/REFERENCE.md +4 -0
- package/skills/sdd-tasks/SKILL.md +8 -6
- package/templates/CLAUDE.md +1 -1
- package/templates/codex-AGENTS.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,55 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [5.5.0] - 2026-10-20
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Add opt-in, advisory JEV checks that call the TypeSafe Jev decision API. They judge requirements testability, design-to-requirement fidelity, planned test cases, RED validity, security finding triage, and coding-principle adherence. JEV is off by default. It needs `SDD_JEV_ENABLED` (or `.spec/sdd-config.json`) and `SDD_JEV_API_KEY`. Code excerpts need the separate `SDD_JEV_ALLOW_CODE`.
|
|
14
|
+
- Add the `sdd-jev-check` MCP tool, the 17th tool. It never changes `spec.json`, approval, validation, or the next action. Results are saved as sidecar files bound to the artifact revision and SHA-256.
|
|
15
|
+
- Add a local RED classifier. It flags setup failures such as `Cannot find module` and `TS2304` without any request.
|
|
16
|
+
- Redact secrets from every JEV request, bound excerpts to 15 lines around a finding and 8 KB per request, and never send `.env*` files, keys, lockfiles, or generated output.
|
|
17
|
+
- Add an optional `baseRef` input to the `security` and `principles` checks of `sdd-jev-check`. A PR or branch review can now judge already-committed changes: the change set is everything that differs between the merge base of `baseRef` and `HEAD`, and the working tree. `baseRef` is validated before any Git command runs and resolved with read-only Git (`--end-of-options`, Git 2.30 or later). A bad ref gives a `skipped` result with the reason `invalid-base-ref`. Without `baseRef`, behavior is unchanged.
|
|
18
|
+
- Committed-range results are saved as `jev-review-<kind>-range-<hash>.json`, bound to the merge-base commit, the `HEAD` commit, and the diff hash. `sdd-commit` still matches staged diffs only.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- The tool contract grows from 16 to 17 tools. The 16 existing tools keep their inputs and outputs.
|
|
22
|
+
- Seven Skills (`sdd-requirements`, `sdd-design`, `sdd-tasks`, `sdd-implement`, `sdd-review`, `sdd-security-check`, `sdd-commit`) show at most one JEV line. `sdd-commit` only reads a saved sidecar.
|
|
23
|
+
- `sdd-security-check` and the `security-auditor` agent now forbid contact with the audited target or its systems. The agent still has no network access. Only the parent may make the opt-in JEV triage call.
|
|
24
|
+
- The installer git-ignores code-review sidecars (`.spec/reviews/` and `jev-review-*`).
|
|
25
|
+
- `.claude-plugin/plugin.json` now lists `sdd-review-test-cases` and `sdd-jev-check`.
|
|
26
|
+
- JEV score answers are zero-based only: an integer from 0 to the index of the last level. Any other value is `invalid-response`. Sidecars no longer record a score base. The question-set version is now 3, so JEV ignores sidecars saved by earlier versions.
|
|
27
|
+
- `sdd-review` and `sdd-security-check` pass the PR or branch base as `baseRef` when they run the JEV code checks.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
- `sdd-status`, `sdd-context-load`, and other reads of a missing feature no longer create an empty `.spec/specs/<feature>/` folder. They report `Feature metadata not found` as before.
|
|
31
|
+
- `sdd-init` now accepts an empty feature folder left behind by earlier versions. It still rejects a folder that contains files with `Feature already exists`.
|
|
32
|
+
|
|
33
|
+
- `sdd-jev-check` no longer reports `unavailable (invalid-response)` against the real TypeSafe Jev API (model `jev-1.13.0`). The response validator now accepts the real answer shape: `type`, `confidence`, `legend`, `probabilities`, and unknown extra fields. A `type` that differs from the question type, a `legend` with a different number of levels, a `choice` outside the question labels, and a `noul` outside 0 to 1 are still `invalid-response`.
|
|
34
|
+
- A score level is now the highest-probability entry of `probabilities` (ties go to the lowest index) when that object is valid, and `round(score)` otherwise. The level must stay within 0 to the last level index. The raw fractional score is kept, and sidecars accept it. The question-set version stays 3 and the sidecar schema stays 1, because the questions and the stored fields do not change.
|
|
35
|
+
|
|
36
|
+
### Documentation
|
|
37
|
+
- Add the Optional JEV advisory section to `docs/WORKFLOW.md`. Update the tool count in the README and `ARCHITECTURE.md`.
|
|
38
|
+
|
|
39
|
+
## [5.4.0] - 2026-10-04
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
- Add the `output-clarity-ladder` skill. It applies to explanation, summary, and teaching replies. It writes about 80% of the way to ASD-STE100. It moves to a diagram, HTML page, or explainer video only when that format makes the same facts easier to understand.
|
|
43
|
+
- Ship Japanese (`references/ja.md`) and Traditional Chinese (`references/zh-TW.md`) guidance with the skill. Other languages use the English rules.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
- `output-clarity-ladder` is the only model-invocable skill. Claude Code and OMP render it without `disable-model-invocation`. Codex renders `allow_implicit_invocation: true`. All workflow skills stay manual-only.
|
|
47
|
+
- Root guidance and the `CLAUDE.md` and `AGENTS.md` templates name the skill in one sentence.
|
|
48
|
+
- Validation blockers and recoverable runtime errors state the fact, then the next action. Each message has at most 2 sentences and at most 25 words per sentence. Error codes, blocker references, and numeric limits do not change.
|
|
49
|
+
|
|
50
|
+
### Build
|
|
51
|
+
- Add GitHub Actions CI for pull requests and pushes to `develop`.
|
|
52
|
+
- Automate releases. A `develop` to `master` pull request commits the next version, chosen from Conventional Commits or a `release:*` label. The merge publishes to npm with trusted publishing and creates the tag and GitHub Release. See `docs/RELEASING.md`.
|
|
53
|
+
|
|
54
|
+
### Documentation
|
|
55
|
+
- Rewrite the README, `AGENTS.md`, `ARCHITECTURE.md`, `DEPLOYMENT.md`, and the guides in `docs/` to follow the skill's writing rules. Headings, commands, links, and numbers stay the same.
|
|
56
|
+
|
|
8
57
|
## [5.3.0] - 2026-10-02
|
|
9
58
|
|
|
10
59
|
### Changed
|
package/README.md
CHANGED
|
@@ -5,16 +5,74 @@
|
|
|
5
5
|
|
|
6
6
|
A Model Context Protocol server and target-native installer for governed Spec-Driven Development (SDD) in Claude Code, Codex, and Oh My Pi (OMP).
|
|
7
7
|
|
|
8
|
-
> **v5.
|
|
8
|
+
> **v5.4.0** — The new `output-clarity-ladder` skill makes explanation, summary, and teaching replies easier to check. It is the only skill the model applies without a command, and it ships English, Japanese, and Traditional Chinese guidance. Runtime blockers and errors now state the next action. Releases are automated: a merge to `master` publishes to npm with provenance. Claude Code requires 2.1.284+ for the pinned 5.5 defaults.
|
|
9
9
|
|
|
10
10
|
## Why sdd-mcp?
|
|
11
11
|
|
|
12
|
-
Skills own the requirements, design, task-planning, and TDD method
|
|
12
|
+
Skills own the requirements, design, task-planning, and TDD method. Skills also own the user conversation. The MCP runtime stays behind the Skill boundary. The runtime owns feature identity, canonical artifact writes, deterministic validation, revision-bound approvals, optional test review, implementation progress, and restart-safe context.
|
|
13
13
|
|
|
14
14
|
```text
|
|
15
15
|
User -> Skill -> MCP -> .spec
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
## Is sdd-mcp the right fit?
|
|
19
|
+
|
|
20
|
+
No workflow tool is best for every team. Each one makes a different trade-off between control, speed, and reach. Pick the option that matches your situation.
|
|
21
|
+
|
|
22
|
+
### Which option fits
|
|
23
|
+
|
|
24
|
+
Answer these questions in order. Each path ends at one option.
|
|
25
|
+
|
|
26
|
+
```mermaid
|
|
27
|
+
flowchart TD
|
|
28
|
+
A[New piece of work] --> B{Small, clear change?}
|
|
29
|
+
B -->|Yes| S[sdd-mcp simple-task<br/>or any light tool]
|
|
30
|
+
B -->|No| C{Host is Claude Code,<br/>Codex, or OMP?}
|
|
31
|
+
C -->|No| D{Want the agent to apply<br/>a method automatically?}
|
|
32
|
+
D -->|Yes| SP[Superpowers]
|
|
33
|
+
D -->|No| OS[OpenSpec]
|
|
34
|
+
C -->|Yes| E{Does a missed approval<br/>cost more than extra steps?}
|
|
35
|
+
E -->|Yes| SDD[sdd-mcp formal workflow]
|
|
36
|
+
E -->|No| F{Want free iteration on<br/>any artifact at any time?}
|
|
37
|
+
F -->|Yes| OS
|
|
38
|
+
F -->|No| SP
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
On the sdd-mcp path, you also get these properties:
|
|
42
|
+
|
|
43
|
+
- The runtime binds each approval to an exact revision and artifact hash.
|
|
44
|
+
- A later phase Skill stops when the earlier phase is not approved.
|
|
45
|
+
- Durable state in `.spec/specs/<feature>/spec.json` lets any session or person resume the same feature.
|
|
46
|
+
- Validation checks that EARS requirements, design decisions, and tasks trace to each other.
|
|
47
|
+
|
|
48
|
+
sdd-mcp does not support other hosts, for example Cursor or GitHub Copilot. Superpowers and OpenSpec support many more hosts.
|
|
49
|
+
|
|
50
|
+
### Comparison with Superpowers and OpenSpec
|
|
51
|
+
|
|
52
|
+
This table uses each project's own README as of October 2026. Check the linked projects for current details.
|
|
53
|
+
|
|
54
|
+
| | sdd-mcp | [Superpowers](https://github.com/obra/superpowers) | [OpenSpec](https://github.com/Fission-AI/OpenSpec) |
|
|
55
|
+
|---|---|---|---|
|
|
56
|
+
| Core idea | Governed phases with a state runtime | A development method built from composable skills | Lightweight specs organized as changes |
|
|
57
|
+
| Hosts | Claude Code, Codex, OMP | 18+ coding agents | 30+ AI tools |
|
|
58
|
+
| How it starts | You invoke a phase Skill | Skills activate automatically before a task | You run slash commands such as `/opsx:propose` |
|
|
59
|
+
| Phase gates | Enforced by the MCP runtime | Approval asked in the conversation | None by design ("no rigid phase gates") |
|
|
60
|
+
| Approval record | Revision and hash in `spec.json` | Not described as a stored record | No approval record in the core workflow |
|
|
61
|
+
| Artifacts | `requirements.md`, `design.md`, `tasks.md` per feature | Design document and implementation plan | `proposal.md`, delta specs, `design.md`, `tasks.md` per change, then archive |
|
|
62
|
+
| Implementation method | Test-first tasks with recorded RED and GREEN evidence | TDD, git worktrees, subagent per task with review | `/opsx:apply` works through tasks |
|
|
63
|
+
| Extra moving parts | MCP server plus installed Skills | Plugin or skill install | CLI plus generated commands |
|
|
64
|
+
| License | MIT | MIT | MIT |
|
|
65
|
+
|
|
66
|
+
### Trade-offs
|
|
67
|
+
|
|
68
|
+
| Choice | What you gain | What you pay |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| sdd-mcp | Enforced gates, an auditable approval record, and resumable state | More ceremony, an MCP runtime to install, and only three hosts |
|
|
71
|
+
| Superpowers | A strong default method that the agent applies without commands, on many hosts | Gates live in the conversation, so nothing outside the agent enforces them |
|
|
72
|
+
| OpenSpec | Low ceremony, free iteration, change history through archive, and many hosts | Alignment depends on team discipline, because no tool blocks a skipped step |
|
|
73
|
+
|
|
74
|
+
A short rule: choose sdd-mcp when a missed approval costs more than the extra steps. Choose a lighter tool when speed of iteration matters more than proof of approval.
|
|
75
|
+
|
|
18
76
|
## One-time personal setup
|
|
19
77
|
|
|
20
78
|
Install the runtime and manual Skills for Claude Code, Codex, and OMP once per local user/profile, from any directory:
|
|
@@ -67,12 +125,12 @@ Use this path when the repository has never had sdd-mcp-generated guidance. The
|
|
|
67
125
|
|
|
68
126
|
```bash
|
|
69
127
|
# Recommended explicit lean installation
|
|
70
|
-
npx sdd-mcp-server@5.
|
|
71
|
-
npx sdd-mcp-server@5.
|
|
72
|
-
npx sdd-mcp-server@5.
|
|
128
|
+
npx sdd-mcp-server@5.5.0 install --profile lean --target claude-code
|
|
129
|
+
npx sdd-mcp-server@5.5.0 install --profile lean --target codex
|
|
130
|
+
npx sdd-mcp-server@5.5.0 install --profile lean --target omp
|
|
73
131
|
|
|
74
132
|
# Interactive full installation: choose Claude Code, Codex, or OMP
|
|
75
|
-
npx sdd-mcp-server@5.
|
|
133
|
+
npx sdd-mcp-server@5.5.0 install --profile full
|
|
76
134
|
```
|
|
77
135
|
|
|
78
136
|
Use the local entrypoint only when this checkout itself is the intended project install. It does not repair personal runtime connection. Build once, then install explicitly:
|
|
@@ -113,16 +171,19 @@ npx sdd-mcp-server install --profile full --target codex --model-roles models.ya
|
|
|
113
171
|
npx sdd-mcp-server install --target omp --model-roles models.yaml
|
|
114
172
|
```
|
|
115
173
|
|
|
116
|
-
The six SDD roles are `planner`, `architect`, `reviewer`, `security-auditor`, `implementer`, and `tdd-guide
|
|
174
|
+
The six SDD roles are `planner`, `architect`, `reviewer`, `security-auditor`, `implementer`, and `tdd-guide`. Omitted roles keep the package defaults. Claude Code routes installed skills and subagents by model and effort. Codex routes generated custom agents by model and reasoning effort. This cannot change the model of the inline parent. OMP routes its generated agents, not host-level `default`/`smol`/`slow`/`plan`/`task`/`advisor` roles. `--model-roles` reads project YAML. It does not change host configuration, and it does not validate model availability. See [Model Routing](docs/MODEL-ROUTING.md) for selector syntax and host-specific limits.
|
|
117
175
|
|
|
118
176
|
## Manual workflow invocation
|
|
119
177
|
|
|
120
|
-
SDD skills are explicit commands and do not activate implicitly from ordinary prose.
|
|
178
|
+
SDD workflow skills are explicit commands and do not activate implicitly from ordinary prose. The one exception is `output-clarity-ladder`, which the model applies on its own (see the last row).
|
|
121
179
|
|
|
122
180
|
| Path | Claude Code | Codex | Oh My Pi |
|
|
123
181
|
|---|---|---|---|
|
|
124
182
|
| Small task | `/simple-task` | `$simple-task` | `/skill:simple-task` |
|
|
125
183
|
| Formal SDD | `/sdd-requirements` → `/sdd-design` → `/sdd-tasks` → `/sdd-implement` | `$sdd-requirements` → `$sdd-design` → `$sdd-tasks` → `$sdd-implement` | `/skill:sdd-requirements` → `/skill:sdd-design` → `/skill:sdd-tasks` → `/skill:sdd-implement` |
|
|
184
|
+
| Clearer replies (automatic) | `/output-clarity-ladder` | `$output-clarity-ladder` | `/skill:output-clarity-ladder` |
|
|
185
|
+
|
|
186
|
+
`output-clarity-ladder` makes explanation, summary, and teaching replies easier to check. It writes about 80% of the way to ASD-STE100 and uses a diagram, HTML page, or video only when that format is easier to understand. It ships English rules plus Japanese (`ja`) and Traditional Chinese (`zh-TW`) guidance. It does not check an approved-word list, and it does not make output ASD-STE100 conformant.
|
|
126
187
|
|
|
127
188
|
Approvals and optional test-case review are explicit questions inside the relevant Skill flow. Status, context, validation, persistence, and progress recording happen internally.
|
|
128
189
|
|
|
@@ -139,7 +200,7 @@ Approvals and optional test-case review are explicit questions inside the releva
|
|
|
139
200
|
|
|
140
201
|
Claude Code and Codex lean profiles install skills, steering, and their supported hook guidance. OMP lean installs skills, steering, and agents. Full profiles add rules, contexts, and agents as supported by each host. OMP does not install Markdown as an executable hook; `--target omp --hooks` fails with an explanation.
|
|
141
202
|
|
|
142
|
-
**Project agents and execution mode.** Whether a skill can use project agents depends on the profile. With no agents installed (Claude Code/Codex `lean`) every skill runs inline and does not ask. With agents installed (`full`; OMP always), `sdd-review`, `sdd-security-check`, and any run with at least two independent implementation slices
|
|
203
|
+
**Project agents and execution mode.** Whether a skill can use project agents depends on the profile. With no agents installed (Claude Code/Codex `lean`), every skill runs inline and does not ask. With agents installed (`full`; OMP always), these skills ask once per session, inline or project agent: `sdd-review`, `sdd-security-check`, and any run with at least two independent implementation slices. Planning skills (requirements, design, tasks, steering) never ask. Every skill reports the mode, agents started, parallelism, configured model/effort, and fallbacks. To verify on each host, see [Model Routing](docs/MODEL-ROUTING.md#execution-mode-and-reporting). SDD agents are subagents, not the Claude Code `/advisor` tool.
|
|
143
204
|
|
|
144
205
|
See [Installation Guide](docs/INSTALL-GUIDE.md) and [Model Routing](docs/MODEL-ROUTING.md).
|
|
145
206
|
|
|
@@ -153,7 +214,7 @@ Use this path when the project already contains generated sdd-mcp files from an
|
|
|
153
214
|
|
|
154
215
|
```bash
|
|
155
216
|
# Replace <target> with claude-code, codex, or omp
|
|
156
|
-
npx sdd-mcp-server@5.
|
|
217
|
+
npx sdd-mcp-server@5.5.0 install \
|
|
157
218
|
--profile full \
|
|
158
219
|
--target <target> \
|
|
159
220
|
--refresh-generated
|
|
@@ -166,7 +227,7 @@ For an OMP migration, Codex TOML agents remain preserved but are not executable
|
|
|
166
227
|
After this one-time migration, use a normal install without `--refresh-generated` for subsequent v5 updates. Review conflicts, then reload/restart the host and accept project trust before invoking a native phase Skill.
|
|
167
228
|
|
|
168
229
|
## Integrator/runtime reference: canonical v5 inventory
|
|
169
|
-
Every packaged entrypoint exposes the same
|
|
230
|
+
Every packaged entrypoint exposes the same 17 tools:
|
|
170
231
|
|
|
171
232
|
1. `sdd-init`
|
|
172
233
|
2. `sdd-requirements`
|
|
@@ -184,12 +245,15 @@ Every packaged entrypoint exposes the same 16 tools:
|
|
|
184
245
|
14. `sdd-validate-design`
|
|
185
246
|
15. `sdd-validate-gap`
|
|
186
247
|
16. `sdd-spec-impl`
|
|
248
|
+
17. `sdd-jev-check`
|
|
249
|
+
|
|
250
|
+
`sdd-jev-check` is opt-in and advisory. It is off by default and never changes workflow state. For a PR or branch review, its code checks accept an optional `baseRef` to judge committed changes. See [Optional JEV advisory](docs/WORKFLOW.md#optional-jev-advisory).
|
|
187
251
|
|
|
188
252
|
Feature-scoped tools use `featureName`; v5 payloads bind phase mutations and approvals to exact revisions and artifact hashes. This inventory is for MCP integrators and runtime maintainers—not end-user workflow instructions. Hosts discover installed Skills, and the installer supports `--list`.
|
|
189
253
|
|
|
190
254
|
## Integrator/runtime reference: compact continuation
|
|
191
255
|
|
|
192
|
-
The runtime's context API defaults to bounded approved context and supports exact-response ETags. Phase Skills manage fingerprints and draft opt-in internally. Integrators that call the protocol directly must
|
|
256
|
+
The runtime's context API defaults to bounded approved context and supports exact-response ETags. Phase Skills manage fingerprints and draft opt-in internally. Integrators that call the protocol directly must follow three rules. Preserve the returned fingerprint for `ifNoneMatch`. Request unapproved source only explicitly in full mode. Treat `.spec/specs/<feature>/spec.json` as workflow authority. Compact, standard, and full default bounds are 2,048, 4,096, and 16,384 `estimatedTokens`. Full mode never silently truncates raw documents.
|
|
193
257
|
|
|
194
258
|
## Context and usage measurement
|
|
195
259
|
|
|
@@ -209,7 +273,13 @@ Three-run fresh-session A/B comparisons used comparable provider-reported median
|
|
|
209
273
|
|
|
210
274
|
## Routing summary
|
|
211
275
|
|
|
212
|
-
Claude executes a skill in the current turn with its configured model and effort
|
|
276
|
+
Claude executes a skill in the current turn with its configured model and effort. The defaults are Claude Opus 5.5/high for high-level work and Claude Sonnet 5.5/medium for implementation/TDD. Installed Claude subagents have matching metadata.
|
|
277
|
+
|
|
278
|
+
Codex runs inline by default. It may use one configured GPT-6 Sol/xhigh custom agent for review and security, after the user chooses it once per session. Its agent metadata does not change the parent turn.
|
|
279
|
+
|
|
280
|
+
OMP executes invoked SDD skills inline. The installed `.omp/extensions/sdd-skill-routing.js` switches the parent to the configured model/thinking level for the turn, then restores it afterward. The defaults are GPT-6 Sol/xhigh for high-level work and GPT-6 Luna/medium for implementation/TDD. OMP’s `.omp/agents` advisors remain explicit opt-in only. They allow one child, and they cannot nest or retry.
|
|
281
|
+
|
|
282
|
+
Implementation, TDD, and simple tasks remain inline unless genuinely independent parallel slices justify delegation.
|
|
213
283
|
|
|
214
284
|
See [docs/MODEL-ROUTING.md](docs/MODEL-ROUTING.md) for enforcement and fallback boundaries.
|
|
215
285
|
|
|
@@ -231,4 +301,6 @@ npm run build
|
|
|
231
301
|
npm test
|
|
232
302
|
```
|
|
233
303
|
|
|
304
|
+
Releases are automated. A merge from `develop` to `master` bumps the version, tags it, and publishes it to npm. See [docs/RELEASING.md](docs/RELEASING.md).
|
|
305
|
+
|
|
234
306
|
MIT licensed. See [CHANGELOG.md](CHANGELOG.md) for release history.
|
|
@@ -14,10 +14,10 @@ Audit only the handed-off scope. Treat security as a source-to-sink and trust-bo
|
|
|
14
14
|
- Identify actors, assets, entry points, privileges, data sensitivity, and trust transitions.
|
|
15
15
|
- Trace untrusted values through validation, authorization, queries, commands, templates, URLs, serialization, storage, errors, and logs.
|
|
16
16
|
- Check applicable access-control, cryptography, injection, authentication, integrity, dependency, configuration, SSRF, abuse, and resource-exhaustion risks.
|
|
17
|
-
- Redact any secret; identify only its type and location. Perform no destructive probe or
|
|
17
|
+
- Redact any secret; identify only its type and location. Perform no destructive probe, no contact with the audited target or its systems, and no network access of any kind.
|
|
18
18
|
- Report a finding only with preconditions, exploit path, impact, severity rationale, remediation, and regression test. Separate confirmed flaws from hardening.
|
|
19
19
|
|
|
20
|
-
This is a depth-one read-only assignment. Do not spawn or delegate. Unknown controls remain unresolved, not assumed absent.
|
|
20
|
+
This is a depth-one read-only assignment. Do not spawn or delegate. Unknown controls remain unresolved, not assumed absent. Only the parent may make the opt-in JEV triage call after this assignment returns.
|
|
21
21
|
|
|
22
22
|
## Result Contract
|
|
23
23
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ClarityBounds {
|
|
2
|
+
maxSentences: number;
|
|
3
|
+
maxWords: number;
|
|
4
|
+
}
|
|
5
|
+
export interface ClarityResult {
|
|
6
|
+
ok: boolean;
|
|
7
|
+
reason?: string;
|
|
8
|
+
sentence?: string;
|
|
9
|
+
wordCount?: number;
|
|
10
|
+
}
|
|
11
|
+
/** Split on . ? ! followed by whitespace or end of text; dotted identifiers stay whole. */
|
|
12
|
+
export declare function splitSentences(text: string): string[];
|
|
13
|
+
/** Words are whitespace-separated tokens; a placeholder such as X is one word. */
|
|
14
|
+
export declare function countWords(sentence: string): number;
|
|
15
|
+
export declare function checkClarity(text: string, bounds: ClarityBounds): ClarityResult;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Split on . ? ! followed by whitespace or end of text; dotted identifiers stay whole. */
|
|
2
|
+
export function splitSentences(text) {
|
|
3
|
+
const parts = text.trim().match(/[\s\S]*?[.?!](?=\s|$)|[\s\S]+$/g) ?? [];
|
|
4
|
+
return parts.map((p) => p.trim()).filter((p) => p.length > 0);
|
|
5
|
+
}
|
|
6
|
+
/** Words are whitespace-separated tokens; a placeholder such as X is one word. */
|
|
7
|
+
export function countWords(sentence) {
|
|
8
|
+
const trimmed = sentence.trim();
|
|
9
|
+
return trimmed === '' ? 0 : trimmed.split(/\s+/).length;
|
|
10
|
+
}
|
|
11
|
+
export function checkClarity(text, bounds) {
|
|
12
|
+
if (text.trim() === '')
|
|
13
|
+
return { ok: false, reason: 'Message is empty or whitespace-only' };
|
|
14
|
+
const sentences = splitSentences(text);
|
|
15
|
+
if (sentences.length > bounds.maxSentences) {
|
|
16
|
+
return { ok: false, reason: `Message has ${sentences.length} sentences; maximum is ${bounds.maxSentences}`, sentence: text };
|
|
17
|
+
}
|
|
18
|
+
for (const sentence of sentences) {
|
|
19
|
+
const wordCount = countWords(sentence);
|
|
20
|
+
if (wordCount > bounds.maxWords) {
|
|
21
|
+
return { ok: false, reason: `Sentence has ${wordCount} words; maximum is ${bounds.maxWords}`, sentence, wordCount };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return { ok: true };
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=message-clarity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-clarity.js","sourceRoot":"","sources":["../../../src/__tests__/helpers/message-clarity.ts"],"names":[],"mappings":"AAYA,2FAA2F;AAC3F,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,iCAAiC,CAAC,IAAI,EAAE,CAAC;IACzE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAChE,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;IAChC,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,MAAqB;IAC9D,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,qCAAqC,EAAE,CAAC;IAC5F,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,YAAY,EAAE,CAAC;QAC3C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,SAAS,CAAC,MAAM,0BAA0B,MAAM,CAAC,YAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/H,CAAC;IACD,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QACvC,IAAI,SAAS,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;YAChC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,SAAS,sBAAsB,MAAM,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QACtH,CAAC;IACH,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const MESSAGE_FILES: readonly ["src/application/services/WorkflowValidationService.ts", "src/application/services/WorkflowEngineService.ts", "src/adapters/cli/SDDToolAdapter.ts", "src/infrastructure/mcp/ToolRegistry.ts"];
|
|
2
|
+
export declare const DYNAMIC_CODE = "<dynamic>";
|
|
3
|
+
export interface ExtractedMessage {
|
|
4
|
+
file: string;
|
|
5
|
+
code: string | null;
|
|
6
|
+
message: string;
|
|
7
|
+
line: number;
|
|
8
|
+
literal: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface ContractSnapshot {
|
|
11
|
+
files: Record<string, {
|
|
12
|
+
codes: string[];
|
|
13
|
+
numbers: Record<string, string[]>;
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
16
|
+
export declare function extractMessagesFromSource(file: string, text: string): ExtractedMessage[];
|
|
17
|
+
export declare function extractMessages(file: string, root?: string): ExtractedMessage[];
|
|
18
|
+
export declare function loadAllMessages(root?: string): ExtractedMessage[];
|
|
19
|
+
export declare function numericTokens(message: string): string[];
|
|
20
|
+
export declare function buildContractSnapshot(messages: ExtractedMessage[]): ContractSnapshot;
|
|
21
|
+
/** Returns a list of violations; empty means current satisfies the baseline. */
|
|
22
|
+
export declare function compareToBaseline(current: ContractSnapshot, baseline: ContractSnapshot): string[];
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import ts from 'typescript';
|
|
4
|
+
export const MESSAGE_FILES = [
|
|
5
|
+
'src/application/services/WorkflowValidationService.ts',
|
|
6
|
+
'src/application/services/WorkflowEngineService.ts',
|
|
7
|
+
'src/adapters/cli/SDDToolAdapter.ts',
|
|
8
|
+
'src/infrastructure/mcp/ToolRegistry.ts',
|
|
9
|
+
];
|
|
10
|
+
export const DYNAMIC_CODE = '<dynamic>';
|
|
11
|
+
/** readRequired(path, message) throws this code with the caller's message. */
|
|
12
|
+
const READ_REQUIRED_CODE = 'StateInvariantViolation';
|
|
13
|
+
/** Returns folded literal text, or undefined when the expression is not a literal. */
|
|
14
|
+
function literalText(node) {
|
|
15
|
+
if (ts.isParenthesizedExpression(node))
|
|
16
|
+
return literalText(node.expression);
|
|
17
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
|
|
18
|
+
return node.text;
|
|
19
|
+
if (ts.isTemplateExpression(node)) {
|
|
20
|
+
return node.head.text + node.templateSpans.map((s) => `X${s.literal.text}`).join('');
|
|
21
|
+
}
|
|
22
|
+
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.PlusToken) {
|
|
23
|
+
const l = literalText(node.left);
|
|
24
|
+
const r = literalText(node.right);
|
|
25
|
+
return l !== undefined && r !== undefined ? l + r : undefined;
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
export function extractMessagesFromSource(file, text) {
|
|
30
|
+
let sf;
|
|
31
|
+
try {
|
|
32
|
+
sf = ts.createSourceFile(file, text, ts.ScriptTarget.ES2022, true, ts.ScriptKind.TS);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
throw new Error(`Cannot parse ${file}: ${error.message}`);
|
|
36
|
+
}
|
|
37
|
+
const parseDiagnostics = sf.parseDiagnostics ?? [];
|
|
38
|
+
if (parseDiagnostics.length > 0)
|
|
39
|
+
throw new Error(`Cannot parse ${file}: syntax errors`);
|
|
40
|
+
const out = [];
|
|
41
|
+
const visit = (node) => {
|
|
42
|
+
const isBlocker = ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'blocker';
|
|
43
|
+
const isGov = ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'GovernanceError';
|
|
44
|
+
const isReadRequired = ts.isCallExpression(node) &&
|
|
45
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
46
|
+
node.expression.name.text === 'readRequired';
|
|
47
|
+
if ((isBlocker || isGov || isReadRequired) && (node.arguments?.length ?? 0) >= 2) {
|
|
48
|
+
const [codeArg, msgArg] = node.arguments;
|
|
49
|
+
const code = isReadRequired ? READ_REQUIRED_CODE : literalText(codeArg);
|
|
50
|
+
const message = literalText(msgArg);
|
|
51
|
+
out.push({
|
|
52
|
+
file,
|
|
53
|
+
code: code === undefined ? null : code,
|
|
54
|
+
message: message ?? '',
|
|
55
|
+
line: sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1,
|
|
56
|
+
literal: message !== undefined,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
ts.forEachChild(node, visit);
|
|
60
|
+
};
|
|
61
|
+
visit(sf);
|
|
62
|
+
if (out.length === 0)
|
|
63
|
+
throw new Error(`No blocker or GovernanceError messages found in ${file}`);
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
export function extractMessages(file, root = process.cwd()) {
|
|
67
|
+
let text;
|
|
68
|
+
try {
|
|
69
|
+
text = readFileSync(resolve(root, file), 'utf8');
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
throw new Error(`Cannot read ${file}: ${error.message}`);
|
|
73
|
+
}
|
|
74
|
+
return extractMessagesFromSource(file, text);
|
|
75
|
+
}
|
|
76
|
+
export function loadAllMessages(root = process.cwd()) {
|
|
77
|
+
return MESSAGE_FILES.flatMap((file) => extractMessages(file, root));
|
|
78
|
+
}
|
|
79
|
+
export function numericTokens(message) {
|
|
80
|
+
return (message.match(/\d+(?:,\d{3})*(?:\.\d+)*/g) ?? []).map((t) => t.replace(/,/g, ''));
|
|
81
|
+
}
|
|
82
|
+
export function buildContractSnapshot(messages) {
|
|
83
|
+
const files = {};
|
|
84
|
+
for (const m of messages) {
|
|
85
|
+
const entry = (files[m.file] ??= { codes: [], numbers: {} });
|
|
86
|
+
const code = m.code ?? DYNAMIC_CODE;
|
|
87
|
+
if (!entry.codes.includes(code))
|
|
88
|
+
entry.codes.push(code);
|
|
89
|
+
(entry.numbers[code] ??= []).push(...numericTokens(m.message));
|
|
90
|
+
}
|
|
91
|
+
for (const file of Object.keys(files).sort()) {
|
|
92
|
+
const entry = files[file];
|
|
93
|
+
entry.codes.sort();
|
|
94
|
+
for (const code of Object.keys(entry.numbers))
|
|
95
|
+
entry.numbers[code].sort();
|
|
96
|
+
}
|
|
97
|
+
const sorted = {};
|
|
98
|
+
for (const file of Object.keys(files).sort()) {
|
|
99
|
+
const entry = files[file];
|
|
100
|
+
sorted[file] = {
|
|
101
|
+
codes: entry.codes,
|
|
102
|
+
numbers: Object.fromEntries(entry.codes.map((c) => [c, entry.numbers[c] ?? []])),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return { files: sorted };
|
|
106
|
+
}
|
|
107
|
+
/** Returns a list of violations; empty means current satisfies the baseline. */
|
|
108
|
+
export function compareToBaseline(current, baseline) {
|
|
109
|
+
const problems = [];
|
|
110
|
+
for (const file of Object.keys(baseline.files)) {
|
|
111
|
+
const base = baseline.files[file];
|
|
112
|
+
const cur = current.files[file];
|
|
113
|
+
if (!cur) {
|
|
114
|
+
problems.push(`${file}: file missing from current extraction`);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (JSON.stringify(cur.codes) !== JSON.stringify(base.codes)) {
|
|
118
|
+
const missing = base.codes.filter((c) => !cur.codes.includes(c));
|
|
119
|
+
const added = cur.codes.filter((c) => !base.codes.includes(c));
|
|
120
|
+
problems.push(`${file}: code set changed; missing [${missing}] added [${added}]`);
|
|
121
|
+
}
|
|
122
|
+
for (const code of base.codes) {
|
|
123
|
+
const remaining = [...(cur.numbers[code] ?? [])];
|
|
124
|
+
for (const token of base.numbers[code] ?? []) {
|
|
125
|
+
const i = remaining.indexOf(token);
|
|
126
|
+
if (i === -1)
|
|
127
|
+
problems.push(`${file}: code ${code} lost numeric token ${token}`);
|
|
128
|
+
else
|
|
129
|
+
remaining.splice(i, 1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return problems;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=message-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-extractor.js","sourceRoot":"","sources":["../../../src/__tests__/helpers/message-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { GitPort, GitResult, JevHttpPort, JevHttpRequest, JevHttpResponse, LoggerPort } from '../../../domain/ports';
|
|
2
|
+
import { NodeFileSystemAdapter } from '../../../infrastructure/adapters/NodeFileSystemAdapter';
|
|
3
|
+
import { WorkflowEngineService } from '../../../application/services/WorkflowEngineService';
|
|
4
|
+
export declare const TEST_KEY = "sk-test-abcdef123456";
|
|
5
|
+
export declare const FIXTURE_REQUIREMENTS: string;
|
|
6
|
+
export declare const FIXTURE_DESIGN: string;
|
|
7
|
+
export declare const FIXTURE_TASKS: string;
|
|
8
|
+
type Phase = 'requirements' | 'design' | 'tasks';
|
|
9
|
+
export interface EngineProject {
|
|
10
|
+
root: string;
|
|
11
|
+
feature: string;
|
|
12
|
+
engine: WorkflowEngineService;
|
|
13
|
+
fileSystem: NodeFileSystemAdapter;
|
|
14
|
+
submit(phase: Phase, content: string, revision?: number, hash?: string | null): ReturnType<WorkflowEngineService['submitPhaseArtifact']>;
|
|
15
|
+
approve(phase: Phase, result: {
|
|
16
|
+
revision: number;
|
|
17
|
+
artifact: {
|
|
18
|
+
sha256: string;
|
|
19
|
+
};
|
|
20
|
+
}): ReturnType<WorkflowEngineService['approve']>;
|
|
21
|
+
cleanup(): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
/** A real engine over a temporary project; the feature is initialized and ready for submissions. */
|
|
24
|
+
export declare function createEngineProject(feature?: string): Promise<EngineProject>;
|
|
25
|
+
type Answer = {
|
|
26
|
+
choice: string;
|
|
27
|
+
} | {
|
|
28
|
+
score: number;
|
|
29
|
+
} | {
|
|
30
|
+
noul: number;
|
|
31
|
+
};
|
|
32
|
+
/** A valid, unflagged answer for every posted question, with per-id overrides. */
|
|
33
|
+
export declare function benignAnswers(requestBody: string, overrides?: Record<string, Answer>): string;
|
|
34
|
+
export declare class FakeJevHttpPort implements JevHttpPort {
|
|
35
|
+
readonly calls: JevHttpRequest[];
|
|
36
|
+
respond: (request: JevHttpRequest) => Promise<JevHttpResponse>;
|
|
37
|
+
post(request: JevHttpRequest): Promise<JevHttpResponse>;
|
|
38
|
+
}
|
|
39
|
+
export declare class FakeGitPort implements GitPort {
|
|
40
|
+
readonly calls: string[][];
|
|
41
|
+
handler: (args: readonly string[]) => GitResult;
|
|
42
|
+
run(args: readonly string[]): Promise<GitResult>;
|
|
43
|
+
}
|
|
44
|
+
/** Real filesystem that records every call so tests can prove what was (not) touched. */
|
|
45
|
+
export declare class SpyFileSystem extends NodeFileSystemAdapter {
|
|
46
|
+
readonly calls: Array<{
|
|
47
|
+
method: string;
|
|
48
|
+
path: string;
|
|
49
|
+
}>;
|
|
50
|
+
throwOn?: {
|
|
51
|
+
method: string;
|
|
52
|
+
match: RegExp;
|
|
53
|
+
};
|
|
54
|
+
private record;
|
|
55
|
+
readFile(target: string): Promise<string>;
|
|
56
|
+
writeFile(target: string, content: string): Promise<void>;
|
|
57
|
+
writeFileAtomic(target: string, content: string): Promise<void>;
|
|
58
|
+
exists(target: string): Promise<boolean>;
|
|
59
|
+
mkdir(target: string): Promise<void>;
|
|
60
|
+
readdir(target: string): Promise<string[]>;
|
|
61
|
+
stat(target: string): Promise<{
|
|
62
|
+
isFile(): boolean;
|
|
63
|
+
isDirectory(): boolean;
|
|
64
|
+
}>;
|
|
65
|
+
unlink(target: string): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
export declare const silentLogger: () => LoggerPort & {
|
|
68
|
+
all(): unknown[];
|
|
69
|
+
};
|
|
70
|
+
/** Sorted `path|dir` and `path|file|sha256` lines for the whole tree. */
|
|
71
|
+
export declare function snapshotTree(root: string): Promise<string[]>;
|
|
72
|
+
export declare function readAllFiles(root: string): Promise<string>;
|
|
73
|
+
export interface TempProject {
|
|
74
|
+
root: string;
|
|
75
|
+
cleanup(): Promise<void>;
|
|
76
|
+
}
|
|
77
|
+
/** A project root with `.spec/specs/<feature>/spec.json` and a source file. */
|
|
78
|
+
export declare function createTempProject(feature?: string): Promise<TempProject>;
|
|
79
|
+
/** Scripts a FakeGitPort so the review manifest sees one modified file, `src/a.ts`. */
|
|
80
|
+
export declare function scriptOneChangedFile(git: FakeGitPort, options?: {
|
|
81
|
+
blob?: string;
|
|
82
|
+
head?: string | null;
|
|
83
|
+
}): void;
|
|
84
|
+
export {};
|