@spec-engine/spec-engine 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -378,7 +378,15 @@
378
378
 
379
379
 
380
380
 
381
- <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, issue</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, a fresh-tags callback</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">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>
381
+
382
+
383
+
384
+
385
+
386
+
387
+
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, 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, a fresh-tags callback</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>
382
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>
383
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>
384
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>
@@ -443,7 +451,7 @@
443
451
  <li>Render text to stdout, or the sorted <code dir="auto">--json</code> array. Warnings and guidance go to stderr.</li>
444
452
  </ol>
445
453
  <div class="sl-heading-wrapper level-h2"><h2 id="write-path">Write path</h2><a class="sl-anchor-link" href="#write-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 “Write path”</span></a></div>
446
- <p><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 term</code>:</p>
454
+ <p><code dir="auto">spec req</code>, <code dir="auto">spec amend</code>, <code dir="auto">spec accept</code>, <code dir="auto">spec supersede</code>, <code dir="auto">spec move</code>, <code dir="auto">spec deprecate</code>, <code dir="auto">spec term</code>:</p>
447
455
  <ol>
448
456
  <li>Resolve the target domain and run every guard before writing a byte.</li>
449
457
  <li>Allocate the next id from the filesystem, never from the index.</li>
@@ -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 &lt;KEY-NNN&gt; [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 &lt;text&gt; [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 &lt;files...&gt; [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 &lt;reqId&gt; [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 &lt;repo&gt; &lt;reqId&gt; [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 &lt;name&gt; / 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 &lt;domain-prefix&gt; [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 &lt;reqId&gt; [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 &lt;reqId&gt; &lt;NEW-DOMAIN&gt; [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 &lt;reqId&gt; [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-serve-platformdir---port-n" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec serve [platformDir] [--port N]</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 &lt;KEY-NNN&gt; [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 &lt;text&gt; [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 &lt;files...&gt; [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 &lt;reqId&gt; [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 &lt;repo&gt; &lt;reqId&gt; [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 &lt;name&gt; / 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 &lt;domain-prefix&gt; [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 &lt;reqId&gt; [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 &lt;reqId&gt; &lt;NEW-DOMAIN&gt; [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 &lt;reqId&gt; [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-serve-platformdir---port-n" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec serve [platformDir] [--port N]</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 &#x3C;command> [args]</code>; from a checkout, as <code dir="auto">bun packages/engine/src/cli.ts &#x3C;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 &lt;KEY-NNN&gt; [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 &lt;text&gt; [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 &lt;files...&gt; [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 &lt;reqId&gt; [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 &lt;repo&gt; &lt;reqId&gt; [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 &lt;name&gt; / 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 &lt;domain-prefix&gt; [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 &lt;reqId&gt; [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 &lt;reqId&gt; &lt;NEW-DOMAIN&gt; [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 &lt;reqId&gt; [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 &lt;reqId&gt; [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-serve-platformdir---port-n" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec serve [platformDir] [--port N]</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 &lt;KEY-NNN&gt; [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 &lt;text&gt; [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 &lt;files...&gt; [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 &lt;reqId&gt; [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 &lt;repo&gt; &lt;reqId&gt; [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 &lt;name&gt; / 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 &lt;domain-prefix&gt; [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 &lt;reqId&gt; [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 &lt;reqId&gt; &lt;NEW-DOMAIN&gt; [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 &lt;reqId&gt; [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 &lt;reqId&gt; [platformDir]</span> </a> </li><li class="astro-xz67lhkg" style="--depth: 1;"> <a href="#spec-serve-platformdir---port-n" class="astro-xz67lhkg" style="--depth: 1;"> <span class="astro-xz67lhkg" style="--depth: 1;">spec serve [platformDir] [--port N]</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 &#x3C;command> [args]</code>; from a checkout, as <code dir="auto">bun packages/engine/src/cli.ts &#x3C;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
 
@@ -197,7 +197,7 @@
197
197
  <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
198
  <p><strong>Exit codes:</strong> 0 / 2</p>
199
199
  <div class="sl-heading-wrapper level-h3"><h3 id="spec-req-domain-prefix-platformdir"><code dir="auto">spec req &#x3C;domain-prefix> [platformDir]</code></h3><a class="sl-anchor-link" href="#spec-req-domain-prefix-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 req &#x3C;domain-prefix> [platformDir]”</span></a></div>
200
- <p><strong>Piped:</strong> prints the next unused requirement ID (e.g. <code dir="auto">BILLING-010</code>) — zero prompts, zero writes. <strong>TTY:</strong> interactive authoring. <strong><code dir="auto">--text</code> (+ <code dir="auto">--why</code> / <code dir="auto">--lives</code>):</strong> appends a born-active entry non-interactively through the validated write seam. Prefix is case-insensitive (<code dir="auto">bil</code> → <code dir="auto">BILLING</code>; ambiguous → exit 2). <code dir="auto">spec req &#x3C;domain></code> alone prints the domain’s charter before authoring.</p>
200
+ <p><strong>Piped:</strong> prints the next unused requirement ID (e.g. <code dir="auto">BILLING-010</code>) — zero prompts, zero writes. <strong>TTY:</strong> interactive authoring. <strong><code dir="auto">--text</code> (+ <code dir="auto">--why</code> / <code dir="auto">--lives</code>):</strong> appends a born-active entry non-interactively through the validated write seam. <strong><code dir="auto">--draft</code>:</strong> the entry is born Draft instead (promote it with <code dir="auto">spec accept</code>). Prefix is case-insensitive (<code dir="auto">bil</code> → <code dir="auto">BILLING</code>; ambiguous → exit 2). <code dir="auto">spec req &#x3C;domain></code> alone prints the domain’s charter before authoring.</p>
201
201
  <p><strong>Exit codes:</strong> 0 / 2</p>
202
202
  <div class="sl-heading-wrapper level-h3"><h3 id="spec-term-"><code dir="auto">spec term …</code></h3><a class="sl-anchor-link" href="#spec-term-"><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 term …”</span></a></div>
203
203
  <p>The glossary authoring surface — a term is a requirement row in the reserved <code dir="auto">TERM</code> domain (definition in the statement, headword in <code dir="auto">term</code>, synonyms in <code dir="auto">aliases</code>):</p>
@@ -221,7 +221,10 @@
221
221
  <p>Cross-domain supersede for taxonomy reorganization: mints the successor as the next id in <code dir="auto">&#x3C;NEW-DOMAIN></code> (which must exist), carrying the source’s fields (<code dir="auto">--text</code>/<code dir="auto">--why</code>/<code dir="auto">--lives</code> override), flips the source to superseded with a cross-domain <code dir="auto">supersededBy</code>, and emits the retag worklist. Both envelopes validate before either is written — a reject never half-applies. Only Active requirements move; moving into the source’s own domain is a usage error (use <code dir="auto">supersede</code>).</p>
222
222
  <p><strong>Exit codes:</strong> 0 / 2</p>
223
223
  <div class="sl-heading-wrapper level-h3"><h3 id="spec-amend-reqid-platformdir"><code dir="auto">spec amend &#x3C;reqId> [platformDir]</code></h3><a class="sl-anchor-link" href="#spec-amend-reqid-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 amend &#x3C;reqId> [platformDir]”</span></a></div>
224
- <p>Revise an unshipped requirement’s fields in place — same ID, no version change (the domain version only derives from supersessions). Field flags name what changes (<code dir="auto">--text</code>/<code dir="auto">--why</code>/<code dir="auto">--lives</code>, at least one); untouched fields stay byte-identical. Only Active and Draft entries amend.</p>
224
+ <p>Revise an unshipped requirement’s fields in place — same ID, no version change (the domain version only derives from supersessions). Field flags name what changes (<code dir="auto">--text</code>/<code dir="auto">--why</code>/<code dir="auto">--lives</code>, at least one); untouched fields stay byte-identical. <code dir="auto">--lives</code> repeats or comma-separates and replaces the whole list; <code dir="auto">--lives ""</code> clears it. Only Active and Draft entries amend.</p>
225
+ <p><strong>Exit codes:</strong> 0 / 2</p>
226
+ <div class="sl-heading-wrapper level-h3"><h3 id="spec-accept-reqid-platformdir"><code dir="auto">spec accept &#x3C;reqId> [platformDir]</code></h3><a class="sl-anchor-link" href="#spec-accept-reqid-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 accept &#x3C;reqId> [platformDir]”</span></a></div>
227
+ <p>Promote a Draft requirement to Active — same ID, every other field untouched, no version change. A Draft is never an orphan, never unverified, and never counted in coverage; accepting it makes it an obligation.</p>
225
228
  <p><strong>Exit codes:</strong> 0 / 2</p>
226
229
  <div class="sl-heading-wrapper level-h3"><h3 id="spec-serve-platformdir---port-n"><code dir="auto">spec serve [platformDir] [--port N]</code></h3><a class="sl-anchor-link" href="#spec-serve-platformdir---port-n"><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 serve [platformDir] [--port N]”</span></a></div>
227
230
  <p>Launch the local webapp over the derived index. Binds <code dir="auto">127.0.0.1</code> only. <code dir="auto">--probe</code> boots on an ephemeral port, smoke-tests, and exits.</p>
@@ -229,5 +232,5 @@
229
232
  <p><strong>Feature-flagged routes:</strong> <code dir="auto">/api/query?q=&#x26;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>
230
233
  <p><strong>Exit codes:</strong> 0 / 1 / 2</p>
231
234
  <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>
232
- <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> — 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>
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>
233
236
  <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>
@@ -1 +1 @@
1
- {"version":"1.5.2","languages":{"en":{"hash":"en_20f81448c5","wasm":"en","page_count":12}},"include_characters":["_","‿","⁀","⁔","︳","︴","﹍","﹎","﹏","_"]}
1
+ {"version":"1.5.2","languages":{"en":{"hash":"en_beb6d752c1","wasm":"en","page_count":12}},"include_characters":["_","‿","⁀","⁔","︳","︴","﹍","﹎","﹏","_"]}
@@ -119,11 +119,12 @@
119
119
  <div class="sl-heading-wrapper level-h2"><h2 id="monorepo-members-and-their-packages">Monorepo members and their packages</h2><a class="sl-anchor-link" href="#monorepo-members-and-their-packages"><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 “Monorepo members and their packages”</span></a></div>
120
120
  <p>platform-map classifies every member as <code dir="auto">single-repo</code> or <code dir="auto">monorepo</code> from its ecosystem’s workspace manifest: <code dir="auto">pnpm-workspace.yaml</code>, <code dir="auto">package.json</code> <code dir="auto">workspaces</code>, <code dir="auto">lerna.json</code>, a <code dir="auto">uv</code> workspace, a Cargo workspace, or <code dir="auto">go.work</code>. A monorepo member’s packages are the directories its workspace manifest lists.</p>
121
121
  <p>Each package is its own coverage column, named by the package’s path relative to the member (<code dir="auto">packages/ui</code>), prefixed by the member name when the member belongs to a declared platform (<code dir="auto">shared/packages/ui</code>). A package inherits its member’s pin unless it carries a nested <code dir="auto">spec-engine.member.json</code>, whose pin and <code dir="auto">ignore</code> then apply to that package alone. That is how one package can sit on <code dir="auto">spec-engine@2</code> while a sibling lags on <code dir="auto">@1</code>.</p>
122
+ <p>A monorepo’s files outside every workspace package (CI scripts, infrastructure, migrations at the repository root) are one more column: named by the member for a declared member, and by the directory for a lone monorepo. That column skips every package path, so no file is scanned twice, and it inherits the member’s <code dir="auto">ignore</code> list. When a workspace lists the repository root itself as a package, the root is already a column and no extra one is added. <!-- @spec INIT-040 --></p>
122
123
  <p>There is no <code dir="auto">members</code> glob in <code dir="auto">spec-engine.member.json</code>. The workspace manifest is the one place a monorepo names its packages.</p>
123
124
  <div class="sl-heading-wrapper level-h2"><h2 id="a-lone-repository">A lone repository</h2><a class="sl-anchor-link" href="#a-lone-repository"><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 “A lone repository”</span></a></div>
124
125
  <p>A repository that holds its own <code dir="auto">spec-engine/</code> tree and is not a declared platform is a lone repository. platform-map maps it as <code dir="auto">single-repo</code> or <code dir="auto">monorepo</code> with one repo entry, and Spec Engine scans that repo without any member config or CI gate. <!-- @spec INIT-035 --></p>
125
- <p>A lone <code dir="auto">single-repo</code> is one coverage column named by the directory name. A lone <code dir="auto">monorepo</code> has one coverage column per workspace package, named by the package’s repo-relative path (<code dir="auto">packages/engine</code>), with no member prefix. Every lone-repository column, and the canonical <code dir="auto">spec-engine</code> row itself, is pinned to the derived platform version, so a repository is never reported drifted against its own working-tree domains. A nested <code dir="auto">spec-engine.member.json</code> inside a package still overrides that package’s pin. <!-- @spec INIT-028 --></p>
126
- <p>This repository is a lone <code dir="auto">monorepo</code>. Its coverage columns are <code dir="auto">packages/engine</code>, <code dir="auto">packages/shared</code>, <code dir="auto">packages/site</code>, <code dir="auto">packages/tracker</code>, <code dir="auto">packages/webapp</code>, and <code dir="auto">scripts</code>, each a workspace package of the root <code dir="auto">package.json</code>.</p>
126
+ <p>A lone <code dir="auto">single-repo</code> is one coverage column named by the directory name. A lone <code dir="auto">monorepo</code> has one coverage column per workspace package, named by the package’s repo-relative path (<code dir="auto">packages/engine</code>), with no member prefix, plus one column named by the directory for its files outside every package. Every lone-repository column, and the canonical <code dir="auto">spec-engine</code> row itself, is pinned to the derived platform version, so a repository is never reported drifted against its own working-tree domains. A nested <code dir="auto">spec-engine.member.json</code> inside a package still overrides that package’s pin. <!-- @spec INIT-028 --></p>
127
+ <p>This repository is a lone <code dir="auto">monorepo</code>. Its coverage columns are <code dir="auto">packages/engine</code>, <code dir="auto">packages/shared</code>, <code dir="auto">packages/site</code>, <code dir="auto">packages/tracker</code>, <code dir="auto">packages/webapp</code>, and <code dir="auto">scripts</code>, each a workspace package of the root <code dir="auto">package.json</code>. It has no root column: a checkout directory named <code dir="auto">spec-engine</code> would collide with the canonical <code dir="auto">spec-engine</code> row, the same limitation a lone single repository of that name has.</p>
127
128
  <div class="sl-heading-wrapper level-h2"><h2 id="names">Names</h2><a class="sl-anchor-link" href="#names"><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 “Names”</span></a></div>
128
129
  <p>One name serves everywhere. For a declared member it is the member name from the platform file. For a package it is the member name and the package path joined by <code dir="auto">/</code>, or the bare package path in a lone monorepo. That same string is:</p>
129
130
  <ul>
@@ -256,6 +257,6 @@
256
257
  <p><code dir="auto">spec check . --ci</code> runs platform-map’s own check underneath, so one gate fails when a requirement, a tag, a pin, or the platform declaration is broken.</p>
257
258
  <div class="sl-heading-wrapper level-h2"><h2 id="propagation-order-dependson">Propagation order: <code dir="auto">dependsOn</code></h2><a class="sl-anchor-link" href="#propagation-order-dependson"><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 “Propagation order: dependsOn”</span></a></div>
258
259
  <p>Every repo and package in the map carries <code dir="auto">dependsOn</code>, the platform’s own package names its manifest depends on, matched within one ecosystem. Spec Engine reads it as the order in which a superseded requirement propagates: a consumer can only move to the successor after the packages it depends on ship it. <code dir="auto">spec propagation</code>, <code dir="auto">/api/propagation/:id</code>, <code dir="auto">spec_propagation</code>, and the webapp’s propagation page list members in dependency order, a member after every member it depends on, and members at the same depth by name. <!-- @spec PROP-006 --></p>
259
- <p>The depth is a coverage-column fact, computed at discovery and stored in the derived index. A single-repo member’s node is its map repo; a monorepo member’s package column, and a lone monorepo’s package column, is its map <code dir="auto">Package</code>. A column depends on another column when the other’s <code dir="auto">packageName</code> appears in its <code dir="auto">dependsOn</code>; a name that is no column (a monorepo root’s own package, an absent member) is no edge. A column with no dependency is depth 0; every other column is one deeper than its deepest dependency. A dependency cycle, and every column downstream of it, sits at one depth after every column outside it, by name.</p>
260
+ <p>The depth is a coverage-column fact, computed at discovery and stored in the derived index. A single-repo member’s node is its map repo; a monorepo member’s package column, and a lone monorepo’s package column, is its map <code dir="auto">Package</code>. A column depends on another column when the other’s <code dir="auto">packageName</code> appears in its <code dir="auto">dependsOn</code>; a name that is no column (an absent member or package) is no edge; a monorepo’s root column carries its root manifest’s name and dependencies. A column with no dependency is depth 0; every other column is one deeper than its deepest dependency. A dependency cycle, and every column downstream of it, sits at one depth after every column outside it, by name.</p>
260
261
  <div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="plaintext"><code><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">$ spec propagation BILLING-002 .</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">REPO STATE VIA DRIFT?</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">shared MIGRATED_VERIFIED — no</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">zed NO_DOMAIN_REFERENCE — no</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">api ON_PREDECESSOR BILLING-001 no</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">web MIGRATED_UNVERIFIED — no</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="$ spec propagation BILLING-002 .REPO STATE VIA DRIFT?shared MIGRATED_VERIFIED — nozed NO_DOMAIN_REFERENCE — noapi ON_PREDECESSOR BILLING-001 noweb MIGRATED_UNVERIFIED — no"><div></div></button></div></figure></div>
261
262
  <p>Here <code dir="auto">api</code> depends on <code dir="auto">shared</code> and <code dir="auto">web</code> on <code dir="auto">api</code>, so <code dir="auto">api</code> is the member to migrate next. A platform with no in-platform dependencies lists members by name, as before.</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/platform-mapping.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="/architecture/" 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">Architecture</span> </span> </a> <a href="/commands/" 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">Commands</span> </span> </a> </div> </footer> </div> </div> </main> </div> </div> </div> </div> </body></html>
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.5.0",
23
+ version: "0.6.0",
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",
@@ -1274,6 +1274,37 @@ async function packageColumns(repoPath, prefix, repo, parentPin) {
1274
1274
  }
1275
1275
  return out;
1276
1276
  }
1277
+ function packageDirs(repo) {
1278
+ const dirs = repo.packages.map((p) => p.path);
1279
+ return dirs.some((d) => d === "" || d === ".") ? null : dirs;
1280
+ }
1281
+ function loneRootColumn(absPlatform, repo, platformVersion) {
1282
+ const dirs = packageDirs(repo);
1283
+ const { dependency_depth: _depth, ...self } = selfMember(absPlatform, platformVersion);
1284
+ if (dirs === null || self.name === CANONICAL_SPECS_DIR)
1285
+ return [];
1286
+ return [{ repo: { ...self, ignore: dirs }, node: repo }];
1287
+ }
1288
+ async function loneMembers(repo, absPlatform, platformVersion) {
1289
+ if (repo === undefined || repo.mode !== "monorepo" || repo.packages.length === 0) {
1290
+ return [selfMember(absPlatform, platformVersion)];
1291
+ }
1292
+ return toRepos([
1293
+ ...await packageColumns(absPlatform, "", repo, platformVersion),
1294
+ ...loneRootColumn(absPlatform, repo, platformVersion)
1295
+ ]);
1296
+ }
1297
+ async function memberColumns(repo, path, cfg) {
1298
+ const pin = extractPin(cfg.specs);
1299
+ if (repo.mode !== "monorepo" || repo.packages.length === 0) {
1300
+ return [column(repo.name, path, pin, repo, cfg.ignore)];
1301
+ }
1302
+ const packages = await packageColumns(path, `${repo.name}/`, repo, pin);
1303
+ const dirs = packageDirs(repo);
1304
+ if (dirs === null)
1305
+ return packages;
1306
+ return [...packages, column(repo.name, path, pin, repo, [...cfg.ignore ?? [], ...dirs])];
1307
+ }
1277
1308
  function describe(d, mapped) {
1278
1309
  const s = d.subject;
1279
1310
  switch (d.code) {
@@ -1306,8 +1337,7 @@ async function membersOf(reading, absPlatform, platformVersion) {
1306
1337
  const { mapped, located } = reading;
1307
1338
  const diagnostics2 = surfaced(mapped);
1308
1339
  if (mapped.mode !== "multi-repo") {
1309
- const repo = mapped.repos[0];
1310
- const members = repo !== undefined && repo.mode === "monorepo" && repo.packages.length > 0 ? toRepos(await packageColumns(absPlatform, "", repo, platformVersion)) : [selfMember(absPlatform, platformVersion)];
1340
+ const members = await loneMembers(mapped.repos[0], absPlatform, platformVersion);
1311
1341
  return { mode: mapped.mode, members, unpinned: [], undeclared: [], diagnostics: diagnostics2 };
1312
1342
  }
1313
1343
  if (!mapped.declared) {
@@ -1325,13 +1355,7 @@ async function membersOf(reading, absPlatform, platformVersion) {
1325
1355
  unpinned.push({ name: repo.name, path });
1326
1356
  continue;
1327
1357
  }
1328
- const cfg = await readRepoConfig(configPath);
1329
- const pin = extractPin(cfg.specs);
1330
- if (repo.mode === "monorepo" && repo.packages.length > 0) {
1331
- columns.push(...await packageColumns(path, `${repo.name}/`, repo, pin));
1332
- } else {
1333
- columns.push(column(repo.name, path, pin, repo, cfg.ignore));
1334
- }
1358
+ columns.push(...await memberColumns(repo, path, await readRepoConfig(configPath)));
1335
1359
  }
1336
1360
  const undeclared = mapped.diagnostics.filter((d) => d.code === "UNLISTED_REPO").map((d) => ({ name: d.subject, path: join3(absPlatform, d.subject) }));
1337
1361
  return { mode: "multi-repo", members: toRepos(columns), unpinned, undeclared, diagnostics: diagnostics2 };
@@ -3314,7 +3338,24 @@ import { resolve as resolve6 } from "path";
3314
3338
  function resolvePlatformDir(args) {
3315
3339
  return resolve6(args.platformDir ?? process.cwd());
3316
3340
  }
3317
- var platformDirArg, outArg, jsonArg, freshArg, noPromptArg;
3341
+ function listFlag(rawArgs, name, parsed) {
3342
+ const flag = `--${name}`;
3343
+ const raw = [];
3344
+ for (let i = 0;i < rawArgs.length; i++) {
3345
+ const arg = rawArgs[i];
3346
+ if (arg === flag && i + 1 < rawArgs.length)
3347
+ raw.push(rawArgs[++i]);
3348
+ else if (arg.startsWith(`${flag}=`))
3349
+ raw.push(arg.slice(flag.length + 1));
3350
+ }
3351
+ if (raw.length === 0) {
3352
+ if (parsed === undefined)
3353
+ return;
3354
+ raw.push(parsed);
3355
+ }
3356
+ return raw.flatMap((v) => v.split(",")).map((v) => v.trim()).filter((v) => v !== "");
3357
+ }
3358
+ var platformDirArg, outArg, jsonArg, freshArg, noPromptArg, livesArg;
3318
3359
  var init__args = __esm(() => {
3319
3360
  init_constants();
3320
3361
  platformDirArg = {
@@ -3335,6 +3376,10 @@ var init__args = __esm(() => {
3335
3376
  type: "boolean",
3336
3377
  description: "Suppress the interactive onboarding prompt for siblings missing spec-engine.member.json (they surface as NO_SPEC_CONFIG warnings)"
3337
3378
  };
3379
+ livesArg = {
3380
+ type: "string",
3381
+ description: "Lives in (livesIn) paths: repeat the flag or comma-separate; an empty value clears the list"
3382
+ };
3338
3383
  });
3339
3384
 
3340
3385
  // src/commands/index.ts
@@ -6448,24 +6493,27 @@ __export(exports_req, {
6448
6493
  });
6449
6494
  import { createInterface as createInterface2 } from "readline";
6450
6495
  import { defineCommand as defineCommand12 } from "citty";
6451
- async function mintFromFlags(platformDir, key, textFlag, args) {
6496
+ async function mintFromFlags(platformDir, key, textFlag, args, rawArgs) {
6452
6497
  const statement = textFlag.trim();
6453
6498
  if (statement === "") {
6454
6499
  console.error("spec req: --text must be a non-empty Requirement");
6455
6500
  process.exit(EXIT.USAGE);
6456
6501
  }
6457
6502
  await printResolvedCharter(platformDir, key);
6458
- const lives = (args.lives ?? "").trim();
6459
6503
  await mintAndReport(platformDir, key, {
6460
6504
  statement,
6461
6505
  why: (args.why ?? "").trim(),
6462
- livesIn: lives ? [lives] : [],
6506
+ livesIn: listFlag(rawArgs, "lives", args.lives) ?? [],
6463
6507
  issue: (args.issue ?? "").trim() || undefined,
6508
+ status: statusOf(args),
6464
6509
  json: Boolean(args.json)
6465
6510
  });
6466
6511
  }
6467
- async function mintInteractively(platformDir, key, id) {
6468
- console.error(`Authoring ${id} \u2014 Active`);
6512
+ function statusOf(args) {
6513
+ return args.draft ? "draft" : "active";
6514
+ }
6515
+ async function mintInteractively(platformDir, key, id, status) {
6516
+ console.error(`Authoring ${id} \u2014 ${status === "draft" ? "Draft" : "Active"}`);
6469
6517
  await printResolvedCharter(platformDir, key);
6470
6518
  const requirement = (await askLine("Requirement: ")).trim();
6471
6519
  if (requirement === "") {
@@ -6474,12 +6522,13 @@ async function mintInteractively(platformDir, key, id) {
6474
6522
  return;
6475
6523
  }
6476
6524
  const why = (await askLine("Why it matters: ")).trim();
6477
- const lives = (await askLine("Lives in: ")).trim();
6525
+ const livesIn = listFlag([], "lives", await askLine("Lives in: ")) ?? [];
6478
6526
  await mintAndReport(platformDir, key, {
6479
6527
  statement: requirement,
6480
6528
  why,
6481
- livesIn: lives ? [lives] : [],
6529
+ livesIn,
6482
6530
  issue: undefined,
6531
+ status,
6483
6532
  json: false
6484
6533
  });
6485
6534
  }
@@ -6532,10 +6581,14 @@ var init_req = __esm(() => {
6532
6581
  description: "Non-interactive authoring: the Requirement field. When set, the entry appends with zero prompts."
6533
6582
  },
6534
6583
  why: { type: "string", description: "Why it matters field (only with --text)" },
6535
- lives: { type: "string", description: "Lives in field (only with --text)" },
6584
+ lives: livesArg,
6536
6585
  issue: {
6537
6586
  type: "string",
6538
6587
  description: "Originating ticket id, recorded as created-provenance on the entry (only with --text; opaque \u2014 never a requirement id)"
6588
+ },
6589
+ draft: {
6590
+ type: "boolean",
6591
+ description: "Author the entry as Draft instead of Active (promote it later with spec accept)"
6539
6592
  }
6540
6593
  };
6541
6594
  reqCommand = defineCommand12({
@@ -6544,7 +6597,7 @@ var init_req = __esm(() => {
6544
6597
  description: "Author a new requirement interactively (TTY) or print the next unused id (non-TTY)"
6545
6598
  },
6546
6599
  args: reqArgs,
6547
- async run({ args }) {
6600
+ async run({ args, rawArgs }) {
6548
6601
  const input = args.domainPrefix;
6549
6602
  const platformDir = resolvePlatformDir(args);
6550
6603
  try {
@@ -6558,7 +6611,7 @@ var init_req = __esm(() => {
6558
6611
  const { key, nextId: id } = allocated;
6559
6612
  const textFlag = args.text;
6560
6613
  if (textFlag !== undefined) {
6561
- await mintFromFlags(platformDir, key, textFlag, args);
6614
+ await mintFromFlags(platformDir, key, textFlag, args, rawArgs);
6562
6615
  return;
6563
6616
  }
6564
6617
  const hasFieldFlags = typeof args.why === "string" || typeof args.lives === "string" || typeof args.issue === "string";
@@ -6567,6 +6620,11 @@ var init_req = __esm(() => {
6567
6620
  process.exit(EXIT.USAGE);
6568
6621
  return;
6569
6622
  }
6623
+ if (args.draft && (args.json || !process.stdin.isTTY)) {
6624
+ console.error("spec req: --draft authors an entry \u2014 pass --text, or run on a TTY");
6625
+ process.exit(EXIT.USAGE);
6626
+ return;
6627
+ }
6570
6628
  if (args.json) {
6571
6629
  console.log(JSON.stringify({ domain: key, next_id: id }));
6572
6630
  return;
@@ -6575,7 +6633,7 @@ var init_req = __esm(() => {
6575
6633
  console.log(id);
6576
6634
  return;
6577
6635
  }
6578
- await mintInteractively(platformDir, key, id);
6636
+ await mintInteractively(platformDir, key, id, statusOf(args));
6579
6637
  }
6580
6638
  });
6581
6639
  });
@@ -7122,14 +7180,13 @@ async function resolveSuccessorText(args, platformDir, id) {
7122
7180
  function splitAliases(raw) {
7123
7181
  return raw.split(",").map((s) => s.trim()).filter((s) => s !== "");
7124
7182
  }
7125
- function inputFromArgs(args, platformDir, id, statement) {
7183
+ function inputFromArgs(args, rawArgs, platformDir, id, statement) {
7126
7184
  const input = { platformDir, id, statement, noBump: Boolean(args.noBump) };
7127
7185
  if (typeof args.why === "string")
7128
7186
  input.why = args.why;
7129
- if (typeof args.lives === "string") {
7130
- const lives = args.lives.trim();
7131
- input.livesIn = lives === "" ? [] : [lives];
7132
- }
7187
+ const livesIn = listFlag(rawArgs, "lives", args.lives);
7188
+ if (livesIn !== undefined)
7189
+ input.livesIn = livesIn;
7133
7190
  if (typeof args.term === "string")
7134
7191
  input.term = args.term;
7135
7192
  if (typeof args.aliases === "string")
@@ -7166,7 +7223,7 @@ var init_supersede2 = __esm(() => {
7166
7223
  },
7167
7224
  lives: {
7168
7225
  type: "string",
7169
- description: "Successor's Lives in (default: copied from the old entry)"
7226
+ description: "Successor's Lives in: repeat or comma-separate; empty clears (default: copied from the old entry)"
7170
7227
  },
7171
7228
  term: {
7172
7229
  type: "string",
@@ -7195,7 +7252,7 @@ var init_supersede2 = __esm(() => {
7195
7252
  description: "Supersede a shipped requirement: flip it to superseded, mint the successor, bump specVersion, and emit the retag worklist."
7196
7253
  },
7197
7254
  args: supersedeArgs,
7198
- async run({ args }) {
7255
+ async run({ args, rawArgs }) {
7199
7256
  const id = args.id;
7200
7257
  if (!ID_RE.test(id)) {
7201
7258
  console.error(`spec supersede: id must be a requirement id (KEY-NNN); got ${id}`);
@@ -7208,7 +7265,7 @@ var init_supersede2 = __esm(() => {
7208
7265
  handleNotAPlatform(e);
7209
7266
  }
7210
7267
  const statement = await resolveSuccessorText(args, platformDir, id);
7211
- const result = await supersede(inputFromArgs(args, platformDir, id, statement), coldFreshTags(platformDir));
7268
+ const result = await supersede(inputFromArgs(args, rawArgs, platformDir, id, statement), coldFreshTags(platformDir));
7212
7269
  if (!result.ok)
7213
7270
  exitOnFailure("spec supersede", result);
7214
7271
  printWarnings("spec supersede", result.warnings);
@@ -7454,16 +7511,15 @@ __export(exports_move, {
7454
7511
  moveCommand: () => moveCommand
7455
7512
  });
7456
7513
  import { defineCommand as defineCommand17 } from "citty";
7457
- function inputFromArgs2(args, platformDir, id, targetKey) {
7514
+ function inputFromArgs2(args, rawArgs, platformDir, id, targetKey) {
7458
7515
  const input = { platformDir, id, targetKey, noBump: Boolean(args.noBump) };
7459
7516
  if (typeof args.text === "string")
7460
7517
  input.statement = args.text;
7461
7518
  if (typeof args.why === "string")
7462
7519
  input.why = args.why;
7463
- if (typeof args.lives === "string") {
7464
- const lives = args.lives.trim();
7465
- input.livesIn = lives === "" ? [] : [lives];
7466
- }
7520
+ const livesIn = listFlag(rawArgs, "lives", args.lives);
7521
+ if (livesIn !== undefined)
7522
+ input.livesIn = livesIn;
7467
7523
  return input;
7468
7524
  }
7469
7525
  var moveArgs, moveCommand;
@@ -7498,7 +7554,7 @@ var init_move2 = __esm(() => {
7498
7554
  },
7499
7555
  lives: {
7500
7556
  type: "string",
7501
- description: "Rewrite the successor's Lives in (default: copied from the source)"
7557
+ description: "Rewrite the successor's Lives in: repeat or comma-separate; empty clears (default: copied from the source)"
7502
7558
  },
7503
7559
  noBump: {
7504
7560
  type: "boolean",
@@ -7515,7 +7571,7 @@ var init_move2 = __esm(() => {
7515
7571
  description: "Move a requirement to another domain: mint the successor in <NEW-DOMAIN> carrying the source's fields, mark the source superseded, bump both specVersions, and emit the retag worklist."
7516
7572
  },
7517
7573
  args: moveArgs,
7518
- async run({ args }) {
7574
+ async run({ args, rawArgs }) {
7519
7575
  const id = args.id;
7520
7576
  const rawTargetKey = args.newDomain;
7521
7577
  const platformDir = resolvePlatformDir(args);
@@ -7533,7 +7589,7 @@ var init_move2 = __esm(() => {
7533
7589
  } catch (e) {
7534
7590
  handleNotAPlatform(e);
7535
7591
  }
7536
- const result = await move(inputFromArgs2(args, platformDir, id, targetKey), coldFreshTags(platformDir));
7592
+ const result = await move(inputFromArgs2(args, rawArgs, platformDir, id, targetKey), coldFreshTags(platformDir));
7537
7593
  if (!result.ok)
7538
7594
  exitOnFailure("spec move", result);
7539
7595
  printWarnings("spec move", result.warnings);
@@ -7679,7 +7735,7 @@ function assertAmendFlags(args) {
7679
7735
  function splitAliases2(raw) {
7680
7736
  return raw === "" ? [] : raw.split(",").map((s) => s.trim()).filter((s) => s !== "");
7681
7737
  }
7682
- function fieldsFromArgs(args) {
7738
+ function fieldsFromArgs(args, rawArgs) {
7683
7739
  assertAmendFlags(args);
7684
7740
  const fields = {};
7685
7741
  if (args.text !== undefined)
@@ -7688,10 +7744,9 @@ function fieldsFromArgs(args) {
7688
7744
  const v = args.why.trim();
7689
7745
  fields.why = v === "" ? null : v;
7690
7746
  }
7691
- if (args.lives !== undefined) {
7692
- const v = args.lives.trim();
7693
- fields.livesIn = v === "" ? [] : [v];
7694
- }
7747
+ const livesIn = listFlag(rawArgs, "lives", args.lives);
7748
+ if (livesIn !== undefined)
7749
+ fields.livesIn = livesIn;
7695
7750
  if (args.term !== undefined)
7696
7751
  fields.term = args.term.trim();
7697
7752
  if (args.issue !== undefined && args.issue.trim() !== "")
@@ -7718,7 +7773,7 @@ var init_amend2 = __esm(() => {
7718
7773
  platformDir: platformDirArg,
7719
7774
  text: { type: "string", description: "New Requirement (statement) field value" },
7720
7775
  why: { type: "string", description: "New Why it matters field value" },
7721
- lives: { type: "string", description: "New Lives in (livesIn) field value" },
7776
+ lives: livesArg,
7722
7777
  issue: {
7723
7778
  type: "string",
7724
7779
  description: "Ticket that motivated this amendment \u2014 recorded as amends-via provenance (opaque, never a requirement id)"
@@ -7736,7 +7791,7 @@ var init_amend2 = __esm(() => {
7736
7791
  description: "Revise an unshipped requirement's fields in place (same id, no specVersion bump). Supersede shipped truth instead."
7737
7792
  },
7738
7793
  args: amendArgs,
7739
- async run({ args }) {
7794
+ async run({ args, rawArgs }) {
7740
7795
  const id = args.id;
7741
7796
  if (!ID_RE.test(id)) {
7742
7797
  console.error(`spec amend: id must be a requirement id (KEY-NNN); got ${id}`);
@@ -7749,7 +7804,7 @@ var init_amend2 = __esm(() => {
7749
7804
  } catch (e) {
7750
7805
  handleNotAPlatform(e);
7751
7806
  }
7752
- const fields = fieldsFromArgs(args);
7807
+ const fields = fieldsFromArgs(args, rawArgs);
7753
7808
  const result = await amend({ platformDir, id, fields }, coldFreshTags(platformDir));
7754
7809
  if (!result.ok)
7755
7810
  exitOnFailure("spec amend", result);
@@ -7763,6 +7818,86 @@ var init_amend2 = __esm(() => {
7763
7818
  });
7764
7819
  });
7765
7820
 
7821
+ // src/operations/accept.ts
7822
+ async function accept(input) {
7823
+ const { platformDir, id } = input;
7824
+ const located = await locateEntry(platformDir, id);
7825
+ if (!located.ok)
7826
+ return located;
7827
+ const { specPath, relFile, domain: domain2, req } = located;
7828
+ if (req.status.toLowerCase() !== "draft") {
7829
+ return fail("conflict", `${id} is ${displayStatus(req.status)} \u2014 only a Draft entry accepts`);
7830
+ }
7831
+ req.status = "active";
7832
+ domain2.updated = localToday();
7833
+ const res = await validateAndWrite(specPath, domain2, relFile);
7834
+ if (!res.ok) {
7835
+ return fail("invalid_domain_file", res.diagnostics.map((d) => d.detail).join(`
7836
+ `), {
7837
+ diagnostics: res.diagnostics
7838
+ });
7839
+ }
7840
+ return { ok: true, id, file: relFile };
7841
+ }
7842
+ var init_accept = __esm(() => {
7843
+ init_src();
7844
+ init__envelope();
7845
+ init__result();
7846
+ });
7847
+
7848
+ // src/commands/accept.ts
7849
+ var exports_accept = {};
7850
+ __export(exports_accept, {
7851
+ acceptCommand: () => acceptCommand
7852
+ });
7853
+ import { defineCommand as defineCommand19 } from "citty";
7854
+ var acceptCommand;
7855
+ var init_accept2 = __esm(() => {
7856
+ init_constants();
7857
+ init_discover();
7858
+ init_accept();
7859
+ init_grammar3();
7860
+ init__args();
7861
+ init__shared();
7862
+ acceptCommand = defineCommand19({
7863
+ meta: {
7864
+ name: "accept",
7865
+ description: "Promote a Draft requirement to Active (same id, fields untouched)"
7866
+ },
7867
+ args: {
7868
+ id: {
7869
+ type: "positional",
7870
+ required: true,
7871
+ description: "Draft requirement id to accept (KEY-NNN)"
7872
+ },
7873
+ platformDir: platformDirArg,
7874
+ json: jsonArg
7875
+ },
7876
+ async run({ args }) {
7877
+ const id = args.id;
7878
+ if (!ID_RE.test(id)) {
7879
+ console.error(`spec accept: id must be a requirement id (KEY-NNN); got ${id}`);
7880
+ process.exit(EXIT.USAGE);
7881
+ return;
7882
+ }
7883
+ const platformDir = resolvePlatformDir(args);
7884
+ try {
7885
+ assertSpecPlatform(platformDir);
7886
+ } catch (e) {
7887
+ handleNotAPlatform(e);
7888
+ }
7889
+ const result = await accept({ platformDir, id });
7890
+ if (!result.ok)
7891
+ exitOnFailure("spec accept", result);
7892
+ if (args.json) {
7893
+ console.log(JSON.stringify({ id, file: result.file }));
7894
+ return;
7895
+ }
7896
+ console.log(`accepted ${id} in ${result.file} \u2014 now Active`);
7897
+ }
7898
+ });
7899
+ });
7900
+
7766
7901
  // src/server/authorPrompt.ts
7767
7902
  function renderAuthorPrompt(opts) {
7768
7903
  const charterBlock = opts.charter ? `
@@ -7938,6 +8073,19 @@ function buildMcpServer(platformDir) {
7938
8073
  const r = await deprecate({ platformDir, id: req_id, reason: trimmed }, coldFreshTags(platformDir));
7939
8074
  return r.ok ? jsonResult({ id: r.id, file: r.file, reason: r.reason, sites: r.sites }) : errorResult(r.detail);
7940
8075
  });
8076
+ server.registerTool("spec_accept", {
8077
+ title: "Accept a draft requirement",
8078
+ description: "Promote a Draft requirement to Active, leaving every other field untouched. The same JSON as `spec accept --json`.",
8079
+ inputSchema: {
8080
+ req_id: z3.string().describe("The Draft requirement id to accept (KEY-NNN)")
8081
+ }
8082
+ }, async ({ req_id }) => {
8083
+ if (!ID_RE.test(req_id)) {
8084
+ return errorResult(`req_id must be a requirement id (KEY-NNN); got ${req_id}`);
8085
+ }
8086
+ const r = await accept({ platformDir, id: req_id });
8087
+ return r.ok ? jsonResult({ id: r.id, file: r.file }) : errorResult(r.detail);
8088
+ });
7941
8089
  server.registerPrompt("author_requirements", {
7942
8090
  title: "Author requirements from a brief",
7943
8091
  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.",
@@ -7972,6 +8120,7 @@ var init_mcp = __esm(() => {
7972
8120
  init_src();
7973
8121
  init_domains();
7974
8122
  init__index();
8123
+ init_accept();
7975
8124
  init_check();
7976
8125
  init_deprecate();
7977
8126
  init_nextId();
@@ -7987,14 +8136,14 @@ __export(exports_mcp, {
7987
8136
  mcpCommand: () => mcpCommand
7988
8137
  });
7989
8138
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7990
- import { defineCommand as defineCommand19 } from "citty";
8139
+ import { defineCommand as defineCommand20 } from "citty";
7991
8140
  var mcpCommand;
7992
8141
  var init_mcp2 = __esm(() => {
7993
8142
  init_discover();
7994
8143
  init_mcp();
7995
8144
  init__args();
7996
8145
  init__shared();
7997
- mcpCommand = defineCommand19({
8146
+ mcpCommand = defineCommand20({
7998
8147
  meta: {
7999
8148
  name: "mcp",
8000
8149
  description: "Serve Spec Engine as an MCP (Model Context Protocol) server over stdio \u2014 agent-native query/resolve/check/report tools."
@@ -8023,7 +8172,7 @@ __export(exports_resolve, {
8023
8172
  });
8024
8173
  import { statSync as statSync5 } from "fs";
8025
8174
  import { join as join12, relative as relative2, resolve as resolve13, sep as sep3 } from "path";
8026
- import { defineCommand as defineCommand20 } from "citty";
8175
+ import { defineCommand as defineCommand21 } from "citty";
8027
8176
  function classifyArg(a) {
8028
8177
  if (a.startsWith("--")) {
8029
8178
  const eq = a.indexOf("=");
@@ -8209,7 +8358,7 @@ var init_resolve2 = __esm(() => {
8209
8358
  description: "Suppress interactive onboarding prompt for siblings missing spec-engine.member.json (defaults to NO_SPEC_CONFIG warning)"
8210
8359
  }
8211
8360
  };
8212
- resolveCommand = defineCommand20({
8361
+ resolveCommand = defineCommand21({
8213
8362
  meta: {
8214
8363
  name: "resolve",
8215
8364
  description: "Return the requirements tagged in the given files (RSLV-01). Accepts multiple positional file paths (e.g. `spec resolve api/src/renew.ts api/src/charge.ts`) and a comma-split fallback inside a single positional (`spec resolve a.ts,b.ts`). Paths may be platform-relative or absolute under the platform tree."
@@ -8374,7 +8523,7 @@ __export(exports_gate, {
8374
8523
  });
8375
8524
  import { existsSync as existsSync12 } from "fs";
8376
8525
  import { join as join13 } from "path";
8377
- import { defineCommand as defineCommand21 } from "citty";
8526
+ import { defineCommand as defineCommand22 } from "citty";
8378
8527
  function isEpipe(e) {
8379
8528
  if (e === null || typeof e !== "object" || !("code" in e))
8380
8529
  return false;
@@ -8437,7 +8586,7 @@ var init_gate2 = __esm(() => {
8437
8586
  description: "Suppress interactive onboarding prompt for siblings missing spec-engine.member.json (defaults to NO_SPEC_CONFIG warning)"
8438
8587
  }
8439
8588
  };
8440
- gateCommand = defineCommand21({
8589
+ gateCommand = defineCommand22({
8441
8590
  meta: {
8442
8591
  name: "gate",
8443
8592
  description: "Rung-3 approval primitive: passes iff <reqId> is Active and <repo>'s pinned spec_version covers its changed_at_version. Exits 0 PASS / 1 any gate failure / 2 crash or bad args."
@@ -8759,14 +8908,14 @@ var exports_guard = {};
8759
8908
  __export(exports_guard, {
8760
8909
  guardCommand: () => guardCommand
8761
8910
  });
8762
- import { defineCommand as defineCommand22 } from "citty";
8911
+ import { defineCommand as defineCommand23 } from "citty";
8763
8912
  var guardCommand;
8764
8913
  var init_guard2 = __esm(() => {
8765
8914
  init_constants();
8766
8915
  init_guard();
8767
8916
  init__args();
8768
8917
  init__shared();
8769
- guardCommand = defineCommand22({
8918
+ guardCommand = defineCommand23({
8770
8919
  meta: {
8771
8920
  name: "guard",
8772
8921
  description: "Loss detection: diff the requirement derivation at a git ref (default HEAD) against the working tree and block requirements about to be steamrolled."
@@ -11730,6 +11879,15 @@ function toLivesIn(v) {
11730
11879
  return [v];
11731
11880
  return [];
11732
11881
  }
11882
+ function mintFieldsFromBody(body) {
11883
+ return {
11884
+ statement: typeof body.statement === "string" ? body.statement : "",
11885
+ why: typeof body.why === "string" ? body.why : "",
11886
+ livesIn: toLivesIn(body.livesIn),
11887
+ issue: typeof body.issue === "string" && body.issue.trim() !== "" ? body.issue.trim() : undefined,
11888
+ status: body.status === "draft" ? "draft" : "active"
11889
+ };
11890
+ }
11733
11891
  function amendFieldsFromBody(body) {
11734
11892
  const fields = {};
11735
11893
  if (typeof body.statement === "string")
@@ -11814,14 +11972,7 @@ function mountApi(app, storage2, platformDir = process.cwd()) {
11814
11972
  return c.json({ error: "unknown domain key" }, 400);
11815
11973
  }
11816
11974
  return withWriteLock(async () => {
11817
- const r = await mint({
11818
- platformDir,
11819
- key,
11820
- statement: typeof body.statement === "string" ? body.statement : "",
11821
- why: typeof body.why === "string" ? body.why : "",
11822
- livesIn: toLivesIn(body.livesIn),
11823
- issue: typeof body.issue === "string" && body.issue.trim() !== "" ? body.issue.trim() : undefined
11824
- });
11975
+ const r = await mint({ platformDir, key, ...mintFieldsFromBody(body) });
11825
11976
  if (!r.ok)
11826
11977
  return failureResponse(c, r);
11827
11978
  await runIndex({ platformDir, storage: storage2 });
@@ -11932,6 +12083,30 @@ function mountApi(app, storage2, platformDir = process.cwd()) {
11932
12083
  return c.json({ ok: true, id: r.id, file: r.file, reason: r.reason, sites: r.sites }, 200);
11933
12084
  });
11934
12085
  }));
12086
+ app.post("/api/requirements/:id/accept", guarded(async function acceptRequirement(c) {
12087
+ if (!featureEnabled("editor")) {
12088
+ return c.json({ error: featureDisabledMessage("editor") }, 404);
12089
+ }
12090
+ const originReject = rejectCrossOrigin(c);
12091
+ if (originReject)
12092
+ return originReject;
12093
+ const headerReject = rejectBadWriteHeaders(c);
12094
+ if (headerReject)
12095
+ return headerReject;
12096
+ const parsed = await readJsonBody(c);
12097
+ if (!parsed.ok)
12098
+ return parsed.res;
12099
+ const id = c.req.param("id") ?? "";
12100
+ if (!ID_RE.test(id))
12101
+ return c.json({ error: "id must be a requirement id (KEY-NNN)" }, 400);
12102
+ return withWriteLock(async () => {
12103
+ const r = await accept({ platformDir, id });
12104
+ if (!r.ok)
12105
+ return failureResponse(c, r);
12106
+ await runIndex({ platformDir, storage: storage2 });
12107
+ return c.json({ ok: true, id: r.id, file: r.file }, 200);
12108
+ });
12109
+ }));
11935
12110
  app.get("/api/propagation/:id", guarded((c) => c.json(propagation2(storage2, c.req.param("id") ?? "").rows)));
11936
12111
  app.get("/api/query", guarded((c) => {
11937
12112
  if (!featureEnabled("query")) {
@@ -12012,6 +12187,7 @@ var init_api = __esm(() => {
12012
12187
  init_discover();
12013
12188
  init_pipeline();
12014
12189
  init__result();
12190
+ init_accept();
12015
12191
  init_amend();
12016
12192
  init_deprecate();
12017
12193
  init_mint();
@@ -12033,7 +12209,7 @@ __export(exports_serve, {
12033
12209
  });
12034
12210
  import { existsSync as existsSync13, mkdirSync as mkdirSync6 } from "fs";
12035
12211
  import { dirname as dirname8 } from "path";
12036
- import { defineCommand as defineCommand23 } from "citty";
12212
+ import { defineCommand as defineCommand24 } from "citty";
12037
12213
  import { Hono as Hono2 } from "hono";
12038
12214
  function composeServeApp(storage2, platformDir = process.cwd()) {
12039
12215
  const app = new Hono2;
@@ -12082,7 +12258,7 @@ var init_serve = __esm(() => {
12082
12258
  init_sqlite();
12083
12259
  init__args();
12084
12260
  init__shared();
12085
- serveCommand = defineCommand23({
12261
+ serveCommand = defineCommand24({
12086
12262
  meta: {
12087
12263
  name: "serve",
12088
12264
  description: "Run the local webapp. Binds 127.0.0.1 on --port (loopback only \u2014 no --host). --probe is the SERV-04 asset-embedding smoke."
@@ -12185,7 +12361,7 @@ __export(exports_docs, {
12185
12361
  import { existsSync as existsSync14, statSync as statSync6 } from "fs";
12186
12362
  import { join as join15, normalize, sep as sep4 } from "path";
12187
12363
  import { fileURLToPath } from "url";
12188
- import { defineCommand as defineCommand24 } from "citty";
12364
+ import { defineCommand as defineCommand25 } from "citty";
12189
12365
  function resolveDocsRootFrom(candidates) {
12190
12366
  for (const candidate of candidates) {
12191
12367
  if (existsSync14(join15(candidate, "index.html")))
@@ -12263,7 +12439,7 @@ function exitNoDocsRoot() {
12263
12439
  var docsCommand;
12264
12440
  var init_docs2 = __esm(() => {
12265
12441
  init_constants();
12266
- docsCommand = defineCommand24({
12442
+ docsCommand = defineCommand25({
12267
12443
  meta: {
12268
12444
  name: "docs",
12269
12445
  description: "Serve the bundled documentation site offline. Binds 127.0.0.1 on --port (loopback only \u2014 no --host). --probe is the boot-and-fetch smoke."
@@ -12324,10 +12500,10 @@ __export(exports___smoke, {
12324
12500
  schemaSmokeCommand: () => schemaSmokeCommand,
12325
12501
  schemaMismatchSmokeCommand: () => schemaMismatchSmokeCommand
12326
12502
  });
12327
- import { defineCommand as defineCommand25 } from "citty";
12503
+ import { defineCommand as defineCommand26 } from "citty";
12328
12504
  var schemaSmokeCommand, schemaMismatchSmokeCommand;
12329
12505
  var init___smoke = __esm(() => {
12330
- schemaSmokeCommand = defineCommand25({
12506
+ schemaSmokeCommand = defineCommand26({
12331
12507
  meta: {
12332
12508
  name: "__schema-smoke",
12333
12509
  description: "(hidden CI) open a fresh DB, exec DDL, assert the schema shape",
@@ -12370,7 +12546,7 @@ var init___smoke = __esm(() => {
12370
12546
  console.log("__schema-smoke OK");
12371
12547
  }
12372
12548
  });
12373
- schemaMismatchSmokeCommand = defineCommand25({
12549
+ schemaMismatchSmokeCommand = defineCommand26({
12374
12550
  meta: {
12375
12551
  name: "__schema-mismatch-smoke",
12376
12552
  description: "(hidden CI) corrupt _schema_version, re-open, assert silent rebuild",
@@ -12396,8 +12572,8 @@ var init___smoke = __esm(() => {
12396
12572
 
12397
12573
  // src/cli.ts
12398
12574
  init_package();
12399
- import { defineCommand as defineCommand26, runMain, showUsage } from "citty";
12400
- var main = defineCommand26({
12575
+ import { defineCommand as defineCommand27, runMain, showUsage } from "citty";
12576
+ var main = defineCommand27({
12401
12577
  meta: {
12402
12578
  name: "spec",
12403
12579
  version: package_default.version,
@@ -12422,6 +12598,7 @@ var main = defineCommand26({
12422
12598
  deprecate: () => Promise.resolve().then(() => (init_deprecate2(), exports_deprecate)).then((m) => m.deprecateCommand),
12423
12599
  move: () => Promise.resolve().then(() => (init_move2(), exports_move)).then((m) => m.moveCommand),
12424
12600
  amend: () => Promise.resolve().then(() => (init_amend2(), exports_amend)).then((m) => m.amendCommand),
12601
+ accept: () => Promise.resolve().then(() => (init_accept2(), exports_accept)).then((m) => m.acceptCommand),
12425
12602
  mcp: () => Promise.resolve().then(() => (init_mcp2(), exports_mcp)).then((m) => m.mcpCommand),
12426
12603
  resolve: () => Promise.resolve().then(() => (init_resolve2(), exports_resolve)).then((m) => m.resolveCommand),
12427
12604
  gate: () => Promise.resolve().then(() => (init_gate2(), exports_gate)).then((m) => m.gateCommand),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spec-engine/spec-engine",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
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",