homegraph 1.1.2 → 1.1.3

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 (175) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +243 -243
  3. package/dist/bin/homegraph.js +371 -9
  4. package/dist/bin/homegraph.js.map +1 -1
  5. package/dist/db/migrations.js +18 -18
  6. package/dist/db/queries.js +140 -140
  7. package/dist/db/schema.sql +152 -152
  8. package/dist/directory.js +5 -5
  9. package/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  10. package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  11. package/dist/installer/instructions-template.js +9 -9
  12. package/dist/installer/targets/shared.d.ts +5 -6
  13. package/dist/installer/targets/shared.d.ts.map +1 -1
  14. package/dist/installer/targets/shared.js +5 -6
  15. package/dist/installer/targets/shared.js.map +1 -1
  16. package/dist/mcp/liveness-watchdog.js +16 -16
  17. package/dist/mcp/server-instructions.js +69 -69
  18. package/dist/mcp/tools.d.ts +17 -4
  19. package/dist/mcp/tools.d.ts.map +1 -1
  20. package/dist/mcp/tools.js +192 -22
  21. package/dist/mcp/tools.js.map +1 -1
  22. package/dist/reasoning/reasoner.js +32 -32
  23. package/dist/spec/config.d.ts +43 -0
  24. package/dist/spec/config.d.ts.map +1 -0
  25. package/dist/spec/config.js +275 -0
  26. package/dist/spec/config.js.map +1 -0
  27. package/dist/spec/db/commit-node.d.ts +23 -0
  28. package/dist/spec/db/commit-node.d.ts.map +1 -0
  29. package/dist/spec/db/commit-node.js +62 -0
  30. package/dist/spec/db/commit-node.js.map +1 -0
  31. package/dist/spec/db/fragment-node.d.ts +23 -0
  32. package/dist/spec/db/fragment-node.d.ts.map +1 -0
  33. package/dist/spec/db/fragment-node.js +120 -0
  34. package/dist/spec/db/fragment-node.js.map +1 -0
  35. package/dist/spec/db/fts.d.ts +60 -0
  36. package/dist/spec/db/fts.d.ts.map +1 -0
  37. package/dist/spec/db/fts.js +285 -0
  38. package/dist/spec/db/fts.js.map +1 -0
  39. package/dist/spec/db/index.d.ts +13 -0
  40. package/dist/spec/db/index.d.ts.map +1 -0
  41. package/dist/spec/db/index.js +50 -0
  42. package/dist/spec/db/index.js.map +1 -0
  43. package/dist/spec/db/relations.d.ts +55 -0
  44. package/dist/spec/db/relations.d.ts.map +1 -0
  45. package/dist/spec/db/relations.js +158 -0
  46. package/dist/spec/db/relations.js.map +1 -0
  47. package/dist/spec/db/schema.d.ts +30 -0
  48. package/dist/spec/db/schema.d.ts.map +1 -0
  49. package/dist/spec/db/schema.js +87 -0
  50. package/dist/spec/db/schema.js.map +1 -0
  51. package/dist/spec/db/schema.sql +107 -0
  52. package/dist/spec/db/spec-node.d.ts +41 -0
  53. package/dist/spec/db/spec-node.d.ts.map +1 -0
  54. package/dist/spec/db/spec-node.js +114 -0
  55. package/dist/spec/db/spec-node.js.map +1 -0
  56. package/dist/spec/evolve/impact-locator.d.ts +13 -0
  57. package/dist/spec/evolve/impact-locator.d.ts.map +1 -0
  58. package/dist/spec/evolve/impact-locator.js +25 -0
  59. package/dist/spec/evolve/impact-locator.js.map +1 -0
  60. package/dist/spec/evolve/llm-client.d.ts +50 -0
  61. package/dist/spec/evolve/llm-client.d.ts.map +1 -0
  62. package/dist/spec/evolve/llm-client.js +176 -0
  63. package/dist/spec/evolve/llm-client.js.map +1 -0
  64. package/dist/spec/evolve/logic-checker.d.ts +12 -0
  65. package/dist/spec/evolve/logic-checker.d.ts.map +1 -0
  66. package/dist/spec/evolve/logic-checker.js +48 -0
  67. package/dist/spec/evolve/logic-checker.js.map +1 -0
  68. package/dist/spec/evolve/pipeline.d.ts +40 -0
  69. package/dist/spec/evolve/pipeline.d.ts.map +1 -0
  70. package/dist/spec/evolve/pipeline.js +314 -0
  71. package/dist/spec/evolve/pipeline.js.map +1 -0
  72. package/dist/spec/evolve/spec-rewriter.d.ts +42 -0
  73. package/dist/spec/evolve/spec-rewriter.d.ts.map +1 -0
  74. package/dist/spec/evolve/spec-rewriter.js +254 -0
  75. package/dist/spec/evolve/spec-rewriter.js.map +1 -0
  76. package/dist/spec/graph/queries.d.ts +58 -0
  77. package/dist/spec/graph/queries.d.ts.map +1 -0
  78. package/dist/spec/graph/queries.js +215 -0
  79. package/dist/spec/graph/queries.js.map +1 -0
  80. package/dist/spec/mining/diff-parser.d.ts +33 -0
  81. package/dist/spec/mining/diff-parser.d.ts.map +1 -0
  82. package/dist/spec/mining/diff-parser.js +166 -0
  83. package/dist/spec/mining/diff-parser.js.map +1 -0
  84. package/dist/spec/mining/git-scanner.d.ts +91 -0
  85. package/dist/spec/mining/git-scanner.d.ts.map +1 -0
  86. package/dist/spec/mining/git-scanner.js +266 -0
  87. package/dist/spec/mining/git-scanner.js.map +1 -0
  88. package/dist/spec/mining/pipeline.d.ts +53 -0
  89. package/dist/spec/mining/pipeline.d.ts.map +1 -0
  90. package/dist/spec/mining/pipeline.js +165 -0
  91. package/dist/spec/mining/pipeline.js.map +1 -0
  92. package/dist/spec/mining/scope-resolver.d.ts +45 -0
  93. package/dist/spec/mining/scope-resolver.d.ts.map +1 -0
  94. package/dist/spec/mining/scope-resolver.js +103 -0
  95. package/dist/spec/mining/scope-resolver.js.map +1 -0
  96. package/dist/spec/mining/spec-extractor.d.ts +69 -0
  97. package/dist/spec/mining/spec-extractor.d.ts.map +1 -0
  98. package/dist/spec/mining/spec-extractor.js +369 -0
  99. package/dist/spec/mining/spec-extractor.js.map +1 -0
  100. package/dist/spec/types.d.ts +149 -0
  101. package/dist/spec/types.d.ts.map +1 -0
  102. package/dist/spec/types.js +15 -0
  103. package/dist/spec/types.js.map +1 -0
  104. package/dist/spec/utils.d.ts +166 -0
  105. package/dist/spec/utils.d.ts.map +1 -0
  106. package/dist/spec/utils.js +461 -0
  107. package/dist/spec/utils.js.map +1 -0
  108. package/package.json +58 -57
  109. package/scripts/add-lang/bench.sh +60 -60
  110. package/scripts/add-lang/check-grammar.mjs +75 -75
  111. package/scripts/add-lang/dump-ast.mjs +103 -103
  112. package/scripts/add-lang/verify-extraction.mjs +70 -70
  113. package/scripts/agent-eval/ab-adoption.sh +91 -91
  114. package/scripts/agent-eval/ab-hook.sh +86 -86
  115. package/scripts/agent-eval/ab-impl.sh +78 -78
  116. package/scripts/agent-eval/ab-new-vs-baseline.sh +102 -102
  117. package/scripts/agent-eval/ab-sufficiency.sh +78 -78
  118. package/scripts/agent-eval/arms-F.sh +21 -21
  119. package/scripts/agent-eval/arms-matrix.sh +37 -37
  120. package/scripts/agent-eval/audit.sh +68 -68
  121. package/scripts/agent-eval/bench-readme.sh +28 -28
  122. package/scripts/agent-eval/bench-why-repo.sh +22 -22
  123. package/scripts/agent-eval/block-read-hook.sh +19 -19
  124. package/scripts/agent-eval/hook-settings.json +15 -15
  125. package/scripts/agent-eval/itrun.sh +120 -120
  126. package/scripts/agent-eval/offload-eval-3arm.sh +72 -72
  127. package/scripts/agent-eval/offload-eval-cost.mjs +133 -133
  128. package/scripts/agent-eval/offload-eval-effort.mjs +108 -108
  129. package/scripts/agent-eval/offload-eval-frontload-matrix.sh +25 -25
  130. package/scripts/agent-eval/offload-eval-frontload.sh +47 -47
  131. package/scripts/agent-eval/offload-eval-ground-truth.json +18 -18
  132. package/scripts/agent-eval/offload-eval-hook.mjs +84 -84
  133. package/scripts/agent-eval/offload-eval-judge.mjs +103 -103
  134. package/scripts/agent-eval/offload-eval-matrix.sh +20 -20
  135. package/scripts/agent-eval/offload-eval-metrics.mjs +94 -94
  136. package/scripts/agent-eval/offload-eval-refs1.sh +50 -50
  137. package/scripts/agent-eval/offload-eval-setup.sh +24 -24
  138. package/scripts/agent-eval/offload-eval-styles.sh +71 -71
  139. package/scripts/agent-eval/offload-eval-summarize.mjs +68 -68
  140. package/scripts/agent-eval/offload-eval.md +76 -76
  141. package/scripts/agent-eval/parse-arms.mjs +116 -116
  142. package/scripts/agent-eval/parse-bench-readme.mjs +84 -84
  143. package/scripts/agent-eval/parse-run.mjs +45 -45
  144. package/scripts/agent-eval/parse-session.mjs +93 -93
  145. package/scripts/agent-eval/probe-context.mjs +21 -21
  146. package/scripts/agent-eval/probe-explore.mjs +40 -40
  147. package/scripts/agent-eval/probe-node.mjs +20 -20
  148. package/scripts/agent-eval/probe-sweep.mjs +119 -119
  149. package/scripts/agent-eval/probe-trace.mjs +20 -20
  150. package/scripts/agent-eval/redirect-read-hook.sh +38 -38
  151. package/scripts/agent-eval/run-agent.sh +34 -34
  152. package/scripts/agent-eval/run-all.sh +69 -69
  153. package/scripts/agent-eval/run-arms.sh +56 -56
  154. package/scripts/agent-eval/seq-matrix.mjs +137 -137
  155. package/scripts/build-bundle.sh +118 -118
  156. package/scripts/extract-release-notes.mjs +130 -130
  157. package/scripts/local-install.sh +41 -41
  158. package/scripts/npm-sdk.js +75 -75
  159. package/scripts/npm-shim.js +246 -246
  160. package/scripts/pack-npm.sh +119 -119
  161. package/scripts/prepare-release.mjs +270 -270
  162. package/scripts/qa_eval/README.md +407 -404
  163. package/scripts/qa_eval/_test_deveco_probe.py +41 -41
  164. package/scripts/qa_eval/agent_runner.py +526 -526
  165. package/scripts/qa_eval/data/.gitignore +4 -4
  166. package/scripts/qa_eval/data/test-set.jsonl +2 -22
  167. package/scripts/qa_eval/eval_metrics.py +274 -233
  168. package/scripts/qa_eval/external_agent.py +976 -671
  169. package/scripts/qa_eval/llm_config.py +92 -92
  170. package/scripts/qa_eval/memory_monitor.py +132 -132
  171. package/scripts/qa_eval/my_answer_accuracy.py +187 -187
  172. package/scripts/qa_eval/requirements.txt +2 -2
  173. package/scripts/qa_eval/run_pipeline.py +804 -711
  174. package/scripts/qa_eval/stats_efficiency.py +279 -279
  175. package/scripts/qa_eval/stats_scores.py +207 -207
@@ -0,0 +1,42 @@
1
+ import { SqliteDatabase } from '../../db/sqlite-adapter';
2
+ import { LLMClient } from './llm-client';
3
+ export interface EvolveDecision {
4
+ action: 'UPDATE' | 'DEPRECATE' | 'UNCHANGED';
5
+ title?: string;
6
+ subtitles?: string[];
7
+ plan_content?: string;
8
+ }
9
+ /**
10
+ * Ask the LLM whether a commit requires updating, deprecating, or leaving
11
+ * a spec unchanged.
12
+ *
13
+ * @param specId - The spec identifier (for logging / context).
14
+ * @param specStoragePath - Root spec storage directory.
15
+ * @param specFilePath - Absolute path to the plan.md file.
16
+ * @param commitMessage - First line of the commit message.
17
+ * @param commitDiff - Full unified diff of the commit.
18
+ * @param scheduleNextSpecs - Remaining spec IDs to be processed (for context).
19
+ * @param client - Optional LLM client; if absent, returns UNCHANGED.
20
+ */
21
+ export declare function evaluateSpec(specId: string, _specStoragePath: string, specFilePath: string, commitMessage: string, commitDiff: string, scheduleNextSpecs: string[], client?: LLMClient): Promise<EvolveDecision>;
22
+ /**
23
+ * Apply an UPDATE decision: backup the old plan, write the new plan, version
24
+ * the spec in the database, and transfer relations.
25
+ *
26
+ * 10-step process (ported from spec_rewriter.py:79-178).
27
+ *
28
+ * @returns The new spec ID (same as old) and bumped version number.
29
+ */
30
+ export declare function applyUpdate(db: SqliteDatabase, specStoragePath: string, oldSpecId: string, oldFilePath: string, oldVersion: number, decision: EvolveDecision, commitHash: string): {
31
+ newSpecId: string;
32
+ newVersion: number;
33
+ };
34
+ /**
35
+ * Apply a DEPRECATE decision: mark the spec as deprecated, create a
36
+ * deprecated target node, create the EVOLVED_FROM relation, and clean
37
+ * SIMILAR_TO relations.
38
+ *
39
+ * Ported from spec_rewriter.py:181-231.
40
+ */
41
+ export declare function applyDeprecate(db: SqliteDatabase, oldSpecId: string): void;
42
+ //# sourceMappingURL=spec-rewriter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-rewriter.d.ts","sourceRoot":"","sources":["../../../src/spec/evolve/spec-rewriter.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAgBzD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAOzC,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAMD;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,EACxB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,MAAM,EAAE,EAC3B,MAAM,CAAC,EAAE,SAAS,GACjB,OAAO,CAAC,cAAc,CAAC,CA2EzB;AAMD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,EAAE,EAAE,cAAc,EAClB,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,EACxB,UAAU,EAAE,MAAM,GACjB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAmF3C;AAMD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,GAChB,IAAI,CAqBN"}
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.evaluateSpec = evaluateSpec;
37
+ exports.applyUpdate = applyUpdate;
38
+ exports.applyDeprecate = applyDeprecate;
39
+ /**
40
+ * Spec rewriter — evaluates a commit's impact on a design spec and applies
41
+ * UPDATE / DEPRECATE decisions.
42
+ *
43
+ * Replaces `commit4spec/self_evolve/spec_rewriter.py` (lines 1-231).
44
+ *
45
+ * @module spec/evolve/spec-rewriter
46
+ */
47
+ const fs = __importStar(require("fs"));
48
+ const utils_1 = require("../utils");
49
+ const spec_node_1 = require("../db/spec-node");
50
+ const relations_1 = require("../db/relations");
51
+ const spec_extractor_1 = require("../mining/spec-extractor");
52
+ const errors_1 = require("../../errors");
53
+ // =============================================================================
54
+ // evaluateSpec
55
+ // =============================================================================
56
+ /**
57
+ * Ask the LLM whether a commit requires updating, deprecating, or leaving
58
+ * a spec unchanged.
59
+ *
60
+ * @param specId - The spec identifier (for logging / context).
61
+ * @param specStoragePath - Root spec storage directory.
62
+ * @param specFilePath - Absolute path to the plan.md file.
63
+ * @param commitMessage - First line of the commit message.
64
+ * @param commitDiff - Full unified diff of the commit.
65
+ * @param scheduleNextSpecs - Remaining spec IDs to be processed (for context).
66
+ * @param client - Optional LLM client; if absent, returns UNCHANGED.
67
+ */
68
+ async function evaluateSpec(specId, _specStoragePath, specFilePath, commitMessage, commitDiff, scheduleNextSpecs, client) {
69
+ // 1. Read plan content
70
+ const planContent = (0, utils_1.readFileContent)(specFilePath);
71
+ if (planContent === null) {
72
+ (0, errors_1.logDebug)('evaluateSpec: plan file not found, returning UNCHANGED', {
73
+ specId,
74
+ specFilePath,
75
+ });
76
+ return { action: 'UNCHANGED' };
77
+ }
78
+ // 2. Truncate commitDiff to 6000 chars
79
+ const truncatedDiff = (0, utils_1.truncateText)(commitDiff, 6000);
80
+ // 3. Build system prompt (EXACT from spec_rewriter.py:238-245)
81
+ const systemPrompt = `You are a technical documentation maintainer. Your task is to evaluate whether a git commit requires updating a software design specification (plan.md).
82
+
83
+ Given the current plan content, the commit message, and the code diff, determine:
84
+ 1. Whether the plan needs to be updated (UPDATE), deprecated (DEPRECATE), or left unchanged (UNCHANGED).
85
+ 2. If UPDATE: provide the new title, subtitles (as an array of heading-preview strings), and full rewritten plan_content.
86
+ 3. If DEPRECATE: provide a brief explanation in the plan_content field.
87
+
88
+ Response format (JSON):
89
+ {
90
+ "action": "UPDATE" | "DEPRECATE" | "UNCHANGED",
91
+ "title": "New spec title (for UPDATE)",
92
+ "subtitles": ["heading1 → heading2 - preview", ...],
93
+ "plan_content": "Full rewritten markdown content (for UPDATE) or deprecation reason (for DEPRECATE)"
94
+ }`;
95
+ // 4. Build user prompt (EXACT from spec_rewriter.py:247-280)
96
+ const scheduleStr = scheduleNextSpecs.length > 0 ? scheduleNextSpecs.join(', ') : 'none';
97
+ const userPrompt = `Current Plan Content:
98
+ ${planContent}
99
+
100
+ Commit Message:
101
+ ${commitMessage}
102
+
103
+ Code Diff:
104
+ ${truncatedDiff}
105
+
106
+ Scheduled specs to be processed next (for context): ${scheduleStr}`;
107
+ // 5. Call LLM
108
+ if (!client) {
109
+ (0, errors_1.logDebug)('evaluateSpec: no LLM client, returning UNCHANGED', { specId });
110
+ return { action: 'UNCHANGED' };
111
+ }
112
+ const result = await client.chatJson(systemPrompt, userPrompt);
113
+ // 6. Validate action
114
+ const rawAction = result.action;
115
+ let action = 'UNCHANGED';
116
+ if (rawAction === 'UPDATE' || rawAction === 'DEPRECATE' || rawAction === 'UNCHANGED') {
117
+ action = rawAction;
118
+ }
119
+ else {
120
+ (0, errors_1.logWarn)('evaluateSpec: unknown action from LLM, defaulting to UNCHANGED', {
121
+ specId,
122
+ rawAction: String(rawAction),
123
+ });
124
+ }
125
+ // 7. Return EvolveDecision
126
+ return {
127
+ action,
128
+ title: typeof result.title === 'string' ? result.title : undefined,
129
+ subtitles: Array.isArray(result.subtitles)
130
+ ? result.subtitles.filter((s) => typeof s === 'string')
131
+ : undefined,
132
+ plan_content: typeof result.plan_content === 'string' ? result.plan_content : undefined,
133
+ };
134
+ }
135
+ // =============================================================================
136
+ // applyUpdate
137
+ // =============================================================================
138
+ /**
139
+ * Apply an UPDATE decision: backup the old plan, write the new plan, version
140
+ * the spec in the database, and transfer relations.
141
+ *
142
+ * 10-step process (ported from spec_rewriter.py:79-178).
143
+ *
144
+ * @returns The new spec ID (same as old) and bumped version number.
145
+ */
146
+ function applyUpdate(db, specStoragePath, oldSpecId, oldFilePath, oldVersion, decision, commitHash) {
147
+ // ---- Step 1: Write new plan content to temp file ----
148
+ // We write to a temp file first so the original is preserved until every
149
+ // DB operation succeeds. If the caller's transaction rolls back, the
150
+ // temp file is harmless garbage — the original plan.md is untouched.
151
+ const tmpPath = oldFilePath + '.new';
152
+ (0, utils_1.writeFileContent)(tmpPath, decision.plan_content || '');
153
+ // ---- Step 2: Extract new metadata from the temp file ----
154
+ const metadata = (0, spec_extractor_1.extractSpecMetadata)(specStoragePath, oldSpecId);
155
+ const metadataTitle = metadata?.title;
156
+ const metadataSubtitles = metadata?.subtitles;
157
+ // ---- Step 3: Read old spec data ----
158
+ const oldSpec = (0, spec_node_1.findSpecById)(db, oldSpecId);
159
+ if (!oldSpec) {
160
+ (0, errors_1.logDebug)('applyUpdate: old spec not found in DB, returning early', {
161
+ oldSpecId,
162
+ });
163
+ // Clean up the temp file — no DB changes were made.
164
+ try {
165
+ fs.unlinkSync(tmpPath);
166
+ }
167
+ catch { /* best-effort */ }
168
+ return { newSpecId: oldSpecId, newVersion: oldVersion + 1 };
169
+ }
170
+ // ---- Step 4: Insert deprecated record ----
171
+ const bakPath = oldFilePath + '.bak';
172
+ const deprecatedId = `${oldSpecId}_v${oldVersion}`;
173
+ (0, spec_node_1.insertSpecNode)(db, {
174
+ id: deprecatedId,
175
+ title: oldSpec.title,
176
+ subtitles: oldSpec.subtitles,
177
+ status: 'deprecated',
178
+ version: oldVersion,
179
+ filePath: bakPath,
180
+ timestamp: Date.now(),
181
+ });
182
+ // ---- Step 5: Transfer relations from old active to deprecated ----
183
+ (0, relations_1.transferSpecCommitRelations)(db, oldSpecId, deprecatedId);
184
+ (0, relations_1.transferSpecSpecRelations)(db, oldSpecId, deprecatedId);
185
+ // ---- Step 6: Delete old active spec ----
186
+ (0, spec_node_1.deleteSpec)(db, oldSpecId);
187
+ // ---- Step 7: Insert new active spec ----
188
+ const newVersion = oldVersion + 1;
189
+ (0, spec_node_1.insertSpecNode)(db, {
190
+ id: oldSpecId,
191
+ title: decision.title || metadataTitle || oldSpecId,
192
+ subtitles: decision.subtitles || metadataSubtitles || [],
193
+ status: 'active',
194
+ version: newVersion,
195
+ filePath: oldFilePath,
196
+ timestamp: Date.now(),
197
+ });
198
+ // ---- Step 8: Create EVOLVED_FROM relation ----
199
+ (0, relations_1.insertSpecSpecRelation)(db, oldSpecId, deprecatedId, 'EVOLVED_FROM');
200
+ // ---- Step 9: Create GENERATE relation ----
201
+ (0, relations_1.insertSpecCommitRelation)(db, oldSpecId, commitHash, 'GENERATE');
202
+ // ---- Step 10: File system finalisation ----
203
+ // Only now — after all DB ops succeeded — do we touch the original disk
204
+ // files. rename() is atomic on the same filesystem.
205
+ try {
206
+ fs.renameSync(oldFilePath, bakPath);
207
+ }
208
+ catch {
209
+ (0, errors_1.logDebug)('applyUpdate: backup rename failed (file may be missing), continuing', {
210
+ oldFilePath,
211
+ bakPath,
212
+ });
213
+ }
214
+ try {
215
+ fs.renameSync(tmpPath, oldFilePath);
216
+ }
217
+ catch {
218
+ (0, errors_1.logDebug)('applyUpdate: temp rename failed, leaving .new in place', {
219
+ tmpPath,
220
+ oldFilePath,
221
+ });
222
+ }
223
+ return { newSpecId: oldSpecId, newVersion };
224
+ }
225
+ // =============================================================================
226
+ // applyDeprecate
227
+ // =============================================================================
228
+ /**
229
+ * Apply a DEPRECATE decision: mark the spec as deprecated, create a
230
+ * deprecated target node, create the EVOLVED_FROM relation, and clean
231
+ * SIMILAR_TO relations.
232
+ *
233
+ * Ported from spec_rewriter.py:181-231.
234
+ */
235
+ function applyDeprecate(db, oldSpecId) {
236
+ // 1. Mark the spec as deprecated
237
+ (0, spec_node_1.updateSpecStatus)(db, oldSpecId, 'deprecated');
238
+ // 2. Create deprecated target node
239
+ const deprecatedTargetId = `${oldSpecId}_deprecated`;
240
+ (0, spec_node_1.insertSpecNode)(db, {
241
+ id: deprecatedTargetId,
242
+ status: 'deprecated',
243
+ version: 1,
244
+ title: oldSpecId,
245
+ subtitles: [],
246
+ filePath: '', // Synthetic deprecated node — no on-disk file.
247
+ timestamp: Date.now(),
248
+ });
249
+ // 3. Create EVOLVED_FROM relation
250
+ (0, relations_1.insertSpecSpecRelation)(db, oldSpecId, deprecatedTargetId, 'EVOLVED_FROM');
251
+ // 4. Clean SIMILAR_TO relations
252
+ (0, relations_1.deleteSimilarToRelations)(db, oldSpecId);
253
+ }
254
+ //# sourceMappingURL=spec-rewriter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-rewriter.js","sourceRoot":"","sources":["../../../src/spec/evolve/spec-rewriter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Graph traversal queries for the Spec→Commit→CodeFragment knowledge graph.
3
+ *
4
+ * Replaces `commit4spec/graph/queries.py`. Provides spec context retrieval,
5
+ * stats, and search+traverse functionality.
6
+ */
7
+ import { SqliteDatabase } from '../../db/sqlite-adapter';
8
+ import { SpecContext, SpecStats } from '../types';
9
+ /**
10
+ * Retrieve full context for a Spec — the spec node itself, all linked
11
+ * commits (with their relation types), and optionally the code fragments
12
+ * each commit touched.
13
+ *
14
+ * Commits are ordered by relation-type priority (GENERATE first, then
15
+ * SUMMARIZED_FROM, then others) and then by timestamp descending within
16
+ * each priority tier.
17
+ *
18
+ * @param db Active SQLite database handle.
19
+ * @param specId Spec identifier to look up.
20
+ * @param maxCommits Maximum number of linked commits to return (default 5).
21
+ * @param includeFragments Whether to load code fragments for each commit (default true).
22
+ * @returns SpecContext on success, or null when the spec is not found.
23
+ */
24
+ export declare function getSpecContext(db: SqliteDatabase, specId: string, maxCommits?: number, includeFragments?: boolean): SpecContext | null;
25
+ /**
26
+ * Find all spec IDs related to code fragments whose file path matches
27
+ * the given substring.
28
+ *
29
+ * @param db Active SQLite database handle.
30
+ * @param filePath File path substring to search for (LIKE-wrapped).
31
+ * @returns Array of distinct spec IDs.
32
+ */
33
+ export declare function findSpecsByFragmentPath(db: SqliteDatabase, filePath: string): string[];
34
+ /**
35
+ * Compute statistics about the Spec knowledge graph.
36
+ *
37
+ * @param db Active SQLite database handle.
38
+ * @returns SpecStats with entity counts and relation totals.
39
+ */
40
+ export declare function getSpecStats(db: SqliteDatabase): SpecStats;
41
+ /**
42
+ * Full-text search for specs and return full context for each match.
43
+ *
44
+ * Combines FTS5 + LIKE fallback search with graph traversal so callers
45
+ * get everything in one call: spec metadata, linked commits, and
46
+ * optionally code fragments.
47
+ *
48
+ * Results are sorted by `_score` descending, then by spec timestamp
49
+ * descending as a tiebreaker.
50
+ *
51
+ * @param db Active SQLite database handle.
52
+ * @param query Search query string.
53
+ * @param topK Maximum number of results to return (default 10).
54
+ * @param includeFragments Whether to load code fragments per commit (default true).
55
+ * @returns Array of SpecContext (null results filtered out).
56
+ */
57
+ export declare function searchAndGetContext(db: SqliteDatabase, query: string, topK?: number, includeFragments?: boolean): SpecContext[];
58
+ //# sourceMappingURL=queries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../../src/spec/graph/queries.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAIL,WAAW,EAEX,SAAS,EACV,MAAM,UAAU,CAAC;AAoElB;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,cAAc,EAClB,MAAM,EAAE,MAAM,EACd,UAAU,GAAE,MAAU,EACtB,gBAAgB,GAAE,OAAc,GAC/B,WAAW,GAAG,IAAI,CAyEpB;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,GACf,MAAM,EAAE,CAaV;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,cAAc,GAAG,SAAS,CAqD1D;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,MAAW,EACjB,gBAAgB,GAAE,OAAc,GAC/B,WAAW,EAAE,CAwBf"}
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+ /**
3
+ * Graph traversal queries for the Spec→Commit→CodeFragment knowledge graph.
4
+ *
5
+ * Replaces `commit4spec/graph/queries.py`. Provides spec context retrieval,
6
+ * stats, and search+traverse functionality.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.getSpecContext = getSpecContext;
10
+ exports.findSpecsByFragmentPath = findSpecsByFragmentPath;
11
+ exports.getSpecStats = getSpecStats;
12
+ exports.searchAndGetContext = searchAndGetContext;
13
+ const fts_1 = require("../db/fts");
14
+ // ===========================================================================
15
+ // Private helpers
16
+ // ===========================================================================
17
+ /**
18
+ * Parse a JSON string into a string array, returning [] on any failure.
19
+ */
20
+ function _parseSubtitles(raw) {
21
+ try {
22
+ return JSON.parse(raw || '[]');
23
+ }
24
+ catch {
25
+ return [];
26
+ }
27
+ }
28
+ /**
29
+ * Convert a DB row (snake_case keys) to a SpecNode (camelCase).
30
+ */
31
+ function _rowToSpecNode(row) {
32
+ return {
33
+ id: row.id,
34
+ title: row.title,
35
+ subtitles: _parseSubtitles(row.subtitles),
36
+ status: row.status,
37
+ version: row.version,
38
+ filePath: row.file_path,
39
+ timestamp: row.timestamp,
40
+ };
41
+ }
42
+ // ===========================================================================
43
+ // Public API
44
+ // ===========================================================================
45
+ /**
46
+ * Retrieve full context for a Spec — the spec node itself, all linked
47
+ * commits (with their relation types), and optionally the code fragments
48
+ * each commit touched.
49
+ *
50
+ * Commits are ordered by relation-type priority (GENERATE first, then
51
+ * SUMMARIZED_FROM, then others) and then by timestamp descending within
52
+ * each priority tier.
53
+ *
54
+ * @param db Active SQLite database handle.
55
+ * @param specId Spec identifier to look up.
56
+ * @param maxCommits Maximum number of linked commits to return (default 5).
57
+ * @param includeFragments Whether to load code fragments for each commit (default true).
58
+ * @returns SpecContext on success, or null when the spec is not found.
59
+ */
60
+ function getSpecContext(db, specId, maxCommits = 5, includeFragments = true) {
61
+ // 1. Look up the spec by id
62
+ const specRow = db
63
+ .prepare(`SELECT id, title, subtitles, status, version, file_path, timestamp
64
+ FROM spec_nodes
65
+ WHERE id = ?`)
66
+ .get(specId);
67
+ if (!specRow)
68
+ return null;
69
+ const spec = _rowToSpecNode(specRow);
70
+ // 2. Query linked commits with priority ordering
71
+ const commitRows = db
72
+ .prepare(`SELECT c.hash, c.message, c.author, c.timestamp, r.relation_type
73
+ FROM spec_commit_relations r
74
+ JOIN commit_nodes c ON c.hash = r.commit_hash
75
+ WHERE r.spec_id = ?
76
+ ORDER BY
77
+ CASE r.relation_type
78
+ WHEN 'GENERATE' THEN 0
79
+ WHEN 'SUMMARIZED_FROM' THEN 1
80
+ ELSE 2
81
+ END,
82
+ c.timestamp DESC
83
+ LIMIT ?`)
84
+ .all(specId, maxCommits);
85
+ // 3. Build commit contexts (with optional fragments)
86
+ const commits = [];
87
+ for (const crow of commitRows) {
88
+ const commit = {
89
+ hash: crow.hash,
90
+ message: crow.message,
91
+ author: crow.author,
92
+ timestamp: crow.timestamp,
93
+ };
94
+ let fragments = [];
95
+ if (includeFragments) {
96
+ const fragRows = db
97
+ .prepare(`SELECT f.id, f.change_type, f.file_path, f.start_line,
98
+ f.end_line, f.code_diff
99
+ FROM commit_fragment_relations r
100
+ JOIN code_fragment_nodes f ON f.id = r.fragment_id
101
+ WHERE r.commit_hash = ?
102
+ ORDER BY f.file_path, f.start_line`)
103
+ .all(crow.hash);
104
+ fragments = fragRows.map((fr) => ({
105
+ id: fr.id,
106
+ changeType: fr.change_type,
107
+ filePath: fr.file_path,
108
+ startLine: fr.start_line,
109
+ endLine: fr.end_line,
110
+ codeDiff: fr.code_diff,
111
+ }));
112
+ }
113
+ commits.push({
114
+ commit,
115
+ relationType: crow.relation_type,
116
+ fragments,
117
+ });
118
+ }
119
+ return { spec, commits };
120
+ }
121
+ /**
122
+ * Find all spec IDs related to code fragments whose file path matches
123
+ * the given substring.
124
+ *
125
+ * @param db Active SQLite database handle.
126
+ * @param filePath File path substring to search for (LIKE-wrapped).
127
+ * @returns Array of distinct spec IDs.
128
+ */
129
+ function findSpecsByFragmentPath(db, filePath) {
130
+ const pattern = `%${filePath}%`;
131
+ const rows = db
132
+ .prepare(`SELECT DISTINCT r.spec_id
133
+ FROM spec_commit_relations r
134
+ JOIN commit_fragment_relations cfr ON cfr.commit_hash = r.commit_hash
135
+ JOIN code_fragment_nodes cf ON cf.id = cfr.fragment_id
136
+ WHERE cf.file_path LIKE ?`)
137
+ .all(pattern);
138
+ return rows.map((r) => r.spec_id);
139
+ }
140
+ /**
141
+ * Compute statistics about the Spec knowledge graph.
142
+ *
143
+ * @param db Active SQLite database handle.
144
+ * @returns SpecStats with entity counts and relation totals.
145
+ */
146
+ function getSpecStats(db) {
147
+ const specCount = db.prepare('SELECT COUNT(*) as cnt FROM spec_nodes').get().cnt;
148
+ const commitCount = db.prepare('SELECT COUNT(*) as cnt FROM commit_nodes').get().cnt;
149
+ const fragmentCount = db
150
+ .prepare('SELECT COUNT(*) as cnt FROM code_fragment_nodes')
151
+ .get().cnt;
152
+ const scrCount = db
153
+ .prepare('SELECT COUNT(*) as cnt FROM spec_commit_relations')
154
+ .get().cnt;
155
+ const cfrCount = db
156
+ .prepare('SELECT COUNT(*) as cnt FROM commit_fragment_relations')
157
+ .get().cnt;
158
+ const ssrCount = db
159
+ .prepare('SELECT COUNT(*) as cnt FROM spec_spec_relations')
160
+ .get().cnt;
161
+ const activeSpecCount = db
162
+ .prepare("SELECT COUNT(*) as cnt FROM spec_nodes WHERE status = 'active'")
163
+ .get().cnt;
164
+ const deprecatedSpecCount = db
165
+ .prepare("SELECT COUNT(*) as cnt FROM spec_nodes WHERE status = 'deprecated'")
166
+ .get().cnt;
167
+ return {
168
+ specCount,
169
+ commitCount,
170
+ fragmentCount,
171
+ relationCount: scrCount + cfrCount + ssrCount,
172
+ activeSpecCount,
173
+ deprecatedSpecCount,
174
+ };
175
+ }
176
+ /**
177
+ * Full-text search for specs and return full context for each match.
178
+ *
179
+ * Combines FTS5 + LIKE fallback search with graph traversal so callers
180
+ * get everything in one call: spec metadata, linked commits, and
181
+ * optionally code fragments.
182
+ *
183
+ * Results are sorted by `_score` descending, then by spec timestamp
184
+ * descending as a tiebreaker.
185
+ *
186
+ * @param db Active SQLite database handle.
187
+ * @param query Search query string.
188
+ * @param topK Maximum number of results to return (default 10).
189
+ * @param includeFragments Whether to load code fragments per commit (default true).
190
+ * @returns Array of SpecContext (null results filtered out).
191
+ */
192
+ function searchAndGetContext(db, query, topK = 10, includeFragments = true) {
193
+ // 1. Search specs via FTS5 + LIKE fallback
194
+ const results = (0, fts_1.searchSpecs)(db, query, topK);
195
+ // 2. Get full context for each result
196
+ const scored = [];
197
+ for (const result of results) {
198
+ const context = getSpecContext(db, result.id, 5, includeFragments);
199
+ if (context !== null) {
200
+ scored.push({
201
+ score: result._score ?? 0,
202
+ timestamp: context.spec.timestamp,
203
+ context,
204
+ });
205
+ }
206
+ }
207
+ // 3. Sort by _score descending, then by spec timestamp descending
208
+ scored.sort((a, b) => {
209
+ if (b.score !== a.score)
210
+ return b.score - a.score;
211
+ return b.timestamp - a.timestamp;
212
+ });
213
+ return scored.map((c) => c.context);
214
+ }
215
+ //# sourceMappingURL=queries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.js","sourceRoot":"","sources":["../../../src/spec/graph/queries.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Unified diff parser — converts `git diff` output into structured code
3
+ * fragments.
4
+ *
5
+ * Replaces `commit4spec/reverse_engineer/change_analyzer.py`. Parses
6
+ * unified diff output and extracts file-level change information (change
7
+ * type, line range, diff text).
8
+ *
9
+ * @module spec/mining/diff-parser
10
+ */
11
+ export interface DiffFragment {
12
+ /** Path of the changed file (no `a/` / `b/` prefix). */
13
+ filePath: string;
14
+ /** Kind of change: added, modified, or deleted. */
15
+ changeType: 'ADD' | 'MODIFY' | 'DELETE';
16
+ /** First new-line number in this diff fragment (1-based). */
17
+ startLine: number;
18
+ /** Last new-line number covered by this fragment (1-based). */
19
+ endLine: number;
20
+ /** Raw unified diff text for this file. */
21
+ codeDiff: string;
22
+ }
23
+ /**
24
+ * Retrieve and parse the diff for a single commit.
25
+ *
26
+ * 1. Call `getCommitDiff(repoPath, commitHash)` to get raw diff text.
27
+ * 2. Parse the diff into structured `DiffFragment` objects.
28
+ * 3. Return the fragments.
29
+ *
30
+ * Returns an empty array when the diff cannot be retrieved or is empty.
31
+ */
32
+ export declare function analyzeCommitDiff(repoPath: string, commitHash: string, preFetchedDiff?: string): DiffFragment[];
33
+ //# sourceMappingURL=diff-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-parser.d.ts","sourceRoot":"","sources":["../../../src/spec/mining/diff-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,UAAU,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACxC,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAMD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,MAAM,GACtB,YAAY,EAAE,CAMhB"}