homegraph 1.5.1 → 1.5.2

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 (258) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +305 -305
  3. package/dist/bin/homegraph.js +9 -9
  4. package/dist/db/index.js +36 -36
  5. package/dist/db/migrations.js +37 -37
  6. package/dist/db/queries.js +156 -156
  7. package/dist/db/schema.sql +203 -203
  8. package/dist/directory.js +5 -5
  9. package/dist/extraction/index.d.ts +1 -1
  10. package/dist/extraction/index.d.ts.map +1 -1
  11. package/dist/extraction/index.js +54 -43
  12. package/dist/extraction/index.js.map +1 -1
  13. package/dist/extraction/languages/arkts.d.ts +53 -6
  14. package/dist/extraction/languages/arkts.d.ts.map +1 -1
  15. package/dist/extraction/languages/arkts.js +565 -65
  16. package/dist/extraction/languages/arkts.js.map +1 -1
  17. package/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  18. package/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
  19. package/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
  20. package/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
  21. package/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
  22. package/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
  23. package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  24. package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  25. package/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +3 -2
  28. package/dist/index.js.map +1 -1
  29. package/dist/installer/instructions-template.js +9 -9
  30. package/dist/mcp/diff-impact.d.ts +131 -0
  31. package/dist/mcp/diff-impact.d.ts.map +1 -0
  32. package/dist/mcp/diff-impact.js +385 -0
  33. package/dist/mcp/diff-impact.js.map +1 -0
  34. package/dist/mcp/liveness-watchdog.js +51 -51
  35. package/dist/mcp/server-instructions.d.ts +1 -1
  36. package/dist/mcp/server-instructions.d.ts.map +1 -1
  37. package/dist/mcp/server-instructions.js +49 -47
  38. package/dist/mcp/server-instructions.js.map +1 -1
  39. package/dist/mcp/tools.d.ts +21 -5
  40. package/dist/mcp/tools.d.ts.map +1 -1
  41. package/dist/mcp/tools.js +305 -55
  42. package/dist/mcp/tools.js.map +1 -1
  43. package/dist/resolution/callback-synthesizer.d.ts +2 -1
  44. package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  45. package/dist/resolution/callback-synthesizer.js +153 -53
  46. package/dist/resolution/callback-synthesizer.js.map +1 -1
  47. package/dist/resolution/frameworks/arkts-entry.d.ts.map +1 -1
  48. package/dist/resolution/frameworks/arkts-entry.js +20 -9
  49. package/dist/resolution/frameworks/arkts-entry.js.map +1 -1
  50. package/dist/resolution/index.d.ts.map +1 -1
  51. package/dist/resolution/index.js +1 -1
  52. package/dist/resolution/index.js.map +1 -1
  53. package/dist/resolution/memory-budget.d.ts +33 -0
  54. package/dist/resolution/memory-budget.d.ts.map +1 -1
  55. package/dist/resolution/memory-budget.js +58 -0
  56. package/dist/resolution/memory-budget.js.map +1 -1
  57. package/dist/resolution/resolver-pool.d.ts +2 -0
  58. package/dist/resolution/resolver-pool.d.ts.map +1 -1
  59. package/dist/resolution/resolver-pool.js +4 -0
  60. package/dist/resolution/resolver-pool.js.map +1 -1
  61. package/dist/spec/db/commit-node.js +4 -4
  62. package/dist/spec/db/fragment-node.js +10 -10
  63. package/dist/spec/db/fts.js +8 -8
  64. package/dist/spec/db/relations.js +88 -88
  65. package/dist/spec/db/schema.js +6 -6
  66. package/dist/spec/db/schema.sql +121 -121
  67. package/dist/spec/db/spec-node.js +4 -4
  68. package/dist/spec/llm/prompts.js +53 -53
  69. package/dist/ui/shimmer-progress.d.ts.map +1 -1
  70. package/dist/ui/shimmer-progress.js +4 -1
  71. package/dist/ui/shimmer-progress.js.map +1 -1
  72. package/package.json +61 -62
  73. package/dist/extraction/languages/arkts-viewtree.d.ts +0 -22
  74. package/dist/extraction/languages/arkts-viewtree.d.ts.map +0 -1
  75. package/dist/extraction/languages/arkts-viewtree.js +0 -133
  76. package/dist/extraction/languages/arkts-viewtree.js.map +0 -1
  77. package/dist/reasoning/config.d.ts +0 -45
  78. package/dist/reasoning/config.d.ts.map +0 -1
  79. package/dist/reasoning/config.js +0 -171
  80. package/dist/reasoning/config.js.map +0 -1
  81. package/dist/reasoning/credentials.d.ts +0 -5
  82. package/dist/reasoning/credentials.d.ts.map +0 -1
  83. package/dist/reasoning/credentials.js +0 -83
  84. package/dist/reasoning/credentials.js.map +0 -1
  85. package/dist/reasoning/login.d.ts +0 -21
  86. package/dist/reasoning/login.d.ts.map +0 -1
  87. package/dist/reasoning/login.js +0 -85
  88. package/dist/reasoning/login.js.map +0 -1
  89. package/dist/reasoning/reasoner.d.ts +0 -43
  90. package/dist/reasoning/reasoner.d.ts.map +0 -1
  91. package/dist/reasoning/reasoner.js +0 -308
  92. package/dist/reasoning/reasoner.js.map +0 -1
  93. package/dist/spec/evolve/llm-client.d.ts +0 -50
  94. package/dist/spec/evolve/llm-client.d.ts.map +0 -1
  95. package/dist/spec/evolve/llm-client.js +0 -176
  96. package/dist/spec/evolve/llm-client.js.map +0 -1
  97. package/dist/spec/evolve/logic-checker.d.ts +0 -12
  98. package/dist/spec/evolve/logic-checker.d.ts.map +0 -1
  99. package/dist/spec/evolve/logic-checker.js +0 -24
  100. package/dist/spec/evolve/logic-checker.js.map +0 -1
  101. package/dist/spec/llm/index.d.ts +0 -3
  102. package/dist/spec/llm/index.d.ts.map +0 -1
  103. package/dist/spec/llm/index.js +0 -11
  104. package/dist/spec/llm/index.js.map +0 -1
  105. package/dist/spec/mining/diff-parser.d.ts +0 -33
  106. package/dist/spec/mining/diff-parser.d.ts.map +0 -1
  107. package/dist/spec/mining/diff-parser.js +0 -166
  108. package/dist/spec/mining/diff-parser.js.map +0 -1
  109. package/dist/spec/mining/git-scanner.d.ts +0 -103
  110. package/dist/spec/mining/git-scanner.d.ts.map +0 -1
  111. package/dist/spec/mining/git-scanner.js +0 -307
  112. package/dist/spec/mining/git-scanner.js.map +0 -1
  113. package/dist/spec/mining/pipeline.d.ts +0 -53
  114. package/dist/spec/mining/pipeline.d.ts.map +0 -1
  115. package/dist/spec/mining/pipeline.js +0 -178
  116. package/dist/spec/mining/pipeline.js.map +0 -1
  117. package/dist/spec/mining/scope-resolver.d.ts +0 -45
  118. package/dist/spec/mining/scope-resolver.d.ts.map +0 -1
  119. package/dist/spec/mining/scope-resolver.js +0 -103
  120. package/dist/spec/mining/scope-resolver.js.map +0 -1
  121. package/dist/spec/mining/spec-extractor.d.ts +0 -69
  122. package/dist/spec/mining/spec-extractor.d.ts.map +0 -1
  123. package/dist/spec/mining/spec-extractor.js +0 -369
  124. package/dist/spec/mining/spec-extractor.js.map +0 -1
  125. package/dist/spec/utils.d.ts +0 -167
  126. package/dist/spec/utils.d.ts.map +0 -1
  127. package/dist/spec/utils.js +0 -463
  128. package/dist/spec/utils.js.map +0 -1
  129. package/scripts/_tmp-cfwk-resolve.log +0 -0
  130. package/scripts/_tmp-cfwk-sig.log +0 -0
  131. package/scripts/_tmp-cfwk-vt.log +0 -0
  132. package/scripts/add-lang/bench.sh +0 -60
  133. package/scripts/add-lang/check-grammar.mjs +0 -75
  134. package/scripts/add-lang/dump-ast.mjs +0 -103
  135. package/scripts/add-lang/verify-extraction.mjs +0 -70
  136. package/scripts/agent-eval/ab-adoption.sh +0 -91
  137. package/scripts/agent-eval/ab-hook.sh +0 -86
  138. package/scripts/agent-eval/ab-impl.sh +0 -78
  139. package/scripts/agent-eval/ab-new-vs-baseline.sh +0 -102
  140. package/scripts/agent-eval/ab-sufficiency.sh +0 -78
  141. package/scripts/agent-eval/arms-F.sh +0 -21
  142. package/scripts/agent-eval/arms-matrix.sh +0 -37
  143. package/scripts/agent-eval/audit.sh +0 -68
  144. package/scripts/agent-eval/bench-readme.sh +0 -28
  145. package/scripts/agent-eval/bench-why-repo.sh +0 -22
  146. package/scripts/agent-eval/block-read-hook.sh +0 -19
  147. package/scripts/agent-eval/hook-settings.json +0 -15
  148. package/scripts/agent-eval/itrun.sh +0 -120
  149. package/scripts/agent-eval/offload-eval-3arm.sh +0 -72
  150. package/scripts/agent-eval/offload-eval-cost.mjs +0 -133
  151. package/scripts/agent-eval/offload-eval-effort.mjs +0 -108
  152. package/scripts/agent-eval/offload-eval-frontload-matrix.sh +0 -25
  153. package/scripts/agent-eval/offload-eval-frontload.sh +0 -47
  154. package/scripts/agent-eval/offload-eval-ground-truth.json +0 -18
  155. package/scripts/agent-eval/offload-eval-hook.mjs +0 -84
  156. package/scripts/agent-eval/offload-eval-judge.mjs +0 -103
  157. package/scripts/agent-eval/offload-eval-matrix.sh +0 -20
  158. package/scripts/agent-eval/offload-eval-metrics.mjs +0 -94
  159. package/scripts/agent-eval/offload-eval-refs1.sh +0 -50
  160. package/scripts/agent-eval/offload-eval-setup.sh +0 -24
  161. package/scripts/agent-eval/offload-eval-styles.sh +0 -72
  162. package/scripts/agent-eval/offload-eval-summarize.mjs +0 -68
  163. package/scripts/agent-eval/offload-eval.md +0 -76
  164. package/scripts/agent-eval/parse-arms.mjs +0 -116
  165. package/scripts/agent-eval/parse-bench-readme.mjs +0 -84
  166. package/scripts/agent-eval/parse-run.mjs +0 -45
  167. package/scripts/agent-eval/parse-session.mjs +0 -93
  168. package/scripts/agent-eval/probe-context.mjs +0 -21
  169. package/scripts/agent-eval/probe-explore.mjs +0 -40
  170. package/scripts/agent-eval/probe-node.mjs +0 -20
  171. package/scripts/agent-eval/probe-sweep.mjs +0 -119
  172. package/scripts/agent-eval/probe-trace.mjs +0 -20
  173. package/scripts/agent-eval/redirect-read-hook.sh +0 -38
  174. package/scripts/agent-eval/repro-concurrent-explore.mjs +0 -119
  175. package/scripts/agent-eval/repro-daemon-clients.mjs +0 -125
  176. package/scripts/agent-eval/run-agent.sh +0 -34
  177. package/scripts/agent-eval/run-all.sh +0 -75
  178. package/scripts/agent-eval/run-arms.sh +0 -56
  179. package/scripts/agent-eval/seq-matrix.mjs +0 -137
  180. package/scripts/bench-arkts-init-rss.log +0 -0
  181. package/scripts/build-bundle.sh +0 -123
  182. package/scripts/exp_boundary_eval/README.md +0 -247
  183. package/scripts/exp_boundary_eval/__pycache__/_utils.cpython-310.pyc +0 -0
  184. package/scripts/exp_boundary_eval/__pycache__/_utils.cpython-38.pyc +0 -0
  185. package/scripts/exp_boundary_eval/__pycache__/analyze.cpython-310.pyc +0 -0
  186. package/scripts/exp_boundary_eval/__pycache__/analyze.cpython-38.pyc +0 -0
  187. package/scripts/exp_boundary_eval/__pycache__/deveco_arm.cpython-38.pyc +0 -0
  188. package/scripts/exp_boundary_eval/__pycache__/run_all.cpython-310.pyc +0 -0
  189. package/scripts/exp_boundary_eval/__pycache__/run_all.cpython-38.pyc +0 -0
  190. package/scripts/exp_boundary_eval/__pycache__/run_one.cpython-310.pyc +0 -0
  191. package/scripts/exp_boundary_eval/__pycache__/run_one.cpython-38.pyc +0 -0
  192. package/scripts/exp_boundary_eval/__pycache__/run_session.cpython-310.pyc +0 -0
  193. package/scripts/exp_boundary_eval/__pycache__/run_session.cpython-38.pyc +0 -0
  194. package/scripts/exp_boundary_eval/__pycache__/setup.cpython-310.pyc +0 -0
  195. package/scripts/exp_boundary_eval/__pycache__/setup.cpython-38.pyc +0 -0
  196. package/scripts/exp_boundary_eval/__pycache__/win_mcp_launcher.cpython-38.pyc +0 -0
  197. package/scripts/exp_boundary_eval/_test_mcp_chain.py +0 -78
  198. package/scripts/exp_boundary_eval/_test_stdin.py +0 -8
  199. package/scripts/exp_boundary_eval/_utils.py +0 -1116
  200. package/scripts/exp_boundary_eval/analyze.py +0 -1313
  201. package/scripts/exp_boundary_eval/deveco_arm.py +0 -519
  202. package/scripts/exp_boundary_eval/run_all.py +0 -378
  203. package/scripts/exp_boundary_eval/run_one.py +0 -165
  204. package/scripts/exp_boundary_eval/run_session.py +0 -158
  205. package/scripts/exp_boundary_eval/setup.py +0 -120
  206. package/scripts/exp_boundary_eval/win_mcp_launcher.py +0 -73
  207. package/scripts/exp_boundary_eval/win_mcp_stdio_wrap.js +0 -36
  208. package/scripts/exp_boundary_eval/win_node_launcher.py +0 -24
  209. package/scripts/extract-release-notes.mjs +0 -130
  210. package/scripts/local-install.sh +0 -41
  211. package/scripts/npm-sdk.js +0 -75
  212. package/scripts/npm-shim.js +0 -275
  213. package/scripts/ohos-sdk-publish.mjs +0 -133
  214. package/scripts/pack-npm.sh +0 -119
  215. package/scripts/prepare-release.mjs +0 -270
  216. package/scripts/probe-arkts-mem-why-run.log +0 -0
  217. package/scripts/probe-banner-livecard-ir.log +0 -0
  218. package/scripts/probe-cfwk-ast.stderr.log +0 -0
  219. package/scripts/probe-cfwk-ast.stdout.log +0 -0
  220. package/scripts/probe-cfwk-attr-shape.log +0 -0
  221. package/scripts/probe-cfwk-cfgdump.stderr.log +0 -0
  222. package/scripts/probe-cfwk-cfgdump.stdout.log +0 -0
  223. package/scripts/probe-cfwk-cvc.stderr.log +0 -0
  224. package/scripts/probe-cfwk-cvc.stdout.log +0 -0
  225. package/scripts/probe-cfwk-diag2.log +0 -0
  226. package/scripts/probe-cfwk-diag3.log +0 -0
  227. package/scripts/probe-cfwk-fedbg.stderr.log +0 -0
  228. package/scripts/probe-cfwk-fedbg.stdout.log +0 -0
  229. package/scripts/probe-cfwk-fileresult.log +0 -0
  230. package/scripts/probe-cfwk-fix.stderr.log +0 -0
  231. package/scripts/probe-cfwk-fix.stdout.log +0 -0
  232. package/scripts/probe-cfwk-fix2.stderr.log +0 -0
  233. package/scripts/probe-cfwk-fix2.stdout.log +0 -0
  234. package/scripts/probe-cfwk-fix3.stderr.log +0 -0
  235. package/scripts/probe-cfwk-fix3.stdout.log +0 -0
  236. package/scripts/probe-cfwk-foreach.log +0 -0
  237. package/scripts/probe-cfwk-getmethod-throw.log +0 -0
  238. package/scripts/probe-cfwk-hg-extract.log +0 -0
  239. package/scripts/probe-cfwk-pr1003-noprior.stderr.log +0 -0
  240. package/scripts/probe-cfwk-pr1003-noprior.stdout.log +0 -0
  241. package/scripts/probe-cfwk-pr1003.stderr.log +0 -0
  242. package/scripts/probe-cfwk-pr1003.stdout.log +0 -0
  243. package/scripts/probe-cfwk-preroot-noprior.stderr.log +0 -0
  244. package/scripts/probe-cfwk-preroot-noprior.stdout.log +0 -0
  245. package/scripts/probe-cfwk-preroot-prior.stderr.log +0 -0
  246. package/scripts/probe-cfwk-preroot-prior.stdout.log +0 -0
  247. package/scripts/probe-cfwk-preroot-skipstate.stderr.log +0 -0
  248. package/scripts/probe-cfwk-preroot-skipstate.stdout.log +0 -0
  249. package/scripts/probe-cfwk-resolve-sim.log +0 -0
  250. package/scripts/probe-cfwk-tree-shape.log +0 -0
  251. package/scripts/probe-cfwk-walk-abort.log +0 -0
  252. package/scripts/probe-cfwk.log +0 -0
  253. package/scripts/probe-force-index.log +0 -0
  254. package/scripts/probe-no-force-index.log +0 -0
  255. package/scripts/probe-samefile-ir.stderr.log +0 -0
  256. package/scripts/probe-samefile-ir.stdout.log +0 -224
  257. package/scripts/probe-sdk-vs-project.log +0 -0
  258. package/scripts/probe-viewtree-downgrade.log +0 -0
@@ -1,463 +0,0 @@
1
- "use strict";
2
- /**
3
- * Spec utility functions — replaces `file_utils.py`, `meta.py`, and
4
- * `truncate.py` from Python Commit4Spec.
5
- *
6
- * Covers file I/O, meta.json read/write, text/diff truncation, and budget
7
- * computation. All functions are synchronous and use Node.js built-ins.
8
- *
9
- * @module spec/utils
10
- */
11
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
- if (k2 === undefined) k2 = k;
13
- var desc = Object.getOwnPropertyDescriptor(m, k);
14
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
- desc = { enumerable: true, get: function() { return m[k]; } };
16
- }
17
- Object.defineProperty(o, k2, desc);
18
- }) : (function(o, m, k, k2) {
19
- if (k2 === undefined) k2 = k;
20
- o[k2] = m[k];
21
- }));
22
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
- Object.defineProperty(o, "default", { enumerable: true, value: v });
24
- }) : function(o, v) {
25
- o["default"] = v;
26
- });
27
- var __importStar = (this && this.__importStar) || (function () {
28
- var ownKeys = function(o) {
29
- ownKeys = Object.getOwnPropertyNames || function (o) {
30
- var ar = [];
31
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
- return ar;
33
- };
34
- return ownKeys(o);
35
- };
36
- return function (mod) {
37
- if (mod && mod.__esModule) return mod;
38
- var result = {};
39
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
- __setModuleDefault(result, mod);
41
- return result;
42
- };
43
- })();
44
- Object.defineProperty(exports, "__esModule", { value: true });
45
- exports.SPEC_DATA_DIR = void 0;
46
- exports.readFileContent = readFileContent;
47
- exports.writeFileContent = writeFileContent;
48
- exports.parseCommitInfoMd = parseCommitInfoMd;
49
- exports.discoverSpecs = discoverSpecs;
50
- exports.resolveDbPath = resolveDbPath;
51
- exports.readMeta = readMeta;
52
- exports.writeMeta = writeMeta;
53
- exports.truncateCodeDiff = truncateCodeDiff;
54
- exports.truncateText = truncateText;
55
- exports.truncateSubtitles = truncateSubtitles;
56
- exports.computeBudgetProfile = computeBudgetProfile;
57
- const fs = __importStar(require("fs"));
58
- const path = __importStar(require("path"));
59
- const errors_1 = require("../errors");
60
- // =============================================================================
61
- // Constants
62
- // =============================================================================
63
- /** Root directory for spec data, relative to the repository root. */
64
- exports.SPEC_DATA_DIR = '.homegraph/commit4spec';
65
- const TRUNCATION_SUFFIX = ' …(truncated)';
66
- const TRUNCATION_SUFFIX_LENGTH = TRUNCATION_SUFFIX.length;
67
- // =============================================================================
68
- // File I/O
69
- // =============================================================================
70
- /**
71
- * Read a UTF-8 file synchronously.
72
- *
73
- * Returns `null` for ENOENT (not found), EISDIR (is a directory), or EACCES
74
- * (permission denied). Other errors propagate.
75
- */
76
- function readFileContent(filePath) {
77
- try {
78
- return fs.readFileSync(filePath, 'utf-8');
79
- }
80
- catch (err) {
81
- if (err.code === 'ENOENT' || err.code === 'EISDIR' || err.code === 'EACCES') {
82
- return null;
83
- }
84
- throw err;
85
- }
86
- }
87
- /**
88
- * Write a UTF-8 file synchronously, creating parent directories as needed.
89
- *
90
- * Parent directories are created with `fs.mkdirSync({ recursive: true })`.
91
- * If the parent exists but is not a directory the underlying error propagates.
92
- */
93
- function writeFileContent(filePath, content) {
94
- const dir = path.dirname(filePath);
95
- if (dir && dir !== '.') {
96
- fs.mkdirSync(dir, { recursive: true });
97
- }
98
- fs.writeFileSync(filePath, content, 'utf-8');
99
- }
100
- // =============================================================================
101
- // Commit-info.md parsing
102
- // =============================================================================
103
- /**
104
- * Regex matching commit-hash annotations in commit-info.md content.
105
- *
106
- * Matches lines like:
107
- * commit: abc123def456...
108
- * commit-id: abc123def456...
109
- * commit_id: abc123def456...
110
- * commit-id=abc123def456...
111
- * abc123def456789... (bare 40-char hex on its own line)
112
- */
113
- const COMMIT_HASH_RE = /^commit[-_ ]?(?:id)?[:= ]\s*([a-f0-9]{7,40})\s*$/i;
114
- const BARE_HASH_RE = /^([a-f0-9]{40})\s*$/i;
115
- /**
116
- * Parse commit-info.md content for a commit hash.
117
- *
118
- * Searches line by line for one of:
119
- * - `commit: HASH`
120
- * - `commit-id: HASH`
121
- * - `commit_id: HASH`
122
- * - a bare 40-char hex hash on its own line
123
- *
124
- * Returns the first matched hash (7–40 hex chars), or `null` if no hash is
125
- * found. Empty or whitespace-only input returns `null`.
126
- */
127
- function parseCommitInfoMd(content) {
128
- if (!content)
129
- return null;
130
- for (const line of content.split('\n')) {
131
- const trimmed = line.trim();
132
- if (!trimmed)
133
- continue;
134
- // Try annotated hash first
135
- const match = COMMIT_HASH_RE.exec(trimmed);
136
- if (match) {
137
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
138
- return match[1].toLowerCase();
139
- }
140
- // Try bare 40-char hex
141
- const bareMatch = BARE_HASH_RE.exec(trimmed);
142
- if (bareMatch) {
143
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
144
- return bareMatch[1].toLowerCase();
145
- }
146
- }
147
- return null;
148
- }
149
- // =============================================================================
150
- // Spec discovery
151
- // =============================================================================
152
- /**
153
- * Discover specs in a storage directory.
154
- *
155
- * A "spec" is any `.md` file OR any subdirectory. For `.md` files the
156
- * `specId` is the basename without the extension. For subdirectories the
157
- * `specId` is the directory name.
158
- *
159
- * Returns an empty array when `specStoragePath` does not exist or is not a
160
- * directory.
161
- */
162
- function discoverSpecs(specStoragePath) {
163
- let entries;
164
- try {
165
- entries = fs.readdirSync(specStoragePath, { withFileTypes: true });
166
- }
167
- catch (err) {
168
- // Non-existent or not a directory → empty list
169
- return [];
170
- }
171
- const result = [];
172
- for (const entry of entries) {
173
- if (entry.isDirectory()) {
174
- result.push({
175
- specId: entry.name,
176
- entryType: 'directory',
177
- path: path.join(specStoragePath, entry.name),
178
- });
179
- }
180
- else if (entry.isFile() && entry.name.endsWith('.md')) {
181
- result.push({
182
- specId: entry.name.slice(0, -3), // strip ".md"
183
- entryType: 'file',
184
- path: path.join(specStoragePath, entry.name),
185
- });
186
- }
187
- // Symlinks, other file types, and dot-files are silently skipped.
188
- }
189
- return result;
190
- }
191
- // =============================================================================
192
- // Database path resolution
193
- // =============================================================================
194
- /**
195
- * Resolve the database path.
196
- *
197
- * Priority:
198
- * 1. If `explicitDbPath` is given, return it as-is (no side effects).
199
- * 2. If `repoPath` is given, default to `{repoPath}/${SPEC_DATA_DIR}/commit4spec.db`
200
- * and ensure `${SPEC_DATA_DIR}/.gitignore` exists containing `"*"`.
201
- * 3. If neither is given, return `"./commit4spec.db"`.
202
- */
203
- function resolveDbPath(repoPath, explicitDbPath) {
204
- if (explicitDbPath) {
205
- return explicitDbPath;
206
- }
207
- if (repoPath) {
208
- const commit4specDir = path.join(repoPath, exports.SPEC_DATA_DIR);
209
- const gitignorePath = path.join(commit4specDir, '.gitignore');
210
- const dbPath = path.join(commit4specDir, 'commit4spec.db');
211
- // Ensure ${SPEC_DATA_DIR} directory exists
212
- try {
213
- fs.mkdirSync(commit4specDir, { recursive: true });
214
- }
215
- catch {
216
- // Directory may already exist; ok.
217
- }
218
- // Write .gitignore if it doesn't exist
219
- if (!fs.existsSync(gitignorePath)) {
220
- try {
221
- fs.writeFileSync(gitignorePath, '*\n', 'utf-8');
222
- }
223
- catch (err) {
224
- (0, errors_1.logWarn)(`Failed to write ${exports.SPEC_DATA_DIR}/.gitignore`, {
225
- path: gitignorePath,
226
- error: err instanceof Error ? err.message : String(err),
227
- });
228
- }
229
- }
230
- return dbPath;
231
- }
232
- return './commit4spec.db';
233
- }
234
- // =============================================================================
235
- // Meta read / write
236
- // =============================================================================
237
- /**
238
- * Read `${SPEC_DATA_DIR}/meta.json` from `repoPath`.
239
- *
240
- * Returns `null` when the file is missing, unreadable, or contains invalid
241
- * JSON or a JSON value that is not a plain object.
242
- */
243
- function readMeta(repoPath) {
244
- const metaPath = path.join(repoPath, exports.SPEC_DATA_DIR, 'meta.json');
245
- let raw;
246
- try {
247
- raw = fs.readFileSync(metaPath, 'utf-8');
248
- }
249
- catch {
250
- return null;
251
- }
252
- let parsed;
253
- try {
254
- parsed = JSON.parse(raw);
255
- }
256
- catch {
257
- (0, errors_1.logWarn)(`Ignoring ${exports.SPEC_DATA_DIR}/meta.json: not valid JSON`, {
258
- path: metaPath,
259
- });
260
- return null;
261
- }
262
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
263
- (0, errors_1.logWarn)(`Ignoring ${exports.SPEC_DATA_DIR}/meta.json: top-level value must be a JSON object`, { path: metaPath });
264
- return null;
265
- }
266
- const obj = parsed;
267
- // Validate required string fields
268
- if (typeof obj.repoPath !== 'string' || typeof obj.specStoragePath !== 'string') {
269
- (0, errors_1.logWarn)(`Ignoring ${exports.SPEC_DATA_DIR}/meta.json: repoPath and specStoragePath must be strings`, { path: metaPath });
270
- return null;
271
- }
272
- return {
273
- repoPath: obj.repoPath,
274
- specStoragePath: obj.specStoragePath,
275
- currentCommitID: typeof obj.currentCommitID === 'string' ? obj.currentCommitID : undefined,
276
- createdAt: typeof obj.createdAt === 'string' ? obj.createdAt : undefined,
277
- updatedAt: typeof obj.updatedAt === 'string' ? obj.updatedAt : new Date().toISOString(),
278
- };
279
- }
280
- /**
281
- * Write `${SPEC_DATA_DIR}/meta.json` to `repoPath`.
282
- *
283
- * If existing meta has a `createdAt` field it is preserved; otherwise a new
284
- * ISO-8601 timestamp is generated. `updatedAt` is always set to the current
285
- * time. The `${SPEC_DATA_DIR}` directory is created if it does not exist.
286
- *
287
- * Returns the full `SpecMeta` that was written.
288
- */
289
- function writeMeta(repoPath, specStoragePath, currentCommitID) {
290
- const metaDir = path.join(repoPath, exports.SPEC_DATA_DIR);
291
- const metaPath = path.join(metaDir, 'meta.json');
292
- const now = new Date().toISOString();
293
- // Preserve createdAt from existing meta if available
294
- let createdAt;
295
- const existing = readMeta(repoPath);
296
- if (existing && existing.createdAt) {
297
- createdAt = existing.createdAt;
298
- }
299
- const meta = {
300
- repoPath,
301
- specStoragePath,
302
- currentCommitID,
303
- createdAt,
304
- updatedAt: now,
305
- };
306
- fs.mkdirSync(metaDir, { recursive: true });
307
- fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2) + '\n', 'utf-8');
308
- return meta;
309
- }
310
- // =============================================================================
311
- // Truncation utilities
312
- // =============================================================================
313
- /**
314
- * Truncate a unified diff at a sensible boundary.
315
- *
316
- * The algorithm prefers semantic boundaries to keep diffs readable:
317
- *
318
- * 1. If the diff is shorter than `maxChars` (default 3800), return it unchanged.
319
- * 2. Try: find the **last** `@@` hunk header in the trailing 50% portion of
320
- * the diff and cut right before it.
321
- * 3. Fallback: find the **last** newline in the trailing 80% portion and cut
322
- * after it.
323
- * 4. Hard fallback: cut exactly at `maxChars`.
324
- *
325
- * Always appends `" …(truncated)"` when truncation occurs.
326
- *
327
- * @param diff - The unified diff text to truncate.
328
- * @param maxChars - Maximum allowed characters (including suffix). Default 3800.
329
- * @returns The (possibly truncated) diff text.
330
- */
331
- function truncateCodeDiff(diff, maxChars = 3800) {
332
- if (diff.length <= maxChars) {
333
- return diff;
334
- }
335
- const suffix = TRUNCATION_SUFFIX;
336
- const effectiveMax = maxChars - TRUNCATION_SUFFIX_LENGTH;
337
- if (effectiveMax <= 0) {
338
- return diff.slice(0, Math.max(0, maxChars - 1)) + suffix;
339
- }
340
- // Step 2: find last "@@" hunk header in trailing 50%
341
- const halfPoint = Math.floor(diff.length * 0.5);
342
- // We search for "\n@@" or "@@" at the very start. Use a regex that
343
- // matches a line starting with "@@" (after optional leading whitespace in
344
- // some edge cases, but standard diffs have "@@" at column 0).
345
- const hunkHeaderRe = /(^|\n)@@/g;
346
- let lastHunkHeaderPos = -1;
347
- let match;
348
- // Reset lastIndex and scan from the beginning; we only accept matches whose
349
- // index is >= halfPoint.
350
- hunkHeaderRe.lastIndex = 0;
351
- while ((match = hunkHeaderRe.exec(diff)) !== null) {
352
- const cap = match[1]; // the newline-or-start anchor
353
- const pos = match.index + (cap ? cap.length : 0); // position of "@@"
354
- if (pos >= halfPoint && pos <= effectiveMax) {
355
- lastHunkHeaderPos = pos;
356
- }
357
- }
358
- if (lastHunkHeaderPos > 0) {
359
- return diff.slice(0, lastHunkHeaderPos) + suffix;
360
- }
361
- // Step 3: find last "\n" in trailing 80%
362
- const eightyPoint = Math.floor(diff.length * 0.2);
363
- for (let i = effectiveMax - 1; i >= eightyPoint; i--) {
364
- if (diff[i] === '\n' && i < effectiveMax) {
365
- return diff.slice(0, i + 1) + suffix;
366
- }
367
- }
368
- // Step 4: hard cut
369
- return diff.slice(0, effectiveMax) + suffix;
370
- }
371
- /**
372
- * Truncate plain text with newline-awareness.
373
- *
374
- * When the text is longer than `maxChars`, it is cut at the last newline
375
- * found before `maxChars` (minus the suffix length). If no suitable newline
376
- * exists, a hard cut is made.
377
- *
378
- * The suffix `" …(truncated)"` is appended only when truncation occurs.
379
- *
380
- * @param text - The text to truncate.
381
- * @param maxChars - Maximum allowed characters (including suffix).
382
- * @returns The (possibly truncated) text.
383
- */
384
- function truncateText(text, maxChars) {
385
- if (text.length <= maxChars) {
386
- return text;
387
- }
388
- const suffix = TRUNCATION_SUFFIX;
389
- const effectiveMax = maxChars - TRUNCATION_SUFFIX_LENGTH;
390
- if (effectiveMax <= 0) {
391
- return text.slice(0, Math.max(0, maxChars - 1)) + suffix;
392
- }
393
- // Try to find the last newline within the budget
394
- for (let i = effectiveMax - 1; i >= 0; i--) {
395
- if (text[i] === '\n') {
396
- return text.slice(0, i) + suffix;
397
- }
398
- }
399
- // Hard cut
400
- return text.slice(0, effectiveMax) + suffix;
401
- }
402
- /**
403
- * Truncate a subtitles array.
404
- *
405
- * 1. If the array has more than `maxEntries`, it is sliced to `maxEntries`.
406
- * 2. Each remaining entry that exceeds `maxChars` is individually truncated
407
- * via `truncateText`.
408
- *
409
- * Entries that already fit within both limits are left unchanged.
410
- *
411
- * @param subtitles - Array of subtitle strings.
412
- * @param maxChars - Maximum characters per entry (including suffix).
413
- * @param maxEntries - Maximum number of entries to keep.
414
- * @returns The truncated subtitles array (always a new array).
415
- */
416
- function truncateSubtitles(subtitles, maxChars, maxEntries) {
417
- // Cap entry count
418
- const capped = subtitles.length > maxEntries ? subtitles.slice(0, maxEntries) : [...subtitles];
419
- // Truncate each entry
420
- return capped.map((entry) => truncateText(entry, maxChars));
421
- }
422
- // =============================================================================
423
- // Budget profile
424
- // =============================================================================
425
- /**
426
- * Compute a budget profile based on spec and optional fragment counts.
427
- *
428
- * The profile determines how many fragments, contents, and characters the
429
- * pipeline should budget when building LLM prompts. Higher counts produce
430
- * tighter budgets to keep prompts manageable.
431
- *
432
- * Thresholds (matching `truncate.py:38-57`):
433
- *
434
- * | specCount | tier | maxFragments | maxContents | contentBudget |
435
- * |-----------|---------|--------------|-------------|---------------|
436
- * | ≤ 3 | tiny | 12 | 16 | 48000 |
437
- * | ≤ 8 | small | 10 | 14 | 40000 |
438
- * | ≤ 15 | medium | 8 | 12 | 32000 |
439
- * | ≤ 30 | large | 6 | 10 | 24000 |
440
- * | > 30 | vlarge | 0 | 0 | 16000 |
441
- *
442
- * The `vlarge` tier disables fragment/contents inclusion entirely (0 values).
443
- *
444
- * @param specCount - Number of specs in the knowledge graph.
445
- * @param fragmentCount - (Unused) reserved for future per-fragment weighting.
446
- * @returns The budget profile for the given scale.
447
- */
448
- function computeBudgetProfile(specCount, _fragmentCount) {
449
- if (specCount <= 3) {
450
- return { tier: 'tiny', maxFragments: 12, maxContents: 16, contentBudget: 48000 };
451
- }
452
- if (specCount <= 8) {
453
- return { tier: 'small', maxFragments: 10, maxContents: 14, contentBudget: 40000 };
454
- }
455
- if (specCount <= 15) {
456
- return { tier: 'medium', maxFragments: 8, maxContents: 12, contentBudget: 32000 };
457
- }
458
- if (specCount <= 30) {
459
- return { tier: 'large', maxFragments: 6, maxContents: 10, contentBudget: 24000 };
460
- }
461
- return { tier: 'vlarge', maxFragments: 0, maxContents: 0, contentBudget: 16000 };
462
- }
463
- //# sourceMappingURL=utils.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/spec/utils.ts"],"names":[],"mappings":"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"}
Binary file
Binary file
Binary file
@@ -1,60 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Add-lang benchmark for ONE repo:
3
- # clone -> wipe+index (with the homegraph on PATH) -> verify extraction ->
4
- # with/without retrieval A/B (reuses scripts/agent-eval/run-all.sh).
5
- #
6
- # Assumes the homegraph dev build is already built + linked on PATH — the skill
7
- # runs `npm run build && ./scripts/local-install.sh` ONCE before looping repos.
8
- # The A/B is skipped if extraction fails its critical checks (don't burn $ on a
9
- # broken extractor); set FORCE_AB=1 to run it anyway.
10
- #
11
- # Usage: bench.sh <lang> <repo-name> <repo-url> "<question>" [headless|tmux|all]
12
- # Env: CORPUS corpus dir (default /tmp/homegraph-corpus, shared with agent-eval)
13
- set -uo pipefail
14
-
15
- LANG_TOKEN="${1:?usage: bench.sh <lang> <repo-name> <repo-url> \"<question>\" [mode]}"
16
- NAME="${2:?repo-name required}"
17
- URL="${3:?repo-url required}"
18
- Q="${4:?question required}"
19
- MODE="${5:-headless}"
20
-
21
- HARNESS="$(cd "$(dirname "$0")" && pwd)"
22
- AGENT_EVAL="$(cd "$HARNESS/../agent-eval" && pwd)"
23
- CORPUS="${CORPUS:-/tmp/homegraph-corpus}"
24
- REPO="$CORPUS/$NAME"
25
-
26
- command -v homegraph >/dev/null || { echo "no homegraph on PATH (build + ./scripts/local-install.sh first)"; exit 1; }
27
-
28
- echo "==================== add-lang bench: $NAME ($LANG_TOKEN) ===================="
29
- echo "homegraph: $(command -v homegraph) -> $(homegraph --version 2>/dev/null || echo '?')"
30
-
31
- # 1. Ensure the repo (shallow clone, reuse if present).
32
- mkdir -p "$CORPUS"
33
- if [ -d "$REPO/.git" ]; then
34
- echo "→ reusing checkout: $REPO"
35
- else
36
- echo "→ cloning $URL"
37
- git clone --depth 1 "$URL" "$REPO" || { echo "git clone failed"; exit 1; }
38
- fi
39
-
40
- # 2. Wipe + index with the binary under test.
41
- echo "→ wiping .homegraph and indexing"
42
- rm -rf "$REPO/.homegraph"
43
- ( cd "$REPO" && homegraph init -i ) || { echo "indexing failed"; exit 1; }
44
-
45
- # 3. Verify extraction (cheap guard before the paid A/B).
46
- echo "→ verifying extraction"
47
- node "$HARNESS/verify-extraction.mjs" "$REPO" "$LANG_TOKEN"
48
- VERIFY=$?
49
-
50
- # 4. Retrieval A/B (skipped if extraction is broken, unless FORCE_AB=1).
51
- if [ "$VERIFY" -ne 0 ] && [ "${FORCE_AB:-0}" != "1" ]; then
52
- echo "→ SKIPPING A/B — extraction failed critical checks (set FORCE_AB=1 to override)"
53
- else
54
- echo "→ retrieval A/B (mode=$MODE)"
55
- bash "$AGENT_EVAL/run-all.sh" "$REPO" "$Q" "$MODE"
56
- fi
57
-
58
- echo "==================== bench complete: $NAME (verify exit=$VERIFY) ===================="
59
- # Exit reflects extraction: 0 = pass/warn, 1 = critical fail, 2 = couldn't read status.
60
- exit "$VERIFY"
@@ -1,75 +0,0 @@
1
- #!/usr/bin/env node
2
- // Verify a tree-sitter grammar wasm is HEALTHY under the project's web-tree-sitter
3
- // runtime BEFORE writing an extractor. Prints the ABI version and parses a valid
4
- // sample many times in a multi-grammar context, to catch heap-corruption bugs
5
- // that silently drop nodes on every parse after the first.
6
- //
7
- // Why this exists: the tree-sitter-wasms Lua grammar is ABI 13 and corrupts the
8
- // shared WASM heap under web-tree-sitter 0.25 — Lua extraction degraded on every
9
- // file after the first (nested calls/imports vanished). The fix was to vendor the
10
- // upstream ABI-15 wasm. Run this on any new grammar first; if it FAILs, vendor a
11
- // newer build instead of using the tree-sitter-wasms one.
12
- //
13
- // Usage: node scripts/add-lang/check-grammar.mjs <lang|wasm-path> <valid-sample> [iterations]
14
- // Exit: 0 healthy, 1 corruption / parse errors, 2 could not run.
15
- // NOTE: the sample must be SYNTACTICALLY VALID — a broken sample fails for the
16
- // wrong reason.
17
-
18
- import { readFileSync, existsSync } from 'node:fs';
19
- import { createRequire } from 'node:module';
20
- import { Parser, Language } from 'web-tree-sitter';
21
-
22
- const require = createRequire(import.meta.url);
23
- const fail = (code, msg) => { console.error(`[check-grammar] ${msg}`); process.exit(code); };
24
-
25
- const [token, sample, iterArg] = process.argv.slice(2);
26
- if (!token || !sample) fail(2, 'usage: check-grammar.mjs <lang|wasm-path> <valid-sample> [iterations]');
27
- if (!existsSync(sample)) fail(2, `sample not found: ${sample}`);
28
- const iters = iterArg ? parseInt(iterArg, 10) : 20;
29
-
30
- const SPECIAL = { csharp: 'c_sharp', 'c#': 'c_sharp' };
31
- function resolveWasm(t) {
32
- if (t.endsWith('.wasm')) return existsSync(t) ? t : fail(2, `wasm not found: ${t}`);
33
- const base = SPECIAL[t.toLowerCase()] ?? t.toLowerCase();
34
- try { return require.resolve(`tree-sitter-wasms/out/tree-sitter-${base}.wasm`); } catch { /* try vendored */ }
35
- const vendored = `src/extraction/wasm/tree-sitter-${base}.wasm`;
36
- if (existsSync(vendored)) return vendored;
37
- return fail(2, `no grammar for "${t}" — not in tree-sitter-wasms and not vendored`);
38
- }
39
-
40
- const wasmPath = resolveWasm(token);
41
- const source = readFileSync(sample, 'utf8');
42
-
43
- try { await Parser.init(); }
44
- catch { await Parser.init({ locateFile: () => require.resolve('web-tree-sitter/tree-sitter.wasm') }); }
45
-
46
- // Load a second, known-good grammar — the corruption surfaces under the
47
- // multi-grammar runtime that real indexing uses, not a single grammar in isolation.
48
- try { await Language.load(require.resolve('tree-sitter-wasms/out/tree-sitter-python.wasm')); } catch { /* ok */ }
49
-
50
- let language;
51
- try { language = await Language.load(wasmPath); }
52
- catch (e) { fail(2, `failed to load ${wasmPath}: ${e.message}`); }
53
-
54
- const parser = new Parser();
55
- parser.setLanguage(language);
56
-
57
- let ok = 0, err = 0;
58
- for (let i = 0; i < iters; i++) {
59
- const tree = parser.parse(source);
60
- if (tree.rootNode.hasError) err++; else ok++;
61
- }
62
-
63
- console.log(`grammar: ${wasmPath.split('/').pop()}`);
64
- console.log(` ABI version: ${language.abiVersion}`);
65
- console.log(` parses: ${ok} clean / ${err} with errors (of ${iters})`);
66
- if (err > 0) {
67
- console.log(
68
- `RESULT: FAIL — ${err}/${iters} parses produced ERROR trees on a valid sample. ` +
69
- `This grammar corrupts under web-tree-sitter; vendor a newer (ABI 14/15) wasm ` +
70
- `(see SKILL.md "Find a grammar"). Confirm your sample is syntactically valid first.`
71
- );
72
- process.exit(1);
73
- }
74
- console.log('RESULT: PASS — grammar parses cleanly and reuses safely.');
75
- process.exit(0);