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.
Files changed (117) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +85 -13
  3. package/agents/security-auditor.md +2 -2
  4. package/dist/__tests__/helpers/message-clarity.d.ts +15 -0
  5. package/dist/__tests__/helpers/message-clarity.js +26 -0
  6. package/dist/__tests__/helpers/message-clarity.js.map +1 -0
  7. package/dist/__tests__/helpers/message-extractor.d.ts +22 -0
  8. package/dist/__tests__/helpers/message-extractor.js +135 -0
  9. package/dist/__tests__/helpers/message-extractor.js.map +1 -0
  10. package/dist/__tests__/unit/jev/helpers.d.ts +84 -0
  11. package/dist/__tests__/unit/jev/helpers.js +188 -0
  12. package/dist/__tests__/unit/jev/helpers.js.map +1 -0
  13. package/dist/adapters/cli/SDDToolAdapter.d.ts +5 -1
  14. package/dist/adapters/cli/SDDToolAdapter.js +22 -3
  15. package/dist/adapters/cli/SDDToolAdapter.js.map +1 -1
  16. package/dist/application/services/JevAdvisoryService.d.ts +53 -0
  17. package/dist/application/services/JevAdvisoryService.js +310 -0
  18. package/dist/application/services/JevAdvisoryService.js.map +1 -0
  19. package/dist/application/services/JevBaseRef.d.ts +21 -0
  20. package/dist/application/services/JevBaseRef.js +46 -0
  21. package/dist/application/services/JevBaseRef.js.map +1 -0
  22. package/dist/application/services/JevChangeManifest.d.ts +33 -0
  23. package/dist/application/services/JevChangeManifest.js +208 -0
  24. package/dist/application/services/JevChangeManifest.js.map +1 -0
  25. package/dist/application/services/JevCheckTypes.d.ts +74 -0
  26. package/dist/application/services/JevCheckTypes.js +2 -0
  27. package/dist/application/services/JevCheckTypes.js.map +1 -0
  28. package/dist/application/services/JevCodeExcerpts.d.ts +23 -0
  29. package/dist/application/services/JevCodeExcerpts.js +102 -0
  30. package/dist/application/services/JevCodeExcerpts.js.map +1 -0
  31. package/dist/application/services/JevCommitLookup.d.ts +17 -0
  32. package/dist/application/services/JevCommitLookup.js +72 -0
  33. package/dist/application/services/JevCommitLookup.js.map +1 -0
  34. package/dist/application/services/JevExchange.d.ts +48 -0
  35. package/dist/application/services/JevExchange.js +119 -0
  36. package/dist/application/services/JevExchange.js.map +1 -0
  37. package/dist/application/services/JevQuestionCatalog.d.ts +28 -0
  38. package/dist/application/services/JevQuestionCatalog.js +194 -0
  39. package/dist/application/services/JevQuestionCatalog.js.map +1 -0
  40. package/dist/application/services/JevRedaction.d.ts +12 -0
  41. package/dist/application/services/JevRedaction.js +56 -0
  42. package/dist/application/services/JevRedaction.js.map +1 -0
  43. package/dist/application/services/JevReport.d.ts +41 -0
  44. package/dist/application/services/JevReport.js +101 -0
  45. package/dist/application/services/JevReport.js.map +1 -0
  46. package/dist/application/services/JevResponseValidator.d.ts +34 -0
  47. package/dist/application/services/JevResponseValidator.js +98 -0
  48. package/dist/application/services/JevResponseValidator.js.map +1 -0
  49. package/dist/application/services/JevResults.d.ts +3 -0
  50. package/dist/application/services/JevResults.js +7 -0
  51. package/dist/application/services/JevResults.js.map +1 -0
  52. package/dist/application/services/JevReviewCheck.d.ts +20 -0
  53. package/dist/application/services/JevReviewCheck.js +120 -0
  54. package/dist/application/services/JevReviewCheck.js.map +1 -0
  55. package/dist/application/services/JevSettings.d.ts +18 -0
  56. package/dist/application/services/JevSettings.js +72 -0
  57. package/dist/application/services/JevSettings.js.map +1 -0
  58. package/dist/application/services/JevSidecarStore.d.ts +68 -0
  59. package/dist/application/services/JevSidecarStore.js +201 -0
  60. package/dist/application/services/JevSidecarStore.js.map +1 -0
  61. package/dist/application/services/JevSpecState.d.ts +38 -0
  62. package/dist/application/services/JevSpecState.js +109 -0
  63. package/dist/application/services/JevSpecState.js.map +1 -0
  64. package/dist/application/services/JevTestcaseState.d.ts +22 -0
  65. package/dist/application/services/JevTestcaseState.js +85 -0
  66. package/dist/application/services/JevTestcaseState.js.map +1 -0
  67. package/dist/application/services/RedEvidenceClassifier.d.ts +10 -0
  68. package/dist/application/services/RedEvidenceClassifier.js +32 -0
  69. package/dist/application/services/RedEvidenceClassifier.js.map +1 -0
  70. package/dist/application/services/WorkflowEngineService.d.ts +28 -0
  71. package/dist/application/services/WorkflowEngineService.js +111 -63
  72. package/dist/application/services/WorkflowEngineService.js.map +1 -1
  73. package/dist/application/services/WorkflowValidationService.d.ts +16 -0
  74. package/dist/application/services/WorkflowValidationService.js +39 -34
  75. package/dist/application/services/WorkflowValidationService.js.map +1 -1
  76. package/dist/cli/install-skills.js +0 -0
  77. package/dist/cli/install-target.d.ts +8 -6
  78. package/dist/cli/install-target.js +11 -3
  79. package/dist/cli/install-target.js.map +1 -1
  80. package/dist/cli/tool-support/root-guidance.js +1 -0
  81. package/dist/cli/tool-support/root-guidance.js.map +1 -1
  82. package/dist/cli/tool-support/target-installer.d.ts +1 -0
  83. package/dist/cli/tool-support/target-installer.js +9 -5
  84. package/dist/cli/tool-support/target-installer.js.map +1 -1
  85. package/dist/domain/ports.d.ts +34 -0
  86. package/dist/domain/ports.js +11 -1
  87. package/dist/domain/ports.js.map +1 -1
  88. package/dist/infrastructure/adapters/FetchJevHttpAdapter.d.ts +4 -0
  89. package/dist/infrastructure/adapters/FetchJevHttpAdapter.js +73 -0
  90. package/dist/infrastructure/adapters/FetchJevHttpAdapter.js.map +1 -0
  91. package/dist/infrastructure/adapters/NodeGitAdapter.d.ts +5 -0
  92. package/dist/infrastructure/adapters/NodeGitAdapter.js +31 -0
  93. package/dist/infrastructure/adapters/NodeGitAdapter.js.map +1 -0
  94. package/dist/infrastructure/di/container.js +10 -0
  95. package/dist/infrastructure/di/container.js.map +1 -1
  96. package/dist/infrastructure/di/types.d.ts +4 -0
  97. package/dist/infrastructure/di/types.js +4 -0
  98. package/dist/infrastructure/di/types.js.map +1 -1
  99. package/dist/infrastructure/mcp/sddToolDefinitions.d.ts +1 -1
  100. package/dist/infrastructure/mcp/sddToolDefinitions.js +72 -1
  101. package/dist/infrastructure/mcp/sddToolDefinitions.js.map +1 -1
  102. package/dist/shared/version.d.ts +1 -1
  103. package/dist/shared/version.js +1 -1
  104. package/package.json +1 -1
  105. package/skills/output-clarity-ladder/SKILL.md +43 -0
  106. package/skills/output-clarity-ladder/references/ja.md +30 -0
  107. package/skills/output-clarity-ladder/references/zh-TW.md +30 -0
  108. package/skills/sdd-commit/SKILL.md +4 -3
  109. package/skills/sdd-design/SKILL.md +1 -1
  110. package/skills/sdd-implement/SKILL.md +4 -4
  111. package/skills/sdd-requirements/SKILL.md +6 -6
  112. package/skills/sdd-review/SKILL.md +3 -2
  113. package/skills/sdd-security-check/SKILL.md +3 -2
  114. package/skills/sdd-tasks/REFERENCE.md +4 -0
  115. package/skills/sdd-tasks/SKILL.md +8 -6
  116. package/templates/CLAUDE.md +1 -1
  117. 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.3.0** — Review, security, and multi-slice implementation skills ask once per session whether to run inline or use project agents, and every skill reports agents, parallelism, configured model/effort, and fallbacks. Installed guidance forbids commit/PR attribution lines. Claude Code requires 2.1.284+ for the pinned 5.5 defaults.
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 plus the user conversation. The MCP runtime stays behind the Skill boundary and owns feature identity, canonical artifact writes, deterministic validation, revision-bound approvals, optional test review, implementation progress, and restart-safe context.
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.3.0 install --profile lean --target claude-code
71
- npx sdd-mcp-server@5.3.0 install --profile lean --target codex
72
- npx sdd-mcp-server@5.3.0 install --profile lean --target omp
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.3.0 install --profile full
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`; omitted roles retain package defaults. Claude Code routes installed skills and subagents by model and effort. Codex routes generated custom agents by model and reasoning effort, but this cannot change the inline parent's model. 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 or validate model availability. See [Model Routing](docs/MODEL-ROUTING.md) for selector syntax and host-specific limits.
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 ask once per session: inline or project agent. Planning skills (requirements, design, tasks, steering) never ask. Every skill reports the mode, agents started, parallelism, configured model/effort, and fallbacks; see [Model Routing](docs/MODEL-ROUTING.md#execution-mode-and-reporting) to verify on each host. SDD agents are subagents, not the Claude Code `/advisor` tool.
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.3.0 install \
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 16 tools:
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 preserve the returned fingerprint for `ifNoneMatch`, request unapproved source only explicitly in full mode, and 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.
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 (default Claude Opus 5.5/high for high-level work and Claude Sonnet 5.5/medium for implementation/TDD); installed Claude subagents have matching metadata. Codex runs inline by default and 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. OMP executes invoked SDD skills inline, with the installed `.omp/extensions/sdd-skill-routing.js` switching the parent to the configured model/thinking level (GPT-6 Sol/xhigh for high-level work, GPT-6 Luna/medium for implementation/TDD) for the turn and restoring it afterward. OMP’s `.omp/agents` advisors remain explicit opt-in only, allow one child, and cannot nest or retry. Implementation, TDD, and simple tasks remain inline unless genuinely independent parallel slices justify delegation.
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 external contact.
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 {};