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,120 @@
1
+ "use strict";
2
+ /**
3
+ * CodeFragmentNode CRUD — persistence operations for ``code_fragment_nodes``.
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.insertCodeFragment = insertCodeFragment;
40
+ exports.findFragmentById = findFragmentById;
41
+ exports.findFragmentsByCommit = findFragmentsByCommit;
42
+ exports.countFragments = countFragments;
43
+ const crypto = __importStar(require("crypto"));
44
+ // ---------------------------------------------------------------------------
45
+ // ID generation
46
+ // ---------------------------------------------------------------------------
47
+ /**
48
+ * Generate a 12-character hex identifier (first 12 chars of a UUID v4).
49
+ * Matches the Python `uuid.uuid4().hex[:12]` convention from Commit4Spec.
50
+ */
51
+ function makeId() {
52
+ return crypto.randomUUID().replace(/-/g, '').slice(0, 12);
53
+ }
54
+ // ---------------------------------------------------------------------------
55
+ // Helpers
56
+ // ---------------------------------------------------------------------------
57
+ function rowToFragment(row) {
58
+ return {
59
+ id: row.id,
60
+ changeType: row.change_type,
61
+ filePath: row.file_path,
62
+ startLine: row.start_line,
63
+ endLine: row.end_line,
64
+ codeDiff: row.code_diff,
65
+ };
66
+ }
67
+ // ---------------------------------------------------------------------------
68
+ // Persistence
69
+ // ---------------------------------------------------------------------------
70
+ /**
71
+ * Insert a CodeFragmentNode, auto-generating an ID if the `id` field is empty.
72
+ */
73
+ function insertCodeFragment(db, node) {
74
+ const fragment = { ...node };
75
+ if (!fragment.id) {
76
+ fragment.id = makeId();
77
+ }
78
+ db.prepare(`
79
+ INSERT OR REPLACE INTO code_fragment_nodes
80
+ (id, change_type, file_path, start_line, end_line, code_diff)
81
+ VALUES (@id, @changeType, @filePath, @startLine, @endLine, @codeDiff)
82
+ `).run({
83
+ id: fragment.id,
84
+ changeType: fragment.changeType,
85
+ filePath: fragment.filePath,
86
+ startLine: fragment.startLine,
87
+ endLine: fragment.endLine,
88
+ codeDiff: fragment.codeDiff,
89
+ });
90
+ return fragment;
91
+ }
92
+ /**
93
+ * Look up a CodeFragmentNode by primary key.
94
+ * Returns null when not found.
95
+ */
96
+ function findFragmentById(db, fragmentId) {
97
+ const row = db.prepare('SELECT id, change_type, file_path, start_line, end_line, code_diff FROM code_fragment_nodes WHERE id = ?').get(fragmentId);
98
+ return row ? rowToFragment(row) : null;
99
+ }
100
+ /**
101
+ * Find all fragments for a given commit hash, via the join table.
102
+ */
103
+ function findFragmentsByCommit(db, commitHash) {
104
+ const rows = db.prepare(`
105
+ SELECT f.id, f.change_type, f.file_path, f.start_line, f.end_line, f.code_diff
106
+ FROM code_fragment_nodes f
107
+ JOIN commit_fragment_relations r ON r.fragment_id = f.id
108
+ WHERE r.commit_hash = ?
109
+ ORDER BY f.file_path, f.start_line
110
+ `).all(commitHash);
111
+ return rows.map(rowToFragment);
112
+ }
113
+ /**
114
+ * Count total fragments.
115
+ */
116
+ function countFragments(db) {
117
+ const row = db.prepare('SELECT COUNT(*) as cnt FROM code_fragment_nodes').get();
118
+ return row?.cnt ?? 0;
119
+ }
120
+ //# sourceMappingURL=fragment-node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragment-node.js","sourceRoot":"","sources":["../../../src/spec/db/fragment-node.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDH,gDAuBC;AAMD,4CASC;AAKD,sDAaC;AAKD,wCAKC;AArHD,+CAAiC;AAiBjC,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E;;;GAGG;AACH,SAAS,MAAM;IACb,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,SAAS,aAAa,CAAC,GAAoB;IACzC,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,UAAU,EAAE,GAAG,CAAC,WAA6C;QAC7D,QAAQ,EAAE,GAAG,CAAC,SAAS;QACvB,SAAS,EAAE,GAAG,CAAC,UAAU;QACzB,OAAO,EAAE,GAAG,CAAC,QAAQ;QACrB,QAAQ,EAAE,GAAG,CAAC,SAAS;KACxB,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E;;GAEG;AACH,SAAgB,kBAAkB,CAChC,EAAkB,EAClB,IAAsB;IAEtB,MAAM,QAAQ,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,QAAQ,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC;IACzB,CAAC;IAED,EAAE,CAAC,OAAO,CAAC;;;;GAIV,CAAC,CAAC,GAAG,CAAC;QACL,EAAE,EAAE,QAAQ,CAAC,EAAE;QACf,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;KAC5B,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,SAAgB,gBAAgB,CAC9B,EAAkB,EAClB,UAAkB;IAElB,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CACpB,0GAA0G,CAC3G,CAAC,GAAG,CAAC,UAAU,CAAgC,CAAC;IAEjD,OAAO,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACzC,CAAC;AAED;;GAEG;AACH,SAAgB,qBAAqB,CACnC,EAAkB,EAClB,UAAkB;IAElB,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC;;;;;;GAMvB,CAAC,CAAC,GAAG,CAAC,UAAU,CAAsB,CAAC;IAExC,OAAO,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,SAAgB,cAAc,CAAC,EAAkB;IAC/C,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,iDAAiD,CAAC,CAAC,GAAG,EAEhE,CAAC;IACd,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;AACvB,CAAC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * FTS5 Full-Text Search for Specs — with CJK tokenisation,
3
+ * weighted ranking, and multi-stage LIKE fallback.
4
+ *
5
+ * FTS5's ``unicode61`` tokeniser treats consecutive CJK characters as a
6
+ * single token. To support single-character / phrase queries we insert
7
+ * a space between each CJK codepoint before indexing *and* before
8
+ * searching.
9
+ *
10
+ * Weighting strategy:
11
+ * Title matches are boosted over subtitle matches via post-scoring.
12
+ * Both FTS5 and LIKE stages use the same score semantics:
13
+ *
14
+ * ===== ====================
15
+ * Score Meaning
16
+ * ===== ====================
17
+ * 3.0 exact title match
18
+ * 2.5 title starts with query
19
+ * 2.0 title contains query
20
+ * 1.0 keyword match in subtitles only
21
+ * ===== ====================
22
+ *
23
+ * FTS5 returns ``limit * 3`` candidates; they are re-ranked and the
24
+ * top *limit* are returned.
25
+ *
26
+ * Fallback strategy (3 stages):
27
+ * 1. FTS5 weighted search (primary)
28
+ * 2. LIKE tiered scoring (same score semantics as FTS5)
29
+ * 3. If still no results, return most-recently-added specs as a
30
+ * "discovery" fallback.
31
+ */
32
+ import { SqliteDatabase } from '../../db/sqlite-adapter';
33
+ import { SpecSearchResult } from '../types';
34
+ /**
35
+ * Insert a space between adjacent CJK characters so FTS5's unicode61
36
+ * tokeniser can index them individually.
37
+ */
38
+ export declare function segmentCjk(text: string): string;
39
+ /**
40
+ * Escape / pre-process a user query for FTS5.
41
+ *
42
+ * 1. Segment CJK runs by inserting spaces.
43
+ * 2. Quote each token so FTS5 treats it as a literal term.
44
+ */
45
+ export declare function escapeFtsQuery(query: string): string;
46
+ /**
47
+ * Combined FTS5 + LIKE fallback search for specs.
48
+ *
49
+ * Strategy:
50
+ * 1. FTS5 weighted search (title 3× boost).
51
+ * 2. If fewer than *limit* results, backfill with LIKE tiered scoring.
52
+ * 3. If still no results, return most-recently-added specs as a
53
+ * discovery fallback.
54
+ *
55
+ * Every result contains ``id, title, subtitles, _score`` and
56
+ * optionally ``_method`` (``"fts5"`` | ``"like"`` | ``"all"`` | ``"discovery"``)
57
+ * and ``_tier`` (for LIKE matches).
58
+ */
59
+ export declare function searchSpecs(db: SqliteDatabase, query: string, limit?: number): SpecSearchResult[];
60
+ //# sourceMappingURL=fts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fts.d.ts","sourceRoot":"","sources":["../../../src/spec/db/fts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAS5C;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOpD;AA4MD;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CACzB,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAW,GACjB,gBAAgB,EAAE,CA8CpB"}
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+ /**
3
+ * FTS5 Full-Text Search for Specs — with CJK tokenisation,
4
+ * weighted ranking, and multi-stage LIKE fallback.
5
+ *
6
+ * FTS5's ``unicode61`` tokeniser treats consecutive CJK characters as a
7
+ * single token. To support single-character / phrase queries we insert
8
+ * a space between each CJK codepoint before indexing *and* before
9
+ * searching.
10
+ *
11
+ * Weighting strategy:
12
+ * Title matches are boosted over subtitle matches via post-scoring.
13
+ * Both FTS5 and LIKE stages use the same score semantics:
14
+ *
15
+ * ===== ====================
16
+ * Score Meaning
17
+ * ===== ====================
18
+ * 3.0 exact title match
19
+ * 2.5 title starts with query
20
+ * 2.0 title contains query
21
+ * 1.0 keyword match in subtitles only
22
+ * ===== ====================
23
+ *
24
+ * FTS5 returns ``limit * 3`` candidates; they are re-ranked and the
25
+ * top *limit* are returned.
26
+ *
27
+ * Fallback strategy (3 stages):
28
+ * 1. FTS5 weighted search (primary)
29
+ * 2. LIKE tiered scoring (same score semantics as FTS5)
30
+ * 3. If still no results, return most-recently-added specs as a
31
+ * "discovery" fallback.
32
+ */
33
+ Object.defineProperty(exports, "__esModule", { value: true });
34
+ exports.segmentCjk = segmentCjk;
35
+ exports.escapeFtsQuery = escapeFtsQuery;
36
+ exports.searchSpecs = searchSpecs;
37
+ // ===========================================================================
38
+ // CJK Segmentation
39
+ // ===========================================================================
40
+ /** Regex matching a CJK Unified Ideograph (U+4E00–U+9FFF). */
41
+ const CJK_RANGE = /[\u4e00-\u9fff]+/g;
42
+ /**
43
+ * Insert a space between adjacent CJK characters so FTS5's unicode61
44
+ * tokeniser can index them individually.
45
+ */
46
+ function segmentCjk(text) {
47
+ return text.replace(CJK_RANGE, (m) => m.split('').join(' '));
48
+ }
49
+ /**
50
+ * Escape / pre-process a user query for FTS5.
51
+ *
52
+ * 1. Segment CJK runs by inserting spaces.
53
+ * 2. Quote each token so FTS5 treats it as a literal term.
54
+ */
55
+ function escapeFtsQuery(query) {
56
+ const trimmed = query.trim();
57
+ if (!trimmed)
58
+ return '""';
59
+ const segmented = segmentCjk(trimmed);
60
+ const tokens = segmented.split(/\s+/).filter(Boolean);
61
+ return tokens.map((t) => `"${t}"`).join(' ') || '""';
62
+ }
63
+ // ===========================================================================
64
+ // Weighted Scoring
65
+ // ===========================================================================
66
+ /**
67
+ * Compute a title-match boost factor.
68
+ *
69
+ * 3.0 — exact match
70
+ * 2.5 — title starts with query
71
+ * 2.0 — title contains query
72
+ * 1.0 — no title match (subtitles only)
73
+ */
74
+ function scoreTitleMatch(title, query) {
75
+ const q = query.toLowerCase().trim();
76
+ const t = title.toLowerCase();
77
+ if (q === t)
78
+ return 3.0;
79
+ if (t.startsWith(q))
80
+ return 2.5;
81
+ if (t.includes(q))
82
+ return 2.0;
83
+ return 1.0;
84
+ }
85
+ /**
86
+ * LIKE tiered scoring.
87
+ *
88
+ * Returns ``[score, tierLabel]`` where higher score = more relevant.
89
+ * Score semantics are aligned with ``scoreTitleMatch`` so cross-stage
90
+ * re-ranking works correctly.
91
+ */
92
+ function likeScore(title, query) {
93
+ const t = title.toLowerCase().trim();
94
+ const q = query.toLowerCase().trim();
95
+ if (t === q)
96
+ return [3.0, 'exact_title'];
97
+ if (t.startsWith(q))
98
+ return [2.5, 'starts_with_title'];
99
+ if (t.includes(q))
100
+ return [2.0, 'contains_title'];
101
+ return [1.0, 'contains_subtitle'];
102
+ }
103
+ // ===========================================================================
104
+ // Stage 1: FTS5 Weighted Search
105
+ // ===========================================================================
106
+ /**
107
+ * Weighted FTS5 search with title boost.
108
+ *
109
+ * Fetches ``limit * oversample`` candidates from FTS5, re-ranks by
110
+ * weighted score, and returns the top *limit*.
111
+ */
112
+ function searchSpecsFts(db, query, limit, oversample = 3) {
113
+ const escaped = escapeFtsQuery(query);
114
+ const rawLimit = limit * oversample;
115
+ let rows;
116
+ try {
117
+ rows = db
118
+ .prepare(`SELECT s.id, s.title, s.subtitles
119
+ FROM specs_fts f
120
+ JOIN spec_nodes s ON s.id = f.id
121
+ WHERE specs_fts MATCH ?
122
+ ORDER BY rank LIMIT ?`)
123
+ .all(escaped, rawLimit);
124
+ }
125
+ catch {
126
+ return [];
127
+ }
128
+ const scored = [];
129
+ for (const row of rows) {
130
+ let subtitles;
131
+ try {
132
+ subtitles = JSON.parse(row.subtitles || '[]');
133
+ }
134
+ catch {
135
+ subtitles = [];
136
+ }
137
+ const boost = scoreTitleMatch(row.title, query);
138
+ scored.push({
139
+ score: boost,
140
+ result: {
141
+ id: row.id,
142
+ title: row.title,
143
+ subtitles,
144
+ _score: boost,
145
+ _method: 'fts5',
146
+ },
147
+ });
148
+ }
149
+ scored.sort((a, b) => b.score - a.score);
150
+ return scored.slice(0, limit).map((s) => s.result);
151
+ }
152
+ // ===========================================================================
153
+ // Stage 2: LIKE Tiered Fallback
154
+ // ===========================================================================
155
+ /**
156
+ * LIKE fallback search with tiered scoring.
157
+ *
158
+ * Skips any spec whose id appears in *excludeIds* (to avoid
159
+ * duplicating FTS5 results).
160
+ */
161
+ function searchSpecsLike(db, query, limit, excludeIds) {
162
+ const likePat = `%${query}%`;
163
+ const rows = db
164
+ .prepare(`SELECT id, title, subtitles FROM spec_nodes
165
+ WHERE title LIKE ? OR subtitles LIKE ?
166
+ ORDER BY id LIMIT ?`)
167
+ .all(likePat, likePat, limit * 3);
168
+ const scored = [];
169
+ for (const row of rows) {
170
+ if (excludeIds.has(row.id))
171
+ continue;
172
+ let subtitles;
173
+ try {
174
+ subtitles = JSON.parse(row.subtitles || '[]');
175
+ }
176
+ catch {
177
+ subtitles = [];
178
+ }
179
+ const [score, tier] = likeScore(row.title, query);
180
+ scored.push({
181
+ score,
182
+ tier,
183
+ result: {
184
+ id: row.id,
185
+ title: row.title,
186
+ subtitles,
187
+ _score: score,
188
+ _method: 'like',
189
+ _tier: tier,
190
+ },
191
+ });
192
+ }
193
+ scored.sort((a, b) => {
194
+ if (b.score !== a.score)
195
+ return b.score - a.score;
196
+ return a.tier.localeCompare(b.tier);
197
+ });
198
+ return scored.slice(0, limit).map((s) => s.result);
199
+ }
200
+ // ===========================================================================
201
+ // Stage 3: Discovery Fallback
202
+ // ===========================================================================
203
+ /**
204
+ * Return the most-recently-added specs when all searches produce
205
+ * no results — so the caller always gets something.
206
+ */
207
+ function discoveryFallback(db, limit) {
208
+ const rows = db
209
+ .prepare('SELECT id, title, subtitles FROM spec_nodes ORDER BY id LIMIT ?')
210
+ .all(limit);
211
+ return rows.map((row) => {
212
+ let subtitles;
213
+ try {
214
+ subtitles = JSON.parse(row.subtitles || '[]');
215
+ }
216
+ catch {
217
+ subtitles = [];
218
+ }
219
+ return {
220
+ id: row.id,
221
+ title: row.title,
222
+ subtitles,
223
+ _score: 0.0,
224
+ _method: 'discovery',
225
+ };
226
+ });
227
+ }
228
+ // ===========================================================================
229
+ // Combined Search (public API)
230
+ // ===========================================================================
231
+ /**
232
+ * Combined FTS5 + LIKE fallback search for specs.
233
+ *
234
+ * Strategy:
235
+ * 1. FTS5 weighted search (title 3× boost).
236
+ * 2. If fewer than *limit* results, backfill with LIKE tiered scoring.
237
+ * 3. If still no results, return most-recently-added specs as a
238
+ * discovery fallback.
239
+ *
240
+ * Every result contains ``id, title, subtitles, _score`` and
241
+ * optionally ``_method`` (``"fts5"`` | ``"like"`` | ``"all"`` | ``"discovery"``)
242
+ * and ``_tier`` (for LIKE matches).
243
+ */
244
+ function searchSpecs(db, query, limit = 10) {
245
+ // Empty query — return all specs (recent first)
246
+ if (!query.trim()) {
247
+ const rows = db
248
+ .prepare('SELECT id, title, subtitles FROM spec_nodes ORDER BY id LIMIT ?')
249
+ .all(limit);
250
+ return rows.map((row) => {
251
+ let subtitles;
252
+ try {
253
+ subtitles = JSON.parse(row.subtitles || '[]');
254
+ }
255
+ catch {
256
+ subtitles = [];
257
+ }
258
+ return {
259
+ id: row.id,
260
+ title: row.title,
261
+ subtitles,
262
+ _score: 0.0,
263
+ _method: 'all',
264
+ };
265
+ });
266
+ }
267
+ // Stage 1: FTS5 weighted search
268
+ const ftsResults = searchSpecsFts(db, query, limit);
269
+ if (ftsResults.length >= limit) {
270
+ return ftsResults;
271
+ }
272
+ // Stage 2: LIKE fallback
273
+ const seenIds = new Set(ftsResults.map((r) => r.id));
274
+ const needed = limit - ftsResults.length;
275
+ const likeResults = searchSpecsLike(db, query, needed, seenIds);
276
+ // Cross-stage re-sort: LIKE and FTS5 share the same score semantics
277
+ const combined = [...ftsResults, ...likeResults];
278
+ combined.sort((a, b) => (b._score ?? 0) - (a._score ?? 0));
279
+ // Stage 3: discovery fallback — no matches at all
280
+ if (combined.length === 0) {
281
+ return discoveryFallback(db, limit);
282
+ }
283
+ return combined.slice(0, limit);
284
+ }
285
+ //# sourceMappingURL=fts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fts.js","sourceRoot":"","sources":["../../../src/spec/db/fts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Spec Database Layer — Barrel Export
3
+ *
4
+ * Re-exports all CRUD operations, schema initialisation, and FTS5
5
+ * search so consumers can `import { ... } from '../spec/db'`.
6
+ */
7
+ export { initSpecSchema, getCurrentSpecVersion, runSpecMigrations, CURRENT_SPEC_SCHEMA_VERSION } from './schema';
8
+ export { insertSpecNode, findSpecById, updateSpecStatus, updateSpecVersion, deleteSpec, listAllSpecs, countSpecsByStatus, } from './spec-node';
9
+ export { insertCommitNode, findCommitByHash, listAllCommits, countCommits, } from './commit-node';
10
+ export { insertCodeFragment, findFragmentById, findFragmentsByCommit, countFragments, } from './fragment-node';
11
+ export { insertSpecCommitRelation, findCommitsBySpec, transferSpecCommitRelations, insertCommitFragmentRelation, insertSpecSpecRelation, deleteSimilarToRelations, transferSpecSpecRelations, countAllRelations, } from './relations';
12
+ export { searchSpecs, escapeFtsQuery, segmentCjk, } from './fts';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/spec/db/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAGjH,OAAO,EACL,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,YAAY,GACb,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,qBAAqB,EACrB,cAAc,GACf,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,2BAA2B,EAC3B,4BAA4B,EAC5B,sBAAsB,EACtB,wBAAwB,EACxB,yBAAyB,EACzB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,WAAW,EACX,cAAc,EACd,UAAU,GACX,MAAM,OAAO,CAAC"}
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ /**
3
+ * Spec Database Layer — Barrel Export
4
+ *
5
+ * Re-exports all CRUD operations, schema initialisation, and FTS5
6
+ * search so consumers can `import { ... } from '../spec/db'`.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.segmentCjk = exports.escapeFtsQuery = exports.searchSpecs = exports.countAllRelations = exports.transferSpecSpecRelations = exports.deleteSimilarToRelations = exports.insertSpecSpecRelation = exports.insertCommitFragmentRelation = exports.transferSpecCommitRelations = exports.findCommitsBySpec = exports.insertSpecCommitRelation = exports.countFragments = exports.findFragmentsByCommit = exports.findFragmentById = exports.insertCodeFragment = exports.countCommits = exports.listAllCommits = exports.findCommitByHash = exports.insertCommitNode = exports.countSpecsByStatus = exports.listAllSpecs = exports.deleteSpec = exports.updateSpecVersion = exports.updateSpecStatus = exports.findSpecById = exports.insertSpecNode = exports.CURRENT_SPEC_SCHEMA_VERSION = exports.runSpecMigrations = exports.getCurrentSpecVersion = exports.initSpecSchema = void 0;
10
+ // Schema
11
+ var schema_1 = require("./schema");
12
+ Object.defineProperty(exports, "initSpecSchema", { enumerable: true, get: function () { return schema_1.initSpecSchema; } });
13
+ Object.defineProperty(exports, "getCurrentSpecVersion", { enumerable: true, get: function () { return schema_1.getCurrentSpecVersion; } });
14
+ Object.defineProperty(exports, "runSpecMigrations", { enumerable: true, get: function () { return schema_1.runSpecMigrations; } });
15
+ Object.defineProperty(exports, "CURRENT_SPEC_SCHEMA_VERSION", { enumerable: true, get: function () { return schema_1.CURRENT_SPEC_SCHEMA_VERSION; } });
16
+ // Entity CRUD
17
+ var spec_node_1 = require("./spec-node");
18
+ Object.defineProperty(exports, "insertSpecNode", { enumerable: true, get: function () { return spec_node_1.insertSpecNode; } });
19
+ Object.defineProperty(exports, "findSpecById", { enumerable: true, get: function () { return spec_node_1.findSpecById; } });
20
+ Object.defineProperty(exports, "updateSpecStatus", { enumerable: true, get: function () { return spec_node_1.updateSpecStatus; } });
21
+ Object.defineProperty(exports, "updateSpecVersion", { enumerable: true, get: function () { return spec_node_1.updateSpecVersion; } });
22
+ Object.defineProperty(exports, "deleteSpec", { enumerable: true, get: function () { return spec_node_1.deleteSpec; } });
23
+ Object.defineProperty(exports, "listAllSpecs", { enumerable: true, get: function () { return spec_node_1.listAllSpecs; } });
24
+ Object.defineProperty(exports, "countSpecsByStatus", { enumerable: true, get: function () { return spec_node_1.countSpecsByStatus; } });
25
+ var commit_node_1 = require("./commit-node");
26
+ Object.defineProperty(exports, "insertCommitNode", { enumerable: true, get: function () { return commit_node_1.insertCommitNode; } });
27
+ Object.defineProperty(exports, "findCommitByHash", { enumerable: true, get: function () { return commit_node_1.findCommitByHash; } });
28
+ Object.defineProperty(exports, "listAllCommits", { enumerable: true, get: function () { return commit_node_1.listAllCommits; } });
29
+ Object.defineProperty(exports, "countCommits", { enumerable: true, get: function () { return commit_node_1.countCommits; } });
30
+ var fragment_node_1 = require("./fragment-node");
31
+ Object.defineProperty(exports, "insertCodeFragment", { enumerable: true, get: function () { return fragment_node_1.insertCodeFragment; } });
32
+ Object.defineProperty(exports, "findFragmentById", { enumerable: true, get: function () { return fragment_node_1.findFragmentById; } });
33
+ Object.defineProperty(exports, "findFragmentsByCommit", { enumerable: true, get: function () { return fragment_node_1.findFragmentsByCommit; } });
34
+ Object.defineProperty(exports, "countFragments", { enumerable: true, get: function () { return fragment_node_1.countFragments; } });
35
+ // Relations
36
+ var relations_1 = require("./relations");
37
+ Object.defineProperty(exports, "insertSpecCommitRelation", { enumerable: true, get: function () { return relations_1.insertSpecCommitRelation; } });
38
+ Object.defineProperty(exports, "findCommitsBySpec", { enumerable: true, get: function () { return relations_1.findCommitsBySpec; } });
39
+ Object.defineProperty(exports, "transferSpecCommitRelations", { enumerable: true, get: function () { return relations_1.transferSpecCommitRelations; } });
40
+ Object.defineProperty(exports, "insertCommitFragmentRelation", { enumerable: true, get: function () { return relations_1.insertCommitFragmentRelation; } });
41
+ Object.defineProperty(exports, "insertSpecSpecRelation", { enumerable: true, get: function () { return relations_1.insertSpecSpecRelation; } });
42
+ Object.defineProperty(exports, "deleteSimilarToRelations", { enumerable: true, get: function () { return relations_1.deleteSimilarToRelations; } });
43
+ Object.defineProperty(exports, "transferSpecSpecRelations", { enumerable: true, get: function () { return relations_1.transferSpecSpecRelations; } });
44
+ Object.defineProperty(exports, "countAllRelations", { enumerable: true, get: function () { return relations_1.countAllRelations; } });
45
+ // FTS5 Search
46
+ var fts_1 = require("./fts");
47
+ Object.defineProperty(exports, "searchSpecs", { enumerable: true, get: function () { return fts_1.searchSpecs; } });
48
+ Object.defineProperty(exports, "escapeFtsQuery", { enumerable: true, get: function () { return fts_1.escapeFtsQuery; } });
49
+ Object.defineProperty(exports, "segmentCjk", { enumerable: true, get: function () { return fts_1.segmentCjk; } });
50
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/spec/db/index.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEH,SAAS;AACT,mCAAiH;AAAxG,wGAAA,cAAc,OAAA;AAAE,+GAAA,qBAAqB,OAAA;AAAE,2GAAA,iBAAiB,OAAA;AAAE,qHAAA,2BAA2B,OAAA;AAE9F,cAAc;AACd,yCAQqB;AAPnB,2GAAA,cAAc,OAAA;AACd,yGAAA,YAAY,OAAA;AACZ,6GAAA,gBAAgB,OAAA;AAChB,8GAAA,iBAAiB,OAAA;AACjB,uGAAA,UAAU,OAAA;AACV,yGAAA,YAAY,OAAA;AACZ,+GAAA,kBAAkB,OAAA;AAGpB,6CAKuB;AAJrB,+GAAA,gBAAgB,OAAA;AAChB,+GAAA,gBAAgB,OAAA;AAChB,6GAAA,cAAc,OAAA;AACd,2GAAA,YAAY,OAAA;AAGd,iDAKyB;AAJvB,mHAAA,kBAAkB,OAAA;AAClB,iHAAA,gBAAgB,OAAA;AAChB,sHAAA,qBAAqB,OAAA;AACrB,+GAAA,cAAc,OAAA;AAGhB,YAAY;AACZ,yCASqB;AARnB,qHAAA,wBAAwB,OAAA;AACxB,8GAAA,iBAAiB,OAAA;AACjB,wHAAA,2BAA2B,OAAA;AAC3B,yHAAA,4BAA4B,OAAA;AAC5B,mHAAA,sBAAsB,OAAA;AACtB,qHAAA,wBAAwB,OAAA;AACxB,sHAAA,yBAAyB,OAAA;AACzB,8GAAA,iBAAiB,OAAA;AAGnB,cAAc;AACd,6BAIe;AAHb,kGAAA,WAAW,OAAA;AACX,qGAAA,cAAc,OAAA;AACd,iGAAA,UAAU,OAAA"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Relation CRUD — persistence operations for the three relation tables:
3
+ *
4
+ * - ``spec_commit_relations`` (Spec ↔ Commit)
5
+ * - ``commit_fragment_relations`` (Commit ↔ CodeFragment)
6
+ * - ``spec_spec_relations`` (Spec ↔ Spec)
7
+ */
8
+ import { SqliteDatabase } from '../../db/sqlite-adapter';
9
+ import { RelationType, SpecCommitRelation } from '../types';
10
+ /**
11
+ * Insert a relation between a Spec and a Commit.
12
+ * `INSERT OR IGNORE` — duplicates are silently skipped.
13
+ */
14
+ export declare function insertSpecCommitRelation(db: SqliteDatabase, specId: string, commitHash: string, relationType: RelationType): void;
15
+ /**
16
+ * Find all commits linked to a spec, ordered by commit timestamp
17
+ * (oldest first — matches Commit4Spec's mining pipeline sort).
18
+ */
19
+ export declare function findCommitsBySpec(db: SqliteDatabase, specId: string): Array<SpecCommitRelation & {
20
+ message: string;
21
+ author: string;
22
+ timestamp: number;
23
+ }>;
24
+ /**
25
+ * Transfer all spec–commit relations from one spec to another.
26
+ * Used during self-evolve when a spec is versioned:
27
+ * old active spec's relations move to the deprecated record.
28
+ */
29
+ export declare function transferSpecCommitRelations(db: SqliteDatabase, fromSpecId: string, toSpecId: string): void;
30
+ /**
31
+ * Link a commit to a code fragment.
32
+ * `INSERT OR IGNORE` — duplicates are silently skipped.
33
+ */
34
+ export declare function insertCommitFragmentRelation(db: SqliteDatabase, commitHash: string, fragmentId: string): void;
35
+ /**
36
+ * Insert a relation between two specs (SIMILAR_TO or EVOLVED_FROM).
37
+ * `INSERT OR IGNORE` — duplicates are silently skipped.
38
+ */
39
+ export declare function insertSpecSpecRelation(db: SqliteDatabase, sourceId: string, targetId: string, relationType: RelationType): void;
40
+ /**
41
+ * Remove all SIMILAR_TO relations involving a spec.
42
+ * Called when a spec is deprecated (similarity links become stale).
43
+ */
44
+ export declare function deleteSimilarToRelations(db: SqliteDatabase, specId: string): void;
45
+ /**
46
+ * Transfer all spec–spec relations from one spec to another.
47
+ * Used during self-evolve versioning (old spec's relations move to
48
+ * the deprecated record).
49
+ */
50
+ export declare function transferSpecSpecRelations(db: SqliteDatabase, fromSpecId: string, toSpecId: string): void;
51
+ /**
52
+ * Count total rows across all three relation tables.
53
+ */
54
+ export declare function countAllRelations(db: SqliteDatabase): number;
55
+ //# sourceMappingURL=relations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relations.d.ts","sourceRoot":"","sources":["../../../src/spec/db/relations.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAM5D;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,cAAc,EAClB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,YAAY,GACzB,IAAI,CAMN;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,cAAc,EAClB,MAAM,EAAE,MAAM,GACb,KAAK,CAAC,kBAAkB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAwBpF;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,EAAE,EAAE,cAAc,EAClB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,IAAI,CAMN;AAMD;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,EAAE,EAAE,cAAc,EAClB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,IAAI,CAMN;AAMD;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,YAAY,GACzB,IAAI,CAMN;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAMjF;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,cAAc,EAClB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,IAAI,CA6BN;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,GAAG,MAAM,CAa5D"}