githolon 0.52.0 → 0.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.mjs +444 -126
  2. package/package.json +3 -3
package/dist/cli.mjs CHANGED
@@ -1276,6 +1276,112 @@ var init_editor = __esm({
1276
1276
  }
1277
1277
  });
1278
1278
 
1279
+ // src/compile.ts
1280
+ import { spawn, spawnSync } from "node:child_process";
1281
+ import { createRequire } from "node:module";
1282
+ import { dirname as dirname2, join as join4 } from "node:path";
1283
+ import { pathToFileURL } from "node:url";
1284
+ function resolveCompileLauncher(cwd) {
1285
+ const resolveDslPkg = (fromDir) => {
1286
+ try {
1287
+ const req = fromDir === void 0 ? createRequire(import.meta.url) : createRequire(pathToFileURL(join4(fromDir, "noop.js")));
1288
+ return req.resolve("@githolon/dsl/package.json");
1289
+ } catch {
1290
+ return void 0;
1291
+ }
1292
+ };
1293
+ const dslPkg = resolveDslPkg(cwd) ?? resolveDslPkg(void 0);
1294
+ return dslPkg === void 0 ? void 0 : join4(dirname2(dslPkg), "compile_package.mjs");
1295
+ }
1296
+ function runCompile(args) {
1297
+ const launcher = resolveCompileLauncher(process.cwd());
1298
+ if (launcher === void 0) {
1299
+ process.stderr.write(`error: ${NO_DSL_REMEDY}
1300
+ `);
1301
+ return 1;
1302
+ }
1303
+ const r = spawnSync(process.execPath, [launcher, ...args], { stdio: "inherit", cwd: process.cwd() });
1304
+ const status2 = r.status ?? 1;
1305
+ if (status2 === 0 && process.stdout.isTTY) {
1306
+ try {
1307
+ if (!hasEditorRecommendation(process.cwd())) process.stdout.write(`
1308
+ ${INIT_EDITOR_HINT}
1309
+ `);
1310
+ } catch {
1311
+ }
1312
+ }
1313
+ return status2;
1314
+ }
1315
+ function resolveCheckLauncher(cwd) {
1316
+ const launcher = resolveCompileLauncher(cwd);
1317
+ return launcher === void 0 ? void 0 : join4(dirname2(launcher), "check_package.mjs");
1318
+ }
1319
+ function runCheck(args) {
1320
+ const launcher = resolveCheckLauncher(process.cwd());
1321
+ if (launcher === void 0) {
1322
+ process.stderr.write(`error: ${NO_DSL_REMEDY}
1323
+ `);
1324
+ return 1;
1325
+ }
1326
+ const r = spawnSync(process.execPath, [launcher, ...args], { stdio: "inherit", cwd: process.cwd() });
1327
+ return r.status ?? 1;
1328
+ }
1329
+ function resolveCompatLauncher(cwd) {
1330
+ const launcher = resolveCompileLauncher(cwd);
1331
+ return launcher === void 0 ? void 0 : join4(dirname2(launcher), "compat_package.mjs");
1332
+ }
1333
+ function runCompat(args) {
1334
+ const launcher = resolveCompatLauncher(process.cwd());
1335
+ if (launcher === void 0) {
1336
+ process.stderr.write(`error: ${NO_DSL_REMEDY}
1337
+ `);
1338
+ return 1;
1339
+ }
1340
+ const r = spawnSync(process.execPath, [launcher, ...args], { stdio: "inherit", cwd: process.cwd() });
1341
+ return r.status ?? 1;
1342
+ }
1343
+ function resolveLspLauncher(cwd) {
1344
+ const launcher = resolveCompileLauncher(cwd);
1345
+ return launcher === void 0 ? void 0 : join4(dirname2(launcher), "lsp_server.mjs");
1346
+ }
1347
+ function runLsp(args) {
1348
+ const launcher = resolveLspLauncher(process.cwd());
1349
+ if (launcher === void 0) {
1350
+ process.stderr.write(`error: ${NO_DSL_REMEDY}
1351
+ `);
1352
+ return 1;
1353
+ }
1354
+ const r = spawnSync(process.execPath, [launcher, ...args], { stdio: "inherit", cwd: process.cwd() });
1355
+ return r.status ?? 1;
1356
+ }
1357
+ function compileAsync(cwd, args = []) {
1358
+ const launcher = resolveCompileLauncher(cwd);
1359
+ const t0 = performance.now();
1360
+ if (launcher === void 0) {
1361
+ return Promise.resolve({ ok: false, ms: 0, output: NO_DSL_REMEDY });
1362
+ }
1363
+ return new Promise((resolveRun) => {
1364
+ const chunks = [];
1365
+ const dec4 = new TextDecoder();
1366
+ const child = spawn(process.execPath, [launcher, ...args], { cwd, stdio: ["ignore", "pipe", "pipe"] });
1367
+ child.stdout?.on("data", (c) => chunks.push(dec4.decode(c)));
1368
+ child.stderr?.on("data", (c) => chunks.push(dec4.decode(c)));
1369
+ child.on("error", (e) => resolveRun({ ok: false, ms: performance.now() - t0, output: String(e) }));
1370
+ child.on(
1371
+ "close",
1372
+ (code) => resolveRun({ ok: code === 0, ms: performance.now() - t0, output: chunks.join("") })
1373
+ );
1374
+ });
1375
+ }
1376
+ var NO_DSL_REMEDY;
1377
+ var init_compile = __esm({
1378
+ "src/compile.ts"() {
1379
+ "use strict";
1380
+ init_editor();
1381
+ NO_DSL_REMEDY = "@githolon/dsl not found \u2014 add it to your project's dependencies";
1382
+ }
1383
+ });
1384
+
1279
1385
  // vendor/engine/git-fs.mjs
1280
1386
  function fsErr(code, p) {
1281
1387
  const e = new Error(`${code}: ${p}`);
@@ -1291,7 +1397,7 @@ function makeGitFs(preopen, mount, { File: File3, Directory: Directory3 }) {
1291
1397
  const rel = p.slice(mount.length + 1).replace(/\/+$/, "");
1292
1398
  return rel === "" ? [] : rel.split("/");
1293
1399
  }
1294
- function resolve4(ps) {
1400
+ function resolve5(ps) {
1295
1401
  let cur = root;
1296
1402
  for (const part of ps) {
1297
1403
  if (!isDir(cur)) return null;
@@ -1301,11 +1407,11 @@ function makeGitFs(preopen, mount, { File: File3, Directory: Directory3 }) {
1301
1407
  }
1302
1408
  return cur;
1303
1409
  }
1304
- const parent = (ps) => resolve4(ps.slice(0, -1));
1410
+ const parent = (ps) => resolve5(ps.slice(0, -1));
1305
1411
  const leaf = (ps) => ps[ps.length - 1];
1306
1412
  const promises = {
1307
1413
  async readFile(p, opts) {
1308
- const n = resolve4(parts(p));
1414
+ const n = resolve5(parts(p));
1309
1415
  if (n === null) throw fsErr("ENOENT", p);
1310
1416
  if (isDir(n)) throw fsErr("EISDIR", p);
1311
1417
  const data = n.data ?? new Uint8Array(0);
@@ -1324,7 +1430,7 @@ function makeGitFs(preopen, mount, { File: File3, Directory: Directory3 }) {
1324
1430
  if (!isDir(par) || !par.contents.delete(leaf(ps))) throw fsErr("ENOENT", p);
1325
1431
  },
1326
1432
  async readdir(p) {
1327
- const n = resolve4(parts(p));
1433
+ const n = resolve5(parts(p));
1328
1434
  if (n === null) throw fsErr("ENOENT", p);
1329
1435
  if (!isDir(n)) throw fsErr("ENOTDIR", p);
1330
1436
  return [...n.contents.keys()];
@@ -1344,7 +1450,7 @@ function makeGitFs(preopen, mount, { File: File3, Directory: Directory3 }) {
1344
1450
  return promises.lstat(p);
1345
1451
  },
1346
1452
  async lstat(p) {
1347
- const n = resolve4(parts(p));
1453
+ const n = resolve5(parts(p));
1348
1454
  if (n === null) throw fsErr("ENOENT", p);
1349
1455
  const dir = isDir(n);
1350
1456
  return {
@@ -1851,7 +1957,7 @@ var init_engine = __esm({
1851
1957
  cryptoUnwrapKey = (eng, { secret, hpkeEpk, ct }) => JSON.parse(call(eng.ex, "query", { queryBytes: b64Json({ op: "cryptoUnwrapKey", secret, hpkeEpk, ct }) }, eng.STDERR)).scopeKey;
1852
1958
  count = (eng, ws, countId, groupKey, principal = "") => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "count", countId, groupKey }), principal, branch: BRANCH }, eng.STDERR));
1853
1959
  sum = (eng, ws, sumId, groupKey, principal = "") => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "sum", sumId, groupKey }), principal, branch: BRANCH }, eng.STDERR));
1854
- spatialWithin = (eng, ws, spatialId, bbox, principal = "") => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "spatial", spatialId, minLng: bbox.minLng, minLat: bbox.minLat, maxLng: bbox.maxLng, maxLat: bbox.maxLat }), principal, branch: BRANCH }, eng.STDERR));
1960
+ spatialWithin = (eng, ws, spatialId, bbox, principal = "") => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "spatial", spatialId, minX: bbox.minX ?? bbox.minLng, minY: bbox.minY ?? bbox.minLat, maxX: bbox.maxX ?? bbox.maxLng, maxY: bbox.maxY ?? bbox.maxLat }), principal, branch: BRANCH }, eng.STDERR));
1855
1961
  }
1856
1962
  });
1857
1963
 
@@ -2076,7 +2182,7 @@ async function runOfflineLegs(eng, ws, lawHash, legs, frameworkHash, deployUsda)
2076
2182
  lines.push(`\u2713 declared count ${leg.count.id}(${JSON.stringify(leg.count.group)}) = 1 locally`);
2077
2183
  }
2078
2184
  if (leg.spatial !== void 0) {
2079
- const world = { minLng: -180, minLat: -90, maxLng: 180, maxLat: 90 };
2185
+ const world = { minX: -180, minY: -90, maxX: 180, maxY: 90 };
2080
2186
  const sr = spatialWithin(eng, ws, leg.spatial.id, world);
2081
2187
  const ids = (sr.rows ?? []).map((r) => r.id);
2082
2188
  if (sr.ok === false || createdId === void 0 || !ids.includes(createdId)) {
@@ -2387,21 +2493,289 @@ var init_proof_offline = __esm({
2387
2493
  }
2388
2494
  });
2389
2495
 
2496
+ // src/mcp.ts
2497
+ var mcp_exports = {};
2498
+ __export(mcp_exports, {
2499
+ callTool: () => callTool,
2500
+ extractDispositions: () => extractDispositions,
2501
+ handleRequest: () => handleRequest,
2502
+ runMcpServer: () => runMcpServer,
2503
+ summarize: () => summarize,
2504
+ toolCheck: () => toolCheck,
2505
+ toolEvolvePreview: () => toolEvolvePreview
2506
+ });
2507
+ import { spawnSync as spawnSync4 } from "node:child_process";
2508
+ import { existsSync as existsSync10 } from "node:fs";
2509
+ import { dirname as dirname6, isAbsolute, resolve as resolve2 } from "node:path";
2510
+ function resolveConfig(config) {
2511
+ const arg = config ?? "nomos.package.mjs";
2512
+ const configPath = isAbsolute(arg) ? arg : resolve2(process.cwd(), arg);
2513
+ return { configPath, cfgDir: dirname6(configPath) };
2514
+ }
2515
+ function runCheckLauncher(configPath, extraArgs) {
2516
+ const cfgDir = dirname6(configPath);
2517
+ const launcher = resolveCheckLauncher(cfgDir) ?? resolveCheckLauncher(process.cwd());
2518
+ if (launcher === void 0) {
2519
+ return { findings: [], stdout: "", stderr: "", status: 1, error: NO_DSL_REMEDY };
2520
+ }
2521
+ const r = spawnSync4(process.execPath, [launcher, configPath, ...extraArgs], {
2522
+ cwd: cfgDir,
2523
+ encoding: "utf8"
2524
+ });
2525
+ return {
2526
+ findings: [],
2527
+ stdout: r.stdout ?? "",
2528
+ stderr: r.stderr ?? "",
2529
+ status: r.status ?? 1
2530
+ };
2531
+ }
2532
+ function runCheckJson(configPath, extraArgs) {
2533
+ const base = runCheckLauncher(configPath, [...extraArgs, "--json"]);
2534
+ if (base.error !== void 0) return base;
2535
+ const line = base.stdout.trim().split("\n").filter(Boolean).pop() ?? "";
2536
+ let findings;
2537
+ try {
2538
+ const parsed = JSON.parse(line);
2539
+ if (!Array.isArray(parsed)) throw new Error("not an array");
2540
+ findings = parsed;
2541
+ } catch (e) {
2542
+ return {
2543
+ ...base,
2544
+ error: `could not parse check --json output: ${e.message}
2545
+ ${base.stdout}${base.stderr}`
2546
+ };
2547
+ }
2548
+ return { ...base, findings };
2549
+ }
2550
+ function summarize(findings) {
2551
+ const n = { refuse: 0, warn: 0, note: 0 };
2552
+ for (const f of findings) n[f.level]++;
2553
+ if (findings.length === 0) return "githolon check: all clear \u2014 no static twin flagged anything.";
2554
+ const head = `githolon check: ${findings.length} finding(s) \u2014 ${n.refuse} refuse, ${n.warn} warn, ${n.note} note.`;
2555
+ const lines = findings.map((f) => {
2556
+ const where = f.file !== void 0 ? ` (${f.file}:${f.line ?? 1})` : "";
2557
+ const who = f.directiveId !== void 0 ? `${f.domain}/${f.directiveId}` : f.domain;
2558
+ return ` ${f.level.toUpperCase()} ${who}${where}: ${f.what} \u2014 fix: ${f.fix}`;
2559
+ });
2560
+ return [head, ...lines].join("\n");
2561
+ }
2562
+ function extractDispositions(humanReport) {
2563
+ const marker = "add `dispositions` to the domain's deploy JSON:";
2564
+ const at = humanReport.indexOf(marker);
2565
+ if (at < 0) return void 0;
2566
+ const rest = humanReport.slice(at + marker.length);
2567
+ const start = rest.indexOf("{");
2568
+ if (start < 0) return void 0;
2569
+ let depth = 0;
2570
+ for (let i = start; i < rest.length; i++) {
2571
+ if (rest[i] === "{") depth++;
2572
+ else if (rest[i] === "}") {
2573
+ depth--;
2574
+ if (depth === 0) {
2575
+ try {
2576
+ return JSON.parse(rest.slice(start, i + 1));
2577
+ } catch {
2578
+ return void 0;
2579
+ }
2580
+ }
2581
+ }
2582
+ }
2583
+ return void 0;
2584
+ }
2585
+ function toolCheck(args) {
2586
+ const { configPath } = resolveConfig(args.config);
2587
+ if (!existsSync10(configPath)) {
2588
+ return { text: `nomos_check: config not found: ${configPath}`, isError: true };
2589
+ }
2590
+ const extra = args.against !== void 0 ? ["--against", args.against] : [];
2591
+ const run = runCheckJson(configPath, extra);
2592
+ if (run.error !== void 0) {
2593
+ return { text: `nomos_check: ${run.error}${run.stderr ? `
2594
+ ${run.stderr}` : ""}`, isError: true };
2595
+ }
2596
+ const summary = summarize(run.findings);
2597
+ const payload = { summary, findings: run.findings };
2598
+ return { text: `${summary}
2599
+
2600
+ ${JSON.stringify(payload, null, 2)}`, isError: false };
2601
+ }
2602
+ function toolEvolvePreview(args) {
2603
+ const { configPath } = resolveConfig(args.config);
2604
+ if (!existsSync10(configPath)) {
2605
+ return { text: `nomos_evolve_preview: config not found: ${configPath}`, isError: true };
2606
+ }
2607
+ const againstPath = isAbsolute(args.against) ? args.against : resolve2(dirname6(configPath), args.against);
2608
+ const priorArg = existsSync10(againstPath) ? againstPath : args.against;
2609
+ const jsonRun = runCheckJson(configPath, ["--against", priorArg]);
2610
+ if (jsonRun.error !== void 0) {
2611
+ return {
2612
+ text: `nomos_evolve_preview: ${jsonRun.error}${jsonRun.stderr ? `
2613
+ ${jsonRun.stderr}` : ""}`,
2614
+ isError: true
2615
+ };
2616
+ }
2617
+ const humanRun = runCheckLauncher(configPath, ["--against", priorArg]);
2618
+ const dispositions = extractDispositions(humanRun.stdout);
2619
+ const summary = summarize(jsonRun.findings);
2620
+ const dispLine = dispositions !== void 0 ? "A DESTRUCTIVE evolve was detected. Add this `dispositions` block to the domain's deploy JSON before deploying:" : "No destructive evolve \u2014 the diff admits without dispositions.";
2621
+ const payload = { summary, findings: jsonRun.findings, dispositions: dispositions ?? null };
2622
+ return {
2623
+ text: `${summary}
2624
+
2625
+ ${dispLine}
2626
+
2627
+ ${JSON.stringify(payload, null, 2)}`,
2628
+ isError: false
2629
+ };
2630
+ }
2631
+ function callTool(name, argsIn) {
2632
+ const a = argsIn ?? {};
2633
+ const config = typeof a.config === "string" ? a.config : void 0;
2634
+ if (name === "nomos_check") {
2635
+ return toolCheck({
2636
+ ...config !== void 0 ? { config } : {},
2637
+ ...typeof a.against === "string" ? { against: a.against } : {}
2638
+ });
2639
+ }
2640
+ if (name === "nomos_evolve_preview") {
2641
+ if (typeof a.against !== "string" || a.against.length === 0) {
2642
+ return { text: "nomos_evolve_preview: `against` is required (a prior deploy.json / lockfile).", isError: true };
2643
+ }
2644
+ return toolEvolvePreview({ against: a.against, ...config !== void 0 ? { config } : {} });
2645
+ }
2646
+ return { text: `unknown tool: ${name}`, isError: true };
2647
+ }
2648
+ function handleRequest(req) {
2649
+ const reply = (result) => ({ jsonrpc: "2.0", id: req.id ?? null, result });
2650
+ const fail = (code, message) => ({
2651
+ jsonrpc: "2.0",
2652
+ id: req.id ?? null,
2653
+ error: { code, message }
2654
+ });
2655
+ switch (req.method) {
2656
+ case "initialize": {
2657
+ const clientVersion = req.params?.protocolVersion;
2658
+ return reply({
2659
+ protocolVersion: clientVersion ?? PROTOCOL_VERSION,
2660
+ capabilities: { tools: {} },
2661
+ serverInfo: { name: SERVER_NAME, version: SERVER_VERSION }
2662
+ });
2663
+ }
2664
+ case "notifications/initialized":
2665
+ case "initialized":
2666
+ return void 0;
2667
+ // notification — no response
2668
+ case "ping":
2669
+ return reply({});
2670
+ case "tools/list":
2671
+ return reply({ tools: TOOLS });
2672
+ case "tools/call": {
2673
+ const p = req.params ?? {};
2674
+ if (typeof p.name !== "string") return fail(-32602, "tools/call requires a string `name`");
2675
+ const res = callTool(p.name, p.arguments);
2676
+ return reply({ content: [{ type: "text", text: res.text }], isError: res.isError });
2677
+ }
2678
+ default:
2679
+ if (req.id === void 0 || req.id === null) return void 0;
2680
+ return fail(-32601, `method not found: ${req.method}`);
2681
+ }
2682
+ }
2683
+ function runMcpServer() {
2684
+ return new Promise((resolveRun) => {
2685
+ let buf = "";
2686
+ const write = (obj) => {
2687
+ process.stdout.write(JSON.stringify(obj) + "\n");
2688
+ };
2689
+ process.stdin.setEncoding("utf8");
2690
+ process.stdin.on("data", (chunk) => {
2691
+ buf += chunk;
2692
+ let nl;
2693
+ while ((nl = buf.indexOf("\n")) >= 0) {
2694
+ const line = buf.slice(0, nl).trim();
2695
+ buf = buf.slice(nl + 1);
2696
+ if (line.length === 0) continue;
2697
+ let req;
2698
+ try {
2699
+ req = JSON.parse(line);
2700
+ } catch {
2701
+ write({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "parse error" } });
2702
+ continue;
2703
+ }
2704
+ try {
2705
+ const res = handleRequest(req);
2706
+ if (res !== void 0) write(res);
2707
+ } catch (e) {
2708
+ write({ jsonrpc: "2.0", id: req.id ?? null, error: { code: -32603, message: e.message } });
2709
+ }
2710
+ }
2711
+ });
2712
+ process.stdin.on("end", () => resolveRun(0));
2713
+ process.stdin.on("close", () => resolveRun(0));
2714
+ });
2715
+ }
2716
+ var SERVER_NAME, SERVER_VERSION, PROTOCOL_VERSION, TOOLS;
2717
+ var init_mcp = __esm({
2718
+ "src/mcp.ts"() {
2719
+ "use strict";
2720
+ init_compile();
2721
+ SERVER_NAME = "nomos";
2722
+ SERVER_VERSION = "0.1.0";
2723
+ PROTOCOL_VERSION = "2024-11-05";
2724
+ TOOLS = [
2725
+ {
2726
+ name: "nomos_check",
2727
+ description: "Run the Nomos author-time gate (githolon check static twins) over a nomos.package.mjs and return the findings (refuse/warn/note) plus a human summary. Optionally diff against a prior deployed law for evolve-safety. Call before compiling or deploying domain law.",
2728
+ inputSchema: {
2729
+ type: "object",
2730
+ properties: {
2731
+ config: {
2732
+ type: "string",
2733
+ description: "Path to nomos.package.mjs (default: ./nomos.package.mjs, relative to the server working dir)."
2734
+ },
2735
+ against: {
2736
+ type: "string",
2737
+ description: "Optional prior law to diff evolve-safety against: a build/<name>.deploy.json, a build/ dir, or a nomos.stable-ids.json lockfile."
2738
+ }
2739
+ }
2740
+ }
2741
+ },
2742
+ {
2743
+ name: "nomos_evolve_preview",
2744
+ description: "Preview a Nomos LAW EVOLUTION: diff the current build against a prior deployed law and return the evolve findings plus the pasteable `dispositions` block a destructive change (removal/retype) requires. Call before deploying a change to already-deployed law.",
2745
+ inputSchema: {
2746
+ type: "object",
2747
+ properties: {
2748
+ against: {
2749
+ type: "string",
2750
+ description: "REQUIRED prior law: a build/<name>.deploy.json, a build/ dir, or a nomos.stable-ids.json lockfile."
2751
+ },
2752
+ config: {
2753
+ type: "string",
2754
+ description: "Path to nomos.package.mjs (default: ./nomos.package.mjs)."
2755
+ }
2756
+ },
2757
+ required: ["against"]
2758
+ }
2759
+ }
2760
+ ];
2761
+ }
2762
+ });
2763
+
2390
2764
  // src/harness.ts
2391
2765
  var harness_exports = {};
2392
2766
  __export(harness_exports, {
2393
2767
  harnessInstall: () => harnessInstall
2394
2768
  });
2395
- import { existsSync as existsSync10, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "node:fs";
2396
- import { join as join11 } from "node:path";
2769
+ import { existsSync as existsSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "node:fs";
2770
+ import { join as join12 } from "node:path";
2397
2771
  import { randomBytes as randomBytes3 } from "node:crypto";
2398
2772
  async function harnessInstall(opts) {
2399
2773
  const { deploy: deployPaths, workspace, out: outPath } = opts;
2400
2774
  if (deployPaths.length === 0) die("no --deploy JSON path(s) given");
2401
- const runtimeDir = opts.runtime || process.env["NOMOS_OFFLINE_RUNTIME"] || join11(configDir(), "runtime");
2402
- const wasmFile = join11(runtimeDir, "holon.wasm");
2403
- const pkgsFile = join11(runtimeDir, "packages.json");
2404
- if (!existsSync10(wasmFile) || !existsSync10(pkgsFile)) {
2775
+ const runtimeDir = opts.runtime || process.env["NOMOS_OFFLINE_RUNTIME"] || join12(configDir(), "runtime");
2776
+ const wasmFile = join12(runtimeDir, "holon.wasm");
2777
+ const pkgsFile = join12(runtimeDir, "packages.json");
2778
+ if (!existsSync11(wasmFile) || !existsSync11(pkgsFile)) {
2405
2779
  die(
2406
2780
  `runtime cache missing at ${runtimeDir} (holon.wasm + packages.json). Warm it once with a \`githolon\` run (it fetches + caches the runtime), then re-run offline.`
2407
2781
  );
@@ -2420,7 +2794,7 @@ async function harnessInstall(opts) {
2420
2794
  author(eng, workspace, "bootstrap", "installDomain", installPayload(eng.hashes.nomos, eng.nomosPkg, "nomos-test-harness"), "");
2421
2795
  const lawHashes = [];
2422
2796
  for (const p of deployPaths) {
2423
- if (!existsSync10(p)) die(`deploy JSON not found: ${p} \u2014 run \`npx githolon compile\` first`);
2797
+ if (!existsSync11(p)) die(`deploy JSON not found: ${p} \u2014 run \`npx githolon compile\` first`);
2424
2798
  const deploy2 = JSON.parse(readFileSync12(p, "utf8"));
2425
2799
  const usda = deploy2.packageUsda;
2426
2800
  if (typeof usda !== "string" || !usda.startsWith("#usda")) die(`${p} has no #usda packageUsda`);
@@ -3511,8 +3885,8 @@ var scene_exports = {};
3511
3885
  __export(scene_exports, {
3512
3886
  scene: () => scene
3513
3887
  });
3514
- import { existsSync as existsSync11, mkdirSync as mkdirSync5, writeFileSync as writeFileSync8 } from "node:fs";
3515
- import { join as join12, resolve as resolve2 } from "node:path";
3888
+ import { existsSync as existsSync12, mkdirSync as mkdirSync5, writeFileSync as writeFileSync8 } from "node:fs";
3889
+ import { join as join13, resolve as resolve3 } from "node:path";
3516
3890
  import { createHash as createHash5 } from "node:crypto";
3517
3891
  import git3 from "isomorphic-git";
3518
3892
  async function walkChain2(eng, ws) {
@@ -3533,8 +3907,8 @@ async function projectOnce(cloud, target, isDir, name) {
3533
3907
  const { eng } = await holonEngine(cloud, void 0, "githolon-scene");
3534
3908
  const SOURCE3 = "source";
3535
3909
  if (isDir) {
3536
- const gitDir = existsSync11(join12(target, ".git")) ? join12(target, ".git") : target;
3537
- if (!existsSync11(join12(gitDir, "HEAD"))) throw new Error(`${target} is not a git repo (no HEAD)`);
3910
+ const gitDir = existsSync12(join13(target, ".git")) ? join13(target, ".git") : target;
3911
+ if (!existsSync12(join13(gitDir, "HEAD"))) throw new Error(`${target} is not a git repo (no HEAD)`);
3538
3912
  await mountFresh(eng, SOURCE3);
3539
3913
  eng.preopen.dir.contents.get("ws").contents.get(SOURCE3).contents.set("nomos.git", readTreeFromDisk(gitDir));
3540
3914
  } else {
@@ -3593,9 +3967,9 @@ async function projectOnce(cloud, target, isDir, name) {
3593
3967
  }
3594
3968
  async function scene(target, opts) {
3595
3969
  const cloud = cloudBase(opts.cloud);
3596
- const isDir = existsSync11(target) || target.includes("/") || target.startsWith(".");
3597
- const name = isDir ? (resolve2(target).split("/").filter(Boolean).pop() ?? "scene").replace(/\.git$/, "") : target;
3598
- out11(`scene \u2014 ${name} (${isDir ? resolve2(target) : `${cloud} :: ${target}`})`);
3970
+ const isDir = existsSync12(target) || target.includes("/") || target.startsWith(".");
3971
+ const name = isDir ? (resolve3(target).split("/").filter(Boolean).pop() ?? "scene").replace(/\.git$/, "") : target;
3972
+ out11(`scene \u2014 ${name} (${isDir ? resolve3(target) : `${cloud} :: ${target}`})`);
3599
3973
  let first, second;
3600
3974
  try {
3601
3975
  first = await projectOnce(cloud, target, isDir, name);
@@ -3613,10 +3987,10 @@ async function scene(target, opts) {
3613
3987
  err11(`the emitted stage violates the portable profile: ${violations.join("; ")}`);
3614
3988
  return 1;
3615
3989
  }
3616
- const outDir = resolve2(opts.out ?? join12("scene", name));
3990
+ const outDir = resolve3(opts.out ?? join13("scene", name));
3617
3991
  mkdirSync5(outDir, { recursive: true });
3618
- const usdaPath = join12(outDir, `${name}.usda`);
3619
- const usdzPath = join12(outDir, `${name}.usdz`);
3992
+ const usdaPath = join13(outDir, `${name}.usda`);
3993
+ const usdzPath = join13(outDir, `${name}.usdz`);
3620
3994
  const usdaBytes = new TextEncoder().encode(first.usda);
3621
3995
  writeFileSync8(usdaPath, usdaBytes);
3622
3996
  writeFileSync8(usdzPath, usdzPack([[`${name}.usda`, usdaBytes]]));
@@ -3645,10 +4019,10 @@ var init_scene = __esm({
3645
4019
  });
3646
4020
 
3647
4021
  // src/cli.ts
3648
- import { existsSync as existsSync12, readdirSync as readdirSync4, readFileSync as readFileSync13 } from "node:fs";
3649
- import { spawnSync as spawnSync4 } from "node:child_process";
4022
+ import { existsSync as existsSync13, readdirSync as readdirSync4, readFileSync as readFileSync13 } from "node:fs";
4023
+ import { spawnSync as spawnSync5 } from "node:child_process";
3650
4024
  import { createRequire as createRequire2 } from "node:module";
3651
- import { dirname as dirname6, join as join13, resolve as resolve3 } from "node:path";
4025
+ import { dirname as dirname7, join as join14, resolve as resolve4 } from "node:path";
3652
4026
  import { pathToFileURL as pathToFileURL4 } from "node:url";
3653
4027
 
3654
4028
  // src/generate.ts
@@ -3831,101 +4205,17 @@ Re-run with --force to replace it.`
3831
4205
 
3832
4206
  // src/cli.ts
3833
4207
  init_cloud();
3834
-
3835
- // src/compile.ts
3836
- init_editor();
3837
- import { spawn, spawnSync } from "node:child_process";
3838
- import { createRequire } from "node:module";
3839
- import { dirname as dirname2, join as join4 } from "node:path";
3840
- import { pathToFileURL } from "node:url";
3841
- function resolveCompileLauncher(cwd) {
3842
- const resolveDslPkg = (fromDir) => {
3843
- try {
3844
- const req = fromDir === void 0 ? createRequire(import.meta.url) : createRequire(pathToFileURL(join4(fromDir, "noop.js")));
3845
- return req.resolve("@githolon/dsl/package.json");
3846
- } catch {
3847
- return void 0;
3848
- }
3849
- };
3850
- const dslPkg = resolveDslPkg(cwd) ?? resolveDslPkg(void 0);
3851
- return dslPkg === void 0 ? void 0 : join4(dirname2(dslPkg), "compile_package.mjs");
3852
- }
3853
- var NO_DSL_REMEDY = "@githolon/dsl not found \u2014 add it to your project's dependencies";
3854
- function runCompile(args) {
3855
- const launcher = resolveCompileLauncher(process.cwd());
3856
- if (launcher === void 0) {
3857
- process.stderr.write(`error: ${NO_DSL_REMEDY}
3858
- `);
3859
- return 1;
3860
- }
3861
- const r = spawnSync(process.execPath, [launcher, ...args], { stdio: "inherit", cwd: process.cwd() });
3862
- const status2 = r.status ?? 1;
3863
- if (status2 === 0 && process.stdout.isTTY) {
3864
- try {
3865
- if (!hasEditorRecommendation(process.cwd())) process.stdout.write(`
3866
- ${INIT_EDITOR_HINT}
3867
- `);
3868
- } catch {
3869
- }
3870
- }
3871
- return status2;
3872
- }
3873
- function resolveCheckLauncher(cwd) {
3874
- const launcher = resolveCompileLauncher(cwd);
3875
- return launcher === void 0 ? void 0 : join4(dirname2(launcher), "check_package.mjs");
3876
- }
3877
- function runCheck(args) {
3878
- const launcher = resolveCheckLauncher(process.cwd());
3879
- if (launcher === void 0) {
3880
- process.stderr.write(`error: ${NO_DSL_REMEDY}
3881
- `);
3882
- return 1;
3883
- }
3884
- const r = spawnSync(process.execPath, [launcher, ...args], { stdio: "inherit", cwd: process.cwd() });
3885
- return r.status ?? 1;
3886
- }
3887
- function resolveLspLauncher(cwd) {
3888
- const launcher = resolveCompileLauncher(cwd);
3889
- return launcher === void 0 ? void 0 : join4(dirname2(launcher), "lsp_server.mjs");
3890
- }
3891
- function runLsp(args) {
3892
- const launcher = resolveLspLauncher(process.cwd());
3893
- if (launcher === void 0) {
3894
- process.stderr.write(`error: ${NO_DSL_REMEDY}
3895
- `);
3896
- return 1;
3897
- }
3898
- const r = spawnSync(process.execPath, [launcher, ...args], { stdio: "inherit", cwd: process.cwd() });
3899
- return r.status ?? 1;
3900
- }
3901
- function compileAsync(cwd, args = []) {
3902
- const launcher = resolveCompileLauncher(cwd);
3903
- const t0 = performance.now();
3904
- if (launcher === void 0) {
3905
- return Promise.resolve({ ok: false, ms: 0, output: NO_DSL_REMEDY });
3906
- }
3907
- return new Promise((resolveRun) => {
3908
- const chunks = [];
3909
- const dec4 = new TextDecoder();
3910
- const child = spawn(process.execPath, [launcher, ...args], { cwd, stdio: ["ignore", "pipe", "pipe"] });
3911
- child.stdout?.on("data", (c) => chunks.push(dec4.decode(c)));
3912
- child.stderr?.on("data", (c) => chunks.push(dec4.decode(c)));
3913
- child.on("error", (e) => resolveRun({ ok: false, ms: performance.now() - t0, output: String(e) }));
3914
- child.on(
3915
- "close",
3916
- (code) => resolveRun({ ok: code === 0, ms: performance.now() - t0, output: chunks.join("") })
3917
- );
3918
- });
3919
- }
4208
+ init_compile();
3920
4209
 
3921
4210
  // src/dev.ts
3922
4211
  init_engine();
3923
4212
  init_cloud();
4213
+ init_compile();
4214
+ init_local_holon();
3924
4215
  import { existsSync as existsSync7, readFileSync as readFileSync7, watch } from "node:fs";
3925
4216
  import { createServer } from "node:http";
3926
4217
  import { dirname as dirname5, join as join8, resolve } from "node:path";
3927
4218
  import { pathToFileURL as pathToFileURL3 } from "node:url";
3928
- init_local_holon();
3929
4219
 
3930
4220
  // src/project.ts
3931
4221
  import { existsSync as existsSync5, statSync as statSync2 } from "node:fs";
@@ -4446,6 +4736,21 @@ var HELP = {
4446
4736
  ],
4447
4737
  examples: ["githolon status", "githolon status my-guestbook", "githolon status --target prod"]
4448
4738
  },
4739
+ compat: {
4740
+ usage: "githolon compat <ws-url | deploy.json | interface.json | dir> [--config <cfg>] [--workspace <ws>] [--json]",
4741
+ what: "THE CI GATE (the compat superpower): compare the CURRENT build's structural interface\n(build/<name>.interface.json \u2014 run `githolon compile` first) against a TARGET's installed law,\nPER MEMBER via #72 subtyping. Exits NON-ZERO on any incompatible member, so a client/law\nmismatch is caught before it ships. Target: a deploy.json (its staged .interface is the offer),\nan .interface.json / build dir, or a LIVE workspace URL (connect()s + reads the on-ledger offer).",
4742
+ flags: [
4743
+ ["<target>", "a workspace URL (https://host/v2/workspaces/<ws>), a deploy.json/interface.json, or a dir"],
4744
+ ["--config <cfg>", "the package config naming the current build (default: nomos.package.mjs)"],
4745
+ ["--workspace <ws>", "the workspace name when the live URL is a bare host"],
4746
+ ["--json", "emit { package, target, verdict, members[] } as one JSON line (the CI surface)"]
4747
+ ],
4748
+ examples: [
4749
+ "githolon compat build/guestbook.deploy.json",
4750
+ "githolon compat https://nomos.captainapp.co.uk/v2/workspaces/my-guestbook",
4751
+ "githolon compat ../deployed-law --json"
4752
+ ]
4753
+ },
4449
4754
  generate: {
4450
4755
  usage: "githolon generate <domain|aggregate|intent> <name> [--out <dir>] [--force] [--dry-run]",
4451
4756
  what: "Rails-style scaffolds on @githolon/dsl: a full domain (aggregate + create/mutate directives),\na lone aggregate, or a lone directive. Names arrive in any casing.",
@@ -5057,8 +5362,14 @@ Authoring:
5057
5362
  ./nomos.package.mjs \u2014 the unified report; exits
5058
5363
  non-zero on any REFUSE (compile runs it implicitly).
5059
5364
  --json emits the findings (with file:line) as an array
5365
+ githolon compat <ws-url|deploy.json|dir> [--json] THE CI GATE: compare the current build's interface
5366
+ against a target's installed law, PER MEMBER (#72
5367
+ subtyping); exits non-zero on any incompatible member
5060
5368
  githolon lsp the author-time gate as a stdio Language Server:
5061
5369
  red squiggles while you write law (VS Code/Neovim/\u2026)
5370
+ githolon mcp the author-time gate as a stdio MCP server for AI
5371
+ agents (Codex/Claude): tools nomos_check +
5372
+ nomos_evolve_preview. Setup: codex mcp add nomos -- githolon mcp
5062
5373
  githolon compile [compiler args] compile the package by ./nomos.package.mjs
5063
5374
  githolon init-editor write .vscode/extensions.json recommending the
5064
5375
  Nomos Law extension \u2014 the check gate as red
@@ -5247,11 +5558,11 @@ async function runProof(args) {
5247
5558
  const explicit = args.find((a, i) => !a.startsWith("--") && i !== domainValueIdx);
5248
5559
  let proofPath;
5249
5560
  if (explicit !== void 0) {
5250
- proofPath = resolve3(process.cwd(), explicit);
5251
- if (!existsSync12(proofPath)) return refuse(`proof not found: ${proofPath} \u2014 run \`githolon compile\` to (re)generate it`);
5561
+ proofPath = resolve4(process.cwd(), explicit);
5562
+ if (!existsSync13(proofPath)) return refuse(`proof not found: ${proofPath} \u2014 run \`githolon compile\` to (re)generate it`);
5252
5563
  } else {
5253
- const buildDir = join13(process.cwd(), "build");
5254
- if (!existsSync12(buildDir)) {
5564
+ const buildDir = join14(process.cwd(), "build");
5565
+ if (!existsSync13(buildDir)) {
5255
5566
  return refuse("no build/ directory here \u2014 run `githolon compile` first (every compile generates build/<name>.proof.mts from your law)");
5256
5567
  }
5257
5568
  const proofs = readdirSync4(buildDir).filter((f) => f.endsWith(".proof.mts"));
@@ -5261,7 +5572,7 @@ async function runProof(args) {
5261
5572
  if (proofs.length > 1) {
5262
5573
  return refuse(`found ${proofs.length} proofs (${proofs.join(", ")}) \u2014 name one: githolon proof build/<name>.proof.mts`);
5263
5574
  }
5264
- proofPath = join13(buildDir, proofs[0]);
5575
+ proofPath = join14(buildDir, proofs[0]);
5265
5576
  }
5266
5577
  if (!live) {
5267
5578
  try {
@@ -5274,17 +5585,17 @@ async function runProof(args) {
5274
5585
  }
5275
5586
  const resolvePkgDir = (name, fromDir) => {
5276
5587
  try {
5277
- return dirname6(createRequire2(pathToFileURL4(join13(fromDir, "noop.js"))).resolve(`${name}/package.json`));
5588
+ return dirname7(createRequire2(pathToFileURL4(join14(fromDir, "noop.js"))).resolve(`${name}/package.json`));
5278
5589
  } catch {
5279
5590
  return void 0;
5280
5591
  }
5281
5592
  };
5282
5593
  const dslDir = (() => {
5283
5594
  try {
5284
- return dirname6(createRequire2(pathToFileURL4(join13(process.cwd(), "noop.js"))).resolve("@githolon/dsl/package.json"));
5595
+ return dirname7(createRequire2(pathToFileURL4(join14(process.cwd(), "noop.js"))).resolve("@githolon/dsl/package.json"));
5285
5596
  } catch {
5286
5597
  try {
5287
- return dirname6(createRequire2(import.meta.url).resolve("@githolon/dsl/package.json"));
5598
+ return dirname7(createRequire2(import.meta.url).resolve("@githolon/dsl/package.json"));
5288
5599
  } catch {
5289
5600
  return void 0;
5290
5601
  }
@@ -5294,9 +5605,9 @@ async function runProof(args) {
5294
5605
  if (tsxDir === void 0) {
5295
5606
  return refuse("tsx not found \u2014 add @githolon/dsl to your project's dependencies (tsx rides along) and npm install");
5296
5607
  }
5297
- const tsxPkg = JSON.parse(readFileSync13(join13(tsxDir, "package.json"), "utf8"));
5608
+ const tsxPkg = JSON.parse(readFileSync13(join14(tsxDir, "package.json"), "utf8"));
5298
5609
  const tsxBinRel = typeof tsxPkg.bin === "string" ? tsxPkg.bin : tsxPkg.bin?.["tsx"];
5299
- const tsxCli = join13(tsxDir, tsxBinRel ?? "dist/cli.mjs");
5610
+ const tsxCli = join14(tsxDir, tsxBinRel ?? "dist/cli.mjs");
5300
5611
  console.log("githolon proof --live \u2014 the full cloud loop (throwaway workspace, retired on exit" + (keep ? "; --keep: kept" : "") + ")");
5301
5612
  const { wsCreate: wsCreate2, wsRetire: wsRetire2 } = await Promise.resolve().then(() => (init_cloud(), cloud_exports));
5302
5613
  const { resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
@@ -5325,7 +5636,7 @@ async function runProof(args) {
5325
5636
  );
5326
5637
  }
5327
5638
  }
5328
- const r = spawnSync4(process.execPath, [tsxCli, proofPath], {
5639
+ const r = spawnSync5(process.execPath, [tsxCli, proofPath], {
5329
5640
  stdio: "inherit",
5330
5641
  cwd: process.cwd(),
5331
5642
  env: {
@@ -5354,7 +5665,7 @@ function parseArgs(argv) {
5354
5665
  const [command, ...rest] = argv;
5355
5666
  if (command !== "generate" && command !== "g") {
5356
5667
  throw new Error(
5357
- `Unknown command '${command ?? "(none)"}'. Expected dev | compile | check | init-editor | proof | replay | scene | status | generate | ws | deploy | domains | secret.`
5668
+ `Unknown command '${command ?? "(none)"}'. Expected dev | compile | check | lsp | mcp | init-editor | proof | replay | scene | status | generate | ws | deploy | domains | secret.`
5358
5669
  );
5359
5670
  }
5360
5671
  let outDir = DEFAULT_OUT;
@@ -5407,9 +5718,16 @@ async function main(argv) {
5407
5718
  if (argv[0] === "check") {
5408
5719
  return runCheck(argv.slice(1));
5409
5720
  }
5721
+ if (argv[0] === "compat") {
5722
+ return runCompat(argv.slice(1));
5723
+ }
5410
5724
  if (argv[0] === "lsp") {
5411
5725
  return runLsp(argv.slice(1));
5412
5726
  }
5727
+ if (argv[0] === "mcp") {
5728
+ const { runMcpServer: runMcpServer2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
5729
+ return runMcpServer2();
5730
+ }
5413
5731
  if (argv[0] === "init-editor") {
5414
5732
  const { initEditor: initEditor2 } = await Promise.resolve().then(() => (init_editor(), editor_exports));
5415
5733
  return initEditor2(process.cwd());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "githolon",
3
- "version": "0.52.0",
3
+ "version": "0.54.0",
4
4
  "type": "module",
5
5
  "description": "githolon — the Nomos developer CLI: Rails-style generators for @githolon/dsl domains + the package compiler. Kernel-independent.",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -29,8 +29,8 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@bjorn3/browser_wasi_shim": "0.4.2",
32
- "@githolon/client": "^0.52.0",
33
- "@githolon/dsl": "^0.52.0",
32
+ "@githolon/client": "^0.54.0",
33
+ "@githolon/dsl": "^0.54.0",
34
34
  "isomorphic-git": "^1.38.4"
35
35
  },
36
36
  "devDependencies": {