@wairon/cli 5.1.1-dev.60 → 5.1.1-dev.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -65,7 +65,7 @@ var init_defaults = __esm({
65
65
  copilot: ".github/prompts",
66
66
  codex: ".codex/agents"
67
67
  };
68
- WAIRON_VERSION = "5.1.1-dev.60";
68
+ WAIRON_VERSION = "5.1.1-dev.61";
69
69
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
70
70
  SUPPORTED_ALIASES = ["wai"];
71
71
  SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
@@ -23826,313 +23826,6 @@ var init_skills = __esm({
23826
23826
  }
23827
23827
  });
23828
23828
 
23829
- // src/core/stamp.ts
23830
- function versionStamp() {
23831
- return `<!-- wairon-version: ${WAIRON_VERSION} -->`;
23832
- }
23833
- function readStampVersion(content) {
23834
- const m = content.match(STAMP_RE);
23835
- return m ? m[1] : null;
23836
- }
23837
- var STAMP_RE;
23838
- var init_stamp = __esm({
23839
- "src/core/stamp.ts"() {
23840
- "use strict";
23841
- init_defaults();
23842
- STAMP_RE = /<!--\s*wairon-version:\s*([^\s]+)\s*-->/;
23843
- }
23844
- });
23845
-
23846
- // src/utils/ai-guide.ts
23847
- function globalGuideFilePath(targetType) {
23848
- if (targetType === "claude") return path24.join(process.env["CLAUDE_CONFIG_DIR"] || path24.join(os5.homedir(), ".claude"), "CLAUDE.md");
23849
- if (targetType === "gemini") return path24.join(process.env["GEMINI_CONFIG_DIR"] || path24.join(os5.homedir(), ".gemini"), "GEMINI.md");
23850
- return null;
23851
- }
23852
- function localGuideFilePath(projectRoot2, targetType) {
23853
- if (targetType === "claude") return path24.join(projectRoot2, ".claude", "CLAUDE.md");
23854
- if (targetType === "gemini" || targetType === "agy") return path24.join(projectRoot2, ".gemini", "GEMINI.md");
23855
- return null;
23856
- }
23857
- function injectGuide(filePath, scope) {
23858
- const body = scope === "global" ? GLOBAL_GUIDE_BODY : LOCAL_GUIDE_BODY;
23859
- const section = `
23860
-
23861
- ${GUIDE_MARKER_START}
23862
- ${versionStamp()}
23863
- ${body}
23864
- ${GUIDE_MARKER_END}
23865
- `;
23866
- const existing = fs19.existsSync(filePath) ? fs19.readFileSync(filePath, "utf-8") : "";
23867
- const stripped = stripGuideSection(existing);
23868
- const newContent = stripped.trimEnd() + section;
23869
- fs19.mkdirSync(path24.dirname(filePath), { recursive: true });
23870
- fs19.writeFileSync(filePath, newContent, "utf-8");
23871
- }
23872
- function stripGuideSection(content) {
23873
- const start2 = content.indexOf(GUIDE_MARKER_START);
23874
- const end = content.indexOf(GUIDE_MARKER_END);
23875
- if (start2 === -1 || end === -1) return content;
23876
- return content.slice(0, start2) + content.slice(end + GUIDE_MARKER_END.length);
23877
- }
23878
- function writeRootGuideDelegator(projectRoot2, targetType) {
23879
- if (targetType === "claude") {
23880
- const filePath = path24.join(projectRoot2, "CLAUDE.md");
23881
- const content = `@.claude/CLAUDE.md
23882
-
23883
- # Wairon SDD Project
23884
-
23885
- This project uses the Wairon Spec-Driven Development (SDD) framework. The imported
23886
- \`.claude/CLAUDE.md\` above is your complete operating guide \u2014 you already have the
23887
- full context, so don't search the project to learn how wairon or SDD works.
23888
-
23889
- To design or modify the system, invoke the **\`sdd-architect\`** skill
23890
- (in \`.claude/skills/\`). Author and validate specs with the \`sdd_*\` MCP tools;
23891
- the \`wairon\` CLI is the human developer's tool, not yours.
23892
- `;
23893
- fs19.writeFileSync(filePath, content, "utf-8");
23894
- } else if (targetType === "gemini" || targetType === "agy") {
23895
- const filePath = path24.join(projectRoot2, "GEMINI.md");
23896
- const content = `# Wairon SDD Project
23897
- ${GUIDE_MARKER_START}
23898
- ${versionStamp()}
23899
- ${LOCAL_GUIDE_BODY}
23900
- ${GUIDE_MARKER_END}
23901
- `;
23902
- fs19.writeFileSync(filePath, content, "utf-8");
23903
- } else if (targetType === "cursor") {
23904
- const filePath = path24.join(projectRoot2, ".cursorrules");
23905
- const content = `# Wairon SDD Project
23906
-
23907
- This project uses the Wairon Spec-Driven Development (SDD) framework.
23908
-
23909
- Refer to the rules in [.cursor/rules/](.cursor/rules/) for full instructions.
23910
- `;
23911
- fs19.writeFileSync(filePath, content, "utf-8");
23912
- } else if (targetType === "copilot") {
23913
- const filePath = path24.join(projectRoot2, ".github", "copilot-instructions.md");
23914
- fs19.mkdirSync(path24.dirname(filePath), { recursive: true });
23915
- const content = `# Wairon SDD Project
23916
-
23917
- This project uses the Wairon Spec-Driven Development (SDD) framework.
23918
-
23919
- Refer to the prompts in [.github/prompts/](.github/prompts/) for instructions.
23920
- `;
23921
- fs19.writeFileSync(filePath, content, "utf-8");
23922
- } else if (targetType === "codex") {
23923
- const filePath = path24.join(projectRoot2, ".codexrules");
23924
- const content = `# Wairon SDD Project
23925
-
23926
- Refer to [.codex/agents/](.codex/agents/) for full instructions.
23927
- `;
23928
- fs19.writeFileSync(filePath, content, "utf-8");
23929
- }
23930
- }
23931
- function reinjectLocalGuides(projectRoot2, targetTypes) {
23932
- const written = [];
23933
- for (const type of targetTypes) {
23934
- if (!GUIDE_TARGETS.includes(type)) continue;
23935
- const guidePath = localGuideFilePath(projectRoot2, type);
23936
- if (guidePath) {
23937
- injectGuide(guidePath, "local");
23938
- if (!written.includes(guidePath)) written.push(guidePath);
23939
- }
23940
- writeRootGuideDelegator(projectRoot2, type);
23941
- }
23942
- return written;
23943
- }
23944
- var fs19, os5, path24, GUIDE_MARKER_START, GUIDE_MARKER_END, GLOBAL_GUIDE_BODY, LOCAL_GUIDE_BODY, GUIDE_TARGETS;
23945
- var init_ai_guide = __esm({
23946
- "src/utils/ai-guide.ts"() {
23947
- "use strict";
23948
- fs19 = __toESM(require("fs"));
23949
- os5 = __toESM(require("os"));
23950
- path24 = __toESM(require("path"));
23951
- init_stamp();
23952
- GUIDE_MARKER_START = "<!-- wairon-guide-start -->";
23953
- GUIDE_MARKER_END = "<!-- wairon-guide-end -->";
23954
- GLOBAL_GUIDE_BODY = `## wairon \u2014 Spec-Driven Development (optional)
23955
-
23956
- If \`.wai/specs/\` exists, the wairon SDD workflow is active; otherwise ignore it. wairon does not orchestrate sessions \u2014 it equips yours.
23957
-
23958
- ### In SDD Projects:
23959
- - **Source of Truth**: All architecture lives in the spec tree under \`.wai/specs/\` (L0 System \u2192 L1 Subsystem \u2192 L2 Component \u2192 L3 Interface \u2192 L4 Implementation \u2192 L5 Narrative). Do not edit generated agent config files under \`.claude/agents/\` (rebuilt via \`wairon generate\`).
23960
- - **Validation**: Conformance checks (stereotype rules, cycle checks, reference integrity) are run via the \`sdd_validate_tree\` MCP tool.
23961
- - **Operating Rules**:
23962
- 1. **Skills**: Use \`sdd-architect\` to design (and \`sdd-implement\`, \`sdd-narrative\`, \`sdd-auditor\`). Refer to project's local guide file for detailed constraints.
23963
- 2. **MCP Tools Only**: Author/validate specs *only* via \`sdd_*\` tools (e.g. \`sdd_initialize_system\`, \`sdd_validate_tree\`).
23964
- 3. **No CLI Exec**: Do not run the \`wairon\` CLI (human tool). Use MCP tools \`sdd_validate_tree\` and \`sdd_get_status\` instead.
23965
- 4. **Delegation**: Delegate implementation via the \`sdd-delegate\` skill \u2014 live agent briefs (\`sdd_get_agent_brief\` MCP tool / \`wairon-agent://\` resource) are composed per call and always current; no session restart. Generated agent files are an optional materialized view of the same topology. User-owned per-agent guidance may live in \`.wai/agents/<agent-id>.md\` (folded into every brief; scaffold via \`wairon agent customize <id>\`).
23966
- 5. **Design First**: Complete spec and pass \`sdd_validate_tree\` before writing code.
23967
- 6. **Consistency**: Code must match L3 interfaces and L5 narratives exactly. If the spec is wrong, stop and update the spec.
23968
- 7. **Subprojects & Namespacing**: If a subsystem uses \`projectPath\` delegation, target its specs using namespaced IDs (e.g. \`subsystem::component\`). Use leading \`::\` to target root (e.g. \`::shared::type\`) and \`super::\` to go up a level (e.g. \`super::sibling\`). wairon automatically resolves the path and strips the prefix on writes.`;
23969
- LOCAL_GUIDE_BODY = `## Wairon \u2014 Spec-Driven Development (you are operating inside it)
23970
-
23971
- This project uses **wairon**. System specs live under \`.wai/specs/\` (L0 System \u2192 L1 Subsystem \u2192 L2 Component \u2192 L3 Interface \u2192 L4 Implementation \u2192 Narrative); agent topology and code are derived from it.
23972
-
23973
- **Do NOT search files or read agent configs to learn about wairon or SDD. Use the context here and the \`sdd-architect\` skill to start.**
23974
-
23975
- **Your first move: call the \`sdd_get_status\` MCP tool** (or \`wairon/sdd_get_status\`) to see the spec tree. Do not parse files or run CLI commands manually.
23976
-
23977
- ### How you operate
23978
- - **To design/modify specs**: Use **\`sdd-architect\`** skill (in \`.claude/skills/\` or \`.gemini/skills/\`).
23979
- - **Manage specs via MCP tools only**: Use \`sdd_initialize_system\`, \`sdd_add_subsystem\`, \`sdd_add_component\`, \`sdd_define_interface\`, \`sdd_write_narrative\`, \`sdd_add_type\`, \`sdd_get_spec\`, \`sdd_delete_spec\`, \`sdd_validate_tree\`, and \`sdd_get_status\` (namespaced if needed). Do not edit specs manually.
23980
- - **Subprojects & Namespacing (Chaining)**: If a subsystem defines a \`projectPath\`, its entire \`.wai/\` spec tree is recursively loaded and namespaced with the subsystem ID as a prefix (using \`::\`, e.g. \`billing::invoice::invoice_portal\`). Use the qualified namespaced ID with the parent MCP tools; wairon will resolve the path and strip the prefix automatically.
23981
- - **Leading \`::\`**: Bypasses the local subsystem prefix to resolve absolute from the system root (e.g. \`::shared::error-type\`).
23982
- - **\`super::\`**: Goes up one parent subsystem level (e.g. \`super::sibling_comp\`, \`super::super::parent_sibling\`).
23983
- - **Do not run the \`wairon\` CLI**: Use \`sdd_validate_tree\` and \`sdd_get_status\` instead of CLI commands.
23984
- - **Handoff to implementation**: Once design is complete and validates cleanly, tell the human: *"The specs are complete and validate. Please run \`wairon lock\` to confirm and freeze them."* No session restart is needed after the lock \u2014 delegate implementation right away via the \`sdd-delegate\` skill.
23985
- - **To implement code**: Delegate via the \`sdd-delegate\` skill: fetch the component's live brief with the \`sdd_get_agent_brief\` MCP tool (or the \`wairon-agent://\` resource) and spawn a subagent from it. Briefs are composed per call from the current spec tree, so they are always current \u2014 never wait for a restart. Implementations must match L3 interfaces and L5 narratives exactly. Generated agent files under \`.claude/agents/\` are an optional materialized view of the same topology \u2014 the live briefs are canonical.
23986
-
23987
- ### Rules (enforced by \`sdd_validate_tree\`)
23988
- 1. **Design before code**: Complete spec and pass validator before writing source code.
23989
- 2. **Human-in-the-loop**: Ask user approval for each spec layer before proceeding.
23990
- 3. **Spec consistency**: If a 1:1 narrative match is incorrect or conflicts with L0 requirements, escalate a spec revision first. Never ship mismatched code.
23991
- 4. **No persistence shortcuts & strict layers**: A Portal must never depend directly on a Store, Registry, or Adapter. Portal reads MAY go through a Repository/Index facade (passthrough reads need no per-entity Orchestrator ceremony), but a Portal narrative call or dispatch-table binding that reaches a write-effect facade method is an error (\`PORTAL_WRITE_SHORTCUT\`) \u2014 writes always route through an Orchestrator. Held domain state always lives in a dedicated data component, never as fields inside an Orchestrator. Two sanctioned shapes: the RECOMMENDED Repository pattern (owns Store + Registry + Index; consumers depend on the facade), or \u2014 for genuinely simple state \u2014 a deliberately standalone Store (workflow-layer consumers only, acknowledged with a lint.allow reason on the UNOWNED_STORE warning). Never combine Store/Registry/Index roles into one component, and never fold state into a consuming component because a link was refused.
23992
-
23993
- ### Component Vocabulary
23994
- * **Blocks**: Portal, Orchestrator, Supervisor, Actor, Store, Index, Query, Registry, Adapter, Observer.
23995
- * **Patterns**: Repository \u2014 the composable pattern that \`owns\` its member blocks: one Store with its Registry, Indexes and Queries, and optionally an Adapter.
23996
- * **Variants**: \`gateway\`, a Portal that authenticates, authorizes, validates or rate-limits before it dispatches. Logic is an Orchestrator, and \`dependencyClass: pure | read\` bounds what it may depend on (unset = a workflow). Specialist and Gateway are retired stereotypes.
23997
- * Use \`owns\` for private member containment (exactly one hop) and \`dependsOn\` for collaborators. Never use generic suffixes like "Manager", "Helper", or "Utils".`;
23998
- GUIDE_TARGETS = ["claude", "gemini", "agy", "cursor", "copilot", "codex"];
23999
- }
24000
- });
24001
-
24002
- // src/core/context.ts
24003
- function contextDir(...segments) {
24004
- return aiDir("context", ...segments);
24005
- }
24006
- function hasContext() {
24007
- return pathExists(CONTEXT_PATHS.projectMd());
24008
- }
24009
- function readProjectContext() {
24010
- return readFileOrNull(CONTEXT_PATHS.projectMd());
24011
- }
24012
- function readArchitectureContext() {
24013
- return readFileOrNull(CONTEXT_PATHS.architectureMd());
24014
- }
24015
- function renderDomainsDoc() {
24016
- const domains = resolveDomains();
24017
- const lines = [
24018
- versionStamp(),
24019
- `# Domains`,
24020
- ``,
24021
- `> Auto-generated by wairon. The human developer rebuilds this with \`wairon generate\`. `,
24022
- ``
24023
- ];
24024
- if (domains.length === 0) {
24025
- lines.push("_No domains yet. Define subsystems in the spec tree, or add free-standing domains with `wairon domains add`._");
24026
- } else {
24027
- lines.push(`**${domains.length} domain(s).**`);
24028
- lines.push("");
24029
- lines.push("| ID | Name | Source | Owned paths |");
24030
- lines.push("|----|------|--------|-------------|");
24031
- for (const d of domains) {
24032
- const name = d.name ?? d.id;
24033
- const source = d.boundTo ? `subsystem \`${d.boundTo}\`` : "free-standing";
24034
- lines.push(`| \`${d.id}\` | ${name} | ${source} | \`${d.ownedPaths.join("`, `") || "\u2014"}\` |`);
24035
- }
24036
- }
24037
- return lines.join("\n") + "\n";
24038
- }
24039
- function renderWaironGuide() {
24040
- const domains = resolveDomains();
24041
- let projectName = "this project";
24042
- try {
24043
- projectName = projectConfigRepository.load()?.name ?? projectName;
24044
- } catch {
24045
- }
24046
- const projectCtx = readProjectContext();
24047
- const archCtx = readArchitectureContext();
24048
- const lines = [
24049
- versionStamp(),
24050
- `<!-- wairon-generated \u2014 do not edit directly; the human developer rebuilds this with \`wairon generate\` -->`,
24051
- ``
24052
- ];
24053
- if (projectCtx) {
24054
- lines.push(`# Project Context \u2014 ${projectName}`);
24055
- lines.push("");
24056
- lines.push(projectCtx.trim());
24057
- lines.push("");
24058
- lines.push("---");
24059
- lines.push("");
24060
- }
24061
- if (archCtx) {
24062
- lines.push("# Architecture");
24063
- lines.push("");
24064
- lines.push(archCtx.trim());
24065
- lines.push("");
24066
- lines.push("---");
24067
- lines.push("");
24068
- }
24069
- lines.push(`# Domain Map (${domains.length} domain${domains.length !== 1 ? "s" : ""})`);
24070
- lines.push("");
24071
- if (domains.length === 0) {
24072
- lines.push("_No domains yet._");
24073
- } else {
24074
- lines.push("| ID | Source | Name |");
24075
- lines.push("|----|--------|------|");
24076
- for (const d of domains) {
24077
- const name = d.name ?? d.id;
24078
- const source = d.boundTo ? `subsystem \`${d.boundTo}\`` : "free-standing";
24079
- lines.push(`| \`${d.id}\` | ${source} | ${name} |`);
24080
- }
24081
- }
24082
- lines.push("");
24083
- lines.push("---");
24084
- lines.push("");
24085
- const mcpSettingsPath = require("path").join(process.cwd(), ".claude", "settings.json");
24086
- let mcpInstalled = false;
24087
- try {
24088
- const s = JSON.parse(require("fs").readFileSync(mcpSettingsPath, "utf8"));
24089
- mcpInstalled = !!s?.mcpServers?.wairon;
24090
- } catch {
24091
- }
24092
- if (mcpInstalled) {
24093
- lines.push("# wairon MCP Tools");
24094
- lines.push("");
24095
- lines.push("The **wairon MCP server** is active in this project. The tools are self-describing; call them directly:");
24096
- lines.push("");
24097
- lines.push("- Authoring: `sdd_initialize_system`, `sdd_add_subsystem`, `sdd_set_public_interfaces`, `sdd_set_subsystem_project_path`, `sdd_add_component`, `sdd_define_interface`, `sdd_set_endpoints`, `sdd_write_narrative`, `sdd_add_type`.");
24098
- lines.push("- Reading & maintenance: `sdd_get_spec`, `sdd_update_spec`, `sdd_delete_spec`, `sdd_validate_tree`, `sdd_get_status`.");
24099
- lines.push("- Topology: `listAgents`, `getAgent`, `listDomains`, `validateTopology`, `getProjectConfig`.");
24100
- lines.push("");
24101
- lines.push("Use these MCP tools to query and change project state \u2014 never the `wairon` CLI (that is the human developer's tool).");
24102
- lines.push("");
24103
- lines.push("---");
24104
- lines.push("");
24105
- }
24106
- lines.push(GLOBAL_GUIDE_BODY);
24107
- lines.push("");
24108
- return lines.join("\n");
24109
- }
24110
- function syncContextFiles() {
24111
- const domainsContent = renderDomainsDoc();
24112
- const guideContent = renderWaironGuide();
24113
- const domainsUpdated = writeFileIfChanged(CONTEXT_PATHS.domainsMd(), domainsContent);
24114
- const guideUpdated = writeFileIfChanged(CONTEXT_PATHS.waironGuideMd(), guideContent);
24115
- return { domainsUpdated, guideUpdated };
24116
- }
24117
- var CONTEXT_PATHS;
24118
- var init_context = __esm({
24119
- "src/core/context.ts"() {
24120
- "use strict";
24121
- init_fs();
24122
- init_ai_guide();
24123
- init_stamp();
24124
- init_domain_projector();
24125
- init_project_config();
24126
- CONTEXT_PATHS = {
24127
- dir: () => contextDir(),
24128
- projectMd: () => contextDir("project.md"),
24129
- architectureMd: () => contextDir("architecture.md"),
24130
- domainsMd: () => contextDir("domains.md"),
24131
- waironGuideMd: () => contextDir("wairon-guide.md")
24132
- };
24133
- }
24134
- });
24135
-
24136
23829
  // node_modules/fflate/lib/node.cjs
24137
23830
  var require_node = __commonJS({
24138
23831
  "node_modules/fflate/lib/node.cjs"(exports2) {
@@ -27508,41 +27201,41 @@ function hasAuthoredSpecs(root) {
27508
27201
  if (!pathExists(specsDir)) {
27509
27202
  return false;
27510
27203
  }
27511
- const systemIndex = path25.resolve(paths.specsSystem());
27204
+ const systemIndex = path24.resolve(paths.specsSystem());
27512
27205
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
27513
- if (path25.resolve(file) !== systemIndex) {
27206
+ if (path24.resolve(file) !== systemIndex) {
27514
27207
  return true;
27515
27208
  }
27516
27209
  }
27517
27210
  return false;
27518
27211
  }
27519
27212
  function createStagingDir(destDir) {
27520
- const root = path25.resolve(destDir);
27521
- fs20.mkdirSync(root, { recursive: true });
27522
- const stagingDir = fs20.mkdtempSync(path25.join(root, STAGING_PREFIX));
27213
+ const root = path24.resolve(destDir);
27214
+ fs19.mkdirSync(root, { recursive: true });
27215
+ const stagingDir = fs19.mkdtempSync(path24.join(root, STAGING_PREFIX));
27523
27216
  return stagingDir;
27524
27217
  }
27525
27218
  function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
27526
- const root = path25.resolve(destDir);
27527
- const backupDir = path25.join(root, BACKUP_DIR, timestampSlug());
27219
+ const root = path24.resolve(destDir);
27220
+ const backupDir = path24.join(root, BACKUP_DIR, timestampSlug());
27528
27221
  let backedUp = false;
27529
27222
  for (const rootRel of roots) {
27530
27223
  const relative15 = rootRel === "." ? "" : rootRel;
27531
- const stagedSpecDir = path25.join(stagingDir, relative15, ".wai");
27532
- if (!fs20.existsSync(stagedSpecDir)) continue;
27533
- const liveSpecDir = liveSpecDirFor(path25.join(root, relative15));
27534
- if (backupExisting && liveSpecDir && fs20.existsSync(liveSpecDir)) {
27535
- const backupTarget = path25.join(backupDir, relative15, path25.basename(liveSpecDir));
27536
- fs20.mkdirSync(path25.dirname(backupTarget), { recursive: true });
27537
- fs20.renameSync(liveSpecDir, backupTarget);
27224
+ const stagedSpecDir = path24.join(stagingDir, relative15, ".wai");
27225
+ if (!fs19.existsSync(stagedSpecDir)) continue;
27226
+ const liveSpecDir = liveSpecDirFor(path24.join(root, relative15));
27227
+ if (backupExisting && liveSpecDir && fs19.existsSync(liveSpecDir)) {
27228
+ const backupTarget = path24.join(backupDir, relative15, path24.basename(liveSpecDir));
27229
+ fs19.mkdirSync(path24.dirname(backupTarget), { recursive: true });
27230
+ fs19.renameSync(liveSpecDir, backupTarget);
27538
27231
  backedUp = true;
27539
- } else if (liveSpecDir && fs20.existsSync(liveSpecDir)) {
27540
- fs20.rmSync(liveSpecDir, { recursive: true, force: true });
27232
+ } else if (liveSpecDir && fs19.existsSync(liveSpecDir)) {
27233
+ fs19.rmSync(liveSpecDir, { recursive: true, force: true });
27541
27234
  }
27542
- const target = path25.join(root, relative15, ".wai");
27543
- fs20.mkdirSync(path25.dirname(target), { recursive: true });
27544
- if (fs20.existsSync(target)) fs20.rmSync(target, { recursive: true, force: true });
27545
- fs20.renameSync(stagedSpecDir, target);
27235
+ const target = path24.join(root, relative15, ".wai");
27236
+ fs19.mkdirSync(path24.dirname(target), { recursive: true });
27237
+ if (fs19.existsSync(target)) fs19.rmSync(target, { recursive: true, force: true });
27238
+ fs19.renameSync(stagedSpecDir, target);
27546
27239
  }
27547
27240
  if (backedUp) {
27548
27241
  return backupDir;
@@ -27551,7 +27244,7 @@ function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
27551
27244
  }
27552
27245
  function discardStagingDir(stagingDir) {
27553
27246
  try {
27554
- fs20.rmSync(stagingDir, { recursive: true, force: true });
27247
+ fs19.rmSync(stagingDir, { recursive: true, force: true });
27555
27248
  } catch {
27556
27249
  }
27557
27250
  }
@@ -27565,7 +27258,7 @@ function exportSpecTree(includeDerived, allowPartial) {
27565
27258
  const skipped = [...inspection.skipped];
27566
27259
  const roots = [{ relativePath: ".", waiDir: aiPathsAt(root).root() }];
27567
27260
  for (const rel2 of inspection.roots) {
27568
- const waiDir = aiPathsAt(path25.resolve(root, rel2)).root();
27261
+ const waiDir = aiPathsAt(path24.resolve(root, rel2)).root();
27569
27262
  if (pathExists(waiDir)) {
27570
27263
  roots.push({ relativePath: rel2, waiDir });
27571
27264
  } else {
@@ -27596,7 +27289,7 @@ function importSpecTree(archive, options) {
27596
27289
  if (!info.compatible) {
27597
27290
  throw new Error("incompatible .waitree archive (format or minimum wairon version)");
27598
27291
  }
27599
- const destDir = path25.resolve(options.destDir ?? getProjectRoot());
27292
+ const destDir = path24.resolve(options.destDir ?? getProjectRoot());
27600
27293
  const occupied = hasSpecTree(destDir);
27601
27294
  const authored = hasAuthoredSpecs(destDir);
27602
27295
  if (authored && options.replaceExisting !== true) {
@@ -27627,21 +27320,21 @@ function importSpecTree(archive, options) {
27627
27320
  return result;
27628
27321
  }
27629
27322
  function liveSpecDirFor(rootDir) {
27630
- const modern = path25.join(rootDir, ".wai");
27631
- if (fs20.existsSync(modern)) return modern;
27632
- const legacy = path25.join(rootDir, ".wairon");
27633
- if (fs20.existsSync(legacy)) return legacy;
27323
+ const modern = path24.join(rootDir, ".wai");
27324
+ if (fs19.existsSync(modern)) return modern;
27325
+ const legacy = path24.join(rootDir, ".wairon");
27326
+ if (fs19.existsSync(legacy)) return legacy;
27634
27327
  return null;
27635
27328
  }
27636
27329
  function timestampSlug() {
27637
27330
  return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
27638
27331
  }
27639
- var fs20, path25, sdkPortal, STAGING_PREFIX, BACKUP_DIR;
27332
+ var fs19, path24, sdkPortal, STAGING_PREFIX, BACKUP_DIR;
27640
27333
  var init_treetransfer = __esm({
27641
27334
  "src/core/treetransfer.ts"() {
27642
27335
  "use strict";
27643
- fs20 = __toESM(require("fs"));
27644
- path25 = __toESM(require("path"));
27336
+ fs19 = __toESM(require("fs"));
27337
+ path24 = __toESM(require("path"));
27645
27338
  sdkPortal = __toESM(require_dist());
27646
27339
  init_paths();
27647
27340
  init_fs();
@@ -27670,11 +27363,11 @@ function mcpServersFor(access) {
27670
27363
  function yamlScalar(value) {
27671
27364
  return /[:#\n]/.test(value) ? `"${value.replace(/"/g, '\\"')}"` : value;
27672
27365
  }
27673
- var path26, MODEL_BY_TIER, TOOLS_BY_CLASS, ClaudeExporter;
27366
+ var path25, MODEL_BY_TIER, TOOLS_BY_CLASS, ClaudeExporter;
27674
27367
  var init_claude = __esm({
27675
27368
  "src/exporters/claude.ts"() {
27676
27369
  "use strict";
27677
- path26 = __toESM(require("path"));
27370
+ path25 = __toESM(require("path"));
27678
27371
  init_fs();
27679
27372
  MODEL_BY_TIER = {
27680
27373
  small: "haiku",
@@ -27697,7 +27390,7 @@ var init_claude = __esm({
27697
27390
  outputPath(ctx) {
27698
27391
  const { agent, target, projectRoot: projectRoot2 } = ctx;
27699
27392
  const outputDir = "outputDir" in target ? target.outputDir : ".claude/agents";
27700
- return path26.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
27393
+ return path25.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
27701
27394
  }
27702
27395
  export(ctx) {
27703
27396
  const { agent, renderedInstructions, budget } = ctx;
@@ -27724,11 +27417,11 @@ var init_claude = __esm({
27724
27417
  });
27725
27418
 
27726
27419
  // src/exporters/custom.ts
27727
- var path27, CustomExporter;
27420
+ var path26, CustomExporter;
27728
27421
  var init_custom = __esm({
27729
27422
  "src/exporters/custom.ts"() {
27730
27423
  "use strict";
27731
- path27 = __toESM(require("path"));
27424
+ path26 = __toESM(require("path"));
27732
27425
  init_fs();
27733
27426
  CustomExporter = class {
27734
27427
  constructor() {
@@ -27739,7 +27432,7 @@ var init_custom = __esm({
27739
27432
  if (!("outputDir" in target)) {
27740
27433
  throw new Error("CustomExporter requires target.outputDir");
27741
27434
  }
27742
- return path27.resolve(projectRoot2, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
27435
+ return path26.resolve(projectRoot2, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
27743
27436
  }
27744
27437
  export(ctx) {
27745
27438
  const { agent, target, renderedInstructions } = ctx;
@@ -27770,11 +27463,11 @@ function yamlString(value) {
27770
27463
  }
27771
27464
  return value;
27772
27465
  }
27773
- var path28, GeminiExporter;
27466
+ var path27, GeminiExporter;
27774
27467
  var init_gemini = __esm({
27775
27468
  "src/exporters/gemini.ts"() {
27776
27469
  "use strict";
27777
- path28 = __toESM(require("path"));
27470
+ path27 = __toESM(require("path"));
27778
27471
  init_fs();
27779
27472
  GeminiExporter = class {
27780
27473
  constructor() {
@@ -27783,7 +27476,7 @@ var init_gemini = __esm({
27783
27476
  outputPath(ctx) {
27784
27477
  const { agent, target, projectRoot: projectRoot2 } = ctx;
27785
27478
  const outputDir = "outputDir" in target ? target.outputDir : ".gemini/agents";
27786
- return path28.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
27479
+ return path27.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
27787
27480
  }
27788
27481
  export(ctx) {
27789
27482
  const { agent, renderedInstructions } = ctx;
@@ -27889,7 +27582,7 @@ function resolveExpectedOutputPaths(agents, projectConfig, projectRoot2 = getPro
27889
27582
  const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
27890
27583
  if (!targetConfig) continue;
27891
27584
  const ctx = { agent, projectRoot: projectRoot2, target: targetConfig };
27892
- expected.add(path29.resolve(getExporter(targetConfig).outputPath(ctx)));
27585
+ expected.add(path28.resolve(getExporter(targetConfig).outputPath(ctx)));
27893
27586
  }
27894
27587
  }
27895
27588
  return expected;
@@ -27901,11 +27594,11 @@ function resolveTargetConfig(agentTarget, projectConfig) {
27901
27594
  return t.type === targetType;
27902
27595
  });
27903
27596
  }
27904
- var path29;
27597
+ var path28;
27905
27598
  var init_generate = __esm({
27906
27599
  "src/exporters/generate.ts"() {
27907
27600
  "use strict";
27908
- path29 = __toESM(require("path"));
27601
+ path28 = __toESM(require("path"));
27909
27602
  init_templates();
27910
27603
  init_agent_resolver();
27911
27604
  init_execution_profile();
@@ -28072,12 +27765,12 @@ function digest(content) {
28072
27765
  return crypto4.createHash("sha256").update(normalized, "utf8").digest("hex");
28073
27766
  }
28074
27767
  function currentSpecDigests(root = getProjectRoot()) {
28075
- const mountDirs = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::")).map((s) => `${path30.resolve(root, s.projectPath).split(path30.sep).join("/")}/`);
27768
+ const mountDirs = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::")).map((s) => `${path29.resolve(root, s.projectPath).split(path29.sep).join("/")}/`);
28076
27769
  const out = {};
28077
27770
  for (const [abs, content] of snapshotSpecFiles()) {
28078
- const normalized = path30.resolve(abs).split(path30.sep).join("/");
27771
+ const normalized = path29.resolve(abs).split(path29.sep).join("/");
28079
27772
  if (mountDirs.some((dir) => normalized.startsWith(dir))) continue;
28080
- out[path30.relative(root, abs).split(path30.sep).join("/")] = digest(content);
27773
+ out[path29.relative(root, abs).split(path29.sep).join("/")] = digest(content);
28081
27774
  }
28082
27775
  return out;
28083
27776
  }
@@ -28135,7 +27828,7 @@ function currentChildPins(mounts, root = getProjectRoot()) {
28135
27828
  const pins = {};
28136
27829
  for (const mount of mounts) {
28137
27830
  if (!mount.projectPath || mount.id.includes("::")) continue;
28138
- const child = readLockRecordAt(path30.resolve(root, mount.projectPath));
27831
+ const child = readLockRecordAt(path29.resolve(root, mount.projectPath));
28139
27832
  if (child) pins[mount.id] = pinOf(child.stateId);
28140
27833
  }
28141
27834
  return pins;
@@ -28151,12 +27844,12 @@ function movedChildren(mounts, root = getProjectRoot()) {
28151
27844
  }
28152
27845
  return moved;
28153
27846
  }
28154
- var crypto4, path30;
27847
+ var crypto4, path29;
28155
27848
  var init_approval = __esm({
28156
27849
  "src/core/approval.ts"() {
28157
27850
  "use strict";
28158
27851
  crypto4 = __toESM(require("crypto"));
28159
- path30 = __toESM(require("path"));
27852
+ path29 = __toESM(require("path"));
28160
27853
  init_fs();
28161
27854
  init_specs2();
28162
27855
  init_lockfile();
@@ -28179,12 +27872,12 @@ function gitConfig(key) {
28179
27872
  function machineIdentity() {
28180
27873
  let user = "unknown";
28181
27874
  try {
28182
- user = os6.userInfo().username || user;
27875
+ user = os5.userInfo().username || user;
28183
27876
  } catch {
28184
27877
  }
28185
27878
  let host = "";
28186
27879
  try {
28187
- host = os6.hostname();
27880
+ host = os5.hostname();
28188
27881
  } catch {
28189
27882
  }
28190
27883
  return { id: host ? `${user}@${host}` : user, source: "os" };
@@ -28196,12 +27889,328 @@ function localApprover() {
28196
27889
  if (email) return { id: email, source: "git" };
28197
27890
  return machineIdentity();
28198
27891
  }
28199
- var import_child_process2, os6;
27892
+ var import_child_process2, os5;
28200
27893
  var init_approver = __esm({
28201
27894
  "src/core/approver.ts"() {
28202
27895
  "use strict";
28203
27896
  import_child_process2 = require("child_process");
27897
+ os5 = __toESM(require("os"));
27898
+ }
27899
+ });
27900
+
27901
+ // src/core/stamp.ts
27902
+ function versionStamp() {
27903
+ return `<!-- wairon-version: ${WAIRON_VERSION} -->`;
27904
+ }
27905
+ function readStampVersion(content) {
27906
+ const m = content.match(STAMP_RE);
27907
+ return m ? m[1] : null;
27908
+ }
27909
+ var STAMP_RE;
27910
+ var init_stamp = __esm({
27911
+ "src/core/stamp.ts"() {
27912
+ "use strict";
27913
+ init_defaults();
27914
+ STAMP_RE = /<!--\s*wairon-version:\s*([^\s]+)\s*-->/;
27915
+ }
27916
+ });
27917
+
27918
+ // src/utils/ai-guide.ts
27919
+ function globalGuideFilePath(targetType) {
27920
+ if (targetType === "claude") return path30.join(process.env["CLAUDE_CONFIG_DIR"] || path30.join(os6.homedir(), ".claude"), "CLAUDE.md");
27921
+ if (targetType === "gemini") return path30.join(process.env["GEMINI_CONFIG_DIR"] || path30.join(os6.homedir(), ".gemini"), "GEMINI.md");
27922
+ return null;
27923
+ }
27924
+ function localGuideFilePath(projectRoot2, targetType) {
27925
+ if (targetType === "claude") return path30.join(projectRoot2, ".claude", "CLAUDE.md");
27926
+ if (targetType === "gemini" || targetType === "agy") return path30.join(projectRoot2, ".gemini", "GEMINI.md");
27927
+ return null;
27928
+ }
27929
+ function injectGuide(filePath, scope) {
27930
+ const body = scope === "global" ? GLOBAL_GUIDE_BODY : LOCAL_GUIDE_BODY;
27931
+ const section = `
27932
+
27933
+ ${GUIDE_MARKER_START}
27934
+ ${versionStamp()}
27935
+ ${body}
27936
+ ${GUIDE_MARKER_END}
27937
+ `;
27938
+ const existing = fs20.existsSync(filePath) ? fs20.readFileSync(filePath, "utf-8") : "";
27939
+ const stripped = stripGuideSection(existing);
27940
+ const newContent = stripped.trimEnd() + section;
27941
+ fs20.mkdirSync(path30.dirname(filePath), { recursive: true });
27942
+ fs20.writeFileSync(filePath, newContent, "utf-8");
27943
+ }
27944
+ function stripGuideSection(content) {
27945
+ const start2 = content.indexOf(GUIDE_MARKER_START);
27946
+ const end = content.indexOf(GUIDE_MARKER_END);
27947
+ if (start2 === -1 || end === -1) return content;
27948
+ return content.slice(0, start2) + content.slice(end + GUIDE_MARKER_END.length);
27949
+ }
27950
+ function writeRootGuideDelegator(projectRoot2, targetType) {
27951
+ if (targetType === "claude") {
27952
+ const filePath = path30.join(projectRoot2, "CLAUDE.md");
27953
+ const content = `@.claude/CLAUDE.md
27954
+
27955
+ # Wairon SDD Project
27956
+
27957
+ This project uses the Wairon Spec-Driven Development (SDD) framework. The imported
27958
+ \`.claude/CLAUDE.md\` above is your complete operating guide \u2014 you already have the
27959
+ full context, so don't search the project to learn how wairon or SDD works.
27960
+
27961
+ To design or modify the system, invoke the **\`sdd-architect\`** skill
27962
+ (in \`.claude/skills/\`). Author and validate specs with the \`sdd_*\` MCP tools;
27963
+ the \`wairon\` CLI is the human developer's tool, not yours.
27964
+ `;
27965
+ fs20.writeFileSync(filePath, content, "utf-8");
27966
+ } else if (targetType === "gemini" || targetType === "agy") {
27967
+ const filePath = path30.join(projectRoot2, "GEMINI.md");
27968
+ const content = `# Wairon SDD Project
27969
+ ${GUIDE_MARKER_START}
27970
+ ${versionStamp()}
27971
+ ${LOCAL_GUIDE_BODY}
27972
+ ${GUIDE_MARKER_END}
27973
+ `;
27974
+ fs20.writeFileSync(filePath, content, "utf-8");
27975
+ } else if (targetType === "cursor") {
27976
+ const filePath = path30.join(projectRoot2, ".cursorrules");
27977
+ const content = `# Wairon SDD Project
27978
+
27979
+ This project uses the Wairon Spec-Driven Development (SDD) framework.
27980
+
27981
+ Refer to the rules in [.cursor/rules/](.cursor/rules/) for full instructions.
27982
+ `;
27983
+ fs20.writeFileSync(filePath, content, "utf-8");
27984
+ } else if (targetType === "copilot") {
27985
+ const filePath = path30.join(projectRoot2, ".github", "copilot-instructions.md");
27986
+ fs20.mkdirSync(path30.dirname(filePath), { recursive: true });
27987
+ const content = `# Wairon SDD Project
27988
+
27989
+ This project uses the Wairon Spec-Driven Development (SDD) framework.
27990
+
27991
+ Refer to the prompts in [.github/prompts/](.github/prompts/) for instructions.
27992
+ `;
27993
+ fs20.writeFileSync(filePath, content, "utf-8");
27994
+ } else if (targetType === "codex") {
27995
+ const filePath = path30.join(projectRoot2, ".codexrules");
27996
+ const content = `# Wairon SDD Project
27997
+
27998
+ Refer to [.codex/agents/](.codex/agents/) for full instructions.
27999
+ `;
28000
+ fs20.writeFileSync(filePath, content, "utf-8");
28001
+ }
28002
+ }
28003
+ function reinjectLocalGuides(projectRoot2, targetTypes) {
28004
+ const written = [];
28005
+ for (const type of targetTypes) {
28006
+ if (!GUIDE_TARGETS.includes(type)) continue;
28007
+ const guidePath = localGuideFilePath(projectRoot2, type);
28008
+ if (guidePath) {
28009
+ injectGuide(guidePath, "local");
28010
+ if (!written.includes(guidePath)) written.push(guidePath);
28011
+ }
28012
+ writeRootGuideDelegator(projectRoot2, type);
28013
+ }
28014
+ return written;
28015
+ }
28016
+ var fs20, os6, path30, GUIDE_MARKER_START, GUIDE_MARKER_END, GLOBAL_GUIDE_BODY, LOCAL_GUIDE_BODY, GUIDE_TARGETS;
28017
+ var init_ai_guide = __esm({
28018
+ "src/utils/ai-guide.ts"() {
28019
+ "use strict";
28020
+ fs20 = __toESM(require("fs"));
28204
28021
  os6 = __toESM(require("os"));
28022
+ path30 = __toESM(require("path"));
28023
+ init_stamp();
28024
+ GUIDE_MARKER_START = "<!-- wairon-guide-start -->";
28025
+ GUIDE_MARKER_END = "<!-- wairon-guide-end -->";
28026
+ GLOBAL_GUIDE_BODY = `## wairon \u2014 Spec-Driven Development (optional)
28027
+
28028
+ If \`.wai/specs/\` exists, the wairon SDD workflow is active; otherwise ignore it. wairon does not orchestrate sessions \u2014 it equips yours.
28029
+
28030
+ ### In SDD Projects:
28031
+ - **Source of Truth**: All architecture lives in the spec tree under \`.wai/specs/\` (L0 System \u2192 L1 Subsystem \u2192 L2 Component \u2192 L3 Interface \u2192 L4 Implementation \u2192 L5 Narrative). Do not edit generated agent config files under \`.claude/agents/\` (rebuilt via \`wairon generate\`).
28032
+ - **Validation**: Conformance checks (stereotype rules, cycle checks, reference integrity) are run via the \`sdd_validate_tree\` MCP tool.
28033
+ - **Operating Rules**:
28034
+ 1. **Skills**: Use \`sdd-architect\` to design (and \`sdd-implement\`, \`sdd-narrative\`, \`sdd-auditor\`). Refer to project's local guide file for detailed constraints.
28035
+ 2. **MCP Tools Only**: Author/validate specs *only* via \`sdd_*\` tools (e.g. \`sdd_initialize_system\`, \`sdd_validate_tree\`).
28036
+ 3. **No CLI Exec**: Do not run the \`wairon\` CLI (human tool). Use MCP tools \`sdd_validate_tree\` and \`sdd_get_status\` instead.
28037
+ 4. **Delegation**: Delegate implementation via the \`sdd-delegate\` skill \u2014 live agent briefs (\`sdd_get_agent_brief\` MCP tool / \`wairon-agent://\` resource) are composed per call and always current; no session restart. Generated agent files are an optional materialized view of the same topology. User-owned per-agent guidance may live in \`.wai/agents/<agent-id>.md\` (folded into every brief; scaffold via \`wairon agent customize <id>\`).
28038
+ 5. **Design First**: Complete spec and pass \`sdd_validate_tree\` before writing code.
28039
+ 6. **Consistency**: Code must match L3 interfaces and L5 narratives exactly. If the spec is wrong, stop and update the spec.
28040
+ 7. **Subprojects & Namespacing**: If a subsystem uses \`projectPath\` delegation, target its specs using namespaced IDs (e.g. \`subsystem::component\`). Use leading \`::\` to target root (e.g. \`::shared::type\`) and \`super::\` to go up a level (e.g. \`super::sibling\`). wairon automatically resolves the path and strips the prefix on writes.`;
28041
+ LOCAL_GUIDE_BODY = `## Wairon \u2014 Spec-Driven Development (you are operating inside it)
28042
+
28043
+ This project uses **wairon**. System specs live under \`.wai/specs/\` (L0 System \u2192 L1 Subsystem \u2192 L2 Component \u2192 L3 Interface \u2192 L4 Implementation \u2192 Narrative); agent topology and code are derived from it.
28044
+
28045
+ **Do NOT search files or read agent configs to learn about wairon or SDD. Use the context here and the \`sdd-architect\` skill to start.**
28046
+
28047
+ **Your first move: call the \`sdd_get_status\` MCP tool** (or \`wairon/sdd_get_status\`) to see the spec tree. Do not parse files or run CLI commands manually.
28048
+
28049
+ ### How you operate
28050
+ - **To design/modify specs**: Use **\`sdd-architect\`** skill (in \`.claude/skills/\` or \`.gemini/skills/\`).
28051
+ - **Manage specs via MCP tools only**: Use \`sdd_initialize_system\`, \`sdd_add_subsystem\`, \`sdd_add_component\`, \`sdd_define_interface\`, \`sdd_write_narrative\`, \`sdd_add_type\`, \`sdd_get_spec\`, \`sdd_delete_spec\`, \`sdd_validate_tree\`, and \`sdd_get_status\` (namespaced if needed). Do not edit specs manually.
28052
+ - **Subprojects & Namespacing (Chaining)**: If a subsystem defines a \`projectPath\`, its entire \`.wai/\` spec tree is recursively loaded and namespaced with the subsystem ID as a prefix (using \`::\`, e.g. \`billing::invoice::invoice_portal\`). Use the qualified namespaced ID with the parent MCP tools; wairon will resolve the path and strip the prefix automatically.
28053
+ - **Leading \`::\`**: Bypasses the local subsystem prefix to resolve absolute from the system root (e.g. \`::shared::error-type\`).
28054
+ - **\`super::\`**: Goes up one parent subsystem level (e.g. \`super::sibling_comp\`, \`super::super::parent_sibling\`).
28055
+ - **Do not run the \`wairon\` CLI**: Use \`sdd_validate_tree\` and \`sdd_get_status\` instead of CLI commands.
28056
+ - **Handoff to implementation**: Once design is complete and validates cleanly, tell the human: *"The specs are complete and validate. Please run \`wairon lock\` to confirm and freeze them."* No session restart is needed after the lock \u2014 delegate implementation right away via the \`sdd-delegate\` skill.
28057
+ - **To implement code**: Delegate via the \`sdd-delegate\` skill: fetch the component's live brief with the \`sdd_get_agent_brief\` MCP tool (or the \`wairon-agent://\` resource) and spawn a subagent from it. Briefs are composed per call from the current spec tree, so they are always current \u2014 never wait for a restart. Implementations must match L3 interfaces and L5 narratives exactly. Generated agent files under \`.claude/agents/\` are an optional materialized view of the same topology \u2014 the live briefs are canonical.
28058
+
28059
+ ### Rules (enforced by \`sdd_validate_tree\`)
28060
+ 1. **Design before code**: Complete spec and pass validator before writing source code.
28061
+ 2. **Human-in-the-loop**: Ask user approval for each spec layer before proceeding.
28062
+ 3. **Spec consistency**: If a 1:1 narrative match is incorrect or conflicts with L0 requirements, escalate a spec revision first. Never ship mismatched code.
28063
+ 4. **No persistence shortcuts & strict layers**: A Portal must never depend directly on a Store, Registry, or Adapter. Portal reads MAY go through a Repository/Index facade (passthrough reads need no per-entity Orchestrator ceremony), but a Portal narrative call or dispatch-table binding that reaches a write-effect facade method is an error (\`PORTAL_WRITE_SHORTCUT\`) \u2014 writes always route through an Orchestrator. Held domain state always lives in a dedicated data component, never as fields inside an Orchestrator. Two sanctioned shapes: the RECOMMENDED Repository pattern (owns Store + Registry + Index; consumers depend on the facade), or \u2014 for genuinely simple state \u2014 a deliberately standalone Store (workflow-layer consumers only, acknowledged with a lint.allow reason on the UNOWNED_STORE warning). Never combine Store/Registry/Index roles into one component, and never fold state into a consuming component because a link was refused.
28064
+
28065
+ ### Component Vocabulary
28066
+ * **Blocks**: Portal, Orchestrator, Supervisor, Actor, Store, Index, Query, Registry, Adapter, Observer.
28067
+ * **Patterns**: Repository \u2014 the composable pattern that \`owns\` its member blocks: one Store with its Registry, Indexes and Queries, and optionally an Adapter.
28068
+ * **Variants**: \`gateway\`, a Portal that authenticates, authorizes, validates or rate-limits before it dispatches. Logic is an Orchestrator, and \`dependencyClass: pure | read\` bounds what it may depend on (unset = a workflow). Specialist and Gateway are retired stereotypes.
28069
+ * Use \`owns\` for private member containment (exactly one hop) and \`dependsOn\` for collaborators. Never use generic suffixes like "Manager", "Helper", or "Utils".`;
28070
+ GUIDE_TARGETS = ["claude", "gemini", "agy", "cursor", "copilot", "codex"];
28071
+ }
28072
+ });
28073
+
28074
+ // src/core/context.ts
28075
+ function contextDir(...segments) {
28076
+ return aiDir("context", ...segments);
28077
+ }
28078
+ function hasContext() {
28079
+ return pathExists(CONTEXT_PATHS.projectMd());
28080
+ }
28081
+ function readProjectContext() {
28082
+ return readFileOrNull(CONTEXT_PATHS.projectMd());
28083
+ }
28084
+ function readArchitectureContext() {
28085
+ return readFileOrNull(CONTEXT_PATHS.architectureMd());
28086
+ }
28087
+ function writeDomainsDoc(content) {
28088
+ return writeFileIfChanged(CONTEXT_PATHS.domainsMd(), content);
28089
+ }
28090
+ function writeGuideDoc(content) {
28091
+ return writeFileIfChanged(CONTEXT_PATHS.waironGuideMd(), content);
28092
+ }
28093
+ function derivedDocPaths() {
28094
+ return [CONTEXT_PATHS.waironGuideMd(), CONTEXT_PATHS.domainsMd()];
28095
+ }
28096
+ function renderDomainsDoc() {
28097
+ const domains = resolveDomains();
28098
+ const lines = [
28099
+ versionStamp(),
28100
+ `# Domains`,
28101
+ ``,
28102
+ `> Auto-generated by wairon. The human developer rebuilds this with \`wairon generate\`. `,
28103
+ ``
28104
+ ];
28105
+ if (domains.length === 0) {
28106
+ lines.push("_No domains yet. Define subsystems in the spec tree, or add free-standing domains with `wairon domains add`._");
28107
+ } else {
28108
+ lines.push(`**${domains.length} domain(s).**`);
28109
+ lines.push("");
28110
+ lines.push("| ID | Name | Source | Owned paths |");
28111
+ lines.push("|----|------|--------|-------------|");
28112
+ for (const d of domains) {
28113
+ const name = d.name ?? d.id;
28114
+ const source = d.boundTo ? `subsystem \`${d.boundTo}\`` : "free-standing";
28115
+ lines.push(`| \`${d.id}\` | ${name} | ${source} | \`${d.ownedPaths.join("`, `") || "\u2014"}\` |`);
28116
+ }
28117
+ }
28118
+ return lines.join("\n") + "\n";
28119
+ }
28120
+ function renderWaironGuide() {
28121
+ const domains = resolveDomains();
28122
+ let projectName = "this project";
28123
+ try {
28124
+ projectName = projectConfigRepository.load()?.name ?? projectName;
28125
+ } catch {
28126
+ }
28127
+ const projectCtx = readProjectContext();
28128
+ const archCtx = readArchitectureContext();
28129
+ const lines = [
28130
+ versionStamp(),
28131
+ `<!-- wairon-generated \u2014 do not edit directly; the human developer rebuilds this with \`wairon generate\` -->`,
28132
+ ``
28133
+ ];
28134
+ if (projectCtx) {
28135
+ lines.push(`# Project Context \u2014 ${projectName}`);
28136
+ lines.push("");
28137
+ lines.push(projectCtx.trim());
28138
+ lines.push("");
28139
+ lines.push("---");
28140
+ lines.push("");
28141
+ }
28142
+ if (archCtx) {
28143
+ lines.push("# Architecture");
28144
+ lines.push("");
28145
+ lines.push(archCtx.trim());
28146
+ lines.push("");
28147
+ lines.push("---");
28148
+ lines.push("");
28149
+ }
28150
+ lines.push(`# Domain Map (${domains.length} domain${domains.length !== 1 ? "s" : ""})`);
28151
+ lines.push("");
28152
+ if (domains.length === 0) {
28153
+ lines.push("_No domains yet._");
28154
+ } else {
28155
+ lines.push("| ID | Source | Name |");
28156
+ lines.push("|----|--------|------|");
28157
+ for (const d of domains) {
28158
+ const name = d.name ?? d.id;
28159
+ const source = d.boundTo ? `subsystem \`${d.boundTo}\`` : "free-standing";
28160
+ lines.push(`| \`${d.id}\` | ${source} | ${name} |`);
28161
+ }
28162
+ }
28163
+ lines.push("");
28164
+ lines.push("---");
28165
+ lines.push("");
28166
+ const mcpSettingsPath = require("path").join(process.cwd(), ".claude", "settings.json");
28167
+ let mcpInstalled = false;
28168
+ try {
28169
+ const s = JSON.parse(require("fs").readFileSync(mcpSettingsPath, "utf8"));
28170
+ mcpInstalled = !!s?.mcpServers?.wairon;
28171
+ } catch {
28172
+ }
28173
+ if (mcpInstalled) {
28174
+ lines.push("# wairon MCP Tools");
28175
+ lines.push("");
28176
+ lines.push("The **wairon MCP server** is active in this project. The tools are self-describing; call them directly:");
28177
+ lines.push("");
28178
+ lines.push("- Authoring: `sdd_initialize_system`, `sdd_add_subsystem`, `sdd_set_public_interfaces`, `sdd_set_subsystem_project_path`, `sdd_add_component`, `sdd_define_interface`, `sdd_set_endpoints`, `sdd_write_narrative`, `sdd_add_type`.");
28179
+ lines.push("- Reading & maintenance: `sdd_get_spec`, `sdd_update_spec`, `sdd_delete_spec`, `sdd_validate_tree`, `sdd_get_status`.");
28180
+ lines.push("- Topology: `listAgents`, `getAgent`, `listDomains`, `validateTopology`, `getProjectConfig`.");
28181
+ lines.push("");
28182
+ lines.push("Use these MCP tools to query and change project state \u2014 never the `wairon` CLI (that is the human developer's tool).");
28183
+ lines.push("");
28184
+ lines.push("---");
28185
+ lines.push("");
28186
+ }
28187
+ lines.push(GLOBAL_GUIDE_BODY);
28188
+ lines.push("");
28189
+ return lines.join("\n");
28190
+ }
28191
+ function syncContextFiles() {
28192
+ const domainsContent = renderDomainsDoc();
28193
+ const guideContent = renderWaironGuide();
28194
+ const domainsUpdated = writeDomainsDoc(domainsContent);
28195
+ const guideUpdated = writeGuideDoc(guideContent);
28196
+ return { domainsUpdated, guideUpdated };
28197
+ }
28198
+ var CONTEXT_PATHS;
28199
+ var init_context = __esm({
28200
+ "src/core/context.ts"() {
28201
+ "use strict";
28202
+ init_fs();
28203
+ init_ai_guide();
28204
+ init_stamp();
28205
+ init_domain_projector();
28206
+ init_project_config();
28207
+ CONTEXT_PATHS = {
28208
+ dir: () => contextDir(),
28209
+ projectMd: () => contextDir("project.md"),
28210
+ architectureMd: () => contextDir("architecture.md"),
28211
+ domainsMd: () => contextDir("domains.md"),
28212
+ waironGuideMd: () => contextDir("wairon-guide.md")
28213
+ };
28205
28214
  }
28206
28215
  });
28207
28216
 
@@ -28240,7 +28249,6 @@ var init_core = __esm({
28240
28249
  init_statehash();
28241
28250
  init_agent_resolver();
28242
28251
  init_skills();
28243
- init_context();
28244
28252
  init_surfaces();
28245
28253
  init_openapi();
28246
28254
  init_packstore();
@@ -28260,6 +28268,7 @@ var init_core = __esm({
28260
28268
  init_budget_policy();
28261
28269
  init_ai_guide();
28262
28270
  init_stamp();
28271
+ init_context();
28263
28272
  }
28264
28273
  });
28265
28274
 
@@ -31132,6 +31141,15 @@ function reinjectLocalGuides2(projectRoot2, targets) {
31132
31141
  function readStampVersion2(content) {
31133
31142
  return readStampVersion(content);
31134
31143
  }
31144
+ function syncContextFiles2() {
31145
+ return syncContextFiles();
31146
+ }
31147
+ function hasContext2() {
31148
+ return hasContext();
31149
+ }
31150
+ function derivedDocPaths2() {
31151
+ return derivedDocPaths();
31152
+ }
31135
31153
  async function runSubsystemAdd(id, options = {}) {
31136
31154
  logger.header("wairon subsystem add");
31137
31155
  if (!projectConfigExists2()) {
@@ -31240,7 +31258,6 @@ async function runSkillsInstall() {
31240
31258
  // src/commands/init.ts
31241
31259
  init_defaults();
31242
31260
  init_project();
31243
- init_context();
31244
31261
  async function runInit(options = {}) {
31245
31262
  logger.header("wairon init");
31246
31263
  const cwd = process.cwd();
@@ -31531,7 +31548,7 @@ async function executeInit(projectName, projectConfig, guidePlan) {
31531
31548
  logger.warn(`Failed to automatically register MCP server for Antigravity: ${err}`);
31532
31549
  }
31533
31550
  }
31534
- syncContextFiles();
31551
+ syncContextFiles2();
31535
31552
  logger.verbose("Context files seeded in .wai/context/");
31536
31553
  try {
31537
31554
  ensureProjectInitialized2(projectName);
@@ -31720,7 +31737,6 @@ init_logger();
31720
31737
  init_paths();
31721
31738
  init_errors();
31722
31739
  init_base();
31723
- init_context();
31724
31740
  init_fs();
31725
31741
  init_project();
31726
31742
  var WAIRON_AGENT_FILE = /-(owner|implementer|architect)\.md$/;
@@ -31789,8 +31805,8 @@ async function generateLayer(options = {}) {
31789
31805
  }
31790
31806
  }
31791
31807
  if (options.dryRun) return;
31792
- if (hasContext()) {
31793
- syncContextFiles();
31808
+ if (hasContext2()) {
31809
+ syncContextFiles2();
31794
31810
  logger.verbose("Context files synced.");
31795
31811
  }
31796
31812
  if (pathExists(AI_PATHS.specsSystem())) {
@@ -32964,7 +32980,6 @@ init_paths();
32964
32980
  init_errors();
32965
32981
  init_fs();
32966
32982
  init_provision();
32967
- init_context();
32968
32983
  init_skills();
32969
32984
  init_specs2();
32970
32985
  init_lockfile();
@@ -33107,10 +33122,8 @@ async function runDoctor(options = {}) {
33107
33122
  }
33108
33123
  }
33109
33124
  console.log(import_chalk12.default.bold(`Generated files ${import_chalk12.default.gray("(stale = older than installed)")}`));
33110
- for (const [label, p] of [
33111
- [".wai/context/wairon-guide.md", CONTEXT_PATHS.waironGuideMd()],
33112
- [".wai/context/domains.md", CONTEXT_PATHS.domainsMd()]
33113
- ]) {
33125
+ for (const p of derivedDocPaths2()) {
33126
+ const label = path38.relative(getProjectRoot(), p).replace(/\\/g, "/");
33114
33127
  if (!pathExists(p)) {
33115
33128
  line(tally, "warn", `${label} \u2014 not generated yet (run \`wairon generate\`)`);
33116
33129
  continue;
@@ -33252,7 +33265,7 @@ async function applyFixes() {
33252
33265
  }
33253
33266
  console.log(import_chalk12.default.bold("Applying fixes\u2026"));
33254
33267
  try {
33255
- syncContextFiles();
33268
+ syncContextFiles2();
33256
33269
  exportSddSkills(targets);
33257
33270
  const guides = reinjectLocalGuides2(process.cwd(), targets);
33258
33271
  console.log(` ${icon("ok")} Regenerated context files, skills, and ${guides.length} local guide(s).`);