@vibgrate/cli 2026.729.2 → 2026.730.1

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 (107) hide show
  1. package/DOCS.md +5 -1
  2. package/dist/agent-33BMKWJC.js +22 -0
  3. package/dist/agent-33BMKWJC.js.map +1 -0
  4. package/dist/baseline-V6MYDEMS.js +9 -0
  5. package/dist/{baseline-UOTGTJYT.js.map → baseline-V6MYDEMS.js.map} +1 -1
  6. package/dist/checkpoint-4WFZMKEO.js +56 -0
  7. package/dist/checkpoint-4WFZMKEO.js.map +1 -0
  8. package/dist/chunk-543E6WDJ.js +61 -0
  9. package/dist/chunk-543E6WDJ.js.map +1 -0
  10. package/dist/{chunk-P3QIO4ZU.js → chunk-5G7C6H2G.js} +14 -204
  11. package/dist/chunk-5G7C6H2G.js.map +1 -0
  12. package/dist/{chunk-JIBOJTGS.js → chunk-5HYCB55P.js} +101 -101
  13. package/dist/chunk-5HYCB55P.js.map +1 -0
  14. package/dist/{chunk-67YG3R5F.js → chunk-6D4BCGRQ.js} +6 -62
  15. package/dist/chunk-6D4BCGRQ.js.map +1 -0
  16. package/dist/{chunk-F6ZPUKED.js → chunk-6WNJJXFY.js} +4 -4
  17. package/dist/{chunk-F6ZPUKED.js.map → chunk-6WNJJXFY.js.map} +1 -1
  18. package/dist/{chunk-PXXVLX7K.js → chunk-CFL3QQQZ.js} +7 -6
  19. package/dist/chunk-CFL3QQQZ.js.map +1 -0
  20. package/dist/{chunk-74ZMAOVF.js → chunk-CQDCZQFV.js} +55 -9
  21. package/dist/chunk-CQDCZQFV.js.map +1 -0
  22. package/dist/{chunk-VZRQUI54.js → chunk-DBMYWNLC.js} +4 -4
  23. package/dist/{chunk-VZRQUI54.js.map → chunk-DBMYWNLC.js.map} +1 -1
  24. package/dist/{chunk-QDZ47G45.js → chunk-EKCNSRNJ.js} +20 -11
  25. package/dist/chunk-EKCNSRNJ.js.map +1 -0
  26. package/dist/{chunk-SIPGDJT4.js → chunk-EVLS2M5G.js} +4 -4
  27. package/dist/{chunk-SIPGDJT4.js.map → chunk-EVLS2M5G.js.map} +1 -1
  28. package/dist/{chunk-G4HTI4ZM.js → chunk-FHJBGPJZ.js} +3 -2
  29. package/dist/chunk-FHJBGPJZ.js.map +1 -0
  30. package/dist/{chunk-CYC3ZI32.js → chunk-GLT3N7T2.js} +9 -6
  31. package/dist/chunk-GLT3N7T2.js.map +1 -0
  32. package/dist/chunk-HTLDXEHW.js +60 -0
  33. package/dist/chunk-HTLDXEHW.js.map +1 -0
  34. package/dist/{chunk-GI6W53LM.js → chunk-IDAQV3NQ.js} +4 -2
  35. package/dist/chunk-IDAQV3NQ.js.map +1 -0
  36. package/dist/{chunk-2ZHWF7C6.js → chunk-KGWEBM2J.js} +43 -68
  37. package/dist/chunk-KGWEBM2J.js.map +1 -0
  38. package/dist/{chunk-RZEXHDCF.js → chunk-MYM5VIJ4.js} +14 -3
  39. package/dist/chunk-MYM5VIJ4.js.map +1 -0
  40. package/dist/{chunk-HDQG3QYV.js → chunk-ON7PZNYV.js} +42 -26
  41. package/dist/chunk-ON7PZNYV.js.map +1 -0
  42. package/dist/chunk-Q4UQ73NA.js +6 -0
  43. package/dist/{chunk-WQE4OIZS.js.map → chunk-Q4UQ73NA.js.map} +1 -1
  44. package/dist/{chunk-PMYWZRRS.js → chunk-RBIRXQR6.js} +21 -3
  45. package/dist/chunk-RBIRXQR6.js.map +1 -0
  46. package/dist/{chunk-TCJTQGAO.js → chunk-RGEVBSLK.js} +3 -3
  47. package/dist/{chunk-TCJTQGAO.js.map → chunk-RGEVBSLK.js.map} +1 -1
  48. package/dist/chunk-TJ3WIJ7H.js +216 -0
  49. package/dist/chunk-TJ3WIJ7H.js.map +1 -0
  50. package/dist/chunk-VOEYUAER.js +203 -0
  51. package/dist/chunk-VOEYUAER.js.map +1 -0
  52. package/dist/{chunk-PRGCCW7D.js → chunk-WBMMRERH.js} +3 -3
  53. package/dist/{chunk-PRGCCW7D.js.map → chunk-WBMMRERH.js.map} +1 -1
  54. package/dist/{chunk-6HX47EBL.js → chunk-X634FZVG.js} +3 -3
  55. package/dist/{chunk-6HX47EBL.js.map → chunk-X634FZVG.js.map} +1 -1
  56. package/dist/cli.js +563 -340
  57. package/dist/cli.js.map +1 -1
  58. package/dist/{fs-6JKZNQ3U.js → fs-HNR6LAI7.js} +3 -3
  59. package/dist/fs-HNR6LAI7.js.map +1 -0
  60. package/dist/{graph-backend-GBL33GMW.js → graph-backend-G5Y6QQV4.js} +4 -4
  61. package/dist/{graph-backend-GBL33GMW.js.map → graph-backend-G5Y6QQV4.js.map} +1 -1
  62. package/dist/index.d.ts +1 -1
  63. package/dist/index.js +12 -10
  64. package/dist/index.js.map +1 -1
  65. package/dist/{interactive-HLGJJFLI.js → interactive-NJG6467J.js} +27 -24
  66. package/dist/interactive-NJG6467J.js.map +1 -0
  67. package/dist/{llm-host-5RE34322.js → llm-host-DZBD5D36.js} +5 -5
  68. package/dist/{llm-host-5RE34322.js.map → llm-host-DZBD5D36.js.map} +1 -1
  69. package/dist/load-YPSEDYIW.js +10 -0
  70. package/dist/{load-4MLIHGXF.js.map → load-YPSEDYIW.js.map} +1 -1
  71. package/dist/rules-ASYEUNB5.js +74 -0
  72. package/dist/rules-ASYEUNB5.js.map +1 -0
  73. package/dist/run-outcome-5FQWHWYK.js +253 -0
  74. package/dist/run-outcome-5FQWHWYK.js.map +1 -0
  75. package/dist/runtime-session-Y4ZD3QDO.js +20 -0
  76. package/dist/{runtime-session-5M6QZZOS.js.map → runtime-session-Y4ZD3QDO.js.map} +1 -1
  77. package/dist/{session-2JT4AUZT.js → session-3OBIQLE4.js} +5 -5
  78. package/dist/{session-2JT4AUZT.js.map → session-3OBIQLE4.js.map} +1 -1
  79. package/dist/{stream-json-Y45JBNYP.js → stream-json-3YOPU3LF.js} +82 -10
  80. package/dist/stream-json-3YOPU3LF.js.map +1 -0
  81. package/dist/{vg-mcp-bridge-ZCKBYQ2D.js → vg-mcp-bridge-PZFGTZSA.js} +8 -7
  82. package/dist/vg-mcp-bridge-PZFGTZSA.js.map +1 -0
  83. package/dist/{vgd-TGGEAGQZ.js → vgd-DH7AHQ23.js} +9 -8
  84. package/dist/vgd-DH7AHQ23.js.map +1 -0
  85. package/package.json +1 -1
  86. package/dist/baseline-UOTGTJYT.js +0 -9
  87. package/dist/chunk-2ZHWF7C6.js.map +0 -1
  88. package/dist/chunk-67YG3R5F.js.map +0 -1
  89. package/dist/chunk-74ZMAOVF.js.map +0 -1
  90. package/dist/chunk-CYC3ZI32.js.map +0 -1
  91. package/dist/chunk-G4HTI4ZM.js.map +0 -1
  92. package/dist/chunk-GI6W53LM.js.map +0 -1
  93. package/dist/chunk-HDQG3QYV.js.map +0 -1
  94. package/dist/chunk-JIBOJTGS.js.map +0 -1
  95. package/dist/chunk-P3QIO4ZU.js.map +0 -1
  96. package/dist/chunk-PMYWZRRS.js.map +0 -1
  97. package/dist/chunk-PXXVLX7K.js.map +0 -1
  98. package/dist/chunk-QDZ47G45.js.map +0 -1
  99. package/dist/chunk-RZEXHDCF.js.map +0 -1
  100. package/dist/chunk-WQE4OIZS.js +0 -6
  101. package/dist/fs-6JKZNQ3U.js.map +0 -1
  102. package/dist/interactive-HLGJJFLI.js.map +0 -1
  103. package/dist/load-4MLIHGXF.js +0 -9
  104. package/dist/runtime-session-5M6QZZOS.js +0 -19
  105. package/dist/stream-json-Y45JBNYP.js.map +0 -1
  106. package/dist/vg-mcp-bridge-ZCKBYQ2D.js.map +0 -1
  107. package/dist/vgd-TGGEAGQZ.js.map +0 -1
package/DOCS.md CHANGED
@@ -1306,6 +1306,8 @@ Most developers never need this directly — Vibgrate for VS Code and `vg code`
1306
1306
  vg daemon status
1307
1307
  vg daemon ensure
1308
1308
  vg daemon start
1309
+ vg daemon stop
1310
+ vg daemon restart
1309
1311
  vg daemon register
1310
1312
  vg daemon list
1311
1313
  vg daemon federation
@@ -1320,10 +1322,12 @@ vg daemon graphs
1320
1322
  | `status` | Whether the daemon is running and how many workspaces it tracks |
1321
1323
  | `start` | Run in the foreground (Ctrl-C to stop) |
1322
1324
  | `ensure` | Start in the background if not already running (idempotent; for hosts and agents) |
1325
+ | `stop` | Stop the running daemon (idempotent — succeeds if none is running) |
1326
+ | `restart` | Stop the daemon if running, then start it in the background (`vg update` does this automatically after installing a new version) |
1323
1327
  | `register [root]` | Register the current (or given) repository with a running daemon |
1324
1328
  | `list` | List registered workspaces |
1325
1329
  | `federation [root]` | Register a multi-root federation from `.vibgrate/federation.json` (or primary cwd) |
1326
- | `publish [root]` | Load the workspace code map into the daemon active graph (run `vg build` first) |
1330
+ | `publish [root]` | Load the workspace code map into the daemon active graph (run `vg build` first). The daemon reads the map from disk itself, binary snapshot first — nothing heavy crosses the socket |
1327
1331
  | `query <query...>` | Lexical/structural query against the active graph |
1328
1332
  | `impact <symbol>` | Blast radius for a symbol in the active graph |
1329
1333
  | `graphs` | List multi-branch graph slots currently resident |
@@ -0,0 +1,22 @@
1
+ export { CONTEXT_POLICY_VERSION, DISCOVERY_TOOLS, MUTATION_TOOLS, compact, runAgent } from './chunk-KGWEBM2J.js';
2
+ import './chunk-543E6WDJ.js';
3
+ import './chunk-MYM5VIJ4.js';
4
+ import './chunk-REIGWTTH.js';
5
+ import './chunk-RGEVBSLK.js';
6
+ import './chunk-RBIRXQR6.js';
7
+ import './chunk-5HYCB55P.js';
8
+ import './chunk-IPICO2RV.js';
9
+ import './chunk-FHJBGPJZ.js';
10
+ export { summarizeCapsule } from './chunk-US2DAKCM.js';
11
+ import './chunk-3LSG7Z4I.js';
12
+ import './chunk-J7LKEB2R.js';
13
+ import './chunk-EETK7S43.js';
14
+ import './chunk-UTNNGKKX.js';
15
+ import './chunk-RXP66R2E.js';
16
+ import './chunk-ZKNB4YH4.js';
17
+ import './chunk-BWPBB45M.js';
18
+ import './chunk-JFGYT6BI.js';
19
+ import './chunk-BPF3YX2U.js';
20
+ import './chunk-Z6ZWNWWR.js';
21
+ //# sourceMappingURL=agent-33BMKWJC.js.map
22
+ //# sourceMappingURL=agent-33BMKWJC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"agent-33BMKWJC.js"}
@@ -0,0 +1,9 @@
1
+ export { baselineCommand, runBaseline } from './chunk-6WNJJXFY.js';
2
+ import './chunk-ON7PZNYV.js';
3
+ import './chunk-CRAMOEBE.js';
4
+ import './chunk-RXP66R2E.js';
5
+ import './chunk-Q4UQ73NA.js';
6
+ import './chunk-IDAQV3NQ.js';
7
+ import './chunk-Z6ZWNWWR.js';
8
+ //# sourceMappingURL=baseline-V6MYDEMS.js.map
9
+ //# sourceMappingURL=baseline-V6MYDEMS.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"baseline-UOTGTJYT.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"baseline-V6MYDEMS.js"}
@@ -0,0 +1,56 @@
1
+ import './chunk-Z6ZWNWWR.js';
2
+
3
+ // src/code/checkpoint.ts
4
+ function checkpointRef(sessionId, seq) {
5
+ const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "") || "session";
6
+ return `refs/vibgrate/checkpoints/${safe}/${seq}`;
7
+ }
8
+ function isGitRepo(git) {
9
+ return git(["rev-parse", "--is-inside-work-tree"]).exitCode === 0;
10
+ }
11
+ function createCheckpoint(git, opts) {
12
+ if (!isGitRepo(git)) return null;
13
+ const env = { GIT_INDEX_FILE: opts.indexFile };
14
+ if (git(["add", "-A"], env).exitCode !== 0) return null;
15
+ const tree = git(["write-tree"], env);
16
+ if (tree.exitCode !== 0) return null;
17
+ const treeId = tree.stdout.trim();
18
+ if (!treeId) return null;
19
+ const head = git(["rev-parse", "HEAD"]);
20
+ const parent = head.exitCode === 0 && head.stdout.trim() ? ["-p", head.stdout.trim()] : [];
21
+ const message = opts.message ?? `vg code checkpoint ${opts.seq}`;
22
+ const commit = git(["commit-tree", treeId, ...parent, "-m", message], env);
23
+ if (commit.exitCode !== 0) return null;
24
+ const commitId = commit.stdout.trim();
25
+ if (!commitId) return null;
26
+ const ref = checkpointRef(opts.sessionId, opts.seq);
27
+ if (git(["update-ref", ref, commitId]).exitCode !== 0) return null;
28
+ return { ref, commit: commitId, seq: opts.seq, files: [...opts.files] };
29
+ }
30
+ function restoreCheckpoint(git, opts) {
31
+ const restored = [];
32
+ const removed = [];
33
+ const failed = [];
34
+ for (const file of opts.files) {
35
+ const probe = git(["cat-file", "-e", `${opts.commit}:${file}`]);
36
+ if (probe.exitCode === 0) {
37
+ const res = git(["checkout", opts.commit, "--", file]);
38
+ if (res.exitCode === 0) restored.push(file);
39
+ else failed.push(file);
40
+ continue;
41
+ }
42
+ const rm = git(["rm", "-f", "--ignore-unmatch", "--", file]);
43
+ if (rm.exitCode === 0) removed.push(file);
44
+ else failed.push(file);
45
+ }
46
+ return { restored, removed, failed };
47
+ }
48
+ function deleteCheckpoints(git, sessionId, upTo) {
49
+ for (let seq = 1; seq <= upTo; seq++) {
50
+ git(["update-ref", "-d", checkpointRef(sessionId, seq)]);
51
+ }
52
+ }
53
+
54
+ export { checkpointRef, createCheckpoint, deleteCheckpoints, isGitRepo, restoreCheckpoint };
55
+ //# sourceMappingURL=checkpoint-4WFZMKEO.js.map
56
+ //# sourceMappingURL=checkpoint-4WFZMKEO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/code/checkpoint.ts"],"names":[],"mappings":";;;AA4CO,SAAS,aAAA,CAAc,WAAmB,GAAA,EAAqB;AAEpE,EAAA,MAAM,IAAA,GAAO,SAAA,CAAU,OAAA,CAAQ,iBAAA,EAAmB,EAAE,CAAA,IAAK,SAAA;AACzD,EAAA,OAAO,CAAA,0BAAA,EAA6B,IAAI,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA;AACjD;AAGO,SAAS,UAAU,GAAA,EAAyB;AACjD,EAAA,OAAO,IAAI,CAAC,WAAA,EAAa,uBAAuB,CAAC,EAAE,QAAA,KAAa,CAAA;AAClE;AASO,SAAS,gBAAA,CACd,KACA,IAAA,EACmB;AACnB,EAAA,IAAI,CAAC,SAAA,CAAU,GAAG,CAAA,EAAG,OAAO,IAAA;AAC5B,EAAA,MAAM,GAAA,GAAM,EAAE,cAAA,EAAgB,IAAA,CAAK,SAAA,EAAU;AAG7C,EAAA,IAAI,GAAA,CAAI,CAAC,KAAA,EAAO,IAAI,GAAG,GAAG,CAAA,CAAE,QAAA,KAAa,CAAA,EAAG,OAAO,IAAA;AACnD,EAAA,MAAM,IAAA,GAAO,GAAA,CAAI,CAAC,YAAY,GAAG,GAAG,CAAA;AACpC,EAAA,IAAI,IAAA,CAAK,QAAA,KAAa,CAAA,EAAG,OAAO,IAAA;AAChC,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,MAAA,CAAO,IAAA,EAAK;AAChC,EAAA,IAAI,CAAC,QAAQ,OAAO,IAAA;AAIpB,EAAA,MAAM,IAAA,GAAO,GAAA,CAAI,CAAC,WAAA,EAAa,MAAM,CAAC,CAAA;AACtC,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,QAAA,KAAa,CAAA,IAAK,KAAK,MAAA,CAAO,IAAA,EAAK,GAAI,CAAC,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,EAAM,IAAI,EAAC;AACzF,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,OAAA,IAAW,CAAA,mBAAA,EAAsB,KAAK,GAAG,CAAA,CAAA;AAC9D,EAAA,MAAM,MAAA,GAAS,GAAA,CAAI,CAAC,aAAA,EAAe,MAAA,EAAQ,GAAG,MAAA,EAAQ,IAAA,EAAM,OAAO,CAAA,EAAG,GAAG,CAAA;AACzE,EAAA,IAAI,MAAA,CAAO,QAAA,KAAa,CAAA,EAAG,OAAO,IAAA;AAClC,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,MAAA,CAAO,IAAA,EAAK;AACpC,EAAA,IAAI,CAAC,UAAU,OAAO,IAAA;AAEtB,EAAA,MAAM,GAAA,GAAM,aAAA,CAAc,IAAA,CAAK,SAAA,EAAW,KAAK,GAAG,CAAA;AAClD,EAAA,IAAI,GAAA,CAAI,CAAC,YAAA,EAAc,GAAA,EAAK,QAAQ,CAAC,CAAA,CAAE,QAAA,KAAa,CAAA,EAAG,OAAO,IAAA;AAC9D,EAAA,OAAO,EAAE,GAAA,EAAK,MAAA,EAAQ,QAAA,EAAU,GAAA,EAAK,IAAA,CAAK,GAAA,EAAK,KAAA,EAAO,CAAC,GAAG,IAAA,CAAK,KAAK,CAAA,EAAE;AACxE;AAqBO,SAAS,iBAAA,CACd,KACA,IAAA,EACgB;AAChB,EAAA,MAAM,WAAqB,EAAC;AAC5B,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,MAAM,SAAmB,EAAC;AAE1B,EAAA,KAAA,MAAW,IAAA,IAAQ,KAAK,KAAA,EAAO;AAE7B,IAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,CAAC,UAAA,EAAY,IAAA,EAAM,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,CAAA,EAAI,IAAI,CAAA,CAAE,CAAC,CAAA;AAC9D,IAAA,IAAI,KAAA,CAAM,aAAa,CAAA,EAAG;AACxB,MAAA,MAAM,GAAA,GAAM,IAAI,CAAC,UAAA,EAAY,KAAK,MAAA,EAAQ,IAAA,EAAM,IAAI,CAAC,CAAA;AACrD,MAAA,IAAI,GAAA,CAAI,QAAA,KAAa,CAAA,EAAG,QAAA,CAAS,KAAK,IAAI,CAAA;AAAA,WACrC,MAAA,CAAO,KAAK,IAAI,CAAA;AACrB,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,EAAA,GAAK,IAAI,CAAC,IAAA,EAAM,MAAM,kBAAA,EAAoB,IAAA,EAAM,IAAI,CAAC,CAAA;AAC3D,IAAA,IAAI,EAAA,CAAG,QAAA,KAAa,CAAA,EAAG,OAAA,CAAQ,KAAK,IAAI,CAAA;AAAA,SACnC,MAAA,CAAO,KAAK,IAAI,CAAA;AAAA,EACvB;AACA,EAAA,OAAO,EAAE,QAAA,EAAU,OAAA,EAAS,MAAA,EAAO;AACrC;AAGO,SAAS,iBAAA,CAAkB,GAAA,EAAgB,SAAA,EAAmB,IAAA,EAAoB;AACvF,EAAA,KAAA,IAAS,GAAA,GAAM,CAAA,EAAG,GAAA,IAAO,IAAA,EAAM,GAAA,EAAA,EAAO;AACpC,IAAA,GAAA,CAAI,CAAC,YAAA,EAAc,IAAA,EAAM,cAAc,SAAA,EAAW,GAAG,CAAC,CAAC,CAAA;AAAA,EACzD;AACF","file":"checkpoint-4WFZMKEO.js","sourcesContent":["/**\n * Checkpoints for VG Code (VG-CLI-CODE §18.4).\n *\n * A checkpoint is a snapshot of the working tree taken when the user approves a\n * change, so an approved edit can be undone after the fact. Two design choices\n * matter:\n *\n * - **Per approval, not per tool call.** A snapshot costs a tree write; taking\n * one for every read or search makes large repositories crawl. Approvals are\n * the points a user would actually want to return to.\n * - **Out of the way.** Snapshots are commits under `refs/vibgrate/checkpoints/`,\n * written with a private index file. The user's own index, staged changes,\n * branch, and HEAD are never touched, and nothing appears in `git log`.\n * Ignored files stay ignored — a checkpoint captures the project, not\n * `node_modules`.\n *\n * A checkpoint id is also recorded in run provenance, so the audit record of a\n * run points at the exact tree the agent produced: an auditor can check out\n * what happened rather than take the record's word for it.\n *\n * Pure over an injected git runner, so every path is unit-tested without a\n * repository.\n */\n\n/** Result of running a git command. */\nexport interface GitResult {\n stdout: string;\n exitCode: number;\n}\n\n/** Runs a git command with an optional private index file. */\nexport type GitRunner = (args: string[], env?: Record<string, string>) => GitResult;\n\nexport interface Checkpoint {\n /** Full ref name, e.g. `refs/vibgrate/checkpoints/s7a/2`. */\n ref: string;\n /** Commit id the ref points at. */\n commit: string;\n /** Sequence within the session, starting at 1. */\n seq: number;\n /** Repo-relative files the approved change touched. */\n files: string[];\n}\n\nexport function checkpointRef(sessionId: string, seq: number): string {\n // Session ids are engine-generated (`s<base36>`); keep the ref path safe anyway.\n const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, '') || 'session';\n return `refs/vibgrate/checkpoints/${safe}/${seq}`;\n}\n\n/** True when the root is inside a git work tree. */\nexport function isGitRepo(git: GitRunner): boolean {\n return git(['rev-parse', '--is-inside-work-tree']).exitCode === 0;\n}\n\n/**\n * Snapshot the current working tree.\n *\n * Returns null when the repository isn't usable for snapshots (no git, or a\n * fresh repo with no objects yet) — a checkpoint is a convenience, and failing\n * to take one must never block an approved change from being applied.\n */\nexport function createCheckpoint(\n git: GitRunner,\n opts: { sessionId: string; seq: number; files: string[]; indexFile: string; message?: string },\n): Checkpoint | null {\n if (!isGitRepo(git)) return null;\n const env = { GIT_INDEX_FILE: opts.indexFile };\n\n // Stage everything into a *private* index: the user's staged work is untouched.\n if (git(['add', '-A'], env).exitCode !== 0) return null;\n const tree = git(['write-tree'], env);\n if (tree.exitCode !== 0) return null;\n const treeId = tree.stdout.trim();\n if (!treeId) return null;\n\n // Parent the snapshot on HEAD when there is one, so the commit is inspectable\n // with ordinary git tooling. A repo with no commits yet simply has no parent.\n const head = git(['rev-parse', 'HEAD']);\n const parent = head.exitCode === 0 && head.stdout.trim() ? ['-p', head.stdout.trim()] : [];\n const message = opts.message ?? `vg code checkpoint ${opts.seq}`;\n const commit = git(['commit-tree', treeId, ...parent, '-m', message], env);\n if (commit.exitCode !== 0) return null;\n const commitId = commit.stdout.trim();\n if (!commitId) return null;\n\n const ref = checkpointRef(opts.sessionId, opts.seq);\n if (git(['update-ref', ref, commitId]).exitCode !== 0) return null;\n return { ref, commit: commitId, seq: opts.seq, files: [...opts.files] };\n}\n\nexport interface RestoreOutcome {\n restored: string[];\n /** Files that did not exist in the checkpoint and were removed. */\n removed: string[];\n failed: string[];\n}\n\n/**\n * Restore files from a checkpoint.\n *\n * Scoped deliberately: only the paths given are touched, never the whole tree.\n * A checkpoint records which files a change wrote, so restoring undoes that\n * change without reverting unrelated work the user did in the meantime — the\n * behaviour someone expects from \"undo this edit\", and the safe default when\n * the alternative is silently discarding their own edits.\n *\n * A file that the checkpoint does not contain was created after it, so undoing\n * means deleting it.\n */\nexport function restoreCheckpoint(\n git: GitRunner,\n opts: { commit: string; files: string[] },\n): RestoreOutcome {\n const restored: string[] = [];\n const removed: string[] = [];\n const failed: string[] = [];\n\n for (const file of opts.files) {\n // Did this path exist in the snapshot?\n const probe = git(['cat-file', '-e', `${opts.commit}:${file}`]);\n if (probe.exitCode === 0) {\n const res = git(['checkout', opts.commit, '--', file]);\n if (res.exitCode === 0) restored.push(file);\n else failed.push(file);\n continue;\n }\n // Absent from the snapshot → it was created after; undo means removing it.\n const rm = git(['rm', '-f', '--ignore-unmatch', '--', file]);\n if (rm.exitCode === 0) removed.push(file);\n else failed.push(file);\n }\n return { restored, removed, failed };\n}\n\n/** Delete a session's checkpoint refs (e.g. when the session is discarded). */\nexport function deleteCheckpoints(git: GitRunner, sessionId: string, upTo: number): void {\n for (let seq = 1; seq <= upTo; seq++) {\n git(['update-ref', '-d', checkpointRef(sessionId, seq)]);\n }\n}\n"]}
@@ -0,0 +1,61 @@
1
+ // src/code/secrets.ts
2
+ var SECRET_FILE = new RegExp(
3
+ [
4
+ String.raw`(^|/)\.env(\.[\w.-]+)?$`,
5
+ // .env, .env.local, .env.production
6
+ String.raw`(^|/)\.npmrc$`,
7
+ String.raw`(^|/)\.netrc$`,
8
+ String.raw`(^|/)\.pgpass$`,
9
+ String.raw`(^|/)\.git-credentials$`,
10
+ String.raw`(^|/)id_(rsa|dsa|ecdsa|ed25519)$`,
11
+ String.raw`\.pem$`,
12
+ String.raw`\.key$`,
13
+ String.raw`\.p12$`,
14
+ String.raw`\.pfx$`,
15
+ String.raw`(^|/)credentials(\.json)?$`,
16
+ // aws credentials, gcloud
17
+ String.raw`(^|/)\.aws/credentials$`,
18
+ String.raw`(^|/)secrets?\.(ya?ml|json|toml|env)$`
19
+ ].join("|"),
20
+ "i"
21
+ );
22
+ function isSecretPath(file) {
23
+ const normalized = file.replace(/\\/g, "/");
24
+ return SECRET_FILE.test(normalized);
25
+ }
26
+ function secretRefusal(file) {
27
+ return `refusing to read ${file} \u2014 it looks like a secrets/credentials file, and this content would be sent to the model. Reference the values by name instead, or read a non-secret file.`;
28
+ }
29
+ var TOKEN_SHAPES = /\b(sk|pk|rk|xoxb|xoxp|ghp|gho|ghs|ghu|glpat|AKIA|ASIA|AIza|ya29|hf|nvapi|sk-ant|sk-or)[-_][A-Za-z0-9\-_./+]{8,}/g;
30
+ var BEARER = /\b(Authorization|Bearer|token|api[_-]?key)\b\s*[:=]\s*['"]?[A-Za-z0-9\-_./+]{12,}['"]?/gi;
31
+ var ASSIGNMENT = /\b([A-Za-z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|PWD|CREDENTIAL|PRIVATE|ACCESS|SESSION)[A-Za-z0-9_]*)\s*([:=])\s*['"]?([^\s'";,]{6,})/gi;
32
+ var URL_AUTH = /\b([a-z][a-z0-9+.-]*:\/\/)([^:/\s]+):([^@/\s]+)@/gi;
33
+ function redactText(text) {
34
+ return text.replace(URL_AUTH, "$1$2:***redacted***@").replace(TOKEN_SHAPES, (_m, prefix) => `${prefix}-***redacted***`).replace(BEARER, (_m, k) => `${k}: ***redacted***`).replace(ASSIGNMENT, (_m, name, op) => `${name}${op}***redacted***`);
35
+ }
36
+ function findSecretShapes(text) {
37
+ const hits = [];
38
+ const scan = (re, kind) => {
39
+ re.lastIndex = 0;
40
+ let m;
41
+ while ((m = re.exec(text)) !== null) {
42
+ hits.push({ kind, index: m.index });
43
+ if (m[0].length === 0) re.lastIndex++;
44
+ }
45
+ };
46
+ scan(TOKEN_SHAPES, "token");
47
+ scan(BEARER, "bearer");
48
+ scan(ASSIGNMENT, "assignment");
49
+ scan(URL_AUTH, "url-auth");
50
+ return hits.sort((a, b) => a.index - b.index || a.kind.localeCompare(b.kind));
51
+ }
52
+ function secretEgressRefusal(text, surface = "network payload") {
53
+ const hits = findSecretShapes(text);
54
+ if (hits.length === 0) return null;
55
+ const kinds = [...new Set(hits.map((h) => h.kind))].sort().join(", ");
56
+ return `refusing to send ${surface} \u2014 detected credential-shaped content (${kinds}). Remove secrets before any network call; path-level secrets files are also blocked.`;
57
+ }
58
+
59
+ export { isSecretPath, redactText, secretEgressRefusal, secretRefusal };
60
+ //# sourceMappingURL=chunk-543E6WDJ.js.map
61
+ //# sourceMappingURL=chunk-543E6WDJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/code/secrets.ts"],"names":[],"mappings":"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","file":"chunk-543E6WDJ.js","sourcesContent":["/**\n * Secret-safe file access for the VG Code agent (VG-CLI-CODE §13, GUARDRAILS §1.1).\n *\n * The agent reads files and hands them to a model — which, for a hosted model,\n * means they leave the machine. So a coding agent that will happily open `.env`\n * or a private key and paste it into a prompt is a credential-egress bug. Two\n * defenses, applied at the boundary (before anything reaches the model):\n *\n * 1. **Refuse** to read files that exist only to hold secrets (`.env`, key\n * material, `.npmrc`, `.netrc`, cloud credentials…).\n * 2. **Redact** credential shapes from the content of any file we *do* read —\n * token prefixes, `Authorization` headers, and `NAME_KEY=value` style\n * assignments — so a stray secret in an otherwise-normal file is masked.\n *\n * Pure and unit-tested; no I/O here.\n */\n\n/** Files whose whole purpose is secrets — never read them into a prompt. */\nconst SECRET_FILE = new RegExp(\n [\n String.raw`(^|/)\\.env(\\.[\\w.-]+)?$`, // .env, .env.local, .env.production\n String.raw`(^|/)\\.npmrc$`,\n String.raw`(^|/)\\.netrc$`,\n String.raw`(^|/)\\.pgpass$`,\n String.raw`(^|/)\\.git-credentials$`,\n String.raw`(^|/)id_(rsa|dsa|ecdsa|ed25519)$`,\n String.raw`\\.pem$`,\n String.raw`\\.key$`,\n String.raw`\\.p12$`,\n String.raw`\\.pfx$`,\n String.raw`(^|/)credentials(\\.json)?$`, // aws credentials, gcloud\n String.raw`(^|/)\\.aws/credentials$`,\n String.raw`(^|/)secrets?\\.(ya?ml|json|toml|env)$`,\n ].join('|'),\n 'i',\n);\n\n/** Whether a path should never be read into a model prompt. */\nexport function isSecretPath(file: string): boolean {\n const normalized = file.replace(/\\\\/g, '/');\n return SECRET_FILE.test(normalized);\n}\n\n/** A one-line, actionable refusal message for a blocked read. */\nexport function secretRefusal(file: string): string {\n return `refusing to read ${file} — it looks like a secrets/credentials file, and this content would be sent to the model. Reference the values by name instead, or read a non-secret file.`;\n}\n\n// Known credential token prefixes (same family the ingest redaction guards).\nconst TOKEN_SHAPES = /\\b(sk|pk|rk|xoxb|xoxp|ghp|gho|ghs|ghu|glpat|AKIA|ASIA|AIza|ya29|hf|nvapi|sk-ant|sk-or)[-_][A-Za-z0-9\\-_./+]{8,}/g;\nconst BEARER = /\\b(Authorization|Bearer|token|api[_-]?key)\\b\\s*[:=]\\s*['\"]?[A-Za-z0-9\\-_./+]{12,}['\"]?/gi;\n// `NAME_KEY=secret`, `PASSWORD: \"…\"`, connection strings — mask the value only.\nconst ASSIGNMENT = /\\b([A-Za-z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|PWD|CREDENTIAL|PRIVATE|ACCESS|SESSION)[A-Za-z0-9_]*)\\s*([:=])\\s*['\"]?([^\\s'\";,]{6,})/gi;\nconst URL_AUTH = /\\b([a-z][a-z0-9+.-]*:\\/\\/)([^:/\\s]+):([^@/\\s]+)@/gi;\n\n/**\n * Redact credential shapes from text before it reaches the model. Masks the\n * secret value, never the surrounding structure, so the model still sees that a\n * key exists (and its name) without seeing the value.\n */\nexport function redactText(text: string): string {\n return text\n .replace(URL_AUTH, '$1$2:***redacted***@')\n .replace(TOKEN_SHAPES, (_m, prefix) => `${prefix}-***redacted***`)\n .replace(BEARER, (_m, k) => `${k}: ***redacted***`)\n .replace(ASSIGNMENT, (_m, name, op) => `${name}${op}***redacted***`);\n}\n\nexport type SecretShapeKind = 'token' | 'bearer' | 'assignment' | 'url-auth';\n\nexport interface SecretShapeHit {\n kind: SecretShapeKind;\n /** Character index of the match in the original text. */\n index: number;\n}\n\n/**\n * Locate credential shapes without redacting. Used to refuse network egress\n * (and similar) when a payload still contains secrets — defense-in-depth on\n * top of path refusal + redaction.\n */\nexport function findSecretShapes(text: string): SecretShapeHit[] {\n const hits: SecretShapeHit[] = [];\n const scan = (re: RegExp, kind: SecretShapeKind): void => {\n re.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = re.exec(text)) !== null) {\n hits.push({ kind, index: m.index });\n if (m[0].length === 0) re.lastIndex++;\n }\n };\n scan(TOKEN_SHAPES, 'token');\n scan(BEARER, 'bearer');\n scan(ASSIGNMENT, 'assignment');\n scan(URL_AUTH, 'url-auth');\n return hits.sort((a, b) => a.index - b.index || a.kind.localeCompare(b.kind));\n}\n\n/** True when {@link findSecretShapes} finds at least one credential shape. */\nexport function containsSecretShapes(text: string): boolean {\n return findSecretShapes(text).length > 0;\n}\n\n/**\n * Refuse network-bound payloads that still carry secret shapes after redaction\n * would be expected. Returns an actionable reason, or null when safe.\n */\nexport function secretEgressRefusal(text: string, surface = 'network payload'): string | null {\n const hits = findSecretShapes(text);\n if (hits.length === 0) return null;\n const kinds = [...new Set(hits.map((h) => h.kind))].sort().join(', ');\n return (\n `refusing to send ${surface} — detected credential-shaped content (${kinds}). ` +\n `Remove secrets before any network call; path-level secrets files are also blocked.`\n );\n}\n"]}
@@ -1,12 +1,11 @@
1
- import { VERSION } from './chunk-WQE4OIZS.js';
2
- import { parseGraph, stableStringify, serializeGraph, cacheDir } from './chunk-ZKNB4YH4.js';
1
+ import { VERSION } from './chunk-Q4UQ73NA.js';
2
+ import { parseGraph, stableStringify, serializeGraph } from './chunk-ZKNB4YH4.js';
3
3
  import { globalGraphPathForRef, globalGraphPath, repositoryStoreDir } from './chunk-BWPBB45M.js';
4
4
  import { detectGitRef } from './chunk-BPF3YX2U.js';
5
- import { __require } from './chunk-Z6ZWNWWR.js';
6
5
  import * as fs2 from 'fs';
7
- import * as path from 'path';
8
6
  import * as zlib from 'zlib';
9
7
  import { Packr } from 'msgpackr';
8
+ import * as path from 'path';
10
9
 
11
10
  var MAGIC = Buffer.from("VGSNAP1\0", "latin1");
12
11
  var crc322 = zlib.crc32 ?? jsCrc32;
@@ -291,8 +290,6 @@ ${embedded}
291
290
  function esc(s) {
292
291
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
293
292
  }
294
-
295
- // src/engine/artifacts.ts
296
293
  function vibgrateDir(root) {
297
294
  return path.join(root, ".vibgrate");
298
295
  }
@@ -339,7 +336,14 @@ var DEFAULT_GITIGNORE = [
339
336
  "graph.html",
340
337
  "GRAPH_REPORT.md",
341
338
  "facts.jsonl",
342
- "mcp-navigation.json"
339
+ "mcp-navigation.json",
340
+ // Machine-local telemetry the LSP/`vg code` accrue over time — never meant
341
+ // for git: score-history.jsonl is per-machine trend data (lsp/score-history.ts),
342
+ // run-outcomes.jsonl is per-machine agent-run telemetry (code/run-outcome.ts).
343
+ // Neither is a snapshot artifact like scan_result.json/baseline.json, which
344
+ // are intentionally left out of this list for a user to commit if they choose.
345
+ "score-history.jsonl",
346
+ "run-outcomes.jsonl"
343
347
  ];
344
348
  function ensureVibgrateGitignore(root) {
345
349
  const file = path.join(vibgrateDir(root), ".gitignore");
@@ -409,200 +413,6 @@ function isPathInside(child, parent) {
409
413
  return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
410
414
  }
411
415
 
412
- // src/schema.ts
413
- var SCHEMA_VERSION = "vg-graph/1.0";
414
-
415
- // src/engine/index-db.ts
416
- function indexDbPath(root) {
417
- return path.join(cacheDir(root), "graph.db");
418
- }
419
- function writeGraphIndex(root, graph) {
420
- const file = indexDbPath(root);
421
- try {
422
- const { DatabaseSync } = __require("sqlite");
423
- fs2.mkdirSync(path.dirname(file), { recursive: true });
424
- const tmp = `${file}.${process.pid}.tmp`;
425
- try {
426
- if (fs2.existsSync(tmp)) fs2.unlinkSync(tmp);
427
- } catch {
428
- }
429
- const db = new DatabaseSync(tmp);
430
- db.exec(`
431
- PRAGMA journal_mode = OFF;
432
- PRAGMA synchronous = OFF;
433
- CREATE TABLE meta (
434
- key TEXT PRIMARY KEY,
435
- value TEXT NOT NULL
436
- );
437
- CREATE TABLE nodes (
438
- id TEXT PRIMARY KEY,
439
- name TEXT NOT NULL,
440
- qualified_name TEXT NOT NULL,
441
- kind TEXT NOT NULL,
442
- file TEXT NOT NULL,
443
- line INTEGER NOT NULL,
444
- importance REAL NOT NULL,
445
- area INTEGER NOT NULL,
446
- is_hub INTEGER NOT NULL,
447
- lang TEXT NOT NULL,
448
- json TEXT NOT NULL
449
- );
450
- CREATE TABLE edges (
451
- id TEXT PRIMARY KEY,
452
- kind TEXT NOT NULL,
453
- src TEXT NOT NULL,
454
- dst TEXT NOT NULL,
455
- resolution TEXT NOT NULL,
456
- confidence REAL NOT NULL,
457
- epistemic TEXT NOT NULL,
458
- json TEXT NOT NULL
459
- );
460
- CREATE TABLE areas (
461
- id INTEGER PRIMARY KEY,
462
- json TEXT NOT NULL
463
- );
464
- CREATE INDEX idx_nodes_name ON nodes(name);
465
- CREATE INDEX idx_nodes_qn ON nodes(qualified_name);
466
- CREATE INDEX idx_nodes_file ON nodes(file);
467
- CREATE INDEX idx_edges_src ON edges(src);
468
- CREATE INDEX idx_edges_dst ON edges(dst);
469
- CREATE INDEX idx_edges_kind ON edges(kind);
470
- `);
471
- const insMeta = db.prepare("INSERT INTO meta(key, value) VALUES (?, ?)");
472
- insMeta.run("corpusHash", graph.provenance.corpusHash);
473
- insMeta.run("schemaVersion", graph.schemaVersion);
474
- insMeta.run("generatedAt", graph.generatedAt);
475
- insMeta.run("nodeCount", String(graph.nodes.length));
476
- insMeta.run("edgeCount", String(graph.edges.length));
477
- insMeta.run("analysisTier", graph.meta.analysisTier ?? "full");
478
- insMeta.run("indexVersion", "vg-graph-index/2");
479
- insMeta.run("graphMeta", JSON.stringify(graph.meta));
480
- insMeta.run("graphProvenance", JSON.stringify(graph.provenance));
481
- if (graph.facts) insMeta.run("factsJson", JSON.stringify(graph.facts));
482
- if (graph.grounding) insMeta.run("groundingJson", JSON.stringify(graph.grounding));
483
- if (graph.summaries) insMeta.run("summariesJson", JSON.stringify(graph.summaries));
484
- if (graph.unknowns) insMeta.run("unknownsJson", JSON.stringify(graph.unknowns));
485
- const insNode = db.prepare(
486
- `INSERT INTO nodes(id, name, qualified_name, kind, file, line, importance, area, is_hub, lang, json)
487
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
488
- );
489
- for (const n of graph.nodes) {
490
- insNode.run(
491
- n.id,
492
- n.name,
493
- n.qualifiedName,
494
- n.kind,
495
- n.file,
496
- n.span.start,
497
- n.importance,
498
- n.area,
499
- n.isHub ? 1 : 0,
500
- n.lang,
501
- JSON.stringify(n)
502
- );
503
- }
504
- const insEdge = db.prepare(
505
- `INSERT INTO edges(id, kind, src, dst, resolution, confidence, epistemic, json)
506
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
507
- );
508
- for (const e of graph.edges) {
509
- insEdge.run(
510
- e.id,
511
- e.kind,
512
- e.src,
513
- e.dst,
514
- e.resolution,
515
- e.confidence,
516
- e.epistemic ?? "observed",
517
- JSON.stringify(e)
518
- );
519
- }
520
- const insArea = db.prepare("INSERT INTO areas(id, json) VALUES (?, ?)");
521
- for (const a of graph.areas) {
522
- insArea.run(a.id, JSON.stringify(a));
523
- }
524
- db.close();
525
- fs2.renameSync(tmp, file);
526
- return { path: file, nodes: graph.nodes.length, edges: graph.edges.length, ok: true };
527
- } catch (err) {
528
- return {
529
- path: file,
530
- nodes: 0,
531
- edges: 0,
532
- ok: false,
533
- reason: err.message
534
- };
535
- }
536
- }
537
- function loadGraphFromIndex(root) {
538
- const file = indexDbPath(root);
539
- if (!fs2.existsSync(file)) return null;
540
- try {
541
- const { DatabaseSync } = __require("sqlite");
542
- const db = new DatabaseSync(file, { readOnly: true });
543
- const metaRows = db.prepare("SELECT key, value FROM meta").all();
544
- const meta = {};
545
- for (const r of metaRows) meta[r.key] = r.value;
546
- if (meta.indexVersion !== "vg-graph-index/2" || !meta.graphMeta || !meta.graphProvenance) {
547
- db.close();
548
- return null;
549
- }
550
- const nodeRows = db.prepare("SELECT json FROM nodes ORDER BY id").all();
551
- const edgeRows = db.prepare("SELECT json FROM edges ORDER BY id").all();
552
- const areaRows = db.prepare("SELECT json FROM areas ORDER BY id").all();
553
- db.close();
554
- const nodes = nodeRows.map((r) => JSON.parse(r.json));
555
- const edges = edgeRows.map((r) => JSON.parse(r.json));
556
- const areas = areaRows.map((r) => JSON.parse(r.json));
557
- nodes.sort((a, b) => a.id.localeCompare(b.id));
558
- edges.sort(
559
- (a, b) => a.kind.localeCompare(b.kind) || a.src.localeCompare(b.src) || a.dst.localeCompare(b.dst)
560
- );
561
- areas.sort((a, b) => a.id - b.id);
562
- const graph = {
563
- schemaVersion: meta.schemaVersion || SCHEMA_VERSION,
564
- generatedAt: meta.generatedAt ?? (/* @__PURE__ */ new Date(0)).toISOString(),
565
- provenance: JSON.parse(meta.graphProvenance),
566
- meta: JSON.parse(meta.graphMeta),
567
- nodes,
568
- edges,
569
- areas
570
- };
571
- if (meta.factsJson) graph.facts = JSON.parse(meta.factsJson);
572
- if (meta.groundingJson) graph.grounding = JSON.parse(meta.groundingJson);
573
- if (meta.summariesJson) graph.summaries = JSON.parse(meta.summariesJson);
574
- if (meta.unknownsJson) graph.unknowns = JSON.parse(meta.unknownsJson);
575
- for (const e of edges) {
576
- if (!e.resolution) e.resolution = "heuristic";
577
- if (!e.epistemic) e.epistemic = "observed";
578
- }
579
- return graph;
580
- } catch {
581
- return null;
582
- }
583
- }
584
- function loadGraphPreferIndex(root, graphJsonPath) {
585
- const jsonGraph = loadGraphFileWithSnapshot(graphJsonPath);
586
- const fromIndex = loadGraphFromIndex(root);
587
- if (fromIndex) {
588
- if (!jsonGraph || fromIndex.provenance.corpusHash === jsonGraph.provenance.corpusHash) {
589
- return { graph: fromIndex, source: "index" };
590
- }
591
- }
592
- if (jsonGraph) return { graph: jsonGraph, source: "json" };
593
- return null;
594
- }
595
-
596
- // src/engine/load.ts
597
- function loadGraph(root, graphPath) {
598
- const file = resolveGraphPath(root, graphPath);
599
- if (!graphPath) {
600
- const preferred = loadGraphPreferIndex(root, file);
601
- if (preferred) return preferred.graph;
602
- }
603
- return loadGraphFileWithSnapshot(file);
604
- }
605
-
606
- export { SCHEMA_VERSION, defaultGraphPath, ensureVibgrateGitignore, legacyGraphPath, loadGraph, loadGraphFileWithSnapshot, loadGraphPreferIndex, mapFileExists, mapFileStat, preferInRepoGraph, renderHtml, renderReport, resolveGraphPath, vibgrateDir, writeArtifacts, writeGraphIndex };
607
- //# sourceMappingURL=chunk-P3QIO4ZU.js.map
608
- //# sourceMappingURL=chunk-P3QIO4ZU.js.map
416
+ export { defaultGraphPath, ensureVibgrateGitignore, legacyGraphPath, loadGraphFileWithSnapshot, mapFileExists, mapFileStat, preferInRepoGraph, renderHtml, renderReport, resolveGraphPath, vibgrateDir, writeArtifacts };
417
+ //# sourceMappingURL=chunk-5G7C6H2G.js.map
418
+ //# sourceMappingURL=chunk-5G7C6H2G.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/engine/snapshot.ts","../src/engine/report.ts","../src/engine/html.ts","../src/engine/artifacts.ts"],"names":["crc32","fs","legacy"],"mappings":"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","file":"chunk-5G7C6H2G.js","sourcesContent":["import * as fs from 'node:fs';\nimport * as zlib from 'node:zlib';\nimport { Packr } from 'msgpackr';\nimport { parseGraph } from './serialize.js';\nimport { VERSION } from '../version.js';\nimport type { VgGraph } from '../schema.js';\n\n/**\n * Binary graph snapshot — a derived, local **cache** beside `graph.json`.\n *\n * `graph.json` stays the canonical, committed, human-diffable artifact and the\n * only interchange format (`vg export` / `share` / `bundle` are unchanged); the\n * determinism contract attaches to it, not to this file. The snapshot exists\n * purely so hot paths (`vg ask`/`serve`/`code`, vgd session attach) skip the\n * large-string `JSON.parse` on load and the pretty-print on write — measured\n * ~2.6× faster cold load and ~4× faster encode on XL maps, at ~40% of the size.\n *\n * Robustness rules (each one keeps JSON as the unquestioned fallback):\n * - The loader trusts a snapshot only when its recorded (size, mtimeMs) of\n * the source `graph.json` still match — any rebuild or hand-edit of the\n * JSON silently invalidates it. Staleness never needs to parse the JSON.\n * - A CRC-32 over the payload rejects torn/corrupted files; any decode error\n * (unknown magic, short file, bad msgpack, version skew) returns null\n * rather than throwing, and the caller re-reads JSON and rewrites the\n * snapshot (\"self-healing\" — first load after upgrade migrates for free).\n * - Writes are atomic (temp + rename) and best-effort: a full disk or\n * read-only store must never fail a build or a query.\n * - The engine VERSION is recorded and must match: the msgpack record layout\n * tracks the in-memory graph shape, which may change between releases.\n *\n * Format: MAGIC (\"VGSNAP1\\0\") · u32LE header length · msgpack header\n * {engine, sourceSize, sourceMtimeMs, crc32} · msgpack graph payload.\n * Everything is explicit little-endian via DataView/Buffer — no platform\n * variance. msgpackr runs its pure-JS path (its native accelerator is an\n * optional dependency we do not rely on); a fresh Packr per encode/decode\n * keeps record-id assignment deterministic for identical input.\n */\n\nconst MAGIC = Buffer.from('VGSNAP1\\0', 'latin1');\n\n/** node:zlib gained crc32 in 22.2; fall back to a table crc for 22.0/22.1. */\nconst crc32: (data: Uint8Array) => number = zlib.crc32 ?? jsCrc32;\n\nlet crcTable: Uint32Array | null = null;\n\nfunction jsCrc32(data: Uint8Array): number {\n if (!crcTable) {\n crcTable = new Uint32Array(256);\n for (let n = 0; n < 256; n++) {\n let c = n;\n for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n crcTable[n] = c >>> 0;\n }\n }\n let crc = 0xffffffff;\n for (let i = 0; i < data.length; i++) crc = crcTable[(crc ^ data[i]) & 0xff] ^ (crc >>> 8);\n return (crc ^ 0xffffffff) >>> 0;\n}\n\ninterface SnapshotHeader {\n engine: string;\n crc32: number;\n /**\n * True when the snapshot IS the map (global-store mode, no graph.json\n * beside it). False/absent = sidecar of a canonical graph.json, valid only\n * while that JSON's recorded stat still matches.\n */\n standalone?: boolean;\n sourceSize?: number;\n sourceMtimeMs?: number;\n}\n\n/** Sidecar path for a map file: `…/graph.json` → `…/graph.snap`. */\nexport function snapshotPathFor(graphPath: string): string {\n return graphPath.endsWith('.json') ? `${graphPath.slice(0, -5)}.snap` : `${graphPath}.snap`;\n}\n\nfunction newPackr(): Packr {\n return new Packr({ useRecords: true });\n}\n\n/** Msgpack payload bytes for a graph (exported for the determinism test). */\nexport function encodeGraphPayload(graph: VgGraph): Buffer {\n return Buffer.from(newPackr().pack(graph));\n}\n\n/**\n * Write the snapshot beside `graphPath`.\n *\n * Sidecar mode (default): call after `graph.json` is on disk — the header\n * records its stat so readers can cheaply detect staleness.\n * `standalone: true` (global-store mode): the snapshot IS the map; no\n * `graph.json` is required or consulted.\n * Best-effort: returns false (never throws) when the file can't be written.\n */\nexport function writeGraphSnapshot(\n graphPath: string,\n graph: VgGraph,\n opts: { standalone?: boolean } = {},\n): boolean {\n try {\n const payload = encodeGraphPayload(graph);\n let header: Buffer;\n if (opts.standalone) {\n header = newPackr().pack({\n engine: VERSION,\n crc32: crc32(payload),\n standalone: true,\n } satisfies SnapshotHeader);\n } else {\n const stat = fs.statSync(graphPath);\n header = newPackr().pack({\n engine: VERSION,\n sourceSize: stat.size,\n sourceMtimeMs: stat.mtimeMs,\n crc32: crc32(payload),\n } satisfies SnapshotHeader);\n }\n const lenBuf = Buffer.alloc(4);\n lenBuf.writeUInt32LE(header.length, 0);\n const file = snapshotPathFor(graphPath);\n const tmp = `${file}.${process.pid}.tmp`;\n try {\n fs.writeFileSync(tmp, Buffer.concat([MAGIC, lenBuf, header, payload]));\n fs.renameSync(tmp, file);\n } catch (err) {\n fs.rmSync(tmp, { force: true });\n throw err;\n }\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Load the snapshot for `graphPath`, or null when it is absent, stale,\n * or corrupted.\n *\n * Sidecar snapshots additionally require their source `graph.json` to exist\n * with an unchanged stat AND an identical engine version — the JSON fallback\n * is free, so any doubt invalidates. Standalone snapshots are the map itself:\n * they are accepted on magic + CRC + a structural sanity check, including\n * across engine versions (msgpack embeds its record layout, so an older\n * snapshot decodes the same way an older committed graph.json would parse).\n */\nexport function readGraphSnapshot(graphPath: string): VgGraph | null {\n try {\n const file = snapshotPathFor(graphPath);\n if (!fs.existsSync(file)) return null;\n const raw = fs.readFileSync(file);\n if (raw.length < MAGIC.length + 4 || !raw.subarray(0, MAGIC.length).equals(MAGIC)) return null;\n const headerLen = raw.readUInt32LE(MAGIC.length);\n const payloadStart = MAGIC.length + 4 + headerLen;\n if (payloadStart > raw.length) return null;\n const header = newPackr().unpack(raw.subarray(MAGIC.length + 4, payloadStart)) as SnapshotHeader;\n if (!header.standalone) {\n if (header.engine !== VERSION) return null;\n const sourceStat = fs.statSync(graphPath); // no source JSON → nothing to be a cache of\n if (header.sourceSize !== sourceStat.size || header.sourceMtimeMs !== sourceStat.mtimeMs) return null;\n }\n const payload = raw.subarray(payloadStart);\n if (crc32(payload) !== header.crc32) return null;\n const graph = newPackr().unpack(payload) as VgGraph;\n if (!graph || !Array.isArray(graph.nodes) || !Array.isArray(graph.edges)) return null;\n return graph;\n } catch {\n return null;\n }\n}\n\n/**\n * Does a map exist at `graphPath` in either representation — the canonical\n * `graph.json`, or a standalone binary snapshot (global-store mode)?\n * Cheap: reads only the snapshot header, never the payload or the JSON.\n */\nexport function mapFileExists(graphPath: string): boolean {\n if (fs.existsSync(graphPath)) return true;\n return readSnapshotHeader(snapshotPathFor(graphPath))?.standalone === true;\n}\n\n/**\n * Stat for \"did the map change on disk\" mtime tracking: the JSON when\n * present, else the standalone snapshot file. Throws when neither exists —\n * same contract callers had with `fs.statSync(graphPath)`.\n */\nexport function mapFileStat(graphPath: string): fs.Stats {\n try {\n return fs.statSync(graphPath);\n } catch (err) {\n const snap = snapshotPathFor(graphPath);\n if (fs.existsSync(snap)) return fs.statSync(snap);\n throw err;\n }\n}\n\n/** Parse just the envelope header of a snapshot file (null on any problem). */\nfunction readSnapshotHeader(file: string): SnapshotHeader | null {\n let fd: number | null = null;\n try {\n fd = fs.openSync(file, 'r');\n const prefix = Buffer.alloc(4096);\n const read = fs.readSync(fd, prefix, 0, prefix.length, 0);\n if (read < MAGIC.length + 4 || !prefix.subarray(0, MAGIC.length).equals(MAGIC)) return null;\n const headerLen = prefix.readUInt32LE(MAGIC.length);\n if (MAGIC.length + 4 + headerLen > read) return null;\n return newPackr().unpack(prefix.subarray(MAGIC.length + 4, MAGIC.length + 4 + headerLen)) as SnapshotHeader;\n } catch {\n return null;\n } finally {\n if (fd !== null) fs.closeSync(fd);\n }\n}\n\n/**\n * Load a map file, preferring the binary snapshot and self-healing it.\n * Fast path: valid snapshot (sidecar or standalone) → decode it. Fallback:\n * parse `graph.json`, then (best-effort) rewrite the sidecar so the next load\n * takes the fast path. Returns null only when neither representation yields a\n * graph — exactly the cases the pre-snapshot code treated as \"no graph\".\n */\nexport function loadGraphFileWithSnapshot(graphPath: string): VgGraph | null {\n const snap = readGraphSnapshot(graphPath);\n if (snap) return snap;\n let json: string;\n try {\n json = fs.readFileSync(graphPath, 'utf8');\n } catch {\n return null;\n }\n let graph: VgGraph;\n try {\n graph = parseGraph(json);\n } catch {\n return null;\n }\n writeGraphSnapshot(graphPath, graph);\n return graph;\n}\n","import type { VgGraph } from '../schema.js';\n\n/**\n * Deterministic Markdown summary (`GRAPH_REPORT.md`). Given a pinned\n * `generatedAt`, the report is byte-stable. Numbers are derived purely from the\n * graph, so the report can be regenerated from a committed `graph.json`.\n */\nexport function renderReport(graph: VgGraph): string {\n const { meta, provenance, nodes, edges } = graph;\n const lines: string[] = [];\n\n lines.push('# Code Graph Report');\n lines.push('');\n lines.push(`Generated by \\`vg\\` ${provenance.version} · schema \\`${graph.schemaVersion}\\``);\n lines.push('');\n lines.push('## Summary');\n lines.push('');\n lines.push(`- **Nodes:** ${meta.counts.nodes.toLocaleString('en-US')}`);\n lines.push(`- **Edges:** ${meta.counts.edges.toLocaleString('en-US')}`);\n lines.push(`- **Areas:** ${meta.counts.areas.toLocaleString('en-US')}`);\n lines.push(`- **Languages:** ${meta.languages.join(', ') || '—'}`);\n lines.push(`- **Clustering:** ${meta.cluster}`);\n lines.push(`- **Resolvers:** ${provenance.resolver.join(', ')}`);\n lines.push(`- **Edge kinds:** ${meta.edgeKinds.join(', ') || '—'}`);\n lines.push('');\n\n // Files by definition count.\n const fileNodes = nodes.filter((n) => n.kind === 'file');\n const defsByFile = new Map<string, number>();\n for (const n of nodes) {\n if (n.kind === 'file' || n.kind === 'external') continue;\n defsByFile.set(n.file, (defsByFile.get(n.file) ?? 0) + 1);\n }\n const topFiles = [...defsByFile.entries()]\n .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n .slice(0, 15);\n if (topFiles.length) {\n lines.push('## Largest files (by definitions)');\n lines.push('');\n lines.push('| File | Definitions |');\n lines.push('| --- | ---: |');\n for (const [file, count] of topFiles) lines.push(`| \\`${file}\\` | ${count} |`);\n lines.push('');\n }\n\n // Most-connected nodes (Phase 0 importance = normalised degree).\n const topNodes = nodes\n .filter((n) => n.kind !== 'file' && n.kind !== 'external')\n .sort((a, b) => b.importance - a.importance || a.qualifiedName.localeCompare(b.qualifiedName))\n .slice(0, 15);\n if (topNodes.length) {\n lines.push('## Most-connected definitions');\n lines.push('');\n lines.push('| Symbol | Kind | File | Importance |');\n lines.push('| --- | --- | --- | ---: |');\n for (const n of topNodes) {\n lines.push(\n `| \\`${n.qualifiedName}\\` | ${n.kind} | \\`${n.file}\\`:${n.span.start} | ${n.importance.toFixed(3)} |`,\n );\n }\n lines.push('');\n }\n\n // Edge-kind breakdown.\n const byKind = new Map<string, number>();\n for (const e of edges) byKind.set(e.kind, (byKind.get(e.kind) ?? 0) + 1);\n if (byKind.size) {\n lines.push('## Edges by kind');\n lines.push('');\n lines.push('| Kind | Count |');\n lines.push('| --- | ---: |');\n for (const [kind, count] of [...byKind.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {\n lines.push(`| ${kind} | ${count} |`);\n }\n lines.push('');\n }\n\n lines.push('---');\n lines.push('');\n lines.push(`_Files: ${fileNodes.length} · Corpus: \\`${provenance.corpusHash.slice(0, 16)}…\\`_`);\n lines.push('');\n\n return lines.join('\\n');\n}\n","import { stableStringify } from './serialize.js';\nimport type { VgGraph } from '../schema.js';\n\n/**\n * `graph.html`: self-contained, dependency-free, deterministic overview.\n *\n * For small/medium maps we embed the full graph JSON for tooling.\n * For large maps (default: >5k nodes) we embed only a\n * **hub + area summary** so the HTML stays openable; full data stays in\n * graph.json / graph.db. Interactive WebGL (sigma.js) remains a follow-up.\n */\n\nconst EMBED_NODE_LIMIT = 5_000;\n\nexport function renderHtml(graph: VgGraph): string {\n const top = graph.nodes\n .filter((n) => n.kind !== 'file' && n.kind !== 'external')\n .sort((a, b) => b.importance - a.importance || a.qualifiedName.localeCompare(b.qualifiedName))\n .slice(0, 50);\n\n const areas = [...graph.areas]\n .sort((a, b) => b.size - a.size || a.id - b.id)\n .slice(0, 40);\n\n const rows = top\n .map(\n (n) =>\n `<tr><td><code>${esc(n.qualifiedName)}</code></td><td>${esc(n.kind)}</td>` +\n `<td><code>${esc(n.file)}</code>:${n.span.start}</td><td>${n.importance.toFixed(3)}</td></tr>`,\n )\n .join('\\n');\n\n const areaRows = areas\n .map(\n (a) =>\n `<tr><td>${a.id}</td><td><code>${esc(a.label)}</code></td>` +\n `<td>${a.size}</td><td>${a.cohesion.toFixed(3)}</td><td>${a.externalEdges}</td></tr>`,\n )\n .join('\\n');\n\n const large = graph.meta.counts.nodes > EMBED_NODE_LIMIT;\n const embedPayload = large\n ? {\n schemaVersion: graph.schemaVersion,\n generatedAt: graph.generatedAt,\n provenance: graph.provenance,\n meta: graph.meta,\n hubs: top.map((n) => ({\n id: n.id,\n name: n.qualifiedName,\n kind: n.kind,\n file: n.file,\n line: n.span.start,\n importance: n.importance,\n area: n.area,\n })),\n areas: areas.map((a) => ({\n id: a.id,\n label: a.label,\n size: a.size,\n cohesion: a.cohesion,\n externalEdges: a.externalEdges,\n })),\n note: `Full graph omitted (${graph.meta.counts.nodes} nodes > ${EMBED_NODE_LIMIT}). Use graph.json or .vibgrate/cache/graph.db.`,\n }\n : graph;\n\n const embedded = stableStringify(embedPayload, 0);\n const tier = graph.meta.analysisTier ?? 'full';\n\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>vg · code graph</title>\n<style>\n :root { color-scheme: light dark; }\n body { font: 14px/1.5 system-ui, sans-serif; margin: 2rem auto; max-width: 60rem; padding: 0 1rem; }\n h1 { font-size: 1.4rem; } h2 { font-size: 1.1rem; margin-top: 2rem; }\n table { border-collapse: collapse; width: 100%; }\n th, td { text-align: left; padding: 4px 8px; border-bottom: 1px solid #8884; }\n td:last-child, th:last-child { text-align: right; }\n .meta { color: #888; } code { font-size: 0.9em; }\n .banner { background: #8882; padding: 0.6rem 0.8rem; border-radius: 6px; margin: 1rem 0; }\n</style>\n</head>\n<body>\n<h1>vg · code graph</h1>\n<p class=\"meta\">${graph.meta.counts.nodes} nodes · ${graph.meta.counts.edges} edges ·\n${graph.meta.counts.areas} areas · ${esc(graph.meta.languages.join(', ') || '—')} ·\nclustering: ${esc(graph.meta.cluster)} · tier: ${esc(tier)} · vg ${esc(graph.provenance.version)}</p>\n${\n large\n ? `<p class=\"banner\">Large map (${graph.meta.counts.nodes} nodes): HTML shows hubs + areas only.\nFull graph remains in <code>graph.json</code> / <code>.vibgrate/cache/graph.db</code> (query via <code>vg serve</code>).</p>`\n : ''\n}\n<h2>Areas (subsystems)</h2>\n<table>\n<thead><tr><th>Id</th><th>Label</th><th>Size</th><th>Cohesion</th><th>External</th></tr></thead>\n<tbody>\n${areaRows || '<tr><td colspan=\"5\" class=\"meta\">no areas</td></tr>'}\n</tbody>\n</table>\n<h2>Most-connected definitions (hubs)</h2>\n<table>\n<thead><tr><th>Symbol</th><th>Kind</th><th>Location</th><th>Importance</th></tr></thead>\n<tbody>\n${rows}\n</tbody>\n</table>\n<p class=\"meta\">${large ? 'Summary payload' : 'Full graph'} embedded below for tooling.</p>\n<script type=\"application/json\" id=\"vg-graph\">\n${embedded}\n</script>\n</body>\n</html>\n`;\n}\n\nfunction esc(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { serializeGraph } from './serialize.js';\nimport { mapFileExists, snapshotPathFor, writeGraphSnapshot } from './snapshot.js';\nimport { renderReport } from './report.js';\nimport { renderHtml } from './html.js';\nimport { globalGraphPath, globalGraphPathForRef, repositoryStoreDir } from '../runtime/paths.js';\nimport { detectGitRef } from '../runtime/git-ref.js';\nimport type { VgGraph } from '../schema.js';\n\n/**\n * Graph artifact layout (Fusion Runtime Phase 1).\n *\n * By default the code map lives in the **global** application store\n * (XDG / Application Support), keyed by repository id — not inside the repo.\n * That keeps `vg` / `vg serve` / auto-refresh from dirtying the working tree.\n *\n * Legacy in-repo path `.vibgrate/graph.json` is still **read** when present\n * (migration), and is still the write target when:\n * - `VIBGRATE_GRAPH_IN_REPO=1` (or true/yes), or\n * - callers pass an explicit `graphPath` / `--graph`.\n *\n * `vg share` continues to opt into a committed in-repo map by writing under\n * `.vibgrate/` (and rewriting its gitignore).\n */\n\nexport interface WriteOptions {\n root: string;\n html?: boolean; // default true\n report?: boolean; // default true\n graphPath?: string; // override graph.json path\n}\n\nexport interface WrittenArtifacts {\n graphPath: string;\n reportPath?: string;\n htmlPath?: string;\n factsPath?: string;\n}\n\nexport function vibgrateDir(root: string): string {\n return path.join(root, '.vibgrate');\n}\n\n/** Historical in-repo map path (still read as a fallback). */\nexport function legacyGraphPath(root: string): string {\n return path.join(vibgrateDir(root), 'graph.json');\n}\n\n/**\n * When true, default writes go to `.vibgrate/graph.json` (pre–Phase-1 layout).\n * Useful for CI that asserts in-repo artifacts, and for `vg share` workflows.\n */\nexport function preferInRepoGraph(env: NodeJS.ProcessEnv = process.env): boolean {\n const v = env.VIBGRATE_GRAPH_IN_REPO?.trim().toLowerCase();\n return v === '1' || v === 'true' || v === 'yes';\n}\n\n/**\n * Default **write** path for the map. Global store unless the operator opts\n * into the legacy in-repo layout. When git is available, prefer a\n * branch-keyed snapshot (Fusion §4.1.1) so switching branches does not\n * overwrite another ref's on-disk map.\n */\nexport function defaultGraphPath(root: string, env: NodeJS.ProcessEnv = process.env): string {\n if (preferInRepoGraph(env)) return legacyGraphPath(root);\n const { ref, kind } = detectGitRef(root);\n if (kind !== 'none' && ref) return globalGraphPathForRef(root, ref, env);\n return globalGraphPath(root, 'current', env);\n}\n\n/**\n * Resolve where to **read** the map from.\n * Order: explicit override → legacy in-repo when `VIBGRATE_GRAPH_IN_REPO` is set\n * → branch-keyed global (if on a git ref) → `current` global snapshot →\n * legacy in-repo → default write path.\n *\n * The in-repo env short-circuit matters for CI and the release benchmark: they\n * force portable `.vibgrate/graph.json` artifacts and must not pay a synchronous\n * `git rev-parse` (or prefer a leftover global snapshot) on every resolve.\n */\nexport function resolveGraphPath(root: string, override?: string, env: NodeJS.ProcessEnv = process.env): string {\n if (override?.trim()) return path.resolve(override.trim());\n if (preferInRepoGraph(env)) {\n const legacy = legacyGraphPath(root);\n if (fs.existsSync(legacy)) return legacy;\n return legacy;\n }\n const { ref, kind } = detectGitRef(root);\n if (kind !== 'none' && ref) {\n const branchPath = globalGraphPathForRef(root, ref, env);\n if (mapFileExists(branchPath)) return branchPath;\n }\n const globalPath = globalGraphPath(root, 'current', env);\n if (mapFileExists(globalPath)) return globalPath;\n const legacy = legacyGraphPath(root);\n if (fs.existsSync(legacy)) return legacy;\n return defaultGraphPath(root, env);\n}\n\n/**\n * By default the graph artifacts are *local*: builds and auto-refreshes rewrite\n * them constantly, and without an ignore file every `vg ask`/`vg serve` leaves\n * the branch dirty (untracked or modified `.vibgrate/` files) — irritating for\n * humans and a source of junk commits from AI agents. So the first time vg\n * writes into `.vibgrate/` it also drops a `.gitignore` covering the graph\n * artifacts, the cache, and itself.\n *\n * Deliberately create-once: an existing `.vibgrate/.gitignore` is NEVER\n * touched, whatever its content — `vg share` owns it after opt-in (rewritten to\n * commit `graph.json`), and a user-managed file (even an empty one) is theirs.\n * Scanner artifacts meant for git (`baseline.json`, `standards.json`,\n * `attestation.intoto.jsonl`) are intentionally not listed.\n *\n * With the global store default, this gitignore is only created when something\n * is still written under `.vibgrate/` (in-repo mode or other in-repo writers).\n */\nconst DEFAULT_GITIGNORE = [\n '# Created once by vg — local graph artifacts stay out of git by default.',\n '# Run `vg share` to commit the map for your team (it rewrites this file).',\n '# vg never touches an existing .vibgrate/.gitignore: edit it (or leave it',\n '# empty) to manage these ignores yourself.',\n '.gitignore',\n 'cache/',\n 'graph.json',\n 'graph.snap',\n 'graph.html',\n 'GRAPH_REPORT.md',\n 'facts.jsonl',\n 'mcp-navigation.json',\n // Machine-local telemetry the LSP/`vg code` accrue over time — never meant\n // for git: score-history.jsonl is per-machine trend data (lsp/score-history.ts),\n // run-outcomes.jsonl is per-machine agent-run telemetry (code/run-outcome.ts).\n // Neither is a snapshot artifact like scan_result.json/baseline.json, which\n // are intentionally left out of this list for a user to commit if they choose.\n 'score-history.jsonl',\n 'run-outcomes.jsonl',\n];\n\nexport function ensureVibgrateGitignore(root: string): void {\n const file = path.join(vibgrateDir(root), '.gitignore');\n if (fs.existsSync(file)) return;\n fs.mkdirSync(vibgrateDir(root), { recursive: true });\n fs.writeFileSync(file, `${DEFAULT_GITIGNORE.join('\\n')}\\n`);\n}\n\n/**\n * Directory for companion artifacts (report, html, facts) next to the map.\n * Global maps → repository store root; in-repo maps → `.vibgrate/`.\n */\nexport function companionArtifactDir(root: string, graphPath: string, env: NodeJS.ProcessEnv = process.env): string {\n const resolved = path.resolve(graphPath);\n const legacy = path.resolve(legacyGraphPath(root));\n if (resolved === legacy) return vibgrateDir(root);\n const globalDefault = path.resolve(globalGraphPath(root, 'current', env));\n if (resolved === globalDefault) return repositoryStoreDir(root, env);\n // Custom --graph: keep companions next to the map file.\n return path.dirname(resolved);\n}\n\nexport function writeArtifacts(graph: VgGraph, options: WriteOptions): WrittenArtifacts {\n const graphPath = options.graphPath ?? defaultGraphPath(options.root);\n const companions = companionArtifactDir(options.root, graphPath);\n const inRepo = isPathInside(companions, options.root) || isPathInside(graphPath, options.root);\n\n fs.mkdirSync(path.dirname(graphPath), { recursive: true });\n fs.mkdirSync(companions, { recursive: true });\n\n // Only drop `.vibgrate/.gitignore` when we still touch the in-repo layout.\n if (inRepo && isPathInside(companions, vibgrateDir(options.root))) {\n ensureVibgrateGitignore(options.root);\n } else if (path.resolve(graphPath) === path.resolve(legacyGraphPath(options.root))) {\n ensureVibgrateGitignore(options.root);\n }\n\n // Global-store maps are machine-consumed only, so the snapshot IS the map\n // there — skipping the pretty-JSON serialize is the bulk of the write cost\n // on large repos. Everywhere a human (or git) can see the file — legacy\n // in-repo layout, VIBGRATE_GRAPH_IN_REPO, `vg share`, an explicit --graph\n // path — the canonical graph.json is still written, with the snapshot as a\n // sidecar cache.\n const storeMode = isPathInside(graphPath, repositoryStoreDir(options.root));\n\n if (storeMode) {\n if (!writeGraphSnapshot(graphPath, graph, { standalone: true })) {\n // Store not writable for the binary — fall back to the JSON write below\n // rather than losing the build.\n } else {\n // Drop any JSON left by an older CLI at this path so a downgrade can\n // never silently serve a stale map from beside a newer snapshot.\n fs.rmSync(graphPath, { force: true });\n }\n }\n\n if (!storeMode || !fs.existsSync(snapshotPathFor(graphPath))) {\n // Atomic write (temp + rename): `vg serve` hot-reloads the map on mtime\n // change, so a rebuild — including its own in-process auto-refresh — must\n // never expose a half-written file to a concurrent reader.\n const tmp = `${graphPath}.${process.pid}.tmp`;\n try {\n fs.writeFileSync(tmp, serializeGraph(graph));\n fs.renameSync(tmp, graphPath);\n } catch (err) {\n fs.rmSync(tmp, { force: true });\n throw err;\n }\n // Binary sidecar cache for fast loads; best-effort, JSON stays canonical.\n writeGraphSnapshot(graphPath, graph);\n }\n\n const written: WrittenArtifacts = {\n graphPath: storeMode && !fs.existsSync(graphPath) ? snapshotPathFor(graphPath) : graphPath,\n };\n\n if (options.report !== false) {\n const reportPath = path.join(companions, 'GRAPH_REPORT.md');\n fs.writeFileSync(reportPath, renderReport(graph));\n written.reportPath = reportPath;\n }\n\n if (options.html !== false) {\n const htmlPath = path.join(companions, 'graph.html');\n fs.writeFileSync(htmlPath, renderHtml(graph));\n written.htmlPath = htmlPath;\n }\n\n // facts.jsonl (deterministic NDJSON) when facts were derived.\n if (graph.facts && graph.facts.length) {\n const factsPath = path.join(companions, 'facts.jsonl');\n fs.writeFileSync(factsPath, graph.facts.map((f) => JSON.stringify(f)).join('\\n') + '\\n');\n written.factsPath = factsPath;\n }\n\n return written;\n}\n\nfunction isPathInside(child: string, parent: string): boolean {\n const rel = path.relative(path.resolve(parent), path.resolve(child));\n return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));\n}\n"]}