@spec-engine/spec-engine 0.7.0 → 0.7.1
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/dist/docs/architecture/index.html +3 -3
- package/dist/docs/commands/index.html +5 -2
- package/dist/docs/pagefind/fragment/en_aaced0a.pf_fragment +0 -0
- package/dist/docs/pagefind/fragment/en_eb51244.pf_fragment +0 -0
- package/dist/docs/pagefind/index/en_91ab2a1.pf_index +0 -0
- package/dist/docs/pagefind/index/en_b75c632.pf_index +0 -0
- package/dist/docs/pagefind/pagefind-entry.json +1 -1
- package/dist/docs/pagefind/pagefind.en_dac6a68e28.pf_meta +0 -0
- package/dist/impl.js +282 -31
- package/package.json +1 -1
- package/dist/docs/pagefind/fragment/en_6ce6fae.pf_fragment +0 -0
- package/dist/docs/pagefind/fragment/en_775cc8c.pf_fragment +0 -0
- package/dist/docs/pagefind/index/en_6b9dc89.pf_index +0 -0
- package/dist/docs/pagefind/index/en_c5bf3e1.pf_index +0 -0
- package/dist/docs/pagefind/pagefind.en_c6c8e941d5.pf_meta +0 -0
|
@@ -386,8 +386,8 @@
|
|
|
386
386
|
|
|
387
387
|
|
|
388
388
|
|
|
389
|
-
<table><thead><tr><th>Operation</th><th>Input</th><th>Result</th><th>CLI</th><th>HTTP</th><th>MCP</th></tr></thead><tbody><tr><td><code dir="auto">query</code></td><td>storage, text, limit</td><td>ranked hits, or <code dir="auto">usage</code> on an FTS5 grammar error</td><td><code dir="auto">spec query</code></td><td><code dir="auto">GET /api/query</code></td><td><code dir="auto">spec_query</code></td></tr><tr><td><code dir="auto">resolveFiles</code></td><td>storage, platform-relative files</td><td>the requirements tagged in them</td><td><code dir="auto">spec resolve</code></td><td><code dir="auto">GET /api/resolve?files=</code></td><td><code dir="auto">spec_resolve</code></td></tr><tr><td><code dir="auto">reqTags</code></td><td>storage, requirement id</td><td>every tag site, and whether the id is known</td><td><code dir="auto">spec resolve --req</code></td><td><code dir="auto">GET /api/resolve?req=</code></td><td><code dir="auto">spec_req_tags</code></td></tr><tr><td><code dir="auto">propagation</code></td><td>storage, requirement id</td><td>per-member migration state</td><td><code dir="auto">spec propagation</code></td><td><code dir="auto">GET /api/propagation/:id</code></td><td><code dir="auto">spec_propagation</code></td></tr><tr><td><code dir="auto">coverageReport</code></td><td>storage</td><td>per-domain rollup</td><td></td><td><code dir="auto">GET /api/report</code></td><td><code dir="auto">spec_coverage_report</code></td></tr><tr><td><code dir="auto">check</code></td><td>storage, platform dir, results file, base ref, flags</td><td>diagnostics, <code dir="auto">build_id</code>, red or green</td><td><code dir="auto">spec check</code></td><td></td><td><code dir="auto">spec_check</code></td></tr><tr><td><code dir="auto">nextId</code></td><td>platform dir, domain key or prefix</td><td>the resolved key and next unused id, or <code dir="auto">usage</code></td><td><code dir="auto">spec req</code></td><td></td><td><code dir="auto">spec_next_id</code></td></tr><tr><td><code dir="auto">mint</code></td><td>platform dir, key, statement, why, lives-in, relates, issue, status</td><td>the new id and file, or <code dir="auto">not_found</code> / <code dir="auto">usage</code> / <code dir="auto">invalid_domain_file</code></td><td><code dir="auto">spec req --text</code></td><td><code dir="auto">POST /api/requirements</code></td><td></td></tr><tr><td><code dir="auto">amend</code></td><td>platform dir, id, fields</td><td>the changed fields, or <code dir="auto">not_found</code> / <code dir="auto">conflict</code> / <code dir="auto">invalid_domain_file</code></td><td><code dir="auto">spec amend</code></td><td><code dir="auto">PUT /api/requirements/:id</code></td><td></td></tr><tr><td><code dir="auto">supersede</code></td><td>platform dir, id, successor statement and fields, a fresh-tags callback</td><td>the successor id, the domain version, the retag worklist, or <code dir="auto">not_found</code> / <code dir="auto">conflict</code> / <code dir="auto">usage</code> / <code dir="auto">invalid_domain_file</code></td><td><code dir="auto">spec supersede</code></td><td><code dir="auto">POST /api/requirements/:id/supersede</code></td><td><code dir="auto">spec_supersede</code></td></tr><tr><td><code dir="auto">move</code></td><td>platform dir, id, target domain key, optional successor fields, a fresh-tags callback</td><td>both files, both versions, the retag worklist, or <code dir="auto">usage</code> / <code dir="auto">not_found</code> / <code dir="auto">conflict</code> / <code dir="auto">invalid_domain_file</code></td><td><code dir="auto">spec move</code></td><td></td><td></td></tr><tr><td><code dir="auto">deprecate</code></td><td>platform dir, id, reason, a fresh-tags callback</td><td>the file and the tags still bound, or <code dir="auto">not_found</code> / <code dir="auto">conflict</code> / <code dir="auto">invalid_domain_file</code></td><td><code dir="auto">spec deprecate</code></td><td><code dir="auto">POST /api/requirements/:id/deprecate</code></td><td><code dir="auto">spec_deprecate</code></td></tr><tr><td><code dir="auto">accept</code></td><td>platform dir, id</td><td>the file, or <code dir="auto">not_found</code> / <code dir="auto">conflict</code> / <code dir="auto">invalid_domain_file</code></td><td><code dir="auto">spec accept</code></td><td><code dir="auto">POST /api/requirements/:id/accept</code></td><td><code dir="auto">spec_accept</code></td></tr><tr><td><code dir="auto">mintTerm</code>, <code dir="auto">listTerms</code>, <code dir="auto">reviseTerm</code>, <code dir="auto">confirmTerm</code></td><td>platform dir, term fields or ids</td><td>the term id, the store rows, the bumped version, or the re-pinned citation; <code dir="auto">not_found</code> / <code dir="auto">conflict</code> / <code dir="auto">invalid_domain_file</code></td><td><code dir="auto">spec term</code></td><td></td><td></td></tr><tr><td><code dir="auto">coverageMatrix</code></td><td>storage</td><td>requirement × repo rows</td><td><code dir="auto">spec map</code></td><td><code dir="auto">GET /api/coverage</code></td><td></td></tr><tr><td><code dir="auto">getRecord</code></td><td>storage, requirement id</td><td>one full <code dir="auto">Requirement</code> row, or null</td><td><code dir="auto">spec get</code></td><td><code dir="auto">GET /api/requirements/:id</code></td><td><code dir="auto">spec_get</code></td></tr><tr><td><code dir="auto">listRecords</code></td><td>storage, optional key and status</td><td>every <code dir="auto">Requirement</code> row in (key, seq) order</td><td><code dir="auto">spec list</code></td><td><code dir="auto">GET /api/requirements</code></td><td><code dir="auto">spec_list</code></td></tr><tr><td><code dir="auto">relations</code></td><td>storage</td><td><code dir="auto">relates</code> links</td><td><code dir="auto">spec relations</code></td><td><code dir="auto">GET /api/relations</code></td><td></td></tr><tr><td><code dir="auto">provenance</code></td><td>storage, optional issue id</td><td>provenance matrix rows</td><td><code dir="auto">spec provenance</code></td><td><code dir="auto">GET /api/provenance</code></td><td></td></tr><tr><td><code dir="auto">guard</code></td><td>storage, platform dir, git ref</td><td>the losses a change is about to cause</td><td><code dir="auto">spec guard</code></td><td></td><td></td></tr><tr><td><code dir="auto">gate</code></td><td>storage, platform dir, repo, requirement id</td><td>the gate outcome and <code dir="auto">build_id</code>, or <code dir="auto">usage</code> for an unknown repo</td><td><code dir="auto">spec gate</code></td><td></td><td></td></tr><tr><td><code dir="auto">resolveMemberPin</code>, <code dir="auto">writeMemberConfig</code></td><td>repo dir, pin override, force</td><td>the pin and its source; wrote or already configured, or <code dir="auto">usage</code> / <code dir="auto">conflict</code></td><td><code dir="auto">spec init</code></td><td></td><td></td></tr><tr><td><code dir="auto">newDomain</code>, <code dir="auto">listDomains</code></td><td>platform dir, key</td><td>the scaffolded file, or <code dir="auto">usage</code> / <code dir="auto">conflict</code> / <code dir="auto">invalid_domain_file</code>; the <code dir="auto">{ key, scope }</code> rows</td><td><code dir="auto">spec domain</code></td><td></td><td></td></tr><tr><td><code dir="auto">writeGlossary</code>, <code dir="auto">migrateGlossary</code>, <code dir="auto">checkGlossary</code></td><td>platform dir</td><td>the generated count, the migrated count or a skip, clean or drift</td><td><code dir="auto">spec glossary</code></td><td></td><td></td></tr></tbody></table>
|
|
390
|
-
<p>Reads take an open <code dir="auto">Storage</code> handle because the surface owns the handle’s lifetime: the CLI and MCP open one per call through <code dir="auto">withIndex</code>, the API keeps one for the life of the server. Writes take the platform directory because they edit <code dir="auto">SPEC.json</code> files. A lifecycle write takes a fresh-tags callback for its worklist, so a one-shot surface cold-rebuilds a throwaway index while the API re-indexes into the handle it keeps. Every guard runs before the first byte is written (REQ-038 <!-- @spec REQ-038 -->), and <code dir="auto">operations
|
|
389
|
+
<table><thead><tr><th>Operation</th><th>Input</th><th>Result</th><th>CLI</th><th>HTTP</th><th>MCP</th></tr></thead><tbody><tr><td><code dir="auto">query</code></td><td>storage, text, limit</td><td>ranked hits, or <code dir="auto">usage</code> on an FTS5 grammar error</td><td><code dir="auto">spec query</code></td><td><code dir="auto">GET /api/query</code></td><td><code dir="auto">spec_query</code></td></tr><tr><td><code dir="auto">resolveFiles</code></td><td>storage, platform-relative files</td><td>the requirements tagged in them</td><td><code dir="auto">spec resolve</code></td><td><code dir="auto">GET /api/resolve?files=</code></td><td><code dir="auto">spec_resolve</code></td></tr><tr><td><code dir="auto">reqTags</code></td><td>storage, requirement id</td><td>every tag site, and whether the id is known</td><td><code dir="auto">spec resolve --req</code></td><td><code dir="auto">GET /api/resolve?req=</code></td><td><code dir="auto">spec_req_tags</code></td></tr><tr><td><code dir="auto">propagation</code></td><td>storage, requirement id</td><td>per-member migration state</td><td><code dir="auto">spec propagation</code></td><td><code dir="auto">GET /api/propagation/:id</code></td><td><code dir="auto">spec_propagation</code></td></tr><tr><td><code dir="auto">coverageReport</code></td><td>storage</td><td>per-domain rollup</td><td></td><td><code dir="auto">GET /api/report</code></td><td><code dir="auto">spec_coverage_report</code></td></tr><tr><td><code dir="auto">check</code></td><td>storage, platform dir, results file, base ref, flags</td><td>diagnostics, <code dir="auto">build_id</code>, red or green</td><td><code dir="auto">spec check</code></td><td></td><td><code dir="auto">spec_check</code></td></tr><tr><td><code dir="auto">nextId</code></td><td>platform dir, domain key or prefix</td><td>the resolved key and next unused id, or <code dir="auto">usage</code></td><td><code dir="auto">spec req</code></td><td></td><td><code dir="auto">spec_next_id</code></td></tr><tr><td><code dir="auto">mint</code></td><td>platform dir, key, statement, why, lives-in, relates, issue, status</td><td>the new id and file, or <code dir="auto">not_found</code> / <code dir="auto">usage</code> / <code dir="auto">invalid_domain_file</code></td><td><code dir="auto">spec req --text</code></td><td><code dir="auto">POST /api/requirements</code></td><td><code dir="auto">spec_req</code></td></tr><tr><td><code dir="auto">amend</code></td><td>platform dir, id, fields</td><td>the changed fields, or <code dir="auto">not_found</code> / <code dir="auto">conflict</code> / <code dir="auto">invalid_domain_file</code></td><td><code dir="auto">spec amend</code></td><td><code dir="auto">PUT /api/requirements/:id</code></td><td><code dir="auto">spec_amend</code></td></tr><tr><td><code dir="auto">supersede</code></td><td>platform dir, id, successor statement and fields, a fresh-tags callback</td><td>the successor id, the domain version, the retag worklist, or <code dir="auto">not_found</code> / <code dir="auto">conflict</code> / <code dir="auto">usage</code> / <code dir="auto">invalid_domain_file</code></td><td><code dir="auto">spec supersede</code></td><td><code dir="auto">POST /api/requirements/:id/supersede</code></td><td><code dir="auto">spec_supersede</code></td></tr><tr><td><code dir="auto">move</code></td><td>platform dir, id, target domain key, optional successor fields, a fresh-tags callback</td><td>both files, both versions, the retag worklist, or <code dir="auto">usage</code> / <code dir="auto">not_found</code> / <code dir="auto">conflict</code> / <code dir="auto">invalid_domain_file</code></td><td><code dir="auto">spec move</code></td><td><code dir="auto">POST /api/requirements/:id/move</code></td><td><code dir="auto">spec_move</code></td></tr><tr><td><code dir="auto">deprecate</code></td><td>platform dir, id, reason, a fresh-tags callback</td><td>the file and the tags still bound, or <code dir="auto">not_found</code> / <code dir="auto">conflict</code> / <code dir="auto">invalid_domain_file</code></td><td><code dir="auto">spec deprecate</code></td><td><code dir="auto">POST /api/requirements/:id/deprecate</code></td><td><code dir="auto">spec_deprecate</code></td></tr><tr><td><code dir="auto">accept</code></td><td>platform dir, id</td><td>the file, or <code dir="auto">not_found</code> / <code dir="auto">conflict</code> / <code dir="auto">invalid_domain_file</code></td><td><code dir="auto">spec accept</code></td><td><code dir="auto">POST /api/requirements/:id/accept</code></td><td><code dir="auto">spec_accept</code></td></tr><tr><td><code dir="auto">mintTerm</code>, <code dir="auto">listTerms</code>, <code dir="auto">reviseTerm</code>, <code dir="auto">confirmTerm</code></td><td>platform dir, term fields or ids</td><td>the term id, the store rows, the bumped version, or the re-pinned citation; <code dir="auto">not_found</code> / <code dir="auto">conflict</code> / <code dir="auto">invalid_domain_file</code></td><td><code dir="auto">spec term</code></td><td></td><td></td></tr><tr><td><code dir="auto">coverageMatrix</code></td><td>storage</td><td>requirement × repo rows</td><td><code dir="auto">spec map</code></td><td><code dir="auto">GET /api/coverage</code></td><td></td></tr><tr><td><code dir="auto">getRecord</code></td><td>storage, requirement id</td><td>one full <code dir="auto">Requirement</code> row, or null</td><td><code dir="auto">spec get</code></td><td><code dir="auto">GET /api/requirements/:id</code></td><td><code dir="auto">spec_get</code></td></tr><tr><td><code dir="auto">listRecords</code></td><td>storage, optional key and status</td><td>every <code dir="auto">Requirement</code> row in (key, seq) order</td><td><code dir="auto">spec list</code></td><td><code dir="auto">GET /api/requirements</code></td><td><code dir="auto">spec_list</code></td></tr><tr><td><code dir="auto">relations</code></td><td>storage</td><td><code dir="auto">relates</code> links</td><td><code dir="auto">spec relations</code></td><td><code dir="auto">GET /api/relations</code></td><td></td></tr><tr><td><code dir="auto">provenance</code></td><td>storage, optional issue id</td><td>provenance matrix rows</td><td><code dir="auto">spec provenance</code></td><td><code dir="auto">GET /api/provenance</code></td><td></td></tr><tr><td><code dir="auto">guard</code></td><td>storage, platform dir, git ref</td><td>the losses a change is about to cause</td><td><code dir="auto">spec guard</code></td><td></td><td></td></tr><tr><td><code dir="auto">gate</code></td><td>storage, platform dir, repo, requirement id</td><td>the gate outcome and <code dir="auto">build_id</code>, or <code dir="auto">usage</code> for an unknown repo</td><td><code dir="auto">spec gate</code></td><td></td><td></td></tr><tr><td><code dir="auto">resolveMemberPin</code>, <code dir="auto">writeMemberConfig</code></td><td>repo dir, pin override, force</td><td>the pin and its source; wrote or already configured, or <code dir="auto">usage</code> / <code dir="auto">conflict</code></td><td><code dir="auto">spec init</code></td><td></td><td></td></tr><tr><td><code dir="auto">newDomain</code>, <code dir="auto">listDomains</code>, <code dir="auto">setDomainGrammar</code></td><td>platform dir, key</td><td>the scaffolded file, or <code dir="auto">usage</code> / <code dir="auto">conflict</code> / <code dir="auto">invalid_domain_file</code>; the <code dir="auto">{ key, scope }</code> rows; the written <code dir="auto">grammar</code> / <code dir="auto">grammarSeverity</code>, or <code dir="auto">usage</code> / <code dir="auto">not_found</code> / <code dir="auto">invalid_domain_file</code></td><td><code dir="auto">spec domain</code></td><td></td><td></td></tr><tr><td><code dir="auto">writeGlossary</code>, <code dir="auto">migrateGlossary</code>, <code dir="auto">checkGlossary</code></td><td>platform dir</td><td>the generated count, the migrated count or a skip, clean or drift</td><td><code dir="auto">spec glossary</code></td><td></td><td></td></tr></tbody></table>
|
|
390
|
+
<p>Reads take an open <code dir="auto">Storage</code> handle because the surface owns the handle’s lifetime: the CLI and MCP open one per call through <code dir="auto">withIndex</code>, the API keeps one for the life of the server. Writes take the platform directory because they edit <code dir="auto">SPEC.json</code> files. A lifecycle write takes a fresh-tags callback for its worklist, so a one-shot surface cold-rebuilds a throwaway index while the API re-indexes into the handle it keeps. Every guard runs before the first byte is written (REQ-038 <!-- @spec REQ-038 -->), and <code dir="auto">operations/parity.test.ts</code> proves each surface writes the same envelope.</p>
|
|
391
391
|
<div class="sl-heading-wrapper level-h2"><h2 id="the-single-seams">The single seams</h2><a class="sl-anchor-link" href="#the-single-seams"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “The single seams”</span></a></div>
|
|
392
392
|
<p>Each of these exists exactly once, and a grep fence in <code dir="auto">scripts/arch-fences.sh</code> fails CI if a second copy appears. Every fence label starts with the id of the requirement the fence enforces, and the fence function carries that id as its <code dir="auto">@spec</code> tag.</p>
|
|
393
393
|
|
|
@@ -440,7 +440,7 @@
|
|
|
440
440
|
|
|
441
441
|
|
|
442
442
|
|
|
443
|
-
<table><thead><tr><th>Seam</th><th>What goes through it</th><th>Fence</th></tr></thead><tbody><tr><td><code dir="auto">runIndex</code></td><td>Every index build. One transaction, one <code dir="auto">build_id</code></td><td>reviewed, not fenced</td></tr><tr><td><code dir="auto">validateAndWrite</code></td><td>Every write of a <code dir="auto">SPEC.json</code> file, a test’s fixture included; a planted defect under <code dir="auto">src/testing/fixtures/</code> is the only file written by hand</td><td><code dir="auto">SCHM-
|
|
443
|
+
<table><thead><tr><th>Seam</th><th>What goes through it</th><th>Fence</th></tr></thead><tbody><tr><td><code dir="auto">runIndex</code></td><td>Every index build. One transaction, one <code dir="auto">build_id</code></td><td>reviewed, not fenced</td></tr><tr><td><code dir="auto">validateAndWrite</code></td><td>Every write of a <code dir="auto">SPEC.json</code> file, a test’s fixture included; a planted defect under <code dir="auto">src/testing/fixtures/</code> is the only file written by hand</td><td><code dir="auto">SCHM-035 every SPEC.json write goes through validateAndWrite</code></td></tr><tr><td><code dir="auto">bun:sqlite</code> import</td><td>Only <code dir="auto">storage/sqlite.ts</code> may import it</td><td><code dir="auto">SCHM-025 bun:sqlite outside storage/sqlite.ts</code>, <code dir="auto">SCHM-025 bun:sqlite outside the engine</code></td></tr><tr><td><code dir="auto">Storage</code> interface</td><td>Every read the CLI, API, and MCP make</td><td>the webapp import fence in <code dir="auto">packages/webapp/test/import-fence.test.ts</code></td></tr><tr><td>Operations</td><td>The write seam and the id allocator are reached only through <code dir="auto">operations/</code>; no command, route, or tool imports them. A test’s fixture builder (<code dir="auto">src/testing/platform.ts</code>) is one more caller of the same operations</td><td><code dir="auto">SCHM-027 write seam under operations</code></td></tr><tr><td>No model calls</td><td>The engine never runs an LLM. The MCP authoring prompt is a text template</td><td><code dir="auto">AUTHOR-008 llm-free engine</code></td></tr><tr><td>Derived versions</td><td>No authored <code dir="auto">specVersion</code> on requirement domains</td><td><code dir="auto">SCHM-020 no authored specVersion</code></td></tr><tr><td>Generated docs</td><td><code dir="auto">GLOSSARY.md</code> and the TAXONOMY charters are regenerated, never hand-edited</td><td><code dir="auto">CHCK-020 glossary round-trip</code>, <code dir="auto">CHRT-007 charters generated from the envelopes</code></td></tr></tbody></table>
|
|
444
444
|
<div class="sl-heading-wrapper level-h2"><h2 id="read-path">Read path</h2><a class="sl-anchor-link" href="#read-path"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “Read path”</span></a></div>
|
|
445
445
|
<p><code dir="auto">spec map</code>, <code dir="auto">spec check</code>, <code dir="auto">spec query</code>, and the other read commands:</p>
|
|
446
446
|
<ol>
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
})();
|
|
71
71
|
</script> </sl-sidebar-state-persist> <div class="md:sl-hidden"> <div class="mobile-preferences sl-flex astro-2lx56rho"> <div class="social-icons astro-2lx56rho"> <a href="https://github.com/spec-engine/spec-engine" rel="me" class="sl-flex astro-opyhioab"><span class="sr-only astro-opyhioab">GitHub</span><svg aria-hidden="true" class="astro-opyhioab astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M12 .3a12 12 0 0 0-3.8 23.38c.6.12.83-.26.83-.57L9 21.07c-3.34.72-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.08-.74.09-.73.09-.73 1.2.09 1.83 1.24 1.83 1.24 1.08 1.83 2.81 1.3 3.5 1 .1-.78.42-1.31.76-1.61-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.14-.3-.54-1.52.1-3.18 0 0 1-.32 3.3 1.23a11.5 11.5 0 0 1 6 0c2.28-1.55 3.29-1.23 3.29-1.23.64 1.66.24 2.88.12 3.18a4.65 4.65 0 0 1 1.23 3.22c0 4.61-2.8 5.63-5.48 5.92.42.36.81 1.1.81 2.22l-.01 3.29c0 .31.2.69.82.57A12 12 0 0 0 12 .3Z"/></svg></a> </div> <starlight-theme-select> <label style="--sl-select-width: 6.25em" class="astro-oxpvutiw"> <span class="sr-only astro-oxpvutiw">Select theme</span> <svg aria-hidden="true" class="icon label-icon astro-oxpvutiw astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21 14h-1V7a3 3 0 0 0-3-3H7a3 3 0 0 0-3 3v7H3a1 1 0 0 0-1 1v2a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-2a1 1 0 0 0-1-1ZM6 7a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v7H6V7Zm14 10a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-1h16v1Z"/></svg> <select autocomplete="off" class="astro-oxpvutiw"> <option value="dark" class="astro-oxpvutiw">Dark</option><option value="light" class="astro-oxpvutiw">Light</option><option value="auto" selected class="astro-oxpvutiw">Auto</option> </select> <svg aria-hidden="true" class="icon caret astro-oxpvutiw astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M17 9.17a1 1 0 0 0-1.41 0L12 12.71 8.46 9.17a1 1 0 1 0-1.41 1.42l4.24 4.24a1.002 1.002 0 0 0 1.42 0L17 10.59a1.002 1.002 0 0 0 0-1.42Z"/></svg> </label> </starlight-theme-select> <script>
|
|
72
72
|
StarlightThemeProvider.updatePickers();
|
|
73
|
-
</script> </div> </div> </div> </div> </nav> <div class="main-frame astro-to3xypmc"> <script type="module">const a=document.getElementById("starlight__sidebar"),n=a?.querySelector("sl-sidebar-state-persist"),o="sl-sidebar-state",i=()=>{let t=[];const e=n?.dataset.hash||"";try{const s=sessionStorage.getItem(o),r=JSON.parse(s||"{}");Array.isArray(r.open)&&r.hash===e&&(t=r.open)}catch{}return{hash:e,open:t,scroll:a?.scrollTop||0}},c=t=>{try{sessionStorage.setItem(o,JSON.stringify(t))}catch{}},d=()=>c(i()),l=(t,e)=>{const s=i();s.open[e]=t,c(s)};n?.addEventListener("click",t=>{if(!(t.target instanceof Element))return;const e=t.target.closest("summary")?.closest("details");if(!e)return;const s=e.querySelector("sl-sidebar-restore"),r=parseInt(s?.dataset.index||"");isNaN(r)||l(!e.open,r)});addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&d()});addEventListener("pageHide",d);</script> <div class="lg:sl-flex astro-blhl7r6q"> <aside class="right-sidebar-container print:hidden astro-blhl7r6q"> <div class="right-sidebar astro-blhl7r6q"> <div class="lg:sl-hidden astro-l5m3cfel"><mobile-starlight-toc data-min-h="2" data-max-h="3" class="astro-goxitxj7"><nav aria-labelledby="starlight__on-this-page--mobile" class="astro-goxitxj7"><details id="starlight__mobile-toc" class="astro-goxitxj7"><summary id="starlight__on-this-page--mobile" class="sl-flex astro-goxitxj7"><span class="toggle sl-flex astro-goxitxj7">On this page<svg aria-hidden="true" class="caret astro-goxitxj7 astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg></span><span class="display-current astro-goxitxj7"></span></summary><div class="dropdown astro-goxitxj7"><ul class="isMobile astro-xz67lhkg" style="--depth: 0;"> <li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#_top" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Overview</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#exit-code-contract" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Exit code contract</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#common-flags" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Common flags</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#command-reference" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Command reference</span> </a> <ul class="isMobile astro-xz67lhkg" style="--depth: 1;"> <li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-index-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec index [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-check-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec check [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-guard-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec guard [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-map-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec map [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-get-key-nnn-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec get <KEY-NNN> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-list-platformdir---domain-key---status-s" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec list [platformDir] [--domain KEY] [--status S]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-query-text-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec query <text> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-relations-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec relations [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-provenance-issueid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec provenance [issueId] [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-resolve-files-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec resolve <files...> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-propagation-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec propagation <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-gate-repo-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec gate <repo> <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-init-repo" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec init [repo]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-domain-new-name--spec-domain-list-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec domain new <name> / spec domain list [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-migrate-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec migrate [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-req-domain-prefix-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec req <domain-prefix> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-term-" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec term …</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-glossary-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec glossary [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-supersede-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec supersede <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-move-reqid-new-domain-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec move <reqId> <NEW-DOMAIN> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-amend-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec amend <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-accept-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec accept <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-serve-platformdir---port-n---host-ip---token-t---allow-writes" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec serve [platformDir] [--port N] [--host IP] [--token T] [--allow-writes]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-mcp-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec mcp [platformDir]</span> </a> </li> </ul> </li> </ul></div></details></nav></mobile-starlight-toc><script type="module" src="/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.hwBsy0Mo.js"></script></div><div class="right-sidebar-panel sl-hidden lg:sl-block astro-l5m3cfel"><div class="sl-container astro-l5m3cfel"><starlight-toc data-min-h="2" data-max-h="3"><nav aria-labelledby="starlight__on-this-page"><h2 id="starlight__on-this-page">On this page</h2><ul class="astro-xz67lhkg" style="--depth: 0;"> <li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#_top" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Overview</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#exit-code-contract" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Exit code contract</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#common-flags" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Common flags</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#command-reference" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Command reference</span> </a> <ul class="astro-xz67lhkg" style="--depth: 1;"> <li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-index-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec index [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-check-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec check [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-guard-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec guard [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-map-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec map [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-get-key-nnn-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec get <KEY-NNN> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-list-platformdir---domain-key---status-s" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec list [platformDir] [--domain KEY] [--status S]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-query-text-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec query <text> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-relations-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec relations [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-provenance-issueid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec provenance [issueId] [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-resolve-files-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec resolve <files...> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-propagation-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec propagation <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-gate-repo-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec gate <repo> <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-init-repo" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec init [repo]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-domain-new-name--spec-domain-list-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec domain new <name> / spec domain list [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-migrate-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec migrate [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-req-domain-prefix-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec req <domain-prefix> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-term-" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec term …</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-glossary-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec glossary [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-supersede-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec supersede <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-move-reqid-new-domain-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec move <reqId> <NEW-DOMAIN> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-amend-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec amend <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-accept-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec accept <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-serve-platformdir---port-n---host-ip---token-t---allow-writes" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec serve [platformDir] [--port N] [--host IP] [--token T] [--allow-writes]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-mcp-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec mcp [platformDir]</span> </a> </li> </ul> </li> </ul></nav></starlight-toc><script type="module" src="/_astro/TableOfContents.astro_astro_type_script_index_0_lang.FuRcXuRY.js"></script></div></div> </div> </aside> <div class="main-pane astro-blhl7r6q"> <main data-pagefind-body class="astro-ioexmpvj" lang="en" dir="ltr"> <div class="content-panel astro-sfndtt72"> <div class="sl-container astro-sfndtt72"> <h1 id="_top" class="astro-4b4mtvou">Commands</h1> </div> </div> <div class="content-panel astro-sfndtt72"> <div class="sl-container astro-sfndtt72"> <div class="sl-markdown-content"> <p>Install the CLI from npm — <code dir="auto">bun add -g @spec-engine/spec-engine</code> puts <code dir="auto">spec</code> on your PATH, and every command below is written that way. Without a global install, run any of them as <code dir="auto">bunx @spec-engine/spec-engine <command> [args]</code>; from a checkout, as <code dir="auto">bun packages/engine/src/cli.ts <command> [args]</code>. See <a href="/getting-started/">Getting Started</a> for the details.</p>
|
|
73
|
+
</script> </div> </div> </div> </div> </nav> <div class="main-frame astro-to3xypmc"> <script type="module">const a=document.getElementById("starlight__sidebar"),n=a?.querySelector("sl-sidebar-state-persist"),o="sl-sidebar-state",i=()=>{let t=[];const e=n?.dataset.hash||"";try{const s=sessionStorage.getItem(o),r=JSON.parse(s||"{}");Array.isArray(r.open)&&r.hash===e&&(t=r.open)}catch{}return{hash:e,open:t,scroll:a?.scrollTop||0}},c=t=>{try{sessionStorage.setItem(o,JSON.stringify(t))}catch{}},d=()=>c(i()),l=(t,e)=>{const s=i();s.open[e]=t,c(s)};n?.addEventListener("click",t=>{if(!(t.target instanceof Element))return;const e=t.target.closest("summary")?.closest("details");if(!e)return;const s=e.querySelector("sl-sidebar-restore"),r=parseInt(s?.dataset.index||"");isNaN(r)||l(!e.open,r)});addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&d()});addEventListener("pageHide",d);</script> <div class="lg:sl-flex astro-blhl7r6q"> <aside class="right-sidebar-container print:hidden astro-blhl7r6q"> <div class="right-sidebar astro-blhl7r6q"> <div class="lg:sl-hidden astro-l5m3cfel"><mobile-starlight-toc data-min-h="2" data-max-h="3" class="astro-goxitxj7"><nav aria-labelledby="starlight__on-this-page--mobile" class="astro-goxitxj7"><details id="starlight__mobile-toc" class="astro-goxitxj7"><summary id="starlight__on-this-page--mobile" class="sl-flex astro-goxitxj7"><span class="toggle sl-flex astro-goxitxj7">On this page<svg aria-hidden="true" class="caret astro-goxitxj7 astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg></span><span class="display-current astro-goxitxj7"></span></summary><div class="dropdown astro-goxitxj7"><ul class="isMobile astro-xz67lhkg" style="--depth: 0;"> <li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#_top" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Overview</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#exit-code-contract" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Exit code contract</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#common-flags" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Common flags</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#command-reference" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Command reference</span> </a> <ul class="isMobile astro-xz67lhkg" style="--depth: 1;"> <li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-index-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec index [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-check-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec check [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-guard-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec guard [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-map-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec map [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-get-key-nnn-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec get <KEY-NNN> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-list-platformdir---domain-key---status-s" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec list [platformDir] [--domain KEY] [--status S]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-query-text-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec query <text> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-relations-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec relations [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-provenance-issueid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec provenance [issueId] [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-resolve-files-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec resolve <files...> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-propagation-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec propagation <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-gate-repo-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec gate <repo> <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-init-repo" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec init [repo]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-domain-new-name--spec-domain-list-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec domain new <name> / spec domain list [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-domain-grammar-key-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec domain grammar <KEY> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-migrate-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec migrate [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-req-domain-prefix-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec req <domain-prefix> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-term-" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec term …</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-glossary-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec glossary [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-supersede-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec supersede <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-move-reqid-new-domain-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec move <reqId> <NEW-DOMAIN> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-amend-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec amend <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-accept-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec accept <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-serve-platformdir---port-n---host-ip---token-t---allow-writes" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec serve [platformDir] [--port N] [--host IP] [--token T] [--allow-writes]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-mcp-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec mcp [platformDir]</span> </a> </li> </ul> </li> </ul></div></details></nav></mobile-starlight-toc><script type="module" src="/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.hwBsy0Mo.js"></script></div><div class="right-sidebar-panel sl-hidden lg:sl-block astro-l5m3cfel"><div class="sl-container astro-l5m3cfel"><starlight-toc data-min-h="2" data-max-h="3"><nav aria-labelledby="starlight__on-this-page"><h2 id="starlight__on-this-page">On this page</h2><ul class="astro-xz67lhkg" style="--depth: 0;"> <li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#_top" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Overview</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#exit-code-contract" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Exit code contract</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#common-flags" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Common flags</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 0;"> <a href="#command-reference" class="astro-xz67lhkg" style="--depth: 0;"> <span class="astro-xz67lhkg" style="--depth: 0;">Command reference</span> </a> <ul class="astro-xz67lhkg" style="--depth: 1;"> <li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-index-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec index [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-check-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec check [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-guard-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec guard [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-map-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec map [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-get-key-nnn-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec get <KEY-NNN> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-list-platformdir---domain-key---status-s" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec list [platformDir] [--domain KEY] [--status S]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-query-text-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec query <text> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-relations-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec relations [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-provenance-issueid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec provenance [issueId] [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-resolve-files-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec resolve <files...> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-propagation-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec propagation <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-gate-repo-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec gate <repo> <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-init-repo" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec init [repo]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-domain-new-name--spec-domain-list-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec domain new <name> / spec domain list [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-domain-grammar-key-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec domain grammar <KEY> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-migrate-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec migrate [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-req-domain-prefix-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec req <domain-prefix> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-term-" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec term …</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-glossary-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec glossary [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-supersede-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec supersede <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-move-reqid-new-domain-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec move <reqId> <NEW-DOMAIN> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-amend-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec amend <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-accept-reqid-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec accept <reqId> [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-serve-platformdir---port-n---host-ip---token-t---allow-writes" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec serve [platformDir] [--port N] [--host IP] [--token T] [--allow-writes]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-mcp-platformdir" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec mcp [platformDir]</span> </a> </li> </ul> </li> </ul></nav></starlight-toc><script type="module" src="/_astro/TableOfContents.astro_astro_type_script_index_0_lang.FuRcXuRY.js"></script></div></div> </div> </aside> <div class="main-pane astro-blhl7r6q"> <main data-pagefind-body class="astro-ioexmpvj" lang="en" dir="ltr"> <div class="content-panel astro-sfndtt72"> <div class="sl-container astro-sfndtt72"> <h1 id="_top" class="astro-4b4mtvou">Commands</h1> </div> </div> <div class="content-panel astro-sfndtt72"> <div class="sl-container astro-sfndtt72"> <div class="sl-markdown-content"> <p>Install the CLI from npm — <code dir="auto">bun add -g @spec-engine/spec-engine</code> puts <code dir="auto">spec</code> on your PATH, and every command below is written that way. Without a global install, run any of them as <code dir="auto">bunx @spec-engine/spec-engine <command> [args]</code>; from a checkout, as <code dir="auto">bun packages/engine/src/cli.ts <command> [args]</code>. See <a href="/getting-started/">Getting Started</a> for the details.</p>
|
|
74
74
|
<div class="sl-heading-wrapper level-h2"><h2 id="exit-code-contract">Exit code contract</h2><a class="sl-anchor-link" href="#exit-code-contract"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “Exit code contract”</span></a></div>
|
|
75
75
|
|
|
76
76
|
|
|
@@ -193,6 +193,9 @@
|
|
|
193
193
|
<div class="sl-heading-wrapper level-h3"><h3 id="spec-domain-new-name--spec-domain-list-platformdir"><code dir="auto">spec domain new <name></code> / <code dir="auto">spec domain list [platformDir]</code></h3><a class="sl-anchor-link" href="#spec-domain-new-name--spec-domain-list-platformdir"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “spec domain new <name> / spec domain list [platformDir]”</span></a></div>
|
|
194
194
|
<p>Scaffold <code dir="auto">spec-engine/<KEY>/SPEC.json</code> (input normalized to uppercase, whitespace stripped) / list domains with their charters (<code dir="auto">--json</code>: sorted <code dir="auto">{ key, scope }</code> array, read from the filesystem).</p>
|
|
195
195
|
<p><strong>Exit codes:</strong> 0 / 2</p>
|
|
196
|
+
<div class="sl-heading-wrapper level-h3"><h3 id="spec-domain-grammar-key-platformdir"><code dir="auto">spec domain grammar <KEY> [platformDir]</code></h3><a class="sl-anchor-link" href="#spec-domain-grammar-key-platformdir"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “spec domain grammar <KEY> [platformDir]”</span></a></div>
|
|
197
|
+
<p>Set a domain’s statement grammar (<code dir="auto">--grammar ears|freeform</code>) and/or its severity (<code dir="auto">--severity warning|error</code>) through the validating write seam; every other envelope field is written back as read. <code dir="auto">--json</code> prints <code dir="auto">{ key, grammar, grammar_severity, file }</code>. Under <code dir="auto">grammar: "ears"</code>, a statement must fit one of the five EARS shapes and pass the one-promise lint: no <code dir="auto">; </code> after the first <code dir="auto">shall</code>, no <code dir="auto">, and <subject> shall</code> second clause, no rationale connective (<code dir="auto">because</code>, <code dir="auto">so that</code>, <code dir="auto">in order to</code>). A trailing <code dir="auto">, so <observable consequence></code> stays legal. <code dir="auto">spec check</code> reports a miss as <code dir="auto">STATEMENT_GRAMMAR</code>, and <code dir="auto">spec req</code> / <code dir="auto">amend</code> / <code dir="auto">supersede</code> / <code dir="auto">move</code> warn or refuse at write time, both at the declared severity. The TERM domain is exempt.</p>
|
|
198
|
+
<p><strong>Exit codes:</strong> 0 / 2 (unknown domain, neither flag, or a value outside the enums)</p>
|
|
196
199
|
<div class="sl-heading-wrapper level-h3"><h3 id="spec-migrate-platformdir"><code dir="auto">spec migrate [platformDir]</code></h3><a class="sl-anchor-link" href="#spec-migrate-platformdir"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “spec migrate [platformDir]”</span></a></div>
|
|
197
200
|
<p>One-time hard cutover: convert every legacy canonical <code dir="auto">SPEC.md</code> to a schema-validated sibling <code dir="auto">SPEC.json</code>, then delete the <code dir="auto">SPEC.md</code>. Idempotent — skips already-migrated dirs.</p>
|
|
198
201
|
<p><strong>Exit codes:</strong> 0 / 2</p>
|
|
@@ -232,5 +235,5 @@
|
|
|
232
235
|
<p><strong>Feature-flagged routes:</strong> <code dir="auto">/api/query?q=&limit=</code>, <code dir="auto">/api/relations[?format=mermaid]</code>, <code dir="auto">/api/provenance</code>, and the editor’s write routes answer <strong>404</strong> until their feature is enabled — the unshipped webapp features (query, relations, provenance, editor, glossary, logs) all default off. Opt in locally with a comma-separated <code dir="auto">SPEC_FLAGS</code> env var: <code dir="auto">SPEC_FLAGS=query,relations spec serve .</code> (unknown keys are ignored).</p>
|
|
233
236
|
<p><strong>Exit codes:</strong> 0 / 1 / 2</p>
|
|
234
237
|
<div class="sl-heading-wrapper level-h3"><h3 id="spec-mcp-platformdir"><code dir="auto">spec mcp [platformDir]</code></h3><a class="sl-anchor-link" href="#spec-mcp-platformdir"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “spec mcp [platformDir]”</span></a></div>
|
|
235
|
-
<p>Serve Spec Engine as a Model Context Protocol server over stdio. Tools: <code dir="auto">spec_query</code>, <code dir="auto">spec_resolve</code>, <code dir="auto">spec_req_tags</code>, <code dir="auto">spec_coverage_report</code>, <code dir="auto">spec_check</code>, <code dir="auto">spec_propagation</code>, <code dir="auto">spec_next_id</code>, <code dir="auto">spec_get</code>, <code dir="auto">spec_list</code>, <code dir="auto">spec_supersede</code>, <code dir="auto">spec_deprecate</code>, <code dir="auto">spec_accept</code> — the same JSON the CLI’s <code dir="auto">--json</code> modes emit, reindexed fresh per call. Also advertises the <code dir="auto">author_requirements</code> prompt (a static authoring playbook; the engine itself runs no model).</p>
|
|
238
|
+
<p>Serve Spec Engine as a Model Context Protocol server over stdio. Tools: <code dir="auto">spec_query</code>, <code dir="auto">spec_resolve</code>, <code dir="auto">spec_req_tags</code>, <code dir="auto">spec_coverage_report</code>, <code dir="auto">spec_check</code>, <code dir="auto">spec_propagation</code>, <code dir="auto">spec_next_id</code>, <code dir="auto">spec_get</code>, <code dir="auto">spec_list</code>, <code dir="auto">spec_req</code>, <code dir="auto">spec_amend</code>, <code dir="auto">spec_supersede</code>, <code dir="auto">spec_move</code>, <code dir="auto">spec_deprecate</code>, <code dir="auto">spec_accept</code> — the same JSON the CLI’s <code dir="auto">--json</code> modes emit, reindexed fresh per call. Also advertises the <code dir="auto">author_requirements</code> prompt (a static authoring playbook; the engine itself runs no model).</p>
|
|
236
239
|
<p><strong>Exit codes:</strong> 0 / 2</p> </div> <footer class="sl-flex astro-5h22m7ac"> <div class="meta sl-flex astro-5h22m7ac"> <a href="https://github.com/spec-engine/spec-engine/edit/main/packages/site/src/content/docs/commands.md" class="sl-flex print:hidden astro-txs4wy42"><svg aria-hidden="true" class="astro-txs4wy42 astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.2em;"><path d="M22 7.24a1 1 0 0 0-.29-.71l-4.24-4.24a1 1 0 0 0-1.1-.22 1 1 0 0 0-.32.22l-2.83 2.83L2.29 16.05a1 1 0 0 0-.29.71V21a1 1 0 0 0 1 1h4.24a1 1 0 0 0 .76-.29l10.87-10.93L21.71 8c.1-.1.17-.2.22-.33a1 1 0 0 0 0-.24v-.14l.07-.05ZM6.83 20H4v-2.83l9.93-9.93 2.83 2.83L6.83 20ZM18.17 8.66l-2.83-2.83 1.42-1.41 2.82 2.82-1.41 1.42Z"/></svg>Edit page</a> </div> <div class="pagination-links print:hidden astro-7kn6nbmm" dir="ltr"> <a href="/platform-mapping/" rel="prev" class="astro-7kn6nbmm"> <svg aria-hidden="true" class="astro-7kn6nbmm astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M17 11H9.41l3.3-3.29a1.004 1.004 0 1 0-1.42-1.42l-5 5a1 1 0 0 0-.21.33 1 1 0 0 0 0 .76 1 1 0 0 0 .21.33l5 5a1.002 1.002 0 0 0 1.639-.325 1 1 0 0 0-.219-1.095L9.41 13H17a1 1 0 0 0 0-2Z"/></svg> <span class="astro-7kn6nbmm"> Previous <br class="astro-7kn6nbmm"> <span class="link-title astro-7kn6nbmm">Platform Mapping</span> </span> </a> <a href="/format/" rel="next" class="astro-7kn6nbmm"> <svg aria-hidden="true" class="astro-7kn6nbmm astro-lm5mbbcm" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M17.92 11.62a1.001 1.001 0 0 0-.21-.33l-5-5a1.003 1.003 0 1 0-1.42 1.42l3.3 3.29H7a1 1 0 0 0 0 2h7.59l-3.3 3.29a1.002 1.002 0 0 0 .325 1.639 1 1 0 0 0 1.095-.219l5-5a1 1 0 0 0 .21-.33 1 1 0 0 0 0-.76Z"/></svg> <span class="astro-7kn6nbmm"> Next <br class="astro-7kn6nbmm"> <span class="link-title astro-7kn6nbmm">SPEC.json Format</span> </span> </a> </div> </footer> </div> </div> </main> </div> </div> </div> </div> </body></html>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.5.2","languages":{"en":{"hash":"
|
|
1
|
+
{"version":"1.5.2","languages":{"en":{"hash":"en_dac6a68e28","wasm":"en","page_count":12}},"include_characters":["_","‿","⁀","⁔","︳","︴","﹍","﹎","﹏","_"]}
|
|
Binary file
|
package/dist/impl.js
CHANGED
|
@@ -20,7 +20,7 @@ var package_default;
|
|
|
20
20
|
var init_package = __esm(() => {
|
|
21
21
|
package_default = {
|
|
22
22
|
name: "@spec-engine/spec-engine",
|
|
23
|
-
version: "0.7.
|
|
23
|
+
version: "0.7.1",
|
|
24
24
|
description: "Cross-repo spec engine \u2014 durable requirements with permanent IDs, bound to code via @spec tags, checked in CI. Ships the spec CLI, the local coverage webapp (spec serve), and offline docs (spec docs).",
|
|
25
25
|
license: "MIT",
|
|
26
26
|
type: "module",
|
|
@@ -414,8 +414,25 @@ var init_config = __esm(() => {
|
|
|
414
414
|
});
|
|
415
415
|
|
|
416
416
|
// ../shared/src/ears.ts
|
|
417
|
-
function reject(problem) {
|
|
418
|
-
return { ok: false, problem, expected
|
|
417
|
+
function reject(problem, expected = SHAPE_EXPECTED) {
|
|
418
|
+
return { ok: false, problem, expected };
|
|
419
|
+
}
|
|
420
|
+
function onePromiseProblem(statement, response) {
|
|
421
|
+
if (/;\s/.test(response)) {
|
|
422
|
+
return 'joins a second clause with "; " after "shall" \u2014 split it into its own requirement';
|
|
423
|
+
}
|
|
424
|
+
if (/,\s+and\s+(?!shall\b)\S[^,;]*?\s+shall\b/i.test(response)) {
|
|
425
|
+
return 'joins a second clause with its own subject (", and <subject> shall") \u2014 split it into its own requirement';
|
|
426
|
+
}
|
|
427
|
+
const rationale = statement.match(RATIONALE_RE);
|
|
428
|
+
if (rationale) {
|
|
429
|
+
return `carries a rationale ("${rationale[1]}") \u2014 the reason belongs in the why field`;
|
|
430
|
+
}
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
function lint(clauses, statement) {
|
|
434
|
+
const problem = onePromiseProblem(statement, clauses.response);
|
|
435
|
+
return problem === null ? { ok: true, clauses } : reject(problem, ONE_PROMISE_EXPECTED);
|
|
419
436
|
}
|
|
420
437
|
function parseEarsStatement(raw) {
|
|
421
438
|
const statement = raw.trim();
|
|
@@ -430,15 +447,12 @@ function parseEarsStatement(raw) {
|
|
|
430
447
|
const m2 = statement.match(re);
|
|
431
448
|
if (m2) {
|
|
432
449
|
const [, condition = "", system = "", response = ""] = m2;
|
|
433
|
-
return {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
response: response.trim()
|
|
440
|
-
}
|
|
441
|
-
};
|
|
450
|
+
return lint({
|
|
451
|
+
pattern,
|
|
452
|
+
condition: condition.trim(),
|
|
453
|
+
system: system.trim().replace(/^the\s+/i, ""),
|
|
454
|
+
response: response.trim()
|
|
455
|
+
}, statement);
|
|
442
456
|
}
|
|
443
457
|
if (keyword === "If" && !/,\s*then\s/.test(statement)) {
|
|
444
458
|
return reject('starts with "If" but has no ", then" \u2014 the failure clause never hands over to the system');
|
|
@@ -451,19 +465,16 @@ function parseEarsStatement(raw) {
|
|
|
451
465
|
if (/,/.test(system)) {
|
|
452
466
|
return reject('the clause before "shall" contains a comma but starts with no condition keyword (When/While/If/Where) \u2014 either drop the comma or lead with the condition');
|
|
453
467
|
}
|
|
454
|
-
return {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
response: response.trim()
|
|
461
|
-
}
|
|
462
|
-
};
|
|
468
|
+
return lint({
|
|
469
|
+
pattern: "always",
|
|
470
|
+
condition: null,
|
|
471
|
+
system: system.trim().replace(/^the\s+/i, ""),
|
|
472
|
+
response: response.trim()
|
|
473
|
+
}, statement);
|
|
463
474
|
}
|
|
464
475
|
return reject('nothing follows "shall" \u2014 the observable result is missing');
|
|
465
476
|
}
|
|
466
|
-
var EARS_EXPECTED, CONDITIONAL, ALWAYS_RE;
|
|
477
|
+
var EARS_EXPECTED, CONDITIONAL, ALWAYS_RE, SHAPE_EXPECTED, ONE_PROMISE_EXPECTED = "Expected one promise per statement: a second behavior is its own requirement, and the reason goes in the why.", RATIONALE_RE;
|
|
467
478
|
var init_ears = __esm(() => {
|
|
468
479
|
EARS_EXPECTED = [
|
|
469
480
|
'When <trigger>, the <system> shall <result> \u2014 e.g. "When an unknown id is passed, spec resolve shall print [] and exit 0."',
|
|
@@ -480,6 +491,9 @@ var init_ears = __esm(() => {
|
|
|
480
491
|
{ keyword: "Where", pattern: "where", re: /^Where\s+(.+?),\s*(.+?)\s+shall\s+(.+)$/s }
|
|
481
492
|
];
|
|
482
493
|
ALWAYS_RE = /^(.+?)\s+shall\s+(.+)$/s;
|
|
494
|
+
SHAPE_EXPECTED = `Expected one of:
|
|
495
|
+
${EARS_EXPECTED}`;
|
|
496
|
+
RATIONALE_RE = /\b(because|so that|in order to)\b/i;
|
|
483
497
|
});
|
|
484
498
|
|
|
485
499
|
// ../shared/src/flags.ts
|
|
@@ -670,7 +684,8 @@ CREATE INDEX IF NOT EXISTS idx_term_citations_term ON term_citations(term_id);
|
|
|
670
684
|
-- supersedes-via | amends-via), validated at parse time, never as a DB
|
|
671
685
|
-- constraint. Exactly one index, on req_id; never on issue_id, which would
|
|
672
686
|
-- treat the opaque payload as a lookup key.
|
|
673
|
-
-- @spec PROV-
|
|
687
|
+
-- @spec PROV-005
|
|
688
|
+
-- @spec PROV-006
|
|
674
689
|
CREATE TABLE IF NOT EXISTS provenance (
|
|
675
690
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
676
691
|
req_id TEXT NOT NULL,
|
|
@@ -3790,7 +3805,7 @@ var init_proven = __esm(() => {
|
|
|
3790
3805
|
|
|
3791
3806
|
// src/base/gitBase.ts
|
|
3792
3807
|
function isSafeRef(ref) {
|
|
3793
|
-
return /^[A-Za-z0-9._/-]
|
|
3808
|
+
return /^[A-Za-z0-9._/][A-Za-z0-9._/-]*$/.test(ref);
|
|
3794
3809
|
}
|
|
3795
3810
|
function gitRepoPrefix(platformDir) {
|
|
3796
3811
|
const proc = Bun.spawnSync(["git", "rev-parse", "--show-prefix"], { cwd: platformDir });
|
|
@@ -3828,7 +3843,7 @@ function gitLsTree(platformDir, ref, dir) {
|
|
|
3828
3843
|
function gitDiffNameStatus(platformDir, ref) {
|
|
3829
3844
|
if (!isSafeRef(ref))
|
|
3830
3845
|
return [];
|
|
3831
|
-
const proc = Bun.spawnSync(["git", "diff", "--name-status", "--no-renames", "--relative", ref], {
|
|
3846
|
+
const proc = Bun.spawnSync(["git", "diff", "--name-status", "--no-renames", "--relative", ref, "--"], {
|
|
3832
3847
|
cwd: platformDir
|
|
3833
3848
|
});
|
|
3834
3849
|
if (proc.exitCode !== 0)
|
|
@@ -4136,8 +4151,7 @@ function rowsForDomain2(key, domain2) {
|
|
|
4136
4151
|
source_file: specPaths("", key).rel,
|
|
4137
4152
|
line: found >= 0 ? found + 1 : 0,
|
|
4138
4153
|
req_id: req.id,
|
|
4139
|
-
detail: `${result.problem}.
|
|
4140
|
-
${result.expected}`
|
|
4154
|
+
detail: `${result.problem}. ${result.expected}`
|
|
4141
4155
|
});
|
|
4142
4156
|
}
|
|
4143
4157
|
return rows;
|
|
@@ -4832,8 +4846,7 @@ async function judgeStatementGrammar(platformDir, key, statement) {
|
|
|
4832
4846
|
severity: cfg.severity,
|
|
4833
4847
|
key,
|
|
4834
4848
|
message: `the statement does not match the domain's EARS shape \u2014 ${result.problem}.
|
|
4835
|
-
`
|
|
4836
|
-
${result.expected}`
|
|
4849
|
+
${result.expected}`
|
|
4837
4850
|
};
|
|
4838
4851
|
}
|
|
4839
4852
|
function grammarWarningText(cmdName, v) {
|
|
@@ -5718,10 +5731,34 @@ async function newDomain(platformDir, key, options = {}) {
|
|
|
5718
5731
|
async function listDomains(platformDir) {
|
|
5719
5732
|
return { ok: true, domains: await domainsWithScope(platformDir) };
|
|
5720
5733
|
}
|
|
5734
|
+
async function setDomainGrammar(platformDir, key, options) {
|
|
5735
|
+
if (options.grammar === undefined && options.grammarSeverity === undefined) {
|
|
5736
|
+
return fail("usage", "pass --grammar, --severity, or both");
|
|
5737
|
+
}
|
|
5738
|
+
const file = await readEnvelope(platformDir, key);
|
|
5739
|
+
if (!file.ok)
|
|
5740
|
+
return file;
|
|
5741
|
+
const domain2 = { ...file.domain, ...definedFields(options) };
|
|
5742
|
+
const res = await validateAndWrite(file.specPath, domain2, file.relFile);
|
|
5743
|
+
if (!res.ok) {
|
|
5744
|
+
return fail("invalid_domain_file", res.diagnostics.map((d) => d.detail).join(`
|
|
5745
|
+
`), {
|
|
5746
|
+
diagnostics: res.diagnostics
|
|
5747
|
+
});
|
|
5748
|
+
}
|
|
5749
|
+
return {
|
|
5750
|
+
ok: true,
|
|
5751
|
+
key,
|
|
5752
|
+
grammar: domain2.grammar,
|
|
5753
|
+
grammarSeverity: domain2.grammarSeverity,
|
|
5754
|
+
file: file.relFile
|
|
5755
|
+
};
|
|
5756
|
+
}
|
|
5721
5757
|
var init_domain2 = __esm(() => {
|
|
5722
5758
|
init_src();
|
|
5723
5759
|
init_domains();
|
|
5724
5760
|
init_constants();
|
|
5761
|
+
init__envelope();
|
|
5725
5762
|
init__result();
|
|
5726
5763
|
});
|
|
5727
5764
|
|
|
@@ -5730,6 +5767,7 @@ var exports_domain = {};
|
|
|
5730
5767
|
__export(exports_domain, {
|
|
5731
5768
|
domainNewCommand: () => domainNewCommand,
|
|
5732
5769
|
domainListCommand: () => domainListCommand,
|
|
5770
|
+
domainGrammarCommand: () => domainGrammarCommand,
|
|
5733
5771
|
domainCommand: () => domainCommand
|
|
5734
5772
|
});
|
|
5735
5773
|
import { defineCommand as defineCommand4 } from "citty";
|
|
@@ -5740,7 +5778,15 @@ function exitOnNewFailure(failure) {
|
|
|
5740
5778
|
}
|
|
5741
5779
|
exitOnFailure("spec domain new", failure);
|
|
5742
5780
|
}
|
|
5743
|
-
|
|
5781
|
+
function enumFlag(flag, value, allowed) {
|
|
5782
|
+
if (value === undefined)
|
|
5783
|
+
return;
|
|
5784
|
+
if (allowed.includes(value))
|
|
5785
|
+
return value;
|
|
5786
|
+
console.error(`spec domain grammar: --${flag} must be one of ${allowed.join(", ")}`);
|
|
5787
|
+
process.exit(EXIT.USAGE);
|
|
5788
|
+
}
|
|
5789
|
+
var domainNewCommand, domainListCommand, GRAMMARS, SEVERITIES, domainGrammarCommand, domainCommand;
|
|
5744
5790
|
var init_domain3 = __esm(() => {
|
|
5745
5791
|
init_domains();
|
|
5746
5792
|
init_constants();
|
|
@@ -5808,6 +5854,55 @@ var init_domain3 = __esm(() => {
|
|
|
5808
5854
|
}
|
|
5809
5855
|
}
|
|
5810
5856
|
});
|
|
5857
|
+
GRAMMARS = ["ears", "freeform"];
|
|
5858
|
+
SEVERITIES = ["warning", "error"];
|
|
5859
|
+
domainGrammarCommand = defineCommand4({
|
|
5860
|
+
meta: {
|
|
5861
|
+
name: "grammar",
|
|
5862
|
+
description: "Set a domain's statement grammar and its severity"
|
|
5863
|
+
},
|
|
5864
|
+
args: {
|
|
5865
|
+
name: {
|
|
5866
|
+
type: "positional",
|
|
5867
|
+
required: true,
|
|
5868
|
+
description: "Domain key (case-insensitive)"
|
|
5869
|
+
},
|
|
5870
|
+
platformDir: platformDirArg,
|
|
5871
|
+
grammar: {
|
|
5872
|
+
type: "string",
|
|
5873
|
+
description: "ears | freeform"
|
|
5874
|
+
},
|
|
5875
|
+
severity: {
|
|
5876
|
+
type: "string",
|
|
5877
|
+
description: "warning | error \u2014 how STATEMENT_GRAMMAR and write-time judging treat a miss"
|
|
5878
|
+
},
|
|
5879
|
+
json: {
|
|
5880
|
+
type: "boolean",
|
|
5881
|
+
description: "Print { key, grammar, grammar_severity, file }"
|
|
5882
|
+
}
|
|
5883
|
+
},
|
|
5884
|
+
async run({ args }) {
|
|
5885
|
+
const platformDir = resolvePlatformDir(args);
|
|
5886
|
+
const grammar = enumFlag("grammar", args.grammar, GRAMMARS);
|
|
5887
|
+
const grammarSeverity = enumFlag("severity", args.severity, SEVERITIES);
|
|
5888
|
+
const result = await setDomainGrammar(platformDir, normalizeDomainKey(args.name), {
|
|
5889
|
+
grammar,
|
|
5890
|
+
grammarSeverity
|
|
5891
|
+
});
|
|
5892
|
+
if (!result.ok)
|
|
5893
|
+
exitOnFailure("spec domain grammar", result);
|
|
5894
|
+
if (args.json) {
|
|
5895
|
+
console.log(JSON.stringify({
|
|
5896
|
+
key: result.key,
|
|
5897
|
+
grammar: result.grammar ?? null,
|
|
5898
|
+
grammar_severity: result.grammarSeverity ?? null,
|
|
5899
|
+
file: result.file
|
|
5900
|
+
}));
|
|
5901
|
+
return;
|
|
5902
|
+
}
|
|
5903
|
+
console.log(`${result.file}: grammar ${result.grammar ?? "freeform"}, severity ${result.grammarSeverity ?? "warning"}`);
|
|
5904
|
+
}
|
|
5905
|
+
});
|
|
5811
5906
|
domainCommand = defineCommand4({
|
|
5812
5907
|
meta: {
|
|
5813
5908
|
name: "domain",
|
|
@@ -5815,7 +5910,8 @@ var init_domain3 = __esm(() => {
|
|
|
5815
5910
|
},
|
|
5816
5911
|
subCommands: {
|
|
5817
5912
|
new: domainNewCommand,
|
|
5818
|
-
list: domainListCommand
|
|
5913
|
+
list: domainListCommand,
|
|
5914
|
+
grammar: domainGrammarCommand
|
|
5819
5915
|
}
|
|
5820
5916
|
});
|
|
5821
5917
|
});
|
|
@@ -8440,6 +8536,20 @@ function jsonResult(value) {
|
|
|
8440
8536
|
function errorResult(message) {
|
|
8441
8537
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
8442
8538
|
}
|
|
8539
|
+
function amendFieldsOf(args) {
|
|
8540
|
+
const fields = {};
|
|
8541
|
+
if (args.text !== undefined)
|
|
8542
|
+
fields.statement = args.text;
|
|
8543
|
+
if (args.why !== undefined)
|
|
8544
|
+
fields.why = args.why || null;
|
|
8545
|
+
if (args.lives !== undefined)
|
|
8546
|
+
fields.livesIn = args.lives;
|
|
8547
|
+
if (args.relates !== undefined)
|
|
8548
|
+
fields.relates = args.relates;
|
|
8549
|
+
if (args.issue)
|
|
8550
|
+
fields.issue = args.issue;
|
|
8551
|
+
return fields;
|
|
8552
|
+
}
|
|
8443
8553
|
function buildMcpServer(platformDir) {
|
|
8444
8554
|
const server = new McpServer({ name: "spec", version: package_default.version });
|
|
8445
8555
|
const fresh = (fn) => withIndex({ platformDir, build: "fresh" }, (h) => fn(h.storage));
|
|
@@ -8593,6 +8703,94 @@ function buildMcpServer(platformDir) {
|
|
|
8593
8703
|
const r = await accept({ platformDir, id: req_id });
|
|
8594
8704
|
return r.ok ? jsonResult({ id: r.id, file: r.file }) : errorResult(r.detail);
|
|
8595
8705
|
});
|
|
8706
|
+
server.registerTool("spec_req", {
|
|
8707
|
+
title: "Author a requirement",
|
|
8708
|
+
description: "Append a new requirement to a domain (Active, or Draft with draft: true). The same JSON as `spec req --text \u2026 --json`: { id, file }, plus clauses when the statement parses as EARS.",
|
|
8709
|
+
inputSchema: {
|
|
8710
|
+
domain: z4.string().min(1).describe("Domain key or unique case-insensitive prefix"),
|
|
8711
|
+
text: z4.string().trim().min(1).describe("The requirement statement"),
|
|
8712
|
+
why: z4.string().trim().default("").describe("What breaks without it"),
|
|
8713
|
+
lives: z4.array(z4.string()).default([]).describe("livesIn paths"),
|
|
8714
|
+
relates: z4.array(z4.string()).default([]).describe("Related requirement ids, each comma-split"),
|
|
8715
|
+
issue: z4.string().trim().optional().describe("Originating ticket, recorded as created"),
|
|
8716
|
+
draft: z4.boolean().default(false).describe("Author as Draft instead of Active")
|
|
8717
|
+
}
|
|
8718
|
+
}, async ({ domain: domain2, text, why, lives, relates, issue, draft }) => {
|
|
8719
|
+
const allocated = await nextId(platformDir, domain2);
|
|
8720
|
+
if (!allocated.ok)
|
|
8721
|
+
return errorResult(allocated.detail);
|
|
8722
|
+
const r = await mint({
|
|
8723
|
+
platformDir,
|
|
8724
|
+
key: allocated.key,
|
|
8725
|
+
statement: text,
|
|
8726
|
+
why,
|
|
8727
|
+
livesIn: lives,
|
|
8728
|
+
relates,
|
|
8729
|
+
issue: issue || undefined,
|
|
8730
|
+
status: draft ? "draft" : "active"
|
|
8731
|
+
});
|
|
8732
|
+
if (!r.ok)
|
|
8733
|
+
return errorResult(r.detail);
|
|
8734
|
+
for (const w of r.warnings)
|
|
8735
|
+
console.error(`spec_req: ${w.text}`);
|
|
8736
|
+
return jsonResult(r.clauses ? { id: r.id, file: r.file, clauses: r.clauses } : { id: r.id, file: r.file });
|
|
8737
|
+
});
|
|
8738
|
+
server.registerTool("spec_amend", {
|
|
8739
|
+
title: "Amend a requirement in place",
|
|
8740
|
+
description: "Revise an Active or Draft requirement's fields in place (same id, no version change); a shipped requirement's statement and why are superseded instead. The same JSON as `spec amend --json`.",
|
|
8741
|
+
inputSchema: {
|
|
8742
|
+
id: z4.string().describe("The requirement id to amend (KEY-NNN)"),
|
|
8743
|
+
text: z4.string().trim().min(1).optional().describe("New statement"),
|
|
8744
|
+
why: z4.string().trim().optional().describe("New why; empty clears it"),
|
|
8745
|
+
lives: z4.array(z4.string()).optional().describe("Replacement livesIn list; empty clears"),
|
|
8746
|
+
relates: z4.array(z4.string()).optional().describe("Replacement related ids, each comma-split; empty clears"),
|
|
8747
|
+
issue: z4.string().trim().optional().describe("Ticket recorded as amends-via")
|
|
8748
|
+
}
|
|
8749
|
+
}, async ({ id, text, why, lives, relates, issue }) => {
|
|
8750
|
+
if (!ID_RE.test(id))
|
|
8751
|
+
return errorResult(`id must be a requirement id (KEY-NNN); got ${id}`);
|
|
8752
|
+
const fields = amendFieldsOf({ text, why, lives, relates, issue });
|
|
8753
|
+
if (Object.keys(fields).length === 0) {
|
|
8754
|
+
return errorResult("nothing to amend \u2014 pass at least one of text, why, lives, relates");
|
|
8755
|
+
}
|
|
8756
|
+
const r = await amend({ platformDir, id, fields });
|
|
8757
|
+
if (!r.ok)
|
|
8758
|
+
return errorResult(r.detail);
|
|
8759
|
+
for (const w of r.warnings)
|
|
8760
|
+
console.error(`spec_amend: ${w.text}`);
|
|
8761
|
+
return jsonResult({ id, file: r.file, fields_changed: r.fieldsChanged });
|
|
8762
|
+
});
|
|
8763
|
+
server.registerTool("spec_move", {
|
|
8764
|
+
title: "Move a requirement to another domain",
|
|
8765
|
+
description: "Mint the successor in newDomain carrying the source's fields, mark the source superseded, and return the retag worklist. The same JSON as `spec move --json`.",
|
|
8766
|
+
inputSchema: {
|
|
8767
|
+
id: z4.string().describe("The Active requirement id to move (KEY-NNN)"),
|
|
8768
|
+
newDomain: z4.string().min(1).describe("The existing target domain key"),
|
|
8769
|
+
text: z4.string().optional().describe("Successor statement (default: copied)"),
|
|
8770
|
+
why: z4.string().optional().describe("Successor why (default: copied)"),
|
|
8771
|
+
lives: z4.array(z4.string()).optional().describe("Successor livesIn list; empty clears (default: copied)")
|
|
8772
|
+
}
|
|
8773
|
+
}, async ({ id, newDomain: newDomain2, text, why, lives }) => {
|
|
8774
|
+
if (!ID_RE.test(id))
|
|
8775
|
+
return errorResult(`id must be a requirement id (KEY-NNN); got ${id}`);
|
|
8776
|
+
const targetKey = normalizeDomainKey(newDomain2);
|
|
8777
|
+
if (targetKey === "")
|
|
8778
|
+
return errorResult(`newDomain must be a domain key; got ${newDomain2}`);
|
|
8779
|
+
const r = await move({ platformDir, id, targetKey, statement: text, why, livesIn: lives }, coldFreshTags(platformDir));
|
|
8780
|
+
if (!r.ok)
|
|
8781
|
+
return errorResult(r.detail);
|
|
8782
|
+
for (const w of r.warnings)
|
|
8783
|
+
console.error(`spec_move: ${w.text}`);
|
|
8784
|
+
return jsonResult({
|
|
8785
|
+
old_id: r.oldId,
|
|
8786
|
+
new_id: r.newId,
|
|
8787
|
+
from_file: r.fromFile,
|
|
8788
|
+
to_file: r.toFile,
|
|
8789
|
+
source_spec_version: r.sourceSpecVersion,
|
|
8790
|
+
target_spec_version: r.targetSpecVersion,
|
|
8791
|
+
retag: r.retag
|
|
8792
|
+
});
|
|
8793
|
+
});
|
|
8596
8794
|
server.registerPrompt("author_requirements", {
|
|
8597
8795
|
title: "Author requirements from a brief",
|
|
8598
8796
|
description: "Turn a vague brief/ticket into well-formed Spec Engine requirements: one requirement per testable promise, placed by domain charter, deduped via spec query, drafted to the GUARD template + cold-read rubric, minted with spec req. The engine returns a static playbook template \u2014 your model runs it.",
|
|
@@ -8629,8 +8827,11 @@ var init_mcp = __esm(() => {
|
|
|
8629
8827
|
init_domains();
|
|
8630
8828
|
init__index();
|
|
8631
8829
|
init_accept();
|
|
8830
|
+
init_amend();
|
|
8632
8831
|
init_check();
|
|
8633
8832
|
init_deprecate();
|
|
8833
|
+
init_mint();
|
|
8834
|
+
init_move();
|
|
8634
8835
|
init_nextId();
|
|
8635
8836
|
init_reads2();
|
|
8636
8837
|
init_records();
|
|
@@ -12153,6 +12354,9 @@ function guarded(handler) {
|
|
|
12153
12354
|
try {
|
|
12154
12355
|
return await handler(c);
|
|
12155
12356
|
} catch (err) {
|
|
12357
|
+
if (err instanceof InvalidDomainFilesError) {
|
|
12358
|
+
return c.json({ error: "invalid_domain_file", diagnostics: sortDiagnostics(err.diagnostics) }, 422);
|
|
12359
|
+
}
|
|
12156
12360
|
const info = describeStorageError(err);
|
|
12157
12361
|
if (info === null)
|
|
12158
12362
|
throw err;
|
|
@@ -12404,6 +12608,52 @@ function mountApi(app, storage2, platformDir = process.cwd(), bindHost = DEFAULT
|
|
|
12404
12608
|
}, 201);
|
|
12405
12609
|
});
|
|
12406
12610
|
}));
|
|
12611
|
+
app.post("/api/requirements/:id/move", guarded(async function moveRequirement(c) {
|
|
12612
|
+
if (!featureEnabled("editor")) {
|
|
12613
|
+
return c.json({ error: featureDisabledMessage("editor") }, 404);
|
|
12614
|
+
}
|
|
12615
|
+
const originReject = rejectCrossOrigin(c, bindHost);
|
|
12616
|
+
if (originReject)
|
|
12617
|
+
return originReject;
|
|
12618
|
+
const headerReject = rejectBadWriteHeaders(c);
|
|
12619
|
+
if (headerReject)
|
|
12620
|
+
return headerReject;
|
|
12621
|
+
const parsed = await readJsonBody(c);
|
|
12622
|
+
if (!parsed.ok)
|
|
12623
|
+
return parsed.res;
|
|
12624
|
+
const body = parsed.body;
|
|
12625
|
+
const id = c.req.param("id") ?? "";
|
|
12626
|
+
if (!ID_RE.test(id))
|
|
12627
|
+
return c.json({ error: "id must be a requirement id (KEY-NNN)" }, 400);
|
|
12628
|
+
const targetKey = normalizeDomainKey(typeof body.newDomain === "string" ? body.newDomain : "");
|
|
12629
|
+
if (targetKey === "")
|
|
12630
|
+
return c.json({ error: "newDomain is required (a domain key)" }, 400);
|
|
12631
|
+
return withWriteLock(async () => {
|
|
12632
|
+
const r = await move({
|
|
12633
|
+
platformDir,
|
|
12634
|
+
id,
|
|
12635
|
+
targetKey,
|
|
12636
|
+
statement: typeof body.text === "string" ? body.text : undefined,
|
|
12637
|
+
why: typeof body.why === "string" ? body.why : undefined,
|
|
12638
|
+
livesIn: "lives" in body ? toStringList(body.lives) : undefined
|
|
12639
|
+
}, async (reqId) => {
|
|
12640
|
+
await runIndex({ platformDir, storage: storage2 });
|
|
12641
|
+
return storage2.listTags({ req_id: reqId });
|
|
12642
|
+
});
|
|
12643
|
+
if (!r.ok)
|
|
12644
|
+
return failureResponse(c, r);
|
|
12645
|
+
return c.json({
|
|
12646
|
+
ok: true,
|
|
12647
|
+
old_id: r.oldId,
|
|
12648
|
+
new_id: r.newId,
|
|
12649
|
+
from_file: r.fromFile,
|
|
12650
|
+
to_file: r.toFile,
|
|
12651
|
+
source_spec_version: r.sourceSpecVersion,
|
|
12652
|
+
target_spec_version: r.targetSpecVersion,
|
|
12653
|
+
retag: r.retag
|
|
12654
|
+
}, 201);
|
|
12655
|
+
});
|
|
12656
|
+
}));
|
|
12407
12657
|
app.post("/api/requirements/:id/deprecate", guarded(async function deprecateRequirement(c) {
|
|
12408
12658
|
if (!featureEnabled("editor")) {
|
|
12409
12659
|
return c.json({ error: featureDisabledMessage("editor") }, 404);
|
|
@@ -12542,6 +12792,7 @@ var init_api = __esm(() => {
|
|
|
12542
12792
|
init_amend();
|
|
12543
12793
|
init_deprecate();
|
|
12544
12794
|
init_mint();
|
|
12795
|
+
init_move();
|
|
12545
12796
|
init_reads2();
|
|
12546
12797
|
init_records();
|
|
12547
12798
|
init_supersede();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spec-engine/spec-engine",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Cross-repo spec engine — durable requirements with permanent IDs, bound to code via @spec tags, checked in CI. Ships the spec CLI, the local coverage webapp (spec serve), and offline docs (spec docs).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|