@wairon/cli 5.1.1-dev.60 → 5.1.1-dev.62
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 +1487 -1473
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +1142 -2611
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
68
|
+
WAIRON_VERSION = "5.1.1-dev.62";
|
|
69
69
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
70
70
|
SUPPORTED_ALIASES = ["wai"];
|
|
71
71
|
SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -1464,10 +1464,6 @@ function normalizeApprover(value) {
|
|
|
1464
1464
|
}
|
|
1465
1465
|
return { id: typeof value === "string" && value ? value : "unknown", source: "legacy" };
|
|
1466
1466
|
}
|
|
1467
|
-
function describeApprover(who) {
|
|
1468
|
-
const label = who.name ? `${who.name} (${who.id})` : who.id;
|
|
1469
|
-
return who.source === "hosted" ? `${label} [authenticated]` : label;
|
|
1470
|
-
}
|
|
1471
1467
|
function readLockRecord() {
|
|
1472
1468
|
try {
|
|
1473
1469
|
return normalizeRecord(JSON.parse(fs6.readFileSync(lockPath(), "utf8")));
|
|
@@ -8642,6 +8638,7 @@ var init_extensions = __esm({
|
|
|
8642
8638
|
init_canonical_json();
|
|
8643
8639
|
init_project();
|
|
8644
8640
|
init_packstore();
|
|
8641
|
+
init_packstore();
|
|
8645
8642
|
ProfileDefSchema = import_zod9.z.object({
|
|
8646
8643
|
family: import_zod9.z.enum(["backend-like", "frontend-like", "neutral"]).default("neutral"),
|
|
8647
8644
|
forbiddenStereotypes: import_zod9.z.array(import_zod9.z.object({ types: import_zod9.z.array(import_zod9.z.string()).min(1), reason: import_zod9.z.string().min(1) })).default([]),
|
|
@@ -22143,223 +22140,6 @@ var init_domain_curator = __esm({
|
|
|
22143
22140
|
}
|
|
22144
22141
|
});
|
|
22145
22142
|
|
|
22146
|
-
// src/core/detection.ts
|
|
22147
|
-
function detectDomainCandidates(projectRoot2, alreadyTrackedPaths = /* @__PURE__ */ new Set(), alreadyTrackedIds = /* @__PURE__ */ new Set()) {
|
|
22148
|
-
const candidates = /* @__PURE__ */ new Map();
|
|
22149
|
-
for (const c of detectGitSubmodules(projectRoot2)) {
|
|
22150
|
-
candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
|
|
22151
|
-
}
|
|
22152
|
-
for (const c of detectNestedGitRepos(projectRoot2)) {
|
|
22153
|
-
if (!candidates.has(c.path)) {
|
|
22154
|
-
candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
|
|
22155
|
-
}
|
|
22156
|
-
}
|
|
22157
|
-
const gitPaths = new Set(
|
|
22158
|
-
Array.from(candidates.values()).filter((c) => c.type === "git-submodule" || c.type === "git-repo").map((c) => c.path)
|
|
22159
|
-
);
|
|
22160
|
-
for (const c of detectPackageRoots(projectRoot2)) {
|
|
22161
|
-
if (candidates.has(c.path)) continue;
|
|
22162
|
-
const insideGit = Array.from(gitPaths).some(
|
|
22163
|
-
(gp) => c.path === gp || c.path.startsWith(gp + "/")
|
|
22164
|
-
);
|
|
22165
|
-
if (insideGit) continue;
|
|
22166
|
-
candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
|
|
22167
|
-
}
|
|
22168
|
-
const sorted = Array.from(candidates.values()).sort((a, b) => a.path.localeCompare(b.path));
|
|
22169
|
-
return deduplicateIds(sorted, alreadyTrackedIds);
|
|
22170
|
-
}
|
|
22171
|
-
function deduplicateIds(candidates, existingIds = /* @__PURE__ */ new Set()) {
|
|
22172
|
-
const idCount = /* @__PURE__ */ new Map();
|
|
22173
|
-
for (const id of existingIds) {
|
|
22174
|
-
idCount.set(id, (idCount.get(id) ?? 0) + 1);
|
|
22175
|
-
}
|
|
22176
|
-
for (const c of candidates) {
|
|
22177
|
-
idCount.set(c.suggestedId, (idCount.get(c.suggestedId) ?? 0) + 1);
|
|
22178
|
-
}
|
|
22179
|
-
return candidates.map((c) => {
|
|
22180
|
-
if ((idCount.get(c.suggestedId) ?? 0) <= 1) return c;
|
|
22181
|
-
const parts = c.path.split("/");
|
|
22182
|
-
const qualifiedId2 = parts.length >= 2 ? pathToId(`${parts[parts.length - 2]}-${parts[parts.length - 1]}`) : c.suggestedId;
|
|
22183
|
-
return { ...c, suggestedId: qualifiedId2 };
|
|
22184
|
-
});
|
|
22185
|
-
}
|
|
22186
|
-
function parseGitmodules(filePath) {
|
|
22187
|
-
const content = fs15.readFileSync(filePath, "utf-8");
|
|
22188
|
-
const entries = [];
|
|
22189
|
-
let current2 = {};
|
|
22190
|
-
for (const line2 of content.split("\n")) {
|
|
22191
|
-
const trimmed = line2.trim();
|
|
22192
|
-
const headerMatch = trimmed.match(/^\[submodule "(.+)"\]$/);
|
|
22193
|
-
if (headerMatch) {
|
|
22194
|
-
if (current2.path) entries.push(current2);
|
|
22195
|
-
current2 = { name: headerMatch[1] };
|
|
22196
|
-
continue;
|
|
22197
|
-
}
|
|
22198
|
-
const keyVal = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
|
|
22199
|
-
if (keyVal) {
|
|
22200
|
-
const [, key, value] = keyVal;
|
|
22201
|
-
if (key === "path") current2.path = value.trim();
|
|
22202
|
-
if (key === "url") current2.url = value.trim();
|
|
22203
|
-
}
|
|
22204
|
-
}
|
|
22205
|
-
if (current2.path) entries.push(current2);
|
|
22206
|
-
return entries;
|
|
22207
|
-
}
|
|
22208
|
-
function detectGitSubmodules(projectRoot2) {
|
|
22209
|
-
const gitmodulesPath = path19.join(projectRoot2, ".gitmodules");
|
|
22210
|
-
if (!fs15.existsSync(gitmodulesPath)) return [];
|
|
22211
|
-
return parseGitmodules(gitmodulesPath).map((entry) => ({
|
|
22212
|
-
suggestedId: pathToId(entry.path),
|
|
22213
|
-
suggestedName: pathToName(entry.path),
|
|
22214
|
-
path: normalizePath(entry.path),
|
|
22215
|
-
type: "git-submodule",
|
|
22216
|
-
alreadyTracked: false
|
|
22217
|
-
}));
|
|
22218
|
-
}
|
|
22219
|
-
function detectNestedGitRepos(projectRoot2) {
|
|
22220
|
-
const results = [];
|
|
22221
|
-
walkForGit(projectRoot2, projectRoot2, 0, results);
|
|
22222
|
-
return results;
|
|
22223
|
-
}
|
|
22224
|
-
function walkForGit(projectRoot2, currentDir, depth, results) {
|
|
22225
|
-
if (depth > MAX_SCAN_DEPTH) return;
|
|
22226
|
-
let entries;
|
|
22227
|
-
try {
|
|
22228
|
-
entries = fs15.readdirSync(currentDir, { withFileTypes: true });
|
|
22229
|
-
} catch {
|
|
22230
|
-
return;
|
|
22231
|
-
}
|
|
22232
|
-
for (const entry of entries) {
|
|
22233
|
-
if (!entry.isDirectory()) continue;
|
|
22234
|
-
if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
|
|
22235
|
-
const fullPath = path19.join(currentDir, entry.name);
|
|
22236
|
-
const relPath = normalizePath(path19.relative(projectRoot2, fullPath));
|
|
22237
|
-
if (relPath === "" || relPath === ".") continue;
|
|
22238
|
-
const gitPath = path19.join(fullPath, ".git");
|
|
22239
|
-
if (fs15.existsSync(gitPath)) {
|
|
22240
|
-
results.push({
|
|
22241
|
-
suggestedId: pathToId(relPath),
|
|
22242
|
-
suggestedName: pathToName(relPath),
|
|
22243
|
-
path: relPath,
|
|
22244
|
-
type: "git-repo",
|
|
22245
|
-
alreadyTracked: false
|
|
22246
|
-
});
|
|
22247
|
-
continue;
|
|
22248
|
-
}
|
|
22249
|
-
walkForGit(projectRoot2, fullPath, depth + 1, results);
|
|
22250
|
-
}
|
|
22251
|
-
}
|
|
22252
|
-
function detectPackageRoots(projectRoot2) {
|
|
22253
|
-
const results = [];
|
|
22254
|
-
walkForPackages(projectRoot2, projectRoot2, 0, results);
|
|
22255
|
-
return results;
|
|
22256
|
-
}
|
|
22257
|
-
function walkForPackages(projectRoot2, currentDir, depth, results) {
|
|
22258
|
-
if (depth > MAX_SCAN_DEPTH) return;
|
|
22259
|
-
let entries;
|
|
22260
|
-
try {
|
|
22261
|
-
entries = fs15.readdirSync(currentDir, { withFileTypes: true });
|
|
22262
|
-
} catch {
|
|
22263
|
-
return;
|
|
22264
|
-
}
|
|
22265
|
-
for (const entry of entries) {
|
|
22266
|
-
if (!entry.isDirectory()) continue;
|
|
22267
|
-
if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
|
|
22268
|
-
const fullPath = path19.join(currentDir, entry.name);
|
|
22269
|
-
const relPath = normalizePath(path19.relative(projectRoot2, fullPath));
|
|
22270
|
-
if (relPath === "" || relPath === ".") continue;
|
|
22271
|
-
const hasMarker = PACKAGE_MARKERS.some((m) => fs15.existsSync(path19.join(fullPath, m)));
|
|
22272
|
-
if (hasMarker) {
|
|
22273
|
-
results.push({
|
|
22274
|
-
suggestedId: pathToId(relPath),
|
|
22275
|
-
suggestedName: pathToName(relPath),
|
|
22276
|
-
path: relPath,
|
|
22277
|
-
type: "package-root",
|
|
22278
|
-
alreadyTracked: false
|
|
22279
|
-
});
|
|
22280
|
-
}
|
|
22281
|
-
walkForPackages(projectRoot2, fullPath, depth + 1, results);
|
|
22282
|
-
}
|
|
22283
|
-
}
|
|
22284
|
-
function pathToId(relPath) {
|
|
22285
|
-
const basename15 = path19.basename(relPath);
|
|
22286
|
-
return basename15.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
22287
|
-
}
|
|
22288
|
-
function pathToName(relPath) {
|
|
22289
|
-
const id = pathToId(relPath);
|
|
22290
|
-
return id.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
22291
|
-
}
|
|
22292
|
-
function normalizePath(p) {
|
|
22293
|
-
return p.replace(/\\/g, "/");
|
|
22294
|
-
}
|
|
22295
|
-
var fs15, path19, PACKAGE_MARKERS, MAX_SCAN_DEPTH;
|
|
22296
|
-
var init_detection = __esm({
|
|
22297
|
-
"src/core/detection.ts"() {
|
|
22298
|
-
"use strict";
|
|
22299
|
-
fs15 = __toESM(require("fs"));
|
|
22300
|
-
path19 = __toESM(require("path"));
|
|
22301
|
-
init_defaults();
|
|
22302
|
-
PACKAGE_MARKERS = [
|
|
22303
|
-
"package.json",
|
|
22304
|
-
"pyproject.toml",
|
|
22305
|
-
"Cargo.toml",
|
|
22306
|
-
"go.mod",
|
|
22307
|
-
"build.gradle",
|
|
22308
|
-
"build.gradle.kts",
|
|
22309
|
-
"pom.xml"
|
|
22310
|
-
];
|
|
22311
|
-
MAX_SCAN_DEPTH = 5;
|
|
22312
|
-
}
|
|
22313
|
-
});
|
|
22314
|
-
|
|
22315
|
-
// src/core/templates.ts
|
|
22316
|
-
function builtinTemplatesDir() {
|
|
22317
|
-
return path20.resolve(__dirname, "..", "templates");
|
|
22318
|
-
}
|
|
22319
|
-
function loadTemplate(id, globalOverride) {
|
|
22320
|
-
const dirs = templateSearchDirs(globalOverride);
|
|
22321
|
-
for (const dir of dirs) {
|
|
22322
|
-
const filePath = path20.join(dir, `${id}.yaml`);
|
|
22323
|
-
if (pathExists(filePath)) {
|
|
22324
|
-
const raw = readYamlFile(filePath);
|
|
22325
|
-
return { ...TemplateSchema.parse(raw), templateName: id };
|
|
22326
|
-
}
|
|
22327
|
-
}
|
|
22328
|
-
throw new TemplateNotFoundError(id);
|
|
22329
|
-
}
|
|
22330
|
-
function loadAgentOverride(agentId) {
|
|
22331
|
-
const filePath = path20.join(AI_PATHS.root(), "agents", `${agentId}.md`);
|
|
22332
|
-
return readFileOrNull(filePath);
|
|
22333
|
-
}
|
|
22334
|
-
function templateSearchDirs(globalOverride) {
|
|
22335
|
-
return [
|
|
22336
|
-
AI_PATHS.templatesDir(),
|
|
22337
|
-
// 1. project-local
|
|
22338
|
-
globalTemplatesDir(globalOverride),
|
|
22339
|
-
// 2. global user/org
|
|
22340
|
-
builtinTemplatesDir()
|
|
22341
|
-
// 3. built-in fallback
|
|
22342
|
-
];
|
|
22343
|
-
}
|
|
22344
|
-
function renderTemplateInstructions(template, vars) {
|
|
22345
|
-
return template.instructions.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
|
22346
|
-
return key in vars ? vars[key] : `{{${key}}}`;
|
|
22347
|
-
});
|
|
22348
|
-
}
|
|
22349
|
-
var path20;
|
|
22350
|
-
var init_templates = __esm({
|
|
22351
|
-
"src/core/templates.ts"() {
|
|
22352
|
-
"use strict";
|
|
22353
|
-
path20 = __toESM(require("path"));
|
|
22354
|
-
init_fs();
|
|
22355
|
-
init_yaml();
|
|
22356
|
-
init_errors();
|
|
22357
|
-
init_template();
|
|
22358
|
-
init_paths();
|
|
22359
|
-
init_defaults();
|
|
22360
|
-
}
|
|
22361
|
-
});
|
|
22362
|
-
|
|
22363
22143
|
// src/core/provision.ts
|
|
22364
22144
|
var provision_exports = {};
|
|
22365
22145
|
__export(provision_exports, {
|
|
@@ -22422,7 +22202,7 @@ function provisionProject(name) {
|
|
|
22422
22202
|
function ensureProjectInitialized(fallbackName) {
|
|
22423
22203
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
22424
22204
|
const paths = aiPathsAt(getProjectRoot());
|
|
22425
|
-
const hasSystem =
|
|
22205
|
+
const hasSystem = fs15.existsSync(paths.specsSystem());
|
|
22426
22206
|
let name = fallbackName;
|
|
22427
22207
|
if (hasSystem) {
|
|
22428
22208
|
const existing = loadSystemSpec();
|
|
@@ -22444,11 +22224,11 @@ function ensureProjectInitialized(fallbackName) {
|
|
|
22444
22224
|
function walkChainedSubprojects(projectRoot2, onChild) {
|
|
22445
22225
|
const visited = /* @__PURE__ */ new Set();
|
|
22446
22226
|
const walk2 = (dir) => {
|
|
22447
|
-
const resolved =
|
|
22227
|
+
const resolved = path19.resolve(dir);
|
|
22448
22228
|
if (visited.has(resolved)) return;
|
|
22449
22229
|
visited.add(resolved);
|
|
22450
22230
|
const specsDir = aiPathsAt(dir).specsDir();
|
|
22451
|
-
if (!
|
|
22231
|
+
if (!fs15.existsSync(specsDir)) return;
|
|
22452
22232
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
22453
22233
|
let raw;
|
|
22454
22234
|
try {
|
|
@@ -22466,7 +22246,7 @@ function walkChainedSubprojects(projectRoot2, onChild) {
|
|
|
22466
22246
|
continue;
|
|
22467
22247
|
}
|
|
22468
22248
|
const id = raw.id;
|
|
22469
|
-
onChild(childDir, typeof id === "string" ? id :
|
|
22249
|
+
onChild(childDir, typeof id === "string" ? id : path19.basename(childDir));
|
|
22470
22250
|
walk2(childDir);
|
|
22471
22251
|
}
|
|
22472
22252
|
};
|
|
@@ -22475,7 +22255,7 @@ function walkChainedSubprojects(projectRoot2, onChild) {
|
|
|
22475
22255
|
function listDirectChainedSubprojects(projectRoot2) {
|
|
22476
22256
|
const out = [];
|
|
22477
22257
|
const specsDir = aiPathsAt(projectRoot2).specsDir();
|
|
22478
|
-
if (!
|
|
22258
|
+
if (!fs15.existsSync(specsDir)) return out;
|
|
22479
22259
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
22480
22260
|
let raw;
|
|
22481
22261
|
try {
|
|
@@ -22493,12 +22273,12 @@ function listDirectChainedSubprojects(projectRoot2) {
|
|
|
22493
22273
|
continue;
|
|
22494
22274
|
}
|
|
22495
22275
|
const id = raw.id;
|
|
22496
|
-
out.push({ dir, subsystemId: typeof id === "string" ? id :
|
|
22276
|
+
out.push({ dir, subsystemId: typeof id === "string" ? id : path19.basename(dir) });
|
|
22497
22277
|
}
|
|
22498
22278
|
return out;
|
|
22499
22279
|
}
|
|
22500
22280
|
function childHasSpecsButNoConfig(childDir) {
|
|
22501
|
-
return
|
|
22281
|
+
return fs15.existsSync(aiPathsAt(childDir).specsDir()) && !projectConfigRepositoryAt(childDir).exists();
|
|
22502
22282
|
}
|
|
22503
22283
|
function findChainingSubprojectsMissingConfig(projectRoot2) {
|
|
22504
22284
|
const missing = [];
|
|
@@ -22547,14 +22327,14 @@ function moveSubsystemProject(subsystemId, newProjectPath) {
|
|
|
22547
22327
|
const oldDir = assertContainedProjectPath(root, sub.projectPath);
|
|
22548
22328
|
const newDir = assertContainedProjectPath(root, nextPath);
|
|
22549
22329
|
if (oldDir !== newDir) {
|
|
22550
|
-
if (!
|
|
22330
|
+
if (!fs15.existsSync(oldDir)) {
|
|
22551
22331
|
throw new WaironError(`Subproject directory not found at its current path: ${oldDir}`);
|
|
22552
22332
|
}
|
|
22553
|
-
if (
|
|
22333
|
+
if (fs15.existsSync(newDir)) {
|
|
22554
22334
|
throw new WaironError(`Target directory already exists: ${newDir}`);
|
|
22555
22335
|
}
|
|
22556
|
-
ensureDir(
|
|
22557
|
-
|
|
22336
|
+
ensureDir(path19.dirname(newDir));
|
|
22337
|
+
fs15.renameSync(oldDir, newDir);
|
|
22558
22338
|
}
|
|
22559
22339
|
saveSubsystemSpec({ ...sub, projectPath: nextPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
22560
22340
|
invalidateSpecCache();
|
|
@@ -22563,9 +22343,9 @@ function toPosixPath(p) {
|
|
|
22563
22343
|
return p.replace(/\\/g, "/");
|
|
22564
22344
|
}
|
|
22565
22345
|
function isWithinDir(dir, file) {
|
|
22566
|
-
const d =
|
|
22567
|
-
const f =
|
|
22568
|
-
return f === d || f.startsWith(d +
|
|
22346
|
+
const d = path19.resolve(dir);
|
|
22347
|
+
const f = path19.resolve(file);
|
|
22348
|
+
return f === d || f.startsWith(d + path19.sep);
|
|
22569
22349
|
}
|
|
22570
22350
|
function externalizeSubsystem(subsystemId, projectPath) {
|
|
22571
22351
|
if (subsystemId.includes("::")) {
|
|
@@ -22577,14 +22357,14 @@ function externalizeSubsystem(subsystemId, projectPath) {
|
|
|
22577
22357
|
}
|
|
22578
22358
|
const parentRoot = getProjectRoot();
|
|
22579
22359
|
const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
|
|
22580
|
-
const fooDir =
|
|
22581
|
-
if (!
|
|
22360
|
+
const fooDir = path19.join(parentSpecsDir, subsystemId);
|
|
22361
|
+
if (!fs15.existsSync(fooDir)) {
|
|
22582
22362
|
throw new WaironError(`subsystem specs directory not found: ${fooDir}`);
|
|
22583
22363
|
}
|
|
22584
22364
|
const relPath = toPosixPath(projectPath);
|
|
22585
22365
|
const childDir = assertContainedProjectPath(parentRoot, relPath);
|
|
22586
|
-
const childFooDir =
|
|
22587
|
-
if (
|
|
22366
|
+
const childFooDir = path19.join(childDir, ".wai", "specs", subsystemId);
|
|
22367
|
+
if (fs15.existsSync(childFooDir)) {
|
|
22588
22368
|
throw new WaironError(`target already contains a "${subsystemId}" subsystem: ${childFooDir}`);
|
|
22589
22369
|
}
|
|
22590
22370
|
const renameMap = buildRenameMap(
|
|
@@ -22596,18 +22376,18 @@ function externalizeSubsystem(subsystemId, projectPath) {
|
|
|
22596
22376
|
runWithProjectRoot(childDir, () => {
|
|
22597
22377
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
22598
22378
|
projectConfigRepository.create(defaultProjectConfig(childSystemName, now));
|
|
22599
|
-
ensureDir(
|
|
22379
|
+
ensureDir(path19.join(childDir, ".wai", "specs"));
|
|
22600
22380
|
saveSystemSpec(bootstrapSystemSpec(childSystemName, now));
|
|
22601
22381
|
});
|
|
22602
|
-
ensureDir(
|
|
22603
|
-
|
|
22382
|
+
ensureDir(path19.dirname(childFooDir));
|
|
22383
|
+
fs15.renameSync(fooDir, childFooDir);
|
|
22604
22384
|
rebaseImplementationPaths(childFooDir, parentRoot, childDir);
|
|
22605
|
-
patchSubsystemIndex(
|
|
22385
|
+
patchSubsystemIndex(path19.join(childFooDir, ".index.yaml"), (s) => {
|
|
22606
22386
|
s.parentSystem = childSystemName;
|
|
22607
22387
|
delete s.projectPath;
|
|
22608
22388
|
});
|
|
22609
22389
|
ensureDir(fooDir);
|
|
22610
|
-
writeYamlFile(
|
|
22390
|
+
writeYamlFile(path19.join(fooDir, ".index.yaml"), {
|
|
22611
22391
|
id: subsystemId,
|
|
22612
22392
|
name: foo.name,
|
|
22613
22393
|
description: foo.description,
|
|
@@ -22634,9 +22414,9 @@ function internalizeSubsystem(subsystemId) {
|
|
|
22634
22414
|
const parentRoot = getProjectRoot();
|
|
22635
22415
|
const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
|
|
22636
22416
|
const childDir = assertContainedProjectPath(parentRoot, foo.projectPath);
|
|
22637
|
-
const childWai =
|
|
22638
|
-
const childFooDir =
|
|
22639
|
-
if (!
|
|
22417
|
+
const childWai = path19.join(childDir, ".wai");
|
|
22418
|
+
const childFooDir = path19.join(childDir, ".wai", "specs", subsystemId);
|
|
22419
|
+
if (!fs15.existsSync(childFooDir)) {
|
|
22640
22420
|
throw new WaironError(`external subproject missing subsystem "${subsystemId}": ${childFooDir}`);
|
|
22641
22421
|
}
|
|
22642
22422
|
const childOwnSubs = runWithProjectRoot(childDir, () => loadSubsystemSpecs()).filter((s) => !s.id.includes("::"));
|
|
@@ -22649,16 +22429,16 @@ function internalizeSubsystem(subsystemId) {
|
|
|
22649
22429
|
false
|
|
22650
22430
|
);
|
|
22651
22431
|
const parentSystemName = loadSystemSpec()?.name ?? foo.parentSystem;
|
|
22652
|
-
const fooDir =
|
|
22653
|
-
|
|
22654
|
-
ensureDir(
|
|
22655
|
-
|
|
22432
|
+
const fooDir = path19.join(parentSpecsDir, subsystemId);
|
|
22433
|
+
fs15.rmSync(fooDir, { recursive: true, force: true });
|
|
22434
|
+
ensureDir(path19.dirname(fooDir));
|
|
22435
|
+
fs15.renameSync(childFooDir, fooDir);
|
|
22656
22436
|
rebaseImplementationPaths(fooDir, childDir, parentRoot);
|
|
22657
|
-
patchSubsystemIndex(
|
|
22437
|
+
patchSubsystemIndex(path19.join(fooDir, ".index.yaml"), (s) => {
|
|
22658
22438
|
s.parentSystem = parentSystemName;
|
|
22659
22439
|
delete s.projectPath;
|
|
22660
22440
|
});
|
|
22661
|
-
|
|
22441
|
+
fs15.rmSync(childWai, { recursive: true, force: true });
|
|
22662
22442
|
rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
|
|
22663
22443
|
rebaseMovedRefs(fooDir, subsystemId, "outOf");
|
|
22664
22444
|
invalidateSpecCache();
|
|
@@ -22736,8 +22516,8 @@ function rebaseImplementationPaths(specsDir, fromRoot, toRoot) {
|
|
|
22736
22516
|
let changed = false;
|
|
22737
22517
|
const rebase = (holder, key) => {
|
|
22738
22518
|
const p = holder[key];
|
|
22739
|
-
if (typeof p !== "string" || p === "" ||
|
|
22740
|
-
const next = toPosixPath(
|
|
22519
|
+
if (typeof p !== "string" || p === "" || path19.isAbsolute(p)) return;
|
|
22520
|
+
const next = toPosixPath(path19.relative(toRoot, path19.resolve(fromRoot, p)));
|
|
22741
22521
|
if (next !== p) {
|
|
22742
22522
|
holder[key] = next;
|
|
22743
22523
|
changed = true;
|
|
@@ -22753,7 +22533,7 @@ function rebaseImplementationPaths(specsDir, fromRoot, toRoot) {
|
|
|
22753
22533
|
}
|
|
22754
22534
|
}
|
|
22755
22535
|
function patchSubsystemIndex(indexPath, mutate) {
|
|
22756
|
-
if (!
|
|
22536
|
+
if (!fs15.existsSync(indexPath)) return;
|
|
22757
22537
|
const raw = readYamlFile(indexPath);
|
|
22758
22538
|
mutate(raw);
|
|
22759
22539
|
raw.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -22814,8 +22594,8 @@ function moveRenamedSpecs(componentId, newId, component, movingInterface, moving
|
|
|
22814
22594
|
if (movedInterface) saveInterfaceSpec({ ...movedInterface, id: interfaceId, component: newId });
|
|
22815
22595
|
if (movedImplementation) {
|
|
22816
22596
|
const oldFile = getImplementationPath(movedImplementation.id);
|
|
22817
|
-
if (
|
|
22818
|
-
|
|
22597
|
+
if (path19.resolve(getImplementationPath(implementationId, movedImplementation.contract)) === path19.resolve(oldFile)) {
|
|
22598
|
+
fs15.unlinkSync(oldFile);
|
|
22819
22599
|
}
|
|
22820
22600
|
saveImplementationSpec({ ...movedImplementation, id: implementationId });
|
|
22821
22601
|
}
|
|
@@ -22825,17 +22605,17 @@ function moveRenamedSpecs(componentId, newId, component, movingInterface, moving
|
|
|
22825
22605
|
invalidateSpecCache();
|
|
22826
22606
|
}
|
|
22827
22607
|
function removeSpecFile2(file, id) {
|
|
22828
|
-
if (!
|
|
22608
|
+
if (!fs15.existsSync(file)) return;
|
|
22829
22609
|
try {
|
|
22830
22610
|
if (readYamlFile(file)?.id !== id) return;
|
|
22831
22611
|
} catch {
|
|
22832
22612
|
return;
|
|
22833
22613
|
}
|
|
22834
|
-
|
|
22835
|
-
const specsRoot =
|
|
22836
|
-
for (let dir =
|
|
22837
|
-
if (
|
|
22838
|
-
|
|
22614
|
+
fs15.unlinkSync(file);
|
|
22615
|
+
const specsRoot = path19.resolve(aiPathsAt(getProjectRoot()).specsDir());
|
|
22616
|
+
for (let dir = path19.dirname(path19.resolve(file)); dir !== specsRoot && isWithinDir(specsRoot, dir); dir = path19.dirname(dir)) {
|
|
22617
|
+
if (fs15.readdirSync(dir).length > 0) break;
|
|
22618
|
+
fs15.rmdirSync(dir);
|
|
22839
22619
|
}
|
|
22840
22620
|
}
|
|
22841
22621
|
function renameMethod(componentId, methodName, newName, pinSymbol) {
|
|
@@ -22932,12 +22712,12 @@ function collectMentions(specsDir, methodName, moved) {
|
|
|
22932
22712
|
function identifierPattern(methodName) {
|
|
22933
22713
|
return new RegExp(`(?<![A-Za-z0-9_])${methodName}(?![A-Za-z0-9_])`);
|
|
22934
22714
|
}
|
|
22935
|
-
var
|
|
22715
|
+
var fs15, path19, METHOD_NAME, PROSE_FIELDS2;
|
|
22936
22716
|
var init_provision = __esm({
|
|
22937
22717
|
"src/core/provision.ts"() {
|
|
22938
22718
|
"use strict";
|
|
22939
|
-
|
|
22940
|
-
|
|
22719
|
+
fs15 = __toESM(require("fs"));
|
|
22720
|
+
path19 = __toESM(require("path"));
|
|
22941
22721
|
init_specs2();
|
|
22942
22722
|
init_paths();
|
|
22943
22723
|
init_project_config();
|
|
@@ -22960,6 +22740,223 @@ var init_provision = __esm({
|
|
|
22960
22740
|
}
|
|
22961
22741
|
});
|
|
22962
22742
|
|
|
22743
|
+
// src/core/detection.ts
|
|
22744
|
+
function detectDomainCandidates(projectRoot2, alreadyTrackedPaths = /* @__PURE__ */ new Set(), alreadyTrackedIds = /* @__PURE__ */ new Set()) {
|
|
22745
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
22746
|
+
for (const c of detectGitSubmodules(projectRoot2)) {
|
|
22747
|
+
candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
|
|
22748
|
+
}
|
|
22749
|
+
for (const c of detectNestedGitRepos(projectRoot2)) {
|
|
22750
|
+
if (!candidates.has(c.path)) {
|
|
22751
|
+
candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
|
|
22752
|
+
}
|
|
22753
|
+
}
|
|
22754
|
+
const gitPaths = new Set(
|
|
22755
|
+
Array.from(candidates.values()).filter((c) => c.type === "git-submodule" || c.type === "git-repo").map((c) => c.path)
|
|
22756
|
+
);
|
|
22757
|
+
for (const c of detectPackageRoots(projectRoot2)) {
|
|
22758
|
+
if (candidates.has(c.path)) continue;
|
|
22759
|
+
const insideGit = Array.from(gitPaths).some(
|
|
22760
|
+
(gp) => c.path === gp || c.path.startsWith(gp + "/")
|
|
22761
|
+
);
|
|
22762
|
+
if (insideGit) continue;
|
|
22763
|
+
candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
|
|
22764
|
+
}
|
|
22765
|
+
const sorted = Array.from(candidates.values()).sort((a, b) => a.path.localeCompare(b.path));
|
|
22766
|
+
return deduplicateIds(sorted, alreadyTrackedIds);
|
|
22767
|
+
}
|
|
22768
|
+
function deduplicateIds(candidates, existingIds = /* @__PURE__ */ new Set()) {
|
|
22769
|
+
const idCount = /* @__PURE__ */ new Map();
|
|
22770
|
+
for (const id of existingIds) {
|
|
22771
|
+
idCount.set(id, (idCount.get(id) ?? 0) + 1);
|
|
22772
|
+
}
|
|
22773
|
+
for (const c of candidates) {
|
|
22774
|
+
idCount.set(c.suggestedId, (idCount.get(c.suggestedId) ?? 0) + 1);
|
|
22775
|
+
}
|
|
22776
|
+
return candidates.map((c) => {
|
|
22777
|
+
if ((idCount.get(c.suggestedId) ?? 0) <= 1) return c;
|
|
22778
|
+
const parts = c.path.split("/");
|
|
22779
|
+
const qualifiedId2 = parts.length >= 2 ? pathToId(`${parts[parts.length - 2]}-${parts[parts.length - 1]}`) : c.suggestedId;
|
|
22780
|
+
return { ...c, suggestedId: qualifiedId2 };
|
|
22781
|
+
});
|
|
22782
|
+
}
|
|
22783
|
+
function parseGitmodules(filePath) {
|
|
22784
|
+
const content = fs16.readFileSync(filePath, "utf-8");
|
|
22785
|
+
const entries = [];
|
|
22786
|
+
let current2 = {};
|
|
22787
|
+
for (const line2 of content.split("\n")) {
|
|
22788
|
+
const trimmed = line2.trim();
|
|
22789
|
+
const headerMatch = trimmed.match(/^\[submodule "(.+)"\]$/);
|
|
22790
|
+
if (headerMatch) {
|
|
22791
|
+
if (current2.path) entries.push(current2);
|
|
22792
|
+
current2 = { name: headerMatch[1] };
|
|
22793
|
+
continue;
|
|
22794
|
+
}
|
|
22795
|
+
const keyVal = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
|
|
22796
|
+
if (keyVal) {
|
|
22797
|
+
const [, key, value] = keyVal;
|
|
22798
|
+
if (key === "path") current2.path = value.trim();
|
|
22799
|
+
if (key === "url") current2.url = value.trim();
|
|
22800
|
+
}
|
|
22801
|
+
}
|
|
22802
|
+
if (current2.path) entries.push(current2);
|
|
22803
|
+
return entries;
|
|
22804
|
+
}
|
|
22805
|
+
function detectGitSubmodules(projectRoot2) {
|
|
22806
|
+
const gitmodulesPath = path20.join(projectRoot2, ".gitmodules");
|
|
22807
|
+
if (!fs16.existsSync(gitmodulesPath)) return [];
|
|
22808
|
+
return parseGitmodules(gitmodulesPath).map((entry) => ({
|
|
22809
|
+
suggestedId: pathToId(entry.path),
|
|
22810
|
+
suggestedName: pathToName(entry.path),
|
|
22811
|
+
path: normalizePath(entry.path),
|
|
22812
|
+
type: "git-submodule",
|
|
22813
|
+
alreadyTracked: false
|
|
22814
|
+
}));
|
|
22815
|
+
}
|
|
22816
|
+
function detectNestedGitRepos(projectRoot2) {
|
|
22817
|
+
const results = [];
|
|
22818
|
+
walkForGit(projectRoot2, projectRoot2, 0, results);
|
|
22819
|
+
return results;
|
|
22820
|
+
}
|
|
22821
|
+
function walkForGit(projectRoot2, currentDir, depth, results) {
|
|
22822
|
+
if (depth > MAX_SCAN_DEPTH) return;
|
|
22823
|
+
let entries;
|
|
22824
|
+
try {
|
|
22825
|
+
entries = fs16.readdirSync(currentDir, { withFileTypes: true });
|
|
22826
|
+
} catch {
|
|
22827
|
+
return;
|
|
22828
|
+
}
|
|
22829
|
+
for (const entry of entries) {
|
|
22830
|
+
if (!entry.isDirectory()) continue;
|
|
22831
|
+
if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
|
|
22832
|
+
const fullPath = path20.join(currentDir, entry.name);
|
|
22833
|
+
const relPath = normalizePath(path20.relative(projectRoot2, fullPath));
|
|
22834
|
+
if (relPath === "" || relPath === ".") continue;
|
|
22835
|
+
const gitPath = path20.join(fullPath, ".git");
|
|
22836
|
+
if (fs16.existsSync(gitPath)) {
|
|
22837
|
+
results.push({
|
|
22838
|
+
suggestedId: pathToId(relPath),
|
|
22839
|
+
suggestedName: pathToName(relPath),
|
|
22840
|
+
path: relPath,
|
|
22841
|
+
type: "git-repo",
|
|
22842
|
+
alreadyTracked: false
|
|
22843
|
+
});
|
|
22844
|
+
continue;
|
|
22845
|
+
}
|
|
22846
|
+
walkForGit(projectRoot2, fullPath, depth + 1, results);
|
|
22847
|
+
}
|
|
22848
|
+
}
|
|
22849
|
+
function detectPackageRoots(projectRoot2) {
|
|
22850
|
+
const results = [];
|
|
22851
|
+
walkForPackages(projectRoot2, projectRoot2, 0, results);
|
|
22852
|
+
return results;
|
|
22853
|
+
}
|
|
22854
|
+
function walkForPackages(projectRoot2, currentDir, depth, results) {
|
|
22855
|
+
if (depth > MAX_SCAN_DEPTH) return;
|
|
22856
|
+
let entries;
|
|
22857
|
+
try {
|
|
22858
|
+
entries = fs16.readdirSync(currentDir, { withFileTypes: true });
|
|
22859
|
+
} catch {
|
|
22860
|
+
return;
|
|
22861
|
+
}
|
|
22862
|
+
for (const entry of entries) {
|
|
22863
|
+
if (!entry.isDirectory()) continue;
|
|
22864
|
+
if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
|
|
22865
|
+
const fullPath = path20.join(currentDir, entry.name);
|
|
22866
|
+
const relPath = normalizePath(path20.relative(projectRoot2, fullPath));
|
|
22867
|
+
if (relPath === "" || relPath === ".") continue;
|
|
22868
|
+
const hasMarker = PACKAGE_MARKERS.some((m) => fs16.existsSync(path20.join(fullPath, m)));
|
|
22869
|
+
if (hasMarker) {
|
|
22870
|
+
results.push({
|
|
22871
|
+
suggestedId: pathToId(relPath),
|
|
22872
|
+
suggestedName: pathToName(relPath),
|
|
22873
|
+
path: relPath,
|
|
22874
|
+
type: "package-root",
|
|
22875
|
+
alreadyTracked: false
|
|
22876
|
+
});
|
|
22877
|
+
}
|
|
22878
|
+
walkForPackages(projectRoot2, fullPath, depth + 1, results);
|
|
22879
|
+
}
|
|
22880
|
+
}
|
|
22881
|
+
function pathToId(relPath) {
|
|
22882
|
+
const basename15 = path20.basename(relPath);
|
|
22883
|
+
return basename15.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
22884
|
+
}
|
|
22885
|
+
function pathToName(relPath) {
|
|
22886
|
+
const id = pathToId(relPath);
|
|
22887
|
+
return id.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
22888
|
+
}
|
|
22889
|
+
function normalizePath(p) {
|
|
22890
|
+
return p.replace(/\\/g, "/");
|
|
22891
|
+
}
|
|
22892
|
+
var fs16, path20, PACKAGE_MARKERS, MAX_SCAN_DEPTH;
|
|
22893
|
+
var init_detection = __esm({
|
|
22894
|
+
"src/core/detection.ts"() {
|
|
22895
|
+
"use strict";
|
|
22896
|
+
fs16 = __toESM(require("fs"));
|
|
22897
|
+
path20 = __toESM(require("path"));
|
|
22898
|
+
init_defaults();
|
|
22899
|
+
PACKAGE_MARKERS = [
|
|
22900
|
+
"package.json",
|
|
22901
|
+
"pyproject.toml",
|
|
22902
|
+
"Cargo.toml",
|
|
22903
|
+
"go.mod",
|
|
22904
|
+
"build.gradle",
|
|
22905
|
+
"build.gradle.kts",
|
|
22906
|
+
"pom.xml"
|
|
22907
|
+
];
|
|
22908
|
+
MAX_SCAN_DEPTH = 5;
|
|
22909
|
+
}
|
|
22910
|
+
});
|
|
22911
|
+
|
|
22912
|
+
// src/core/templates.ts
|
|
22913
|
+
function builtinTemplatesDir() {
|
|
22914
|
+
return path21.resolve(__dirname, "..", "templates");
|
|
22915
|
+
}
|
|
22916
|
+
function loadTemplate(id, globalOverride) {
|
|
22917
|
+
const dirs = templateSearchDirs(globalOverride);
|
|
22918
|
+
for (const dir of dirs) {
|
|
22919
|
+
const filePath = path21.join(dir, `${id}.yaml`);
|
|
22920
|
+
if (pathExists(filePath)) {
|
|
22921
|
+
const raw = readYamlFile(filePath);
|
|
22922
|
+
return { ...TemplateSchema.parse(raw), templateName: id };
|
|
22923
|
+
}
|
|
22924
|
+
}
|
|
22925
|
+
throw new TemplateNotFoundError(id);
|
|
22926
|
+
}
|
|
22927
|
+
function loadAgentOverride(agentId) {
|
|
22928
|
+
const filePath = path21.join(AI_PATHS.root(), "agents", `${agentId}.md`);
|
|
22929
|
+
return readFileOrNull(filePath);
|
|
22930
|
+
}
|
|
22931
|
+
function templateSearchDirs(globalOverride) {
|
|
22932
|
+
return [
|
|
22933
|
+
AI_PATHS.templatesDir(),
|
|
22934
|
+
// 1. project-local
|
|
22935
|
+
globalTemplatesDir(globalOverride),
|
|
22936
|
+
// 2. global user/org
|
|
22937
|
+
builtinTemplatesDir()
|
|
22938
|
+
// 3. built-in fallback
|
|
22939
|
+
];
|
|
22940
|
+
}
|
|
22941
|
+
function renderTemplateInstructions(template, vars) {
|
|
22942
|
+
return template.instructions.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
|
22943
|
+
return key in vars ? vars[key] : `{{${key}}}`;
|
|
22944
|
+
});
|
|
22945
|
+
}
|
|
22946
|
+
var path21;
|
|
22947
|
+
var init_templates = __esm({
|
|
22948
|
+
"src/core/templates.ts"() {
|
|
22949
|
+
"use strict";
|
|
22950
|
+
path21 = __toESM(require("path"));
|
|
22951
|
+
init_fs();
|
|
22952
|
+
init_yaml();
|
|
22953
|
+
init_errors();
|
|
22954
|
+
init_template();
|
|
22955
|
+
init_paths();
|
|
22956
|
+
init_defaults();
|
|
22957
|
+
}
|
|
22958
|
+
});
|
|
22959
|
+
|
|
22963
22960
|
// src/core/execution_profile.ts
|
|
22964
22961
|
function isSweeping(p) {
|
|
22965
22962
|
return p === "**" || p === "**/*" || p.startsWith("**/");
|
|
@@ -23503,633 +23500,267 @@ var init_agent_resolver = __esm({
|
|
|
23503
23500
|
}
|
|
23504
23501
|
});
|
|
23505
23502
|
|
|
23506
|
-
// src/
|
|
23507
|
-
function
|
|
23508
|
-
|
|
23509
|
-
|
|
23510
|
-
|
|
23511
|
-
return
|
|
23503
|
+
// src/exporters/claude.ts
|
|
23504
|
+
function toolsFor(budget) {
|
|
23505
|
+
const base = TOOLS_BY_CLASS[budget.toolClass];
|
|
23506
|
+
if (!base) return void 0;
|
|
23507
|
+
if (budget.allowNestedDelegation && !base.includes("Agent")) {
|
|
23508
|
+
return [...base, "Agent"];
|
|
23512
23509
|
}
|
|
23510
|
+
if (!budget.allowNestedDelegation) {
|
|
23511
|
+
return base.filter((t) => t !== "Agent");
|
|
23512
|
+
}
|
|
23513
|
+
return base;
|
|
23513
23514
|
}
|
|
23514
|
-
function
|
|
23515
|
-
|
|
23516
|
-
return profile !== null && block.profile.includes(profile);
|
|
23517
|
-
}
|
|
23518
|
-
function skillPointers(skills) {
|
|
23519
|
-
if (skills.length === 0) return "(none published by this server)";
|
|
23520
|
-
return skills.map((s) => `- \`${s.resourceUri}\` \u2014 ${s.description || s.name}`).join("\n");
|
|
23515
|
+
function mcpServersFor(access) {
|
|
23516
|
+
return access === "none" ? "[]" : void 0;
|
|
23521
23517
|
}
|
|
23522
|
-
function
|
|
23523
|
-
return
|
|
23524
|
-
|
|
23525
|
-
This server owns one system's **spec tree**: its architecture, specified top-down
|
|
23526
|
-
and machine-checked. Author it ONLY through this server's \`sdd_*\` tools \u2014 never by
|
|
23527
|
-
writing spec files directly, and never via the \`wairon\` CLI (that is the human
|
|
23528
|
-
developer's tool).
|
|
23529
|
-
|
|
23530
|
-
## The tree: L0 \u2192 L5
|
|
23531
|
-
|
|
23532
|
-
- **L0 System** \u2014 vision, boundaries, global requirements, target language.
|
|
23533
|
-
- **L1 Subsystem** \u2014 an isolated service, its \`publicInterfaces\`, its trusted links.
|
|
23534
|
-
- **L2 Component** \u2014 ONE building block (\`Portal\`, \`Orchestrator\`, \`Supervisor\`,
|
|
23535
|
-
\`Actor\`, \`Store\`, \`Index\`, \`Query\`, \`Registry\`, \`Adapter\`, \`Observer\`, and
|
|
23536
|
-
\`View\` for UIs) or pattern (\`Repository\`, and \`FeatureComponent\` or
|
|
23537
|
-
\`RouterComponent\` for UIs), plus \`owns\` (a pattern's private members) and
|
|
23538
|
-
\`dependsOn\` (collaborators). Logic is an \`Orchestrator\` whose \`dependencyClass\`
|
|
23539
|
-
(\`pure\` or \`read\`; unset, a workflow) bounds what it may depend on; a gateway is
|
|
23540
|
-
a \`Portal\` with the \`gateway\` variant. \`Specialist\` and \`Gateway\` are retired.
|
|
23541
|
-
- **L3 Interface** \u2014 the contract: method signatures with structured \`params\`,
|
|
23542
|
-
plus wire \`endpoint\` bindings for a Portal's methods.
|
|
23543
|
-
- **L4 Implementation** \u2014 a concrete realization of one L3 contract: narrative
|
|
23544
|
-
detail level, bound \`technologies\` (the vendor swap seam), optional \`sourcePath\`.
|
|
23545
|
-
- **L5 Narrative** \u2014 each method's logic as a FLAT numbered step list; \`call\` steps
|
|
23546
|
-
resolve to real dependency methods, and flow steps jump by step number.
|
|
23547
|
-
|
|
23548
|
-
These stereotypes are not labels \u2014 they carry enforced dependency rules (e.g. a
|
|
23549
|
-
Portal may not depend on a Store; state lives in a Store, never as fields inside
|
|
23550
|
-
an Orchestrator). \`sdd_validate_tree\` is the gate, and it is the authority on
|
|
23551
|
-
whether a design is legal.
|
|
23552
|
-
|
|
23553
|
-
## Authoring order
|
|
23554
|
-
|
|
23555
|
-
1. \`sdd_initialize_system\` (L0), then \`sdd_add_subsystem\` (L1).
|
|
23556
|
-
2. Then, **one subsystem at a time**: \`sdd_add_component\` (L2) \u2192 \`sdd_define_interface\`
|
|
23557
|
-
(L3) \u2192 \`sdd_set_endpoints\` for Portals \u2192 \`sdd_write_narrative\` (L4+L5).
|
|
23558
|
-
3. Run \`sdd_validate_tree\` as you go \u2014 early and per subsystem, not once at the end.
|
|
23559
|
-
\`sdd_get_status\` reports completeness; \`sdd_get_spec\` reads any spec back as JSON.
|
|
23560
|
-
4. Edit granularly with \`sdd_update_spec\` (it renumbers narrative steps and
|
|
23561
|
-
relocates jump targets for you).
|
|
23562
|
-
|
|
23563
|
-
## The tool schemas are the field reference
|
|
23564
|
-
|
|
23565
|
-
Every \`sdd_*\` tool's input schema is self-describing and authoritative \u2014 read the
|
|
23566
|
-
schema rather than guessing field names, and prefer structured \`params\` over prose
|
|
23567
|
-
signatures. Specs are ${profile === null ? "validated against the project's configured profile" : `governed by the **\`${profile}\`** profile`}, which decides which stereotypes are legal
|
|
23568
|
-
here, which narrative flow constructs the target language supports, and which
|
|
23569
|
-
extra rules apply. A subsystem may override the profile for itself.
|
|
23570
|
-
|
|
23571
|
-
## Loaded extension packs
|
|
23572
|
-
|
|
23573
|
-
${packNames.length === 0 ? "None \u2014 only wairon's built-in doctrine applies." : `${packNames.map((n) => `\`${n}\``).join(", ")} \u2014 these inject profiles, language tables, patterns, and
|
|
23574
|
-
additional conformance rules. Their doctrine is enforced by the same gate.`}
|
|
23575
|
-
|
|
23576
|
-
${variants.length === 0 ? "" : `## Component variants in play
|
|
23577
|
-
|
|
23578
|
-
This project defines variants \u2014 named, base-anchored kinds carrying implementation
|
|
23579
|
-
guidance, so every component of the same kind is built alike: ${variants.map((v) => `\`${v.id}\` (a kind of ${v.base})`).join(", ")}.
|
|
23580
|
-
Tag a component with \`variant\` where it fits, and read a component's resolved
|
|
23581
|
-
guidance and its same-variant siblings from \`sdd_get_spec\` (kind: component).
|
|
23582
|
-
|
|
23583
|
-
`}## Read the skills BEFORE authoring
|
|
23584
|
-
|
|
23585
|
-
This server publishes its skills as MCP resources. **Read \`wairon-skill://sdd-architect\`
|
|
23586
|
-
before you design anything**, and the others when their turn comes \u2014 they carry the
|
|
23587
|
-
architecture standard, the narrative step reference, and the review checklist that
|
|
23588
|
-
this briefing only points at:
|
|
23589
|
-
|
|
23590
|
-
${skillPointers(skills)}
|
|
23591
|
-
|
|
23592
|
-
## Delegating implementation
|
|
23593
|
-
|
|
23594
|
-
Delegate component work from a LIVE agent brief, never a generated file: discover
|
|
23595
|
-
agents via \`listAgents\` (or the \`wairon-agent://\` resources), fetch the target's
|
|
23596
|
-
brief with \`sdd_get_agent_brief\`, and spawn a scoped subagent from it \u2014 its
|
|
23597
|
-
\`instructions\` as prompt, its \`ownedPaths\` as write fence. Briefs are composed
|
|
23598
|
-
from the current spec tree on every call, so a re-lock never requires a session
|
|
23599
|
-
restart \u2014 fetch fresh per delegation. \`wairon-skill://sdd-delegate\` carries the
|
|
23600
|
-
full flow.`;
|
|
23518
|
+
function yamlScalar(value) {
|
|
23519
|
+
return /[:#\n]/.test(value) ? `"${value.replace(/"/g, '\\"')}"` : value;
|
|
23601
23520
|
}
|
|
23602
|
-
|
|
23603
|
-
|
|
23604
|
-
|
|
23605
|
-
|
|
23606
|
-
|
|
23607
|
-
|
|
23608
|
-
|
|
23609
|
-
|
|
23610
|
-
|
|
23611
|
-
|
|
23612
|
-
|
|
23613
|
-
|
|
23521
|
+
var path23, MODEL_BY_TIER, TOOLS_BY_CLASS, ClaudeExporter;
|
|
23522
|
+
var init_claude = __esm({
|
|
23523
|
+
"src/exporters/claude.ts"() {
|
|
23524
|
+
"use strict";
|
|
23525
|
+
path23 = __toESM(require("path"));
|
|
23526
|
+
init_fs();
|
|
23527
|
+
MODEL_BY_TIER = {
|
|
23528
|
+
small: "haiku",
|
|
23529
|
+
standard: "sonnet",
|
|
23530
|
+
large: "opus",
|
|
23531
|
+
frontier: "fable"
|
|
23532
|
+
};
|
|
23533
|
+
TOOLS_BY_CLASS = {
|
|
23534
|
+
"read-only": ["Read", "Grep", "Glob"],
|
|
23535
|
+
implement: ["Read", "Grep", "Glob", "Edit", "Write", "Bash"],
|
|
23536
|
+
orchestrate: ["Agent", "SendMessage", "TodoWrite"],
|
|
23537
|
+
// `full` means "inherit everything" — expressed by omitting the field.
|
|
23538
|
+
full: void 0
|
|
23539
|
+
};
|
|
23540
|
+
ClaudeExporter = class {
|
|
23541
|
+
constructor(options = {}) {
|
|
23542
|
+
this.options = options;
|
|
23543
|
+
this.targetType = "claude";
|
|
23544
|
+
}
|
|
23545
|
+
outputPath(ctx) {
|
|
23546
|
+
const { agent, target, projectRoot: projectRoot2 } = ctx;
|
|
23547
|
+
const outputDir = "outputDir" in target ? target.outputDir : ".claude/agents";
|
|
23548
|
+
return path23.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
|
|
23549
|
+
}
|
|
23550
|
+
export(ctx) {
|
|
23551
|
+
const { agent, renderedInstructions, budget } = ctx;
|
|
23552
|
+
const filePath = this.outputPath(ctx);
|
|
23553
|
+
const frontmatter = [
|
|
23554
|
+
`name: ${agent.name}`,
|
|
23555
|
+
`description: ${yamlScalar(agent.description)}`
|
|
23556
|
+
];
|
|
23557
|
+
if (budget && this.options.emitBudget) {
|
|
23558
|
+
if (budget.modelTier) frontmatter.push(`model: ${MODEL_BY_TIER[budget.modelTier]}`);
|
|
23559
|
+
if (budget.effort) frontmatter.push(`effort: ${budget.effort}`);
|
|
23560
|
+
if (budget.maxTurns !== void 0) frontmatter.push(`maxTurns: ${budget.maxTurns}`);
|
|
23561
|
+
const tools = toolsFor(budget);
|
|
23562
|
+
if (tools) frontmatter.push(`tools: ${tools.join(", ")}`);
|
|
23563
|
+
const servers = mcpServersFor(budget.mcp);
|
|
23564
|
+
if (servers) frontmatter.push(`mcpServers: ${servers}`);
|
|
23565
|
+
}
|
|
23566
|
+
const content = ["---", ...frontmatter, "---", "", renderedInstructions, ""].join("\n");
|
|
23567
|
+
const changed = writeFileIfChanged(filePath, content);
|
|
23568
|
+
return { outputPath: filePath, content, unchanged: !changed };
|
|
23569
|
+
}
|
|
23570
|
+
};
|
|
23614
23571
|
}
|
|
23615
|
-
|
|
23616
|
-
for (const block of blocks) {
|
|
23617
|
-
if (!blockApplies(block, profile)) continue;
|
|
23618
|
-
sections.push(`## From pack "${block.pack}"
|
|
23572
|
+
});
|
|
23619
23573
|
|
|
23620
|
-
|
|
23621
|
-
|
|
23622
|
-
|
|
23623
|
-
|
|
23624
|
-
var init_instructions = __esm({
|
|
23625
|
-
"src/core/instructions.ts"() {
|
|
23574
|
+
// src/exporters/custom.ts
|
|
23575
|
+
var path24, CustomExporter;
|
|
23576
|
+
var init_custom = __esm({
|
|
23577
|
+
"src/exporters/custom.ts"() {
|
|
23626
23578
|
"use strict";
|
|
23627
|
-
|
|
23579
|
+
path24 = __toESM(require("path"));
|
|
23580
|
+
init_fs();
|
|
23581
|
+
CustomExporter = class {
|
|
23582
|
+
constructor() {
|
|
23583
|
+
this.targetType = "custom";
|
|
23584
|
+
}
|
|
23585
|
+
outputPath(ctx) {
|
|
23586
|
+
const { agent, target, projectRoot: projectRoot2 } = ctx;
|
|
23587
|
+
if (!("outputDir" in target)) {
|
|
23588
|
+
throw new Error("CustomExporter requires target.outputDir");
|
|
23589
|
+
}
|
|
23590
|
+
return path24.resolve(projectRoot2, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
|
|
23591
|
+
}
|
|
23592
|
+
export(ctx) {
|
|
23593
|
+
const { agent, target, renderedInstructions } = ctx;
|
|
23594
|
+
const label = "label" in target ? target.label : "Custom";
|
|
23595
|
+
const filePath = this.outputPath(ctx);
|
|
23596
|
+
const content = [
|
|
23597
|
+
"---",
|
|
23598
|
+
`name: ${agent.name}`,
|
|
23599
|
+
`description: ${agent.description}`,
|
|
23600
|
+
`id: ${agent.id}`,
|
|
23601
|
+
`target: ${label}`,
|
|
23602
|
+
"---",
|
|
23603
|
+
"",
|
|
23604
|
+
renderedInstructions,
|
|
23605
|
+
""
|
|
23606
|
+
].join("\n");
|
|
23607
|
+
const changed = writeFileIfChanged(filePath, content);
|
|
23608
|
+
return { outputPath: filePath, content, unchanged: !changed };
|
|
23609
|
+
}
|
|
23610
|
+
};
|
|
23628
23611
|
}
|
|
23629
23612
|
});
|
|
23630
23613
|
|
|
23631
|
-
// src/
|
|
23632
|
-
function
|
|
23633
|
-
|
|
23634
|
-
}
|
|
23635
|
-
function loadProjectConfig3() {
|
|
23636
|
-
return loadProjectConfig();
|
|
23637
|
-
}
|
|
23638
|
-
function packSkillId(skill) {
|
|
23639
|
-
return `${packNamespace(skill.pack)}-${skill.id}`;
|
|
23640
|
-
}
|
|
23641
|
-
function packNamespace(pack) {
|
|
23642
|
-
return pack.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
23643
|
-
}
|
|
23644
|
-
function extensionsFor(builtin, packSkills) {
|
|
23645
|
-
return packSkills.filter((s) => s.extends === builtin && fs18.existsSync(s.sourcePath));
|
|
23646
|
-
}
|
|
23647
|
-
function composeBuiltinSkill(name, packSkills) {
|
|
23648
|
-
const srcPath = skillTemplatePath(name);
|
|
23649
|
-
const base = fs18.existsSync(srcPath) ? fs18.readFileSync(srcPath, "utf-8") : "";
|
|
23650
|
-
const sections = extensionsFor(name, packSkills);
|
|
23651
|
-
if (sections.length === 0) return base;
|
|
23652
|
-
const parts = [base.trimEnd()];
|
|
23653
|
-
for (const section of sections) {
|
|
23654
|
-
const body = fs18.readFileSync(section.sourcePath, "utf-8").replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
|
|
23655
|
-
parts.push(`## Platform: ${section.pack}`, body.trim());
|
|
23656
|
-
}
|
|
23657
|
-
return `${parts.join("\n\n")}
|
|
23658
|
-
`;
|
|
23659
|
-
}
|
|
23660
|
-
function readFrontmatter(raw, fallbackName) {
|
|
23661
|
-
const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
|
|
23662
|
-
const block = match ? match[1] : "";
|
|
23663
|
-
const field = (key) => {
|
|
23664
|
-
const m = new RegExp(`^${key}:\\s*(.*)$`, "m").exec(block);
|
|
23665
|
-
return m ? m[1].trim() : "";
|
|
23666
|
-
};
|
|
23667
|
-
return { name: field("name") || fallbackName, description: field("description") };
|
|
23668
|
-
}
|
|
23669
|
-
function builtinSkillsDir() {
|
|
23670
|
-
return path23.resolve(__dirname, "..", "templates", "skills");
|
|
23671
|
-
}
|
|
23672
|
-
function skillTemplatePath(name) {
|
|
23673
|
-
return path23.join(builtinSkillsDir(), `${name}.md`);
|
|
23674
|
-
}
|
|
23675
|
-
function skillDestPath(type, destDir, name) {
|
|
23676
|
-
if (type === "claude" || type === "codex" || type === "gemini" || type === "agy") {
|
|
23677
|
-
return path23.join(destDir, name, "SKILL.md");
|
|
23678
|
-
}
|
|
23679
|
-
return path23.join(destDir, `${name}.md`);
|
|
23680
|
-
}
|
|
23681
|
-
function skillsDirForTarget(type) {
|
|
23682
|
-
switch (type) {
|
|
23683
|
-
case "claude":
|
|
23684
|
-
return fromProjectRoot(".claude", "skills");
|
|
23685
|
-
case "gemini":
|
|
23686
|
-
// Gemini-based Antigravity
|
|
23687
|
-
case "agy":
|
|
23688
|
-
case "codex":
|
|
23689
|
-
return fromProjectRoot(".agents", "skills");
|
|
23690
|
-
case "cursor":
|
|
23691
|
-
return fromProjectRoot(".cursor", "skills");
|
|
23692
|
-
default:
|
|
23693
|
-
return null;
|
|
23694
|
-
}
|
|
23695
|
-
}
|
|
23696
|
-
function listSkillNames() {
|
|
23697
|
-
return [...SKILL_NAMES];
|
|
23698
|
-
}
|
|
23699
|
-
function exportSddSkills(targetTypes) {
|
|
23700
|
-
const types = targetTypes ?? activeTargetTypes2();
|
|
23701
|
-
const destinations = [];
|
|
23702
|
-
const skipped = [];
|
|
23703
|
-
let fileCount = 0;
|
|
23704
|
-
const packSkills = loadProjectExtensions2().skills;
|
|
23705
|
-
for (const type of types) {
|
|
23706
|
-
const destDir = skillsDirForTarget(type);
|
|
23707
|
-
if (!destDir) {
|
|
23708
|
-
skipped.push(type);
|
|
23709
|
-
continue;
|
|
23710
|
-
}
|
|
23711
|
-
ensureDir(destDir);
|
|
23712
|
-
destinations.push(destDir);
|
|
23713
|
-
for (const name of SKILL_NAMES) {
|
|
23714
|
-
if (!fs18.existsSync(skillTemplatePath(name))) continue;
|
|
23715
|
-
const content = composeBuiltinSkill(name, packSkills.filter((s) => s.targets.includes(type)));
|
|
23716
|
-
const destPath = skillDestPath(type, destDir, name);
|
|
23717
|
-
ensureDir(path23.dirname(destPath));
|
|
23718
|
-
fs18.writeFileSync(destPath, content, "utf-8");
|
|
23719
|
-
fileCount++;
|
|
23720
|
-
}
|
|
23721
|
-
for (const skill of packSkills) {
|
|
23722
|
-
if (skill.extends !== void 0) continue;
|
|
23723
|
-
if (!skill.targets.includes(type)) continue;
|
|
23724
|
-
if (!fs18.existsSync(skill.sourcePath)) continue;
|
|
23725
|
-
const id = packSkillId(skill);
|
|
23726
|
-
const content = fs18.readFileSync(skill.sourcePath, "utf-8");
|
|
23727
|
-
const destPath = skillDestPath(type, destDir, id);
|
|
23728
|
-
ensureDir(path23.dirname(destPath));
|
|
23729
|
-
fs18.writeFileSync(destPath, content, "utf-8");
|
|
23730
|
-
fileCount++;
|
|
23731
|
-
}
|
|
23732
|
-
}
|
|
23733
|
-
return { destinations, fileCount, skipped };
|
|
23734
|
-
}
|
|
23735
|
-
function checkSkillFreshness(type) {
|
|
23736
|
-
const dir = skillsDirForTarget(type);
|
|
23737
|
-
const result = { dir, missing: [], stale: [], ok: [] };
|
|
23738
|
-
if (!dir) return result;
|
|
23739
|
-
const packSkills = loadProjectExtensions2().skills.filter((s) => s.targets.includes(type));
|
|
23740
|
-
for (const name of SKILL_NAMES) {
|
|
23741
|
-
const destPath = skillDestPath(type, dir, name);
|
|
23742
|
-
if (!fs18.existsSync(destPath)) {
|
|
23743
|
-
result.missing.push(name);
|
|
23744
|
-
continue;
|
|
23745
|
-
}
|
|
23746
|
-
const want = composeBuiltinSkill(name, packSkills);
|
|
23747
|
-
const have = fs18.readFileSync(destPath, "utf-8");
|
|
23748
|
-
if (have === want) result.ok.push(name);
|
|
23749
|
-
else result.stale.push(name);
|
|
23614
|
+
// src/exporters/gemini.ts
|
|
23615
|
+
function yamlString(value) {
|
|
23616
|
+
if (/[:#\[\]{},&*!|>'"%@`]/.test(value) || value.startsWith(" ") || value.endsWith(" ")) {
|
|
23617
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
23750
23618
|
}
|
|
23751
|
-
return
|
|
23752
|
-
}
|
|
23753
|
-
function activeTargetTypes2() {
|
|
23754
|
-
const config = loadProjectConfig3();
|
|
23755
|
-
if (!config) return [];
|
|
23756
|
-
return activeTargetTypes(config);
|
|
23757
|
-
}
|
|
23758
|
-
function readSkillFrontmatter(name) {
|
|
23759
|
-
return readFrontmatter(fs18.readFileSync(skillTemplatePath(name), "utf-8"), name);
|
|
23760
|
-
}
|
|
23761
|
-
function listSkillResources() {
|
|
23762
|
-
const builtin = RESOURCE_SKILL_IDS.map((id) => {
|
|
23763
|
-
const fm = readSkillFrontmatter(id);
|
|
23764
|
-
return {
|
|
23765
|
-
id,
|
|
23766
|
-
name: fm.name,
|
|
23767
|
-
description: fm.description,
|
|
23768
|
-
version: WAIRON_VERSION,
|
|
23769
|
-
resourceUri: `${SKILL_RESOURCE_SCHEME}://${id}`,
|
|
23770
|
-
defaultForHostedMcp: true
|
|
23771
|
-
};
|
|
23772
|
-
});
|
|
23773
|
-
const pack = loadProjectExtensions2().skills.filter((s) => s.extends === void 0 && fs18.existsSync(s.sourcePath)).map((skill) => {
|
|
23774
|
-
const id = packSkillId(skill);
|
|
23775
|
-
const fm = readFrontmatter(fs18.readFileSync(skill.sourcePath, "utf-8"), id);
|
|
23776
|
-
return {
|
|
23777
|
-
id,
|
|
23778
|
-
name: fm.name,
|
|
23779
|
-
description: fm.description,
|
|
23780
|
-
version: skill.packVersion ?? WAIRON_VERSION,
|
|
23781
|
-
resourceUri: `${SKILL_RESOURCE_SCHEME}://${id}`,
|
|
23782
|
-
defaultForHostedMcp: true
|
|
23783
|
-
};
|
|
23784
|
-
});
|
|
23785
|
-
return [...builtin, ...pack];
|
|
23786
|
-
}
|
|
23787
|
-
function readSkillResource(resourceId) {
|
|
23788
|
-
const packSkills = loadProjectExtensions2().skills;
|
|
23789
|
-
if (SKILL_NAMES.includes(resourceId)) return composeBuiltinSkill(resourceId, packSkills);
|
|
23790
|
-
const packSkill = packSkills.find((s) => s.extends === void 0 && packSkillId(s) === resourceId);
|
|
23791
|
-
if (packSkill) return fs18.readFileSync(packSkill.sourcePath, "utf-8");
|
|
23792
|
-
return fs18.readFileSync(skillTemplatePath(resourceId), "utf-8");
|
|
23793
|
-
}
|
|
23794
|
-
function listResources() {
|
|
23795
|
-
return listSkillResources();
|
|
23796
|
-
}
|
|
23797
|
-
function buildServerInstructions2() {
|
|
23798
|
-
return buildServerInstructions();
|
|
23799
|
-
}
|
|
23800
|
-
function readResource(resourceId) {
|
|
23801
|
-
const known = listSkillResources().some((d) => d.id === resourceId);
|
|
23802
|
-
if (!known) throw new SkillResourceNotFoundError(resourceId);
|
|
23803
|
-
return readSkillResource(resourceId);
|
|
23619
|
+
return value;
|
|
23804
23620
|
}
|
|
23805
|
-
var
|
|
23806
|
-
var
|
|
23807
|
-
"src/
|
|
23621
|
+
var path25, GeminiExporter;
|
|
23622
|
+
var init_gemini = __esm({
|
|
23623
|
+
"src/exporters/gemini.ts"() {
|
|
23808
23624
|
"use strict";
|
|
23809
|
-
|
|
23810
|
-
fs18 = __toESM(require("fs"));
|
|
23625
|
+
path25 = __toESM(require("path"));
|
|
23811
23626
|
init_fs();
|
|
23812
|
-
|
|
23813
|
-
|
|
23814
|
-
|
|
23815
|
-
|
|
23816
|
-
|
|
23817
|
-
|
|
23818
|
-
|
|
23819
|
-
|
|
23820
|
-
|
|
23821
|
-
|
|
23822
|
-
|
|
23823
|
-
|
|
23627
|
+
GeminiExporter = class {
|
|
23628
|
+
constructor() {
|
|
23629
|
+
this.targetType = "gemini";
|
|
23630
|
+
}
|
|
23631
|
+
outputPath(ctx) {
|
|
23632
|
+
const { agent, target, projectRoot: projectRoot2 } = ctx;
|
|
23633
|
+
const outputDir = "outputDir" in target ? target.outputDir : ".gemini/agents";
|
|
23634
|
+
return path25.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
|
|
23635
|
+
}
|
|
23636
|
+
export(ctx) {
|
|
23637
|
+
const { agent, renderedInstructions } = ctx;
|
|
23638
|
+
const filePath = this.outputPath(ctx);
|
|
23639
|
+
const indentedInstructions = renderedInstructions.split("\n").map((line2) => line2.length > 0 ? ` ${line2}` : "").join("\n");
|
|
23640
|
+
const content = [
|
|
23641
|
+
`name: ${yamlString(agent.name)}`,
|
|
23642
|
+
`description: ${yamlString(agent.description)}`,
|
|
23643
|
+
`system_prompt: |`,
|
|
23644
|
+
indentedInstructions,
|
|
23645
|
+
""
|
|
23646
|
+
].join("\n");
|
|
23647
|
+
const changed = writeFileIfChanged(filePath, content);
|
|
23648
|
+
return { outputPath: filePath, content, unchanged: !changed };
|
|
23824
23649
|
}
|
|
23825
23650
|
};
|
|
23826
23651
|
}
|
|
23827
23652
|
});
|
|
23828
23653
|
|
|
23829
|
-
// src/
|
|
23830
|
-
function
|
|
23831
|
-
|
|
23832
|
-
|
|
23833
|
-
|
|
23834
|
-
|
|
23835
|
-
|
|
23654
|
+
// src/exporters/registry.ts
|
|
23655
|
+
function getExporter(target) {
|
|
23656
|
+
const type = typeof target === "string" ? target : target.type;
|
|
23657
|
+
const exporter = EXPORTERS.get(type);
|
|
23658
|
+
if (!exporter) {
|
|
23659
|
+
throw new WaironError(`No exporter registered for target type: "${type}"`);
|
|
23660
|
+
}
|
|
23661
|
+
return exporter;
|
|
23836
23662
|
}
|
|
23837
|
-
var
|
|
23838
|
-
var
|
|
23839
|
-
"src/
|
|
23663
|
+
var EXPORTERS;
|
|
23664
|
+
var init_registry2 = __esm({
|
|
23665
|
+
"src/exporters/registry.ts"() {
|
|
23840
23666
|
"use strict";
|
|
23841
|
-
|
|
23842
|
-
|
|
23667
|
+
init_claude();
|
|
23668
|
+
init_custom();
|
|
23669
|
+
init_gemini();
|
|
23670
|
+
init_errors();
|
|
23671
|
+
EXPORTERS = /* @__PURE__ */ new Map([
|
|
23672
|
+
["claude", new ClaudeExporter({ emitBudget: true })],
|
|
23673
|
+
["gemini", new GeminiExporter()],
|
|
23674
|
+
["agy", new GeminiExporter()],
|
|
23675
|
+
["cursor", new ClaudeExporter()],
|
|
23676
|
+
["copilot", new ClaudeExporter()],
|
|
23677
|
+
["codex", new ClaudeExporter()],
|
|
23678
|
+
["custom", new CustomExporter()]
|
|
23679
|
+
]);
|
|
23843
23680
|
}
|
|
23844
23681
|
});
|
|
23845
23682
|
|
|
23846
|
-
// src/
|
|
23847
|
-
|
|
23848
|
-
|
|
23849
|
-
|
|
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"() {
|
|
23683
|
+
// src/exporters/base.ts
|
|
23684
|
+
var WAIRON_MANAGED_MARKER, WAIRON_MANAGED_BANNER;
|
|
23685
|
+
var init_base = __esm({
|
|
23686
|
+
"src/exporters/base.ts"() {
|
|
23947
23687
|
"use strict";
|
|
23948
|
-
|
|
23949
|
-
|
|
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"];
|
|
23688
|
+
WAIRON_MANAGED_MARKER = "wairon:managed";
|
|
23689
|
+
WAIRON_MANAGED_BANNER = `<!-- ${WAIRON_MANAGED_MARKER} \u2014 generated by \`wairon generate\`; do not edit, changes are overwritten -->`;
|
|
23999
23690
|
}
|
|
24000
23691
|
});
|
|
24001
23692
|
|
|
24002
|
-
// src/
|
|
24003
|
-
function
|
|
24004
|
-
|
|
24005
|
-
|
|
24006
|
-
|
|
24007
|
-
|
|
24008
|
-
|
|
24009
|
-
|
|
24010
|
-
|
|
24011
|
-
|
|
24012
|
-
|
|
24013
|
-
|
|
24014
|
-
|
|
24015
|
-
|
|
24016
|
-
|
|
24017
|
-
|
|
24018
|
-
|
|
24019
|
-
|
|
24020
|
-
|
|
24021
|
-
|
|
24022
|
-
|
|
24023
|
-
|
|
24024
|
-
|
|
24025
|
-
|
|
24026
|
-
|
|
24027
|
-
|
|
24028
|
-
|
|
24029
|
-
|
|
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"}\` |`);
|
|
23693
|
+
// src/exporters/generate.ts
|
|
23694
|
+
function generateAgent(agent, projectConfig, options = {}) {
|
|
23695
|
+
const projectRoot2 = options.projectRoot ?? getProjectRoot();
|
|
23696
|
+
const template = loadTemplate(agent.template, projectConfig.globalTemplatesDir);
|
|
23697
|
+
const rendered = `${WAIRON_MANAGED_BANNER}
|
|
23698
|
+
${composeAgentBrief(agent.id).instructions}`;
|
|
23699
|
+
const results = [];
|
|
23700
|
+
const budget = resolveBudget(
|
|
23701
|
+
deriveExecutionProfile(agent),
|
|
23702
|
+
projectConfig.execution,
|
|
23703
|
+
agent.id
|
|
23704
|
+
);
|
|
23705
|
+
for (const agentTarget of agent.targets) {
|
|
23706
|
+
const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
|
|
23707
|
+
if (!targetConfig) continue;
|
|
23708
|
+
if (options.filterTargets) {
|
|
23709
|
+
const type = "type" in targetConfig ? targetConfig.type : targetConfig;
|
|
23710
|
+
if (!options.filterTargets.includes(type)) continue;
|
|
23711
|
+
}
|
|
23712
|
+
if (!options.dryRun) {
|
|
23713
|
+
results.push(getExporter(targetConfig).export({
|
|
23714
|
+
agent,
|
|
23715
|
+
template,
|
|
23716
|
+
renderedInstructions: rendered,
|
|
23717
|
+
projectRoot: projectRoot2,
|
|
23718
|
+
target: targetConfig,
|
|
23719
|
+
budget
|
|
23720
|
+
}));
|
|
24035
23721
|
}
|
|
24036
23722
|
}
|
|
24037
|
-
return
|
|
23723
|
+
return { agent, results };
|
|
24038
23724
|
}
|
|
24039
|
-
function
|
|
24040
|
-
|
|
24041
|
-
|
|
24042
|
-
|
|
24043
|
-
|
|
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("");
|
|
23725
|
+
function generateAll(agents, projectConfig, options = {}) {
|
|
23726
|
+
let pool = agents;
|
|
23727
|
+
if (options.filterDomainIds && options.filterDomainIds.length > 0) {
|
|
23728
|
+
const ids = new Set(options.filterDomainIds);
|
|
23729
|
+
pool = agents.filter((a) => ids.has(a.domainRoot ?? "root"));
|
|
24068
23730
|
}
|
|
24069
|
-
|
|
24070
|
-
|
|
24071
|
-
|
|
24072
|
-
|
|
24073
|
-
|
|
24074
|
-
|
|
24075
|
-
|
|
24076
|
-
|
|
24077
|
-
const
|
|
24078
|
-
|
|
24079
|
-
lines.push(`| \`${d.id}\` | ${source} | ${name} |`);
|
|
23731
|
+
return pool.map((agent) => generateAgent(agent, projectConfig, options));
|
|
23732
|
+
}
|
|
23733
|
+
function resolveExpectedOutputPaths(agents, projectConfig, projectRoot2 = getProjectRoot()) {
|
|
23734
|
+
const expected = /* @__PURE__ */ new Set();
|
|
23735
|
+
for (const agent of agents) {
|
|
23736
|
+
for (const agentTarget of agent.targets) {
|
|
23737
|
+
const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
|
|
23738
|
+
if (!targetConfig) continue;
|
|
23739
|
+
const ctx = { agent, projectRoot: projectRoot2, target: targetConfig };
|
|
23740
|
+
expected.add(path26.resolve(getExporter(targetConfig).outputPath(ctx)));
|
|
24080
23741
|
}
|
|
24081
23742
|
}
|
|
24082
|
-
|
|
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");
|
|
23743
|
+
return expected;
|
|
24109
23744
|
}
|
|
24110
|
-
function
|
|
24111
|
-
const
|
|
24112
|
-
|
|
24113
|
-
|
|
24114
|
-
|
|
24115
|
-
|
|
23745
|
+
function resolveTargetConfig(agentTarget, projectConfig) {
|
|
23746
|
+
const targetType = typeof agentTarget === "string" ? agentTarget : agentTarget.type;
|
|
23747
|
+
return projectConfig.targets.find((t) => {
|
|
23748
|
+
if (typeof t === "string") return t === targetType;
|
|
23749
|
+
return t.type === targetType;
|
|
23750
|
+
});
|
|
24116
23751
|
}
|
|
24117
|
-
var
|
|
24118
|
-
var
|
|
24119
|
-
"src/
|
|
23752
|
+
var path26;
|
|
23753
|
+
var init_generate = __esm({
|
|
23754
|
+
"src/exporters/generate.ts"() {
|
|
24120
23755
|
"use strict";
|
|
23756
|
+
path26 = __toESM(require("path"));
|
|
23757
|
+
init_templates();
|
|
23758
|
+
init_agent_resolver();
|
|
23759
|
+
init_execution_profile();
|
|
23760
|
+
init_budget_policy();
|
|
24121
23761
|
init_fs();
|
|
24122
|
-
|
|
24123
|
-
|
|
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
|
-
};
|
|
23762
|
+
init_registry2();
|
|
23763
|
+
init_base();
|
|
24133
23764
|
}
|
|
24134
23765
|
});
|
|
24135
23766
|
|
|
@@ -27508,41 +27139,41 @@ function hasAuthoredSpecs(root) {
|
|
|
27508
27139
|
if (!pathExists(specsDir)) {
|
|
27509
27140
|
return false;
|
|
27510
27141
|
}
|
|
27511
|
-
const systemIndex =
|
|
27142
|
+
const systemIndex = path27.resolve(paths.specsSystem());
|
|
27512
27143
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
27513
|
-
if (
|
|
27144
|
+
if (path27.resolve(file) !== systemIndex) {
|
|
27514
27145
|
return true;
|
|
27515
27146
|
}
|
|
27516
27147
|
}
|
|
27517
27148
|
return false;
|
|
27518
27149
|
}
|
|
27519
27150
|
function createStagingDir(destDir) {
|
|
27520
|
-
const root =
|
|
27521
|
-
|
|
27522
|
-
const stagingDir =
|
|
27151
|
+
const root = path27.resolve(destDir);
|
|
27152
|
+
fs18.mkdirSync(root, { recursive: true });
|
|
27153
|
+
const stagingDir = fs18.mkdtempSync(path27.join(root, STAGING_PREFIX));
|
|
27523
27154
|
return stagingDir;
|
|
27524
27155
|
}
|
|
27525
27156
|
function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
|
|
27526
|
-
const root =
|
|
27527
|
-
const backupDir =
|
|
27157
|
+
const root = path27.resolve(destDir);
|
|
27158
|
+
const backupDir = path27.join(root, BACKUP_DIR, timestampSlug());
|
|
27528
27159
|
let backedUp = false;
|
|
27529
27160
|
for (const rootRel of roots) {
|
|
27530
27161
|
const relative15 = rootRel === "." ? "" : rootRel;
|
|
27531
|
-
const stagedSpecDir =
|
|
27532
|
-
if (!
|
|
27533
|
-
const liveSpecDir = liveSpecDirFor(
|
|
27534
|
-
if (backupExisting && liveSpecDir &&
|
|
27535
|
-
const backupTarget =
|
|
27536
|
-
|
|
27537
|
-
|
|
27162
|
+
const stagedSpecDir = path27.join(stagingDir, relative15, ".wai");
|
|
27163
|
+
if (!fs18.existsSync(stagedSpecDir)) continue;
|
|
27164
|
+
const liveSpecDir = liveSpecDirFor(path27.join(root, relative15));
|
|
27165
|
+
if (backupExisting && liveSpecDir && fs18.existsSync(liveSpecDir)) {
|
|
27166
|
+
const backupTarget = path27.join(backupDir, relative15, path27.basename(liveSpecDir));
|
|
27167
|
+
fs18.mkdirSync(path27.dirname(backupTarget), { recursive: true });
|
|
27168
|
+
fs18.renameSync(liveSpecDir, backupTarget);
|
|
27538
27169
|
backedUp = true;
|
|
27539
|
-
} else if (liveSpecDir &&
|
|
27540
|
-
|
|
27170
|
+
} else if (liveSpecDir && fs18.existsSync(liveSpecDir)) {
|
|
27171
|
+
fs18.rmSync(liveSpecDir, { recursive: true, force: true });
|
|
27541
27172
|
}
|
|
27542
|
-
const target =
|
|
27543
|
-
|
|
27544
|
-
if (
|
|
27545
|
-
|
|
27173
|
+
const target = path27.join(root, relative15, ".wai");
|
|
27174
|
+
fs18.mkdirSync(path27.dirname(target), { recursive: true });
|
|
27175
|
+
if (fs18.existsSync(target)) fs18.rmSync(target, { recursive: true, force: true });
|
|
27176
|
+
fs18.renameSync(stagedSpecDir, target);
|
|
27546
27177
|
}
|
|
27547
27178
|
if (backedUp) {
|
|
27548
27179
|
return backupDir;
|
|
@@ -27551,7 +27182,7 @@ function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
|
|
|
27551
27182
|
}
|
|
27552
27183
|
function discardStagingDir(stagingDir) {
|
|
27553
27184
|
try {
|
|
27554
|
-
|
|
27185
|
+
fs18.rmSync(stagingDir, { recursive: true, force: true });
|
|
27555
27186
|
} catch {
|
|
27556
27187
|
}
|
|
27557
27188
|
}
|
|
@@ -27565,7 +27196,7 @@ function exportSpecTree(includeDerived, allowPartial) {
|
|
|
27565
27196
|
const skipped = [...inspection.skipped];
|
|
27566
27197
|
const roots = [{ relativePath: ".", waiDir: aiPathsAt(root).root() }];
|
|
27567
27198
|
for (const rel2 of inspection.roots) {
|
|
27568
|
-
const waiDir = aiPathsAt(
|
|
27199
|
+
const waiDir = aiPathsAt(path27.resolve(root, rel2)).root();
|
|
27569
27200
|
if (pathExists(waiDir)) {
|
|
27570
27201
|
roots.push({ relativePath: rel2, waiDir });
|
|
27571
27202
|
} else {
|
|
@@ -27596,7 +27227,7 @@ function importSpecTree(archive, options) {
|
|
|
27596
27227
|
if (!info.compatible) {
|
|
27597
27228
|
throw new Error("incompatible .waitree archive (format or minimum wairon version)");
|
|
27598
27229
|
}
|
|
27599
|
-
const destDir =
|
|
27230
|
+
const destDir = path27.resolve(options.destDir ?? getProjectRoot());
|
|
27600
27231
|
const occupied = hasSpecTree(destDir);
|
|
27601
27232
|
const authored = hasAuthoredSpecs(destDir);
|
|
27602
27233
|
if (authored && options.replaceExisting !== true) {
|
|
@@ -27627,21 +27258,21 @@ function importSpecTree(archive, options) {
|
|
|
27627
27258
|
return result;
|
|
27628
27259
|
}
|
|
27629
27260
|
function liveSpecDirFor(rootDir) {
|
|
27630
|
-
const modern =
|
|
27631
|
-
if (
|
|
27632
|
-
const legacy =
|
|
27633
|
-
if (
|
|
27261
|
+
const modern = path27.join(rootDir, ".wai");
|
|
27262
|
+
if (fs18.existsSync(modern)) return modern;
|
|
27263
|
+
const legacy = path27.join(rootDir, ".wairon");
|
|
27264
|
+
if (fs18.existsSync(legacy)) return legacy;
|
|
27634
27265
|
return null;
|
|
27635
27266
|
}
|
|
27636
27267
|
function timestampSlug() {
|
|
27637
27268
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
27638
27269
|
}
|
|
27639
|
-
var
|
|
27270
|
+
var fs18, path27, sdkPortal, STAGING_PREFIX, BACKUP_DIR;
|
|
27640
27271
|
var init_treetransfer = __esm({
|
|
27641
27272
|
"src/core/treetransfer.ts"() {
|
|
27642
27273
|
"use strict";
|
|
27643
|
-
|
|
27644
|
-
|
|
27274
|
+
fs18 = __toESM(require("fs"));
|
|
27275
|
+
path27 = __toESM(require("path"));
|
|
27645
27276
|
sdkPortal = __toESM(require_dist());
|
|
27646
27277
|
init_paths();
|
|
27647
27278
|
init_fs();
|
|
@@ -27652,270 +27283,6 @@ var init_treetransfer = __esm({
|
|
|
27652
27283
|
}
|
|
27653
27284
|
});
|
|
27654
27285
|
|
|
27655
|
-
// src/exporters/claude.ts
|
|
27656
|
-
function toolsFor(budget) {
|
|
27657
|
-
const base = TOOLS_BY_CLASS[budget.toolClass];
|
|
27658
|
-
if (!base) return void 0;
|
|
27659
|
-
if (budget.allowNestedDelegation && !base.includes("Agent")) {
|
|
27660
|
-
return [...base, "Agent"];
|
|
27661
|
-
}
|
|
27662
|
-
if (!budget.allowNestedDelegation) {
|
|
27663
|
-
return base.filter((t) => t !== "Agent");
|
|
27664
|
-
}
|
|
27665
|
-
return base;
|
|
27666
|
-
}
|
|
27667
|
-
function mcpServersFor(access) {
|
|
27668
|
-
return access === "none" ? "[]" : void 0;
|
|
27669
|
-
}
|
|
27670
|
-
function yamlScalar(value) {
|
|
27671
|
-
return /[:#\n]/.test(value) ? `"${value.replace(/"/g, '\\"')}"` : value;
|
|
27672
|
-
}
|
|
27673
|
-
var path26, MODEL_BY_TIER, TOOLS_BY_CLASS, ClaudeExporter;
|
|
27674
|
-
var init_claude = __esm({
|
|
27675
|
-
"src/exporters/claude.ts"() {
|
|
27676
|
-
"use strict";
|
|
27677
|
-
path26 = __toESM(require("path"));
|
|
27678
|
-
init_fs();
|
|
27679
|
-
MODEL_BY_TIER = {
|
|
27680
|
-
small: "haiku",
|
|
27681
|
-
standard: "sonnet",
|
|
27682
|
-
large: "opus",
|
|
27683
|
-
frontier: "fable"
|
|
27684
|
-
};
|
|
27685
|
-
TOOLS_BY_CLASS = {
|
|
27686
|
-
"read-only": ["Read", "Grep", "Glob"],
|
|
27687
|
-
implement: ["Read", "Grep", "Glob", "Edit", "Write", "Bash"],
|
|
27688
|
-
orchestrate: ["Agent", "SendMessage", "TodoWrite"],
|
|
27689
|
-
// `full` means "inherit everything" — expressed by omitting the field.
|
|
27690
|
-
full: void 0
|
|
27691
|
-
};
|
|
27692
|
-
ClaudeExporter = class {
|
|
27693
|
-
constructor(options = {}) {
|
|
27694
|
-
this.options = options;
|
|
27695
|
-
this.targetType = "claude";
|
|
27696
|
-
}
|
|
27697
|
-
outputPath(ctx) {
|
|
27698
|
-
const { agent, target, projectRoot: projectRoot2 } = ctx;
|
|
27699
|
-
const outputDir = "outputDir" in target ? target.outputDir : ".claude/agents";
|
|
27700
|
-
return path26.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
|
|
27701
|
-
}
|
|
27702
|
-
export(ctx) {
|
|
27703
|
-
const { agent, renderedInstructions, budget } = ctx;
|
|
27704
|
-
const filePath = this.outputPath(ctx);
|
|
27705
|
-
const frontmatter = [
|
|
27706
|
-
`name: ${agent.name}`,
|
|
27707
|
-
`description: ${yamlScalar(agent.description)}`
|
|
27708
|
-
];
|
|
27709
|
-
if (budget && this.options.emitBudget) {
|
|
27710
|
-
if (budget.modelTier) frontmatter.push(`model: ${MODEL_BY_TIER[budget.modelTier]}`);
|
|
27711
|
-
if (budget.effort) frontmatter.push(`effort: ${budget.effort}`);
|
|
27712
|
-
if (budget.maxTurns !== void 0) frontmatter.push(`maxTurns: ${budget.maxTurns}`);
|
|
27713
|
-
const tools = toolsFor(budget);
|
|
27714
|
-
if (tools) frontmatter.push(`tools: ${tools.join(", ")}`);
|
|
27715
|
-
const servers = mcpServersFor(budget.mcp);
|
|
27716
|
-
if (servers) frontmatter.push(`mcpServers: ${servers}`);
|
|
27717
|
-
}
|
|
27718
|
-
const content = ["---", ...frontmatter, "---", "", renderedInstructions, ""].join("\n");
|
|
27719
|
-
const changed = writeFileIfChanged(filePath, content);
|
|
27720
|
-
return { outputPath: filePath, content, unchanged: !changed };
|
|
27721
|
-
}
|
|
27722
|
-
};
|
|
27723
|
-
}
|
|
27724
|
-
});
|
|
27725
|
-
|
|
27726
|
-
// src/exporters/custom.ts
|
|
27727
|
-
var path27, CustomExporter;
|
|
27728
|
-
var init_custom = __esm({
|
|
27729
|
-
"src/exporters/custom.ts"() {
|
|
27730
|
-
"use strict";
|
|
27731
|
-
path27 = __toESM(require("path"));
|
|
27732
|
-
init_fs();
|
|
27733
|
-
CustomExporter = class {
|
|
27734
|
-
constructor() {
|
|
27735
|
-
this.targetType = "custom";
|
|
27736
|
-
}
|
|
27737
|
-
outputPath(ctx) {
|
|
27738
|
-
const { agent, target, projectRoot: projectRoot2 } = ctx;
|
|
27739
|
-
if (!("outputDir" in target)) {
|
|
27740
|
-
throw new Error("CustomExporter requires target.outputDir");
|
|
27741
|
-
}
|
|
27742
|
-
return path27.resolve(projectRoot2, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
|
|
27743
|
-
}
|
|
27744
|
-
export(ctx) {
|
|
27745
|
-
const { agent, target, renderedInstructions } = ctx;
|
|
27746
|
-
const label = "label" in target ? target.label : "Custom";
|
|
27747
|
-
const filePath = this.outputPath(ctx);
|
|
27748
|
-
const content = [
|
|
27749
|
-
"---",
|
|
27750
|
-
`name: ${agent.name}`,
|
|
27751
|
-
`description: ${agent.description}`,
|
|
27752
|
-
`id: ${agent.id}`,
|
|
27753
|
-
`target: ${label}`,
|
|
27754
|
-
"---",
|
|
27755
|
-
"",
|
|
27756
|
-
renderedInstructions,
|
|
27757
|
-
""
|
|
27758
|
-
].join("\n");
|
|
27759
|
-
const changed = writeFileIfChanged(filePath, content);
|
|
27760
|
-
return { outputPath: filePath, content, unchanged: !changed };
|
|
27761
|
-
}
|
|
27762
|
-
};
|
|
27763
|
-
}
|
|
27764
|
-
});
|
|
27765
|
-
|
|
27766
|
-
// src/exporters/gemini.ts
|
|
27767
|
-
function yamlString(value) {
|
|
27768
|
-
if (/[:#\[\]{},&*!|>'"%@`]/.test(value) || value.startsWith(" ") || value.endsWith(" ")) {
|
|
27769
|
-
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
27770
|
-
}
|
|
27771
|
-
return value;
|
|
27772
|
-
}
|
|
27773
|
-
var path28, GeminiExporter;
|
|
27774
|
-
var init_gemini = __esm({
|
|
27775
|
-
"src/exporters/gemini.ts"() {
|
|
27776
|
-
"use strict";
|
|
27777
|
-
path28 = __toESM(require("path"));
|
|
27778
|
-
init_fs();
|
|
27779
|
-
GeminiExporter = class {
|
|
27780
|
-
constructor() {
|
|
27781
|
-
this.targetType = "gemini";
|
|
27782
|
-
}
|
|
27783
|
-
outputPath(ctx) {
|
|
27784
|
-
const { agent, target, projectRoot: projectRoot2 } = ctx;
|
|
27785
|
-
const outputDir = "outputDir" in target ? target.outputDir : ".gemini/agents";
|
|
27786
|
-
return path28.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
|
|
27787
|
-
}
|
|
27788
|
-
export(ctx) {
|
|
27789
|
-
const { agent, renderedInstructions } = ctx;
|
|
27790
|
-
const filePath = this.outputPath(ctx);
|
|
27791
|
-
const indentedInstructions = renderedInstructions.split("\n").map((line2) => line2.length > 0 ? ` ${line2}` : "").join("\n");
|
|
27792
|
-
const content = [
|
|
27793
|
-
`name: ${yamlString(agent.name)}`,
|
|
27794
|
-
`description: ${yamlString(agent.description)}`,
|
|
27795
|
-
`system_prompt: |`,
|
|
27796
|
-
indentedInstructions,
|
|
27797
|
-
""
|
|
27798
|
-
].join("\n");
|
|
27799
|
-
const changed = writeFileIfChanged(filePath, content);
|
|
27800
|
-
return { outputPath: filePath, content, unchanged: !changed };
|
|
27801
|
-
}
|
|
27802
|
-
};
|
|
27803
|
-
}
|
|
27804
|
-
});
|
|
27805
|
-
|
|
27806
|
-
// src/exporters/registry.ts
|
|
27807
|
-
function getExporter(target) {
|
|
27808
|
-
const type = typeof target === "string" ? target : target.type;
|
|
27809
|
-
const exporter = EXPORTERS.get(type);
|
|
27810
|
-
if (!exporter) {
|
|
27811
|
-
throw new WaironError(`No exporter registered for target type: "${type}"`);
|
|
27812
|
-
}
|
|
27813
|
-
return exporter;
|
|
27814
|
-
}
|
|
27815
|
-
var EXPORTERS;
|
|
27816
|
-
var init_registry2 = __esm({
|
|
27817
|
-
"src/exporters/registry.ts"() {
|
|
27818
|
-
"use strict";
|
|
27819
|
-
init_claude();
|
|
27820
|
-
init_custom();
|
|
27821
|
-
init_gemini();
|
|
27822
|
-
init_errors();
|
|
27823
|
-
EXPORTERS = /* @__PURE__ */ new Map([
|
|
27824
|
-
["claude", new ClaudeExporter({ emitBudget: true })],
|
|
27825
|
-
["gemini", new GeminiExporter()],
|
|
27826
|
-
["agy", new GeminiExporter()],
|
|
27827
|
-
["cursor", new ClaudeExporter()],
|
|
27828
|
-
["copilot", new ClaudeExporter()],
|
|
27829
|
-
["codex", new ClaudeExporter()],
|
|
27830
|
-
["custom", new CustomExporter()]
|
|
27831
|
-
]);
|
|
27832
|
-
}
|
|
27833
|
-
});
|
|
27834
|
-
|
|
27835
|
-
// src/exporters/base.ts
|
|
27836
|
-
var WAIRON_MANAGED_MARKER, WAIRON_MANAGED_BANNER;
|
|
27837
|
-
var init_base = __esm({
|
|
27838
|
-
"src/exporters/base.ts"() {
|
|
27839
|
-
"use strict";
|
|
27840
|
-
WAIRON_MANAGED_MARKER = "wairon:managed";
|
|
27841
|
-
WAIRON_MANAGED_BANNER = `<!-- ${WAIRON_MANAGED_MARKER} \u2014 generated by \`wairon generate\`; do not edit, changes are overwritten -->`;
|
|
27842
|
-
}
|
|
27843
|
-
});
|
|
27844
|
-
|
|
27845
|
-
// src/exporters/generate.ts
|
|
27846
|
-
function generateAgent(agent, projectConfig, options = {}) {
|
|
27847
|
-
const projectRoot2 = options.projectRoot ?? getProjectRoot();
|
|
27848
|
-
const template = loadTemplate(agent.template, projectConfig.globalTemplatesDir);
|
|
27849
|
-
const rendered = `${WAIRON_MANAGED_BANNER}
|
|
27850
|
-
${composeAgentBrief(agent.id).instructions}`;
|
|
27851
|
-
const results = [];
|
|
27852
|
-
const budget = resolveBudget(
|
|
27853
|
-
deriveExecutionProfile(agent),
|
|
27854
|
-
projectConfig.execution,
|
|
27855
|
-
agent.id
|
|
27856
|
-
);
|
|
27857
|
-
for (const agentTarget of agent.targets) {
|
|
27858
|
-
const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
|
|
27859
|
-
if (!targetConfig) continue;
|
|
27860
|
-
if (options.filterTargets) {
|
|
27861
|
-
const type = "type" in targetConfig ? targetConfig.type : targetConfig;
|
|
27862
|
-
if (!options.filterTargets.includes(type)) continue;
|
|
27863
|
-
}
|
|
27864
|
-
if (!options.dryRun) {
|
|
27865
|
-
results.push(getExporter(targetConfig).export({
|
|
27866
|
-
agent,
|
|
27867
|
-
template,
|
|
27868
|
-
renderedInstructions: rendered,
|
|
27869
|
-
projectRoot: projectRoot2,
|
|
27870
|
-
target: targetConfig,
|
|
27871
|
-
budget
|
|
27872
|
-
}));
|
|
27873
|
-
}
|
|
27874
|
-
}
|
|
27875
|
-
return { agent, results };
|
|
27876
|
-
}
|
|
27877
|
-
function generateAll(agents, projectConfig, options = {}) {
|
|
27878
|
-
let pool = agents;
|
|
27879
|
-
if (options.filterDomainIds && options.filterDomainIds.length > 0) {
|
|
27880
|
-
const ids = new Set(options.filterDomainIds);
|
|
27881
|
-
pool = agents.filter((a) => ids.has(a.domainRoot ?? "root"));
|
|
27882
|
-
}
|
|
27883
|
-
return pool.map((agent) => generateAgent(agent, projectConfig, options));
|
|
27884
|
-
}
|
|
27885
|
-
function resolveExpectedOutputPaths(agents, projectConfig, projectRoot2 = getProjectRoot()) {
|
|
27886
|
-
const expected = /* @__PURE__ */ new Set();
|
|
27887
|
-
for (const agent of agents) {
|
|
27888
|
-
for (const agentTarget of agent.targets) {
|
|
27889
|
-
const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
|
|
27890
|
-
if (!targetConfig) continue;
|
|
27891
|
-
const ctx = { agent, projectRoot: projectRoot2, target: targetConfig };
|
|
27892
|
-
expected.add(path29.resolve(getExporter(targetConfig).outputPath(ctx)));
|
|
27893
|
-
}
|
|
27894
|
-
}
|
|
27895
|
-
return expected;
|
|
27896
|
-
}
|
|
27897
|
-
function resolveTargetConfig(agentTarget, projectConfig) {
|
|
27898
|
-
const targetType = typeof agentTarget === "string" ? agentTarget : agentTarget.type;
|
|
27899
|
-
return projectConfig.targets.find((t) => {
|
|
27900
|
-
if (typeof t === "string") return t === targetType;
|
|
27901
|
-
return t.type === targetType;
|
|
27902
|
-
});
|
|
27903
|
-
}
|
|
27904
|
-
var path29;
|
|
27905
|
-
var init_generate = __esm({
|
|
27906
|
-
"src/exporters/generate.ts"() {
|
|
27907
|
-
"use strict";
|
|
27908
|
-
path29 = __toESM(require("path"));
|
|
27909
|
-
init_templates();
|
|
27910
|
-
init_agent_resolver();
|
|
27911
|
-
init_execution_profile();
|
|
27912
|
-
init_budget_policy();
|
|
27913
|
-
init_fs();
|
|
27914
|
-
init_registry2();
|
|
27915
|
-
init_base();
|
|
27916
|
-
}
|
|
27917
|
-
});
|
|
27918
|
-
|
|
27919
27286
|
// src/core/stereotype-migration.ts
|
|
27920
27287
|
function retireSpecialists(apply) {
|
|
27921
27288
|
const components = loadComponentSpecs();
|
|
@@ -28063,6 +27430,17 @@ var init_narrative_repair = __esm({
|
|
|
28063
27430
|
}
|
|
28064
27431
|
});
|
|
28065
27432
|
|
|
27433
|
+
// src/models/lock.ts
|
|
27434
|
+
function describeApprover(who) {
|
|
27435
|
+
const label = who.name ? `${who.name} (${who.id})` : who.id;
|
|
27436
|
+
return who.source === "hosted" ? `${label} [authenticated]` : label;
|
|
27437
|
+
}
|
|
27438
|
+
var init_lock = __esm({
|
|
27439
|
+
"src/models/lock.ts"() {
|
|
27440
|
+
"use strict";
|
|
27441
|
+
}
|
|
27442
|
+
});
|
|
27443
|
+
|
|
28066
27444
|
// src/core/approval.ts
|
|
28067
27445
|
function diffSize(d) {
|
|
28068
27446
|
return d.added.length + d.changed.length + d.removed.length;
|
|
@@ -28072,12 +27450,12 @@ function digest(content) {
|
|
|
28072
27450
|
return crypto4.createHash("sha256").update(normalized, "utf8").digest("hex");
|
|
28073
27451
|
}
|
|
28074
27452
|
function currentSpecDigests(root = getProjectRoot()) {
|
|
28075
|
-
const mountDirs = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::")).map((s) => `${
|
|
27453
|
+
const mountDirs = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::")).map((s) => `${path28.resolve(root, s.projectPath).split(path28.sep).join("/")}/`);
|
|
28076
27454
|
const out = {};
|
|
28077
27455
|
for (const [abs, content] of snapshotSpecFiles()) {
|
|
28078
|
-
const normalized =
|
|
27456
|
+
const normalized = path28.resolve(abs).split(path28.sep).join("/");
|
|
28079
27457
|
if (mountDirs.some((dir) => normalized.startsWith(dir))) continue;
|
|
28080
|
-
out[
|
|
27458
|
+
out[path28.relative(root, abs).split(path28.sep).join("/")] = digest(content);
|
|
28081
27459
|
}
|
|
28082
27460
|
return out;
|
|
28083
27461
|
}
|
|
@@ -28135,7 +27513,7 @@ function currentChildPins(mounts, root = getProjectRoot()) {
|
|
|
28135
27513
|
const pins = {};
|
|
28136
27514
|
for (const mount of mounts) {
|
|
28137
27515
|
if (!mount.projectPath || mount.id.includes("::")) continue;
|
|
28138
|
-
const child = readLockRecordAt(
|
|
27516
|
+
const child = readLockRecordAt(path28.resolve(root, mount.projectPath));
|
|
28139
27517
|
if (child) pins[mount.id] = pinOf(child.stateId);
|
|
28140
27518
|
}
|
|
28141
27519
|
return pins;
|
|
@@ -28149,17 +27527,64 @@ function movedChildren(mounts, root = getProjectRoot()) {
|
|
|
28149
27527
|
const current2 = now[id] ?? null;
|
|
28150
27528
|
if (current2 !== was) moved.push({ id, pinned: was, now: current2 });
|
|
28151
27529
|
}
|
|
28152
|
-
return moved;
|
|
27530
|
+
return moved;
|
|
27531
|
+
}
|
|
27532
|
+
function approvalVerdict() {
|
|
27533
|
+
const quiet = { text: "", drifted: false };
|
|
27534
|
+
try {
|
|
27535
|
+
const approval = approvalRecord();
|
|
27536
|
+
if (!approval) return quiet;
|
|
27537
|
+
const by = describeApprover(approval.lockedBy);
|
|
27538
|
+
const moved = movedChildren(loadSubsystemSpecs());
|
|
27539
|
+
const childNote = moved.length ? `
|
|
27540
|
+
${moved.length} chained child project(s) moved since approval: ${moved.map((m) => m.id).join(", ")}.` : "";
|
|
27541
|
+
const diff = diffAgainstApproval();
|
|
27542
|
+
if (!diff) {
|
|
27543
|
+
return {
|
|
27544
|
+
text: `
|
|
27545
|
+
Approved: ${approval.lockedAt} by ${by} \u2014 this lock predates per-spec approval, so drift is only visible at whole-tree level. Re-lock to record it.${childNote}
|
|
27546
|
+
`,
|
|
27547
|
+
drifted: moved.length > 0
|
|
27548
|
+
};
|
|
27549
|
+
}
|
|
27550
|
+
if (diffSize(diff) === 0) {
|
|
27551
|
+
return {
|
|
27552
|
+
text: `
|
|
27553
|
+
Approved: ${approval.lockedAt} by ${by} \u2014 no spec has changed since.${childNote}
|
|
27554
|
+
`,
|
|
27555
|
+
drifted: moved.length > 0
|
|
27556
|
+
};
|
|
27557
|
+
}
|
|
27558
|
+
const parts = [];
|
|
27559
|
+
if (diff.changed.length) parts.push(`${diff.changed.length} changed`);
|
|
27560
|
+
if (diff.added.length) parts.push(`${diff.added.length} added`);
|
|
27561
|
+
if (diff.removed.length) parts.push(`${diff.removed.length} removed`);
|
|
27562
|
+
const named = [...diff.changed, ...diff.added, ...diff.removed].slice(0, 5);
|
|
27563
|
+
const rest = diffSize(diff) - named.length;
|
|
27564
|
+
const n = diffSize(diff);
|
|
27565
|
+
const noun = `${n} spec${n === 1 ? "" : "s"}`;
|
|
27566
|
+
const headline = parts.length === 1 ? `${noun} ${parts[0].slice(String(diffSize(diff)).length + 1)} since approval` : `${noun} changed since approval (${parts.join(", ")})`;
|
|
27567
|
+
return {
|
|
27568
|
+
text: `
|
|
27569
|
+
${headline} \u2014 approved ${approval.lockedAt} by ${by}:
|
|
27570
|
+
` + named.map((p) => ` ${p}`).join("\n") + (rest > 0 ? `
|
|
27571
|
+
\u2026 and ${rest} more` : "") + childNote + "\n",
|
|
27572
|
+
drifted: true
|
|
27573
|
+
};
|
|
27574
|
+
} catch {
|
|
27575
|
+
return quiet;
|
|
27576
|
+
}
|
|
28153
27577
|
}
|
|
28154
|
-
var crypto4,
|
|
27578
|
+
var crypto4, path28;
|
|
28155
27579
|
var init_approval = __esm({
|
|
28156
27580
|
"src/core/approval.ts"() {
|
|
28157
27581
|
"use strict";
|
|
28158
27582
|
crypto4 = __toESM(require("crypto"));
|
|
28159
|
-
|
|
27583
|
+
path28 = __toESM(require("path"));
|
|
28160
27584
|
init_fs();
|
|
28161
27585
|
init_specs2();
|
|
28162
27586
|
init_lockfile();
|
|
27587
|
+
init_lock();
|
|
28163
27588
|
}
|
|
28164
27589
|
});
|
|
28165
27590
|
|
|
@@ -28179,12 +27604,12 @@ function gitConfig(key) {
|
|
|
28179
27604
|
function machineIdentity() {
|
|
28180
27605
|
let user = "unknown";
|
|
28181
27606
|
try {
|
|
28182
|
-
user =
|
|
27607
|
+
user = os5.userInfo().username || user;
|
|
28183
27608
|
} catch {
|
|
28184
27609
|
}
|
|
28185
27610
|
let host = "";
|
|
28186
27611
|
try {
|
|
28187
|
-
host =
|
|
27612
|
+
host = os5.hostname();
|
|
28188
27613
|
} catch {
|
|
28189
27614
|
}
|
|
28190
27615
|
return { id: host ? `${user}@${host}` : user, source: "os" };
|
|
@@ -28196,257 +27621,332 @@ function localApprover() {
|
|
|
28196
27621
|
if (email) return { id: email, source: "git" };
|
|
28197
27622
|
return machineIdentity();
|
|
28198
27623
|
}
|
|
28199
|
-
var import_child_process2,
|
|
27624
|
+
var import_child_process2, os5;
|
|
28200
27625
|
var init_approver = __esm({
|
|
28201
27626
|
"src/core/approver.ts"() {
|
|
28202
27627
|
"use strict";
|
|
28203
27628
|
import_child_process2 = require("child_process");
|
|
28204
|
-
|
|
27629
|
+
os5 = __toESM(require("os"));
|
|
28205
27630
|
}
|
|
28206
27631
|
});
|
|
28207
27632
|
|
|
28208
|
-
// src/core/
|
|
28209
|
-
function
|
|
28210
|
-
return
|
|
27633
|
+
// src/core/stamp.ts
|
|
27634
|
+
function versionStamp() {
|
|
27635
|
+
return `<!-- wairon-version: ${WAIRON_VERSION} -->`;
|
|
28211
27636
|
}
|
|
28212
|
-
function
|
|
28213
|
-
|
|
27637
|
+
function readStampVersion(content) {
|
|
27638
|
+
const m = content.match(STAMP_RE);
|
|
27639
|
+
return m ? m[1] : null;
|
|
28214
27640
|
}
|
|
28215
|
-
|
|
28216
|
-
|
|
27641
|
+
var STAMP_RE;
|
|
27642
|
+
var init_stamp = __esm({
|
|
27643
|
+
"src/core/stamp.ts"() {
|
|
27644
|
+
"use strict";
|
|
27645
|
+
init_defaults();
|
|
27646
|
+
STAMP_RE = /<!--\s*wairon-version:\s*([^\s]+)\s*-->/;
|
|
27647
|
+
}
|
|
27648
|
+
});
|
|
27649
|
+
|
|
27650
|
+
// src/utils/ai-guide.ts
|
|
27651
|
+
function globalGuideFilePath(targetType) {
|
|
27652
|
+
if (targetType === "claude") return path29.join(process.env["CLAUDE_CONFIG_DIR"] || path29.join(os6.homedir(), ".claude"), "CLAUDE.md");
|
|
27653
|
+
if (targetType === "gemini") return path29.join(process.env["GEMINI_CONFIG_DIR"] || path29.join(os6.homedir(), ".gemini"), "GEMINI.md");
|
|
27654
|
+
return null;
|
|
28217
27655
|
}
|
|
28218
|
-
function
|
|
28219
|
-
return
|
|
27656
|
+
function localGuideFilePath(projectRoot2, targetType) {
|
|
27657
|
+
if (targetType === "claude") return path29.join(projectRoot2, ".claude", "CLAUDE.md");
|
|
27658
|
+
if (targetType === "gemini" || targetType === "agy") return path29.join(projectRoot2, ".gemini", "GEMINI.md");
|
|
27659
|
+
return null;
|
|
28220
27660
|
}
|
|
28221
|
-
function
|
|
28222
|
-
|
|
27661
|
+
function injectGuide(filePath, scope) {
|
|
27662
|
+
const body = scope === "global" ? GLOBAL_GUIDE_BODY : LOCAL_GUIDE_BODY;
|
|
27663
|
+
const section = `
|
|
27664
|
+
|
|
27665
|
+
${GUIDE_MARKER_START}
|
|
27666
|
+
${versionStamp()}
|
|
27667
|
+
${body}
|
|
27668
|
+
${GUIDE_MARKER_END}
|
|
27669
|
+
`;
|
|
27670
|
+
const existing = fs19.existsSync(filePath) ? fs19.readFileSync(filePath, "utf-8") : "";
|
|
27671
|
+
const stripped = stripGuideSection(existing);
|
|
27672
|
+
const newContent = stripped.trimEnd() + section;
|
|
27673
|
+
fs19.mkdirSync(path29.dirname(filePath), { recursive: true });
|
|
27674
|
+
fs19.writeFileSync(filePath, newContent, "utf-8");
|
|
28223
27675
|
}
|
|
28224
|
-
|
|
28225
|
-
|
|
27676
|
+
function stripGuideSection(content) {
|
|
27677
|
+
const start2 = content.indexOf(GUIDE_MARKER_START);
|
|
27678
|
+
const end = content.indexOf(GUIDE_MARKER_END);
|
|
27679
|
+
if (start2 === -1 || end === -1) return content;
|
|
27680
|
+
return content.slice(0, start2) + content.slice(end + GUIDE_MARKER_END.length);
|
|
27681
|
+
}
|
|
27682
|
+
function writeRootGuideDelegator(projectRoot2, targetType) {
|
|
27683
|
+
if (targetType === "claude") {
|
|
27684
|
+
const filePath = path29.join(projectRoot2, "CLAUDE.md");
|
|
27685
|
+
const content = `@.claude/CLAUDE.md
|
|
27686
|
+
|
|
27687
|
+
# Wairon SDD Project
|
|
27688
|
+
|
|
27689
|
+
This project uses the Wairon Spec-Driven Development (SDD) framework. The imported
|
|
27690
|
+
\`.claude/CLAUDE.md\` above is your complete operating guide \u2014 you already have the
|
|
27691
|
+
full context, so don't search the project to learn how wairon or SDD works.
|
|
27692
|
+
|
|
27693
|
+
To design or modify the system, invoke the **\`sdd-architect\`** skill
|
|
27694
|
+
(in \`.claude/skills/\`). Author and validate specs with the \`sdd_*\` MCP tools;
|
|
27695
|
+
the \`wairon\` CLI is the human developer's tool, not yours.
|
|
27696
|
+
`;
|
|
27697
|
+
fs19.writeFileSync(filePath, content, "utf-8");
|
|
27698
|
+
} else if (targetType === "gemini" || targetType === "agy") {
|
|
27699
|
+
const filePath = path29.join(projectRoot2, "GEMINI.md");
|
|
27700
|
+
const content = `# Wairon SDD Project
|
|
27701
|
+
${GUIDE_MARKER_START}
|
|
27702
|
+
${versionStamp()}
|
|
27703
|
+
${LOCAL_GUIDE_BODY}
|
|
27704
|
+
${GUIDE_MARKER_END}
|
|
27705
|
+
`;
|
|
27706
|
+
fs19.writeFileSync(filePath, content, "utf-8");
|
|
27707
|
+
} else if (targetType === "cursor") {
|
|
27708
|
+
const filePath = path29.join(projectRoot2, ".cursorrules");
|
|
27709
|
+
const content = `# Wairon SDD Project
|
|
27710
|
+
|
|
27711
|
+
This project uses the Wairon Spec-Driven Development (SDD) framework.
|
|
27712
|
+
|
|
27713
|
+
Refer to the rules in [.cursor/rules/](.cursor/rules/) for full instructions.
|
|
27714
|
+
`;
|
|
27715
|
+
fs19.writeFileSync(filePath, content, "utf-8");
|
|
27716
|
+
} else if (targetType === "copilot") {
|
|
27717
|
+
const filePath = path29.join(projectRoot2, ".github", "copilot-instructions.md");
|
|
27718
|
+
fs19.mkdirSync(path29.dirname(filePath), { recursive: true });
|
|
27719
|
+
const content = `# Wairon SDD Project
|
|
27720
|
+
|
|
27721
|
+
This project uses the Wairon Spec-Driven Development (SDD) framework.
|
|
27722
|
+
|
|
27723
|
+
Refer to the prompts in [.github/prompts/](.github/prompts/) for instructions.
|
|
27724
|
+
`;
|
|
27725
|
+
fs19.writeFileSync(filePath, content, "utf-8");
|
|
27726
|
+
} else if (targetType === "codex") {
|
|
27727
|
+
const filePath = path29.join(projectRoot2, ".codexrules");
|
|
27728
|
+
const content = `# Wairon SDD Project
|
|
27729
|
+
|
|
27730
|
+
Refer to [.codex/agents/](.codex/agents/) for full instructions.
|
|
27731
|
+
`;
|
|
27732
|
+
fs19.writeFileSync(filePath, content, "utf-8");
|
|
27733
|
+
}
|
|
27734
|
+
}
|
|
27735
|
+
function reinjectLocalGuides(projectRoot2, targetTypes) {
|
|
27736
|
+
const written = [];
|
|
27737
|
+
for (const type of targetTypes) {
|
|
27738
|
+
if (!GUIDE_TARGETS.includes(type)) continue;
|
|
27739
|
+
const guidePath = localGuideFilePath(projectRoot2, type);
|
|
27740
|
+
if (guidePath) {
|
|
27741
|
+
injectGuide(guidePath, "local");
|
|
27742
|
+
if (!written.includes(guidePath)) written.push(guidePath);
|
|
27743
|
+
}
|
|
27744
|
+
writeRootGuideDelegator(projectRoot2, type);
|
|
27745
|
+
}
|
|
27746
|
+
return written;
|
|
27747
|
+
}
|
|
27748
|
+
var fs19, os6, path29, GUIDE_MARKER_START, GUIDE_MARKER_END, GLOBAL_GUIDE_BODY, LOCAL_GUIDE_BODY, GUIDE_TARGETS;
|
|
27749
|
+
var init_ai_guide = __esm({
|
|
27750
|
+
"src/utils/ai-guide.ts"() {
|
|
28226
27751
|
"use strict";
|
|
28227
|
-
|
|
28228
|
-
|
|
28229
|
-
|
|
28230
|
-
init_detection();
|
|
28231
|
-
init_templates();
|
|
28232
|
-
init_validation();
|
|
28233
|
-
init_extensions();
|
|
28234
|
-
init_variants();
|
|
28235
|
-
init_rules();
|
|
28236
|
-
init_specs2();
|
|
28237
|
-
init_provision();
|
|
28238
|
-
init_diagram();
|
|
28239
|
-
init_lockfile();
|
|
28240
|
-
init_statehash();
|
|
28241
|
-
init_agent_resolver();
|
|
28242
|
-
init_skills();
|
|
28243
|
-
init_context();
|
|
28244
|
-
init_surfaces();
|
|
28245
|
-
init_openapi();
|
|
28246
|
-
init_packstore();
|
|
28247
|
-
init_treetransfer();
|
|
28248
|
-
init_extensions();
|
|
28249
|
-
init_agent_resolver();
|
|
28250
|
-
init_generate();
|
|
28251
|
-
init_treetransfer();
|
|
28252
|
-
init_provision();
|
|
28253
|
-
init_stereotype_migration();
|
|
28254
|
-
init_narrative_repair();
|
|
28255
|
-
init_approval();
|
|
28256
|
-
init_approver();
|
|
28257
|
-
init_specs2();
|
|
28258
|
-
init_project_config();
|
|
28259
|
-
init_execution_profile();
|
|
28260
|
-
init_budget_policy();
|
|
28261
|
-
init_ai_guide();
|
|
27752
|
+
fs19 = __toESM(require("fs"));
|
|
27753
|
+
os6 = __toESM(require("os"));
|
|
27754
|
+
path29 = __toESM(require("path"));
|
|
28262
27755
|
init_stamp();
|
|
27756
|
+
GUIDE_MARKER_START = "<!-- wairon-guide-start -->";
|
|
27757
|
+
GUIDE_MARKER_END = "<!-- wairon-guide-end -->";
|
|
27758
|
+
GLOBAL_GUIDE_BODY = `## wairon \u2014 Spec-Driven Development (optional)
|
|
27759
|
+
|
|
27760
|
+
If \`.wai/specs/\` exists, the wairon SDD workflow is active; otherwise ignore it. wairon does not orchestrate sessions \u2014 it equips yours.
|
|
27761
|
+
|
|
27762
|
+
### In SDD Projects:
|
|
27763
|
+
- **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\`).
|
|
27764
|
+
- **Validation**: Conformance checks (stereotype rules, cycle checks, reference integrity) are run via the \`sdd_validate_tree\` MCP tool.
|
|
27765
|
+
- **Operating Rules**:
|
|
27766
|
+
1. **Skills**: Use \`sdd-architect\` to design (and \`sdd-implement\`, \`sdd-narrative\`, \`sdd-auditor\`). Refer to project's local guide file for detailed constraints.
|
|
27767
|
+
2. **MCP Tools Only**: Author/validate specs *only* via \`sdd_*\` tools (e.g. \`sdd_initialize_system\`, \`sdd_validate_tree\`).
|
|
27768
|
+
3. **No CLI Exec**: Do not run the \`wairon\` CLI (human tool). Use MCP tools \`sdd_validate_tree\` and \`sdd_get_status\` instead.
|
|
27769
|
+
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>\`).
|
|
27770
|
+
5. **Design First**: Complete spec and pass \`sdd_validate_tree\` before writing code.
|
|
27771
|
+
6. **Consistency**: Code must match L3 interfaces and L5 narratives exactly. If the spec is wrong, stop and update the spec.
|
|
27772
|
+
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.`;
|
|
27773
|
+
LOCAL_GUIDE_BODY = `## Wairon \u2014 Spec-Driven Development (you are operating inside it)
|
|
27774
|
+
|
|
27775
|
+
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.
|
|
27776
|
+
|
|
27777
|
+
**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.**
|
|
27778
|
+
|
|
27779
|
+
**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.
|
|
27780
|
+
|
|
27781
|
+
### How you operate
|
|
27782
|
+
- **To design/modify specs**: Use **\`sdd-architect\`** skill (in \`.claude/skills/\` or \`.gemini/skills/\`).
|
|
27783
|
+
- **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.
|
|
27784
|
+
- **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.
|
|
27785
|
+
- **Leading \`::\`**: Bypasses the local subsystem prefix to resolve absolute from the system root (e.g. \`::shared::error-type\`).
|
|
27786
|
+
- **\`super::\`**: Goes up one parent subsystem level (e.g. \`super::sibling_comp\`, \`super::super::parent_sibling\`).
|
|
27787
|
+
- **Do not run the \`wairon\` CLI**: Use \`sdd_validate_tree\` and \`sdd_get_status\` instead of CLI commands.
|
|
27788
|
+
- **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.
|
|
27789
|
+
- **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.
|
|
27790
|
+
|
|
27791
|
+
### Rules (enforced by \`sdd_validate_tree\`)
|
|
27792
|
+
1. **Design before code**: Complete spec and pass validator before writing source code.
|
|
27793
|
+
2. **Human-in-the-loop**: Ask user approval for each spec layer before proceeding.
|
|
27794
|
+
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.
|
|
27795
|
+
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.
|
|
27796
|
+
|
|
27797
|
+
### Component Vocabulary
|
|
27798
|
+
* **Blocks**: Portal, Orchestrator, Supervisor, Actor, Store, Index, Query, Registry, Adapter, Observer.
|
|
27799
|
+
* **Patterns**: Repository \u2014 the composable pattern that \`owns\` its member blocks: one Store with its Registry, Indexes and Queries, and optionally an Adapter.
|
|
27800
|
+
* **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.
|
|
27801
|
+
* Use \`owns\` for private member containment (exactly one hop) and \`dependsOn\` for collaborators. Never use generic suffixes like "Manager", "Helper", or "Utils".`;
|
|
27802
|
+
GUIDE_TARGETS = ["claude", "gemini", "agy", "cursor", "copilot", "codex"];
|
|
28263
27803
|
}
|
|
28264
27804
|
});
|
|
28265
27805
|
|
|
28266
|
-
// src/
|
|
28267
|
-
|
|
28268
|
-
|
|
28269
|
-
|
|
28270
|
-
|
|
28271
|
-
|
|
28272
|
-
|
|
28273
|
-
|
|
28274
|
-
|
|
28275
|
-
|
|
28276
|
-
|
|
27806
|
+
// src/core/context.ts
|
|
27807
|
+
function contextDir(...segments) {
|
|
27808
|
+
return aiDir("context", ...segments);
|
|
27809
|
+
}
|
|
27810
|
+
function hasContext() {
|
|
27811
|
+
return pathExists(CONTEXT_PATHS.projectMd());
|
|
27812
|
+
}
|
|
27813
|
+
function readProjectContext() {
|
|
27814
|
+
return readFileOrNull(CONTEXT_PATHS.projectMd());
|
|
27815
|
+
}
|
|
27816
|
+
function readArchitectureContext() {
|
|
27817
|
+
return readFileOrNull(CONTEXT_PATHS.architectureMd());
|
|
27818
|
+
}
|
|
27819
|
+
function writeDomainsDoc(content) {
|
|
27820
|
+
return writeFileIfChanged(CONTEXT_PATHS.domainsMd(), content);
|
|
27821
|
+
}
|
|
27822
|
+
function writeGuideDoc(content) {
|
|
27823
|
+
return writeFileIfChanged(CONTEXT_PATHS.waironGuideMd(), content);
|
|
27824
|
+
}
|
|
27825
|
+
function derivedDocPaths() {
|
|
27826
|
+
return [CONTEXT_PATHS.waironGuideMd(), CONTEXT_PATHS.domainsMd()];
|
|
27827
|
+
}
|
|
27828
|
+
function renderDomainsDoc() {
|
|
27829
|
+
const domains = resolveDomains();
|
|
27830
|
+
const lines = [
|
|
27831
|
+
versionStamp(),
|
|
27832
|
+
`# Domains`,
|
|
27833
|
+
``,
|
|
27834
|
+
`> Auto-generated by wairon. The human developer rebuilds this with \`wairon generate\`. `,
|
|
27835
|
+
``
|
|
27836
|
+
];
|
|
27837
|
+
if (domains.length === 0) {
|
|
27838
|
+
lines.push("_No domains yet. Define subsystems in the spec tree, or add free-standing domains with `wairon domains add`._");
|
|
27839
|
+
} else {
|
|
27840
|
+
lines.push(`**${domains.length} domain(s).**`);
|
|
27841
|
+
lines.push("");
|
|
27842
|
+
lines.push("| ID | Name | Source | Owned paths |");
|
|
27843
|
+
lines.push("|----|------|--------|-------------|");
|
|
27844
|
+
for (const d of domains) {
|
|
27845
|
+
const name = d.name ?? d.id;
|
|
27846
|
+
const source = d.boundTo ? `subsystem \`${d.boundTo}\`` : "free-standing";
|
|
27847
|
+
lines.push(`| \`${d.id}\` | ${name} | ${source} | \`${d.ownedPaths.join("`, `") || "\u2014"}\` |`);
|
|
28277
27848
|
}
|
|
28278
|
-
process.exit(1);
|
|
28279
27849
|
}
|
|
28280
|
-
|
|
28281
|
-
|
|
28282
|
-
|
|
28283
|
-
|
|
28284
|
-
|
|
28285
|
-
|
|
28286
|
-
|
|
28287
|
-
|
|
28288
|
-
const c = components.find((comp) => comp.id === i.component);
|
|
28289
|
-
return c !== void 0;
|
|
28290
|
-
});
|
|
28291
|
-
implementations = implementations.filter((im) => {
|
|
28292
|
-
const inf = interfaces.find((i) => i.id === im.contract);
|
|
28293
|
-
return inf !== void 0;
|
|
28294
|
-
});
|
|
27850
|
+
return lines.join("\n") + "\n";
|
|
27851
|
+
}
|
|
27852
|
+
function renderWaironGuide() {
|
|
27853
|
+
const domains = resolveDomains();
|
|
27854
|
+
let projectName = "this project";
|
|
27855
|
+
try {
|
|
27856
|
+
projectName = projectConfigRepository.load()?.name ?? projectName;
|
|
27857
|
+
} catch {
|
|
28295
27858
|
}
|
|
28296
|
-
|
|
28297
|
-
|
|
28298
|
-
|
|
27859
|
+
const projectCtx = readProjectContext();
|
|
27860
|
+
const archCtx = readArchitectureContext();
|
|
27861
|
+
const lines = [
|
|
27862
|
+
versionStamp(),
|
|
27863
|
+
`<!-- wairon-generated \u2014 do not edit directly; the human developer rebuilds this with \`wairon generate\` -->`,
|
|
27864
|
+
``
|
|
27865
|
+
];
|
|
27866
|
+
if (projectCtx) {
|
|
27867
|
+
lines.push(`# Project Context \u2014 ${projectName}`);
|
|
27868
|
+
lines.push("");
|
|
27869
|
+
lines.push(projectCtx.trim());
|
|
27870
|
+
lines.push("");
|
|
27871
|
+
lines.push("---");
|
|
27872
|
+
lines.push("");
|
|
28299
27873
|
}
|
|
28300
|
-
|
|
28301
|
-
|
|
28302
|
-
|
|
28303
|
-
|
|
28304
|
-
|
|
28305
|
-
|
|
28306
|
-
|
|
28307
|
-
score += 30;
|
|
28308
|
-
}
|
|
28309
|
-
const impl = implementations.find((im) => intf && im.contract === intf.id);
|
|
28310
|
-
if (impl) {
|
|
28311
|
-
score += 30;
|
|
28312
|
-
const sourceFiles = implementationSourceFiles(impl);
|
|
28313
|
-
if (sourceFiles.length > 0 && sourceFiles.every((f) => pathExists(fromProjectRoot(f)))) {
|
|
28314
|
-
score += 20;
|
|
28315
|
-
}
|
|
28316
|
-
}
|
|
28317
|
-
const isDraft = comp.status === "draft" || comp.status === "design" || intf && (intf.status === "draft" || intf.status === "design") || impl && (impl.status === "draft" || impl.status === "design");
|
|
28318
|
-
if (isDraft) {
|
|
28319
|
-
score = Math.min(score, 50);
|
|
28320
|
-
}
|
|
28321
|
-
componentScores.set(comp.id, score);
|
|
27874
|
+
if (archCtx) {
|
|
27875
|
+
lines.push("# Architecture");
|
|
27876
|
+
lines.push("");
|
|
27877
|
+
lines.push(archCtx.trim());
|
|
27878
|
+
lines.push("");
|
|
27879
|
+
lines.push("---");
|
|
27880
|
+
lines.push("");
|
|
28322
27881
|
}
|
|
28323
|
-
|
|
28324
|
-
|
|
28325
|
-
|
|
28326
|
-
|
|
28327
|
-
|
|
28328
|
-
|
|
28329
|
-
|
|
28330
|
-
|
|
28331
|
-
|
|
28332
|
-
|
|
28333
|
-
|
|
28334
|
-
};
|
|
28335
|
-
const totalSubsystemsScore = subsystems.reduce((acc, s) => acc + getSubsystemScore(s.id), 0);
|
|
28336
|
-
const systemScore = subsystems.length > 0 ? Math.round(totalSubsystemsScore / subsystems.length) : 0;
|
|
28337
|
-
const systemColor = systemScore === 100 ? import_chalk4.default.green : systemScore >= 50 ? import_chalk4.default.yellow : import_chalk4.default.red;
|
|
28338
|
-
console.log(`${import_chalk4.default.bold.blue("\u25CF System:")} ${import_chalk4.default.bold(system.name)} ${systemColor(`(${systemScore}% Complete)`)}`);
|
|
28339
|
-
for (let i = 0; i < subsystems.length; i++) {
|
|
28340
|
-
const sub = subsystems[i];
|
|
28341
|
-
const isLastSub = i === subsystems.length - 1;
|
|
28342
|
-
const subPrefix = isLastSub ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
28343
|
-
const subIndent = isLastSub ? " " : "\u2502 ";
|
|
28344
|
-
const subScore = getSubsystemScore(sub.id);
|
|
28345
|
-
const subStatusStr = sub.status !== "complete" ? import_chalk4.default.yellow(` [${sub.status}]`) : "";
|
|
28346
|
-
const subScoreColor = subScore === 100 ? import_chalk4.default.green : subScore >= 50 ? import_chalk4.default.yellow : import_chalk4.default.red;
|
|
28347
|
-
console.log(`${import_chalk4.default.gray(subPrefix)}${import_chalk4.default.bold.cyan(`[Subsystem] ${sub.id}`)}${subStatusStr} ${subScoreColor(`(${subScore}%)`)}`);
|
|
28348
|
-
const subComps = components.filter((c) => c.subsystem === sub.id);
|
|
28349
|
-
for (let j = 0; j < subComps.length; j++) {
|
|
28350
|
-
const comp = subComps[j];
|
|
28351
|
-
const isLastComp = j === subComps.length - 1;
|
|
28352
|
-
const compPrefix = isLastComp ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
28353
|
-
const compIndent = isLastComp ? " " : "\u2502 ";
|
|
28354
|
-
const compScore = componentScores.get(comp.id) ?? 0;
|
|
28355
|
-
const compStatusStr = comp.status !== "complete" ? import_chalk4.default.yellow(` [${comp.status}]`) : "";
|
|
28356
|
-
const compScoreColor = compScore === 100 ? import_chalk4.default.green : compScore >= 50 ? import_chalk4.default.yellow : import_chalk4.default.red;
|
|
28357
|
-
console.log(`${import_chalk4.default.gray(subIndent + compPrefix)}${import_chalk4.default.magenta(`[Component: ${comp.componentType}] ${comp.id}`)}${compStatusStr} ${compScoreColor(`(${compScore}%)`)}`);
|
|
28358
|
-
const intf = interfaces.find((inf) => inf.component === comp.id);
|
|
28359
|
-
const impl = implementations.find((im) => intf && im.contract === intf.id);
|
|
28360
|
-
const intfPrefix = intf && impl ? "\u251C\u2500\u2500 " : "\u2514\u2500\u2500 ";
|
|
28361
|
-
if (intf) {
|
|
28362
|
-
const intfStatusStr = intf.status !== "complete" ? import_chalk4.default.yellow(` [${intf.status}]`) : "";
|
|
28363
|
-
console.log(`${import_chalk4.default.gray(subIndent + compIndent + intfPrefix)}${import_chalk4.default.blue(`Interface: ${intf.id}`)}${intfStatusStr} (${intf.methods.length} methods)`);
|
|
28364
|
-
} else {
|
|
28365
|
-
console.log(`${import_chalk4.default.gray(subIndent + compIndent + intfPrefix)}${import_chalk4.default.red("Interface: Missing (-30%)")}`);
|
|
28366
|
-
}
|
|
28367
|
-
if (impl) {
|
|
28368
|
-
const implStatusStr = impl.status !== "complete" ? import_chalk4.default.yellow(` [${impl.status}]`) : "";
|
|
28369
|
-
const methodsWithOwnPath = impl.methods.filter((m) => m.sourcePath);
|
|
28370
|
-
let pathStr;
|
|
28371
|
-
if (impl.sourcePath) {
|
|
28372
|
-
pathStr = pathExists(fromProjectRoot(impl.sourcePath)) ? import_chalk4.default.green(` -> ${impl.sourcePath}`) : import_chalk4.default.red(` -> ${impl.sourcePath} (File Missing!)`);
|
|
28373
|
-
} else if (methodsWithOwnPath.length > 0) {
|
|
28374
|
-
pathStr = "";
|
|
28375
|
-
} else {
|
|
28376
|
-
pathStr = import_chalk4.default.gray(" (No source path)");
|
|
28377
|
-
}
|
|
28378
|
-
console.log(`${import_chalk4.default.gray(subIndent + compIndent + "\u2514\u2500\u2500 ")}${import_chalk4.default.green(`Implementation: ${impl.id}`)}${implStatusStr}${pathStr}`);
|
|
28379
|
-
const methodIndent = subIndent + compIndent + " ";
|
|
28380
|
-
for (let k = 0; k < methodsWithOwnPath.length; k++) {
|
|
28381
|
-
const method2 = methodsWithOwnPath[k];
|
|
28382
|
-
const isLastMethod = k === methodsWithOwnPath.length - 1;
|
|
28383
|
-
const methodPrefix = isLastMethod ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
28384
|
-
const methodPath = method2.sourcePath;
|
|
28385
|
-
const methodLine = pathExists(fromProjectRoot(methodPath)) ? import_chalk4.default.green(`method ${method2.name} -> ${methodPath}`) : import_chalk4.default.red(`method ${method2.name} -> ${methodPath} (File Missing!)`);
|
|
28386
|
-
console.log(`${import_chalk4.default.gray(methodIndent + methodPrefix)}${methodLine}`);
|
|
28387
|
-
}
|
|
28388
|
-
} else {
|
|
28389
|
-
console.log(`${import_chalk4.default.gray(subIndent + compIndent + "\u2514\u2500\u2500 ")}${import_chalk4.default.red("Implementation: Missing (-30%)")}`);
|
|
28390
|
-
}
|
|
27882
|
+
lines.push(`# Domain Map (${domains.length} domain${domains.length !== 1 ? "s" : ""})`);
|
|
27883
|
+
lines.push("");
|
|
27884
|
+
if (domains.length === 0) {
|
|
27885
|
+
lines.push("_No domains yet._");
|
|
27886
|
+
} else {
|
|
27887
|
+
lines.push("| ID | Source | Name |");
|
|
27888
|
+
lines.push("|----|--------|------|");
|
|
27889
|
+
for (const d of domains) {
|
|
27890
|
+
const name = d.name ?? d.id;
|
|
27891
|
+
const source = d.boundTo ? `subsystem \`${d.boundTo}\`` : "free-standing";
|
|
27892
|
+
lines.push(`| \`${d.id}\` | ${source} | ${name} |`);
|
|
28391
27893
|
}
|
|
28392
27894
|
}
|
|
28393
|
-
|
|
28394
|
-
|
|
28395
|
-
|
|
28396
|
-
|
|
28397
|
-
|
|
27895
|
+
lines.push("");
|
|
27896
|
+
lines.push("---");
|
|
27897
|
+
lines.push("");
|
|
27898
|
+
const mcpSettingsPath = require("path").join(process.cwd(), ".claude", "settings.json");
|
|
27899
|
+
let mcpInstalled = false;
|
|
27900
|
+
try {
|
|
27901
|
+
const s = JSON.parse(require("fs").readFileSync(mcpSettingsPath, "utf8"));
|
|
27902
|
+
mcpInstalled = !!s?.mcpServers?.wairon;
|
|
27903
|
+
} catch {
|
|
28398
27904
|
}
|
|
28399
|
-
|
|
27905
|
+
if (mcpInstalled) {
|
|
27906
|
+
lines.push("# wairon MCP Tools");
|
|
27907
|
+
lines.push("");
|
|
27908
|
+
lines.push("The **wairon MCP server** is active in this project. The tools are self-describing; call them directly:");
|
|
27909
|
+
lines.push("");
|
|
27910
|
+
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`.");
|
|
27911
|
+
lines.push("- Reading & maintenance: `sdd_get_spec`, `sdd_update_spec`, `sdd_delete_spec`, `sdd_validate_tree`, `sdd_get_status`.");
|
|
27912
|
+
lines.push("- Topology: `listAgents`, `getAgent`, `listDomains`, `validateTopology`, `getProjectConfig`.");
|
|
27913
|
+
lines.push("");
|
|
27914
|
+
lines.push("Use these MCP tools to query and change project state \u2014 never the `wairon` CLI (that is the human developer's tool).");
|
|
27915
|
+
lines.push("");
|
|
27916
|
+
lines.push("---");
|
|
27917
|
+
lines.push("");
|
|
27918
|
+
}
|
|
27919
|
+
lines.push(GLOBAL_GUIDE_BODY);
|
|
27920
|
+
lines.push("");
|
|
27921
|
+
return lines.join("\n");
|
|
28400
27922
|
}
|
|
28401
|
-
function
|
|
28402
|
-
|
|
27923
|
+
function syncContextFiles() {
|
|
27924
|
+
const domainsContent = renderDomainsDoc();
|
|
27925
|
+
const guideContent = renderWaironGuide();
|
|
27926
|
+
const domainsUpdated = writeDomainsDoc(domainsContent);
|
|
27927
|
+
const guideUpdated = writeGuideDoc(guideContent);
|
|
27928
|
+
return { domainsUpdated, guideUpdated };
|
|
28403
27929
|
}
|
|
28404
|
-
|
|
28405
|
-
|
|
28406
|
-
|
|
28407
|
-
|
|
28408
|
-
|
|
28409
|
-
|
|
28410
|
-
|
|
28411
|
-
|
|
28412
|
-
|
|
28413
|
-
|
|
28414
|
-
|
|
28415
|
-
|
|
28416
|
-
|
|
28417
|
-
|
|
28418
|
-
|
|
28419
|
-
drifted: moved.length > 0
|
|
28420
|
-
};
|
|
28421
|
-
}
|
|
28422
|
-
if (diffSize(diff) === 0) {
|
|
28423
|
-
return {
|
|
28424
|
-
text: `
|
|
28425
|
-
Approved: ${approval.lockedAt} by ${by} \u2014 no spec has changed since.${childNote}
|
|
28426
|
-
`,
|
|
28427
|
-
drifted: moved.length > 0
|
|
28428
|
-
};
|
|
28429
|
-
}
|
|
28430
|
-
const parts = [];
|
|
28431
|
-
if (diff.changed.length) parts.push(`${diff.changed.length} changed`);
|
|
28432
|
-
if (diff.added.length) parts.push(`${diff.added.length} added`);
|
|
28433
|
-
if (diff.removed.length) parts.push(`${diff.removed.length} removed`);
|
|
28434
|
-
const named = [...diff.changed, ...diff.added, ...diff.removed].slice(0, 5);
|
|
28435
|
-
const rest = diffSize(diff) - named.length;
|
|
28436
|
-
const n = diffSize(diff);
|
|
28437
|
-
const noun = `${n} spec${n === 1 ? "" : "s"}`;
|
|
28438
|
-
const headline = parts.length === 1 ? `${noun} ${parts[0].slice(String(diffSize(diff)).length + 1)} since approval` : `${noun} changed since approval (${parts.join(", ")})`;
|
|
28439
|
-
return {
|
|
28440
|
-
text: `
|
|
28441
|
-
${headline} \u2014 approved ${approval.lockedAt} by ${by}:
|
|
28442
|
-
` + named.map((p) => ` ${p}`).join("\n") + (rest > 0 ? `
|
|
28443
|
-
\u2026 and ${rest} more` : "") + childNote + "\n",
|
|
28444
|
-
drifted: true
|
|
27930
|
+
var CONTEXT_PATHS;
|
|
27931
|
+
var init_context = __esm({
|
|
27932
|
+
"src/core/context.ts"() {
|
|
27933
|
+
"use strict";
|
|
27934
|
+
init_fs();
|
|
27935
|
+
init_ai_guide();
|
|
27936
|
+
init_stamp();
|
|
27937
|
+
init_domain_projector();
|
|
27938
|
+
init_project_config();
|
|
27939
|
+
CONTEXT_PATHS = {
|
|
27940
|
+
dir: () => contextDir(),
|
|
27941
|
+
projectMd: () => contextDir("project.md"),
|
|
27942
|
+
architectureMd: () => contextDir("architecture.md"),
|
|
27943
|
+
domainsMd: () => contextDir("domains.md"),
|
|
27944
|
+
waironGuideMd: () => contextDir("wairon-guide.md")
|
|
28445
27945
|
};
|
|
28446
|
-
} catch {
|
|
28447
|
-
return quiet;
|
|
28448
27946
|
}
|
|
28449
|
-
}
|
|
27947
|
+
});
|
|
27948
|
+
|
|
27949
|
+
// src/core/status.ts
|
|
28450
27950
|
function getStatusReport(options = {}) {
|
|
28451
27951
|
scanAllSpecs({ recursive: options.recursive ?? true });
|
|
28452
27952
|
const system = loadSystemSpec();
|
|
@@ -28576,21 +28076,388 @@ function getStatusReport(options = {}) {
|
|
|
28576
28076
|
}
|
|
28577
28077
|
}
|
|
28578
28078
|
}
|
|
28579
|
-
output += lockLine();
|
|
28580
28079
|
return output;
|
|
28581
28080
|
}
|
|
28582
|
-
var import_chalk4;
|
|
28583
28081
|
var init_status = __esm({
|
|
28584
|
-
"src/
|
|
28082
|
+
"src/core/status.ts"() {
|
|
28585
28083
|
"use strict";
|
|
28586
|
-
|
|
28587
|
-
|
|
28588
|
-
init_paths();
|
|
28084
|
+
init_specs2();
|
|
28085
|
+
init_specs();
|
|
28589
28086
|
init_fs();
|
|
28087
|
+
}
|
|
28088
|
+
});
|
|
28089
|
+
|
|
28090
|
+
// src/core/index.ts
|
|
28091
|
+
function resolveDomains2() {
|
|
28092
|
+
return resolveDomains();
|
|
28093
|
+
}
|
|
28094
|
+
function addDomain(domain) {
|
|
28095
|
+
registerDomain(domain);
|
|
28096
|
+
}
|
|
28097
|
+
function removeDomain(id) {
|
|
28098
|
+
unregisterDomain(id);
|
|
28099
|
+
}
|
|
28100
|
+
function loadProjectConfig() {
|
|
28101
|
+
return projectConfigRepository.load();
|
|
28102
|
+
}
|
|
28103
|
+
function projectConfigExists() {
|
|
28104
|
+
return projectConfigRepository.exists();
|
|
28105
|
+
}
|
|
28106
|
+
var init_core = __esm({
|
|
28107
|
+
"src/core/index.ts"() {
|
|
28108
|
+
"use strict";
|
|
28109
|
+
init_project_config();
|
|
28110
|
+
init_domain_projector();
|
|
28111
|
+
init_domain_curator();
|
|
28112
|
+
init_rules();
|
|
28590
28113
|
init_specs2();
|
|
28591
|
-
|
|
28114
|
+
init_provision();
|
|
28115
|
+
init_statehash();
|
|
28592
28116
|
init_lockfile();
|
|
28593
|
-
|
|
28117
|
+
init_diagram();
|
|
28118
|
+
init_diagram();
|
|
28119
|
+
init_detection();
|
|
28120
|
+
init_extensions();
|
|
28121
|
+
init_packstore();
|
|
28122
|
+
init_agent_resolver();
|
|
28123
|
+
init_generate();
|
|
28124
|
+
init_treetransfer();
|
|
28125
|
+
init_provision();
|
|
28126
|
+
init_stereotype_migration();
|
|
28127
|
+
init_narrative_repair();
|
|
28128
|
+
init_approval();
|
|
28129
|
+
init_approver();
|
|
28130
|
+
init_specs2();
|
|
28131
|
+
init_project_config();
|
|
28132
|
+
init_execution_profile();
|
|
28133
|
+
init_budget_policy();
|
|
28134
|
+
init_ai_guide();
|
|
28135
|
+
init_stamp();
|
|
28136
|
+
init_context();
|
|
28137
|
+
init_status();
|
|
28138
|
+
}
|
|
28139
|
+
});
|
|
28140
|
+
|
|
28141
|
+
// src/core/instructions.ts
|
|
28142
|
+
function governingProfile() {
|
|
28143
|
+
try {
|
|
28144
|
+
return loadProjectConfig4()?.projectType ?? null;
|
|
28145
|
+
} catch {
|
|
28146
|
+
return null;
|
|
28147
|
+
}
|
|
28148
|
+
}
|
|
28149
|
+
function blockApplies(block, profile) {
|
|
28150
|
+
if (!block.profile || block.profile.length === 0) return true;
|
|
28151
|
+
return profile !== null && block.profile.includes(profile);
|
|
28152
|
+
}
|
|
28153
|
+
function skillPointers(skills) {
|
|
28154
|
+
if (skills.length === 0) return "(none published by this server)";
|
|
28155
|
+
return skills.map((s) => `- \`${s.resourceUri}\` \u2014 ${s.description || s.name}`).join("\n");
|
|
28156
|
+
}
|
|
28157
|
+
function waironInstructions(profile, packNames, skills, variants) {
|
|
28158
|
+
return `# wairon \u2014 Spec-Driven Development (SDD)
|
|
28159
|
+
|
|
28160
|
+
This server owns one system's **spec tree**: its architecture, specified top-down
|
|
28161
|
+
and machine-checked. Author it ONLY through this server's \`sdd_*\` tools \u2014 never by
|
|
28162
|
+
writing spec files directly, and never via the \`wairon\` CLI (that is the human
|
|
28163
|
+
developer's tool).
|
|
28164
|
+
|
|
28165
|
+
## The tree: L0 \u2192 L5
|
|
28166
|
+
|
|
28167
|
+
- **L0 System** \u2014 vision, boundaries, global requirements, target language.
|
|
28168
|
+
- **L1 Subsystem** \u2014 an isolated service, its \`publicInterfaces\`, its trusted links.
|
|
28169
|
+
- **L2 Component** \u2014 ONE building block (\`Portal\`, \`Orchestrator\`, \`Supervisor\`,
|
|
28170
|
+
\`Actor\`, \`Store\`, \`Index\`, \`Query\`, \`Registry\`, \`Adapter\`, \`Observer\`, and
|
|
28171
|
+
\`View\` for UIs) or pattern (\`Repository\`, and \`FeatureComponent\` or
|
|
28172
|
+
\`RouterComponent\` for UIs), plus \`owns\` (a pattern's private members) and
|
|
28173
|
+
\`dependsOn\` (collaborators). Logic is an \`Orchestrator\` whose \`dependencyClass\`
|
|
28174
|
+
(\`pure\` or \`read\`; unset, a workflow) bounds what it may depend on; a gateway is
|
|
28175
|
+
a \`Portal\` with the \`gateway\` variant. \`Specialist\` and \`Gateway\` are retired.
|
|
28176
|
+
- **L3 Interface** \u2014 the contract: method signatures with structured \`params\`,
|
|
28177
|
+
plus wire \`endpoint\` bindings for a Portal's methods.
|
|
28178
|
+
- **L4 Implementation** \u2014 a concrete realization of one L3 contract: narrative
|
|
28179
|
+
detail level, bound \`technologies\` (the vendor swap seam), optional \`sourcePath\`.
|
|
28180
|
+
- **L5 Narrative** \u2014 each method's logic as a FLAT numbered step list; \`call\` steps
|
|
28181
|
+
resolve to real dependency methods, and flow steps jump by step number.
|
|
28182
|
+
|
|
28183
|
+
These stereotypes are not labels \u2014 they carry enforced dependency rules (e.g. a
|
|
28184
|
+
Portal may not depend on a Store; state lives in a Store, never as fields inside
|
|
28185
|
+
an Orchestrator). \`sdd_validate_tree\` is the gate, and it is the authority on
|
|
28186
|
+
whether a design is legal.
|
|
28187
|
+
|
|
28188
|
+
## Authoring order
|
|
28189
|
+
|
|
28190
|
+
1. \`sdd_initialize_system\` (L0), then \`sdd_add_subsystem\` (L1).
|
|
28191
|
+
2. Then, **one subsystem at a time**: \`sdd_add_component\` (L2) \u2192 \`sdd_define_interface\`
|
|
28192
|
+
(L3) \u2192 \`sdd_set_endpoints\` for Portals \u2192 \`sdd_write_narrative\` (L4+L5).
|
|
28193
|
+
3. Run \`sdd_validate_tree\` as you go \u2014 early and per subsystem, not once at the end.
|
|
28194
|
+
\`sdd_get_status\` reports completeness; \`sdd_get_spec\` reads any spec back as JSON.
|
|
28195
|
+
4. Edit granularly with \`sdd_update_spec\` (it renumbers narrative steps and
|
|
28196
|
+
relocates jump targets for you).
|
|
28197
|
+
|
|
28198
|
+
## The tool schemas are the field reference
|
|
28199
|
+
|
|
28200
|
+
Every \`sdd_*\` tool's input schema is self-describing and authoritative \u2014 read the
|
|
28201
|
+
schema rather than guessing field names, and prefer structured \`params\` over prose
|
|
28202
|
+
signatures. Specs are ${profile === null ? "validated against the project's configured profile" : `governed by the **\`${profile}\`** profile`}, which decides which stereotypes are legal
|
|
28203
|
+
here, which narrative flow constructs the target language supports, and which
|
|
28204
|
+
extra rules apply. A subsystem may override the profile for itself.
|
|
28205
|
+
|
|
28206
|
+
## Loaded extension packs
|
|
28207
|
+
|
|
28208
|
+
${packNames.length === 0 ? "None \u2014 only wairon's built-in doctrine applies." : `${packNames.map((n) => `\`${n}\``).join(", ")} \u2014 these inject profiles, language tables, patterns, and
|
|
28209
|
+
additional conformance rules. Their doctrine is enforced by the same gate.`}
|
|
28210
|
+
|
|
28211
|
+
${variants.length === 0 ? "" : `## Component variants in play
|
|
28212
|
+
|
|
28213
|
+
This project defines variants \u2014 named, base-anchored kinds carrying implementation
|
|
28214
|
+
guidance, so every component of the same kind is built alike: ${variants.map((v) => `\`${v.id}\` (a kind of ${v.base})`).join(", ")}.
|
|
28215
|
+
Tag a component with \`variant\` where it fits, and read a component's resolved
|
|
28216
|
+
guidance and its same-variant siblings from \`sdd_get_spec\` (kind: component).
|
|
28217
|
+
|
|
28218
|
+
`}## Read the skills BEFORE authoring
|
|
28219
|
+
|
|
28220
|
+
This server publishes its skills as MCP resources. **Read \`wairon-skill://sdd-architect\`
|
|
28221
|
+
before you design anything**, and the others when their turn comes \u2014 they carry the
|
|
28222
|
+
architecture standard, the narrative step reference, and the review checklist that
|
|
28223
|
+
this briefing only points at:
|
|
28224
|
+
|
|
28225
|
+
${skillPointers(skills)}
|
|
28226
|
+
|
|
28227
|
+
## Delegating implementation
|
|
28228
|
+
|
|
28229
|
+
Delegate component work from a LIVE agent brief, never a generated file: discover
|
|
28230
|
+
agents via \`listAgents\` (or the \`wairon-agent://\` resources), fetch the target's
|
|
28231
|
+
brief with \`sdd_get_agent_brief\`, and spawn a scoped subagent from it \u2014 its
|
|
28232
|
+
\`instructions\` as prompt, its \`ownedPaths\` as write fence. Briefs are composed
|
|
28233
|
+
from the current spec tree on every call, so a re-lock never requires a session
|
|
28234
|
+
restart \u2014 fetch fresh per delegation. \`wairon-skill://sdd-delegate\` carries the
|
|
28235
|
+
full flow.`;
|
|
28236
|
+
}
|
|
28237
|
+
function buildServerInstructions() {
|
|
28238
|
+
const profile = governingProfile();
|
|
28239
|
+
let skills = [];
|
|
28240
|
+
let blocks = [];
|
|
28241
|
+
let packNames = [];
|
|
28242
|
+
const variants = [];
|
|
28243
|
+
try {
|
|
28244
|
+
skills = listSkillResources();
|
|
28245
|
+
const extensions = loadProjectExtensions2();
|
|
28246
|
+
blocks = extensions.instructions;
|
|
28247
|
+
packNames = extensions.packNames;
|
|
28248
|
+
} catch {
|
|
28249
|
+
}
|
|
28250
|
+
const sections = [waironInstructions(profile, packNames, skills, variants)];
|
|
28251
|
+
for (const block of blocks) {
|
|
28252
|
+
if (!blockApplies(block, profile)) continue;
|
|
28253
|
+
sections.push(`## From pack "${block.pack}"
|
|
28254
|
+
|
|
28255
|
+
${block.text.trim()}`);
|
|
28256
|
+
}
|
|
28257
|
+
return sections.join("\n\n");
|
|
28258
|
+
}
|
|
28259
|
+
var init_instructions = __esm({
|
|
28260
|
+
"src/core/instructions.ts"() {
|
|
28261
|
+
"use strict";
|
|
28262
|
+
init_skills();
|
|
28263
|
+
}
|
|
28264
|
+
});
|
|
28265
|
+
|
|
28266
|
+
// src/core/skills.ts
|
|
28267
|
+
function loadProjectExtensions2() {
|
|
28268
|
+
return loadProjectExtensions();
|
|
28269
|
+
}
|
|
28270
|
+
function loadProjectConfig4() {
|
|
28271
|
+
return loadProjectConfig();
|
|
28272
|
+
}
|
|
28273
|
+
function packSkillId(skill) {
|
|
28274
|
+
return `${packNamespace(skill.pack)}-${skill.id}`;
|
|
28275
|
+
}
|
|
28276
|
+
function packNamespace(pack) {
|
|
28277
|
+
return pack.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
28278
|
+
}
|
|
28279
|
+
function extensionsFor(builtin, packSkills) {
|
|
28280
|
+
return packSkills.filter((s) => s.extends === builtin && fs20.existsSync(s.sourcePath));
|
|
28281
|
+
}
|
|
28282
|
+
function composeBuiltinSkill(name, packSkills) {
|
|
28283
|
+
const srcPath = skillTemplatePath(name);
|
|
28284
|
+
const base = fs20.existsSync(srcPath) ? fs20.readFileSync(srcPath, "utf-8") : "";
|
|
28285
|
+
const sections = extensionsFor(name, packSkills);
|
|
28286
|
+
if (sections.length === 0) return base;
|
|
28287
|
+
const parts = [base.trimEnd()];
|
|
28288
|
+
for (const section of sections) {
|
|
28289
|
+
const body = fs20.readFileSync(section.sourcePath, "utf-8").replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
|
|
28290
|
+
parts.push(`## Platform: ${section.pack}`, body.trim());
|
|
28291
|
+
}
|
|
28292
|
+
return `${parts.join("\n\n")}
|
|
28293
|
+
`;
|
|
28294
|
+
}
|
|
28295
|
+
function readFrontmatter(raw, fallbackName) {
|
|
28296
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
|
|
28297
|
+
const block = match ? match[1] : "";
|
|
28298
|
+
const field = (key) => {
|
|
28299
|
+
const m = new RegExp(`^${key}:\\s*(.*)$`, "m").exec(block);
|
|
28300
|
+
return m ? m[1].trim() : "";
|
|
28301
|
+
};
|
|
28302
|
+
return { name: field("name") || fallbackName, description: field("description") };
|
|
28303
|
+
}
|
|
28304
|
+
function builtinSkillsDir() {
|
|
28305
|
+
return path31.resolve(__dirname, "..", "templates", "skills");
|
|
28306
|
+
}
|
|
28307
|
+
function skillTemplatePath(name) {
|
|
28308
|
+
return path31.join(builtinSkillsDir(), `${name}.md`);
|
|
28309
|
+
}
|
|
28310
|
+
function skillDestPath(type, destDir, name) {
|
|
28311
|
+
if (type === "claude" || type === "codex" || type === "gemini" || type === "agy") {
|
|
28312
|
+
return path31.join(destDir, name, "SKILL.md");
|
|
28313
|
+
}
|
|
28314
|
+
return path31.join(destDir, `${name}.md`);
|
|
28315
|
+
}
|
|
28316
|
+
function skillsDirForTarget(type) {
|
|
28317
|
+
switch (type) {
|
|
28318
|
+
case "claude":
|
|
28319
|
+
return fromProjectRoot(".claude", "skills");
|
|
28320
|
+
case "gemini":
|
|
28321
|
+
// Gemini-based Antigravity
|
|
28322
|
+
case "agy":
|
|
28323
|
+
case "codex":
|
|
28324
|
+
return fromProjectRoot(".agents", "skills");
|
|
28325
|
+
case "cursor":
|
|
28326
|
+
return fromProjectRoot(".cursor", "skills");
|
|
28327
|
+
default:
|
|
28328
|
+
return null;
|
|
28329
|
+
}
|
|
28330
|
+
}
|
|
28331
|
+
function listSkillNames() {
|
|
28332
|
+
return [...SKILL_NAMES];
|
|
28333
|
+
}
|
|
28334
|
+
function exportSddSkills(targetTypes) {
|
|
28335
|
+
const types = targetTypes ?? activeTargetTypes2();
|
|
28336
|
+
const destinations = [];
|
|
28337
|
+
const skipped = [];
|
|
28338
|
+
let fileCount = 0;
|
|
28339
|
+
const packSkills = loadProjectExtensions2().skills;
|
|
28340
|
+
for (const type of types) {
|
|
28341
|
+
const destDir = skillsDirForTarget(type);
|
|
28342
|
+
if (!destDir) {
|
|
28343
|
+
skipped.push(type);
|
|
28344
|
+
continue;
|
|
28345
|
+
}
|
|
28346
|
+
ensureDir(destDir);
|
|
28347
|
+
destinations.push(destDir);
|
|
28348
|
+
for (const name of SKILL_NAMES) {
|
|
28349
|
+
if (!fs20.existsSync(skillTemplatePath(name))) continue;
|
|
28350
|
+
const content = composeBuiltinSkill(name, packSkills.filter((s) => s.targets.includes(type)));
|
|
28351
|
+
const destPath = skillDestPath(type, destDir, name);
|
|
28352
|
+
ensureDir(path31.dirname(destPath));
|
|
28353
|
+
fs20.writeFileSync(destPath, content, "utf-8");
|
|
28354
|
+
fileCount++;
|
|
28355
|
+
}
|
|
28356
|
+
for (const skill of packSkills) {
|
|
28357
|
+
if (skill.extends !== void 0) continue;
|
|
28358
|
+
if (!skill.targets.includes(type)) continue;
|
|
28359
|
+
if (!fs20.existsSync(skill.sourcePath)) continue;
|
|
28360
|
+
const id = packSkillId(skill);
|
|
28361
|
+
const content = fs20.readFileSync(skill.sourcePath, "utf-8");
|
|
28362
|
+
const destPath = skillDestPath(type, destDir, id);
|
|
28363
|
+
ensureDir(path31.dirname(destPath));
|
|
28364
|
+
fs20.writeFileSync(destPath, content, "utf-8");
|
|
28365
|
+
fileCount++;
|
|
28366
|
+
}
|
|
28367
|
+
}
|
|
28368
|
+
return { destinations, fileCount, skipped };
|
|
28369
|
+
}
|
|
28370
|
+
function checkSkillFreshness(type) {
|
|
28371
|
+
const dir = skillsDirForTarget(type);
|
|
28372
|
+
const result = { dir, missing: [], stale: [], ok: [] };
|
|
28373
|
+
if (!dir) return result;
|
|
28374
|
+
const packSkills = loadProjectExtensions2().skills.filter((s) => s.targets.includes(type));
|
|
28375
|
+
for (const name of SKILL_NAMES) {
|
|
28376
|
+
const destPath = skillDestPath(type, dir, name);
|
|
28377
|
+
if (!fs20.existsSync(destPath)) {
|
|
28378
|
+
result.missing.push(name);
|
|
28379
|
+
continue;
|
|
28380
|
+
}
|
|
28381
|
+
const want = composeBuiltinSkill(name, packSkills);
|
|
28382
|
+
const have = fs20.readFileSync(destPath, "utf-8");
|
|
28383
|
+
if (have === want) result.ok.push(name);
|
|
28384
|
+
else result.stale.push(name);
|
|
28385
|
+
}
|
|
28386
|
+
return result;
|
|
28387
|
+
}
|
|
28388
|
+
function activeTargetTypes2() {
|
|
28389
|
+
const config = loadProjectConfig4();
|
|
28390
|
+
if (!config) return [];
|
|
28391
|
+
return activeTargetTypes(config);
|
|
28392
|
+
}
|
|
28393
|
+
function readSkillFrontmatter(name) {
|
|
28394
|
+
return readFrontmatter(fs20.readFileSync(skillTemplatePath(name), "utf-8"), name);
|
|
28395
|
+
}
|
|
28396
|
+
function listSkillResources() {
|
|
28397
|
+
const builtin = RESOURCE_SKILL_IDS.map((id) => {
|
|
28398
|
+
const fm = readSkillFrontmatter(id);
|
|
28399
|
+
return {
|
|
28400
|
+
id,
|
|
28401
|
+
name: fm.name,
|
|
28402
|
+
description: fm.description,
|
|
28403
|
+
version: WAIRON_VERSION,
|
|
28404
|
+
resourceUri: `${SKILL_RESOURCE_SCHEME}://${id}`,
|
|
28405
|
+
defaultForHostedMcp: true
|
|
28406
|
+
};
|
|
28407
|
+
});
|
|
28408
|
+
const pack = loadProjectExtensions2().skills.filter((s) => s.extends === void 0 && fs20.existsSync(s.sourcePath)).map((skill) => {
|
|
28409
|
+
const id = packSkillId(skill);
|
|
28410
|
+
const fm = readFrontmatter(fs20.readFileSync(skill.sourcePath, "utf-8"), id);
|
|
28411
|
+
return {
|
|
28412
|
+
id,
|
|
28413
|
+
name: fm.name,
|
|
28414
|
+
description: fm.description,
|
|
28415
|
+
version: skill.packVersion ?? WAIRON_VERSION,
|
|
28416
|
+
resourceUri: `${SKILL_RESOURCE_SCHEME}://${id}`,
|
|
28417
|
+
defaultForHostedMcp: true
|
|
28418
|
+
};
|
|
28419
|
+
});
|
|
28420
|
+
return [...builtin, ...pack];
|
|
28421
|
+
}
|
|
28422
|
+
function readSkillResource(resourceId) {
|
|
28423
|
+
const packSkills = loadProjectExtensions2().skills;
|
|
28424
|
+
if (SKILL_NAMES.includes(resourceId)) return composeBuiltinSkill(resourceId, packSkills);
|
|
28425
|
+
const packSkill = packSkills.find((s) => s.extends === void 0 && packSkillId(s) === resourceId);
|
|
28426
|
+
if (packSkill) return fs20.readFileSync(packSkill.sourcePath, "utf-8");
|
|
28427
|
+
return fs20.readFileSync(skillTemplatePath(resourceId), "utf-8");
|
|
28428
|
+
}
|
|
28429
|
+
function listResources() {
|
|
28430
|
+
return listSkillResources();
|
|
28431
|
+
}
|
|
28432
|
+
function buildServerInstructions2() {
|
|
28433
|
+
return buildServerInstructions();
|
|
28434
|
+
}
|
|
28435
|
+
function readResource(resourceId) {
|
|
28436
|
+
const known = listSkillResources().some((d) => d.id === resourceId);
|
|
28437
|
+
if (!known) throw new SkillResourceNotFoundError(resourceId);
|
|
28438
|
+
return readSkillResource(resourceId);
|
|
28439
|
+
}
|
|
28440
|
+
var path31, fs20, SKILL_NAMES, SKILL_RESOURCE_SCHEME, RESOURCE_SKILL_IDS, SkillResourceNotFoundError;
|
|
28441
|
+
var init_skills = __esm({
|
|
28442
|
+
"src/core/skills.ts"() {
|
|
28443
|
+
"use strict";
|
|
28444
|
+
path31 = __toESM(require("path"));
|
|
28445
|
+
fs20 = __toESM(require("fs"));
|
|
28446
|
+
init_fs();
|
|
28447
|
+
init_defaults();
|
|
28448
|
+
init_project();
|
|
28449
|
+
init_extensions();
|
|
28450
|
+
init_instructions();
|
|
28451
|
+
init_core();
|
|
28452
|
+
SKILL_NAMES = ["sdd-architect", "sdd-narrative", "sdd-auditor", "sdd-implement", "sdd-delegate"];
|
|
28453
|
+
SKILL_RESOURCE_SCHEME = "wairon-skill";
|
|
28454
|
+
RESOURCE_SKILL_IDS = [...SKILL_NAMES].sort();
|
|
28455
|
+
SkillResourceNotFoundError = class extends Error {
|
|
28456
|
+
constructor(resourceId) {
|
|
28457
|
+
super(`Unknown SDD skill resource id: "${resourceId}".`);
|
|
28458
|
+
this.name = "SkillResourceNotFoundError";
|
|
28459
|
+
}
|
|
28460
|
+
};
|
|
28594
28461
|
}
|
|
28595
28462
|
});
|
|
28596
28463
|
|
|
@@ -30134,7 +30001,9 @@ function createMcpServer(options = {}) {
|
|
|
30134
30001
|
subsystem,
|
|
30135
30002
|
recursive: recursive ?? true
|
|
30136
30003
|
});
|
|
30137
|
-
|
|
30004
|
+
const family = statusFamilyContext();
|
|
30005
|
+
const verdict = approvalVerdict();
|
|
30006
|
+
return text(`${family}${report2}${verdict.text}`);
|
|
30138
30007
|
} catch (e) {
|
|
30139
30008
|
return errText(String(e));
|
|
30140
30009
|
}
|
|
@@ -30333,7 +30202,7 @@ var init_server = __esm({
|
|
|
30333
30202
|
init_fs();
|
|
30334
30203
|
init_yaml();
|
|
30335
30204
|
init_errors();
|
|
30336
|
-
|
|
30205
|
+
init_core();
|
|
30337
30206
|
init_core();
|
|
30338
30207
|
init_defaults();
|
|
30339
30208
|
init_narrative_labels();
|
|
@@ -30686,7 +30555,7 @@ async function runMcpInstall(options = {}) {
|
|
|
30686
30555
|
const desiredEntry = options.hostedUrl ? hostedMcpEntry(options) : useDirectNode ? { command: "node", args: [scriptPath, "mcp", "serve"], env } : { command: "wairon", args: ["mcp", "serve"], env };
|
|
30687
30556
|
const existingEntry = mcpServers["wairon"];
|
|
30688
30557
|
if (existingEntry && JSON.stringify(existingEntry) === JSON.stringify(desiredEntry)) {
|
|
30689
|
-
logger.info(`wairon MCP server already registered (up to date) for ${agentLabel} in ${
|
|
30558
|
+
logger.info(`wairon MCP server already registered (up to date) for ${agentLabel} in ${import_chalk4.default.gray(settingsPath)}.`);
|
|
30690
30559
|
continue;
|
|
30691
30560
|
}
|
|
30692
30561
|
const wasStale = !!existingEntry;
|
|
@@ -30694,7 +30563,7 @@ async function runMcpInstall(options = {}) {
|
|
|
30694
30563
|
settings["mcpServers"] = mcpServers;
|
|
30695
30564
|
if (!fs22.existsSync(configBase)) fs22.mkdirSync(configBase, { recursive: true });
|
|
30696
30565
|
fs22.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
30697
|
-
logger.success(`wairon MCP server ${wasStale ? "updated (was stale)" : "registered"} for ${agentLabel} in ${
|
|
30566
|
+
logger.success(`wairon MCP server ${wasStale ? "updated (was stale)" : "registered"} for ${agentLabel} in ${import_chalk4.default.cyan(settingsPath)}.`);
|
|
30698
30567
|
logger.blank();
|
|
30699
30568
|
logger.info("AI tools using this config will have access to these wairon tools:");
|
|
30700
30569
|
logger.info(" listAgents \xB7 getAgent \xB7 listDomains \xB7 validateTopology \xB7 getProjectConfig");
|
|
@@ -30702,13 +30571,13 @@ async function runMcpInstall(options = {}) {
|
|
|
30702
30571
|
logger.info(" sdd_write_narrative \xB7 sdd_validate_tree \xB7 sdd_get_status");
|
|
30703
30572
|
logger.blank();
|
|
30704
30573
|
const restartApp = backend === "gemini" ? "Antigravity CLI (agy)" : "claude";
|
|
30705
|
-
logger.info(`Restart ${
|
|
30574
|
+
logger.info(`Restart ${import_chalk4.default.bold(restartApp)} (or reload MCP servers) to activate.`);
|
|
30706
30575
|
if (backend === "claude" && !useGlobal) {
|
|
30707
30576
|
logger.warn('Claude shows project .mcp.json servers as "\u23F8 Pending approval" \u2014 approve wairon once on next launch (or run `claude` and accept the trust prompt).');
|
|
30708
30577
|
}
|
|
30709
30578
|
if (backend === "gemini" && !useGlobal) {
|
|
30710
30579
|
logger.warn("Note: Antigravity (agy) reads MCP servers from its global ~/.gemini/antigravity-cli/mcp_config.json, not this project file.");
|
|
30711
|
-
logger.warn("If the agent cannot see the sdd_* tools, run: " +
|
|
30580
|
+
logger.warn("If the agent cannot see the sdd_* tools, run: " + import_chalk4.default.bold("wairon mcp install --backend gemini --global"));
|
|
30712
30581
|
}
|
|
30713
30582
|
logger.blank();
|
|
30714
30583
|
}
|
|
@@ -30722,7 +30591,7 @@ async function runMcpStatus() {
|
|
|
30722
30591
|
const geminiProject = path33.join(process.cwd(), ".gemini", "settings.json");
|
|
30723
30592
|
const geminiGlobal = path33.join(geminiGlobalDir(), "antigravity-cli", "mcp_config.json");
|
|
30724
30593
|
logger.blank();
|
|
30725
|
-
logger.info(`${
|
|
30594
|
+
logger.info(`${import_chalk4.default.bold("wairon MCP Server")}`);
|
|
30726
30595
|
logger.blank();
|
|
30727
30596
|
const checks = [
|
|
30728
30597
|
{ label: "Claude (project)", filePath: claudeProject, fallbackName: ".mcp.json" },
|
|
@@ -30732,28 +30601,28 @@ async function runMcpStatus() {
|
|
|
30732
30601
|
];
|
|
30733
30602
|
for (const { label, filePath, fallbackName } of checks) {
|
|
30734
30603
|
if (!fs22.existsSync(filePath)) {
|
|
30735
|
-
console.log(` ${label}: ${
|
|
30604
|
+
console.log(` ${label}: ${import_chalk4.default.gray(`${fallbackName} not found`)}`);
|
|
30736
30605
|
continue;
|
|
30737
30606
|
}
|
|
30738
30607
|
try {
|
|
30739
30608
|
const s = JSON.parse(fs22.readFileSync(filePath, "utf8"));
|
|
30740
30609
|
const registered = !!s["mcpServers"]?.["wairon"];
|
|
30741
|
-
const mark = registered ?
|
|
30742
|
-
console.log(` ${label}: ${mark} ${
|
|
30610
|
+
const mark = registered ? import_chalk4.default.green("\u2713 registered") : import_chalk4.default.gray("not registered");
|
|
30611
|
+
console.log(` ${label}: ${mark} ${import_chalk4.default.gray(filePath)}`);
|
|
30743
30612
|
} catch {
|
|
30744
|
-
console.log(` ${label}: ${
|
|
30613
|
+
console.log(` ${label}: ${import_chalk4.default.red("parse error")} ${import_chalk4.default.gray(filePath)}`);
|
|
30745
30614
|
}
|
|
30746
30615
|
}
|
|
30747
30616
|
logger.blank();
|
|
30748
|
-
logger.info(`Project: ${
|
|
30617
|
+
logger.info(`Project: ${import_chalk4.default.bold(projectConfig.name)}`);
|
|
30749
30618
|
logger.blank();
|
|
30750
|
-
logger.info(`To register for Claude: ${
|
|
30751
|
-
logger.info(`To register for Antigravity (agy): ${
|
|
30752
|
-
logger.info(`To start manually: ${
|
|
30619
|
+
logger.info(`To register for Claude: ${import_chalk4.default.bold("wairon mcp install --backend claude")}`);
|
|
30620
|
+
logger.info(`To register for Antigravity (agy): ${import_chalk4.default.bold("wairon mcp install --backend gemini")}`);
|
|
30621
|
+
logger.info(`To start manually: ${import_chalk4.default.bold("wairon mcp serve")}`);
|
|
30753
30622
|
logger.blank();
|
|
30754
30623
|
const mcpDir = aiDir("mcp");
|
|
30755
30624
|
if (fs22.existsSync(mcpDir)) {
|
|
30756
|
-
logger.info(`MCP state dir: ${
|
|
30625
|
+
logger.info(`MCP state dir: ${import_chalk4.default.gray(mcpDir)}`);
|
|
30757
30626
|
}
|
|
30758
30627
|
}
|
|
30759
30628
|
function hostedMcpEntry(options) {
|
|
@@ -30774,7 +30643,7 @@ function removeLegacyGlobalPlugin() {
|
|
|
30774
30643
|
try {
|
|
30775
30644
|
if (fs22.existsSync(pluginDir)) {
|
|
30776
30645
|
fs22.rmSync(pluginDir, { recursive: true, force: true });
|
|
30777
|
-
logger.info(`Removed legacy global Antigravity plugin at ${
|
|
30646
|
+
logger.info(`Removed legacy global Antigravity plugin at ${import_chalk4.default.gray(pluginDir)} (it collides with the wairon MCP server).`);
|
|
30778
30647
|
return true;
|
|
30779
30648
|
}
|
|
30780
30649
|
} catch (e) {
|
|
@@ -30782,14 +30651,14 @@ function removeLegacyGlobalPlugin() {
|
|
|
30782
30651
|
}
|
|
30783
30652
|
return false;
|
|
30784
30653
|
}
|
|
30785
|
-
var fs22, os7, path33,
|
|
30654
|
+
var fs22, os7, path33, import_chalk4;
|
|
30786
30655
|
var init_mcp = __esm({
|
|
30787
30656
|
"src/commands/mcp.ts"() {
|
|
30788
30657
|
"use strict";
|
|
30789
30658
|
fs22 = __toESM(require("fs"));
|
|
30790
30659
|
os7 = __toESM(require("os"));
|
|
30791
30660
|
path33 = __toESM(require("path"));
|
|
30792
|
-
|
|
30661
|
+
import_chalk4 = __toESM(require("chalk"));
|
|
30793
30662
|
init_logger();
|
|
30794
30663
|
init_paths();
|
|
30795
30664
|
init_core();
|
|
@@ -31023,7 +30892,7 @@ function isSupportedAlias(name) {
|
|
|
31023
30892
|
|
|
31024
30893
|
// src/commands/init.ts
|
|
31025
30894
|
var path34 = __toESM(require("path"));
|
|
31026
|
-
var
|
|
30895
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
31027
30896
|
var import_inquirer = __toESM(require("inquirer"));
|
|
31028
30897
|
init_logger();
|
|
31029
30898
|
init_errors();
|
|
@@ -31032,7 +30901,7 @@ init_yaml();
|
|
|
31032
30901
|
init_paths();
|
|
31033
30902
|
|
|
31034
30903
|
// src/commands/subsystem.ts
|
|
31035
|
-
var
|
|
30904
|
+
var path30 = __toESM(require("path"));
|
|
31036
30905
|
init_logger();
|
|
31037
30906
|
init_errors();
|
|
31038
30907
|
init_fs();
|
|
@@ -31048,7 +30917,7 @@ function exportSpecTree2(includeDerived, allowPartial) {
|
|
|
31048
30917
|
function importSpecTree2(archive, options) {
|
|
31049
30918
|
return importSpecTree(archive, options);
|
|
31050
30919
|
}
|
|
31051
|
-
function
|
|
30920
|
+
function loadProjectConfig3() {
|
|
31052
30921
|
return loadProjectConfig();
|
|
31053
30922
|
}
|
|
31054
30923
|
function createProjectConfig2(config) {
|
|
@@ -31132,6 +31001,15 @@ function reinjectLocalGuides2(projectRoot2, targets) {
|
|
|
31132
31001
|
function readStampVersion2(content) {
|
|
31133
31002
|
return readStampVersion(content);
|
|
31134
31003
|
}
|
|
31004
|
+
function syncContextFiles2() {
|
|
31005
|
+
return syncContextFiles();
|
|
31006
|
+
}
|
|
31007
|
+
function hasContext2() {
|
|
31008
|
+
return hasContext();
|
|
31009
|
+
}
|
|
31010
|
+
function derivedDocPaths2() {
|
|
31011
|
+
return derivedDocPaths();
|
|
31012
|
+
}
|
|
31135
31013
|
async function runSubsystemAdd(id, options = {}) {
|
|
31136
31014
|
logger.header("wairon subsystem add");
|
|
31137
31015
|
if (!projectConfigExists2()) {
|
|
@@ -31161,9 +31039,9 @@ async function runSubsystemAdd(id, options = {}) {
|
|
|
31161
31039
|
updatedAt: now
|
|
31162
31040
|
};
|
|
31163
31041
|
createChainedSubsystem2(subsystem, displayName);
|
|
31164
|
-
const childDir =
|
|
31042
|
+
const childDir = path30.resolve(getProjectRoot(), options.projectPath);
|
|
31165
31043
|
logger.success(`Added external subsystem "${id}" \u2192 ${options.projectPath}`);
|
|
31166
|
-
logger.info(`Scaffolded child project at ${
|
|
31044
|
+
logger.info(`Scaffolded child project at ${path30.relative(process.cwd(), childDir) || "."}`);
|
|
31167
31045
|
logger.info(`Design its spec tree from this parent using namespaced ids (e.g. ${id}::<component>).`);
|
|
31168
31046
|
}
|
|
31169
31047
|
async function runSubsystemMove(id, options = {}) {
|
|
@@ -31186,9 +31064,9 @@ async function runSubsystemExternalize(id, options = {}) {
|
|
|
31186
31064
|
throw new WaironError("--project-path (the subproject destination) is required.");
|
|
31187
31065
|
}
|
|
31188
31066
|
externalizeSubsystem(id, options.projectPath);
|
|
31189
|
-
const childDir =
|
|
31067
|
+
const childDir = path30.resolve(getProjectRoot(), options.projectPath);
|
|
31190
31068
|
logger.success(`Externalized subsystem "${id}" \u2192 ${options.projectPath}`);
|
|
31191
|
-
logger.info(`Moved its specs into ${
|
|
31069
|
+
logger.info(`Moved its specs into ${path30.relative(process.cwd(), childDir) || "."} (now a standalone subproject).`);
|
|
31192
31070
|
logger.info("Move the source code there yourself, then run `wairon validate` to confirm the tree.");
|
|
31193
31071
|
}
|
|
31194
31072
|
async function runSubsystemInternalize(id) {
|
|
@@ -31240,7 +31118,6 @@ async function runSkillsInstall() {
|
|
|
31240
31118
|
// src/commands/init.ts
|
|
31241
31119
|
init_defaults();
|
|
31242
31120
|
init_project();
|
|
31243
|
-
init_context();
|
|
31244
31121
|
async function runInit(options = {}) {
|
|
31245
31122
|
logger.header("wairon init");
|
|
31246
31123
|
const cwd = process.cwd();
|
|
@@ -31427,10 +31304,10 @@ async function runInitInteractive() {
|
|
|
31427
31304
|
logger.blank();
|
|
31428
31305
|
logger.header("Init summary");
|
|
31429
31306
|
logger.blank();
|
|
31430
|
-
console.log(` ${
|
|
31431
|
-
console.log(` ${
|
|
31432
|
-
console.log(` ${
|
|
31433
|
-
console.log(` ${
|
|
31307
|
+
console.log(` ${import_chalk5.default.bold("Project:")} ${projectName}`);
|
|
31308
|
+
console.log(` ${import_chalk5.default.bold("Profile:")} ${projectType}`);
|
|
31309
|
+
console.log(` ${import_chalk5.default.bold("Targets:")} ${targets.map((t) => t.type).join(", ")}`);
|
|
31310
|
+
console.log(` ${import_chalk5.default.bold("Guide:")} Auto-inject local AI rules card (${selectedTypes.filter((t) => ["claude", "gemini", "agy"].includes(t)).join(", ") || "none"})`);
|
|
31434
31311
|
logger.blank();
|
|
31435
31312
|
const { confirmed } = await import_inquirer.default.prompt([
|
|
31436
31313
|
{
|
|
@@ -31531,7 +31408,7 @@ async function executeInit(projectName, projectConfig, guidePlan) {
|
|
|
31531
31408
|
logger.warn(`Failed to automatically register MCP server for Antigravity: ${err}`);
|
|
31532
31409
|
}
|
|
31533
31410
|
}
|
|
31534
|
-
|
|
31411
|
+
syncContextFiles2();
|
|
31535
31412
|
logger.verbose("Context files seeded in .wai/context/");
|
|
31536
31413
|
try {
|
|
31537
31414
|
ensureProjectInitialized2(projectName);
|
|
@@ -31720,7 +31597,6 @@ init_logger();
|
|
|
31720
31597
|
init_paths();
|
|
31721
31598
|
init_errors();
|
|
31722
31599
|
init_base();
|
|
31723
|
-
init_context();
|
|
31724
31600
|
init_fs();
|
|
31725
31601
|
init_project();
|
|
31726
31602
|
var WAIRON_AGENT_FILE = /-(owner|implementer|architect)\.md$/;
|
|
@@ -31770,7 +31646,7 @@ async function runGenerate(options = {}) {
|
|
|
31770
31646
|
}
|
|
31771
31647
|
async function generateLayer(options = {}) {
|
|
31772
31648
|
assertProjectInitialized();
|
|
31773
|
-
const projectConfig =
|
|
31649
|
+
const projectConfig = loadProjectConfig3();
|
|
31774
31650
|
if (!projectConfig) throw new ProjectNotInitializedError();
|
|
31775
31651
|
const agents = resolveAgentTopology2();
|
|
31776
31652
|
if (agents.length === 0) {
|
|
@@ -31789,8 +31665,8 @@ async function generateLayer(options = {}) {
|
|
|
31789
31665
|
}
|
|
31790
31666
|
}
|
|
31791
31667
|
if (options.dryRun) return;
|
|
31792
|
-
if (
|
|
31793
|
-
|
|
31668
|
+
if (hasContext2()) {
|
|
31669
|
+
syncContextFiles2();
|
|
31794
31670
|
logger.verbose("Context files synced.");
|
|
31795
31671
|
}
|
|
31796
31672
|
if (pathExists(AI_PATHS.specsSystem())) {
|
|
@@ -31869,6 +31745,7 @@ init_logger();
|
|
|
31869
31745
|
init_defaults();
|
|
31870
31746
|
var path36 = __toESM(require("path"));
|
|
31871
31747
|
init_core();
|
|
31748
|
+
init_lock();
|
|
31872
31749
|
init_fs();
|
|
31873
31750
|
init_validation();
|
|
31874
31751
|
function checkApproval(strict) {
|
|
@@ -31963,7 +31840,7 @@ async function runLock(options = {}, gate) {
|
|
|
31963
31840
|
}
|
|
31964
31841
|
|
|
31965
31842
|
// src/commands/validate.ts
|
|
31966
|
-
var
|
|
31843
|
+
var import_chalk6 = __toESM(require("chalk"));
|
|
31967
31844
|
init_logger();
|
|
31968
31845
|
init_agent_resolver();
|
|
31969
31846
|
init_paths();
|
|
@@ -32033,7 +31910,7 @@ async function runValidate(options = {}) {
|
|
|
32033
31910
|
logger.success("Registry is valid.");
|
|
32034
31911
|
} else {
|
|
32035
31912
|
for (const issue2 of regResult.issues) {
|
|
32036
|
-
const prefix = issue2.agentId ?
|
|
31913
|
+
const prefix = issue2.agentId ? import_chalk6.default.gray(`[${issue2.agentId}] `) : "";
|
|
32037
31914
|
if (issue2.severity === "error") {
|
|
32038
31915
|
logger.error(`${prefix}[${issue2.code}] ${issue2.message}`);
|
|
32039
31916
|
hasErrors = true;
|
|
@@ -32068,7 +31945,7 @@ async function runValidate(options = {}) {
|
|
|
32068
31945
|
let skippedErrors = 0;
|
|
32069
31946
|
let skippedWarnings = 0;
|
|
32070
31947
|
for (const issue2 of sddResult.issues) {
|
|
32071
|
-
const prefix = issue2.specId ?
|
|
31948
|
+
const prefix = issue2.specId ? import_chalk6.default.gray(`[${issue2.specId}] `) : "";
|
|
32072
31949
|
if (issue2.severity === "error") {
|
|
32073
31950
|
hasErrors = true;
|
|
32074
31951
|
if (errorCount < MAX_PRINT) {
|
|
@@ -32102,12 +31979,12 @@ async function runValidate(options = {}) {
|
|
|
32102
31979
|
const summary = carriedDebtSummary(projectConfig.rules?.conformance?.carried);
|
|
32103
31980
|
if (summary) {
|
|
32104
31981
|
logger.blank();
|
|
32105
|
-
logger.info(
|
|
32106
|
-
for (const note of summary.revisits) logger.info(
|
|
31982
|
+
logger.info(import_chalk6.default.yellow(summary.line));
|
|
31983
|
+
for (const note of summary.revisits) logger.info(import_chalk6.default.gray(note));
|
|
32107
31984
|
}
|
|
32108
31985
|
logger.blank();
|
|
32109
31986
|
if (options.ci && waivedWarnings > 0) {
|
|
32110
|
-
logger.info(
|
|
31987
|
+
logger.info(import_chalk6.default.gray(`${waivedWarnings} draft-related warning(s) (non-fatal in --ci): excluded from the failure decision because the referenced specs are in draft/design status.`));
|
|
32111
31988
|
}
|
|
32112
31989
|
const failOnWarnings = !!options.ci && hasFatalWarnings;
|
|
32113
31990
|
if (hasErrors || failOnWarnings) {
|
|
@@ -32115,7 +31992,7 @@ async function runValidate(options = {}) {
|
|
|
32115
31992
|
logger.error("Validation failed: warnings are treated as errors in --ci mode.");
|
|
32116
31993
|
} else {
|
|
32117
31994
|
logger.error("Validation failed. Fix the errors above.");
|
|
32118
|
-
logger.info(
|
|
31995
|
+
logger.info(import_chalk6.default.cyan("Tip: If you need to temporarily bypass an architectural rule, you can override its severity level in your `.wai/project.yaml` config (e.g. `rules.sddRuleSeverity.CIRCULAR_DEPENDENCY: warning`)."));
|
|
32119
31996
|
}
|
|
32120
31997
|
process.exit(1);
|
|
32121
31998
|
} else {
|
|
@@ -32132,7 +32009,7 @@ init_paths();
|
|
|
32132
32009
|
init_fs();
|
|
32133
32010
|
|
|
32134
32011
|
// src/commands/list.ts
|
|
32135
|
-
var
|
|
32012
|
+
var import_chalk7 = __toESM(require("chalk"));
|
|
32136
32013
|
init_logger();
|
|
32137
32014
|
init_paths();
|
|
32138
32015
|
async function runList() {
|
|
@@ -32150,26 +32027,26 @@ async function runList() {
|
|
|
32150
32027
|
}
|
|
32151
32028
|
}
|
|
32152
32029
|
function printAgent(agent) {
|
|
32153
|
-
const status2 = agent.status === "active" ?
|
|
32154
|
-
console.log(`${
|
|
32030
|
+
const status2 = agent.status === "active" ? import_chalk7.default.green(agent.status) : agent.status === "draft" ? import_chalk7.default.yellow(agent.status) : import_chalk7.default.gray(agent.status);
|
|
32031
|
+
console.log(`${import_chalk7.default.bold(agent.id)} ${import_chalk7.default.gray(`[${agent.template}]`)} ${status2}`);
|
|
32155
32032
|
console.log(` ${agent.description}`);
|
|
32156
32033
|
if (agent.ownedPaths.length > 0) {
|
|
32157
|
-
console.log(
|
|
32034
|
+
console.log(import_chalk7.default.gray(` owns: ${agent.ownedPaths.join(", ")}`));
|
|
32158
32035
|
}
|
|
32159
32036
|
if (agent.tags.length > 0) {
|
|
32160
|
-
console.log(
|
|
32037
|
+
console.log(import_chalk7.default.gray(` tags: ${agent.tags.join(", ")}`));
|
|
32161
32038
|
}
|
|
32162
32039
|
const targetLabels = agent.targets.map((t) => {
|
|
32163
32040
|
if (typeof t === "string") return t;
|
|
32164
32041
|
const obj = t;
|
|
32165
32042
|
return obj.label ? `${obj.type}(${obj.label})` : obj.type;
|
|
32166
32043
|
});
|
|
32167
|
-
console.log(
|
|
32044
|
+
console.log(import_chalk7.default.gray(` targets: ${targetLabels.join(", ")}`));
|
|
32168
32045
|
console.log();
|
|
32169
32046
|
}
|
|
32170
32047
|
|
|
32171
32048
|
// src/commands/show.ts
|
|
32172
|
-
var
|
|
32049
|
+
var import_chalk8 = __toESM(require("chalk"));
|
|
32173
32050
|
init_logger();
|
|
32174
32051
|
init_paths();
|
|
32175
32052
|
init_errors();
|
|
@@ -32182,60 +32059,60 @@ async function runShow(agentId) {
|
|
|
32182
32059
|
printAgentDetails(agent);
|
|
32183
32060
|
}
|
|
32184
32061
|
function printAgentDetails(agent) {
|
|
32185
|
-
const statusColor = agent.status === "active" ?
|
|
32062
|
+
const statusColor = agent.status === "active" ? import_chalk8.default.green : agent.status === "draft" ? import_chalk8.default.yellow : import_chalk8.default.gray;
|
|
32186
32063
|
logger.header(agent.name);
|
|
32187
32064
|
console.log();
|
|
32188
|
-
console.log(`${
|
|
32189
|
-
console.log(`${
|
|
32190
|
-
console.log(`${
|
|
32065
|
+
console.log(`${import_chalk8.default.bold("ID")} ${agent.id}`);
|
|
32066
|
+
console.log(`${import_chalk8.default.bold("Status")} ${statusColor(agent.status)}`);
|
|
32067
|
+
console.log(`${import_chalk8.default.bold("Template")} ${agent.template}`);
|
|
32191
32068
|
if (agent.bundleOrigin) {
|
|
32192
|
-
console.log(`${
|
|
32069
|
+
console.log(`${import_chalk8.default.bold("Bundle")} ${agent.bundleOrigin}`);
|
|
32193
32070
|
}
|
|
32194
32071
|
if (agent.domainRoot) {
|
|
32195
|
-
console.log(`${
|
|
32072
|
+
console.log(`${import_chalk8.default.bold("Domain")} ${agent.domainRoot}`);
|
|
32196
32073
|
}
|
|
32197
32074
|
console.log();
|
|
32198
|
-
console.log(`${
|
|
32075
|
+
console.log(`${import_chalk8.default.bold("Description")}`);
|
|
32199
32076
|
console.log(` ${agent.description}`);
|
|
32200
32077
|
console.log();
|
|
32201
|
-
console.log(`${
|
|
32078
|
+
console.log(`${import_chalk8.default.bold("Reason")}`);
|
|
32202
32079
|
console.log(` ${agent.creationReason}`);
|
|
32203
32080
|
console.log();
|
|
32204
32081
|
if (agent.ownedPaths.length > 0) {
|
|
32205
|
-
console.log(`${
|
|
32082
|
+
console.log(`${import_chalk8.default.bold("Owned paths")}`);
|
|
32206
32083
|
for (const p of agent.ownedPaths) {
|
|
32207
|
-
console.log(` ${
|
|
32084
|
+
console.log(` ${import_chalk8.default.cyan(p)}`);
|
|
32208
32085
|
}
|
|
32209
32086
|
console.log();
|
|
32210
32087
|
}
|
|
32211
32088
|
if (agent.readPaths.length > 0) {
|
|
32212
|
-
console.log(`${
|
|
32089
|
+
console.log(`${import_chalk8.default.bold("Read paths")}`);
|
|
32213
32090
|
for (const p of agent.readPaths) {
|
|
32214
|
-
console.log(` ${
|
|
32091
|
+
console.log(` ${import_chalk8.default.gray(p)}`);
|
|
32215
32092
|
}
|
|
32216
32093
|
console.log();
|
|
32217
32094
|
}
|
|
32218
32095
|
if (agent.writePaths.length > 0) {
|
|
32219
|
-
console.log(`${
|
|
32096
|
+
console.log(`${import_chalk8.default.bold("Write paths")}`);
|
|
32220
32097
|
for (const p of agent.writePaths) {
|
|
32221
|
-
console.log(` ${
|
|
32098
|
+
console.log(` ${import_chalk8.default.gray(p)}`);
|
|
32222
32099
|
}
|
|
32223
32100
|
console.log();
|
|
32224
32101
|
}
|
|
32225
32102
|
if (agent.tags.length > 0) {
|
|
32226
|
-
console.log(`${
|
|
32103
|
+
console.log(`${import_chalk8.default.bold("Tags")} ${agent.tags.map((t) => import_chalk8.default.magenta(t)).join(", ")}`);
|
|
32227
32104
|
}
|
|
32228
32105
|
if (agent.dependencies.length > 0) {
|
|
32229
|
-
console.log(`${
|
|
32106
|
+
console.log(`${import_chalk8.default.bold("Depends on")} ${agent.dependencies.join(", ")}`);
|
|
32230
32107
|
}
|
|
32231
32108
|
const targetLabels = agent.targets.map((t) => {
|
|
32232
32109
|
if (typeof t === "string") return t;
|
|
32233
32110
|
const obj = t;
|
|
32234
32111
|
return obj.label ? `${obj.type}(${obj.label})` : obj.type;
|
|
32235
32112
|
});
|
|
32236
|
-
console.log(`${
|
|
32113
|
+
console.log(`${import_chalk8.default.bold("Targets")} ${targetLabels.join(", ")}`);
|
|
32237
32114
|
console.log();
|
|
32238
|
-
console.log(
|
|
32115
|
+
console.log(import_chalk8.default.gray(`Created: ${agent.createdAt} Updated: ${agent.updatedAt}`));
|
|
32239
32116
|
}
|
|
32240
32117
|
|
|
32241
32118
|
// src/cli/index.ts
|
|
@@ -32579,8 +32456,148 @@ function cleanStaleBinary(oldPath) {
|
|
|
32579
32456
|
}
|
|
32580
32457
|
var isNewer = isNewerVersion;
|
|
32581
32458
|
|
|
32582
|
-
// src/
|
|
32583
|
-
|
|
32459
|
+
// src/commands/status.ts
|
|
32460
|
+
var import_chalk9 = __toESM(require("chalk"));
|
|
32461
|
+
init_logger();
|
|
32462
|
+
init_paths();
|
|
32463
|
+
init_fs();
|
|
32464
|
+
init_specs2();
|
|
32465
|
+
init_core();
|
|
32466
|
+
init_specs();
|
|
32467
|
+
async function runStatus(options = {}) {
|
|
32468
|
+
assertProjectInitialized();
|
|
32469
|
+
scanAllSpecs({ recursive: options.recursive ?? true });
|
|
32470
|
+
const system = loadSystemSpec();
|
|
32471
|
+
const loaderErrors = getLoaderIssues();
|
|
32472
|
+
if (loaderErrors.length > 0) {
|
|
32473
|
+
logger.error("Failed to parse specification files:");
|
|
32474
|
+
for (const issue2 of loaderErrors) {
|
|
32475
|
+
const prefix = issue2.specId ? import_chalk9.default.gray(`[${issue2.specId}] `) : "";
|
|
32476
|
+
logger.error(`${prefix}[${issue2.code}] ${issue2.message}`);
|
|
32477
|
+
}
|
|
32478
|
+
process.exit(1);
|
|
32479
|
+
}
|
|
32480
|
+
let subsystems = loadSubsystemSpecs();
|
|
32481
|
+
let components = loadComponentSpecs();
|
|
32482
|
+
let interfaces = loadInterfaceSpecs();
|
|
32483
|
+
let implementations = loadImplementationSpecs();
|
|
32484
|
+
if (options.subsystem) {
|
|
32485
|
+
subsystems = subsystems.filter((s) => s.id === options.subsystem || s.id.startsWith(`${options.subsystem}::`));
|
|
32486
|
+
components = components.filter((c) => c.subsystem === options.subsystem || c.subsystem.startsWith(`${options.subsystem}::`));
|
|
32487
|
+
interfaces = interfaces.filter((i) => {
|
|
32488
|
+
const c = components.find((comp) => comp.id === i.component);
|
|
32489
|
+
return c !== void 0;
|
|
32490
|
+
});
|
|
32491
|
+
implementations = implementations.filter((im) => {
|
|
32492
|
+
const inf = interfaces.find((i) => i.id === im.contract);
|
|
32493
|
+
return inf !== void 0;
|
|
32494
|
+
});
|
|
32495
|
+
}
|
|
32496
|
+
if (!system) {
|
|
32497
|
+
logger.error("L0 System specification (system.yaml) is missing. Run `wairon init` first.");
|
|
32498
|
+
process.exit(1);
|
|
32499
|
+
}
|
|
32500
|
+
logger.header("Architecture Status Dashboard");
|
|
32501
|
+
logger.blank();
|
|
32502
|
+
const componentScores = /* @__PURE__ */ new Map();
|
|
32503
|
+
for (const comp of components) {
|
|
32504
|
+
let score = 20;
|
|
32505
|
+
const intf = interfaces.find((i) => i.component === comp.id);
|
|
32506
|
+
if (intf) {
|
|
32507
|
+
score += 30;
|
|
32508
|
+
}
|
|
32509
|
+
const impl = implementations.find((im) => intf && im.contract === intf.id);
|
|
32510
|
+
if (impl) {
|
|
32511
|
+
score += 30;
|
|
32512
|
+
const sourceFiles = implementationSourceFiles(impl);
|
|
32513
|
+
if (sourceFiles.length > 0 && sourceFiles.every((f) => pathExists(fromProjectRoot(f)))) {
|
|
32514
|
+
score += 20;
|
|
32515
|
+
}
|
|
32516
|
+
}
|
|
32517
|
+
const isDraft = comp.status === "draft" || comp.status === "design" || intf && (intf.status === "draft" || intf.status === "design") || impl && (impl.status === "draft" || impl.status === "design");
|
|
32518
|
+
if (isDraft) {
|
|
32519
|
+
score = Math.min(score, 50);
|
|
32520
|
+
}
|
|
32521
|
+
componentScores.set(comp.id, score);
|
|
32522
|
+
}
|
|
32523
|
+
const getSubsystemScore = (subId) => {
|
|
32524
|
+
const sub = subsystems.find((s) => s.id === subId);
|
|
32525
|
+
if (!sub) return 0;
|
|
32526
|
+
const subComps = components.filter((c) => c.subsystem === subId);
|
|
32527
|
+
if (subComps.length === 0) return 0;
|
|
32528
|
+
const totalScore = subComps.reduce((acc, c) => acc + (componentScores.get(c.id) ?? 0), 0);
|
|
32529
|
+
let avg = Math.round(totalScore / subComps.length);
|
|
32530
|
+
if (sub.status === "draft" || sub.status === "design") {
|
|
32531
|
+
avg = Math.min(avg, 50);
|
|
32532
|
+
}
|
|
32533
|
+
return avg;
|
|
32534
|
+
};
|
|
32535
|
+
const totalSubsystemsScore = subsystems.reduce((acc, s) => acc + getSubsystemScore(s.id), 0);
|
|
32536
|
+
const systemScore = subsystems.length > 0 ? Math.round(totalSubsystemsScore / subsystems.length) : 0;
|
|
32537
|
+
const systemColor = systemScore === 100 ? import_chalk9.default.green : systemScore >= 50 ? import_chalk9.default.yellow : import_chalk9.default.red;
|
|
32538
|
+
console.log(`${import_chalk9.default.bold.blue("\u25CF System:")} ${import_chalk9.default.bold(system.name)} ${systemColor(`(${systemScore}% Complete)`)}`);
|
|
32539
|
+
for (let i = 0; i < subsystems.length; i++) {
|
|
32540
|
+
const sub = subsystems[i];
|
|
32541
|
+
const isLastSub = i === subsystems.length - 1;
|
|
32542
|
+
const subPrefix = isLastSub ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
32543
|
+
const subIndent = isLastSub ? " " : "\u2502 ";
|
|
32544
|
+
const subScore = getSubsystemScore(sub.id);
|
|
32545
|
+
const subStatusStr = sub.status !== "complete" ? import_chalk9.default.yellow(` [${sub.status}]`) : "";
|
|
32546
|
+
const subScoreColor = subScore === 100 ? import_chalk9.default.green : subScore >= 50 ? import_chalk9.default.yellow : import_chalk9.default.red;
|
|
32547
|
+
console.log(`${import_chalk9.default.gray(subPrefix)}${import_chalk9.default.bold.cyan(`[Subsystem] ${sub.id}`)}${subStatusStr} ${subScoreColor(`(${subScore}%)`)}`);
|
|
32548
|
+
const subComps = components.filter((c) => c.subsystem === sub.id);
|
|
32549
|
+
for (let j = 0; j < subComps.length; j++) {
|
|
32550
|
+
const comp = subComps[j];
|
|
32551
|
+
const isLastComp = j === subComps.length - 1;
|
|
32552
|
+
const compPrefix = isLastComp ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
32553
|
+
const compIndent = isLastComp ? " " : "\u2502 ";
|
|
32554
|
+
const compScore = componentScores.get(comp.id) ?? 0;
|
|
32555
|
+
const compStatusStr = comp.status !== "complete" ? import_chalk9.default.yellow(` [${comp.status}]`) : "";
|
|
32556
|
+
const compScoreColor = compScore === 100 ? import_chalk9.default.green : compScore >= 50 ? import_chalk9.default.yellow : import_chalk9.default.red;
|
|
32557
|
+
console.log(`${import_chalk9.default.gray(subIndent + compPrefix)}${import_chalk9.default.magenta(`[Component: ${comp.componentType}] ${comp.id}`)}${compStatusStr} ${compScoreColor(`(${compScore}%)`)}`);
|
|
32558
|
+
const intf = interfaces.find((inf) => inf.component === comp.id);
|
|
32559
|
+
const impl = implementations.find((im) => intf && im.contract === intf.id);
|
|
32560
|
+
const intfPrefix = intf && impl ? "\u251C\u2500\u2500 " : "\u2514\u2500\u2500 ";
|
|
32561
|
+
if (intf) {
|
|
32562
|
+
const intfStatusStr = intf.status !== "complete" ? import_chalk9.default.yellow(` [${intf.status}]`) : "";
|
|
32563
|
+
console.log(`${import_chalk9.default.gray(subIndent + compIndent + intfPrefix)}${import_chalk9.default.blue(`Interface: ${intf.id}`)}${intfStatusStr} (${intf.methods.length} methods)`);
|
|
32564
|
+
} else {
|
|
32565
|
+
console.log(`${import_chalk9.default.gray(subIndent + compIndent + intfPrefix)}${import_chalk9.default.red("Interface: Missing (-30%)")}`);
|
|
32566
|
+
}
|
|
32567
|
+
if (impl) {
|
|
32568
|
+
const implStatusStr = impl.status !== "complete" ? import_chalk9.default.yellow(` [${impl.status}]`) : "";
|
|
32569
|
+
const methodsWithOwnPath = impl.methods.filter((m) => m.sourcePath);
|
|
32570
|
+
let pathStr;
|
|
32571
|
+
if (impl.sourcePath) {
|
|
32572
|
+
pathStr = pathExists(fromProjectRoot(impl.sourcePath)) ? import_chalk9.default.green(` -> ${impl.sourcePath}`) : import_chalk9.default.red(` -> ${impl.sourcePath} (File Missing!)`);
|
|
32573
|
+
} else if (methodsWithOwnPath.length > 0) {
|
|
32574
|
+
pathStr = "";
|
|
32575
|
+
} else {
|
|
32576
|
+
pathStr = import_chalk9.default.gray(" (No source path)");
|
|
32577
|
+
}
|
|
32578
|
+
console.log(`${import_chalk9.default.gray(subIndent + compIndent + "\u2514\u2500\u2500 ")}${import_chalk9.default.green(`Implementation: ${impl.id}`)}${implStatusStr}${pathStr}`);
|
|
32579
|
+
const methodIndent = subIndent + compIndent + " ";
|
|
32580
|
+
for (let k = 0; k < methodsWithOwnPath.length; k++) {
|
|
32581
|
+
const method2 = methodsWithOwnPath[k];
|
|
32582
|
+
const isLastMethod = k === methodsWithOwnPath.length - 1;
|
|
32583
|
+
const methodPrefix = isLastMethod ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
32584
|
+
const methodPath = method2.sourcePath;
|
|
32585
|
+
const methodLine = pathExists(fromProjectRoot(methodPath)) ? import_chalk9.default.green(`method ${method2.name} -> ${methodPath}`) : import_chalk9.default.red(`method ${method2.name} -> ${methodPath} (File Missing!)`);
|
|
32586
|
+
console.log(`${import_chalk9.default.gray(methodIndent + methodPrefix)}${methodLine}`);
|
|
32587
|
+
}
|
|
32588
|
+
} else {
|
|
32589
|
+
console.log(`${import_chalk9.default.gray(subIndent + compIndent + "\u2514\u2500\u2500 ")}${import_chalk9.default.red("Implementation: Missing (-30%)")}`);
|
|
32590
|
+
}
|
|
32591
|
+
}
|
|
32592
|
+
}
|
|
32593
|
+
const lock = approvalVerdict();
|
|
32594
|
+
if (lock.text.trim()) {
|
|
32595
|
+
logger.blank();
|
|
32596
|
+
if (lock.drifted) logger.warn(lock.text.trim());
|
|
32597
|
+
else logger.info(lock.text.trim());
|
|
32598
|
+
}
|
|
32599
|
+
logger.blank();
|
|
32600
|
+
}
|
|
32584
32601
|
|
|
32585
32602
|
// src/commands/domains.ts
|
|
32586
32603
|
var import_chalk11 = __toESM(require("chalk"));
|
|
@@ -32964,10 +32981,9 @@ init_paths();
|
|
|
32964
32981
|
init_errors();
|
|
32965
32982
|
init_fs();
|
|
32966
32983
|
init_provision();
|
|
32967
|
-
init_context();
|
|
32968
32984
|
init_skills();
|
|
32969
32985
|
init_specs2();
|
|
32970
|
-
|
|
32986
|
+
init_lock();
|
|
32971
32987
|
init_extensions();
|
|
32972
32988
|
init_mcp();
|
|
32973
32989
|
function icon(mark) {
|
|
@@ -33023,7 +33039,7 @@ async function runDoctor(options = {}) {
|
|
|
33023
33039
|
let targets = [];
|
|
33024
33040
|
let configOk = false;
|
|
33025
33041
|
try {
|
|
33026
|
-
if (!
|
|
33042
|
+
if (!loadProjectConfig3()) throw new ProjectNotInitializedError();
|
|
33027
33043
|
configOk = true;
|
|
33028
33044
|
line(tally, "ok", ".wai/project.yaml is valid");
|
|
33029
33045
|
} catch (e) {
|
|
@@ -33059,7 +33075,7 @@ async function runDoctor(options = {}) {
|
|
|
33059
33075
|
if (hasSystemSpec && configOk) {
|
|
33060
33076
|
console.log(import_chalk12.default.bold("Spec tree"));
|
|
33061
33077
|
try {
|
|
33062
|
-
const cfg =
|
|
33078
|
+
const cfg = loadProjectConfig3();
|
|
33063
33079
|
if (!cfg) throw new ProjectNotInitializedError();
|
|
33064
33080
|
const result = validateSddTree(cfg.rules, cfg.projectType);
|
|
33065
33081
|
const errs = result.issues.filter((i) => i.severity === "error").length;
|
|
@@ -33107,10 +33123,8 @@ async function runDoctor(options = {}) {
|
|
|
33107
33123
|
}
|
|
33108
33124
|
}
|
|
33109
33125
|
console.log(import_chalk12.default.bold(`Generated files ${import_chalk12.default.gray("(stale = older than installed)")}`));
|
|
33110
|
-
for (const
|
|
33111
|
-
|
|
33112
|
-
[".wai/context/domains.md", CONTEXT_PATHS.domainsMd()]
|
|
33113
|
-
]) {
|
|
33126
|
+
for (const p of derivedDocPaths2()) {
|
|
33127
|
+
const label = path38.relative(getProjectRoot(), p).replace(/\\/g, "/");
|
|
33114
33128
|
if (!pathExists(p)) {
|
|
33115
33129
|
line(tally, "warn", `${label} \u2014 not generated yet (run \`wairon generate\`)`);
|
|
33116
33130
|
continue;
|
|
@@ -33243,7 +33257,7 @@ async function applyFixes() {
|
|
|
33243
33257
|
if (!projectConfigExists2()) return;
|
|
33244
33258
|
let targets;
|
|
33245
33259
|
try {
|
|
33246
|
-
if (!
|
|
33260
|
+
if (!loadProjectConfig3()) throw new ProjectNotInitializedError();
|
|
33247
33261
|
targets = activeTargetTypes2();
|
|
33248
33262
|
} catch (e) {
|
|
33249
33263
|
logger.warn(`--fix skipped: .wai/project.yaml is invalid (${e instanceof Error ? e.message : String(e)}). Fix it first.`);
|
|
@@ -33252,7 +33266,7 @@ async function applyFixes() {
|
|
|
33252
33266
|
}
|
|
33253
33267
|
console.log(import_chalk12.default.bold("Applying fixes\u2026"));
|
|
33254
33268
|
try {
|
|
33255
|
-
|
|
33269
|
+
syncContextFiles2();
|
|
33256
33270
|
exportSddSkills(targets);
|
|
33257
33271
|
const guides = reinjectLocalGuides2(process.cwd(), targets);
|
|
33258
33272
|
console.log(` ${icon("ok")} Regenerated context files, skills, and ${guides.length} local guide(s).`);
|
|
@@ -48405,7 +48419,7 @@ var TIER_COLOR = {
|
|
|
48405
48419
|
};
|
|
48406
48420
|
async function showExecution() {
|
|
48407
48421
|
assertInitialized();
|
|
48408
|
-
const config =
|
|
48422
|
+
const config = loadProjectConfig3();
|
|
48409
48423
|
if (!config) throw new ProjectNotInitializedError();
|
|
48410
48424
|
const tier = config.execution.tier;
|
|
48411
48425
|
logger.header("Execution budgets");
|
|
@@ -48467,7 +48481,7 @@ async function setExecutionTier3(raw) {
|
|
|
48467
48481
|
);
|
|
48468
48482
|
}
|
|
48469
48483
|
const tier = parsed.data;
|
|
48470
|
-
const config =
|
|
48484
|
+
const config = loadProjectConfig3();
|
|
48471
48485
|
if (!config) throw new ProjectNotInitializedError();
|
|
48472
48486
|
const previous = config.execution.tier;
|
|
48473
48487
|
if (previous === tier) {
|
|
@@ -48520,7 +48534,7 @@ async function runLock2(options) {
|
|
|
48520
48534
|
logger.error("No SDD spec tree found (.wai/specs). Nothing to lock.");
|
|
48521
48535
|
process.exit(1);
|
|
48522
48536
|
}
|
|
48523
|
-
const projectConfig =
|
|
48537
|
+
const projectConfig = loadProjectConfig3();
|
|
48524
48538
|
if (!projectConfig) throw new ProjectNotInitializedError();
|
|
48525
48539
|
logger.info("Analyzing and validating specifications in-memory...");
|
|
48526
48540
|
const dry = validateAsComplete({
|