githolon 0.51.0 → 0.52.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 +222 -104
  2. package/package.json +3 -3
package/dist/cli.mjs CHANGED
@@ -1195,6 +1195,87 @@ var init_cloud = __esm({
1195
1195
  }
1196
1196
  });
1197
1197
 
1198
+ // src/editor.ts
1199
+ var editor_exports = {};
1200
+ __export(editor_exports, {
1201
+ INIT_EDITOR_HINT: () => INIT_EDITOR_HINT,
1202
+ NOMOS_LAW_EXTENSION_ID: () => NOMOS_LAW_EXTENSION_ID,
1203
+ hasEditorRecommendation: () => hasEditorRecommendation,
1204
+ initEditor: () => initEditor,
1205
+ writeEditorRecommendation: () => writeEditorRecommendation
1206
+ });
1207
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
1208
+ import { dirname, join as join3 } from "node:path";
1209
+ function hasEditorRecommendation(cwd) {
1210
+ const p = join3(cwd, ".vscode", "extensions.json");
1211
+ if (!existsSync3(p)) return false;
1212
+ try {
1213
+ const json = JSON.parse(readFileSync4(p, "utf8"));
1214
+ return Array.isArray(json.recommendations) && json.recommendations.includes(NOMOS_LAW_EXTENSION_ID);
1215
+ } catch {
1216
+ return true;
1217
+ }
1218
+ }
1219
+ function writeEditorRecommendation(cwd) {
1220
+ const dir = join3(cwd, ".vscode");
1221
+ const path = join3(dir, "extensions.json");
1222
+ let json = {};
1223
+ let existed = false;
1224
+ if (existsSync3(path)) {
1225
+ existed = true;
1226
+ try {
1227
+ const parsed = JSON.parse(readFileSync4(path, "utf8"));
1228
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
1229
+ json = parsed;
1230
+ } else {
1231
+ throw new Error("extensions.json is not a JSON object");
1232
+ }
1233
+ } catch (e) {
1234
+ throw new Error(
1235
+ `${path} exists but could not be parsed as JSON (${e.message}) \u2014 fix or remove it, then rerun`
1236
+ );
1237
+ }
1238
+ }
1239
+ const recs = Array.isArray(json.recommendations) ? [...json.recommendations] : [];
1240
+ if (recs.includes(NOMOS_LAW_EXTENSION_ID)) {
1241
+ return { path, action: "unchanged" };
1242
+ }
1243
+ recs.push(NOMOS_LAW_EXTENSION_ID);
1244
+ json.recommendations = recs;
1245
+ mkdirSync3(dirname(path), { recursive: true });
1246
+ writeFileSync4(path, JSON.stringify(json, null, 2) + "\n", "utf8");
1247
+ return { path, action: existed ? "updated" : "created" };
1248
+ }
1249
+ function initEditor(cwd = process.cwd()) {
1250
+ let res;
1251
+ try {
1252
+ res = writeEditorRecommendation(cwd);
1253
+ } catch (e) {
1254
+ process.stderr.write(`error: ${e.message}
1255
+ `);
1256
+ return 1;
1257
+ }
1258
+ if (res.action === "unchanged") {
1259
+ process.stdout.write(`${res.path} already recommends the Nomos Law extension \u2014 nothing to do
1260
+ `);
1261
+ } else {
1262
+ process.stdout.write(
1263
+ `${res.action} ${res.path} \u2014 VS Code will now suggest the Nomos Law extension when this project is opened
1264
+ (install it from the marketplace, or search "Nomos Law"; it surfaces \`githolon check\` as you type)
1265
+ `
1266
+ );
1267
+ }
1268
+ return 0;
1269
+ }
1270
+ var NOMOS_LAW_EXTENSION_ID, INIT_EDITOR_HINT;
1271
+ var init_editor = __esm({
1272
+ "src/editor.ts"() {
1273
+ "use strict";
1274
+ NOMOS_LAW_EXTENSION_ID = "captain-app.nomos-law";
1275
+ INIT_EDITOR_HINT = "tip: the Nomos Law VS Code extension gives you these checks as you type \u2014 npx githolon init-editor";
1276
+ }
1277
+ });
1278
+
1198
1279
  // vendor/engine/git-fs.mjs
1199
1280
  function fsErr(code, p) {
1200
1281
  const e = new Error(`${code}: ${p}`);
@@ -1776,59 +1857,59 @@ var init_engine = __esm({
1776
1857
 
1777
1858
  // src/local_holon.ts
1778
1859
  import { randomBytes } from "node:crypto";
1779
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync, writeFileSync as writeFileSync4 } from "node:fs";
1780
- import { dirname as dirname2, join as join4 } from "node:path";
1860
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync, writeFileSync as writeFileSync5 } from "node:fs";
1861
+ import { dirname as dirname3, join as join5 } from "node:path";
1781
1862
  import { File as File2, Directory as Directory2 } from "@bjorn3/browser_wasi_shim";
1782
1863
  async function fetchJsonCached(url, cacheFile) {
1783
1864
  try {
1784
1865
  const r = await fetch(url);
1785
1866
  if (!r.ok) throw new Error(`${url} \u2192 ${r.status}`);
1786
1867
  const text = await r.text();
1787
- mkdirSync3(dirname2(cacheFile), { recursive: true });
1788
- writeFileSync4(cacheFile, text, "utf8");
1868
+ mkdirSync4(dirname3(cacheFile), { recursive: true });
1869
+ writeFileSync5(cacheFile, text, "utf8");
1789
1870
  return JSON.parse(text);
1790
1871
  } catch (e) {
1791
- if (existsSync3(cacheFile)) return JSON.parse(readFileSync4(cacheFile, "utf8"));
1872
+ if (existsSync4(cacheFile)) return JSON.parse(readFileSync5(cacheFile, "utf8"));
1792
1873
  throw e;
1793
1874
  }
1794
1875
  }
1795
1876
  async function fetchRuntime(cloud) {
1796
- const cache = join4(configDir(), "runtime");
1877
+ const cache = join5(configDir(), "runtime");
1797
1878
  let wasmBytes;
1798
- const wasmCache = join4(cache, "holon.wasm");
1879
+ const wasmCache = join5(cache, "holon.wasm");
1799
1880
  const localWasm = process.env["NOMOS_LOCAL_WASM"];
1800
1881
  if (localWasm) {
1801
- const bytes = readFileSync4(localWasm);
1802
- const packages2 = await fetchJsonCached(`${cloud}/v1/runtime/packages`, join4(cache, "packages.json"));
1882
+ const bytes = readFileSync5(localWasm);
1883
+ const packages2 = await fetchJsonCached(`${cloud}/v1/runtime/packages`, join5(cache, "packages.json"));
1803
1884
  return { wasmBytes: bytes, packages: packages2 };
1804
1885
  }
1805
1886
  try {
1806
1887
  const r = await fetch(`${cloud}/v1/runtime/holon.wasm`);
1807
1888
  if (!r.ok) throw new Error(`runtime wasm \u2192 ${r.status}`);
1808
1889
  wasmBytes = new Uint8Array(await r.arrayBuffer());
1809
- mkdirSync3(cache, { recursive: true });
1810
- writeFileSync4(wasmCache, wasmBytes);
1890
+ mkdirSync4(cache, { recursive: true });
1891
+ writeFileSync5(wasmCache, wasmBytes);
1811
1892
  } catch (e) {
1812
- if (!existsSync3(wasmCache)) throw e;
1813
- wasmBytes = readFileSync4(wasmCache);
1893
+ if (!existsSync4(wasmCache)) throw e;
1894
+ wasmBytes = readFileSync5(wasmCache);
1814
1895
  }
1815
- const packages = await fetchJsonCached(`${cloud}/v1/runtime/packages`, join4(cache, "packages.json"));
1896
+ const packages = await fetchJsonCached(`${cloud}/v1/runtime/packages`, join5(cache, "packages.json"));
1816
1897
  return { wasmBytes, packages };
1817
1898
  }
1818
1899
  function writeTreeToDisk(dir, diskPath) {
1819
- mkdirSync3(diskPath, { recursive: true });
1900
+ mkdirSync4(diskPath, { recursive: true });
1820
1901
  for (const [name, inode] of dir.contents) {
1821
- const p = join4(diskPath, name);
1902
+ const p = join5(diskPath, name);
1822
1903
  if (inode.contents instanceof Map) writeTreeToDisk(inode, p);
1823
- else writeFileSync4(p, inode.data ?? new Uint8Array(0));
1904
+ else writeFileSync5(p, inode.data ?? new Uint8Array(0));
1824
1905
  }
1825
1906
  }
1826
1907
  function readTreeFromDisk(diskPath) {
1827
1908
  const contents = /* @__PURE__ */ new Map();
1828
1909
  for (const name of readdirSync2(diskPath)) {
1829
- const p = join4(diskPath, name);
1910
+ const p = join5(diskPath, name);
1830
1911
  if (statSync(p).isDirectory()) contents.set(name, readTreeFromDisk(p));
1831
- else contents.set(name, new File2(readFileSync4(p)));
1912
+ else contents.set(name, new File2(readFileSync5(p)));
1832
1913
  }
1833
1914
  return new Directory2(contents);
1834
1915
  }
@@ -1886,8 +1967,8 @@ __export(proof_offline_exports, {
1886
1967
  runOfflineProofStandalone: () => runOfflineProofStandalone,
1887
1968
  runRelationBootstrapLeg: () => runRelationBootstrapLeg
1888
1969
  });
1889
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
1890
- import { basename, dirname as dirname3, join as join6 } from "node:path";
1970
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
1971
+ import { basename, dirname as dirname4, join as join7 } from "node:path";
1891
1972
  function parseOfflineLegs(proofSource) {
1892
1973
  if (!proofSource.includes("AUTO-GENERATED by nomos-compile")) {
1893
1974
  throw new Error("not a generated proof (missing the AUTO-GENERATED header) \u2014 run `githolon compile` to (re)generate build/<name>.proof.mts");
@@ -2252,16 +2333,16 @@ function actorFromPayload(payload, field) {
2252
2333
  return typeof value === "string" && value.trim().length > 0 ? value : void 0;
2253
2334
  }
2254
2335
  async function runOfflineProofStandalone(proofPath, cloud, domain) {
2255
- const src = readFileSync5(proofPath, "utf8");
2336
+ const src = readFileSync6(proofPath, "utf8");
2256
2337
  const packageName = basename(proofPath).replace(/\.proof\.mts$/, "");
2257
- const deployFile = join6(dirname3(proofPath), `${packageName}.deploy.json`);
2258
- if (!existsSync5(deployFile)) {
2338
+ const deployFile = join7(dirname4(proofPath), `${packageName}.deploy.json`);
2339
+ if (!existsSync6(deployFile)) {
2259
2340
  throw new Error(`missing ${deployFile} \u2014 run \`githolon compile\` (the proof and the deploy body are built together)`);
2260
2341
  }
2261
2342
  let legs;
2262
- const legsFile = join6(dirname3(proofPath), `${packageName}.proof-legs.json`);
2263
- if (existsSync5(legsFile)) {
2264
- const index = JSON.parse(readFileSync5(legsFile, "utf8"));
2343
+ const legsFile = join7(dirname4(proofPath), `${packageName}.proof-legs.json`);
2344
+ if (existsSync6(legsFile)) {
2345
+ const index = JSON.parse(readFileSync6(legsFile, "utf8"));
2265
2346
  const keys = Object.keys(index.domains);
2266
2347
  const chosen = domain ?? index.default;
2267
2348
  if (index.domains[chosen] === void 0) {
@@ -2279,7 +2360,7 @@ async function runOfflineProofStandalone(proofPath, cloud, domain) {
2279
2360
  if (domain !== void 0) throw new Error(`--domain needs a per-domain legs index (build/${packageName}.proof-legs.json) \u2014 recompile with \`githolon compile\` to emit it`);
2280
2361
  legs = parseOfflineLegs(src);
2281
2362
  }
2282
- const deployBody = JSON.parse(readFileSync5(deployFile, "utf8"));
2363
+ const deployBody = JSON.parse(readFileSync6(deployFile, "utf8"));
2283
2364
  const lawHash = await sha256hex(deployBody.packageUsda);
2284
2365
  console.log(`githolon proof \u2014 OFFLINE legs on a local holon (the cloud loop: githolon proof --live)`);
2285
2366
  const { eng } = await holonEngine(cloud, deployBody, "githolon-proof");
@@ -2311,22 +2392,22 @@ var harness_exports = {};
2311
2392
  __export(harness_exports, {
2312
2393
  harnessInstall: () => harnessInstall
2313
2394
  });
2314
- import { existsSync as existsSync9, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
2315
- import { join as join10 } from "node:path";
2395
+ import { existsSync as existsSync10, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "node:fs";
2396
+ import { join as join11 } from "node:path";
2316
2397
  import { randomBytes as randomBytes3 } from "node:crypto";
2317
2398
  async function harnessInstall(opts) {
2318
2399
  const { deploy: deployPaths, workspace, out: outPath } = opts;
2319
2400
  if (deployPaths.length === 0) die("no --deploy JSON path(s) given");
2320
- const runtimeDir = opts.runtime || process.env["NOMOS_OFFLINE_RUNTIME"] || join10(configDir(), "runtime");
2321
- const wasmFile = join10(runtimeDir, "holon.wasm");
2322
- const pkgsFile = join10(runtimeDir, "packages.json");
2323
- if (!existsSync9(wasmFile) || !existsSync9(pkgsFile)) {
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)) {
2324
2405
  die(
2325
2406
  `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.`
2326
2407
  );
2327
2408
  }
2328
- const wasm = readFileSync11(wasmFile);
2329
- const pkgs = JSON.parse(readFileSync11(pkgsFile, "utf8"));
2409
+ const wasm = readFileSync12(wasmFile);
2410
+ const pkgs = JSON.parse(readFileSync12(pkgsFile, "utf8"));
2330
2411
  const replica = BigInt(`0x${randomBytes3(8).toString("hex")}`) & (1n << 63n) - 1n;
2331
2412
  const eng = await createEngine({
2332
2413
  wasmModule: await WebAssembly.compile(wasm),
@@ -2339,8 +2420,8 @@ async function harnessInstall(opts) {
2339
2420
  author(eng, workspace, "bootstrap", "installDomain", installPayload(eng.hashes.nomos, eng.nomosPkg, "nomos-test-harness"), "");
2340
2421
  const lawHashes = [];
2341
2422
  for (const p of deployPaths) {
2342
- if (!existsSync9(p)) die(`deploy JSON not found: ${p} \u2014 run \`npx githolon compile\` first`);
2343
- const deploy2 = JSON.parse(readFileSync11(p, "utf8"));
2423
+ if (!existsSync10(p)) die(`deploy JSON not found: ${p} \u2014 run \`npx githolon compile\` first`);
2424
+ const deploy2 = JSON.parse(readFileSync12(p, "utf8"));
2344
2425
  const usda = deploy2.packageUsda;
2345
2426
  if (typeof usda !== "string" || !usda.startsWith("#usda")) die(`${p} has no #usda packageUsda`);
2346
2427
  const lawHash = await sha256hex(usda);
@@ -2352,7 +2433,7 @@ async function harnessInstall(opts) {
2352
2433
  const wsNode = eng.preopen.dir.contents.get("ws").contents.get(workspace);
2353
2434
  if (!wsNode) die(`internal: workspace tree '${workspace}' not found after install`);
2354
2435
  const treeBytes = serializeTree(wsNode);
2355
- writeFileSync6(outPath, treeBytes);
2436
+ writeFileSync7(outPath, treeBytes);
2356
2437
  process.stderr.write(`harness-install: snapshot written (${treeBytes.length} bytes) \u2192 ${outPath}
2357
2438
  `);
2358
2439
  process.stdout.write(JSON.stringify({ ok: true, snapshot: outPath, workspace, lawHashes }) + "\n");
@@ -3430,8 +3511,8 @@ var scene_exports = {};
3430
3511
  __export(scene_exports, {
3431
3512
  scene: () => scene
3432
3513
  });
3433
- import { existsSync as existsSync10, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "node:fs";
3434
- import { join as join11, resolve as resolve2 } from "node:path";
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";
3435
3516
  import { createHash as createHash5 } from "node:crypto";
3436
3517
  import git3 from "isomorphic-git";
3437
3518
  async function walkChain2(eng, ws) {
@@ -3452,8 +3533,8 @@ async function projectOnce(cloud, target, isDir, name) {
3452
3533
  const { eng } = await holonEngine(cloud, void 0, "githolon-scene");
3453
3534
  const SOURCE3 = "source";
3454
3535
  if (isDir) {
3455
- const gitDir = existsSync10(join11(target, ".git")) ? join11(target, ".git") : target;
3456
- if (!existsSync10(join11(gitDir, "HEAD"))) throw new Error(`${target} is not a git repo (no HEAD)`);
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)`);
3457
3538
  await mountFresh(eng, SOURCE3);
3458
3539
  eng.preopen.dir.contents.get("ws").contents.get(SOURCE3).contents.set("nomos.git", readTreeFromDisk(gitDir));
3459
3540
  } else {
@@ -3512,7 +3593,7 @@ async function projectOnce(cloud, target, isDir, name) {
3512
3593
  }
3513
3594
  async function scene(target, opts) {
3514
3595
  const cloud = cloudBase(opts.cloud);
3515
- const isDir = existsSync10(target) || target.includes("/") || target.startsWith(".");
3596
+ const isDir = existsSync11(target) || target.includes("/") || target.startsWith(".");
3516
3597
  const name = isDir ? (resolve2(target).split("/").filter(Boolean).pop() ?? "scene").replace(/\.git$/, "") : target;
3517
3598
  out11(`scene \u2014 ${name} (${isDir ? resolve2(target) : `${cloud} :: ${target}`})`);
3518
3599
  let first, second;
@@ -3532,13 +3613,13 @@ async function scene(target, opts) {
3532
3613
  err11(`the emitted stage violates the portable profile: ${violations.join("; ")}`);
3533
3614
  return 1;
3534
3615
  }
3535
- const outDir = resolve2(opts.out ?? join11("scene", name));
3536
- mkdirSync4(outDir, { recursive: true });
3537
- const usdaPath = join11(outDir, `${name}.usda`);
3538
- const usdzPath = join11(outDir, `${name}.usdz`);
3616
+ const outDir = resolve2(opts.out ?? join12("scene", name));
3617
+ mkdirSync5(outDir, { recursive: true });
3618
+ const usdaPath = join12(outDir, `${name}.usda`);
3619
+ const usdzPath = join12(outDir, `${name}.usdz`);
3539
3620
  const usdaBytes = new TextEncoder().encode(first.usda);
3540
- writeFileSync7(usdaPath, usdaBytes);
3541
- writeFileSync7(usdzPath, usdzPack([[`${name}.usda`, usdaBytes]]));
3621
+ writeFileSync8(usdaPath, usdaBytes);
3622
+ writeFileSync8(usdzPath, usdzPack([[`${name}.usda`, usdaBytes]]));
3542
3623
  const s = first.stats;
3543
3624
  out11(` verify_chain ${s.verdictLine}`);
3544
3625
  out11(` law canopy ${s.nodes} node(s), ${s.edges} edge(s) \u2014 from the chain's own installed packages`);
@@ -3564,10 +3645,10 @@ var init_scene = __esm({
3564
3645
  });
3565
3646
 
3566
3647
  // src/cli.ts
3567
- import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync12 } from "node:fs";
3648
+ import { existsSync as existsSync12, readdirSync as readdirSync4, readFileSync as readFileSync13 } from "node:fs";
3568
3649
  import { spawnSync as spawnSync4 } from "node:child_process";
3569
3650
  import { createRequire as createRequire2 } from "node:module";
3570
- import { dirname as dirname5, join as join12, resolve as resolve3 } from "node:path";
3651
+ import { dirname as dirname6, join as join13, resolve as resolve3 } from "node:path";
3571
3652
  import { pathToFileURL as pathToFileURL4 } from "node:url";
3572
3653
 
3573
3654
  // src/generate.ts
@@ -3752,21 +3833,22 @@ Re-run with --force to replace it.`
3752
3833
  init_cloud();
3753
3834
 
3754
3835
  // src/compile.ts
3836
+ init_editor();
3755
3837
  import { spawn, spawnSync } from "node:child_process";
3756
3838
  import { createRequire } from "node:module";
3757
- import { dirname, join as join3 } from "node:path";
3839
+ import { dirname as dirname2, join as join4 } from "node:path";
3758
3840
  import { pathToFileURL } from "node:url";
3759
3841
  function resolveCompileLauncher(cwd) {
3760
3842
  const resolveDslPkg = (fromDir) => {
3761
3843
  try {
3762
- const req = fromDir === void 0 ? createRequire(import.meta.url) : createRequire(pathToFileURL(join3(fromDir, "noop.js")));
3844
+ const req = fromDir === void 0 ? createRequire(import.meta.url) : createRequire(pathToFileURL(join4(fromDir, "noop.js")));
3763
3845
  return req.resolve("@githolon/dsl/package.json");
3764
3846
  } catch {
3765
3847
  return void 0;
3766
3848
  }
3767
3849
  };
3768
3850
  const dslPkg = resolveDslPkg(cwd) ?? resolveDslPkg(void 0);
3769
- return dslPkg === void 0 ? void 0 : join3(dirname(dslPkg), "compile_package.mjs");
3851
+ return dslPkg === void 0 ? void 0 : join4(dirname2(dslPkg), "compile_package.mjs");
3770
3852
  }
3771
3853
  var NO_DSL_REMEDY = "@githolon/dsl not found \u2014 add it to your project's dependencies";
3772
3854
  function runCompile(args) {
@@ -3777,11 +3859,20 @@ function runCompile(args) {
3777
3859
  return 1;
3778
3860
  }
3779
3861
  const r = spawnSync(process.execPath, [launcher, ...args], { stdio: "inherit", cwd: process.cwd() });
3780
- return r.status ?? 1;
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;
3781
3872
  }
3782
3873
  function resolveCheckLauncher(cwd) {
3783
3874
  const launcher = resolveCompileLauncher(cwd);
3784
- return launcher === void 0 ? void 0 : join3(dirname(launcher), "check_package.mjs");
3875
+ return launcher === void 0 ? void 0 : join4(dirname2(launcher), "check_package.mjs");
3785
3876
  }
3786
3877
  function runCheck(args) {
3787
3878
  const launcher = resolveCheckLauncher(process.cwd());
@@ -3793,6 +3884,20 @@ function runCheck(args) {
3793
3884
  const r = spawnSync(process.execPath, [launcher, ...args], { stdio: "inherit", cwd: process.cwd() });
3794
3885
  return r.status ?? 1;
3795
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
+ }
3796
3901
  function compileAsync(cwd, args = []) {
3797
3902
  const launcher = resolveCompileLauncher(cwd);
3798
3903
  const t0 = performance.now();
@@ -3816,20 +3921,20 @@ function compileAsync(cwd, args = []) {
3816
3921
  // src/dev.ts
3817
3922
  init_engine();
3818
3923
  init_cloud();
3819
- import { existsSync as existsSync6, readFileSync as readFileSync6, watch } from "node:fs";
3924
+ import { existsSync as existsSync7, readFileSync as readFileSync7, watch } from "node:fs";
3820
3925
  import { createServer } from "node:http";
3821
- import { dirname as dirname4, join as join7, resolve } from "node:path";
3926
+ import { dirname as dirname5, join as join8, resolve } from "node:path";
3822
3927
  import { pathToFileURL as pathToFileURL3 } from "node:url";
3823
3928
  init_local_holon();
3824
3929
 
3825
3930
  // src/project.ts
3826
- import { existsSync as existsSync4, statSync as statSync2 } from "node:fs";
3827
- import { join as join5 } from "node:path";
3931
+ import { existsSync as existsSync5, statSync as statSync2 } from "node:fs";
3932
+ import { join as join6 } from "node:path";
3828
3933
  import { pathToFileURL as pathToFileURL2 } from "node:url";
3829
3934
  var CONFIG_FILE = "nomos.package.mjs";
3830
3935
  async function loadProject(cwd, opts = {}) {
3831
- const configPath = join5(cwd, CONFIG_FILE);
3832
- if (!existsSync4(configPath)) return void 0;
3936
+ const configPath = join6(cwd, CONFIG_FILE);
3937
+ if (!existsSync5(configPath)) return void 0;
3833
3938
  const href = pathToFileURL2(configPath).href + (opts.bust === true ? `?v=${statSync2(configPath).mtimeMs}-${Date.now()}` : "");
3834
3939
  const mod = await import(href);
3835
3940
  const cfg = mod.default;
@@ -3994,11 +4099,11 @@ function startServer(s) {
3994
4099
  });
3995
4100
  }
3996
4101
  function readDeployBody2(cwd, packageName) {
3997
- const file = join7(cwd, "build", `${packageName}.deploy.json`);
3998
- if (!existsSync6(file)) {
4102
+ const file = join8(cwd, "build", `${packageName}.deploy.json`);
4103
+ if (!existsSync7(file)) {
3999
4104
  throw new Error(`compile produced no ${file} \u2014 check the compile output above`);
4000
4105
  }
4001
- return JSON.parse(readFileSync6(file, "utf8"));
4106
+ return JSON.parse(readFileSync7(file, "utf8"));
4002
4107
  }
4003
4108
  async function installLaw(s, deployBody, installedBy) {
4004
4109
  const newHash = await sha256hex(deployBody.packageUsda);
@@ -4014,11 +4119,11 @@ async function installLaw(s, deployBody, installedBy) {
4014
4119
  return { ok: true };
4015
4120
  }
4016
4121
  async function runProofCycle(s, cwd, deployBody, installedBy) {
4017
- const proofPath = join7(cwd, "build", `${s.packageName}.proof.mts`);
4018
- if (!existsSync6(proofPath)) return void 0;
4122
+ const proofPath = join8(cwd, "build", `${s.packageName}.proof.mts`);
4123
+ if (!existsSync7(proofPath)) return void 0;
4019
4124
  const scratch = `proof-${s.cycle}`;
4020
4125
  try {
4021
- const legs = parseOfflineLegs(readFileSync6(proofPath, "utf8"));
4126
+ const legs = parseOfflineLegs(readFileSync7(proofPath, "utf8"));
4022
4127
  await mountFresh(s.eng, scratch);
4023
4128
  author(s.eng, scratch, "bootstrap", "installDomain", installPayload(s.eng.hashes.nomos, s.eng.nomosPkg, installedBy), "");
4024
4129
  author(s.eng, scratch, "nomos", "installDomain", installPayload(s.lawHash, deployBody.packageUsda, installedBy), s.eng.hashes.nomos);
@@ -4037,7 +4142,7 @@ async function dev(opts) {
4037
4142
  return void 0;
4038
4143
  });
4039
4144
  if (project === void 0) {
4040
- if (!existsSync6(join7(cwd, CONFIG_FILE))) {
4145
+ if (!existsSync7(join8(cwd, CONFIG_FILE))) {
4041
4146
  err5(`no ${CONFIG_FILE} here (${cwd}) \u2014 \`githolon dev\` runs inside a project; scaffold one from examples/guestbook or \`githolon generate domain <name>\``);
4042
4147
  }
4043
4148
  return 1;
@@ -4104,12 +4209,12 @@ ${firstCompile.output}`);
4104
4209
  try {
4105
4210
  const cfgModule = await import(pathToFileURL3(project.configPath).href);
4106
4211
  for (const d of cfgModule.default?.domains ?? []) {
4107
- for (const m of d.modules ?? []) moduleDirs.add(dirname4(resolve(cwd, m)));
4212
+ for (const m of d.modules ?? []) moduleDirs.add(dirname5(resolve(cwd, m)));
4108
4213
  }
4109
4214
  } catch {
4110
- moduleDirs.add(join7(cwd, "domains"));
4215
+ moduleDirs.add(join8(cwd, "domains"));
4111
4216
  }
4112
- if (moduleDirs.size === 0) moduleDirs.add(join7(cwd, "domains"));
4217
+ if (moduleDirs.size === 0) moduleDirs.add(join8(cwd, "domains"));
4113
4218
  let timer;
4114
4219
  let cycling = false;
4115
4220
  let queued = false;
@@ -4159,7 +4264,7 @@ ${run.output}`);
4159
4264
  };
4160
4265
  const watchers = [];
4161
4266
  for (const d of moduleDirs) {
4162
- if (!existsSync6(d)) continue;
4267
+ if (!existsSync7(d)) continue;
4163
4268
  watchers.push(watch(d, { recursive: true }, (_evt, f) => onChange(f)));
4164
4269
  s.watchRoots.push(d.startsWith(cwd) ? d.slice(cwd.length + 1) + "/" : d);
4165
4270
  }
@@ -4186,7 +4291,7 @@ ${run.output}`);
4186
4291
  init_cloud();
4187
4292
  import { spawnSync as spawnSync2 } from "node:child_process";
4188
4293
  import { randomBytes as randomBytes2 } from "node:crypto";
4189
- import { readFileSync as readFileSync7 } from "node:fs";
4294
+ import { readFileSync as readFileSync8 } from "node:fs";
4190
4295
  var out6 = (s) => void process.stdout.write(s + "\n");
4191
4296
  var err6 = (s) => void process.stderr.write("error: " + s + "\n");
4192
4297
  function git(args, opts = {}) {
@@ -4215,7 +4320,7 @@ function workspaceFromPath(path) {
4215
4320
  }
4216
4321
  async function gitCredential(action) {
4217
4322
  if (action !== "get") return 0;
4218
- const kv = parseCredentialInput(readFileSync7(0, "utf8"));
4323
+ const kv = parseCredentialInput(readFileSync8(0, "utf8"));
4219
4324
  const ws = workspaceFromPath(kv["path"]);
4220
4325
  if (kv["protocol"] !== "https" || ws === void 0) return 0;
4221
4326
  const cloud = `https://${kv["host"]}`;
@@ -4503,14 +4608,14 @@ init_engine();
4503
4608
  init_cloud();
4504
4609
  init_local_holon();
4505
4610
  import { spawnSync as spawnSync3 } from "node:child_process";
4506
- import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
4507
- import { join as join8 } from "node:path";
4611
+ import { existsSync as existsSync8, readdirSync as readdirSync3, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "node:fs";
4612
+ import { join as join9 } from "node:path";
4508
4613
  var out7 = (s) => void process.stdout.write(s + "\n");
4509
4614
  var err7 = (s) => void process.stderr.write("error: " + s + "\n");
4510
4615
  var WS2 = "local";
4511
4616
  async function ledgerInit(dir, opts) {
4512
4617
  const cloud = cloudBase(opts.cloud);
4513
- if (existsSync7(dir) && readdirSync3(dir).length > 0) {
4618
+ if (existsSync8(dir) && readdirSync3(dir).length > 0) {
4514
4619
  err7(`refusing to mint into non-empty directory: ${dir}`);
4515
4620
  return 1;
4516
4621
  }
@@ -4521,7 +4626,7 @@ async function ledgerInit(dir, opts) {
4521
4626
  err7(e.message);
4522
4627
  return 1;
4523
4628
  }
4524
- const deployBody = JSON.parse(readFileSync8(deployFile, "utf8"));
4629
+ const deployBody = JSON.parse(readFileSync9(deployFile, "utf8"));
4525
4630
  const domainHash = await sha256hex(deployBody.packageUsda);
4526
4631
  const c = loadCreds();
4527
4632
  const principal = await sessionToken().catch(() => void 0) !== void 0 ? c.session.uid : c.principal;
@@ -4537,10 +4642,10 @@ async function ledgerInit(dir, opts) {
4537
4642
  const verdict = verifyChainLocal(eng, WS2);
4538
4643
  if (!printVerdict(verdict)) return 1;
4539
4644
  const gitTree = eng.preopen.dir.contents.get("ws").contents.get(WS2).contents.get("nomos.git");
4540
- const gitDir = join8(dir, ".git");
4645
+ const gitDir = join9(dir, ".git");
4541
4646
  writeTreeToDisk(gitTree, gitDir);
4542
- const cfgPath = join8(gitDir, "config");
4543
- writeFileSync5(cfgPath, readFileSync8(cfgPath, "utf8").replace(/bare = true/, "bare = false"), "utf8");
4647
+ const cfgPath = join9(gitDir, "config");
4648
+ writeFileSync6(cfgPath, readFileSync9(cfgPath, "utf8").replace(/bare = true/, "bare = false"), "utf8");
4544
4649
  spawnSync3("git", ["-C", dir, "reset", "--hard", "main"], { stdio: "ignore" });
4545
4650
  out7(`\u2713 holon written \u2192 ${dir} (a normal git repo; git log IS the audit trail)`);
4546
4651
  out7(`
@@ -4549,8 +4654,8 @@ Birth it on Nomos Cloud (the cloud re-derives this exact verdict):`);
4549
4654
  return 0;
4550
4655
  }
4551
4656
  async function ledgerVerify(dir, opts) {
4552
- const gitDir = existsSync7(join8(dir, ".git")) ? join8(dir, ".git") : dir;
4553
- if (!existsSync7(join8(gitDir, "HEAD"))) {
4657
+ const gitDir = existsSync8(join9(dir, ".git")) ? join9(dir, ".git") : dir;
4658
+ if (!existsSync8(join9(gitDir, "HEAD"))) {
4554
4659
  err7(`${dir} is not a git repo (no HEAD)`);
4555
4660
  return 1;
4556
4661
  }
@@ -4566,8 +4671,8 @@ init_engine();
4566
4671
  init_engine();
4567
4672
  init_cloud();
4568
4673
  init_local_holon();
4569
- import { existsSync as existsSync8 } from "node:fs";
4570
- import { join as join9 } from "node:path";
4674
+ import { existsSync as existsSync9 } from "node:fs";
4675
+ import { join as join10 } from "node:path";
4571
4676
  import git2 from "isomorphic-git";
4572
4677
  var out8 = (s) => void process.stdout.write(s + "\n");
4573
4678
  var err8 = (s) => void process.stderr.write("error: " + s + "\n");
@@ -4663,10 +4768,10 @@ async function replay(target, opts) {
4663
4768
  the runtime rides ${cloud}/v1/runtime/* (cached in ~/.holon/runtime after one fetch)`);
4664
4769
  return 1;
4665
4770
  }
4666
- const isDir = existsSync8(target) || target.includes("/") || target.startsWith(".");
4771
+ const isDir = existsSync9(target) || target.includes("/") || target.startsWith(".");
4667
4772
  if (isDir) {
4668
- const gitDir = existsSync8(join9(target, ".git")) ? join9(target, ".git") : target;
4669
- if (!existsSync8(join9(gitDir, "HEAD"))) {
4773
+ const gitDir = existsSync9(join10(target, ".git")) ? join10(target, ".git") : target;
4774
+ if (!existsSync9(join10(gitDir, "HEAD"))) {
4670
4775
  err8(`${target} is not a git repo (no HEAD) \u2014 pass a holon directory (githolon ledger init <dir>) or a workspace name`);
4671
4776
  return 1;
4672
4777
  }
@@ -4830,7 +4935,7 @@ async function decrypt(ws, opts) {
4830
4935
  init_engine();
4831
4936
  init_cloud();
4832
4937
  init_lifecycle();
4833
- import { readFileSync as readFileSync10 } from "node:fs";
4938
+ import { readFileSync as readFileSync11 } from "node:fs";
4834
4939
  var out10 = (s) => void process.stdout.write(s + "\n");
4835
4940
  var err10 = (s) => void process.stderr.write("error: " + s + "\n");
4836
4941
  function lawDiffVerdict(localHash, installed, ws, hasCurrencyData) {
@@ -4885,7 +4990,7 @@ async function status(wsArg, opts) {
4885
4990
  }
4886
4991
  let localHash;
4887
4992
  try {
4888
- const body = JSON.parse(readFileSync10(discoverDeployJson(process.cwd(), opts.file), "utf8"));
4993
+ const body = JSON.parse(readFileSync11(discoverDeployJson(process.cwd(), opts.file), "utf8"));
4889
4994
  if (typeof body.packageUsda === "string") localHash = await sha256hex(body.packageUsda);
4890
4995
  } catch {
4891
4996
  localHash = void 0;
@@ -4948,10 +5053,16 @@ The inner loop:
4948
5053
  --query <id> [--param k=v] keys from the chain, read private fields in cleartext
4949
5054
 
4950
5055
  Authoring:
4951
- githolon check [config] run the author-time gate (static twins) on
5056
+ githolon check [config] [--json] [--against <p>] run the author-time gate (static twins) on
4952
5057
  ./nomos.package.mjs \u2014 the unified report; exits
4953
- non-zero on any REFUSE (compile runs it implicitly)
5058
+ non-zero on any REFUSE (compile runs it implicitly).
5059
+ --json emits the findings (with file:line) as an array
5060
+ githolon lsp the author-time gate as a stdio Language Server:
5061
+ red squiggles while you write law (VS Code/Neovim/\u2026)
4954
5062
  githolon compile [compiler args] compile the package by ./nomos.package.mjs
5063
+ githolon init-editor write .vscode/extensions.json recommending the
5064
+ Nomos Law extension \u2014 the check gate as red
5065
+ squiggles while you type (idempotent)
4955
5066
  githolon proof [build/<name>.proof.mts] run the proof GENERATED from your law, live
4956
5067
  (every compile emits it; ALL GREEN or it names the jam)
4957
5068
  githolon harness-install --deploy <deploy.json[,...]> offline install for the headless TEST HARNESS:
@@ -5137,10 +5248,10 @@ async function runProof(args) {
5137
5248
  let proofPath;
5138
5249
  if (explicit !== void 0) {
5139
5250
  proofPath = resolve3(process.cwd(), explicit);
5140
- if (!existsSync11(proofPath)) return refuse(`proof not found: ${proofPath} \u2014 run \`githolon compile\` to (re)generate it`);
5251
+ if (!existsSync12(proofPath)) return refuse(`proof not found: ${proofPath} \u2014 run \`githolon compile\` to (re)generate it`);
5141
5252
  } else {
5142
- const buildDir = join12(process.cwd(), "build");
5143
- if (!existsSync11(buildDir)) {
5253
+ const buildDir = join13(process.cwd(), "build");
5254
+ if (!existsSync12(buildDir)) {
5144
5255
  return refuse("no build/ directory here \u2014 run `githolon compile` first (every compile generates build/<name>.proof.mts from your law)");
5145
5256
  }
5146
5257
  const proofs = readdirSync4(buildDir).filter((f) => f.endsWith(".proof.mts"));
@@ -5150,7 +5261,7 @@ async function runProof(args) {
5150
5261
  if (proofs.length > 1) {
5151
5262
  return refuse(`found ${proofs.length} proofs (${proofs.join(", ")}) \u2014 name one: githolon proof build/<name>.proof.mts`);
5152
5263
  }
5153
- proofPath = join12(buildDir, proofs[0]);
5264
+ proofPath = join13(buildDir, proofs[0]);
5154
5265
  }
5155
5266
  if (!live) {
5156
5267
  try {
@@ -5163,17 +5274,17 @@ async function runProof(args) {
5163
5274
  }
5164
5275
  const resolvePkgDir = (name, fromDir) => {
5165
5276
  try {
5166
- return dirname5(createRequire2(pathToFileURL4(join12(fromDir, "noop.js"))).resolve(`${name}/package.json`));
5277
+ return dirname6(createRequire2(pathToFileURL4(join13(fromDir, "noop.js"))).resolve(`${name}/package.json`));
5167
5278
  } catch {
5168
5279
  return void 0;
5169
5280
  }
5170
5281
  };
5171
5282
  const dslDir = (() => {
5172
5283
  try {
5173
- return dirname5(createRequire2(pathToFileURL4(join12(process.cwd(), "noop.js"))).resolve("@githolon/dsl/package.json"));
5284
+ return dirname6(createRequire2(pathToFileURL4(join13(process.cwd(), "noop.js"))).resolve("@githolon/dsl/package.json"));
5174
5285
  } catch {
5175
5286
  try {
5176
- return dirname5(createRequire2(import.meta.url).resolve("@githolon/dsl/package.json"));
5287
+ return dirname6(createRequire2(import.meta.url).resolve("@githolon/dsl/package.json"));
5177
5288
  } catch {
5178
5289
  return void 0;
5179
5290
  }
@@ -5183,9 +5294,9 @@ async function runProof(args) {
5183
5294
  if (tsxDir === void 0) {
5184
5295
  return refuse("tsx not found \u2014 add @githolon/dsl to your project's dependencies (tsx rides along) and npm install");
5185
5296
  }
5186
- const tsxPkg = JSON.parse(readFileSync12(join12(tsxDir, "package.json"), "utf8"));
5297
+ const tsxPkg = JSON.parse(readFileSync13(join13(tsxDir, "package.json"), "utf8"));
5187
5298
  const tsxBinRel = typeof tsxPkg.bin === "string" ? tsxPkg.bin : tsxPkg.bin?.["tsx"];
5188
- const tsxCli = join12(tsxDir, tsxBinRel ?? "dist/cli.mjs");
5299
+ const tsxCli = join13(tsxDir, tsxBinRel ?? "dist/cli.mjs");
5189
5300
  console.log("githolon proof --live \u2014 the full cloud loop (throwaway workspace, retired on exit" + (keep ? "; --keep: kept" : "") + ")");
5190
5301
  const { wsCreate: wsCreate2, wsRetire: wsRetire2 } = await Promise.resolve().then(() => (init_cloud(), cloud_exports));
5191
5302
  const { resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
@@ -5243,7 +5354,7 @@ function parseArgs(argv) {
5243
5354
  const [command, ...rest] = argv;
5244
5355
  if (command !== "generate" && command !== "g") {
5245
5356
  throw new Error(
5246
- `Unknown command '${command ?? "(none)"}'. Expected dev | compile | proof | replay | scene | status | generate | ws | deploy | domains | secret.`
5357
+ `Unknown command '${command ?? "(none)"}'. Expected dev | compile | check | init-editor | proof | replay | scene | status | generate | ws | deploy | domains | secret.`
5247
5358
  );
5248
5359
  }
5249
5360
  let outDir = DEFAULT_OUT;
@@ -5296,6 +5407,13 @@ async function main(argv) {
5296
5407
  if (argv[0] === "check") {
5297
5408
  return runCheck(argv.slice(1));
5298
5409
  }
5410
+ if (argv[0] === "lsp") {
5411
+ return runLsp(argv.slice(1));
5412
+ }
5413
+ if (argv[0] === "init-editor") {
5414
+ const { initEditor: initEditor2 } = await Promise.resolve().then(() => (init_editor(), editor_exports));
5415
+ return initEditor2(process.cwd());
5416
+ }
5299
5417
  if (argv[0] === "proof") {
5300
5418
  return runProof(argv.slice(1));
5301
5419
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "githolon",
3
- "version": "0.51.0",
3
+ "version": "0.52.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.51.0",
33
- "@githolon/dsl": "^0.51.0",
32
+ "@githolon/client": "^0.52.0",
33
+ "@githolon/dsl": "^0.52.0",
34
34
  "isomorphic-git": "^1.38.4"
35
35
  },
36
36
  "devDependencies": {