@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 +384 -371
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +521 -537
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3401,7 +3401,6 @@ __export(src_exports, {
|
|
|
3401
3401
|
BuiltinTargetSchema: () => BuiltinTargetSchema,
|
|
3402
3402
|
BundleNotFoundError: () => BundleNotFoundError,
|
|
3403
3403
|
COMPONENT_AUTH_SOURCE: () => COMPONENT_AUTH_SOURCE,
|
|
3404
|
-
CONTEXT_PATHS: () => CONTEXT_PATHS,
|
|
3405
3404
|
CarriedDebtKindSchema: () => CarriedDebtKindSchema,
|
|
3406
3405
|
CarriedDebtSchema: () => CarriedDebtSchema,
|
|
3407
3406
|
CarriedFindingSchema: () => CarriedFindingSchema,
|
|
@@ -3556,7 +3555,6 @@ __export(src_exports, {
|
|
|
3556
3555
|
computeStateIdAt: () => computeStateIdAt,
|
|
3557
3556
|
conceptNoun: () => conceptNoun,
|
|
3558
3557
|
consumedContractInputs: () => consumedContractInputs,
|
|
3559
|
-
contextDir: () => contextDir,
|
|
3560
3558
|
createAgentRecord: () => createAgentRecord,
|
|
3561
3559
|
createChainedSubsystem: () => createChainedSubsystem,
|
|
3562
3560
|
createEmptyRegistry: () => createEmptyRegistry,
|
|
@@ -3579,6 +3577,7 @@ __export(src_exports, {
|
|
|
3579
3577
|
deleteTypeSpec: () => deleteTypeSpec,
|
|
3580
3578
|
deregisterPackRef: () => deregisterPackRef,
|
|
3581
3579
|
deriveExecutionProfile: () => deriveExecutionProfile,
|
|
3580
|
+
derivedDocPaths: () => derivedDocPaths,
|
|
3582
3581
|
describeApprover: () => describeApprover,
|
|
3583
3582
|
detectDomainCandidates: () => detectDomainCandidates,
|
|
3584
3583
|
diagnoseProjectPacks: () => diagnoseProjectPacks,
|
|
@@ -3633,7 +3632,6 @@ __export(src_exports, {
|
|
|
3633
3632
|
globalPacksEnabled: () => globalPacksEnabled,
|
|
3634
3633
|
globalTemplatesDir: () => globalTemplatesDir,
|
|
3635
3634
|
globalVariantsDir: () => globalVariantsDir,
|
|
3636
|
-
hasArchitectureContext: () => hasArchitectureContext,
|
|
3637
3635
|
hasAuthoredSpecs: () => hasAuthoredSpecs,
|
|
3638
3636
|
hasContext: () => hasContext,
|
|
3639
3637
|
hasFunctionBody: () => hasFunctionBody,
|
|
@@ -3732,14 +3730,12 @@ __export(src_exports, {
|
|
|
3732
3730
|
projectSubsystemSurface: () => projectSubsystemSurface,
|
|
3733
3731
|
provisionProject: () => provisionProject,
|
|
3734
3732
|
qualifiedTypeId: () => qualifiedTypeId,
|
|
3735
|
-
readArchitectureContext: () => readArchitectureContext,
|
|
3736
3733
|
readFileOrNull: () => readFileOrNull,
|
|
3737
3734
|
readJsonFile: () => readJsonFile,
|
|
3738
3735
|
readLockRecord: () => readLockRecord,
|
|
3739
3736
|
readLockRecordAt: () => readLockRecordAt,
|
|
3740
3737
|
readLockState: () => readLockState,
|
|
3741
3738
|
readManifest: () => readManifest,
|
|
3742
|
-
readProjectContext: () => readProjectContext,
|
|
3743
3739
|
readResource: () => readResource,
|
|
3744
3740
|
readSkillResource: () => readSkillResource,
|
|
3745
3741
|
readStampVersion: () => readStampVersion,
|
|
@@ -3756,9 +3752,7 @@ __export(src_exports, {
|
|
|
3756
3752
|
renameComponent: () => renameComponent,
|
|
3757
3753
|
renameMethod: () => renameMethod,
|
|
3758
3754
|
renderDiagram: () => renderDiagram,
|
|
3759
|
-
renderDomainsDoc: () => renderDomainsDoc,
|
|
3760
3755
|
renderTemplateInstructions: () => renderTemplateInstructions,
|
|
3761
|
-
renderWaironGuide: () => renderWaironGuide,
|
|
3762
3756
|
repairForeignStepFields: () => repairForeignStepFields,
|
|
3763
3757
|
resolveAgentTopology: () => resolveAgentTopology,
|
|
3764
3758
|
resolveBudget: () => resolveBudget,
|
|
@@ -3821,12 +3815,10 @@ __export(src_exports, {
|
|
|
3821
3815
|
validateRegistry: () => validateRegistry,
|
|
3822
3816
|
validateSddTree: () => validateSddTree,
|
|
3823
3817
|
workspaceFor: () => workspaceFor,
|
|
3824
|
-
writeArchitectureContext: () => writeArchitectureContext,
|
|
3825
3818
|
writeFile: () => writeFile,
|
|
3826
3819
|
writeFileIfChanged: () => writeFileIfChanged,
|
|
3827
3820
|
writeJsonFile: () => writeJsonFile,
|
|
3828
3821
|
writeLockRecord: () => writeLockRecord,
|
|
3829
|
-
writeProjectContext: () => writeProjectContext,
|
|
3830
3822
|
writeRootGuideDelegator: () => writeRootGuideDelegator,
|
|
3831
3823
|
writeYamlFile: () => writeYamlFile
|
|
3832
3824
|
});
|
|
@@ -11662,7 +11654,7 @@ function defaultTargetConfig(type) {
|
|
|
11662
11654
|
enabled: true
|
|
11663
11655
|
};
|
|
11664
11656
|
}
|
|
11665
|
-
var WAIRON_VERSION = "5.1.1-dev.
|
|
11657
|
+
var WAIRON_VERSION = "5.1.1-dev.61";
|
|
11666
11658
|
var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
11667
11659
|
var ARCHITECT_AGENT_ID = "agent-architect";
|
|
11668
11660
|
var ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -26347,390 +26339,97 @@ function readResource(resourceId) {
|
|
|
26347
26339
|
return readSkillResource(resourceId);
|
|
26348
26340
|
}
|
|
26349
26341
|
|
|
26350
|
-
// src/
|
|
26342
|
+
// src/core/treetransfer.ts
|
|
26351
26343
|
var fs17 = __toESM(require("fs"));
|
|
26352
|
-
var os4 = __toESM(require("os"));
|
|
26353
26344
|
var path22 = __toESM(require("path"));
|
|
26354
|
-
|
|
26355
|
-
|
|
26356
|
-
|
|
26357
|
-
|
|
26358
|
-
|
|
26359
|
-
var STAMP_RE = /<!--\s*wairon-version:\s*([^\s]+)\s*-->/;
|
|
26360
|
-
function readStampVersion(content) {
|
|
26361
|
-
const m = content.match(STAMP_RE);
|
|
26362
|
-
return m ? m[1] : null;
|
|
26363
|
-
}
|
|
26364
|
-
|
|
26365
|
-
// src/utils/ai-guide.ts
|
|
26366
|
-
var GUIDE_MARKER_START = "<!-- wairon-guide-start -->";
|
|
26367
|
-
var GUIDE_MARKER_END = "<!-- wairon-guide-end -->";
|
|
26368
|
-
var GLOBAL_GUIDE_BODY = `## wairon \u2014 Spec-Driven Development (optional)
|
|
26369
|
-
|
|
26370
|
-
If \`.wai/specs/\` exists, the wairon SDD workflow is active; otherwise ignore it. wairon does not orchestrate sessions \u2014 it equips yours.
|
|
26371
|
-
|
|
26372
|
-
### In SDD Projects:
|
|
26373
|
-
- **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\`).
|
|
26374
|
-
- **Validation**: Conformance checks (stereotype rules, cycle checks, reference integrity) are run via the \`sdd_validate_tree\` MCP tool.
|
|
26375
|
-
- **Operating Rules**:
|
|
26376
|
-
1. **Skills**: Use \`sdd-architect\` to design (and \`sdd-implement\`, \`sdd-narrative\`, \`sdd-auditor\`). Refer to project's local guide file for detailed constraints.
|
|
26377
|
-
2. **MCP Tools Only**: Author/validate specs *only* via \`sdd_*\` tools (e.g. \`sdd_initialize_system\`, \`sdd_validate_tree\`).
|
|
26378
|
-
3. **No CLI Exec**: Do not run the \`wairon\` CLI (human tool). Use MCP tools \`sdd_validate_tree\` and \`sdd_get_status\` instead.
|
|
26379
|
-
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>\`).
|
|
26380
|
-
5. **Design First**: Complete spec and pass \`sdd_validate_tree\` before writing code.
|
|
26381
|
-
6. **Consistency**: Code must match L3 interfaces and L5 narratives exactly. If the spec is wrong, stop and update the spec.
|
|
26382
|
-
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.`;
|
|
26383
|
-
var LOCAL_GUIDE_BODY = `## Wairon \u2014 Spec-Driven Development (you are operating inside it)
|
|
26384
|
-
|
|
26385
|
-
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.
|
|
26386
|
-
|
|
26387
|
-
**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.**
|
|
26388
|
-
|
|
26389
|
-
**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.
|
|
26390
|
-
|
|
26391
|
-
### How you operate
|
|
26392
|
-
- **To design/modify specs**: Use **\`sdd-architect\`** skill (in \`.claude/skills/\` or \`.gemini/skills/\`).
|
|
26393
|
-
- **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.
|
|
26394
|
-
- **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.
|
|
26395
|
-
- **Leading \`::\`**: Bypasses the local subsystem prefix to resolve absolute from the system root (e.g. \`::shared::error-type\`).
|
|
26396
|
-
- **\`super::\`**: Goes up one parent subsystem level (e.g. \`super::sibling_comp\`, \`super::super::parent_sibling\`).
|
|
26397
|
-
- **Do not run the \`wairon\` CLI**: Use \`sdd_validate_tree\` and \`sdd_get_status\` instead of CLI commands.
|
|
26398
|
-
- **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.
|
|
26399
|
-
- **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.
|
|
26400
|
-
|
|
26401
|
-
### Rules (enforced by \`sdd_validate_tree\`)
|
|
26402
|
-
1. **Design before code**: Complete spec and pass validator before writing source code.
|
|
26403
|
-
2. **Human-in-the-loop**: Ask user approval for each spec layer before proceeding.
|
|
26404
|
-
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.
|
|
26405
|
-
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.
|
|
26406
|
-
|
|
26407
|
-
### Component Vocabulary
|
|
26408
|
-
* **Blocks**: Portal, Orchestrator, Supervisor, Actor, Store, Index, Query, Registry, Adapter, Observer.
|
|
26409
|
-
* **Patterns**: Repository \u2014 the composable pattern that \`owns\` its member blocks: one Store with its Registry, Indexes and Queries, and optionally an Adapter.
|
|
26410
|
-
* **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.
|
|
26411
|
-
* Use \`owns\` for private member containment (exactly one hop) and \`dependsOn\` for collaborators. Never use generic suffixes like "Manager", "Helper", or "Utils".`;
|
|
26412
|
-
function globalGuideFilePath(targetType) {
|
|
26413
|
-
if (targetType === "claude") return path22.join(process.env["CLAUDE_CONFIG_DIR"] || path22.join(os4.homedir(), ".claude"), "CLAUDE.md");
|
|
26414
|
-
if (targetType === "gemini") return path22.join(process.env["GEMINI_CONFIG_DIR"] || path22.join(os4.homedir(), ".gemini"), "GEMINI.md");
|
|
26415
|
-
return null;
|
|
26416
|
-
}
|
|
26417
|
-
function localGuideFilePath(projectRoot, targetType) {
|
|
26418
|
-
if (targetType === "claude") return path22.join(projectRoot, ".claude", "CLAUDE.md");
|
|
26419
|
-
if (targetType === "gemini" || targetType === "agy") return path22.join(projectRoot, ".gemini", "GEMINI.md");
|
|
26420
|
-
return null;
|
|
26345
|
+
var sdkPortal = __toESM(require_dist());
|
|
26346
|
+
var STAGING_PREFIX = ".wai-staging-";
|
|
26347
|
+
var BACKUP_DIR = ".wai-backups";
|
|
26348
|
+
function buildTreeArchive2(roots, projectName, stateId, includeDerived) {
|
|
26349
|
+
return sdkPortal.buildTreeArchive(roots, projectName, stateId, includeDerived);
|
|
26421
26350
|
}
|
|
26422
|
-
function
|
|
26423
|
-
|
|
26424
|
-
const section = `
|
|
26425
|
-
|
|
26426
|
-
${GUIDE_MARKER_START}
|
|
26427
|
-
${versionStamp()}
|
|
26428
|
-
${body}
|
|
26429
|
-
${GUIDE_MARKER_END}
|
|
26430
|
-
`;
|
|
26431
|
-
const existing = fs17.existsSync(filePath) ? fs17.readFileSync(filePath, "utf-8") : "";
|
|
26432
|
-
const stripped = stripGuideSection(existing);
|
|
26433
|
-
const newContent = stripped.trimEnd() + section;
|
|
26434
|
-
fs17.mkdirSync(path22.dirname(filePath), { recursive: true });
|
|
26435
|
-
fs17.writeFileSync(filePath, newContent, "utf-8");
|
|
26351
|
+
function inspectTreeArchive2(archive) {
|
|
26352
|
+
return sdkPortal.inspectTreeArchive(archive);
|
|
26436
26353
|
}
|
|
26437
|
-
function
|
|
26438
|
-
const
|
|
26439
|
-
|
|
26440
|
-
if (start === -1 || end === -1) return content;
|
|
26441
|
-
return content.slice(0, start) + content.slice(end + GUIDE_MARKER_END.length);
|
|
26354
|
+
function extractTreeArchive2(archive, destDir, refuseExecutableEntries) {
|
|
26355
|
+
const limits = void 0;
|
|
26356
|
+
return sdkPortal.extractTreeArchive(archive, destDir, limits, refuseExecutableEntries);
|
|
26442
26357
|
}
|
|
26443
|
-
function
|
|
26444
|
-
if (
|
|
26445
|
-
|
|
26446
|
-
const content = `@.claude/CLAUDE.md
|
|
26447
|
-
|
|
26448
|
-
# Wairon SDD Project
|
|
26449
|
-
|
|
26450
|
-
This project uses the Wairon Spec-Driven Development (SDD) framework. The imported
|
|
26451
|
-
\`.claude/CLAUDE.md\` above is your complete operating guide \u2014 you already have the
|
|
26452
|
-
full context, so don't search the project to learn how wairon or SDD works.
|
|
26453
|
-
|
|
26454
|
-
To design or modify the system, invoke the **\`sdd-architect\`** skill
|
|
26455
|
-
(in \`.claude/skills/\`). Author and validate specs with the \`sdd_*\` MCP tools;
|
|
26456
|
-
the \`wairon\` CLI is the human developer's tool, not yours.
|
|
26457
|
-
`;
|
|
26458
|
-
fs17.writeFileSync(filePath, content, "utf-8");
|
|
26459
|
-
} else if (targetType === "gemini" || targetType === "agy") {
|
|
26460
|
-
const filePath = path22.join(projectRoot, "GEMINI.md");
|
|
26461
|
-
const content = `# Wairon SDD Project
|
|
26462
|
-
${GUIDE_MARKER_START}
|
|
26463
|
-
${versionStamp()}
|
|
26464
|
-
${LOCAL_GUIDE_BODY}
|
|
26465
|
-
${GUIDE_MARKER_END}
|
|
26466
|
-
`;
|
|
26467
|
-
fs17.writeFileSync(filePath, content, "utf-8");
|
|
26468
|
-
} else if (targetType === "cursor") {
|
|
26469
|
-
const filePath = path22.join(projectRoot, ".cursorrules");
|
|
26470
|
-
const content = `# Wairon SDD Project
|
|
26471
|
-
|
|
26472
|
-
This project uses the Wairon Spec-Driven Development (SDD) framework.
|
|
26473
|
-
|
|
26474
|
-
Refer to the rules in [.cursor/rules/](.cursor/rules/) for full instructions.
|
|
26475
|
-
`;
|
|
26476
|
-
fs17.writeFileSync(filePath, content, "utf-8");
|
|
26477
|
-
} else if (targetType === "copilot") {
|
|
26478
|
-
const filePath = path22.join(projectRoot, ".github", "copilot-instructions.md");
|
|
26479
|
-
fs17.mkdirSync(path22.dirname(filePath), { recursive: true });
|
|
26480
|
-
const content = `# Wairon SDD Project
|
|
26481
|
-
|
|
26482
|
-
This project uses the Wairon Spec-Driven Development (SDD) framework.
|
|
26483
|
-
|
|
26484
|
-
Refer to the prompts in [.github/prompts/](.github/prompts/) for instructions.
|
|
26485
|
-
`;
|
|
26486
|
-
fs17.writeFileSync(filePath, content, "utf-8");
|
|
26487
|
-
} else if (targetType === "codex") {
|
|
26488
|
-
const filePath = path22.join(projectRoot, ".codexrules");
|
|
26489
|
-
const content = `# Wairon SDD Project
|
|
26490
|
-
|
|
26491
|
-
Refer to [.codex/agents/](.codex/agents/) for full instructions.
|
|
26492
|
-
`;
|
|
26493
|
-
fs17.writeFileSync(filePath, content, "utf-8");
|
|
26358
|
+
function hasSpecTree(root) {
|
|
26359
|
+
if (pathExists(aiPathsAt(root).projectConfig())) {
|
|
26360
|
+
return true;
|
|
26494
26361
|
}
|
|
26362
|
+
return false;
|
|
26495
26363
|
}
|
|
26496
|
-
|
|
26497
|
-
|
|
26498
|
-
const
|
|
26499
|
-
|
|
26500
|
-
|
|
26501
|
-
|
|
26502
|
-
|
|
26503
|
-
|
|
26504
|
-
|
|
26364
|
+
function hasAuthoredSpecs(root) {
|
|
26365
|
+
const paths = aiPathsAt(root);
|
|
26366
|
+
const specsDir = paths.specsDir();
|
|
26367
|
+
if (!pathExists(specsDir)) {
|
|
26368
|
+
return false;
|
|
26369
|
+
}
|
|
26370
|
+
const systemIndex = path22.resolve(paths.specsSystem());
|
|
26371
|
+
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
26372
|
+
if (path22.resolve(file) !== systemIndex) {
|
|
26373
|
+
return true;
|
|
26505
26374
|
}
|
|
26506
|
-
writeRootGuideDelegator(projectRoot, type);
|
|
26507
26375
|
}
|
|
26508
|
-
return
|
|
26509
|
-
}
|
|
26510
|
-
|
|
26511
|
-
// src/core/context.ts
|
|
26512
|
-
function contextDir(...segments) {
|
|
26513
|
-
return aiDir("context", ...segments);
|
|
26514
|
-
}
|
|
26515
|
-
var CONTEXT_PATHS = {
|
|
26516
|
-
dir: () => contextDir(),
|
|
26517
|
-
projectMd: () => contextDir("project.md"),
|
|
26518
|
-
architectureMd: () => contextDir("architecture.md"),
|
|
26519
|
-
domainsMd: () => contextDir("domains.md"),
|
|
26520
|
-
waironGuideMd: () => contextDir("wairon-guide.md")
|
|
26521
|
-
};
|
|
26522
|
-
function hasContext() {
|
|
26523
|
-
return pathExists(CONTEXT_PATHS.projectMd());
|
|
26524
|
-
}
|
|
26525
|
-
function hasArchitectureContext() {
|
|
26526
|
-
return pathExists(CONTEXT_PATHS.architectureMd());
|
|
26527
|
-
}
|
|
26528
|
-
function readProjectContext() {
|
|
26529
|
-
return readFileOrNull(CONTEXT_PATHS.projectMd());
|
|
26530
|
-
}
|
|
26531
|
-
function readArchitectureContext() {
|
|
26532
|
-
return readFileOrNull(CONTEXT_PATHS.architectureMd());
|
|
26533
|
-
}
|
|
26534
|
-
function writeProjectContext(content) {
|
|
26535
|
-
writeFile(CONTEXT_PATHS.projectMd(), content);
|
|
26376
|
+
return false;
|
|
26536
26377
|
}
|
|
26537
|
-
function
|
|
26538
|
-
|
|
26378
|
+
function createStagingDir(destDir) {
|
|
26379
|
+
const root = path22.resolve(destDir);
|
|
26380
|
+
fs17.mkdirSync(root, { recursive: true });
|
|
26381
|
+
const stagingDir = fs17.mkdtempSync(path22.join(root, STAGING_PREFIX));
|
|
26382
|
+
return stagingDir;
|
|
26539
26383
|
}
|
|
26540
|
-
function
|
|
26541
|
-
const
|
|
26542
|
-
const
|
|
26543
|
-
|
|
26544
|
-
|
|
26545
|
-
|
|
26546
|
-
|
|
26547
|
-
|
|
26548
|
-
|
|
26549
|
-
|
|
26550
|
-
|
|
26551
|
-
|
|
26552
|
-
|
|
26553
|
-
|
|
26554
|
-
|
|
26555
|
-
|
|
26556
|
-
for (const d of domains) {
|
|
26557
|
-
const name = d.name ?? d.id;
|
|
26558
|
-
const source = d.boundTo ? `subsystem \`${d.boundTo}\`` : "free-standing";
|
|
26559
|
-
lines.push(`| \`${d.id}\` | ${name} | ${source} | \`${d.ownedPaths.join("`, `") || "\u2014"}\` |`);
|
|
26384
|
+
function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
|
|
26385
|
+
const root = path22.resolve(destDir);
|
|
26386
|
+
const backupDir = path22.join(root, BACKUP_DIR, timestampSlug());
|
|
26387
|
+
let backedUp = false;
|
|
26388
|
+
for (const rootRel of roots) {
|
|
26389
|
+
const relative9 = rootRel === "." ? "" : rootRel;
|
|
26390
|
+
const stagedSpecDir = path22.join(stagingDir, relative9, ".wai");
|
|
26391
|
+
if (!fs17.existsSync(stagedSpecDir)) continue;
|
|
26392
|
+
const liveSpecDir = liveSpecDirFor(path22.join(root, relative9));
|
|
26393
|
+
if (backupExisting && liveSpecDir && fs17.existsSync(liveSpecDir)) {
|
|
26394
|
+
const backupTarget = path22.join(backupDir, relative9, path22.basename(liveSpecDir));
|
|
26395
|
+
fs17.mkdirSync(path22.dirname(backupTarget), { recursive: true });
|
|
26396
|
+
fs17.renameSync(liveSpecDir, backupTarget);
|
|
26397
|
+
backedUp = true;
|
|
26398
|
+
} else if (liveSpecDir && fs17.existsSync(liveSpecDir)) {
|
|
26399
|
+
fs17.rmSync(liveSpecDir, { recursive: true, force: true });
|
|
26560
26400
|
}
|
|
26401
|
+
const target = path22.join(root, relative9, ".wai");
|
|
26402
|
+
fs17.mkdirSync(path22.dirname(target), { recursive: true });
|
|
26403
|
+
if (fs17.existsSync(target)) fs17.rmSync(target, { recursive: true, force: true });
|
|
26404
|
+
fs17.renameSync(stagedSpecDir, target);
|
|
26561
26405
|
}
|
|
26562
|
-
|
|
26406
|
+
if (backedUp) {
|
|
26407
|
+
return backupDir;
|
|
26408
|
+
}
|
|
26409
|
+
return "";
|
|
26563
26410
|
}
|
|
26564
|
-
function
|
|
26565
|
-
const domains = resolveDomains();
|
|
26566
|
-
let projectName = "this project";
|
|
26411
|
+
function discardStagingDir(stagingDir) {
|
|
26567
26412
|
try {
|
|
26568
|
-
|
|
26413
|
+
fs17.rmSync(stagingDir, { recursive: true, force: true });
|
|
26569
26414
|
} catch {
|
|
26570
26415
|
}
|
|
26571
|
-
|
|
26572
|
-
|
|
26573
|
-
const
|
|
26574
|
-
|
|
26575
|
-
|
|
26576
|
-
``
|
|
26577
|
-
];
|
|
26578
|
-
if (projectCtx) {
|
|
26579
|
-
lines.push(`# Project Context \u2014 ${projectName}`);
|
|
26580
|
-
lines.push("");
|
|
26581
|
-
lines.push(projectCtx.trim());
|
|
26582
|
-
lines.push("");
|
|
26583
|
-
lines.push("---");
|
|
26584
|
-
lines.push("");
|
|
26416
|
+
}
|
|
26417
|
+
function exportSpecTree(includeDerived, allowPartial) {
|
|
26418
|
+
const system = loadSystemSpec();
|
|
26419
|
+
if (!system) {
|
|
26420
|
+
throw new Error("no spec tree to export at this project root");
|
|
26585
26421
|
}
|
|
26586
|
-
|
|
26587
|
-
|
|
26588
|
-
|
|
26589
|
-
|
|
26590
|
-
|
|
26591
|
-
|
|
26592
|
-
|
|
26593
|
-
|
|
26594
|
-
|
|
26595
|
-
|
|
26596
|
-
|
|
26597
|
-
lines.push("_No domains yet._");
|
|
26598
|
-
} else {
|
|
26599
|
-
lines.push("| ID | Source | Name |");
|
|
26600
|
-
lines.push("|----|--------|------|");
|
|
26601
|
-
for (const d of domains) {
|
|
26602
|
-
const name = d.name ?? d.id;
|
|
26603
|
-
const source = d.boundTo ? `subsystem \`${d.boundTo}\`` : "free-standing";
|
|
26604
|
-
lines.push(`| \`${d.id}\` | ${source} | ${name} |`);
|
|
26605
|
-
}
|
|
26606
|
-
}
|
|
26607
|
-
lines.push("");
|
|
26608
|
-
lines.push("---");
|
|
26609
|
-
lines.push("");
|
|
26610
|
-
const mcpSettingsPath = require("path").join(process.cwd(), ".claude", "settings.json");
|
|
26611
|
-
let mcpInstalled = false;
|
|
26612
|
-
try {
|
|
26613
|
-
const s = JSON.parse(require("fs").readFileSync(mcpSettingsPath, "utf8"));
|
|
26614
|
-
mcpInstalled = !!s?.mcpServers?.wairon;
|
|
26615
|
-
} catch {
|
|
26616
|
-
}
|
|
26617
|
-
if (mcpInstalled) {
|
|
26618
|
-
lines.push("# wairon MCP Tools");
|
|
26619
|
-
lines.push("");
|
|
26620
|
-
lines.push("The **wairon MCP server** is active in this project. The tools are self-describing; call them directly:");
|
|
26621
|
-
lines.push("");
|
|
26622
|
-
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`.");
|
|
26623
|
-
lines.push("- Reading & maintenance: `sdd_get_spec`, `sdd_update_spec`, `sdd_delete_spec`, `sdd_validate_tree`, `sdd_get_status`.");
|
|
26624
|
-
lines.push("- Topology: `listAgents`, `getAgent`, `listDomains`, `validateTopology`, `getProjectConfig`.");
|
|
26625
|
-
lines.push("");
|
|
26626
|
-
lines.push("Use these MCP tools to query and change project state \u2014 never the `wairon` CLI (that is the human developer's tool).");
|
|
26627
|
-
lines.push("");
|
|
26628
|
-
lines.push("---");
|
|
26629
|
-
lines.push("");
|
|
26630
|
-
}
|
|
26631
|
-
lines.push(GLOBAL_GUIDE_BODY);
|
|
26632
|
-
lines.push("");
|
|
26633
|
-
return lines.join("\n");
|
|
26634
|
-
}
|
|
26635
|
-
function syncContextFiles() {
|
|
26636
|
-
const domainsContent = renderDomainsDoc();
|
|
26637
|
-
const guideContent = renderWaironGuide();
|
|
26638
|
-
const domainsUpdated = writeFileIfChanged(CONTEXT_PATHS.domainsMd(), domainsContent);
|
|
26639
|
-
const guideUpdated = writeFileIfChanged(CONTEXT_PATHS.waironGuideMd(), guideContent);
|
|
26640
|
-
return { domainsUpdated, guideUpdated };
|
|
26641
|
-
}
|
|
26642
|
-
|
|
26643
|
-
// src/core/treetransfer.ts
|
|
26644
|
-
var fs18 = __toESM(require("fs"));
|
|
26645
|
-
var path23 = __toESM(require("path"));
|
|
26646
|
-
var sdkPortal = __toESM(require_dist());
|
|
26647
|
-
var STAGING_PREFIX = ".wai-staging-";
|
|
26648
|
-
var BACKUP_DIR = ".wai-backups";
|
|
26649
|
-
function buildTreeArchive2(roots, projectName, stateId, includeDerived) {
|
|
26650
|
-
return sdkPortal.buildTreeArchive(roots, projectName, stateId, includeDerived);
|
|
26651
|
-
}
|
|
26652
|
-
function inspectTreeArchive2(archive) {
|
|
26653
|
-
return sdkPortal.inspectTreeArchive(archive);
|
|
26654
|
-
}
|
|
26655
|
-
function extractTreeArchive2(archive, destDir, refuseExecutableEntries) {
|
|
26656
|
-
const limits = void 0;
|
|
26657
|
-
return sdkPortal.extractTreeArchive(archive, destDir, limits, refuseExecutableEntries);
|
|
26658
|
-
}
|
|
26659
|
-
function hasSpecTree(root) {
|
|
26660
|
-
if (pathExists(aiPathsAt(root).projectConfig())) {
|
|
26661
|
-
return true;
|
|
26662
|
-
}
|
|
26663
|
-
return false;
|
|
26664
|
-
}
|
|
26665
|
-
function hasAuthoredSpecs(root) {
|
|
26666
|
-
const paths = aiPathsAt(root);
|
|
26667
|
-
const specsDir = paths.specsDir();
|
|
26668
|
-
if (!pathExists(specsDir)) {
|
|
26669
|
-
return false;
|
|
26670
|
-
}
|
|
26671
|
-
const systemIndex = path23.resolve(paths.specsSystem());
|
|
26672
|
-
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
26673
|
-
if (path23.resolve(file) !== systemIndex) {
|
|
26674
|
-
return true;
|
|
26675
|
-
}
|
|
26676
|
-
}
|
|
26677
|
-
return false;
|
|
26678
|
-
}
|
|
26679
|
-
function createStagingDir(destDir) {
|
|
26680
|
-
const root = path23.resolve(destDir);
|
|
26681
|
-
fs18.mkdirSync(root, { recursive: true });
|
|
26682
|
-
const stagingDir = fs18.mkdtempSync(path23.join(root, STAGING_PREFIX));
|
|
26683
|
-
return stagingDir;
|
|
26684
|
-
}
|
|
26685
|
-
function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
|
|
26686
|
-
const root = path23.resolve(destDir);
|
|
26687
|
-
const backupDir = path23.join(root, BACKUP_DIR, timestampSlug());
|
|
26688
|
-
let backedUp = false;
|
|
26689
|
-
for (const rootRel of roots) {
|
|
26690
|
-
const relative9 = rootRel === "." ? "" : rootRel;
|
|
26691
|
-
const stagedSpecDir = path23.join(stagingDir, relative9, ".wai");
|
|
26692
|
-
if (!fs18.existsSync(stagedSpecDir)) continue;
|
|
26693
|
-
const liveSpecDir = liveSpecDirFor(path23.join(root, relative9));
|
|
26694
|
-
if (backupExisting && liveSpecDir && fs18.existsSync(liveSpecDir)) {
|
|
26695
|
-
const backupTarget = path23.join(backupDir, relative9, path23.basename(liveSpecDir));
|
|
26696
|
-
fs18.mkdirSync(path23.dirname(backupTarget), { recursive: true });
|
|
26697
|
-
fs18.renameSync(liveSpecDir, backupTarget);
|
|
26698
|
-
backedUp = true;
|
|
26699
|
-
} else if (liveSpecDir && fs18.existsSync(liveSpecDir)) {
|
|
26700
|
-
fs18.rmSync(liveSpecDir, { recursive: true, force: true });
|
|
26701
|
-
}
|
|
26702
|
-
const target = path23.join(root, relative9, ".wai");
|
|
26703
|
-
fs18.mkdirSync(path23.dirname(target), { recursive: true });
|
|
26704
|
-
if (fs18.existsSync(target)) fs18.rmSync(target, { recursive: true, force: true });
|
|
26705
|
-
fs18.renameSync(stagedSpecDir, target);
|
|
26706
|
-
}
|
|
26707
|
-
if (backedUp) {
|
|
26708
|
-
return backupDir;
|
|
26709
|
-
}
|
|
26710
|
-
return "";
|
|
26711
|
-
}
|
|
26712
|
-
function discardStagingDir(stagingDir) {
|
|
26713
|
-
try {
|
|
26714
|
-
fs18.rmSync(stagingDir, { recursive: true, force: true });
|
|
26715
|
-
} catch {
|
|
26716
|
-
}
|
|
26717
|
-
}
|
|
26718
|
-
function exportSpecTree(includeDerived, allowPartial) {
|
|
26719
|
-
const system = loadSystemSpec();
|
|
26720
|
-
if (!system) {
|
|
26721
|
-
throw new Error("no spec tree to export at this project root");
|
|
26722
|
-
}
|
|
26723
|
-
const inspection = inspectChainedRoots();
|
|
26724
|
-
const root = getProjectRoot();
|
|
26725
|
-
const skipped = [...inspection.skipped];
|
|
26726
|
-
const roots = [{ relativePath: ".", waiDir: aiPathsAt(root).root() }];
|
|
26727
|
-
for (const rel2 of inspection.roots) {
|
|
26728
|
-
const waiDir = aiPathsAt(path23.resolve(root, rel2)).root();
|
|
26729
|
-
if (pathExists(waiDir)) {
|
|
26730
|
-
roots.push({ relativePath: rel2, waiDir });
|
|
26731
|
-
} else {
|
|
26732
|
-
skipped.push({ mount: rel2, projectPath: rel2, reason: "no-spec-tree" });
|
|
26733
|
-
}
|
|
26422
|
+
const inspection = inspectChainedRoots();
|
|
26423
|
+
const root = getProjectRoot();
|
|
26424
|
+
const skipped = [...inspection.skipped];
|
|
26425
|
+
const roots = [{ relativePath: ".", waiDir: aiPathsAt(root).root() }];
|
|
26426
|
+
for (const rel2 of inspection.roots) {
|
|
26427
|
+
const waiDir = aiPathsAt(path22.resolve(root, rel2)).root();
|
|
26428
|
+
if (pathExists(waiDir)) {
|
|
26429
|
+
roots.push({ relativePath: rel2, waiDir });
|
|
26430
|
+
} else {
|
|
26431
|
+
skipped.push({ mount: rel2, projectPath: rel2, reason: "no-spec-tree" });
|
|
26432
|
+
}
|
|
26734
26433
|
}
|
|
26735
26434
|
if (skipped.length > 0 && !allowPartial) {
|
|
26736
26435
|
const detail = skipped.map((s) => `${s.mount} (${s.reason})`).join(", ");
|
|
@@ -26756,7 +26455,7 @@ function importSpecTree(archive, options) {
|
|
|
26756
26455
|
if (!info.compatible) {
|
|
26757
26456
|
throw new Error("incompatible .waitree archive (format or minimum wairon version)");
|
|
26758
26457
|
}
|
|
26759
|
-
const destDir =
|
|
26458
|
+
const destDir = path22.resolve(options.destDir ?? getProjectRoot());
|
|
26760
26459
|
const occupied = hasSpecTree(destDir);
|
|
26761
26460
|
const authored = hasAuthoredSpecs(destDir);
|
|
26762
26461
|
if (authored && options.replaceExisting !== true) {
|
|
@@ -26787,10 +26486,10 @@ function importSpecTree(archive, options) {
|
|
|
26787
26486
|
return result;
|
|
26788
26487
|
}
|
|
26789
26488
|
function liveSpecDirFor(rootDir) {
|
|
26790
|
-
const modern =
|
|
26791
|
-
if (
|
|
26792
|
-
const legacy =
|
|
26793
|
-
if (
|
|
26489
|
+
const modern = path22.join(rootDir, ".wai");
|
|
26490
|
+
if (fs17.existsSync(modern)) return modern;
|
|
26491
|
+
const legacy = path22.join(rootDir, ".wairon");
|
|
26492
|
+
if (fs17.existsSync(legacy)) return legacy;
|
|
26794
26493
|
return null;
|
|
26795
26494
|
}
|
|
26796
26495
|
function timestampSlug() {
|
|
@@ -26798,10 +26497,10 @@ function timestampSlug() {
|
|
|
26798
26497
|
}
|
|
26799
26498
|
|
|
26800
26499
|
// src/exporters/generate.ts
|
|
26801
|
-
var
|
|
26500
|
+
var path26 = __toESM(require("path"));
|
|
26802
26501
|
|
|
26803
26502
|
// src/exporters/claude.ts
|
|
26804
|
-
var
|
|
26503
|
+
var path23 = __toESM(require("path"));
|
|
26805
26504
|
var MODEL_BY_TIER = {
|
|
26806
26505
|
small: "haiku",
|
|
26807
26506
|
standard: "sonnet",
|
|
@@ -26840,7 +26539,7 @@ var ClaudeExporter = class {
|
|
|
26840
26539
|
outputPath(ctx) {
|
|
26841
26540
|
const { agent, target, projectRoot } = ctx;
|
|
26842
26541
|
const outputDir = "outputDir" in target ? target.outputDir : ".claude/agents";
|
|
26843
|
-
return
|
|
26542
|
+
return path23.resolve(projectRoot, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
|
|
26844
26543
|
}
|
|
26845
26544
|
export(ctx) {
|
|
26846
26545
|
const { agent, renderedInstructions, budget } = ctx;
|
|
@@ -26865,7 +26564,7 @@ var ClaudeExporter = class {
|
|
|
26865
26564
|
};
|
|
26866
26565
|
|
|
26867
26566
|
// src/exporters/custom.ts
|
|
26868
|
-
var
|
|
26567
|
+
var path24 = __toESM(require("path"));
|
|
26869
26568
|
var CustomExporter = class {
|
|
26870
26569
|
constructor() {
|
|
26871
26570
|
this.targetType = "custom";
|
|
@@ -26875,7 +26574,7 @@ var CustomExporter = class {
|
|
|
26875
26574
|
if (!("outputDir" in target)) {
|
|
26876
26575
|
throw new Error("CustomExporter requires target.outputDir");
|
|
26877
26576
|
}
|
|
26878
|
-
return
|
|
26577
|
+
return path24.resolve(projectRoot, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
|
|
26879
26578
|
}
|
|
26880
26579
|
export(ctx) {
|
|
26881
26580
|
const { agent, target, renderedInstructions } = ctx;
|
|
@@ -26898,7 +26597,7 @@ var CustomExporter = class {
|
|
|
26898
26597
|
};
|
|
26899
26598
|
|
|
26900
26599
|
// src/exporters/gemini.ts
|
|
26901
|
-
var
|
|
26600
|
+
var path25 = __toESM(require("path"));
|
|
26902
26601
|
var GeminiExporter = class {
|
|
26903
26602
|
constructor() {
|
|
26904
26603
|
this.targetType = "gemini";
|
|
@@ -26906,7 +26605,7 @@ var GeminiExporter = class {
|
|
|
26906
26605
|
outputPath(ctx) {
|
|
26907
26606
|
const { agent, target, projectRoot } = ctx;
|
|
26908
26607
|
const outputDir = "outputDir" in target ? target.outputDir : ".gemini/agents";
|
|
26909
|
-
return
|
|
26608
|
+
return path25.resolve(projectRoot, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
|
|
26910
26609
|
}
|
|
26911
26610
|
export(ctx) {
|
|
26912
26611
|
const { agent, renderedInstructions } = ctx;
|
|
@@ -27003,7 +26702,7 @@ function resolveExpectedOutputPaths(agents, projectConfig, projectRoot = getProj
|
|
|
27003
26702
|
const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
|
|
27004
26703
|
if (!targetConfig) continue;
|
|
27005
26704
|
const ctx = { agent, projectRoot, target: targetConfig };
|
|
27006
|
-
expected.add(
|
|
26705
|
+
expected.add(path26.resolve(getExporter(targetConfig).outputPath(ctx)));
|
|
27007
26706
|
}
|
|
27008
26707
|
}
|
|
27009
26708
|
return expected;
|
|
@@ -27112,167 +26811,460 @@ function retireSpecialists(apply) {
|
|
|
27112
26811
|
if (retype.dependencyClass) orchestrator.dependencyClass = retype.dependencyClass;
|
|
27113
26812
|
saveComponentSpec(orchestrator);
|
|
27114
26813
|
}
|
|
27115
|
-
const rebasedVariants = rebaseProjectVariants("Specialist", "Orchestrator");
|
|
27116
|
-
invalidateSpecCache();
|
|
27117
|
-
return { retyped, rebasedVariants, applied: true };
|
|
26814
|
+
const rebasedVariants = rebaseProjectVariants("Specialist", "Orchestrator");
|
|
26815
|
+
invalidateSpecCache();
|
|
26816
|
+
return { retyped, rebasedVariants, applied: true };
|
|
26817
|
+
}
|
|
26818
|
+
|
|
26819
|
+
// src/core/narrative-repair.ts
|
|
26820
|
+
function repairForeignStepFields(apply) {
|
|
26821
|
+
const implementations = loadImplementationSpecs();
|
|
26822
|
+
const repairs = [];
|
|
26823
|
+
const changed = [];
|
|
26824
|
+
for (const impl of implementations) {
|
|
26825
|
+
let touched = false;
|
|
26826
|
+
for (const method of impl.methods) {
|
|
26827
|
+
for (const step of method.narrative) {
|
|
26828
|
+
const fields = narrativeStepForeignFields(step);
|
|
26829
|
+
if (!fields.length) continue;
|
|
26830
|
+
repairs.push({
|
|
26831
|
+
implementation: impl.id,
|
|
26832
|
+
method: method.name,
|
|
26833
|
+
stepNumber: step.stepNumber,
|
|
26834
|
+
stepType: step.type,
|
|
26835
|
+
fields
|
|
26836
|
+
});
|
|
26837
|
+
for (const field of fields) delete step[field];
|
|
26838
|
+
touched = true;
|
|
26839
|
+
}
|
|
26840
|
+
}
|
|
26841
|
+
if (touched) changed.push(impl);
|
|
26842
|
+
}
|
|
26843
|
+
if (apply && changed.length) {
|
|
26844
|
+
for (const impl of changed) saveImplementationSpec(impl);
|
|
26845
|
+
invalidateSpecCache();
|
|
26846
|
+
}
|
|
26847
|
+
return repairs;
|
|
26848
|
+
}
|
|
26849
|
+
|
|
26850
|
+
// src/core/approval.ts
|
|
26851
|
+
var crypto4 = __toESM(require("crypto"));
|
|
26852
|
+
var path27 = __toESM(require("path"));
|
|
26853
|
+
function diffSize(d) {
|
|
26854
|
+
return d.added.length + d.changed.length + d.removed.length;
|
|
26855
|
+
}
|
|
26856
|
+
function digest(content) {
|
|
26857
|
+
const normalized = content.replace(/\r\n/g, "\n");
|
|
26858
|
+
return crypto4.createHash("sha256").update(normalized, "utf8").digest("hex");
|
|
26859
|
+
}
|
|
26860
|
+
function currentSpecDigests(root = getProjectRoot()) {
|
|
26861
|
+
const mountDirs = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::")).map((s) => `${path27.resolve(root, s.projectPath).split(path27.sep).join("/")}/`);
|
|
26862
|
+
const out = {};
|
|
26863
|
+
for (const [abs, content] of snapshotSpecFiles()) {
|
|
26864
|
+
const normalized = path27.resolve(abs).split(path27.sep).join("/");
|
|
26865
|
+
if (mountDirs.some((dir) => normalized.startsWith(dir))) continue;
|
|
26866
|
+
out[path27.relative(root, abs).split(path27.sep).join("/")] = digest(content);
|
|
26867
|
+
}
|
|
26868
|
+
return out;
|
|
26869
|
+
}
|
|
26870
|
+
function captureApprovedSpecs(root = getProjectRoot(), scope) {
|
|
26871
|
+
const current2 = currentSpecDigests(root);
|
|
26872
|
+
if (!scope) return current2;
|
|
26873
|
+
const previous = readLockRecord()?.specs ?? {};
|
|
26874
|
+
const specs = { ...previous };
|
|
26875
|
+
for (const rel2 of Object.keys(previous)) {
|
|
26876
|
+
if (scope.paths.has(rel2) && current2[rel2] === void 0) delete specs[rel2];
|
|
26877
|
+
}
|
|
26878
|
+
for (const rel2 of scope.paths) {
|
|
26879
|
+
if (current2[rel2] !== void 0) specs[rel2] = current2[rel2];
|
|
26880
|
+
}
|
|
26881
|
+
return specs;
|
|
26882
|
+
}
|
|
26883
|
+
function diffAgainstApproval(root = getProjectRoot()) {
|
|
26884
|
+
const approved = approvedSpecs(root);
|
|
26885
|
+
if (!approved) return null;
|
|
26886
|
+
const current2 = currentSpecDigests(root);
|
|
26887
|
+
const added = [];
|
|
26888
|
+
const changed = [];
|
|
26889
|
+
const removed = [];
|
|
26890
|
+
const unchangedPaths = [];
|
|
26891
|
+
for (const [rel2, d] of Object.entries(current2)) {
|
|
26892
|
+
const before = approved[rel2];
|
|
26893
|
+
if (before === void 0) added.push(rel2);
|
|
26894
|
+
else if (before !== d) changed.push(rel2);
|
|
26895
|
+
else unchangedPaths.push(rel2);
|
|
26896
|
+
}
|
|
26897
|
+
for (const rel2 of Object.keys(approved)) {
|
|
26898
|
+
if (current2[rel2] === void 0) removed.push(rel2);
|
|
26899
|
+
}
|
|
26900
|
+
return {
|
|
26901
|
+
added: added.sort(),
|
|
26902
|
+
changed: changed.sort(),
|
|
26903
|
+
removed: removed.sort(),
|
|
26904
|
+
unchangedPaths: unchangedPaths.sort()
|
|
26905
|
+
};
|
|
26906
|
+
}
|
|
26907
|
+
function approvedSpecs(root) {
|
|
26908
|
+
return approvalRecord(root)?.specs ?? null;
|
|
26909
|
+
}
|
|
26910
|
+
function approvalRecord(root = getProjectRoot()) {
|
|
26911
|
+
return readLockRecordAt(root);
|
|
26912
|
+
}
|
|
26913
|
+
function settledSpecPaths(root = getProjectRoot()) {
|
|
26914
|
+
const diff = diffAgainstApproval(root);
|
|
26915
|
+
return diff ? new Set(diff.unchangedPaths) : null;
|
|
26916
|
+
}
|
|
26917
|
+
function pinOf(stateId) {
|
|
26918
|
+
return `${stateId.algorithm}:${stateId.digest}`;
|
|
26919
|
+
}
|
|
26920
|
+
function currentChildPins(mounts, root = getProjectRoot()) {
|
|
26921
|
+
const pins = {};
|
|
26922
|
+
for (const mount of mounts) {
|
|
26923
|
+
if (!mount.projectPath || mount.id.includes("::")) continue;
|
|
26924
|
+
const child = readLockRecordAt(path27.resolve(root, mount.projectPath));
|
|
26925
|
+
if (child) pins[mount.id] = pinOf(child.stateId);
|
|
26926
|
+
}
|
|
26927
|
+
return pins;
|
|
26928
|
+
}
|
|
26929
|
+
function movedChildren(mounts, root = getProjectRoot()) {
|
|
26930
|
+
const pinned = approvalRecord(root)?.children;
|
|
26931
|
+
if (!pinned) return [];
|
|
26932
|
+
const now = currentChildPins(mounts, root);
|
|
26933
|
+
const moved = [];
|
|
26934
|
+
for (const [id, was] of Object.entries(pinned)) {
|
|
26935
|
+
const current2 = now[id] ?? null;
|
|
26936
|
+
if (current2 !== was) moved.push({ id, pinned: was, now: current2 });
|
|
26937
|
+
}
|
|
26938
|
+
return moved;
|
|
26939
|
+
}
|
|
26940
|
+
|
|
26941
|
+
// src/core/approver.ts
|
|
26942
|
+
var import_child_process = require("child_process");
|
|
26943
|
+
var os4 = __toESM(require("os"));
|
|
26944
|
+
function gitConfig(key) {
|
|
26945
|
+
try {
|
|
26946
|
+
const value = (0, import_child_process.execFileSync)("git", ["config", "--get", key], {
|
|
26947
|
+
encoding: "utf8",
|
|
26948
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
26949
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
26950
|
+
}).trim();
|
|
26951
|
+
return value || void 0;
|
|
26952
|
+
} catch {
|
|
26953
|
+
return void 0;
|
|
26954
|
+
}
|
|
26955
|
+
}
|
|
26956
|
+
function machineIdentity() {
|
|
26957
|
+
let user = "unknown";
|
|
26958
|
+
try {
|
|
26959
|
+
user = os4.userInfo().username || user;
|
|
26960
|
+
} catch {
|
|
26961
|
+
}
|
|
26962
|
+
let host = "";
|
|
26963
|
+
try {
|
|
26964
|
+
host = os4.hostname();
|
|
26965
|
+
} catch {
|
|
26966
|
+
}
|
|
26967
|
+
return { id: host ? `${user}@${host}` : user, source: "os" };
|
|
26968
|
+
}
|
|
26969
|
+
function localApprover() {
|
|
26970
|
+
const name = gitConfig("user.name");
|
|
26971
|
+
const email = gitConfig("user.email");
|
|
26972
|
+
if (name && email) return { id: `${name} <${email}>`, source: "git" };
|
|
26973
|
+
if (email) return { id: email, source: "git" };
|
|
26974
|
+
return machineIdentity();
|
|
26975
|
+
}
|
|
26976
|
+
|
|
26977
|
+
// src/utils/ai-guide.ts
|
|
26978
|
+
var fs18 = __toESM(require("fs"));
|
|
26979
|
+
var os5 = __toESM(require("os"));
|
|
26980
|
+
var path28 = __toESM(require("path"));
|
|
26981
|
+
|
|
26982
|
+
// src/core/stamp.ts
|
|
26983
|
+
function versionStamp() {
|
|
26984
|
+
return `<!-- wairon-version: ${WAIRON_VERSION} -->`;
|
|
26985
|
+
}
|
|
26986
|
+
var STAMP_RE = /<!--\s*wairon-version:\s*([^\s]+)\s*-->/;
|
|
26987
|
+
function readStampVersion(content) {
|
|
26988
|
+
const m = content.match(STAMP_RE);
|
|
26989
|
+
return m ? m[1] : null;
|
|
26990
|
+
}
|
|
26991
|
+
|
|
26992
|
+
// src/utils/ai-guide.ts
|
|
26993
|
+
var GUIDE_MARKER_START = "<!-- wairon-guide-start -->";
|
|
26994
|
+
var GUIDE_MARKER_END = "<!-- wairon-guide-end -->";
|
|
26995
|
+
var GLOBAL_GUIDE_BODY = `## wairon \u2014 Spec-Driven Development (optional)
|
|
26996
|
+
|
|
26997
|
+
If \`.wai/specs/\` exists, the wairon SDD workflow is active; otherwise ignore it. wairon does not orchestrate sessions \u2014 it equips yours.
|
|
26998
|
+
|
|
26999
|
+
### In SDD Projects:
|
|
27000
|
+
- **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\`).
|
|
27001
|
+
- **Validation**: Conformance checks (stereotype rules, cycle checks, reference integrity) are run via the \`sdd_validate_tree\` MCP tool.
|
|
27002
|
+
- **Operating Rules**:
|
|
27003
|
+
1. **Skills**: Use \`sdd-architect\` to design (and \`sdd-implement\`, \`sdd-narrative\`, \`sdd-auditor\`). Refer to project's local guide file for detailed constraints.
|
|
27004
|
+
2. **MCP Tools Only**: Author/validate specs *only* via \`sdd_*\` tools (e.g. \`sdd_initialize_system\`, \`sdd_validate_tree\`).
|
|
27005
|
+
3. **No CLI Exec**: Do not run the \`wairon\` CLI (human tool). Use MCP tools \`sdd_validate_tree\` and \`sdd_get_status\` instead.
|
|
27006
|
+
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>\`).
|
|
27007
|
+
5. **Design First**: Complete spec and pass \`sdd_validate_tree\` before writing code.
|
|
27008
|
+
6. **Consistency**: Code must match L3 interfaces and L5 narratives exactly. If the spec is wrong, stop and update the spec.
|
|
27009
|
+
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.`;
|
|
27010
|
+
var LOCAL_GUIDE_BODY = `## Wairon \u2014 Spec-Driven Development (you are operating inside it)
|
|
27011
|
+
|
|
27012
|
+
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.
|
|
27013
|
+
|
|
27014
|
+
**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.**
|
|
27015
|
+
|
|
27016
|
+
**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.
|
|
27017
|
+
|
|
27018
|
+
### How you operate
|
|
27019
|
+
- **To design/modify specs**: Use **\`sdd-architect\`** skill (in \`.claude/skills/\` or \`.gemini/skills/\`).
|
|
27020
|
+
- **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.
|
|
27021
|
+
- **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.
|
|
27022
|
+
- **Leading \`::\`**: Bypasses the local subsystem prefix to resolve absolute from the system root (e.g. \`::shared::error-type\`).
|
|
27023
|
+
- **\`super::\`**: Goes up one parent subsystem level (e.g. \`super::sibling_comp\`, \`super::super::parent_sibling\`).
|
|
27024
|
+
- **Do not run the \`wairon\` CLI**: Use \`sdd_validate_tree\` and \`sdd_get_status\` instead of CLI commands.
|
|
27025
|
+
- **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.
|
|
27026
|
+
- **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.
|
|
27027
|
+
|
|
27028
|
+
### Rules (enforced by \`sdd_validate_tree\`)
|
|
27029
|
+
1. **Design before code**: Complete spec and pass validator before writing source code.
|
|
27030
|
+
2. **Human-in-the-loop**: Ask user approval for each spec layer before proceeding.
|
|
27031
|
+
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.
|
|
27032
|
+
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.
|
|
27033
|
+
|
|
27034
|
+
### Component Vocabulary
|
|
27035
|
+
* **Blocks**: Portal, Orchestrator, Supervisor, Actor, Store, Index, Query, Registry, Adapter, Observer.
|
|
27036
|
+
* **Patterns**: Repository \u2014 the composable pattern that \`owns\` its member blocks: one Store with its Registry, Indexes and Queries, and optionally an Adapter.
|
|
27037
|
+
* **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.
|
|
27038
|
+
* Use \`owns\` for private member containment (exactly one hop) and \`dependsOn\` for collaborators. Never use generic suffixes like "Manager", "Helper", or "Utils".`;
|
|
27039
|
+
function globalGuideFilePath(targetType) {
|
|
27040
|
+
if (targetType === "claude") return path28.join(process.env["CLAUDE_CONFIG_DIR"] || path28.join(os5.homedir(), ".claude"), "CLAUDE.md");
|
|
27041
|
+
if (targetType === "gemini") return path28.join(process.env["GEMINI_CONFIG_DIR"] || path28.join(os5.homedir(), ".gemini"), "GEMINI.md");
|
|
27042
|
+
return null;
|
|
27043
|
+
}
|
|
27044
|
+
function localGuideFilePath(projectRoot, targetType) {
|
|
27045
|
+
if (targetType === "claude") return path28.join(projectRoot, ".claude", "CLAUDE.md");
|
|
27046
|
+
if (targetType === "gemini" || targetType === "agy") return path28.join(projectRoot, ".gemini", "GEMINI.md");
|
|
27047
|
+
return null;
|
|
27048
|
+
}
|
|
27049
|
+
function injectGuide(filePath, scope) {
|
|
27050
|
+
const body = scope === "global" ? GLOBAL_GUIDE_BODY : LOCAL_GUIDE_BODY;
|
|
27051
|
+
const section = `
|
|
27052
|
+
|
|
27053
|
+
${GUIDE_MARKER_START}
|
|
27054
|
+
${versionStamp()}
|
|
27055
|
+
${body}
|
|
27056
|
+
${GUIDE_MARKER_END}
|
|
27057
|
+
`;
|
|
27058
|
+
const existing = fs18.existsSync(filePath) ? fs18.readFileSync(filePath, "utf-8") : "";
|
|
27059
|
+
const stripped = stripGuideSection(existing);
|
|
27060
|
+
const newContent = stripped.trimEnd() + section;
|
|
27061
|
+
fs18.mkdirSync(path28.dirname(filePath), { recursive: true });
|
|
27062
|
+
fs18.writeFileSync(filePath, newContent, "utf-8");
|
|
27063
|
+
}
|
|
27064
|
+
function stripGuideSection(content) {
|
|
27065
|
+
const start = content.indexOf(GUIDE_MARKER_START);
|
|
27066
|
+
const end = content.indexOf(GUIDE_MARKER_END);
|
|
27067
|
+
if (start === -1 || end === -1) return content;
|
|
27068
|
+
return content.slice(0, start) + content.slice(end + GUIDE_MARKER_END.length);
|
|
27069
|
+
}
|
|
27070
|
+
function writeRootGuideDelegator(projectRoot, targetType) {
|
|
27071
|
+
if (targetType === "claude") {
|
|
27072
|
+
const filePath = path28.join(projectRoot, "CLAUDE.md");
|
|
27073
|
+
const content = `@.claude/CLAUDE.md
|
|
27074
|
+
|
|
27075
|
+
# Wairon SDD Project
|
|
27076
|
+
|
|
27077
|
+
This project uses the Wairon Spec-Driven Development (SDD) framework. The imported
|
|
27078
|
+
\`.claude/CLAUDE.md\` above is your complete operating guide \u2014 you already have the
|
|
27079
|
+
full context, so don't search the project to learn how wairon or SDD works.
|
|
27080
|
+
|
|
27081
|
+
To design or modify the system, invoke the **\`sdd-architect\`** skill
|
|
27082
|
+
(in \`.claude/skills/\`). Author and validate specs with the \`sdd_*\` MCP tools;
|
|
27083
|
+
the \`wairon\` CLI is the human developer's tool, not yours.
|
|
27084
|
+
`;
|
|
27085
|
+
fs18.writeFileSync(filePath, content, "utf-8");
|
|
27086
|
+
} else if (targetType === "gemini" || targetType === "agy") {
|
|
27087
|
+
const filePath = path28.join(projectRoot, "GEMINI.md");
|
|
27088
|
+
const content = `# Wairon SDD Project
|
|
27089
|
+
${GUIDE_MARKER_START}
|
|
27090
|
+
${versionStamp()}
|
|
27091
|
+
${LOCAL_GUIDE_BODY}
|
|
27092
|
+
${GUIDE_MARKER_END}
|
|
27093
|
+
`;
|
|
27094
|
+
fs18.writeFileSync(filePath, content, "utf-8");
|
|
27095
|
+
} else if (targetType === "cursor") {
|
|
27096
|
+
const filePath = path28.join(projectRoot, ".cursorrules");
|
|
27097
|
+
const content = `# Wairon SDD Project
|
|
27098
|
+
|
|
27099
|
+
This project uses the Wairon Spec-Driven Development (SDD) framework.
|
|
27100
|
+
|
|
27101
|
+
Refer to the rules in [.cursor/rules/](.cursor/rules/) for full instructions.
|
|
27102
|
+
`;
|
|
27103
|
+
fs18.writeFileSync(filePath, content, "utf-8");
|
|
27104
|
+
} else if (targetType === "copilot") {
|
|
27105
|
+
const filePath = path28.join(projectRoot, ".github", "copilot-instructions.md");
|
|
27106
|
+
fs18.mkdirSync(path28.dirname(filePath), { recursive: true });
|
|
27107
|
+
const content = `# Wairon SDD Project
|
|
27108
|
+
|
|
27109
|
+
This project uses the Wairon Spec-Driven Development (SDD) framework.
|
|
27110
|
+
|
|
27111
|
+
Refer to the prompts in [.github/prompts/](.github/prompts/) for instructions.
|
|
27112
|
+
`;
|
|
27113
|
+
fs18.writeFileSync(filePath, content, "utf-8");
|
|
27114
|
+
} else if (targetType === "codex") {
|
|
27115
|
+
const filePath = path28.join(projectRoot, ".codexrules");
|
|
27116
|
+
const content = `# Wairon SDD Project
|
|
27117
|
+
|
|
27118
|
+
Refer to [.codex/agents/](.codex/agents/) for full instructions.
|
|
27119
|
+
`;
|
|
27120
|
+
fs18.writeFileSync(filePath, content, "utf-8");
|
|
27121
|
+
}
|
|
27118
27122
|
}
|
|
27119
|
-
|
|
27120
|
-
|
|
27121
|
-
|
|
27122
|
-
const
|
|
27123
|
-
|
|
27124
|
-
|
|
27125
|
-
|
|
27126
|
-
|
|
27127
|
-
|
|
27128
|
-
for (const step of method.narrative) {
|
|
27129
|
-
const fields = narrativeStepForeignFields(step);
|
|
27130
|
-
if (!fields.length) continue;
|
|
27131
|
-
repairs.push({
|
|
27132
|
-
implementation: impl.id,
|
|
27133
|
-
method: method.name,
|
|
27134
|
-
stepNumber: step.stepNumber,
|
|
27135
|
-
stepType: step.type,
|
|
27136
|
-
fields
|
|
27137
|
-
});
|
|
27138
|
-
for (const field of fields) delete step[field];
|
|
27139
|
-
touched = true;
|
|
27140
|
-
}
|
|
27123
|
+
var GUIDE_TARGETS = ["claude", "gemini", "agy", "cursor", "copilot", "codex"];
|
|
27124
|
+
function reinjectLocalGuides(projectRoot, targetTypes) {
|
|
27125
|
+
const written = [];
|
|
27126
|
+
for (const type of targetTypes) {
|
|
27127
|
+
if (!GUIDE_TARGETS.includes(type)) continue;
|
|
27128
|
+
const guidePath = localGuideFilePath(projectRoot, type);
|
|
27129
|
+
if (guidePath) {
|
|
27130
|
+
injectGuide(guidePath, "local");
|
|
27131
|
+
if (!written.includes(guidePath)) written.push(guidePath);
|
|
27141
27132
|
}
|
|
27142
|
-
|
|
27143
|
-
}
|
|
27144
|
-
if (apply && changed.length) {
|
|
27145
|
-
for (const impl of changed) saveImplementationSpec(impl);
|
|
27146
|
-
invalidateSpecCache();
|
|
27133
|
+
writeRootGuideDelegator(projectRoot, type);
|
|
27147
27134
|
}
|
|
27148
|
-
return
|
|
27135
|
+
return written;
|
|
27149
27136
|
}
|
|
27150
27137
|
|
|
27151
|
-
// src/core/
|
|
27152
|
-
|
|
27153
|
-
|
|
27154
|
-
function diffSize(d) {
|
|
27155
|
-
return d.added.length + d.changed.length + d.removed.length;
|
|
27156
|
-
}
|
|
27157
|
-
function digest(content) {
|
|
27158
|
-
const normalized = content.replace(/\r\n/g, "\n");
|
|
27159
|
-
return crypto4.createHash("sha256").update(normalized, "utf8").digest("hex");
|
|
27160
|
-
}
|
|
27161
|
-
function currentSpecDigests(root = getProjectRoot()) {
|
|
27162
|
-
const mountDirs = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::")).map((s) => `${path28.resolve(root, s.projectPath).split(path28.sep).join("/")}/`);
|
|
27163
|
-
const out = {};
|
|
27164
|
-
for (const [abs, content] of snapshotSpecFiles()) {
|
|
27165
|
-
const normalized = path28.resolve(abs).split(path28.sep).join("/");
|
|
27166
|
-
if (mountDirs.some((dir) => normalized.startsWith(dir))) continue;
|
|
27167
|
-
out[path28.relative(root, abs).split(path28.sep).join("/")] = digest(content);
|
|
27168
|
-
}
|
|
27169
|
-
return out;
|
|
27170
|
-
}
|
|
27171
|
-
function captureApprovedSpecs(root = getProjectRoot(), scope) {
|
|
27172
|
-
const current2 = currentSpecDigests(root);
|
|
27173
|
-
if (!scope) return current2;
|
|
27174
|
-
const previous = readLockRecord()?.specs ?? {};
|
|
27175
|
-
const specs = { ...previous };
|
|
27176
|
-
for (const rel2 of Object.keys(previous)) {
|
|
27177
|
-
if (scope.paths.has(rel2) && current2[rel2] === void 0) delete specs[rel2];
|
|
27178
|
-
}
|
|
27179
|
-
for (const rel2 of scope.paths) {
|
|
27180
|
-
if (current2[rel2] !== void 0) specs[rel2] = current2[rel2];
|
|
27181
|
-
}
|
|
27182
|
-
return specs;
|
|
27138
|
+
// src/core/context.ts
|
|
27139
|
+
function contextDir(...segments) {
|
|
27140
|
+
return aiDir("context", ...segments);
|
|
27183
27141
|
}
|
|
27184
|
-
|
|
27185
|
-
|
|
27186
|
-
|
|
27187
|
-
|
|
27188
|
-
|
|
27189
|
-
|
|
27190
|
-
|
|
27191
|
-
|
|
27192
|
-
|
|
27193
|
-
const before = approved[rel2];
|
|
27194
|
-
if (before === void 0) added.push(rel2);
|
|
27195
|
-
else if (before !== d) changed.push(rel2);
|
|
27196
|
-
else unchangedPaths.push(rel2);
|
|
27197
|
-
}
|
|
27198
|
-
for (const rel2 of Object.keys(approved)) {
|
|
27199
|
-
if (current2[rel2] === void 0) removed.push(rel2);
|
|
27200
|
-
}
|
|
27201
|
-
return {
|
|
27202
|
-
added: added.sort(),
|
|
27203
|
-
changed: changed.sort(),
|
|
27204
|
-
removed: removed.sort(),
|
|
27205
|
-
unchangedPaths: unchangedPaths.sort()
|
|
27206
|
-
};
|
|
27142
|
+
var CONTEXT_PATHS = {
|
|
27143
|
+
dir: () => contextDir(),
|
|
27144
|
+
projectMd: () => contextDir("project.md"),
|
|
27145
|
+
architectureMd: () => contextDir("architecture.md"),
|
|
27146
|
+
domainsMd: () => contextDir("domains.md"),
|
|
27147
|
+
waironGuideMd: () => contextDir("wairon-guide.md")
|
|
27148
|
+
};
|
|
27149
|
+
function hasContext() {
|
|
27150
|
+
return pathExists(CONTEXT_PATHS.projectMd());
|
|
27207
27151
|
}
|
|
27208
|
-
function
|
|
27209
|
-
return
|
|
27152
|
+
function readProjectContext() {
|
|
27153
|
+
return readFileOrNull(CONTEXT_PATHS.projectMd());
|
|
27210
27154
|
}
|
|
27211
|
-
function
|
|
27212
|
-
return
|
|
27155
|
+
function readArchitectureContext() {
|
|
27156
|
+
return readFileOrNull(CONTEXT_PATHS.architectureMd());
|
|
27213
27157
|
}
|
|
27214
|
-
function
|
|
27215
|
-
|
|
27216
|
-
return diff ? new Set(diff.unchangedPaths) : null;
|
|
27158
|
+
function writeDomainsDoc(content) {
|
|
27159
|
+
return writeFileIfChanged(CONTEXT_PATHS.domainsMd(), content);
|
|
27217
27160
|
}
|
|
27218
|
-
function
|
|
27219
|
-
return
|
|
27161
|
+
function writeGuideDoc(content) {
|
|
27162
|
+
return writeFileIfChanged(CONTEXT_PATHS.waironGuideMd(), content);
|
|
27220
27163
|
}
|
|
27221
|
-
function
|
|
27222
|
-
|
|
27223
|
-
for (const mount of mounts) {
|
|
27224
|
-
if (!mount.projectPath || mount.id.includes("::")) continue;
|
|
27225
|
-
const child = readLockRecordAt(path28.resolve(root, mount.projectPath));
|
|
27226
|
-
if (child) pins[mount.id] = pinOf(child.stateId);
|
|
27227
|
-
}
|
|
27228
|
-
return pins;
|
|
27164
|
+
function derivedDocPaths() {
|
|
27165
|
+
return [CONTEXT_PATHS.waironGuideMd(), CONTEXT_PATHS.domainsMd()];
|
|
27229
27166
|
}
|
|
27230
|
-
function
|
|
27231
|
-
const
|
|
27232
|
-
|
|
27233
|
-
|
|
27234
|
-
|
|
27235
|
-
|
|
27236
|
-
|
|
27237
|
-
|
|
27167
|
+
function renderDomainsDoc() {
|
|
27168
|
+
const domains = resolveDomains();
|
|
27169
|
+
const lines = [
|
|
27170
|
+
versionStamp(),
|
|
27171
|
+
`# Domains`,
|
|
27172
|
+
``,
|
|
27173
|
+
`> Auto-generated by wairon. The human developer rebuilds this with \`wairon generate\`. `,
|
|
27174
|
+
``
|
|
27175
|
+
];
|
|
27176
|
+
if (domains.length === 0) {
|
|
27177
|
+
lines.push("_No domains yet. Define subsystems in the spec tree, or add free-standing domains with `wairon domains add`._");
|
|
27178
|
+
} else {
|
|
27179
|
+
lines.push(`**${domains.length} domain(s).**`);
|
|
27180
|
+
lines.push("");
|
|
27181
|
+
lines.push("| ID | Name | Source | Owned paths |");
|
|
27182
|
+
lines.push("|----|------|--------|-------------|");
|
|
27183
|
+
for (const d of domains) {
|
|
27184
|
+
const name = d.name ?? d.id;
|
|
27185
|
+
const source = d.boundTo ? `subsystem \`${d.boundTo}\`` : "free-standing";
|
|
27186
|
+
lines.push(`| \`${d.id}\` | ${name} | ${source} | \`${d.ownedPaths.join("`, `") || "\u2014"}\` |`);
|
|
27187
|
+
}
|
|
27238
27188
|
}
|
|
27239
|
-
return
|
|
27189
|
+
return lines.join("\n") + "\n";
|
|
27240
27190
|
}
|
|
27241
|
-
|
|
27242
|
-
|
|
27243
|
-
|
|
27244
|
-
var os5 = __toESM(require("os"));
|
|
27245
|
-
function gitConfig(key) {
|
|
27191
|
+
function renderWaironGuide() {
|
|
27192
|
+
const domains = resolveDomains();
|
|
27193
|
+
let projectName = "this project";
|
|
27246
27194
|
try {
|
|
27247
|
-
|
|
27248
|
-
encoding: "utf8",
|
|
27249
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
27250
|
-
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
27251
|
-
}).trim();
|
|
27252
|
-
return value || void 0;
|
|
27195
|
+
projectName = projectConfigRepository.load()?.name ?? projectName;
|
|
27253
27196
|
} catch {
|
|
27254
|
-
return void 0;
|
|
27255
27197
|
}
|
|
27256
|
-
|
|
27257
|
-
|
|
27258
|
-
|
|
27259
|
-
|
|
27260
|
-
|
|
27261
|
-
|
|
27198
|
+
const projectCtx = readProjectContext();
|
|
27199
|
+
const archCtx = readArchitectureContext();
|
|
27200
|
+
const lines = [
|
|
27201
|
+
versionStamp(),
|
|
27202
|
+
`<!-- wairon-generated \u2014 do not edit directly; the human developer rebuilds this with \`wairon generate\` -->`,
|
|
27203
|
+
``
|
|
27204
|
+
];
|
|
27205
|
+
if (projectCtx) {
|
|
27206
|
+
lines.push(`# Project Context \u2014 ${projectName}`);
|
|
27207
|
+
lines.push("");
|
|
27208
|
+
lines.push(projectCtx.trim());
|
|
27209
|
+
lines.push("");
|
|
27210
|
+
lines.push("---");
|
|
27211
|
+
lines.push("");
|
|
27262
27212
|
}
|
|
27263
|
-
|
|
27213
|
+
if (archCtx) {
|
|
27214
|
+
lines.push("# Architecture");
|
|
27215
|
+
lines.push("");
|
|
27216
|
+
lines.push(archCtx.trim());
|
|
27217
|
+
lines.push("");
|
|
27218
|
+
lines.push("---");
|
|
27219
|
+
lines.push("");
|
|
27220
|
+
}
|
|
27221
|
+
lines.push(`# Domain Map (${domains.length} domain${domains.length !== 1 ? "s" : ""})`);
|
|
27222
|
+
lines.push("");
|
|
27223
|
+
if (domains.length === 0) {
|
|
27224
|
+
lines.push("_No domains yet._");
|
|
27225
|
+
} else {
|
|
27226
|
+
lines.push("| ID | Source | Name |");
|
|
27227
|
+
lines.push("|----|--------|------|");
|
|
27228
|
+
for (const d of domains) {
|
|
27229
|
+
const name = d.name ?? d.id;
|
|
27230
|
+
const source = d.boundTo ? `subsystem \`${d.boundTo}\`` : "free-standing";
|
|
27231
|
+
lines.push(`| \`${d.id}\` | ${source} | ${name} |`);
|
|
27232
|
+
}
|
|
27233
|
+
}
|
|
27234
|
+
lines.push("");
|
|
27235
|
+
lines.push("---");
|
|
27236
|
+
lines.push("");
|
|
27237
|
+
const mcpSettingsPath = require("path").join(process.cwd(), ".claude", "settings.json");
|
|
27238
|
+
let mcpInstalled = false;
|
|
27264
27239
|
try {
|
|
27265
|
-
|
|
27240
|
+
const s = JSON.parse(require("fs").readFileSync(mcpSettingsPath, "utf8"));
|
|
27241
|
+
mcpInstalled = !!s?.mcpServers?.wairon;
|
|
27266
27242
|
} catch {
|
|
27267
27243
|
}
|
|
27268
|
-
|
|
27244
|
+
if (mcpInstalled) {
|
|
27245
|
+
lines.push("# wairon MCP Tools");
|
|
27246
|
+
lines.push("");
|
|
27247
|
+
lines.push("The **wairon MCP server** is active in this project. The tools are self-describing; call them directly:");
|
|
27248
|
+
lines.push("");
|
|
27249
|
+
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`.");
|
|
27250
|
+
lines.push("- Reading & maintenance: `sdd_get_spec`, `sdd_update_spec`, `sdd_delete_spec`, `sdd_validate_tree`, `sdd_get_status`.");
|
|
27251
|
+
lines.push("- Topology: `listAgents`, `getAgent`, `listDomains`, `validateTopology`, `getProjectConfig`.");
|
|
27252
|
+
lines.push("");
|
|
27253
|
+
lines.push("Use these MCP tools to query and change project state \u2014 never the `wairon` CLI (that is the human developer's tool).");
|
|
27254
|
+
lines.push("");
|
|
27255
|
+
lines.push("---");
|
|
27256
|
+
lines.push("");
|
|
27257
|
+
}
|
|
27258
|
+
lines.push(GLOBAL_GUIDE_BODY);
|
|
27259
|
+
lines.push("");
|
|
27260
|
+
return lines.join("\n");
|
|
27269
27261
|
}
|
|
27270
|
-
function
|
|
27271
|
-
const
|
|
27272
|
-
const
|
|
27273
|
-
|
|
27274
|
-
|
|
27275
|
-
return
|
|
27262
|
+
function syncContextFiles() {
|
|
27263
|
+
const domainsContent = renderDomainsDoc();
|
|
27264
|
+
const guideContent = renderWaironGuide();
|
|
27265
|
+
const domainsUpdated = writeDomainsDoc(domainsContent);
|
|
27266
|
+
const guideUpdated = writeGuideDoc(guideContent);
|
|
27267
|
+
return { domainsUpdated, guideUpdated };
|
|
27276
27268
|
}
|
|
27277
27269
|
|
|
27278
27270
|
// src/core/index.ts
|
|
@@ -27372,7 +27364,6 @@ function loadProjectConfig4() {
|
|
|
27372
27364
|
BuiltinTargetSchema,
|
|
27373
27365
|
BundleNotFoundError,
|
|
27374
27366
|
COMPONENT_AUTH_SOURCE,
|
|
27375
|
-
CONTEXT_PATHS,
|
|
27376
27367
|
CarriedDebtKindSchema,
|
|
27377
27368
|
CarriedDebtSchema,
|
|
27378
27369
|
CarriedFindingSchema,
|
|
@@ -27527,7 +27518,6 @@ function loadProjectConfig4() {
|
|
|
27527
27518
|
computeStateIdAt,
|
|
27528
27519
|
conceptNoun,
|
|
27529
27520
|
consumedContractInputs,
|
|
27530
|
-
contextDir,
|
|
27531
27521
|
createAgentRecord,
|
|
27532
27522
|
createChainedSubsystem,
|
|
27533
27523
|
createEmptyRegistry,
|
|
@@ -27550,6 +27540,7 @@ function loadProjectConfig4() {
|
|
|
27550
27540
|
deleteTypeSpec,
|
|
27551
27541
|
deregisterPackRef,
|
|
27552
27542
|
deriveExecutionProfile,
|
|
27543
|
+
derivedDocPaths,
|
|
27553
27544
|
describeApprover,
|
|
27554
27545
|
detectDomainCandidates,
|
|
27555
27546
|
diagnoseProjectPacks,
|
|
@@ -27604,7 +27595,6 @@ function loadProjectConfig4() {
|
|
|
27604
27595
|
globalPacksEnabled,
|
|
27605
27596
|
globalTemplatesDir,
|
|
27606
27597
|
globalVariantsDir,
|
|
27607
|
-
hasArchitectureContext,
|
|
27608
27598
|
hasAuthoredSpecs,
|
|
27609
27599
|
hasContext,
|
|
27610
27600
|
hasFunctionBody,
|
|
@@ -27703,14 +27693,12 @@ function loadProjectConfig4() {
|
|
|
27703
27693
|
projectSubsystemSurface,
|
|
27704
27694
|
provisionProject,
|
|
27705
27695
|
qualifiedTypeId,
|
|
27706
|
-
readArchitectureContext,
|
|
27707
27696
|
readFileOrNull,
|
|
27708
27697
|
readJsonFile,
|
|
27709
27698
|
readLockRecord,
|
|
27710
27699
|
readLockRecordAt,
|
|
27711
27700
|
readLockState,
|
|
27712
27701
|
readManifest,
|
|
27713
|
-
readProjectContext,
|
|
27714
27702
|
readResource,
|
|
27715
27703
|
readSkillResource,
|
|
27716
27704
|
readStampVersion,
|
|
@@ -27727,9 +27715,7 @@ function loadProjectConfig4() {
|
|
|
27727
27715
|
renameComponent,
|
|
27728
27716
|
renameMethod,
|
|
27729
27717
|
renderDiagram,
|
|
27730
|
-
renderDomainsDoc,
|
|
27731
27718
|
renderTemplateInstructions,
|
|
27732
|
-
renderWaironGuide,
|
|
27733
27719
|
repairForeignStepFields,
|
|
27734
27720
|
resolveAgentTopology,
|
|
27735
27721
|
resolveBudget,
|
|
@@ -27792,12 +27778,10 @@ function loadProjectConfig4() {
|
|
|
27792
27778
|
validateRegistry,
|
|
27793
27779
|
validateSddTree,
|
|
27794
27780
|
workspaceFor,
|
|
27795
|
-
writeArchitectureContext,
|
|
27796
27781
|
writeFile,
|
|
27797
27782
|
writeFileIfChanged,
|
|
27798
27783
|
writeJsonFile,
|
|
27799
27784
|
writeLockRecord,
|
|
27800
|
-
writeProjectContext,
|
|
27801
27785
|
writeRootGuideDelegator,
|
|
27802
27786
|
writeYamlFile
|
|
27803
27787
|
});
|