@wairon/cli 5.1.1-dev.101 → 5.1.1-dev.102
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/README.md +83 -37
- package/dist/cli/index.js +986 -393
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +37562 -0
- package/dist/index.js +705 -365
- package/dist/index.js.map +1 -1
- package/dist/templates/architect.yaml +14 -12
- package/dist/templates/domain-owner.yaml +3 -2
- package/dist/templates/skills/sdd-architect.md +11 -5
- package/dist/templates/skills/sdd-auditor.md +5 -4
- package/dist/templates/skills/sdd-delegate.md +3 -2
- package/dist/templates/skills/sdd-implement.md +4 -4
- package/dist/templates/skills/sdd-narrative.md +1 -1
- package/dist/webapp.html +770 -0
- package/package.json +2 -1
- package/schemas/design-export-1.json +4 -4
package/dist/index.js
CHANGED
|
@@ -3316,8 +3316,8 @@ var require_dist = __commonJS({
|
|
|
3316
3316
|
return normalized === "." ? ".wai/" : `${normalized}/.wai/`;
|
|
3317
3317
|
}
|
|
3318
3318
|
function normalizeRootPath(relativePath) {
|
|
3319
|
-
const
|
|
3320
|
-
return
|
|
3319
|
+
const posix7 = relativePath.split(path210.sep).join("/").replace(/^\.\//, "").replace(/\/+$/, "");
|
|
3320
|
+
return posix7 === "" || posix7 === "." ? "." : posix7;
|
|
3321
3321
|
}
|
|
3322
3322
|
function slugify(projectName) {
|
|
3323
3323
|
const slug2 = projectName.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -3709,6 +3709,7 @@ __export(src_exports, {
|
|
|
3709
3709
|
keyIn: () => keyIn,
|
|
3710
3710
|
landReference: () => landReference,
|
|
3711
3711
|
languageOfSourcePath: () => languageOfSourcePath,
|
|
3712
|
+
lineEndingFor: () => lineEndingFor,
|
|
3712
3713
|
listDirectChainedSubprojects: () => listDirectChainedSubprojects,
|
|
3713
3714
|
listExternals: () => listExternals2,
|
|
3714
3715
|
listFiles: () => listFiles,
|
|
@@ -3867,6 +3868,7 @@ __export(src_exports, {
|
|
|
3867
3868
|
typeIsMany: () => typeIsMany,
|
|
3868
3869
|
typeMatchesRef: () => typeMatchesRef,
|
|
3869
3870
|
typeNamedRefs: () => typeNamedRefs,
|
|
3871
|
+
typeProblemEnumProposal: () => typeProblemEnumProposal,
|
|
3870
3872
|
typeProblemIntProposal: () => typeProblemIntProposal,
|
|
3871
3873
|
typeSourceFiles: () => typeSourceFiles,
|
|
3872
3874
|
typeSpellingFacts: () => typeSpellingFacts,
|
|
@@ -3880,6 +3882,7 @@ __export(src_exports, {
|
|
|
3880
3882
|
validateProject: () => validateProject,
|
|
3881
3883
|
validateProjectConfig: () => validateProjectConfig,
|
|
3882
3884
|
validateRegistry: () => validateRegistry,
|
|
3885
|
+
withLineEndings: () => withLineEndings,
|
|
3883
3886
|
withPack: () => withPack,
|
|
3884
3887
|
writeFile: () => writeFile,
|
|
3885
3888
|
writeFileIfChanged: () => writeFileIfChanged,
|
|
@@ -3894,8 +3897,8 @@ __export(src_exports, {
|
|
|
3894
3897
|
module.exports = __toCommonJS(src_exports);
|
|
3895
3898
|
|
|
3896
3899
|
// src/config/project-config.ts
|
|
3897
|
-
var
|
|
3898
|
-
var
|
|
3900
|
+
var fs3 = __toESM(require("fs"));
|
|
3901
|
+
var path5 = __toESM(require("path"));
|
|
3899
3902
|
var YAML = __toESM(require("yaml"));
|
|
3900
3903
|
var import_zod4 = require("zod");
|
|
3901
3904
|
|
|
@@ -3908,17 +3911,119 @@ function ensureDir(dirPath) {
|
|
|
3908
3911
|
}
|
|
3909
3912
|
function writeFile(filePath, content) {
|
|
3910
3913
|
ensureDir(path.dirname(filePath));
|
|
3911
|
-
fs.writeFileSync(filePath, content, "utf-8");
|
|
3914
|
+
fs.writeFileSync(filePath, withLineEndings(filePath, content), "utf-8");
|
|
3912
3915
|
}
|
|
3913
3916
|
function writeFileIfChanged(filePath, content) {
|
|
3917
|
+
const text3 = withLineEndings(filePath, content);
|
|
3914
3918
|
if (fs.existsSync(filePath)) {
|
|
3915
3919
|
const existing = fs.readFileSync(filePath, "utf-8");
|
|
3916
|
-
if (existing ===
|
|
3920
|
+
if (existing === text3) return false;
|
|
3917
3921
|
}
|
|
3918
3922
|
ensureDir(path.dirname(filePath));
|
|
3919
|
-
fs.writeFileSync(filePath,
|
|
3923
|
+
fs.writeFileSync(filePath, text3, "utf-8");
|
|
3920
3924
|
return true;
|
|
3921
3925
|
}
|
|
3926
|
+
var EOL_SAMPLE_BYTES = 64 * 1024;
|
|
3927
|
+
function endingsOf(text3) {
|
|
3928
|
+
const crlf = text3.split("\r\n").length - 1;
|
|
3929
|
+
const lf = text3.split("\n").length - 1 - crlf;
|
|
3930
|
+
if (crlf === 0 && lf === 0) return null;
|
|
3931
|
+
return crlf > lf ? "\r\n" : "\n";
|
|
3932
|
+
}
|
|
3933
|
+
function sampleOf(file) {
|
|
3934
|
+
let fd;
|
|
3935
|
+
try {
|
|
3936
|
+
fd = fs.openSync(file, "r");
|
|
3937
|
+
const buffer = Buffer.alloc(EOL_SAMPLE_BYTES);
|
|
3938
|
+
const read2 = fs.readSync(fd, buffer, 0, EOL_SAMPLE_BYTES, 0);
|
|
3939
|
+
return buffer.subarray(0, read2).toString("utf-8");
|
|
3940
|
+
} catch {
|
|
3941
|
+
return null;
|
|
3942
|
+
} finally {
|
|
3943
|
+
if (fd !== void 0) fs.closeSync(fd);
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
function attributeMatches(pattern, relative20) {
|
|
3947
|
+
const glob = pattern.replace(/^\//, "");
|
|
3948
|
+
const target = glob.includes("/") ? relative20 : path.posix.basename(relative20);
|
|
3949
|
+
const source = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, "\0").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]").replace(/\u0000/g, "(?:.*/)?");
|
|
3950
|
+
return new RegExp(`^${source}$`).test(target);
|
|
3951
|
+
}
|
|
3952
|
+
function attributeEol(attributesFile, file) {
|
|
3953
|
+
const text3 = readFileOrNull(attributesFile);
|
|
3954
|
+
if (text3 === null) return null;
|
|
3955
|
+
const relative20 = path.relative(path.dirname(attributesFile), file).split(path.sep).join("/");
|
|
3956
|
+
let eol = null;
|
|
3957
|
+
for (const raw of text3.split(/\r?\n/)) {
|
|
3958
|
+
const line = raw.trim();
|
|
3959
|
+
if (line === "" || line.startsWith("#")) continue;
|
|
3960
|
+
const [pattern, ...attrs] = line.split(/\s+/);
|
|
3961
|
+
if (!attributeMatches(pattern, relative20)) continue;
|
|
3962
|
+
for (const attr of attrs) {
|
|
3963
|
+
if (attr === "eol=crlf") eol = "\r\n";
|
|
3964
|
+
else if (attr === "eol=lf") eol = "\n";
|
|
3965
|
+
else if (attr === "-text" || attr === "binary") eol = null;
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
return eol;
|
|
3969
|
+
}
|
|
3970
|
+
function conventionFolders(dir) {
|
|
3971
|
+
const folders = [];
|
|
3972
|
+
let at = path.resolve(dir);
|
|
3973
|
+
for (let depth = 0; depth < 8; depth++) {
|
|
3974
|
+
folders.push(at);
|
|
3975
|
+
if (fs.existsSync(path.join(at, ".git")) || fs.existsSync(path.join(at, ".wai"))) return folders;
|
|
3976
|
+
const parent = path.dirname(at);
|
|
3977
|
+
if (parent === at) break;
|
|
3978
|
+
at = parent;
|
|
3979
|
+
}
|
|
3980
|
+
return folders.slice(0, 1);
|
|
3981
|
+
}
|
|
3982
|
+
function folderEndings(folder, ext, exclude) {
|
|
3983
|
+
let names;
|
|
3984
|
+
try {
|
|
3985
|
+
names = fs.readdirSync(folder);
|
|
3986
|
+
} catch {
|
|
3987
|
+
return null;
|
|
3988
|
+
}
|
|
3989
|
+
const candidates = names.filter((n) => path.join(folder, n) !== exclude && /\.(ya?ml|json|md|ts|js|txt)$/i.test(n)).sort((a, b) => Number(path.extname(b) === ext) - Number(path.extname(a) === ext)).slice(0, 24);
|
|
3990
|
+
let crlf = 0;
|
|
3991
|
+
let lf = 0;
|
|
3992
|
+
for (const name of candidates) {
|
|
3993
|
+
const full = path.join(folder, name);
|
|
3994
|
+
try {
|
|
3995
|
+
if (!fs.statSync(full).isFile()) continue;
|
|
3996
|
+
} catch {
|
|
3997
|
+
continue;
|
|
3998
|
+
}
|
|
3999
|
+
const sample = sampleOf(full);
|
|
4000
|
+
const endings = sample === null ? null : endingsOf(sample);
|
|
4001
|
+
if (endings === "\r\n") crlf++;
|
|
4002
|
+
else if (endings === "\n") lf++;
|
|
4003
|
+
}
|
|
4004
|
+
if (crlf === lf) return null;
|
|
4005
|
+
return crlf > lf ? "\r\n" : "\n";
|
|
4006
|
+
}
|
|
4007
|
+
function lineEndingFor(filePath) {
|
|
4008
|
+
const own = sampleOf(filePath);
|
|
4009
|
+
const existing = own === null ? null : endingsOf(own);
|
|
4010
|
+
if (existing) return existing;
|
|
4011
|
+
const folders = conventionFolders(path.dirname(filePath));
|
|
4012
|
+
for (const folder of folders) {
|
|
4013
|
+
const eol = attributeEol(path.join(folder, ".gitattributes"), filePath);
|
|
4014
|
+
if (eol) return eol;
|
|
4015
|
+
}
|
|
4016
|
+
const ext = path.extname(filePath);
|
|
4017
|
+
for (const folder of folders) {
|
|
4018
|
+
const endings = folderEndings(folder, ext, path.resolve(filePath));
|
|
4019
|
+
if (endings) return endings;
|
|
4020
|
+
}
|
|
4021
|
+
return "\n";
|
|
4022
|
+
}
|
|
4023
|
+
function withLineEndings(filePath, content) {
|
|
4024
|
+
const normalised = content.replace(/\r\n/g, "\n");
|
|
4025
|
+
return lineEndingFor(filePath) === "\r\n" ? normalised.replace(/\n/g, "\r\n") : normalised;
|
|
4026
|
+
}
|
|
3922
4027
|
function readFileOrNull(filePath) {
|
|
3923
4028
|
try {
|
|
3924
4029
|
return fs.readFileSync(filePath, "utf-8");
|
|
@@ -4064,44 +4169,67 @@ function aiDirAt(root, ...segments) {
|
|
|
4064
4169
|
return path.join(base, ...segments);
|
|
4065
4170
|
}
|
|
4066
4171
|
|
|
4067
|
-
// src/
|
|
4068
|
-
var
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
}
|
|
4084
|
-
}
|
|
4085
|
-
function readYamlFile(filePath) {
|
|
4086
|
-
const content = readFileOrNull(filePath);
|
|
4087
|
-
if (content === null) return null;
|
|
4088
|
-
return parseYaml(content, filePath);
|
|
4089
|
-
}
|
|
4090
|
-
function writeYamlFile(filePath, value) {
|
|
4091
|
-
writeFile(filePath, serializeYaml(value));
|
|
4172
|
+
// src/config/defaults.ts
|
|
4173
|
+
var os = __toESM(require("os"));
|
|
4174
|
+
var path2 = __toESM(require("path"));
|
|
4175
|
+
var DEFAULT_TARGET_DIRS = {
|
|
4176
|
+
claude: ".claude/agents",
|
|
4177
|
+
gemini: ".gemini/agents",
|
|
4178
|
+
agy: ".gemini/agents",
|
|
4179
|
+
cursor: ".cursor/rules",
|
|
4180
|
+
copilot: ".github/prompts",
|
|
4181
|
+
codex: ".codex/agents"
|
|
4182
|
+
};
|
|
4183
|
+
function defaultTargetConfig(type) {
|
|
4184
|
+
return {
|
|
4185
|
+
type,
|
|
4186
|
+
outputDir: DEFAULT_TARGET_DIRS[type],
|
|
4187
|
+
enabled: true
|
|
4188
|
+
};
|
|
4092
4189
|
}
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4190
|
+
var WAIRON_VERSION = "5.1.1-dev.102";
|
|
4191
|
+
var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
4192
|
+
var ARCHITECT_AGENT_ID = "agent-architect";
|
|
4193
|
+
var ARCHITECT_TEMPLATE_ID = "architect";
|
|
4194
|
+
function globalTemplatesDir(projectOverride) {
|
|
4195
|
+
if (process.env.WAIRON_TEMPLATES_DIR) {
|
|
4196
|
+
return process.env.WAIRON_TEMPLATES_DIR;
|
|
4100
4197
|
}
|
|
4198
|
+
if (projectOverride) {
|
|
4199
|
+
return projectOverride;
|
|
4200
|
+
}
|
|
4201
|
+
return path2.join(os.homedir(), ".wairon", "templates");
|
|
4101
4202
|
}
|
|
4102
|
-
|
|
4103
|
-
|
|
4203
|
+
var BACKEND_COMMANDS = {
|
|
4204
|
+
claude: "claude",
|
|
4205
|
+
gemini: "gemini"
|
|
4206
|
+
};
|
|
4207
|
+
function backendCommand(backend) {
|
|
4208
|
+
return BACKEND_COMMANDS[backend] ?? backend;
|
|
4104
4209
|
}
|
|
4210
|
+
var SUPPORTED_ALIASES = ["wai"];
|
|
4211
|
+
var SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
4212
|
+
"node_modules",
|
|
4213
|
+
"dist",
|
|
4214
|
+
"build",
|
|
4215
|
+
"out",
|
|
4216
|
+
".git",
|
|
4217
|
+
".cache",
|
|
4218
|
+
"coverage",
|
|
4219
|
+
"__pycache__",
|
|
4220
|
+
".venv",
|
|
4221
|
+
"venv",
|
|
4222
|
+
"target",
|
|
4223
|
+
// Rust
|
|
4224
|
+
"vendor"
|
|
4225
|
+
]);
|
|
4226
|
+
|
|
4227
|
+
// src/core/adapters/git-source.ts
|
|
4228
|
+
var import_child_process = require("child_process");
|
|
4229
|
+
var crypto = __toESM(require("crypto"));
|
|
4230
|
+
var fs2 = __toESM(require("fs"));
|
|
4231
|
+
var os2 = __toESM(require("os"));
|
|
4232
|
+
var path3 = __toESM(require("path"));
|
|
4105
4233
|
|
|
4106
4234
|
// src/utils/errors.ts
|
|
4107
4235
|
var WaironError = class extends Error {
|
|
@@ -4151,6 +4279,167 @@ ${reasons.map((r) => ` - ${r}`).join("\n")}`);
|
|
|
4151
4279
|
}
|
|
4152
4280
|
};
|
|
4153
4281
|
|
|
4282
|
+
// src/core/adapters/git-source.ts
|
|
4283
|
+
var SourceUnavailable = class extends WaironError {
|
|
4284
|
+
constructor(message) {
|
|
4285
|
+
super(message);
|
|
4286
|
+
this.name = "SourceUnavailable";
|
|
4287
|
+
}
|
|
4288
|
+
};
|
|
4289
|
+
function osUserCache() {
|
|
4290
|
+
if (process.platform === "win32") return process.env.LOCALAPPDATA ?? path3.join(os2.homedir(), "AppData", "Local");
|
|
4291
|
+
if (process.platform === "darwin") return path3.join(os2.homedir(), "Library", "Caches");
|
|
4292
|
+
return process.env.XDG_CACHE_HOME ?? path3.join(os2.homedir(), ".cache");
|
|
4293
|
+
}
|
|
4294
|
+
var cacheRoots = /* @__PURE__ */ new Map();
|
|
4295
|
+
function cacheRoot() {
|
|
4296
|
+
const setting = process.env.WAIRON_CACHE_DIR ?? "";
|
|
4297
|
+
let root = cacheRoots.get(setting);
|
|
4298
|
+
if (root === void 0) {
|
|
4299
|
+
root = setting !== "" ? path3.resolve(setting) : path3.join(osUserCache(), "wairon");
|
|
4300
|
+
cacheRoots.set(setting, root);
|
|
4301
|
+
}
|
|
4302
|
+
return root;
|
|
4303
|
+
}
|
|
4304
|
+
function git(args, cwd) {
|
|
4305
|
+
return (0, import_child_process.execFileSync)("git", args, {
|
|
4306
|
+
cwd,
|
|
4307
|
+
encoding: "utf8",
|
|
4308
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
4309
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0", GIT_ASKPASS: "echo" }
|
|
4310
|
+
});
|
|
4311
|
+
}
|
|
4312
|
+
function reasonOf(e) {
|
|
4313
|
+
const stderr = e?.stderr;
|
|
4314
|
+
const text3 = typeof stderr === "string" && stderr.trim() !== "" ? stderr : e instanceof Error ? e.message : String(e);
|
|
4315
|
+
return text3.trim().split(/\r?\n/).filter((l) => l.trim() !== "").slice(-1)[0] ?? "git failed";
|
|
4316
|
+
}
|
|
4317
|
+
function cacheEntryOf(url, commit2) {
|
|
4318
|
+
const repo = crypto.createHash("sha256").update(url).digest("hex");
|
|
4319
|
+
return path3.join(cacheRoot(), "git", repo, commit2);
|
|
4320
|
+
}
|
|
4321
|
+
function within(entry, url, commit2, dir) {
|
|
4322
|
+
const at = dir ? path3.join(entry, dir) : entry;
|
|
4323
|
+
if (!fs2.existsSync(at)) {
|
|
4324
|
+
throw new SourceUnavailable(`the commit ${commit2} of ${url} holds no "${dir}"`);
|
|
4325
|
+
}
|
|
4326
|
+
return at;
|
|
4327
|
+
}
|
|
4328
|
+
function fetchInto(tmp, url, commit2) {
|
|
4329
|
+
git(["init", "-q"], tmp);
|
|
4330
|
+
try {
|
|
4331
|
+
git(["fetch", "-q", "--depth", "1", url, commit2], tmp);
|
|
4332
|
+
} catch {
|
|
4333
|
+
git(["fetch", "-q", url, "+refs/heads/*:refs/remotes/source/*", "+refs/tags/*:refs/tags/*"], tmp);
|
|
4334
|
+
}
|
|
4335
|
+
git(["-c", "advice.detachedHead=false", "checkout", "-q", commit2], tmp);
|
|
4336
|
+
fs2.rmSync(path3.join(tmp, ".git"), { recursive: true, force: true });
|
|
4337
|
+
}
|
|
4338
|
+
function fetch(url, commit2, dir) {
|
|
4339
|
+
const entry = cacheEntryOf(url, commit2);
|
|
4340
|
+
if (fs2.existsSync(entry)) return within(entry, url, commit2, dir);
|
|
4341
|
+
fs2.mkdirSync(path3.dirname(entry), { recursive: true });
|
|
4342
|
+
const tmp = `${entry}.tmp-${process.pid}-${crypto.randomBytes(4).toString("hex")}`;
|
|
4343
|
+
fs2.mkdirSync(tmp, { recursive: true });
|
|
4344
|
+
try {
|
|
4345
|
+
fetchInto(tmp, url, commit2);
|
|
4346
|
+
try {
|
|
4347
|
+
fs2.renameSync(tmp, entry);
|
|
4348
|
+
} catch (e) {
|
|
4349
|
+
if (!fs2.existsSync(entry)) throw e;
|
|
4350
|
+
fs2.rmSync(tmp, { recursive: true, force: true });
|
|
4351
|
+
}
|
|
4352
|
+
} catch (e) {
|
|
4353
|
+
fs2.rmSync(tmp, { recursive: true, force: true });
|
|
4354
|
+
if (fs2.readdirSync(path3.dirname(entry)).length === 0) fs2.rmdirSync(path3.dirname(entry));
|
|
4355
|
+
throw e instanceof SourceUnavailable ? e : new SourceUnavailable(`cannot fetch ${commit2} of ${url}: ${reasonOf(e)}`);
|
|
4356
|
+
}
|
|
4357
|
+
return within(entry, url, commit2, dir);
|
|
4358
|
+
}
|
|
4359
|
+
function resolve3(url, ref) {
|
|
4360
|
+
let listed;
|
|
4361
|
+
try {
|
|
4362
|
+
listed = git(["ls-remote", url, ref ?? "HEAD"]);
|
|
4363
|
+
} catch (e) {
|
|
4364
|
+
throw new SourceUnavailable(`cannot reach ${url}: ${reasonOf(e)}`);
|
|
4365
|
+
}
|
|
4366
|
+
const wanted = commitListed(listed, ref ?? "HEAD");
|
|
4367
|
+
if (!wanted) throw new SourceUnavailable(`${url} lists no ref "${ref ?? "HEAD"}"`);
|
|
4368
|
+
return wanted;
|
|
4369
|
+
}
|
|
4370
|
+
function commitListed(listed, ref) {
|
|
4371
|
+
const lines = listed.split(/\r?\n/).filter((l) => l.trim() !== "").map((l) => l.split(/\s+/));
|
|
4372
|
+
const names = [ref, `refs/heads/${ref}`, `refs/tags/${ref}^{}`, `refs/tags/${ref}`];
|
|
4373
|
+
for (const name of names) {
|
|
4374
|
+
const line = lines.find(([, listedName]) => listedName === name);
|
|
4375
|
+
if (line) return line[0];
|
|
4376
|
+
}
|
|
4377
|
+
return void 0;
|
|
4378
|
+
}
|
|
4379
|
+
function trackedFiles(directory, pathspecs) {
|
|
4380
|
+
if (pathspecs.length === 0) return [];
|
|
4381
|
+
try {
|
|
4382
|
+
return git(["ls-files", "-z", "--", ...pathspecs], directory).split("\0").filter((p) => p !== "");
|
|
4383
|
+
} catch {
|
|
4384
|
+
return null;
|
|
4385
|
+
}
|
|
4386
|
+
}
|
|
4387
|
+
function head(directory) {
|
|
4388
|
+
try {
|
|
4389
|
+
return git(["rev-parse", "HEAD"], directory).trim() || null;
|
|
4390
|
+
} catch {
|
|
4391
|
+
return null;
|
|
4392
|
+
}
|
|
4393
|
+
}
|
|
4394
|
+
function repositoryRoot(directory) {
|
|
4395
|
+
let at = path3.resolve(directory);
|
|
4396
|
+
for (; ; ) {
|
|
4397
|
+
if (fs2.existsSync(path3.join(at, ".git"))) return at;
|
|
4398
|
+
const up = path3.dirname(at);
|
|
4399
|
+
if (up === at) return null;
|
|
4400
|
+
at = up;
|
|
4401
|
+
}
|
|
4402
|
+
}
|
|
4403
|
+
|
|
4404
|
+
// src/utils/yaml.ts
|
|
4405
|
+
var yaml = __toESM(require("js-yaml"));
|
|
4406
|
+
function parseYaml(content, sourcePath) {
|
|
4407
|
+
try {
|
|
4408
|
+
return yaml.load(content);
|
|
4409
|
+
} catch (err) {
|
|
4410
|
+
const loc = sourcePath ? ` (${sourcePath})` : "";
|
|
4411
|
+
throw new Error(`Failed to parse YAML${loc}: ${String(err)}`);
|
|
4412
|
+
}
|
|
4413
|
+
}
|
|
4414
|
+
function serializeYaml(value) {
|
|
4415
|
+
return yaml.dump(value, {
|
|
4416
|
+
indent: 2,
|
|
4417
|
+
lineWidth: 100,
|
|
4418
|
+
noRefs: true,
|
|
4419
|
+
sortKeys: false
|
|
4420
|
+
});
|
|
4421
|
+
}
|
|
4422
|
+
function readYamlFile(filePath) {
|
|
4423
|
+
const content = readFileOrNull(filePath);
|
|
4424
|
+
if (content === null) return null;
|
|
4425
|
+
return parseYaml(content, filePath);
|
|
4426
|
+
}
|
|
4427
|
+
function writeYamlFile(filePath, value) {
|
|
4428
|
+
writeFile(filePath, serializeYaml(value));
|
|
4429
|
+
}
|
|
4430
|
+
function readJsonFile(filePath) {
|
|
4431
|
+
const content = readFileOrNull(filePath);
|
|
4432
|
+
if (content === null) return null;
|
|
4433
|
+
try {
|
|
4434
|
+
return JSON.parse(content);
|
|
4435
|
+
} catch (err) {
|
|
4436
|
+
throw new Error(`Failed to parse JSON (${filePath}): ${String(err)}`);
|
|
4437
|
+
}
|
|
4438
|
+
}
|
|
4439
|
+
function writeJsonFile(filePath, value) {
|
|
4440
|
+
writeFile(filePath, JSON.stringify(value, null, 2) + "\n");
|
|
4441
|
+
}
|
|
4442
|
+
|
|
4154
4443
|
// src/models/identity-rename.ts
|
|
4155
4444
|
var EDGE = " -> ";
|
|
4156
4445
|
var QUALIFIED_UNIT = /^(\d+:)?([^.\s]+)\.([^.\s]+)$/;
|
|
@@ -4186,7 +4475,7 @@ function rekeyAnchor(rename, anchor) {
|
|
|
4186
4475
|
}
|
|
4187
4476
|
|
|
4188
4477
|
// src/models/project.ts
|
|
4189
|
-
var
|
|
4478
|
+
var path4 = __toESM(require("path"));
|
|
4190
4479
|
var import_zod3 = require("zod");
|
|
4191
4480
|
|
|
4192
4481
|
// src/models/agent.ts
|
|
@@ -4736,7 +5025,7 @@ var ExternalDeclarationSchema = import_zod3.z.object({
|
|
|
4736
5025
|
/**
|
|
4737
5026
|
* The producer's public names this project imports, so its specs may name
|
|
4738
5027
|
* them bare: `['*']` imports every public name the producer exports to this
|
|
4739
|
-
* project, `[
|
|
5028
|
+
* project, `[shared-error]` only those. Plain strings: a malformed entry is
|
|
4740
5029
|
* REPORTED (the declaration's problem), never unreadable.
|
|
4741
5030
|
*/
|
|
4742
5031
|
use: import_zod3.z.array(import_zod3.z.string()).optional(),
|
|
@@ -4939,7 +5228,7 @@ var PACK_EXT_RE = /\.(ya?ml|cjs|js)$/i;
|
|
|
4939
5228
|
function declaredPackNames(config) {
|
|
4940
5229
|
const selection = config.profileSelection;
|
|
4941
5230
|
return [.../* @__PURE__ */ new Set([
|
|
4942
|
-
...(config.extensions?.packs ?? []).map((entry) => typeof entry === "string" ?
|
|
5231
|
+
...(config.extensions?.packs ?? []).map((entry) => typeof entry === "string" ? path4.basename(entry).replace(PACK_EXT_RE, "") : entry.name),
|
|
4943
5232
|
...selection?.requiredPackNames ?? [],
|
|
4944
5233
|
...selection?.defaultPackNames ?? []
|
|
4945
5234
|
])];
|
|
@@ -5237,13 +5526,13 @@ function isPart(config) {
|
|
|
5237
5526
|
|
|
5238
5527
|
// src/config/project-config.ts
|
|
5239
5528
|
function waiDirAt(root) {
|
|
5240
|
-
const wai =
|
|
5241
|
-
const legacy =
|
|
5242
|
-
return !
|
|
5529
|
+
const wai = path5.join(root, ".wai");
|
|
5530
|
+
const legacy = path5.join(root, ".wairon");
|
|
5531
|
+
return !fs3.existsSync(wai) && fs3.existsSync(legacy) ? legacy : wai;
|
|
5243
5532
|
}
|
|
5244
5533
|
function projectConfigFsAdapterAt(rootDir) {
|
|
5245
|
-
const root =
|
|
5246
|
-
const file = () =>
|
|
5534
|
+
const root = path5.resolve(rootDir);
|
|
5535
|
+
const file = () => path5.join(waiDirAt(root), "project.yaml");
|
|
5247
5536
|
return {
|
|
5248
5537
|
readDocument() {
|
|
5249
5538
|
return readYamlFile(file());
|
|
@@ -5257,7 +5546,7 @@ function projectConfigFsAdapterAt(rootDir) {
|
|
|
5257
5546
|
writeFile(file(), editDocumentText(text3, document, root));
|
|
5258
5547
|
},
|
|
5259
5548
|
documentExists() {
|
|
5260
|
-
return
|
|
5549
|
+
return fs3.existsSync(file());
|
|
5261
5550
|
},
|
|
5262
5551
|
readText() {
|
|
5263
5552
|
return readFileOrNull(file());
|
|
@@ -5279,8 +5568,9 @@ function storeOver(adapter, root) {
|
|
|
5279
5568
|
if (!checked.success) {
|
|
5280
5569
|
throw new WaironError(`Refusing to write an invalid .wai/project.yaml at ${root}: ${checked.error.message}`);
|
|
5281
5570
|
}
|
|
5282
|
-
const
|
|
5283
|
-
|
|
5571
|
+
const onDisk = currentDocument();
|
|
5572
|
+
const document = config.partOf !== void 0 ? { schemaVersion: config.schemaVersion, partOf: config.partOf } : withoutDefaultedMaterialization(config, onDisk);
|
|
5573
|
+
adapter.writeDocument(overlayKnownFields(ProjectConfigSchema, document, onDisk));
|
|
5284
5574
|
},
|
|
5285
5575
|
exists() {
|
|
5286
5576
|
return adapter.documentExists();
|
|
@@ -5680,9 +5970,69 @@ function asReadable(document) {
|
|
|
5680
5970
|
if (!isPlainObject(document) || document.partOf === void 0) return document;
|
|
5681
5971
|
return { name: "", createdAt: PART_EPOCH, updatedAt: PART_EPOCH, ...document };
|
|
5682
5972
|
}
|
|
5973
|
+
var MANAGED_AGENT_MARKER = "wairon:managed";
|
|
5974
|
+
var MANAGED_AGENT_NAME = /-(owner|implementer|architect)\.(md|ya?ml)$/;
|
|
5975
|
+
function holdsMaterializedAgentFiles(document, root) {
|
|
5976
|
+
const dirs = new Set(Object.values(DEFAULT_TARGET_DIRS));
|
|
5977
|
+
for (const target of Array.isArray(document.targets) ? document.targets : []) {
|
|
5978
|
+
const outputDir = isPlainObject(target) ? target.outputDir : void 0;
|
|
5979
|
+
if (typeof outputDir === "string") dirs.add(outputDir);
|
|
5980
|
+
}
|
|
5981
|
+
const base = path5.resolve(root);
|
|
5982
|
+
const inside = [];
|
|
5983
|
+
for (const dir of dirs) {
|
|
5984
|
+
const rel2 = path5.relative(base, path5.resolve(base, dir));
|
|
5985
|
+
if (rel2.startsWith("..") || path5.isAbsolute(rel2)) continue;
|
|
5986
|
+
let present = false;
|
|
5987
|
+
try {
|
|
5988
|
+
present = fs3.statSync(path5.join(base, rel2)).isDirectory();
|
|
5989
|
+
} catch {
|
|
5990
|
+
present = false;
|
|
5991
|
+
}
|
|
5992
|
+
if (present) inside.push(rel2 === "" ? "." : rel2.split(path5.sep).join("/"));
|
|
5993
|
+
}
|
|
5994
|
+
if (inside.length === 0) return false;
|
|
5995
|
+
const tracked = trackedFiles(base, inside);
|
|
5996
|
+
const candidates = [];
|
|
5997
|
+
if (tracked === null) {
|
|
5998
|
+
for (const dir of inside) {
|
|
5999
|
+
let names;
|
|
6000
|
+
try {
|
|
6001
|
+
names = fs3.readdirSync(path5.join(base, dir));
|
|
6002
|
+
} catch {
|
|
6003
|
+
continue;
|
|
6004
|
+
}
|
|
6005
|
+
for (const name of names) candidates.push(path5.posix.join(dir, name));
|
|
6006
|
+
}
|
|
6007
|
+
} else {
|
|
6008
|
+
const folders = new Set(inside.map((dir) => path5.posix.normalize(dir)));
|
|
6009
|
+
for (const file of tracked) if (folders.has(path5.posix.dirname(file))) candidates.push(file);
|
|
6010
|
+
}
|
|
6011
|
+
for (const file of candidates) {
|
|
6012
|
+
const head2 = readFileOrNull(path5.join(base, file));
|
|
6013
|
+
if (head2 === null) continue;
|
|
6014
|
+
if (MANAGED_AGENT_NAME.test(path5.posix.basename(file))) return true;
|
|
6015
|
+
if (head2.slice(0, 4096).includes(MANAGED_AGENT_MARKER)) return true;
|
|
6016
|
+
}
|
|
6017
|
+
return false;
|
|
6018
|
+
}
|
|
6019
|
+
function withInferredMaterialization(document, root) {
|
|
6020
|
+
if (!isPlainObject(document) || document.partOf !== void 0) return document;
|
|
6021
|
+
const rules = isPlainObject(document.rules) ? document.rules : void 0;
|
|
6022
|
+
if (rules !== void 0 && hasOwn(rules, "materializeAgentFiles")) return document;
|
|
6023
|
+
if (!holdsMaterializedAgentFiles(document, root)) return document;
|
|
6024
|
+
return { ...document, rules: { ...rules ?? {}, materializeAgentFiles: true } };
|
|
6025
|
+
}
|
|
6026
|
+
function withoutDefaultedMaterialization(config, onDisk) {
|
|
6027
|
+
if (!isPlainObject(onDisk) || config.rules?.materializeAgentFiles !== false) return config;
|
|
6028
|
+
const stated = isPlainObject(onDisk.rules) && hasOwn(onDisk.rules, "materializeAgentFiles");
|
|
6029
|
+
if (stated) return config;
|
|
6030
|
+
const { materializeAgentFiles: _defaulted, ...rules } = config.rules;
|
|
6031
|
+
return { ...config, rules };
|
|
6032
|
+
}
|
|
5683
6033
|
function parseConfig(document, root) {
|
|
5684
6034
|
try {
|
|
5685
|
-
return ProjectConfigSchema.parse(asReadable(document));
|
|
6035
|
+
return ProjectConfigSchema.parse(asReadable(withInferredMaterialization(document, root)));
|
|
5686
6036
|
} catch (e) {
|
|
5687
6037
|
throw new WaironError(
|
|
5688
6038
|
`Invalid .wai/project.yaml: ${e instanceof Error ? e.message : String(e)}
|
|
@@ -6123,15 +6473,15 @@ function indexOver(store, root) {
|
|
|
6123
6473
|
specsDir() {
|
|
6124
6474
|
try {
|
|
6125
6475
|
const declared = store.readField("paths.specsDir");
|
|
6126
|
-
if (declared) return
|
|
6476
|
+
if (declared) return path5.resolve(root, declared);
|
|
6127
6477
|
} catch {
|
|
6128
6478
|
}
|
|
6129
|
-
return
|
|
6479
|
+
return path5.join(waiDirAt(root), "specs");
|
|
6130
6480
|
}
|
|
6131
6481
|
};
|
|
6132
6482
|
}
|
|
6133
6483
|
function projectConfigRepositoryOver(adapter, rootDir) {
|
|
6134
|
-
const root =
|
|
6484
|
+
const root = path5.resolve(rootDir);
|
|
6135
6485
|
const store = storeOver(adapter, root);
|
|
6136
6486
|
const index = indexOver(store, root);
|
|
6137
6487
|
const registry = registryOver(store, root);
|
|
@@ -6324,15 +6674,15 @@ var fs14 = __toESM(require("fs"));
|
|
|
6324
6674
|
var path21 = __toESM(require("path"));
|
|
6325
6675
|
|
|
6326
6676
|
// src/config/paths.ts
|
|
6327
|
-
var
|
|
6328
|
-
var
|
|
6677
|
+
var fs4 = __toESM(require("fs"));
|
|
6678
|
+
var path6 = __toESM(require("path"));
|
|
6329
6679
|
function aiPathsAt(rootDir) {
|
|
6330
|
-
const resolvedRoot =
|
|
6680
|
+
const resolvedRoot = path6.resolve(rootDir);
|
|
6331
6681
|
const aiDirAt2 = (...segments) => {
|
|
6332
|
-
const waiPath =
|
|
6333
|
-
const waironPath =
|
|
6334
|
-
const base = !
|
|
6335
|
-
return
|
|
6682
|
+
const waiPath = path6.join(resolvedRoot, ".wai");
|
|
6683
|
+
const waironPath = path6.join(resolvedRoot, ".wairon");
|
|
6684
|
+
const base = !fs4.existsSync(waiPath) && fs4.existsSync(waironPath) ? waironPath : waiPath;
|
|
6685
|
+
return path6.join(base, ...segments);
|
|
6336
6686
|
};
|
|
6337
6687
|
const specsDir = () => projectConfigRepositoryAt(resolvedRoot).specsDir();
|
|
6338
6688
|
return {
|
|
@@ -6349,12 +6699,12 @@ function aiPathsAt(rootDir) {
|
|
|
6349
6699
|
contextDomainsMd: () => aiDirAt2("context", "domains.md"),
|
|
6350
6700
|
contextWaironGuideMd: () => aiDirAt2("context", "wairon-guide.md"),
|
|
6351
6701
|
specsDir,
|
|
6352
|
-
specsSystem: () =>
|
|
6353
|
-
specsSubsystemsDir: () =>
|
|
6354
|
-
specsComponentsDir: () =>
|
|
6355
|
-
specsInterfacesDir: () =>
|
|
6356
|
-
specsImplementationsDir: () =>
|
|
6357
|
-
specsTypesDir: () =>
|
|
6702
|
+
specsSystem: () => path6.join(specsDir(), ".index.yaml"),
|
|
6703
|
+
specsSubsystemsDir: () => path6.join(specsDir(), "subsystems"),
|
|
6704
|
+
specsComponentsDir: () => path6.join(specsDir(), "components"),
|
|
6705
|
+
specsInterfacesDir: () => path6.join(specsDir(), "interfaces"),
|
|
6706
|
+
specsImplementationsDir: () => path6.join(specsDir(), "implementations"),
|
|
6707
|
+
specsTypesDir: () => path6.join(specsDir(), "types")
|
|
6358
6708
|
};
|
|
6359
6709
|
}
|
|
6360
6710
|
var AI_PATHS = {
|
|
@@ -6388,8 +6738,8 @@ function assertProjectInitialized() {
|
|
|
6388
6738
|
}
|
|
6389
6739
|
|
|
6390
6740
|
// src/core/statehash.ts
|
|
6391
|
-
var
|
|
6392
|
-
var
|
|
6741
|
+
var crypto2 = __toESM(require("crypto"));
|
|
6742
|
+
var path7 = __toESM(require("path"));
|
|
6393
6743
|
|
|
6394
6744
|
// src/utils/canonical-json.ts
|
|
6395
6745
|
function canonicalize(value) {
|
|
@@ -6428,7 +6778,7 @@ function digestTree(keep, asStored = (impl) => impl) {
|
|
|
6428
6778
|
implementations: kind(loadImplementationSpecs().map(asStored)),
|
|
6429
6779
|
types: kind(loadTypeSpecs())
|
|
6430
6780
|
};
|
|
6431
|
-
const digest3 =
|
|
6781
|
+
const digest3 = crypto2.createHash("sha256").update(canonicalize(tree)).digest("hex");
|
|
6432
6782
|
return { algorithm: CONTENT_ALGORITHM, digest: digest3 };
|
|
6433
6783
|
}
|
|
6434
6784
|
function computeStateId() {
|
|
@@ -6448,11 +6798,11 @@ function computeOwnStateId() {
|
|
|
6448
6798
|
return digestTree((id) => (owners.get(id) ?? "") === "", asStored);
|
|
6449
6799
|
}
|
|
6450
6800
|
function isInside(dir, file) {
|
|
6451
|
-
const rel2 =
|
|
6452
|
-
return rel2 !== "" && !rel2.startsWith("..") && !
|
|
6801
|
+
const rel2 = path7.relative(dir, file);
|
|
6802
|
+
return rel2 !== "" && !rel2.startsWith("..") && !path7.isAbsolute(rel2);
|
|
6453
6803
|
}
|
|
6454
6804
|
function fromPartRoot(impl, root, partDir) {
|
|
6455
|
-
const from = (p) =>
|
|
6805
|
+
const from = (p) => path7.isAbsolute(p) ? p : path7.relative(partDir, path7.resolve(root, p)).split(path7.sep).join("/");
|
|
6456
6806
|
return {
|
|
6457
6807
|
...impl,
|
|
6458
6808
|
...impl.sourcePath ? { sourcePath: from(impl.sourcePath) } : {},
|
|
@@ -6465,8 +6815,8 @@ function stateIdEquals(a, b) {
|
|
|
6465
6815
|
}
|
|
6466
6816
|
|
|
6467
6817
|
// src/core/lockfile.ts
|
|
6468
|
-
var
|
|
6469
|
-
var
|
|
6818
|
+
var fs5 = __toESM(require("fs"));
|
|
6819
|
+
var path8 = __toESM(require("path"));
|
|
6470
6820
|
function lockPath() {
|
|
6471
6821
|
return aiDir("lock.json");
|
|
6472
6822
|
}
|
|
@@ -6480,14 +6830,14 @@ function normalizeApprover(value) {
|
|
|
6480
6830
|
}
|
|
6481
6831
|
function readLockRecord() {
|
|
6482
6832
|
try {
|
|
6483
|
-
return normalizeRecord(JSON.parse(
|
|
6833
|
+
return normalizeRecord(JSON.parse(fs5.readFileSync(lockPath(), "utf8")));
|
|
6484
6834
|
} catch {
|
|
6485
6835
|
return null;
|
|
6486
6836
|
}
|
|
6487
6837
|
}
|
|
6488
6838
|
function readLockRecordAt(root) {
|
|
6489
6839
|
try {
|
|
6490
|
-
return normalizeRecord(JSON.parse(
|
|
6840
|
+
return normalizeRecord(JSON.parse(fs5.readFileSync(aiDirAt(root, "lock.json"), "utf8")));
|
|
6491
6841
|
} catch {
|
|
6492
6842
|
return null;
|
|
6493
6843
|
}
|
|
@@ -6498,10 +6848,10 @@ function normalizeRecord(raw) {
|
|
|
6498
6848
|
}
|
|
6499
6849
|
function writeLockRecord(record) {
|
|
6500
6850
|
const p = lockPath();
|
|
6501
|
-
|
|
6851
|
+
fs5.mkdirSync(path8.dirname(p), { recursive: true });
|
|
6502
6852
|
const tmp = `${p}.tmp`;
|
|
6503
|
-
|
|
6504
|
-
|
|
6853
|
+
fs5.writeFileSync(tmp, withLineEndings(p, JSON.stringify(withSortedMaps(record), null, 2) + "\n"));
|
|
6854
|
+
fs5.renameSync(tmp, p);
|
|
6505
6855
|
}
|
|
6506
6856
|
function withSortedMaps(record) {
|
|
6507
6857
|
const sorted = (m) => {
|
|
@@ -6521,8 +6871,8 @@ function withSortedMaps(record) {
|
|
|
6521
6871
|
}
|
|
6522
6872
|
|
|
6523
6873
|
// src/core/spec-files.ts
|
|
6524
|
-
var
|
|
6525
|
-
var
|
|
6874
|
+
var fs6 = __toESM(require("fs"));
|
|
6875
|
+
var path9 = __toESM(require("path"));
|
|
6526
6876
|
var SPEC_FILE_EXTENSION = ".yaml";
|
|
6527
6877
|
function readSpecFile(specPath) {
|
|
6528
6878
|
return readYamlFile(specPath);
|
|
@@ -6534,136 +6884,17 @@ function listSpecFiles(specsDir) {
|
|
|
6534
6884
|
return listFilesRecursive(specsDir, SPEC_FILE_EXTENSION);
|
|
6535
6885
|
}
|
|
6536
6886
|
function removeSpecFile(specPath, specsRoot) {
|
|
6537
|
-
if (!
|
|
6538
|
-
|
|
6539
|
-
pruneEmptyDirs(
|
|
6887
|
+
if (!fs6.existsSync(specPath)) return false;
|
|
6888
|
+
fs6.unlinkSync(specPath);
|
|
6889
|
+
pruneEmptyDirs(path9.dirname(specPath), path9.resolve(specsRoot));
|
|
6540
6890
|
return true;
|
|
6541
6891
|
}
|
|
6542
6892
|
function pruneEmptyDirs(dir, specsRoot) {
|
|
6543
6893
|
let at = dir;
|
|
6544
6894
|
while (at !== specsRoot && at.startsWith(specsRoot)) {
|
|
6545
|
-
if (!
|
|
6546
|
-
|
|
6547
|
-
at =
|
|
6548
|
-
}
|
|
6549
|
-
}
|
|
6550
|
-
|
|
6551
|
-
// src/core/adapters/git-source.ts
|
|
6552
|
-
var import_child_process = require("child_process");
|
|
6553
|
-
var crypto2 = __toESM(require("crypto"));
|
|
6554
|
-
var fs6 = __toESM(require("fs"));
|
|
6555
|
-
var os = __toESM(require("os"));
|
|
6556
|
-
var path8 = __toESM(require("path"));
|
|
6557
|
-
var SourceUnavailable = class extends WaironError {
|
|
6558
|
-
constructor(message) {
|
|
6559
|
-
super(message);
|
|
6560
|
-
this.name = "SourceUnavailable";
|
|
6561
|
-
}
|
|
6562
|
-
};
|
|
6563
|
-
function osUserCache() {
|
|
6564
|
-
if (process.platform === "win32") return process.env.LOCALAPPDATA ?? path8.join(os.homedir(), "AppData", "Local");
|
|
6565
|
-
if (process.platform === "darwin") return path8.join(os.homedir(), "Library", "Caches");
|
|
6566
|
-
return process.env.XDG_CACHE_HOME ?? path8.join(os.homedir(), ".cache");
|
|
6567
|
-
}
|
|
6568
|
-
var cacheRoots = /* @__PURE__ */ new Map();
|
|
6569
|
-
function cacheRoot() {
|
|
6570
|
-
const setting = process.env.WAIRON_CACHE_DIR ?? "";
|
|
6571
|
-
let root = cacheRoots.get(setting);
|
|
6572
|
-
if (root === void 0) {
|
|
6573
|
-
root = setting !== "" ? path8.resolve(setting) : path8.join(osUserCache(), "wairon");
|
|
6574
|
-
cacheRoots.set(setting, root);
|
|
6575
|
-
}
|
|
6576
|
-
return root;
|
|
6577
|
-
}
|
|
6578
|
-
function git(args, cwd) {
|
|
6579
|
-
return (0, import_child_process.execFileSync)("git", args, {
|
|
6580
|
-
cwd,
|
|
6581
|
-
encoding: "utf8",
|
|
6582
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
6583
|
-
env: { ...process.env, GIT_TERMINAL_PROMPT: "0", GIT_ASKPASS: "echo" }
|
|
6584
|
-
});
|
|
6585
|
-
}
|
|
6586
|
-
function reasonOf(e) {
|
|
6587
|
-
const stderr = e?.stderr;
|
|
6588
|
-
const text3 = typeof stderr === "string" && stderr.trim() !== "" ? stderr : e instanceof Error ? e.message : String(e);
|
|
6589
|
-
return text3.trim().split(/\r?\n/).filter((l) => l.trim() !== "").slice(-1)[0] ?? "git failed";
|
|
6590
|
-
}
|
|
6591
|
-
function cacheEntryOf(url, commit2) {
|
|
6592
|
-
const repo = crypto2.createHash("sha256").update(url).digest("hex");
|
|
6593
|
-
return path8.join(cacheRoot(), "git", repo, commit2);
|
|
6594
|
-
}
|
|
6595
|
-
function within(entry, url, commit2, dir) {
|
|
6596
|
-
const at = dir ? path8.join(entry, dir) : entry;
|
|
6597
|
-
if (!fs6.existsSync(at)) {
|
|
6598
|
-
throw new SourceUnavailable(`the commit ${commit2} of ${url} holds no "${dir}"`);
|
|
6599
|
-
}
|
|
6600
|
-
return at;
|
|
6601
|
-
}
|
|
6602
|
-
function fetchInto(tmp, url, commit2) {
|
|
6603
|
-
git(["init", "-q"], tmp);
|
|
6604
|
-
try {
|
|
6605
|
-
git(["fetch", "-q", "--depth", "1", url, commit2], tmp);
|
|
6606
|
-
} catch {
|
|
6607
|
-
git(["fetch", "-q", url, "+refs/heads/*:refs/remotes/source/*", "+refs/tags/*:refs/tags/*"], tmp);
|
|
6608
|
-
}
|
|
6609
|
-
git(["-c", "advice.detachedHead=false", "checkout", "-q", commit2], tmp);
|
|
6610
|
-
fs6.rmSync(path8.join(tmp, ".git"), { recursive: true, force: true });
|
|
6611
|
-
}
|
|
6612
|
-
function fetch(url, commit2, dir) {
|
|
6613
|
-
const entry = cacheEntryOf(url, commit2);
|
|
6614
|
-
if (fs6.existsSync(entry)) return within(entry, url, commit2, dir);
|
|
6615
|
-
fs6.mkdirSync(path8.dirname(entry), { recursive: true });
|
|
6616
|
-
const tmp = `${entry}.tmp-${process.pid}-${crypto2.randomBytes(4).toString("hex")}`;
|
|
6617
|
-
fs6.mkdirSync(tmp, { recursive: true });
|
|
6618
|
-
try {
|
|
6619
|
-
fetchInto(tmp, url, commit2);
|
|
6620
|
-
try {
|
|
6621
|
-
fs6.renameSync(tmp, entry);
|
|
6622
|
-
} catch (e) {
|
|
6623
|
-
if (!fs6.existsSync(entry)) throw e;
|
|
6624
|
-
fs6.rmSync(tmp, { recursive: true, force: true });
|
|
6625
|
-
}
|
|
6626
|
-
} catch (e) {
|
|
6627
|
-
fs6.rmSync(tmp, { recursive: true, force: true });
|
|
6628
|
-
if (fs6.readdirSync(path8.dirname(entry)).length === 0) fs6.rmdirSync(path8.dirname(entry));
|
|
6629
|
-
throw e instanceof SourceUnavailable ? e : new SourceUnavailable(`cannot fetch ${commit2} of ${url}: ${reasonOf(e)}`);
|
|
6630
|
-
}
|
|
6631
|
-
return within(entry, url, commit2, dir);
|
|
6632
|
-
}
|
|
6633
|
-
function resolve7(url, ref) {
|
|
6634
|
-
let listed;
|
|
6635
|
-
try {
|
|
6636
|
-
listed = git(["ls-remote", url, ref ?? "HEAD"]);
|
|
6637
|
-
} catch (e) {
|
|
6638
|
-
throw new SourceUnavailable(`cannot reach ${url}: ${reasonOf(e)}`);
|
|
6639
|
-
}
|
|
6640
|
-
const wanted = commitListed(listed, ref ?? "HEAD");
|
|
6641
|
-
if (!wanted) throw new SourceUnavailable(`${url} lists no ref "${ref ?? "HEAD"}"`);
|
|
6642
|
-
return wanted;
|
|
6643
|
-
}
|
|
6644
|
-
function commitListed(listed, ref) {
|
|
6645
|
-
const lines = listed.split(/\r?\n/).filter((l) => l.trim() !== "").map((l) => l.split(/\s+/));
|
|
6646
|
-
const names = [ref, `refs/heads/${ref}`, `refs/tags/${ref}^{}`, `refs/tags/${ref}`];
|
|
6647
|
-
for (const name of names) {
|
|
6648
|
-
const line = lines.find(([, listedName]) => listedName === name);
|
|
6649
|
-
if (line) return line[0];
|
|
6650
|
-
}
|
|
6651
|
-
return void 0;
|
|
6652
|
-
}
|
|
6653
|
-
function head(directory) {
|
|
6654
|
-
try {
|
|
6655
|
-
return git(["rev-parse", "HEAD"], directory).trim() || null;
|
|
6656
|
-
} catch {
|
|
6657
|
-
return null;
|
|
6658
|
-
}
|
|
6659
|
-
}
|
|
6660
|
-
function repositoryRoot(directory) {
|
|
6661
|
-
let at = path8.resolve(directory);
|
|
6662
|
-
for (; ; ) {
|
|
6663
|
-
if (fs6.existsSync(path8.join(at, ".git"))) return at;
|
|
6664
|
-
const up = path8.dirname(at);
|
|
6665
|
-
if (up === at) return null;
|
|
6666
|
-
at = up;
|
|
6895
|
+
if (!fs6.existsSync(at) || fs6.readdirSync(at).length > 0) return;
|
|
6896
|
+
fs6.rmdirSync(at);
|
|
6897
|
+
at = path9.dirname(at);
|
|
6667
6898
|
}
|
|
6668
6899
|
}
|
|
6669
6900
|
|
|
@@ -8062,7 +8293,11 @@ var GENERIC_ALIASES = /* @__PURE__ */ new Map([
|
|
|
8062
8293
|
["HashMap", { form: "map", arity: 2 }],
|
|
8063
8294
|
["Option", { form: "optional", arity: 1 }],
|
|
8064
8295
|
["Optional", { form: "optional", arity: 1 }],
|
|
8065
|
-
["Promise", { form: "async", arity: 1 }]
|
|
8296
|
+
["Promise", { form: "async", arity: 1 }],
|
|
8297
|
+
// The success-or-failure type the mainstream languages with typed failures
|
|
8298
|
+
// share (Rust, Swift, Kotlin, F#); Rust's spelling is its alias.
|
|
8299
|
+
["result", { form: "result", arity: 2 }],
|
|
8300
|
+
["Result", { form: "result", arity: 2 }]
|
|
8066
8301
|
]);
|
|
8067
8302
|
var NOT_NEUTRAL = /* @__PURE__ */ new Map([
|
|
8068
8303
|
["uuid", "a named value-object holding a string (e.g. order_id)"],
|
|
@@ -8071,7 +8306,6 @@ var NOT_NEUTRAL = /* @__PURE__ */ new Map([
|
|
|
8071
8306
|
["byte", "int, or bytes for binary data"],
|
|
8072
8307
|
["time", "datetime for an instant, or duration for an elapsed time"],
|
|
8073
8308
|
["tuple", "a named value-object"],
|
|
8074
|
-
["result", "the success type as the returns (failures as findings or throws), or a named value-object"],
|
|
8075
8309
|
["error", "a named value-object"],
|
|
8076
8310
|
["never", "void"],
|
|
8077
8311
|
["box", "the inner type (ownership is an implementation detail)"],
|
|
@@ -8266,9 +8500,9 @@ var RawParser = class {
|
|
|
8266
8500
|
}
|
|
8267
8501
|
name += this.ident();
|
|
8268
8502
|
while (this.isP("::") || this.isP(".")) {
|
|
8269
|
-
const
|
|
8503
|
+
const sep15 = this.peek().v;
|
|
8270
8504
|
this.at++;
|
|
8271
|
-
name +=
|
|
8505
|
+
name += sep15 + this.ident();
|
|
8272
8506
|
}
|
|
8273
8507
|
if (!this.isP("<")) return { k: "name", name };
|
|
8274
8508
|
this.at++;
|
|
@@ -8295,6 +8529,10 @@ var RawParser = class {
|
|
|
8295
8529
|
const close2 = this.closingParen();
|
|
8296
8530
|
const after = close2 === -1 ? void 0 : this.tokens[close2 + 1];
|
|
8297
8531
|
if (!after || after.t !== "p" || after.v !== "=>") {
|
|
8532
|
+
if (this.isP(")", 1)) {
|
|
8533
|
+
this.at += 2;
|
|
8534
|
+
return { k: "name", name: "void" };
|
|
8535
|
+
}
|
|
8298
8536
|
this.expect("(");
|
|
8299
8537
|
const inner2 = this.type();
|
|
8300
8538
|
this.expect(")");
|
|
@@ -8403,7 +8641,7 @@ function optionalOf(inner2) {
|
|
|
8403
8641
|
return { form: "optional", args: [inner2] };
|
|
8404
8642
|
}
|
|
8405
8643
|
function inner(ctx) {
|
|
8406
|
-
return { ...ctx, top: false, voidAllowed: false, omittable: false };
|
|
8644
|
+
return { ...ctx, top: false, voidAllowed: false, resultAllowed: false, omittable: false };
|
|
8407
8645
|
}
|
|
8408
8646
|
function normalise(node, ctx, notes) {
|
|
8409
8647
|
switch (node.k) {
|
|
@@ -8437,9 +8675,21 @@ function asyncOf(innerNode, ctx, notes, spelled) {
|
|
|
8437
8675
|
if (!ctx.top || !RETURNS_POSITIONS.has(ctx.position)) {
|
|
8438
8676
|
throw new TypeProblemError("TYPE_POSITION_INVALID", `${spelled} may stand only at the top of a returns, not in a ${ctx.position}`);
|
|
8439
8677
|
}
|
|
8440
|
-
const read2 = normalise(innerNode, { ...inner(ctx), voidAllowed: true }, notes);
|
|
8678
|
+
const read2 = normalise(innerNode, { ...inner(ctx), voidAllowed: true, resultAllowed: true }, notes);
|
|
8441
8679
|
return { form: "async", args: [read2] };
|
|
8442
8680
|
}
|
|
8681
|
+
function resultOf(args, ctx, notes, spelled) {
|
|
8682
|
+
if (!ctx.resultAllowed) {
|
|
8683
|
+
throw new TypeProblemError(
|
|
8684
|
+
"TYPE_POSITION_INVALID",
|
|
8685
|
+
`${spelled} may stand only as a whole returns (or under its async), not in a ${ctx.top ? ctx.position : "collection, union, optional or argument"}`,
|
|
8686
|
+
"the success type here (a failure belongs to a method's returns)"
|
|
8687
|
+
);
|
|
8688
|
+
}
|
|
8689
|
+
const ok = normalise(args[0], { ...inner(ctx), voidAllowed: true }, notes);
|
|
8690
|
+
const err = normalise(args[1], inner(ctx), notes);
|
|
8691
|
+
return { form: "result", args: [ok, err] };
|
|
8692
|
+
}
|
|
8443
8693
|
function bareName(name, ctx, notes) {
|
|
8444
8694
|
const lower = name.toLowerCase();
|
|
8445
8695
|
if (NONES.has(name)) {
|
|
@@ -8477,6 +8727,7 @@ function appliedName(name, args, ctx, notes) {
|
|
|
8477
8727
|
throw new TypeProblemError("TYPE_EXPRESSION_INVALID", `"${name}" takes ${alias.arity} type argument${alias.arity === 1 ? "" : "s"}, ${args.length} given`);
|
|
8478
8728
|
}
|
|
8479
8729
|
if (alias.form === "async") return asyncOf(args[0], ctx, notes, `${name}<...>`);
|
|
8730
|
+
if (alias.form === "result") return resultOf(args, ctx, notes, `${name}<...>`);
|
|
8480
8731
|
if (alias.form === "optional") return optionalOf(normalise(args[0], inner(ctx), notes));
|
|
8481
8732
|
const read2 = args.map((a) => normalise(a, inner(ctx), notes));
|
|
8482
8733
|
if (alias.form === "map") assertMapKey(read2[0], args[0]);
|
|
@@ -8550,6 +8801,8 @@ function canonicalTypeText(expr) {
|
|
|
8550
8801
|
return `${expr.form}<${canonicalTypeText(expr.args[0])}>`;
|
|
8551
8802
|
case "map":
|
|
8552
8803
|
return `map<${canonicalTypeText(expr.args[0])}, ${canonicalTypeText(expr.args[1])}>`;
|
|
8804
|
+
case "result":
|
|
8805
|
+
return `result<${canonicalTypeText(expr.args[0])}, ${canonicalTypeText(expr.args[1])}>`;
|
|
8553
8806
|
case "optional": {
|
|
8554
8807
|
const innerText = canonicalTypeText(expr.args[0]);
|
|
8555
8808
|
return expr.args[0].form === "union" ? `(${innerText})?` : `${innerText}?`;
|
|
@@ -8569,11 +8822,12 @@ function typeNamedRefs(expr) {
|
|
|
8569
8822
|
}
|
|
8570
8823
|
function typeIsMany(expr) {
|
|
8571
8824
|
if (expr.form === "list" || expr.form === "set" || expr.form === "map") return true;
|
|
8572
|
-
if (expr.form === "optional" || expr.form === "async") return typeIsMany(expr.args[0]);
|
|
8825
|
+
if (expr.form === "optional" || expr.form === "async" || expr.form === "result") return typeIsMany(expr.args[0]);
|
|
8573
8826
|
return false;
|
|
8574
8827
|
}
|
|
8575
8828
|
function contextFor(position, omittable2, looseNumber = false) {
|
|
8576
|
-
|
|
8829
|
+
const returns = RETURNS_POSITIONS.has(position);
|
|
8830
|
+
return { position, top: true, voidAllowed: returns, resultAllowed: returns, omittable: omittable2, looseNumber };
|
|
8577
8831
|
}
|
|
8578
8832
|
function parseTypePosition(text3, position, omittable2 = false) {
|
|
8579
8833
|
let raw;
|
|
@@ -8764,6 +9018,47 @@ function typeProblemIntProposal(problem) {
|
|
|
8764
9018
|
if (!parse.expression || parse.problem) return null;
|
|
8765
9019
|
return { specId: problem.specId, kind: problem.kind, path: problem.path, written: problem.written, stored: parse.canonical };
|
|
8766
9020
|
}
|
|
9021
|
+
function stringLiteralValue(text3) {
|
|
9022
|
+
const quote = text3[0];
|
|
9023
|
+
if (quote !== "'" && quote !== '"' && quote !== "`" || text3[text3.length - 1] !== quote) return null;
|
|
9024
|
+
return text3.slice(1, -1).replace(/\\(.)/g, "$1");
|
|
9025
|
+
}
|
|
9026
|
+
function kebabOf(name) {
|
|
9027
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[\s_.]+/g, "-").toLowerCase();
|
|
9028
|
+
}
|
|
9029
|
+
function typeProblemEnumProposal(problem) {
|
|
9030
|
+
if (problem.code !== "TYPE_FORM_UNSUPPORTED") return null;
|
|
9031
|
+
if (problem.specId === void 0 || problem.kind === void 0 || problem.path === void 0) return null;
|
|
9032
|
+
let raw;
|
|
9033
|
+
try {
|
|
9034
|
+
raw = readRaw(problem.written);
|
|
9035
|
+
} catch (e) {
|
|
9036
|
+
if (e instanceof TypeSyntaxError) return null;
|
|
9037
|
+
throw e;
|
|
9038
|
+
}
|
|
9039
|
+
const members = raw.k === "union" ? raw.members : [raw];
|
|
9040
|
+
const values = [];
|
|
9041
|
+
let optional = false;
|
|
9042
|
+
for (const member of members) {
|
|
9043
|
+
if (member.k === "name" && member.args === void 0 && NONES.has(member.name)) {
|
|
9044
|
+
optional = true;
|
|
9045
|
+
continue;
|
|
9046
|
+
}
|
|
9047
|
+
const value = member.k === "literal" ? stringLiteralValue(member.text) : null;
|
|
9048
|
+
if (value === null) return null;
|
|
9049
|
+
if (!values.includes(value)) values.push(value);
|
|
9050
|
+
}
|
|
9051
|
+
if (values.length === 0) return null;
|
|
9052
|
+
return {
|
|
9053
|
+
specId: problem.specId,
|
|
9054
|
+
kind: problem.kind,
|
|
9055
|
+
path: problem.path,
|
|
9056
|
+
written: problem.written,
|
|
9057
|
+
enumId: kebabOf(positionNameOf(problem.path)),
|
|
9058
|
+
values,
|
|
9059
|
+
optional
|
|
9060
|
+
};
|
|
9061
|
+
}
|
|
8767
9062
|
function emptyTypeSpellingFacts() {
|
|
8768
9063
|
return { respellings: [], problems: [] };
|
|
8769
9064
|
}
|
|
@@ -8914,6 +9209,7 @@ function lastSegment(name) {
|
|
|
8914
9209
|
return name.split(/::|\./).filter(Boolean).pop() ?? name;
|
|
8915
9210
|
}
|
|
8916
9211
|
function sameType(code, spec, codeNames) {
|
|
9212
|
+
if (spec.form === "result" && code.form !== "result") return sameType(code, spec.args[0], codeNames);
|
|
8917
9213
|
if (code.form === "primitive" && code.name === "number") return spec.form === "primitive" && (spec.name === "int" || spec.name === "float");
|
|
8918
9214
|
if (code.form === "named" && (spec.form === "named" || spec.form === "applied")) {
|
|
8919
9215
|
if (spec.form === "applied") return false;
|
|
@@ -8950,6 +9246,8 @@ function writeTypeScript(expr) {
|
|
|
8950
9246
|
return expr.args.map(writeTypeScript).join(" | ");
|
|
8951
9247
|
case "async":
|
|
8952
9248
|
return `Promise<${writeTypeScript(expr.args[0])}>`;
|
|
9249
|
+
case "result":
|
|
9250
|
+
return writeTypeScript(expr.args[0]);
|
|
8953
9251
|
case "applied":
|
|
8954
9252
|
return `${expr.name}<${expr.args.map(writeTypeScript).join(", ")}>`;
|
|
8955
9253
|
}
|
|
@@ -8992,6 +9290,7 @@ var typescriptDialect = {
|
|
|
8992
9290
|
"T? \u2192 T | null",
|
|
8993
9291
|
"A | B \u2192 A | B",
|
|
8994
9292
|
"async T \u2192 Promise<T>",
|
|
9293
|
+
"result<T, E> \u2192 T, throwing E on failure (result<void, E> \u2192 void)",
|
|
8995
9294
|
"an enum E \u2192 type E = 'a' | 'b' (a string-literal union alias)",
|
|
8996
9295
|
"a named scalar E holding P \u2192 type E = P (an alias of the primitive)"
|
|
8997
9296
|
];
|
|
@@ -9266,7 +9565,7 @@ function jumpFieldsOf(s) {
|
|
|
9266
9565
|
}
|
|
9267
9566
|
|
|
9268
9567
|
// src/models/code-model.ts
|
|
9269
|
-
var
|
|
9568
|
+
var path10 = __toESM(require("path"));
|
|
9270
9569
|
function pathKey(sourcePath) {
|
|
9271
9570
|
return sourcePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
9272
9571
|
}
|
|
@@ -9340,7 +9639,7 @@ var EXTENSION_LANGUAGE = {
|
|
|
9340
9639
|
".swift": "swift"
|
|
9341
9640
|
};
|
|
9342
9641
|
function languageOfSourcePath(sourcePath) {
|
|
9343
|
-
return EXTENSION_LANGUAGE[
|
|
9642
|
+
return EXTENSION_LANGUAGE[path10.extname(sourcePath).toLowerCase()];
|
|
9344
9643
|
}
|
|
9345
9644
|
function dialectOf(facts) {
|
|
9346
9645
|
return facts.language ? typeDialectFor(facts.language) : null;
|
|
@@ -9353,7 +9652,7 @@ function resolveImport(fromFile, specifier, knownPaths, packages) {
|
|
|
9353
9652
|
const mapped = ownEntry(packages, specifier);
|
|
9354
9653
|
return mapped !== void 0 && knownPaths.has(mapped) ? mapped : void 0;
|
|
9355
9654
|
}
|
|
9356
|
-
const joined = pathKey(
|
|
9655
|
+
const joined = pathKey(path10.posix.normalize(path10.posix.join(path10.posix.dirname(fromFile), specifier)));
|
|
9357
9656
|
const candidates = [
|
|
9358
9657
|
joined,
|
|
9359
9658
|
joined.replace(/\.js$/, ".ts"),
|
|
@@ -9500,7 +9799,7 @@ function exportTargetKey(e) {
|
|
|
9500
9799
|
}
|
|
9501
9800
|
|
|
9502
9801
|
// src/models/project-family.ts
|
|
9503
|
-
var
|
|
9802
|
+
var path11 = __toESM(require("path"));
|
|
9504
9803
|
function walkMemberPath(byKey3, start, segments) {
|
|
9505
9804
|
let node = start;
|
|
9506
9805
|
for (const segment of segments.slice(0, -1)) {
|
|
@@ -9562,15 +9861,15 @@ function keyIn(project, localId) {
|
|
|
9562
9861
|
return project ? `${project}::${localId}` : localId;
|
|
9563
9862
|
}
|
|
9564
9863
|
function approvalKeyIn(file, root, parts) {
|
|
9565
|
-
const resolved =
|
|
9864
|
+
const resolved = path11.resolve(file);
|
|
9566
9865
|
for (const part of parts) {
|
|
9567
9866
|
if (part.directory === void 0) continue;
|
|
9568
|
-
const rel2 =
|
|
9569
|
-
if (rel2 !== "" && !rel2.startsWith("..") && !
|
|
9570
|
-
return `members/${part.alias}/${rel2.split(
|
|
9867
|
+
const rel2 = path11.relative(part.directory, resolved);
|
|
9868
|
+
if (rel2 !== "" && !rel2.startsWith("..") && !path11.isAbsolute(rel2)) {
|
|
9869
|
+
return `members/${part.alias}/${rel2.split(path11.sep).join("/")}`;
|
|
9571
9870
|
}
|
|
9572
9871
|
}
|
|
9573
|
-
return
|
|
9872
|
+
return path11.relative(root, resolved).split(path11.sep).join("/");
|
|
9574
9873
|
}
|
|
9575
9874
|
function familyNode(family, namespace) {
|
|
9576
9875
|
return family.nodes.find((n) => n.namespace === namespace) ?? null;
|
|
@@ -9703,7 +10002,7 @@ var DesignDependencyRoleSchema = import_zod9.z.enum(DESIGN_DEPENDENCY_ROLES);
|
|
|
9703
10002
|
var ExtSchema = import_zod9.z.record(import_zod9.z.unknown());
|
|
9704
10003
|
var TypeExpressionSchema = import_zod9.z.lazy(
|
|
9705
10004
|
() => import_zod9.z.object({
|
|
9706
|
-
form: import_zod9.z.enum(["primitive", "named", "list", "set", "map", "optional", "union", "async", "applied"]),
|
|
10005
|
+
form: import_zod9.z.enum(["primitive", "named", "list", "set", "map", "optional", "union", "async", "applied", "result"]),
|
|
9707
10006
|
name: import_zod9.z.string().optional(),
|
|
9708
10007
|
args: import_zod9.z.array(TypeExpressionSchema)
|
|
9709
10008
|
}).passthrough()
|
|
@@ -9940,7 +10239,7 @@ function resolveNarrativeLabels(methodName, steps) {
|
|
|
9940
10239
|
|
|
9941
10240
|
// src/core/canvas.ts
|
|
9942
10241
|
var fs7 = __toESM(require("fs"));
|
|
9943
|
-
var
|
|
10242
|
+
var path12 = __toESM(require("path"));
|
|
9944
10243
|
|
|
9945
10244
|
// src/core/canvas-layout.ts
|
|
9946
10245
|
function computeLayout(model, collapsed) {
|
|
@@ -10711,9 +11010,9 @@ function embedJson(value) {
|
|
|
10711
11010
|
}
|
|
10712
11011
|
function loadCytoscapeLib() {
|
|
10713
11012
|
const candidates = [
|
|
10714
|
-
|
|
11013
|
+
path12.resolve(__dirname, "..", "templates", "canvas", "cytoscape.min.js"),
|
|
10715
11014
|
// src/core & dist/cli
|
|
10716
|
-
|
|
11015
|
+
path12.resolve(__dirname, "templates", "canvas", "cytoscape.min.js")
|
|
10717
11016
|
// dist (library entry)
|
|
10718
11017
|
];
|
|
10719
11018
|
for (const p of candidates) {
|
|
@@ -15096,17 +15395,17 @@ function safeFilenamePart(value) {
|
|
|
15096
15395
|
|
|
15097
15396
|
// src/core/externals.ts
|
|
15098
15397
|
var fs8 = __toESM(require("fs"));
|
|
15099
|
-
var
|
|
15398
|
+
var path13 = __toESM(require("path"));
|
|
15100
15399
|
function lockPath2(root) {
|
|
15101
|
-
return
|
|
15400
|
+
return path13.join(root, ".wai", "externals.lock.yaml");
|
|
15102
15401
|
}
|
|
15103
15402
|
function snapshotPath(root, alias) {
|
|
15104
|
-
return
|
|
15403
|
+
return path13.join(root, ".wai", "externals", `${alias}.yaml`);
|
|
15105
15404
|
}
|
|
15106
15405
|
function writeAtomically(file, text3) {
|
|
15107
|
-
fs8.mkdirSync(
|
|
15406
|
+
fs8.mkdirSync(path13.dirname(file), { recursive: true });
|
|
15108
15407
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
15109
|
-
fs8.writeFileSync(tmp, text3);
|
|
15408
|
+
fs8.writeFileSync(tmp, withLineEndings(file, text3));
|
|
15110
15409
|
fs8.renameSync(tmp, file);
|
|
15111
15410
|
}
|
|
15112
15411
|
function sortedLock(lock) {
|
|
@@ -15149,7 +15448,7 @@ var externalsFileAdapter = {
|
|
|
15149
15448
|
const file = snapshotPath(getProjectRoot(), alias);
|
|
15150
15449
|
if (!fs8.existsSync(file)) return false;
|
|
15151
15450
|
fs8.unlinkSync(file);
|
|
15152
|
-
const dir =
|
|
15451
|
+
const dir = path13.dirname(file);
|
|
15153
15452
|
if (fs8.existsSync(dir) && fs8.readdirSync(dir).length === 0) fs8.rmdirSync(dir);
|
|
15154
15453
|
return true;
|
|
15155
15454
|
},
|
|
@@ -15282,6 +15581,8 @@ function schemaOfExpression(expr, closureIds) {
|
|
|
15282
15581
|
return { oneOf: expr.args.map((member) => schemaOfExpression(member, closureIds)) };
|
|
15283
15582
|
case "async":
|
|
15284
15583
|
return schemaOfExpression(expr.args[0], closureIds);
|
|
15584
|
+
case "result":
|
|
15585
|
+
return schemaOfExpression(expr.args[0], closureIds);
|
|
15285
15586
|
}
|
|
15286
15587
|
}
|
|
15287
15588
|
function schemaFor(typeRef2, closureIds) {
|
|
@@ -15291,7 +15592,8 @@ function schemaFor(typeRef2, closureIds) {
|
|
|
15291
15592
|
}
|
|
15292
15593
|
function returnsNothing(returns) {
|
|
15293
15594
|
const expr = expressionOf(returns);
|
|
15294
|
-
const
|
|
15595
|
+
const awaited = expr?.form === "async" ? expr.args[0] : expr;
|
|
15596
|
+
const inner2 = awaited?.form === "result" ? awaited.args[0] : awaited;
|
|
15295
15597
|
return inner2?.form === "primitive" && inner2.name === "void";
|
|
15296
15598
|
}
|
|
15297
15599
|
function enumDescription(values) {
|
|
@@ -15743,61 +16045,6 @@ function fromOpenApi(document, projectName) {
|
|
|
15743
16045
|
});
|
|
15744
16046
|
}
|
|
15745
16047
|
|
|
15746
|
-
// src/config/defaults.ts
|
|
15747
|
-
var os2 = __toESM(require("os"));
|
|
15748
|
-
var path13 = __toESM(require("path"));
|
|
15749
|
-
var DEFAULT_TARGET_DIRS = {
|
|
15750
|
-
claude: ".claude/agents",
|
|
15751
|
-
gemini: ".gemini/agents",
|
|
15752
|
-
agy: ".gemini/agents",
|
|
15753
|
-
cursor: ".cursor/rules",
|
|
15754
|
-
copilot: ".github/prompts",
|
|
15755
|
-
codex: ".codex/agents"
|
|
15756
|
-
};
|
|
15757
|
-
function defaultTargetConfig(type) {
|
|
15758
|
-
return {
|
|
15759
|
-
type,
|
|
15760
|
-
outputDir: DEFAULT_TARGET_DIRS[type],
|
|
15761
|
-
enabled: true
|
|
15762
|
-
};
|
|
15763
|
-
}
|
|
15764
|
-
var WAIRON_VERSION = "5.1.1-dev.101";
|
|
15765
|
-
var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
15766
|
-
var ARCHITECT_AGENT_ID = "agent-architect";
|
|
15767
|
-
var ARCHITECT_TEMPLATE_ID = "architect";
|
|
15768
|
-
function globalTemplatesDir(projectOverride) {
|
|
15769
|
-
if (process.env.WAIRON_TEMPLATES_DIR) {
|
|
15770
|
-
return process.env.WAIRON_TEMPLATES_DIR;
|
|
15771
|
-
}
|
|
15772
|
-
if (projectOverride) {
|
|
15773
|
-
return projectOverride;
|
|
15774
|
-
}
|
|
15775
|
-
return path13.join(os2.homedir(), ".wairon", "templates");
|
|
15776
|
-
}
|
|
15777
|
-
var BACKEND_COMMANDS = {
|
|
15778
|
-
claude: "claude",
|
|
15779
|
-
gemini: "gemini"
|
|
15780
|
-
};
|
|
15781
|
-
function backendCommand(backend) {
|
|
15782
|
-
return BACKEND_COMMANDS[backend] ?? backend;
|
|
15783
|
-
}
|
|
15784
|
-
var SUPPORTED_ALIASES = ["wai"];
|
|
15785
|
-
var SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
15786
|
-
"node_modules",
|
|
15787
|
-
"dist",
|
|
15788
|
-
"build",
|
|
15789
|
-
"out",
|
|
15790
|
-
".git",
|
|
15791
|
-
".cache",
|
|
15792
|
-
"coverage",
|
|
15793
|
-
"__pycache__",
|
|
15794
|
-
".venv",
|
|
15795
|
-
"venv",
|
|
15796
|
-
"target",
|
|
15797
|
-
// Rust
|
|
15798
|
-
"vendor"
|
|
15799
|
-
]);
|
|
15800
|
-
|
|
15801
16048
|
// src/core/design-export.ts
|
|
15802
16049
|
var NoSystemSpecError = class extends Error {
|
|
15803
16050
|
constructor() {
|
|
@@ -17292,7 +17539,7 @@ function approvalFinding(entry, config) {
|
|
|
17292
17539
|
return finding(
|
|
17293
17540
|
config,
|
|
17294
17541
|
"MEMBER_DRIFTED",
|
|
17295
|
-
`The member ${named(entry.key)} was approved under an earlier gate identity \u2014 the gate identity gained inputs in
|
|
17542
|
+
`The member ${named(entry.key)} was approved under an earlier gate identity \u2014 the gate identity gained inputs in this release (members' composition subjects, \`composition\`; code conformance moved beside the claim), which says nothing about its design. Re-lock it once at its own root (\`wairon lock\`).`,
|
|
17296
17543
|
entry.key
|
|
17297
17544
|
);
|
|
17298
17545
|
}
|
|
@@ -18298,6 +18545,16 @@ function verifyIntegrity(selection, packDir, where) {
|
|
|
18298
18545
|
message: `Pack "${wanted}" resolved to content that does not match the pinned integrity: expected ${selection.integrity}, found ${actual} (${where}). The pack was modified after it was pinned \u2014 reinstall or re-bundle the pinned version, or update the pin with \`wairon pack use ${selection.name} --pin\` if the change is intended.`
|
|
18299
18546
|
};
|
|
18300
18547
|
}
|
|
18548
|
+
function bundlesSingleFile(bundleDir, storePath) {
|
|
18549
|
+
try {
|
|
18550
|
+
if (!fs12.statSync(storePath).isFile()) return false;
|
|
18551
|
+
const entry = packDirEntry(bundleDir);
|
|
18552
|
+
if (!entry || fs12.readdirSync(bundleDir).length !== 1) return false;
|
|
18553
|
+
return fs12.readFileSync(entry).equals(fs12.readFileSync(storePath));
|
|
18554
|
+
} catch {
|
|
18555
|
+
return false;
|
|
18556
|
+
}
|
|
18557
|
+
}
|
|
18301
18558
|
function detectBundleStoreDrift(selection, bundleDir) {
|
|
18302
18559
|
const bundledVersion = path17.basename(bundleDir);
|
|
18303
18560
|
const installed = resolveInstalledPack(selection.name, bundledVersion);
|
|
@@ -18305,6 +18562,7 @@ function detectBundleStoreDrift(selection, bundleDir) {
|
|
|
18305
18562
|
const bundleDigest = computePackDigest(bundleDir);
|
|
18306
18563
|
const storeDigest = computePackDigest(installed.path);
|
|
18307
18564
|
if (bundleDigest === storeDigest) return void 0;
|
|
18565
|
+
if (bundlesSingleFile(bundleDir, installed.path)) return void 0;
|
|
18308
18566
|
return {
|
|
18309
18567
|
code: "PACK_STORE_DRIFT",
|
|
18310
18568
|
severity: "warning",
|
|
@@ -18863,7 +19121,7 @@ var localIdsRule = {
|
|
|
18863
19121
|
var projectIdentityRule = {
|
|
18864
19122
|
name: "project-identity",
|
|
18865
19123
|
judges: "design",
|
|
18866
|
-
description: "The bound project is keyed on its id, so the id must be declared, well-formed, stable and its own: a project that declares none answers to its display name slugified until it writes one (PROJECT_ID_DEFAULTED), a project whose name yields no id or whose declared id breaks the grammar has nothing reliable to key on (PROJECT_ID_AMBIGUOUS), an id the rename migration moved from the one the lock approved is owed a re-lock and nothing more (PROJECT_ID_RENAMED: the approved id is one of the configuration's previousIds), and any other id that differs from the one the lock approved has moved under everything that keyed on it (PROJECT_ID_CHANGED). It judges the bound project's own identity only. The family half \u2014 a member with a defaulted id (named with its alias as the id to declare), a member with no id, and two members resolving to one id \u2014 is a family check
|
|
19124
|
+
description: "The bound project is keyed on its id, so the id must be declared, well-formed, stable and its own: a project that declares none answers to its display name slugified until it writes one (PROJECT_ID_DEFAULTED), a project whose name yields no id or whose declared id breaks the grammar has nothing reliable to key on (PROJECT_ID_AMBIGUOUS), an id the rename migration moved from the one the lock approved is owed a re-lock and nothing more (PROJECT_ID_RENAMED: the approved id is one of the configuration's previousIds), and any other id that differs from the one the lock approved has moved under everything that keyed on it (PROJECT_ID_CHANGED). It judges the bound project's own identity only. The family half \u2014 a member with a defaulted id (named with its alias as the id to declare), a member with no id, and two members resolving to one id \u2014 is a family check (family_validator.checkMembers), which reads the family root's graph.",
|
|
18867
19125
|
codes: [
|
|
18868
19126
|
{ code: "PROJECT_ID_AMBIGUOUS", defaultSeverity: "warning", summary: "Project has no usable id: its name yields no slug, or its declared id breaks the grammar" },
|
|
18869
19127
|
{ code: "PROJECT_ID_CHANGED", defaultSeverity: "error", summary: "Project id differs from the id the lock approved" },
|
|
@@ -18881,7 +19139,7 @@ function judgeOwnIdentity(ctx, identity2) {
|
|
|
18881
19139
|
ctx.addIssue(
|
|
18882
19140
|
"notice",
|
|
18883
19141
|
"PROJECT_ID_DEFAULTED",
|
|
18884
|
-
`Project "${identity2.name}" declares no id in .wai/project.yaml, so it answers to "${identity2.id}", derived from its display name \u2014 renaming the project would move it. No save writes it for you; declare it with \`id: ${identity2.id}\` (
|
|
19142
|
+
`Project "${identity2.name}" declares no id in .wai/project.yaml, so it answers to "${identity2.id}", derived from its display name \u2014 renaming the project would move it. No save writes it for you; declare it with \`id: ${identity2.id}\` (a project is keyed on an explicit id).`
|
|
18885
19143
|
);
|
|
18886
19144
|
}
|
|
18887
19145
|
const ambiguous = problem("ambiguous");
|
|
@@ -19270,10 +19528,10 @@ function problemMessage(problem, specId) {
|
|
|
19270
19528
|
var typeExpressionsRule = {
|
|
19271
19529
|
name: "type-expressions",
|
|
19272
19530
|
judges: "design",
|
|
19273
|
-
description: "Reports what the scan's type canonicalisation recorded (ctx.typeSpellingFacts), one finding per position, located by spec and path: a stored text that does not parse (TYPE_EXPRESSION_INVALID); one that breaks a position rule \u2014 void or
|
|
19531
|
+
description: "Reports what the scan's type canonicalisation recorded (ctx.typeSpellingFacts), one finding per position, located by spec and path: a stored text that does not parse (TYPE_EXPRESSION_INVALID); one that breaks a position rule \u2014 void, async or result out of place, a map key that is not string, int or an enum, a named scalar's holds that is not one primitive other than void and any, `T??` (TYPE_POSITION_INVALID); one using a form the grammar leaves out \u2014 an inline object shape, an inline function type, a string-literal union, a union mixing in a primitive or a collection, an intersection, a utility type or a tuple (TYPE_FORM_UNSUPPORTED); one naming `number` or a legacy builtin with no neutral meaning (TYPE_NOT_NEUTRAL); and one that is an alias of its canonical spelling (TYPE_SPELLING_STALE, which any save or doctor --fix repairs). Every message names the replacement: `number` asks \"int or float?\" (and says when the doctor repair proposes int), a function type names a signature type, a literal union an enum (which the doctor repair proposes), an inline object or a mixed union a named value-object. The writer refuses all but the stale spelling at write time; on load every one of them is a warning \u2014 these findings are how a tree that held them before the grammar existed is told, while it still loads, stays lockable with its debt in view, and its consumers read the position as opaque any, which never invents a shape. No finding when the run carries no facts (a candidate run).",
|
|
19274
19532
|
codes: [
|
|
19275
|
-
{ code: "TYPE_EXPRESSION_INVALID", defaultSeverity: "
|
|
19276
|
-
{ code: "TYPE_POSITION_INVALID", defaultSeverity: "
|
|
19533
|
+
{ code: "TYPE_EXPRESSION_INVALID", defaultSeverity: "warning", summary: "A structured type position does not parse under the type grammar" },
|
|
19534
|
+
{ code: "TYPE_POSITION_INVALID", defaultSeverity: "warning", summary: "A type position breaks a position rule: void, async or result out of place, a non-scalar map key, or T??" },
|
|
19277
19535
|
{ code: "TYPE_FORM_UNSUPPORTED", defaultSeverity: "warning", summary: "A type position uses a form the grammar leaves out \u2014 inline object, inline function type, string-literal union, union mixing in a primitive or collection, intersection, utility type or tuple \u2014 and the message names its named replacement" },
|
|
19278
19536
|
{ code: "TYPE_NOT_NEUTRAL", defaultSeverity: "warning", summary: "A type position names number (int or float?) or a legacy builtin with no neutral meaning, and the message names the replacement" },
|
|
19279
19537
|
{ code: "TYPE_SPELLING_STALE", defaultSeverity: "warning", summary: "A stored type position is an alias of its canonical spelling; any save or doctor --fix rewrites it" }
|
|
@@ -19283,9 +19541,8 @@ var typeExpressionsRule = {
|
|
|
19283
19541
|
if (!facts) return;
|
|
19284
19542
|
for (const problem of facts.problems) {
|
|
19285
19543
|
if (problem.specId === void 0 || !ctx.isSpecInScope(problem.specId)) continue;
|
|
19286
|
-
const severity = problem.code === "TYPE_EXPRESSION_INVALID" || problem.code === "TYPE_POSITION_INVALID" ? "error" : "warning";
|
|
19287
19544
|
ctx.addIssue(
|
|
19288
|
-
|
|
19545
|
+
"warning",
|
|
19289
19546
|
problem.code,
|
|
19290
19547
|
problemMessage(problem, problem.specId),
|
|
19291
19548
|
problem.specId,
|
|
@@ -19619,7 +19876,7 @@ var publicSurfaceBoundContractRule = {
|
|
|
19619
19876
|
var exportTablesRule = {
|
|
19620
19877
|
name: "export-tables",
|
|
19621
19878
|
judges: "design",
|
|
19622
|
-
description: "Every level's export table must resolve the way a module resolver resolves `export \u2026 from`: one public name binds one target (EXPORT_ID_DUPLICATE when two wildcards \u2014 or two explicit entries \u2014 bind a name to different targets, or two public names share one nameKey, since a bare import could not tell them apart; the name is left out), no chain leads back to itself (EXPORT_CYCLE \u2014 an error for a named chain that never reaches a component or type, a warning for a wildcard cycle, which resolves to its union), every entry names a source and an item that exist, that the source exports and, for an own type, that the level owns \u2014 a subsystem-owned type at L1, a project-level type (no subsystem) for an L0 `{ typeDef }` without `from` \u2014 under a public name of [a-z0-9-_]+ (EXPORT_INVALID), every re-exported component can serve a boundary caller \u2014 a Portal, a gateway being a Portal variant, or an Observer for events (EXPORT_UNCONSUMABLE, at L0 and for L1 re-exports
|
|
19879
|
+
description: "Every level's export table must resolve the way a module resolver resolves `export \u2026 from`: one public name binds one target (EXPORT_ID_DUPLICATE when two wildcards \u2014 or two explicit entries \u2014 bind a name to different targets, or two public names share one nameKey, since a bare import could not tell them apart; the name is left out), no chain leads back to itself (EXPORT_CYCLE \u2014 an error for a named chain that never reaches a component or type, a warning for a wildcard cycle, which resolves to its union), every entry names a source and an item that exist, that the source exports and, for an own type, that the level owns \u2014 a subsystem-owned type at L1, a project-level type (no subsystem) for an L0 `{ typeDef }` without `from` \u2014 under a public name of [a-z0-9-_]+ (EXPORT_INVALID), every re-exported component can serve a boundary caller \u2014 a Portal, a gateway being a Portal variant, or an Observer for events (EXPORT_UNCONSUMABLE, at L0 and for L1 re-exports), and an L0 entry that re-exports a member's or an external's export never declares a wider audience than that export has (EXPORT_WIDENS_AUDIENCE): a re-export can narrow reach, never widen it. The rule judges the facts the export resolver recorded in the scan \u2014 for the bound root's table and every contained member's \u2014 and resolves nothing itself.",
|
|
19623
19880
|
codes: [
|
|
19624
19881
|
{ code: "EXPORT_ID_DUPLICATE", defaultSeverity: "error", summary: "One public name bound to different targets by two wildcard re-exports or two explicit entries" },
|
|
19625
19882
|
{ code: "EXPORT_CYCLE", defaultSeverity: "error", summary: "A re-export chain leads back to itself (a warning for a wildcard cycle, which resolves to its union)" },
|
|
@@ -19781,7 +20038,7 @@ function whyNoRewrite(ref) {
|
|
|
19781
20038
|
var referenceFormsRule = {
|
|
19782
20039
|
name: "reference-forms",
|
|
19783
20040
|
judges: "design",
|
|
19784
|
-
description: "A cross-project reference is `alias::name` (or a bare name a `use` imports): the alias is one of the referring project's members or externals, the name a public name of that project's L0 table. The older forms still bind for one release and are reported (DEPRECATED_REFERENCE_FORM), each naming the spec, where in it, the form as written and the text to write instead (the scan's rewrite: a bare local id, or `alias::publicName`): a leading `::`, which escapes to the root of whatever checkout loads it; `super::`, which climbs the containing projects \u2014 a place in one family, not a name; a member path (`desktop::shell` written inside desktop, `
|
|
20041
|
+
description: "A cross-project reference is `alias::name` (or a bare name a `use` imports): the alias is one of the referring project's members or externals, the name a public name of that project's L0 table. The older forms still bind for one release and are reported (DEPRECATED_REFERENCE_FORM), each naming the spec, where in it, the form as written and the text to write instead (the scan's rewrite: a bare local id, or `alias::publicName`): a leading `::`, which escapes to the root of whatever checkout loads it; `super::`, which climbs the containing projects \u2014 a place in one family, not a name; a member path (`desktop::shell` written inside desktop, `app_core::engine::x` written inside engine), a first segment the referring project does not declare, read from the bound root; and a self-prefix \u2014 the referring project's own id as the first segment, with `::` or `.` (`registry.advisory-channel` inside registry), which `wairon doctor --fix` rewrites to the bare local id. Wairon's own writer emits none of them. A form with no rewrite (its target is out of reach) is reported with the reason. It reads the project graph's authored references, which the scan records before binding erases the form.",
|
|
19785
20042
|
codes: [
|
|
19786
20043
|
{ code: "DEPRECATED_REFERENCE_FORM", defaultSeverity: "notice", summary: "A reference written with a leading ::, super:: or a member path where a local id or alias::name belongs" }
|
|
19787
20044
|
],
|
|
@@ -19812,9 +20069,9 @@ function carriedFields(mount) {
|
|
|
19812
20069
|
var memberDeclarationsRule = {
|
|
19813
20070
|
name: "member-declarations",
|
|
19814
20071
|
judges: "design",
|
|
19815
|
-
description: "A project declares its members in project.yaml `members`, each by one source in one grammar. A deprecated declaration form loads for one release and is reported (DEPRECATED_MOUNT_FORM, notice): the L1 form \u2014 a subsystem carrying projectPath \u2014 naming the mount, its path, the `members` entry that replaces it, and every field it carries that `doctor --fix` will move into the member (or stop on); a mount whose alias `members` also declares is reported as ignored; and
|
|
20072
|
+
description: "A project declares its members in project.yaml `members`, each by one source in one grammar. A deprecated declaration form loads for one release and is reported (DEPRECATED_MOUNT_FORM, notice): the L1 form \u2014 a subsystem carrying projectPath \u2014 naming the mount, its path, the `members` entry that replaces it, and every field it carries that `doctor --fix` will move into the member (or stop on); a mount whose alias `members` also declares is reported as ignored; and the deprecated long-form `path` key, naming the `source` (or shorthand) `doctor --fix` rewrites it to. It also judges what each member of the bound project IS against what is declared or written about it, from the scan's problems at the bound root (a member's own members are that member's gate's): what a member is follows from its content \u2014 an id, an L0 or a lock make it a project, anything else a part \u2014 and every contradiction is ONE finding, MEMBER_KIND_MISMATCH (error), naming the alias and the contradiction: an asserted `as` the content contradicts; a part whose configuration declares project fields (members, externals, rules, extensions, composition, targets) without the id that would make it a project (add the id, or move them to the parent); a non-contained part with no PartOf, or one naming another project (opened alone it could not say what it belongs to); a contained member declaring PartOf; a `use` on a member whose content is a part (its names are already this project's own). A part whose files could not be read \u2014 its directory absent, or its git commit not cached and not fetchable (offline, refused, unknown) \u2014 is PART_UNAVAILABLE (error): the project's own subsystems are missing, so its verdict cannot be a pass. The member findings name no spec: members live in configuration. It reads the project graph's nodes, their legacy mounts, the bound root's declared members and its problems.",
|
|
19816
20073
|
codes: [
|
|
19817
|
-
{ code: "DEPRECATED_MOUNT_FORM", defaultSeverity: "notice", summary: "A member declared in a deprecated form \u2014 an L1 subsystem carrying projectPath, or
|
|
20074
|
+
{ code: "DEPRECATED_MOUNT_FORM", defaultSeverity: "notice", summary: "A member declared in a deprecated form \u2014 an L1 subsystem carrying projectPath, or the long-form `path` key \u2014 which doctor --fix rewrites to a `members` entry with one `source`." },
|
|
19818
20075
|
{ code: "MEMBER_KIND_MISMATCH", defaultSeverity: "error", summary: "What a member's content makes it (an id, L0 or lock: a project; else a part) contradicts what is declared or written about it: an asserted `as`, project fields in a part, a missing or wrong PartOf, a `use` on a part." },
|
|
19819
20076
|
{ code: "PART_UNAVAILABLE", defaultSeverity: "error", summary: "A part's files could not be read (absent directory, unpinned or unfetchable git commit): the project's own subsystems are missing." }
|
|
19820
20077
|
],
|
|
@@ -19852,7 +20109,7 @@ var memberDeclarationsRule = {
|
|
|
19852
20109
|
ctx.addIssue(
|
|
19853
20110
|
"notice",
|
|
19854
20111
|
"DEPRECATED_MOUNT_FORM",
|
|
19855
|
-
`This project declares the member "${member.alias}" with the long-form \`path\` key, which
|
|
20112
|
+
`This project declares the member "${member.alias}" with the long-form \`path\` key, which the one location key replaced: \`source\`. \`doctor --fix\` rewrites it to ${rest ? `\`source: ${written}\` in its long form` : `the shorthand \`${member.alias}: ${written}\``} \u2014 its meaning unchanged.`
|
|
19856
20113
|
);
|
|
19857
20114
|
}
|
|
19858
20115
|
for (const problem of family.problems) {
|
|
@@ -19889,7 +20146,7 @@ function declaration(producer) {
|
|
|
19889
20146
|
var projectBoundariesRule = {
|
|
19890
20147
|
name: "project-boundaries",
|
|
19891
20148
|
judges: "design",
|
|
19892
|
-
description: "The one judge of every cross-project reference the bound project writes (
|
|
20149
|
+
description: "The one judge of every cross-project reference the bound project writes (every project is a crate of its own, reached only through its L0 exports and only as a dependency it declared). It reads each of the project's own references from the project graph \u2014 `::` forms and bare names bound through `use` imports alike \u2014 takes its resolution from resolveCrossProject, decided before any severity from the project's own files alone, and reports every outcome but resolved once, carrying the resolution: forbidden is EXTERNAL_UNDECLARED (the reference reaches a project the referrer declares neither as a member nor as an external, or names a first segment that is none of its subsystems, aliases or foreign providers); missing is EXTERNAL_NOT_EXPORTED (the producer's table \u2014 a contained member's live one, or the external's pin \u2014 has no public name covering the referrer, or not the used method, or exports the type only inside a signature's closure); an ambiguous import is IMPORT_AMBIGUOUS (two imports supply one bare name: two explicit `use` names, or two `*` with no explicit one \u2014 name it `alias::name` or narrow a `use`); unavailable is EXTERNAL_CHECK_UNAVAILABLE (no pin, an unreadable pin, a member absent on disk, or a deprecated form naming no alias \u2014 never a pass). A bare name no import supplies is not this rule's: it is the unresolved-reference finding it always was, with rule_context.importHint appended. A trustedLinks entry naming a subsystem of another project (`alias::sub`) is TRUSTED_LINK_CROSSES_PROJECT: a sanctioned fast lane stays inside one project. An undeclared, unexported or ambiguous reference is an error, since the owner's gate judges it exactly; a reference is never left to a parent's verdict.",
|
|
19893
20150
|
codes: [
|
|
19894
20151
|
{ code: "EXTERNAL_UNDECLARED", defaultSeverity: "error", summary: "A reference reaches another project the referring project declares neither as a member nor as an external, or names a first segment that is none of its subsystems, aliases or foreign providers" },
|
|
19895
20152
|
{ code: "EXTERNAL_NOT_EXPORTED", defaultSeverity: "error", summary: "A reference reaches another project at anything but a public name, or a member of one, whose audience covers the referrer" },
|
|
@@ -22435,7 +22692,7 @@ var reproducibilityRule = {
|
|
|
22435
22692
|
ctx.addIssue(
|
|
22436
22693
|
"warning",
|
|
22437
22694
|
"PACK_SOURCE_UNFETCHABLE",
|
|
22438
|
-
`Pack selection "${selection.name}" records no source and is not bundled, so a fresh machine or CI runner cannot obtain it \u2014 \`wairon pack sync\` has nothing to fetch.
|
|
22695
|
+
`Pack selection "${selection.name}" records no source and is not bundled, so a fresh machine or CI runner cannot obtain it \u2014 \`wairon pack sync\` has nothing to fetch. Commit a copy the repository carries (\`wairon pack bundle ${selection.name}\`, or \`wairon pack bundle --all\` for every selection), or record a URL \`wairon pack sync\` fetches it from (\`wairon pack use ${selection.name} --source <url>\`). A selection \`wairon doctor --fix\` recorded from a pack installed from a local path has no URL to record: bundle it.`
|
|
22439
22696
|
);
|
|
22440
22697
|
}
|
|
22441
22698
|
}
|
|
@@ -23422,10 +23679,22 @@ var invariantBackingRule = {
|
|
|
23422
23679
|
};
|
|
23423
23680
|
|
|
23424
23681
|
// src/core/rules/wiring/invariant-references.ts
|
|
23682
|
+
function unknownInvariantMessage(where, ref, types, aliases) {
|
|
23683
|
+
const parts = splitInvariantRef(ref);
|
|
23684
|
+
const qualifier = parts ? parts.typeRef.lastIndexOf("::") : -1;
|
|
23685
|
+
if (parts && qualifier > 0) {
|
|
23686
|
+
const bare = `${parts.typeRef.slice(qualifier + 2)}.${parts.invariantId}`;
|
|
23687
|
+
if (resolveInvariantRef(bare, types, aliases)) {
|
|
23688
|
+
const prefix = parts.typeRef.slice(0, qualifier);
|
|
23689
|
+
return `${where} asserts invariant "${ref}", whose qualifier "${prefix}::" names no project this one reaches the type through \u2014 the invariant is declared here, so write "${bare}" (a project's own alias, written from inside it, is not a qualifier it reads as its own; \`wairon doctor --fix\` rewrites it in a migrated family).`;
|
|
23690
|
+
}
|
|
23691
|
+
}
|
|
23692
|
+
return `${where} asserts invariant "${ref}", but no entity declares it (expected "<type-id>.<invariant-id>" naming a declared entry in that entity's invariants).`;
|
|
23693
|
+
}
|
|
23425
23694
|
var invariantReferencesRule = {
|
|
23426
23695
|
name: "invariant-references",
|
|
23427
23696
|
judges: "design",
|
|
23428
|
-
description: "Every invariant a narrative step asserts (step.assertsInvariants) must resolve to a declared entity invariant: the reference splits at its last dot into a type reference and an invariant id, and some entity matching that type reference must declare that id. A reference nothing declares is an error \u2014 the step claims a property that exists nowhere.",
|
|
23697
|
+
description: "Every invariant a narrative step asserts (step.assertsInvariants) must resolve to a declared entity invariant: the reference splits at its last dot into a type reference and an invariant id, and some entity matching that type reference must declare that id. A reference nothing declares is an error \u2014 the step claims a property that exists nowhere. The message says which: when the type reference carries a qualifier (`alias::type`) and the bare reference does resolve, the cause is the qualifier \u2014 typically the project's own alias written from inside it \u2014 and the message names it and the bare reference to write instead, never claiming that no entity declares the invariant.",
|
|
23429
23698
|
codes: [
|
|
23430
23699
|
{ code: "UNKNOWN_INVARIANT_REF", defaultSeverity: "error", summary: "A narrative step asserts an invariant that no entity declares" }
|
|
23431
23700
|
],
|
|
@@ -23440,7 +23709,7 @@ var invariantReferencesRule = {
|
|
|
23440
23709
|
ctx.addIssue(
|
|
23441
23710
|
"error",
|
|
23442
23711
|
"UNKNOWN_INVARIANT_REF",
|
|
23443
|
-
`Step ${step.stepNumber} of "${method.name}" in implementation "${impl.id}"
|
|
23712
|
+
unknownInvariantMessage(`Step ${step.stepNumber} of "${method.name}" in implementation "${impl.id}"`, ref, ctx.types, aliases),
|
|
23444
23713
|
impl.id,
|
|
23445
23714
|
isDraftCtx
|
|
23446
23715
|
);
|
|
@@ -29774,8 +30043,8 @@ function generateSequenceDiagram(componentId, methodName, options) {
|
|
|
29774
30043
|
const steps = [...methodImpl.narrative].sort((a, b) => a.stepNumber - b.stepNumber);
|
|
29775
30044
|
for (const step of steps) {
|
|
29776
30045
|
for (const par of parallelArms) {
|
|
29777
|
-
const
|
|
29778
|
-
if (
|
|
30046
|
+
const sep15 = par.sepByStep.get(step.stepNumber);
|
|
30047
|
+
if (sep15 !== void 0) lines.push(` and ${escapeLabel(sep15)}`);
|
|
29779
30048
|
}
|
|
29780
30049
|
switch (step.type) {
|
|
29781
30050
|
case "local":
|
|
@@ -30505,6 +30774,15 @@ function classifySource(world, sourceId, label7, sources) {
|
|
|
30505
30774
|
detail: `"${label7}" re-exports from "${sourceId}", which is neither a subsystem of this project, a member nor a declared external the scan read \u2014 a project re-exports only from its subsystems, members and externals of its family`
|
|
30506
30775
|
};
|
|
30507
30776
|
}
|
|
30777
|
+
function duplicateDetail(entries, index) {
|
|
30778
|
+
const e = entries[index];
|
|
30779
|
+
if (e.from !== void 0 || e.component !== void 0 || e.interface !== void 0 || e.typeDef !== void 0) return null;
|
|
30780
|
+
const name = e.as ?? e.id;
|
|
30781
|
+
if (name === void 0) return null;
|
|
30782
|
+
const other = entries.findIndex((x, i) => i !== index && (x.as ?? x.id) === name);
|
|
30783
|
+
if (other === -1) return null;
|
|
30784
|
+
return `has a duplicate entry "${name}": entry #${index + 1} names no source and no item, while entry #${other + 1} declares the same id \u2014 delete the copy (entry #${index + 1}), or give it a source and an item under an id of its own`;
|
|
30785
|
+
}
|
|
30508
30786
|
function bindOwnType(world, owner, e, problems) {
|
|
30509
30787
|
const type = (world.types.get(e.typeDef) ?? []).find((t) => !t.subsystem);
|
|
30510
30788
|
if (!type) {
|
|
@@ -30576,7 +30854,8 @@ function resolveProjectTable(system, subsystems, components, interfaces, types,
|
|
|
30576
30854
|
const owner = sources.namespace ? sources.namespace : system.name;
|
|
30577
30855
|
const problems = [];
|
|
30578
30856
|
const candidates = [];
|
|
30579
|
-
|
|
30857
|
+
const entries = system.publicInterfaces ?? [];
|
|
30858
|
+
entries.forEach((e, index) => {
|
|
30580
30859
|
const label7 = e.as ?? e.id ?? e.interface ?? e.component ?? e.typeDef ?? `#${index + 1}`;
|
|
30581
30860
|
const source = classifySource(world, sourceOf(world, e), label7, sources);
|
|
30582
30861
|
switch (source.kind) {
|
|
@@ -30602,7 +30881,7 @@ function resolveProjectTable(system, subsystems, components, interfaces, types,
|
|
|
30602
30881
|
return;
|
|
30603
30882
|
}
|
|
30604
30883
|
default:
|
|
30605
|
-
problems.push({ kind: "invalid", owner, publicName: label7, detail: source.detail });
|
|
30884
|
+
problems.push({ kind: "invalid", owner, publicName: label7, detail: duplicateDetail(entries, index) ?? source.detail });
|
|
30606
30885
|
}
|
|
30607
30886
|
});
|
|
30608
30887
|
return settle(owner, "project", candidates, problems);
|
|
@@ -31195,6 +31474,16 @@ function qualifiedTypeNames(typeStr) {
|
|
|
31195
31474
|
}
|
|
31196
31475
|
return out;
|
|
31197
31476
|
}
|
|
31477
|
+
function invariantTypeRef(ref) {
|
|
31478
|
+
const at = ref.lastIndexOf(".");
|
|
31479
|
+
return at > 0 && at < ref.length - 1 ? ref.slice(0, at) : void 0;
|
|
31480
|
+
}
|
|
31481
|
+
function mapInvariantRef(ref, map) {
|
|
31482
|
+
const typeRef2 = invariantTypeRef(ref);
|
|
31483
|
+
if (typeRef2 === void 0 || !typeRef2.includes("::")) return ref;
|
|
31484
|
+
const next = map("type", typeRef2);
|
|
31485
|
+
return next === typeRef2 ? ref : `${next}${ref.slice(typeRef2.length)}`;
|
|
31486
|
+
}
|
|
31198
31487
|
function rawReferences(kind, spec) {
|
|
31199
31488
|
const out = [];
|
|
31200
31489
|
const add2 = (position, value) => {
|
|
@@ -31217,6 +31506,7 @@ function rawReferences(kind, spec) {
|
|
|
31217
31506
|
for (const step of m.narrative) {
|
|
31218
31507
|
const source = step.auth?.from;
|
|
31219
31508
|
if (typeof source === "string" && source.startsWith(COMPONENT_AUTH_SOURCE)) add2("auth", source.slice(COMPONENT_AUTH_SOURCE.length));
|
|
31509
|
+
for (const ref of step.assertsInvariants ?? []) add2("type", invariantTypeRef(ref));
|
|
31220
31510
|
}
|
|
31221
31511
|
}
|
|
31222
31512
|
break;
|
|
@@ -31336,6 +31626,9 @@ function respellStoredReferences(kind, doc, map) {
|
|
|
31336
31626
|
for (const m of list(doc.methods)) {
|
|
31337
31627
|
for (const step of list(m.narrative)) {
|
|
31338
31628
|
at(step, "targetComponent", "narrative");
|
|
31629
|
+
if (Array.isArray(step.assertsInvariants)) {
|
|
31630
|
+
step.assertsInvariants = step.assertsInvariants.map((ref) => typeof ref === "string" ? mapInvariantRef(ref, map) : ref);
|
|
31631
|
+
}
|
|
31339
31632
|
const auth = step.auth;
|
|
31340
31633
|
if (auth && typeof auth.from === "string" && auth.from.startsWith(COMPONENT_AUTH_SOURCE)) {
|
|
31341
31634
|
auth.from = `${COMPONENT_AUTH_SOURCE}${map("auth", auth.from.slice(COMPONENT_AUTH_SOURCE.length))}`;
|
|
@@ -31395,7 +31688,8 @@ function mapRawReferences(kind, spec, map) {
|
|
|
31395
31688
|
return call && next !== call.compId ? `${next}.${call.methodName}` : entry;
|
|
31396
31689
|
})
|
|
31397
31690
|
} : {},
|
|
31398
|
-
narrative: m.narrative.map((
|
|
31691
|
+
narrative: m.narrative.map((authored) => {
|
|
31692
|
+
const step = authored.assertsInvariants ? { ...authored, assertsInvariants: authored.assertsInvariants.map((ref) => mapInvariantRef(ref, map)) } : authored;
|
|
31399
31693
|
const source = step.auth?.from;
|
|
31400
31694
|
if (typeof source !== "string" || !source.startsWith(COMPONENT_AUTH_SOURCE)) return step;
|
|
31401
31695
|
const auth = step.auth;
|
|
@@ -33491,12 +33785,12 @@ var SpecWorkspace = class {
|
|
|
33491
33785
|
return this.relativizeSpec("interface", stored);
|
|
33492
33786
|
}
|
|
33493
33787
|
prepareImplementationForWrite(spec, carry = true) {
|
|
33494
|
-
const
|
|
33788
|
+
const relative20 = this.relativizeSpec("implementation", spec, carry);
|
|
33495
33789
|
const member = this.getSubprojectPrefix(spec.id);
|
|
33496
33790
|
const memberRoot = member ? this.resolveSubprojectForNamespace(member) : null;
|
|
33497
|
-
if (memberRoot) return childRelativeFilePaths(
|
|
33791
|
+
if (memberRoot) return childRelativeFilePaths(relative20, this.rootDir, memberRoot);
|
|
33498
33792
|
const partDir = this.partHolding(this.getImplementationPath(spec.id, spec.contract))?.part.directory;
|
|
33499
|
-
return partDir ? partRelativeFilePaths(
|
|
33793
|
+
return partDir ? partRelativeFilePaths(relative20, this.rootDir, partDir) : relative20;
|
|
33500
33794
|
}
|
|
33501
33795
|
/**
|
|
33502
33796
|
* A type written in its stored form — every type position canonical (one
|
|
@@ -36047,7 +36341,7 @@ function resolveGit(external, config) {
|
|
|
36047
36341
|
const declared = gitDeclarationOf(config, external);
|
|
36048
36342
|
if (!declared) return { ...external, problem: "git producer unavailable: its declaration names no repository" };
|
|
36049
36343
|
try {
|
|
36050
|
-
const commit2 = external.role === "member" && declared.commit ? declared.commit :
|
|
36344
|
+
const commit2 = external.role === "member" && declared.commit ? declared.commit : resolve3(declared.url, declared.ref);
|
|
36051
36345
|
const directory = fetch(declared.url, commit2, declared.dir);
|
|
36052
36346
|
return { ...external, directory: path23.resolve(directory), commit: commit2, problem: void 0 };
|
|
36053
36347
|
} catch (e) {
|
|
@@ -36476,7 +36770,7 @@ function scaffoldPart(alias, memberDir, contained) {
|
|
|
36476
36770
|
}
|
|
36477
36771
|
function createGitMember(alias, source, description, as) {
|
|
36478
36772
|
const url = source.git;
|
|
36479
|
-
const commit2 = source.commit ??
|
|
36773
|
+
const commit2 = source.commit ?? resolve3(url, source.ref);
|
|
36480
36774
|
const dir = fetch(url, commit2, source.dir);
|
|
36481
36775
|
const kind = holdsProjectContent(dir) ? "project" : "part";
|
|
36482
36776
|
if (as !== void 0 && as !== kind) {
|
|
@@ -37436,7 +37730,7 @@ function advanceMember(alias, ref, commit2, dryRun) {
|
|
|
37436
37730
|
const url = member.source.git;
|
|
37437
37731
|
const from = member.source.commit ?? "";
|
|
37438
37732
|
const followed = commit2 === void 0 ? ref ?? member.source.ref : void 0;
|
|
37439
|
-
const to = commit2 ??
|
|
37733
|
+
const to = commit2 ?? resolve3(url, followed);
|
|
37440
37734
|
const after = specContents(fetch(url, to, member.source.dir));
|
|
37441
37735
|
const before = specContents(from !== "" ? fetch(url, from, member.source.dir) : null);
|
|
37442
37736
|
const added = [...after.keys()].filter((f) => !before.has(f)).sort();
|
|
@@ -38977,17 +39271,17 @@ function memberHop(agentId) {
|
|
|
38977
39271
|
const alias = agentId.slice(0, at);
|
|
38978
39272
|
const member = graph().nodes.find((n) => n.parent === "" && n.mountAlias === alias);
|
|
38979
39273
|
if (!member) return null;
|
|
38980
|
-
const
|
|
38981
|
-
return { alias, rest: agentId.slice(at + 2), directory: member.directory, relative:
|
|
39274
|
+
const relative20 = path29.relative(getProjectRoot(), member.directory).split(path29.sep).join("/");
|
|
39275
|
+
return { alias, rest: agentId.slice(at + 2), directory: member.directory, relative: relative20 };
|
|
38982
39276
|
}
|
|
38983
|
-
function throughMount(brief, alias,
|
|
38984
|
-
const under = (p) => path29.posix.join(
|
|
39277
|
+
function throughMount(brief, alias, relative20) {
|
|
39278
|
+
const under = (p) => path29.posix.join(relative20, p);
|
|
38985
39279
|
return {
|
|
38986
39280
|
...brief,
|
|
38987
39281
|
agentId: `${alias}::${brief.agentId}`,
|
|
38988
39282
|
ownedPaths: brief.ownedPaths.map(under),
|
|
38989
39283
|
...brief.readPaths ? { readPaths: brief.readPaths.map(under) } : {},
|
|
38990
|
-
root: brief.root ? under(brief.root) :
|
|
39284
|
+
root: brief.root ? under(brief.root) : relative20
|
|
38991
39285
|
};
|
|
38992
39286
|
}
|
|
38993
39287
|
function loadRegistry() {
|
|
@@ -39266,19 +39560,19 @@ function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
|
|
|
39266
39560
|
const backupDir = path34.join(root, BACKUP_DIR, timestampSlug());
|
|
39267
39561
|
let backedUp = false;
|
|
39268
39562
|
for (const rootRel of roots) {
|
|
39269
|
-
const
|
|
39270
|
-
const stagedSpecDir = path34.join(stagingDir,
|
|
39563
|
+
const relative20 = rootRel === "." ? "" : rootRel;
|
|
39564
|
+
const stagedSpecDir = path34.join(stagingDir, relative20, ".wai");
|
|
39271
39565
|
if (!fs20.existsSync(stagedSpecDir)) continue;
|
|
39272
|
-
const liveSpecDir = liveSpecDirFor(path34.join(root,
|
|
39566
|
+
const liveSpecDir = liveSpecDirFor(path34.join(root, relative20));
|
|
39273
39567
|
if (backupExisting && liveSpecDir && fs20.existsSync(liveSpecDir)) {
|
|
39274
|
-
const backupTarget = path34.join(backupDir,
|
|
39568
|
+
const backupTarget = path34.join(backupDir, relative20, path34.basename(liveSpecDir));
|
|
39275
39569
|
fs20.mkdirSync(path34.dirname(backupTarget), { recursive: true });
|
|
39276
39570
|
fs20.renameSync(liveSpecDir, backupTarget);
|
|
39277
39571
|
backedUp = true;
|
|
39278
39572
|
} else if (liveSpecDir && fs20.existsSync(liveSpecDir)) {
|
|
39279
39573
|
fs20.rmSync(liveSpecDir, { recursive: true, force: true });
|
|
39280
39574
|
}
|
|
39281
|
-
const target = path34.join(root,
|
|
39575
|
+
const target = path34.join(root, relative20, ".wai");
|
|
39282
39576
|
fs20.mkdirSync(path34.dirname(target), { recursive: true });
|
|
39283
39577
|
if (fs20.existsSync(target)) fs20.rmSync(target, { recursive: true, force: true });
|
|
39284
39578
|
fs20.renameSync(stagedSpecDir, target);
|
|
@@ -39557,7 +39851,7 @@ function repairTypeSpellings(apply3) {
|
|
|
39557
39851
|
const key = `${kind}:${specId}`;
|
|
39558
39852
|
let repair = repairs.get(key);
|
|
39559
39853
|
if (!repair) {
|
|
39560
|
-
repair = { specId, kind, rewritten: [], proposals: [], authorNeeded: [] };
|
|
39854
|
+
repair = { specId, kind, rewritten: [], proposals: [], enumProposals: [], authorNeeded: [] };
|
|
39561
39855
|
repairs.set(key, repair);
|
|
39562
39856
|
}
|
|
39563
39857
|
return repair;
|
|
@@ -39572,7 +39866,9 @@ function repairTypeSpellings(apply3) {
|
|
|
39572
39866
|
if (problem.specId === void 0 || problem.kind === void 0 || !own(problem.specId)) continue;
|
|
39573
39867
|
const repair = repairOf(problem.specId, problem.kind);
|
|
39574
39868
|
const proposal = typeProblemIntProposal(problem);
|
|
39869
|
+
const enumProposal = proposal ? null : typeProblemEnumProposal(problem);
|
|
39575
39870
|
if (proposal) repair.proposals.push(proposal);
|
|
39871
|
+
else if (enumProposal) repair.enumProposals.push(enumProposal);
|
|
39576
39872
|
else repair.authorNeeded.push(problem);
|
|
39577
39873
|
}
|
|
39578
39874
|
const planned = [...repairs.values()];
|
|
@@ -39719,7 +40015,7 @@ ${headline} \u2014 approved ${approval.lockedAt} by ${by}:
|
|
|
39719
40015
|
return quiet;
|
|
39720
40016
|
}
|
|
39721
40017
|
}
|
|
39722
|
-
var GATE_UPGRADE_NOTE = "the gate identity gained inputs in
|
|
40018
|
+
var GATE_UPGRADE_NOTE = "the gate identity gained inputs in this release (members' composition subjects, `composition`; code conformance moved beside the claim)";
|
|
39723
40019
|
function memberNotes(approvals) {
|
|
39724
40020
|
const own = approvals.find((a) => a.key === "");
|
|
39725
40021
|
const direct = approvals.filter((a) => a.parent === "" && a.as !== "part");
|
|
@@ -39801,16 +40097,16 @@ var GLOBAL_GUIDE_BODY = `## wairon \u2014 Spec-Driven Development (optional)
|
|
|
39801
40097
|
If \`.wai/specs/\` exists, the wairon SDD workflow is active; otherwise ignore it. wairon does not orchestrate sessions \u2014 it equips yours.
|
|
39802
40098
|
|
|
39803
40099
|
### In SDD Projects:
|
|
39804
|
-
- **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).
|
|
40100
|
+
- **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). Agent files under \`.claude/agents/\` are an opt-in generated view (\`rules.materializeAgentFiles\`) \u2014 never edit them; agents are served as live briefs.
|
|
39805
40101
|
- **Validation**: Conformance checks (stereotype rules, cycle checks, reference integrity) are run via the \`sdd_validate_tree\` MCP tool.
|
|
39806
40102
|
- **Operating Rules**:
|
|
39807
40103
|
1. **Skills**: Use \`sdd-architect\` to design (and \`sdd-implement\`, \`sdd-narrative\`, \`sdd-auditor\`). Refer to project's local guide file for detailed constraints.
|
|
39808
40104
|
2. **MCP Tools Only**: Author/validate specs *only* via \`sdd_*\` tools (e.g. \`sdd_initialize_system\`, \`sdd_validate_tree\`).
|
|
39809
40105
|
3. **No CLI Exec**: Do not run the \`wairon\` CLI (human tool). Use MCP tools \`sdd_validate_tree\` and \`sdd_get_status\` instead.
|
|
39810
40106
|
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>\`).
|
|
39811
|
-
5. **Design First**: Complete spec and pass \`sdd_validate_tree\` before writing code.
|
|
40107
|
+
5. **Design First, then approval**: Complete the spec and pass \`sdd_validate_tree\`, then ask the human to approve it with \`wairon lock\` before writing code. Approval is the lock record (\`.wai/lock.json\`), not a spec's \`status\` \u2014 the lock does not rewrite statuses; \`sdd_get_status\` reports the approval state.
|
|
39812
40108
|
6. **Consistency**: Code must match L3 interfaces and L5 narratives exactly. If the spec is wrong, stop and update the spec.
|
|
39813
|
-
7. **Members & References**: A project may
|
|
40109
|
+
7. **Members & References**: A project may declare **members** in its \`.wai/project.yaml\` \`members\` (create one with \`sdd_add_member\`). A **part** (the default) stores some of this project's subsystems in another folder or repository: local ids, this project's lock. A **project** member is an independent boundary with its own spec tree and lock, designed from its own root. Reference what another project exports as \`alias::name\` (the alias is a member or a declared external, the name a public name of its L0 export table); an id without \`::\` is local. A leading \`::\`, \`super::\`, member paths and an L1 subsystem carrying \`projectPath\` are deprecated: they still resolve for one release, are reported, and \`wairon doctor --fix\` rewrites them.`;
|
|
39814
40110
|
var LOCAL_GUIDE_BODY = `## Wairon \u2014 Spec-Driven Development (you are operating inside it)
|
|
39815
40111
|
|
|
39816
40112
|
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.
|
|
@@ -39827,7 +40123,8 @@ This project uses **wairon**. System specs live under \`.wai/specs/\` (L0 System
|
|
|
39827
40123
|
- **\`alias::name\`**: An id without \`::\` is local to the project that writes it. Anything another project provides is referenced as \`alias::name\` \u2014 the alias is one of your members or declared \`externals\`, the name a public name in that project's L0 export table. A reference to something it does not export is reported (\`EXTERNAL_NOT_EXPORTED\`).
|
|
39828
40124
|
- **Deprecated forms** (they still resolve for one release, are reported, and \`wairon doctor --fix\` rewrites them): a leading \`::\` (\`::shared::error-type\`), \`super::\` (\`super::sibling_comp\`), member paths (\`billing::invoice::invoice_portal\`), and an L1 subsystem carrying \`projectPath\` (\`DEPRECATED_MOUNT_FORM\`).
|
|
39829
40125
|
- **Do not run the \`wairon\` CLI**: Use \`sdd_validate_tree\` and \`sdd_get_status\` instead of CLI commands.
|
|
39830
|
-
- **Handoff to implementation**: Once design is complete and validates cleanly, tell the human: *"The specs are complete and validate. Please run \`wairon lock\` to
|
|
40126
|
+
- **Handoff to implementation**: Once design is complete and validates cleanly, tell the human: *"The specs are complete and validate. Please run \`wairon lock\` to approve them, and commit \`.wai/lock.json\`."* The lock records the approval; it does not rewrite spec files or their \`status\`. No session restart is needed after the lock \u2014 delegate implementation right away via the \`sdd-delegate\` skill.
|
|
40127
|
+
- **Approval, not status**: a design is ready to implement when it is APPROVED \u2014 \`sdd_get_status\` reports the approval state (approved, or which specs changed since), and \`wairon lock-check\` gives the same verdict in CI. A spec's \`status\` (draft/design/complete) is authoring readiness only; never wait for it to become \`complete\`.
|
|
39831
40128
|
- **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.
|
|
39832
40129
|
|
|
39833
40130
|
### Rules (enforced by \`sdd_validate_tree\`)
|
|
@@ -40296,7 +40593,7 @@ function getStatusReport(options = {}, decor) {
|
|
|
40296
40593
|
}
|
|
40297
40594
|
const own = options.approvals?.find((a) => a.key === "");
|
|
40298
40595
|
const hasMembers = (options.approvals ?? []).some((a) => a.key !== "" && a.as !== "part");
|
|
40299
|
-
if (own && (own.state !== "never" || hasMembers)) output += `${mark.layer("system", "Approval:")} this project is ${own.state}${own.upgraded ? " (locked under
|
|
40596
|
+
if (own && (own.state !== "never" || hasMembers)) output += `${mark.layer("system", "Approval:")} this project is ${own.state}${own.upgraded ? " (locked under an earlier gate identity \u2014 re-lock once)" : ""}
|
|
40300
40597
|
`;
|
|
40301
40598
|
return { text: output, failed: false };
|
|
40302
40599
|
}
|
|
@@ -41478,7 +41775,7 @@ var KINDS_AT = {
|
|
|
41478
41775
|
auth: ["implementation"],
|
|
41479
41776
|
subsystem: ["component", "type"],
|
|
41480
41777
|
group: ["type"],
|
|
41481
|
-
type: ["interface", "type"]
|
|
41778
|
+
type: ["interface", "type", "implementation"]
|
|
41482
41779
|
};
|
|
41483
41780
|
function kindOf(ref) {
|
|
41484
41781
|
const kinds = KINDS_AT[ref.position] ?? ["component", "interface", "implementation", "type", "subsystem"];
|
|
@@ -42558,7 +42855,7 @@ function emptyPlan(family, request) {
|
|
|
42558
42855
|
const whole = !(reach2 !== null && !reach2.parentReach);
|
|
42559
42856
|
return { request, familyRoot: familyNode(family, "").directory, whole, edits: [], refusals: [], changes: [], relock: [], notes: [] };
|
|
42560
42857
|
}
|
|
42561
|
-
var
|
|
42858
|
+
var posix5 = (p) => p.split(path43.sep).join("/");
|
|
42562
42859
|
var refuse = (plan6, code, project, detail) => {
|
|
42563
42860
|
plan6.refusals.push({ code, project, detail });
|
|
42564
42861
|
};
|
|
@@ -42636,7 +42933,7 @@ function attachEdits(plan6, family, bound2, dir, alias, bindingConfig) {
|
|
|
42636
42933
|
edit(plan6, key, "id", `id: ${id} (declared${approved !== void 0 ? ", the id its lock approved" : ""})`, { root: dir, call: "setId", args: [id] });
|
|
42637
42934
|
}
|
|
42638
42935
|
if (!attached) plan6.notes.push(`${dir} has no project.yaml of its own \u2014 \`wairon doctor --fix\` writes one declaring "${alias}" as its id`);
|
|
42639
|
-
const relPath =
|
|
42936
|
+
const relPath = posix5(path43.relative(familyNode(family, bound2).directory, dir));
|
|
42640
42937
|
const declaration2 = { path: relPath, ...plan6.request.description !== void 0 ? { description: plan6.request.description } : {} };
|
|
42641
42938
|
edit(plan6, bound2, "member", `members: ${alias} \u2192 ${relPath} (an existing project, kept as it is)`, { root: familyNode(family, bound2).directory, call: "declareMember", args: [alias, declaration2] });
|
|
42642
42939
|
for (const requirement of requiredPolicies(bindingConfig)) {
|
|
@@ -42683,8 +42980,8 @@ function planDetach(family, bound2, request) {
|
|
|
42683
42980
|
if (plan6.request.widen) planWidening(plan6, member, narrow);
|
|
42684
42981
|
if (plan6.refusals.length > 0) return plan6;
|
|
42685
42982
|
const boundDir = familyNode(family, bound2).directory;
|
|
42686
|
-
const held = memberDeclarationOf(configAt3(boundDir)?.members?.[alias] ??
|
|
42687
|
-
const declared = { ...held, path: memberLocationOf(held) ??
|
|
42983
|
+
const held = memberDeclarationOf(configAt3(boundDir)?.members?.[alias] ?? posix5(path43.relative(boundDir, member.directory)));
|
|
42984
|
+
const declared = { ...held, path: memberLocationOf(held) ?? posix5(path43.relative(boundDir, member.directory)) };
|
|
42688
42985
|
detachEdits(plan6, family, bound2, member, alias, declared, consumers, outward);
|
|
42689
42986
|
return plan6;
|
|
42690
42987
|
}
|
|
@@ -42724,7 +43021,7 @@ function planWidening(plan6, member, narrow) {
|
|
|
42724
43021
|
function detachEdits(plan6, family, bound2, member, alias, declared, consumers, outward) {
|
|
42725
43022
|
const boundDir = familyNode(family, bound2).directory;
|
|
42726
43023
|
const relocation = plan6.request.relocation;
|
|
42727
|
-
const sourceTo = (from, producer) => relocation ? { hosted: producer.id ?? producer.mountAlias ?? producer.namespace } : { path:
|
|
43024
|
+
const sourceTo = (from, producer) => relocation ? { hosted: producer.id ?? producer.mountAlias ?? producer.namespace } : { path: posix5(path43.relative(from, producer.directory)) };
|
|
42728
43025
|
const where = (source) => source.hosted !== void 0 ? `hosted record ${source.hosted}` : source.path;
|
|
42729
43026
|
edit(plan6, bound2, "member", `members: ${alias} removed (${declared.path} ${relocation ? `moves to ${relocation.to}` : "stays as it is"})`, { root: boundDir, call: "removeMember", args: [alias] });
|
|
42730
43027
|
const boundSource = relocation ? sourceTo(boundDir, member) : { path: declared.path };
|
|
@@ -42798,7 +43095,7 @@ function adoptEdits(plan6, family, bound2, alias, external, dir, at = dir) {
|
|
|
42798
43095
|
edit(plan6, bound2, "pin", `pin ${alias} removed (the family supplies it now)`, { root: boundDir, call: "unpin", args: [alias] });
|
|
42799
43096
|
edit(plan6, bound2, "external", `externals: ${alias} removed`, { root: boundDir, call: "removeExternal", args: [alias] });
|
|
42800
43097
|
const declaration2 = {
|
|
42801
|
-
path:
|
|
43098
|
+
path: posix5(path43.relative(boundDir, at)),
|
|
42802
43099
|
...external.description !== void 0 ? { description: external.description } : {},
|
|
42803
43100
|
...external.use && external.use.length > 0 ? { use: external.use } : {}
|
|
42804
43101
|
};
|
|
@@ -42909,7 +43206,7 @@ var KINDS_AT2 = {
|
|
|
42909
43206
|
auth: ["implementation"],
|
|
42910
43207
|
subsystem: ["component", "type"],
|
|
42911
43208
|
group: ["type"],
|
|
42912
|
-
type: ["interface", "type"]
|
|
43209
|
+
type: ["interface", "type", "implementation"]
|
|
42913
43210
|
};
|
|
42914
43211
|
function kindOf2(ref) {
|
|
42915
43212
|
const kinds = KINDS_AT2[ref.position] ?? ["component", "interface", "implementation", "type", "subsystem"];
|
|
@@ -43110,7 +43407,7 @@ function emptyPlan3(family, request) {
|
|
|
43110
43407
|
const whole = !(reach2 !== null && !reach2.parentReach);
|
|
43111
43408
|
return { request, familyRoot: familyNode(family, "").directory, whole, edits: [], refusals: [], changes: [], relock: [], notes: [] };
|
|
43112
43409
|
}
|
|
43113
|
-
var
|
|
43410
|
+
var posix6 = (p) => p.split(path45.sep).join("/");
|
|
43114
43411
|
var label6 = (key) => key === "" ? "the top project" : `"${key}"`;
|
|
43115
43412
|
var refuse3 = (plan6, code, project, detail) => {
|
|
43116
43413
|
plan6.refusals.push({ code, project, detail });
|
|
@@ -43141,7 +43438,7 @@ var KINDS_AT3 = {
|
|
|
43141
43438
|
auth: ["implementation"],
|
|
43142
43439
|
subsystem: ["component", "type"],
|
|
43143
43440
|
group: ["type"],
|
|
43144
|
-
type: ["interface", "type"]
|
|
43441
|
+
type: ["interface", "type", "implementation"]
|
|
43145
43442
|
};
|
|
43146
43443
|
function kindOf3(ref) {
|
|
43147
43444
|
const kinds = KINDS_AT3[ref.position] ?? ["component", "interface", "implementation", "type", "subsystem"];
|
|
@@ -43320,7 +43617,7 @@ function respellConsumer(plan6, family, c, own) {
|
|
|
43320
43617
|
function repointConsumer(plan6, c, boundNode2) {
|
|
43321
43618
|
const declared = configAt5(c.node.directory)?.externals?.[c.alias];
|
|
43322
43619
|
const project = boundNode2.id !== void 0 && boundNode2.id !== c.alias ? boundNode2.id : null;
|
|
43323
|
-
const source = declared?.source ? { path:
|
|
43620
|
+
const source = declared?.source ? { path: posix6(path45.relative(c.node.directory, boundNode2.directory)) } : null;
|
|
43324
43621
|
edit3(plan6, c.node.namespace, "external", `externals: ${c.alias} names ${boundNode2.id ?? "the top project"}${source ? ` at ${source.path}` : ""}`, { root: c.node.directory, call: "repointExternal", args: [c.alias, project, source] });
|
|
43325
43622
|
plan6.notes.push(`${label6(c.node.namespace)}'s pin of "${c.alias}" is taken again against ${boundNode2.id ?? "the top project"}: the producer changed, so its lock reads drifted until it is re-locked.`);
|
|
43326
43623
|
return c;
|
|
@@ -43356,8 +43653,8 @@ function planExternalize(family, bound2, request) {
|
|
|
43356
43653
|
plan6,
|
|
43357
43654
|
bound2,
|
|
43358
43655
|
"move",
|
|
43359
|
-
`externalize ${id}: its specs into ${joined ? `the part "${joined.alias}"` : `a part at ${
|
|
43360
|
-
{ root: node.directory, call: "externalizeSubsystem", args: [id,
|
|
43656
|
+
`externalize ${id}: its specs into ${joined ? `the part "${joined.alias}"` : `a part at ${posix6(path45.relative(node.directory, dir))}, declared as "${id}"`} \u2014 a storage move; no reference, export or pin changes`,
|
|
43657
|
+
{ root: node.directory, call: "externalizeSubsystem", args: [id, posix6(path45.relative(node.directory, dir))] }
|
|
43361
43658
|
);
|
|
43362
43659
|
plan6.notes.push("Source code is not moved: move the subsystem's code yourself if it should live beside its specs.");
|
|
43363
43660
|
return plan6;
|
|
@@ -43366,7 +43663,7 @@ function planExternalize(family, bound2, request) {
|
|
|
43366
43663
|
crossingsBack(plan6, family, bound2, subKey);
|
|
43367
43664
|
checkSiblings(plan6, family, bound2, id, subKey, table);
|
|
43368
43665
|
if (plan6.refusals.length > 0) return plan6;
|
|
43369
|
-
edit3(plan6, bound2, "move", `externalize ${id} as a project: its specs into a member at ${
|
|
43666
|
+
edit3(plan6, bound2, "move", `externalize ${id} as a project: its specs into a member at ${posix6(path45.relative(node.directory, dir))}, declared as "${id}", then promoted`, { root: node.directory, call: "externalizeSubsystem", args: [id, posix6(path45.relative(node.directory, dir)), "project"] });
|
|
43370
43667
|
edit3(plan6, id, "pin", `pin the new member's externals (its external for ${node.id ?? "this project"}, when its specs reference it)`, { root: dir, call: "pinExternals", args: [[]] });
|
|
43371
43668
|
plan6.notes.push("Source code is not moved: move the subsystem's code into the member yourself.");
|
|
43372
43669
|
return plan6;
|
|
@@ -44495,7 +44792,7 @@ function errText(message) {
|
|
|
44495
44792
|
function errMessage(e) {
|
|
44496
44793
|
return errText(e instanceof Error ? e.message : String(e));
|
|
44497
44794
|
}
|
|
44498
|
-
var TYPE_REF_GRAMMAR = (what) => `${what}, in the neutral type grammar: a primitive (string, int, float, bool, bytes, date, datetime, duration, any; void only as a whole returns), a defined type id ("billing.Invoice", "alias::name"), list<T>, set<T>, map<K, V> (K is string, int or an enum), T? (T or no value), a union of NAMED types ("Invoice | Receipt"), and on a returns only, async T ("async void"). Every named type must resolve. TypeScript spellings are accepted and respelled \u2014 "Invoice[]" \u2192 list<Invoice>, "Invoice | null" \u2192 Invoice?, boolean \u2192 bool, Record<string, V> \u2192 map<string, V>, Promise<T> \u2192 async T \u2014 and the answer lists each respelling. Refused, naming the replacement: number ("int or float?"), inline object or function types (name a value-object or a signature type), string-literal unions (name an enum), and unions mixing in a primitive or a collection.`;
|
|
44795
|
+
var TYPE_REF_GRAMMAR = (what) => `${what}, in the neutral type grammar: a primitive (string, int, float, bool, bytes, date, datetime, duration, any; void only as a whole returns), a defined type id ("billing.Invoice", "alias::name"), list<T>, set<T>, map<K, V> (K is string, int or an enum), T? (T or no value), a union of NAMED types ("Invoice | Receipt"), and on a returns only, async T ("async void") and result<T, E> (completes with T or fails with E; "async result<void, E>"). Every named type must resolve. TypeScript and Rust spellings are accepted and respelled \u2014 "Invoice[]" \u2192 list<Invoice>, "Invoice | null" \u2192 Invoice?, boolean \u2192 bool, Record<string, V> \u2192 map<string, V>, Promise<T> \u2192 async T, Result<(), E> \u2192 result<void, E> \u2014 and the answer lists each respelling. Refused, naming the replacement: number ("int or float?"), inline object or function types (name a value-object or a signature type), string-literal unions (name an enum), and unions mixing in a primitive or a collection.`;
|
|
44499
44796
|
function structured(content, data) {
|
|
44500
44797
|
return { content: [{ type: "text", text: content }], structuredContent: data };
|
|
44501
44798
|
}
|
|
@@ -44595,6 +44892,28 @@ function renderChangeReport(report3) {
|
|
|
44595
44892
|
return lines.join("\n");
|
|
44596
44893
|
}
|
|
44597
44894
|
var SPEC_KINDS = ["system", "subsystem", "component", "interface", "implementation", "type"];
|
|
44895
|
+
function specKindsHolding(id) {
|
|
44896
|
+
const system = loadSystemSpec();
|
|
44897
|
+
const held = {
|
|
44898
|
+
system: !!system && (id === "system" || id === system.name),
|
|
44899
|
+
subsystem: !!loadSubsystemSpec(id),
|
|
44900
|
+
component: !!loadComponentSpec(id),
|
|
44901
|
+
interface: !!loadInterfaceSpec(id),
|
|
44902
|
+
implementation: !!loadImplementationSpec(id),
|
|
44903
|
+
type: !!loadTypeSpec(id)
|
|
44904
|
+
};
|
|
44905
|
+
return SPEC_KINDS.filter((k) => held[k]);
|
|
44906
|
+
}
|
|
44907
|
+
var PORTAL_TYPE_TRANSPORT = {
|
|
44908
|
+
HTTP_API: "HTTP",
|
|
44909
|
+
gRPC: "gRPC",
|
|
44910
|
+
GraphQL: "GraphQL",
|
|
44911
|
+
MessageBus: "MessageBus",
|
|
44912
|
+
NamedPipe: "NamedPipe",
|
|
44913
|
+
IPC: "IPC",
|
|
44914
|
+
CLI: "CLI",
|
|
44915
|
+
Custom: void 0
|
|
44916
|
+
};
|
|
44598
44917
|
var STATUS_VALUES = ["draft", "design", "complete"];
|
|
44599
44918
|
var testsToRevisitOutput = import_zod12.z.array(import_zod12.z.object({
|
|
44600
44919
|
method: import_zod12.z.string().describe("The contract method this write changed or deleted."),
|
|
@@ -44737,7 +45056,7 @@ var memberCreationOutput = {
|
|
|
44737
45056
|
profile: import_zod12.z.string().optional()
|
|
44738
45057
|
})).describe("Requirements no installed version satisfies: nothing was written for them, and the next family run reports each as POLICY_NOT_ADOPTED."),
|
|
44739
45058
|
projectType: import_zod12.z.string().optional().describe("The profile written as the new member's projectType, when exactly one adopted requirement names one."),
|
|
44740
|
-
as: import_zod12.z.string().describe("part | project: what was created (
|
|
45059
|
+
as: import_zod12.z.string().describe("part | project: what was created (a part by default)."),
|
|
44741
45060
|
storage: import_zod12.z.string().describe("contained | path | git: where its files live."),
|
|
44742
45061
|
commit: import_zod12.z.string().optional().describe("git: the commit the member was pinned at."),
|
|
44743
45062
|
...staleServerOutput
|
|
@@ -45319,7 +45638,7 @@ ${renderChangeReport(report3)}`,
|
|
|
45319
45638
|
server,
|
|
45320
45639
|
"sdd_add_member",
|
|
45321
45640
|
{
|
|
45322
|
-
description: "Create a member of the bound project at `source`, in the one members grammar: `services/scheduler` (contained), `../admin` (a sibling checkout), `git@host:acme/payments.git` (a git repository, pinned at its default branch head unless `#<commit>` is given). By default a PART
|
|
45641
|
+
description: "Create a member of the bound project at `source`, in the one members grammar: `services/scheduler` (contained), `../admin` (a sibling checkout), `git@host:acme/payments.git` (a git repository, pinned at its default branch head unless `#<commit>` is given). By default a PART: a piece of this project stored in another folder or repository, whose subsystems are this project's own (local ids, the ordinary subsystem rules, this project's lock); it gets only a specs folder, plus a configuration naming this project when it is not contained. With `as: project` an independent PROJECT instead: its project content is created \u2014 a project.yaml declaring the alias as its id, its L0, and the bound project's required packs (composition.requirePolicies) once \u2014 and it is reached only as `alias::name` through its L0 exports. What a member is follows from that content. A git member is not scaffolded; the repository's content decides what it is. Boundaries are earned: create a part unless the piece needs its own team, release, approval or public surface; sdd_promote_member makes one later. Idempotent \u2014 the same call again writes nothing; a different member under the alias is refused.",
|
|
45323
45642
|
inputSchema: {
|
|
45324
45643
|
alias: import_zod12.z.string().describe("The member's alias ([a-z0-9-_]+); a project's new id"),
|
|
45325
45644
|
source: import_zod12.z.string().describe("A contained path, a `../` sibling path, or a git URL (optionally `#<commit>`)"),
|
|
@@ -45389,7 +45708,7 @@ ${renderChangeReport(report3)}`,
|
|
|
45389
45708
|
server,
|
|
45390
45709
|
"sdd_externalize_subsystem",
|
|
45391
45710
|
{
|
|
45392
|
-
description: "Move an internal subsystem out to the path. By default
|
|
45711
|
+
description: "Move an internal subsystem out to the path. By default into a PART \u2014 a storage move: the subsystem's specs move to that directory (a new part, or the existing part there), and nothing else changes: no reference, export, external or lock entry, the same verdict. With `as: project`, into a member PROJECT instead, family-wide and all-or-nothing \u2014 the move followed by a promote: what crosses the new boundary exported and imported, references respelled `alias::name`, the new member's external for this project pinned, and every other family project's references checked to still resolve (a reference that would need a component made public refuses the plan). Source code is not moved. dryRun answers the plan and writes nothing; run it first and show the plan. Never locks: the answer names the projects to re-lock.",
|
|
45393
45712
|
inputSchema: {
|
|
45394
45713
|
subsystem: import_zod12.z.string().describe("The subsystem to move out"),
|
|
45395
45714
|
path: import_zod12.z.string().describe("The part's root, relative to the bound project (a new directory, or an existing part's)"),
|
|
@@ -45403,7 +45722,7 @@ ${renderChangeReport(report3)}`,
|
|
|
45403
45722
|
server,
|
|
45404
45723
|
"sdd_promote_member",
|
|
45405
45724
|
{
|
|
45406
|
-
description: "Promote a part of the bound project to an independent project in place
|
|
45725
|
+
description: "Promote a part of the bound project to an independent project in place \u2014 its files stay where they are; it gets an id, an L0 exporting exactly what the rest of the project uses of it, its references across the new boundary respelled `alias::name`, the parent declared as its external, and pins on both sides. Refused (each reason listed) for a part fetched from git, a trusted link that would cross the new boundary, a used component its subsystem does not publish, or an id already taken. All-or-nothing; dryRun answers the plan and writes nothing \u2014 run it first and show the plan. Never locks: the answer names the project to re-lock and the new one to lock.",
|
|
45407
45726
|
inputSchema: {
|
|
45408
45727
|
alias: import_zod12.z.string().describe("The part's alias"),
|
|
45409
45728
|
id: import_zod12.z.string().optional().describe("The new project's id (default: the alias)"),
|
|
@@ -45416,7 +45735,7 @@ ${renderChangeReport(report3)}`,
|
|
|
45416
45735
|
server,
|
|
45417
45736
|
"sdd_demote_member",
|
|
45418
45737
|
{
|
|
45419
|
-
description: "Demote a project member of the bound project to a part in place
|
|
45738
|
+
description: "Demote a project member of the bound project to a part in place \u2014 sdd_promote_member's inverse: its files stay where they are; its own metadata goes to the homes the destination names (as sdd_internalize_member sends it), its L0, lock and pins end, its references across the old boundary become local ids, and its configuration is reduced to naming its parent. Refused while another family project consumes it (a part has no exports \u2014 each consumer listed), for a member fetched from git, or for internalize's reasons. All-or-nothing; dryRun answers the plan and writes nothing \u2014 run it first and show the plan. Never locks.",
|
|
45420
45739
|
inputSchema: {
|
|
45421
45740
|
alias: import_zod12.z.string().describe("The project member's alias"),
|
|
45422
45741
|
destination: import_zod12.z.object({
|
|
@@ -45759,7 +46078,7 @@ ${renderChangeReport(report3)}`,
|
|
|
45759
46078
|
interface: import_zod12.z.string().describe('The L3 interface ID (e.g. "ibilling-gateway")'),
|
|
45760
46079
|
endpoints: import_zod12.z.array(import_zod12.z.object({
|
|
45761
46080
|
method: import_zod12.z.string().describe("The NAME of the interface method to bind (not the HTTP verb)"),
|
|
45762
|
-
transport: import_zod12.z.enum(["HTTP", "gRPC", "GraphQL", "MessageBus", "NamedPipe", "IPC", "CLI", "Custom"]).describe("Wire protocol; must
|
|
46081
|
+
transport: import_zod12.z.enum(["HTTP", "HTTP_API", "gRPC", "GraphQL", "MessageBus", "NamedPipe", "IPC", "CLI", "Custom"]).describe("Wire protocol; must be the one the Portal's portalType implies. Either vocabulary is accepted: the endpoint's (HTTP) or the portalType's (HTTP_API), stored as HTTP; any other transport is refused, naming the one the portalType implies"),
|
|
45763
46082
|
httpMethod: import_zod12.z.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]).optional().describe("HTTP: verb"),
|
|
45764
46083
|
path: import_zod12.z.string().optional().describe('HTTP: route path, e.g. "/v1/checkout"'),
|
|
45765
46084
|
service: import_zod12.z.string().optional().describe("gRPC: service name"),
|
|
@@ -45782,8 +46101,10 @@ ${renderChangeReport(report3)}`,
|
|
|
45782
46101
|
try {
|
|
45783
46102
|
const intf = loadInterfaceSpec(interfaceId);
|
|
45784
46103
|
if (!intf) return errText(`Interface "${interfaceId}" does not exist.`);
|
|
45785
|
-
const
|
|
45786
|
-
|
|
46104
|
+
const owner = intf.component ? loadComponentSpec(intf.component) : null;
|
|
46105
|
+
const implied = owner?.componentType === "Portal" && owner.portalType ? PORTAL_TYPE_TRANSPORT[owner.portalType] : void 0;
|
|
46106
|
+
const buildRaw = (e, transport) => {
|
|
46107
|
+
switch (transport) {
|
|
45787
46108
|
case "HTTP":
|
|
45788
46109
|
return { transport: "HTTP", method: e.httpMethod, path: e.path };
|
|
45789
46110
|
case "gRPC":
|
|
@@ -45807,13 +46128,19 @@ ${renderChangeReport(report3)}`,
|
|
|
45807
46128
|
for (const e of endpoints) {
|
|
45808
46129
|
const m = intf.methods.find((x) => x.name === e.method);
|
|
45809
46130
|
if (!m) return errText(`Method "${e.method}" not found on interface "${interfaceId}". Define it via sdd_define_interface first.`);
|
|
45810
|
-
const
|
|
46131
|
+
const transport = e.transport === "HTTP_API" ? "HTTP" : e.transport;
|
|
46132
|
+
if (implied && transport !== implied) {
|
|
46133
|
+
return errText(
|
|
46134
|
+
`Method "${e.method}": transport "${e.transport}" does not match ${owner.id}'s portalType ${owner.portalType}, which implies transport "${implied}". Bind it as "${implied}", or change the Portal's portalType.`
|
|
46135
|
+
);
|
|
46136
|
+
}
|
|
46137
|
+
const parsed = EndpointSchema.safeParse(buildRaw(e, transport));
|
|
45811
46138
|
if (!parsed.success) {
|
|
45812
46139
|
const detail = parsed.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
|
|
45813
|
-
return errText(`Invalid ${
|
|
46140
|
+
return errText(`Invalid ${transport} endpoint for method "${e.method}": ${detail}`);
|
|
45814
46141
|
}
|
|
45815
46142
|
methods.push({ name: e.method, endpoint: parsed.data });
|
|
45816
|
-
bound2.push(`${e.method}\u2192${
|
|
46143
|
+
bound2.push(`${e.method}\u2192${transport}`);
|
|
45817
46144
|
}
|
|
45818
46145
|
const report3 = updateSpecGated("interface", interfaceId, { methods });
|
|
45819
46146
|
return structured(
|
|
@@ -46074,14 +46401,24 @@ ${renderChangeReport(report3)}`,
|
|
|
46074
46401
|
{
|
|
46075
46402
|
description: `Get/read the parsed JSON contents of a specific spec from the spec tree. Returns structural contents without file system path searching. Pass "methods" to read only the named methods of a contract, an implementation or a type \u2014 a 45-method spec fetched whole to look at one of them is the read side of the same waste a restatement is on the write side; the answer then carries a "partialResult" marker naming what was left out, and must never be re-authored from. For a variant-tagged COMPONENT the result also carries a derived, read-only "variantGuidance" (the variant's base, its implementation guidance, and the same-variant sibling components to implement alike) \u2014 it is resolved from the variant registry, not part of the spec, so never write it back. A contract's methods come back in their STORED form \u2014 a method that takes its signature from a source carries its signatureFrom, not the params the loader resolves into it \u2014 so the answer can be re-authored from as it is; the resolved params, returns and text of each such method come back as a derived, read-only "resolvedSignatures" marker. The structured content carries the same answer with the derived markers KEPT SEPARATE from the stored spec ({kind, id, spec, partialResult?, variantGuidance?, resolvedSignatures?}), so nothing derived can be mistaken for something stored; the text block folds them in as it always has.`,
|
|
46076
46403
|
inputSchema: {
|
|
46077
|
-
kind: import_zod12.z.enum(["system", "subsystem", "component", "interface", "implementation", "type"]).describe("The kind of specification"),
|
|
46404
|
+
kind: import_zod12.z.enum(["system", "subsystem", "component", "interface", "implementation", "type"]).optional().describe("The kind of specification. Omit it to infer the kind from the id: the read is refused, naming the candidate kinds, when the id names specs at more than one level (an interface and an implementation sharing an id, say), or at none"),
|
|
46078
46405
|
id: import_zod12.z.string().describe('The identifier of the spec to fetch (the L0 system spec is a singleton \u2014 pass the system name or "system")'),
|
|
46079
46406
|
methods: import_zod12.z.array(import_zod12.z.string().min(1)).optional().describe("Return only these methods by name; every other field of the spec comes back unchanged. Only an interface, an implementation or a type declares methods \u2014 asking for one elsewhere is refused, as is a name the spec does not declare (the answer names the ones it does). Omit it for the whole spec.")
|
|
46080
46407
|
},
|
|
46081
46408
|
outputSchema: getSpecOutput
|
|
46082
46409
|
},
|
|
46083
|
-
({ kind, id, methods }) => {
|
|
46410
|
+
({ kind: askedKind, id, methods }) => {
|
|
46084
46411
|
try {
|
|
46412
|
+
let kind;
|
|
46413
|
+
if (askedKind) {
|
|
46414
|
+
kind = askedKind;
|
|
46415
|
+
} else {
|
|
46416
|
+
const holders = specKindsHolding(id);
|
|
46417
|
+
if (holders.length !== 1) {
|
|
46418
|
+
return errText(holders.length === 0 ? `No spec of any kind has the ID "${id}".` : `The ID "${id}" names specs of more than one kind (${holders.join(", ")}). Pass "kind" to choose one.`);
|
|
46419
|
+
}
|
|
46420
|
+
kind = holders[0];
|
|
46421
|
+
}
|
|
46085
46422
|
let result = null;
|
|
46086
46423
|
let partial2 = null;
|
|
46087
46424
|
switch (kind) {
|
|
@@ -46634,6 +46971,7 @@ function loadProjectConfig3() {
|
|
|
46634
46971
|
keyIn,
|
|
46635
46972
|
landReference,
|
|
46636
46973
|
languageOfSourcePath,
|
|
46974
|
+
lineEndingFor,
|
|
46637
46975
|
listDirectChainedSubprojects,
|
|
46638
46976
|
listExternals,
|
|
46639
46977
|
listFiles,
|
|
@@ -46792,6 +47130,7 @@ function loadProjectConfig3() {
|
|
|
46792
47130
|
typeIsMany,
|
|
46793
47131
|
typeMatchesRef,
|
|
46794
47132
|
typeNamedRefs,
|
|
47133
|
+
typeProblemEnumProposal,
|
|
46795
47134
|
typeProblemIntProposal,
|
|
46796
47135
|
typeSourceFiles,
|
|
46797
47136
|
typeSpellingFacts,
|
|
@@ -46805,6 +47144,7 @@ function loadProjectConfig3() {
|
|
|
46805
47144
|
validateProject,
|
|
46806
47145
|
validateProjectConfig,
|
|
46807
47146
|
validateRegistry,
|
|
47147
|
+
withLineEndings,
|
|
46808
47148
|
withPack,
|
|
46809
47149
|
writeFile,
|
|
46810
47150
|
writeFileIfChanged,
|