tokenos 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +571 -0
  2. package/USAGE.md +451 -0
  3. package/dist/config.d.ts +22 -0
  4. package/dist/config.d.ts.map +1 -0
  5. package/dist/config.js +60 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/db/connection.d.ts +3 -0
  8. package/dist/db/connection.d.ts.map +1 -0
  9. package/dist/db/connection.js +78 -0
  10. package/dist/db/connection.js.map +1 -0
  11. package/dist/db/index.d.ts +4 -0
  12. package/dist/db/index.d.ts.map +1 -0
  13. package/dist/db/index.js +4 -0
  14. package/dist/db/index.js.map +1 -0
  15. package/dist/db/memory.d.ts +6 -0
  16. package/dist/db/memory.d.ts.map +1 -0
  17. package/dist/db/memory.js +62 -0
  18. package/dist/db/memory.js.map +1 -0
  19. package/dist/db/queries.d.ts +29 -0
  20. package/dist/db/queries.d.ts.map +1 -0
  21. package/dist/db/queries.js +215 -0
  22. package/dist/db/queries.js.map +1 -0
  23. package/dist/embeddings/client.d.ts +16 -0
  24. package/dist/embeddings/client.d.ts.map +1 -0
  25. package/dist/embeddings/client.js +70 -0
  26. package/dist/embeddings/client.js.map +1 -0
  27. package/dist/embeddings/index.d.ts +11 -0
  28. package/dist/embeddings/index.d.ts.map +1 -0
  29. package/dist/embeddings/index.js +37 -0
  30. package/dist/embeddings/index.js.map +1 -0
  31. package/dist/embeddings/similarity.d.ts +7 -0
  32. package/dist/embeddings/similarity.d.ts.map +1 -0
  33. package/dist/embeddings/similarity.js +31 -0
  34. package/dist/embeddings/similarity.js.map +1 -0
  35. package/dist/indexer/cli.d.ts +8 -0
  36. package/dist/indexer/cli.d.ts.map +1 -0
  37. package/dist/indexer/cli.js +21 -0
  38. package/dist/indexer/cli.js.map +1 -0
  39. package/dist/indexer/ignore.d.ts +4 -0
  40. package/dist/indexer/ignore.d.ts.map +1 -0
  41. package/dist/indexer/ignore.js +30 -0
  42. package/dist/indexer/ignore.js.map +1 -0
  43. package/dist/indexer/index.d.ts +5 -0
  44. package/dist/indexer/index.d.ts.map +1 -0
  45. package/dist/indexer/index.js +4 -0
  46. package/dist/indexer/index.js.map +1 -0
  47. package/dist/indexer/indexer.d.ts +13 -0
  48. package/dist/indexer/indexer.d.ts.map +1 -0
  49. package/dist/indexer/indexer.js +125 -0
  50. package/dist/indexer/indexer.js.map +1 -0
  51. package/dist/indexer/parser.d.ts +10 -0
  52. package/dist/indexer/parser.d.ts.map +1 -0
  53. package/dist/indexer/parser.js +444 -0
  54. package/dist/indexer/parser.js.map +1 -0
  55. package/dist/indexer/watcher.d.ts +7 -0
  56. package/dist/indexer/watcher.d.ts.map +1 -0
  57. package/dist/indexer/watcher.js +64 -0
  58. package/dist/indexer/watcher.js.map +1 -0
  59. package/dist/main.d.ts +3 -0
  60. package/dist/main.d.ts.map +1 -0
  61. package/dist/main.js +92 -0
  62. package/dist/main.js.map +1 -0
  63. package/dist/reset.d.ts +6 -0
  64. package/dist/reset.d.ts.map +1 -0
  65. package/dist/reset.js +23 -0
  66. package/dist/reset.js.map +1 -0
  67. package/dist/server/index.d.ts +2 -0
  68. package/dist/server/index.d.ts.map +1 -0
  69. package/dist/server/index.js +2 -0
  70. package/dist/server/index.js.map +1 -0
  71. package/dist/server/server.d.ts +4 -0
  72. package/dist/server/server.d.ts.map +1 -0
  73. package/dist/server/server.js +558 -0
  74. package/dist/server/server.js.map +1 -0
  75. package/dist/server/visualize.d.ts +2 -0
  76. package/dist/server/visualize.d.ts.map +1 -0
  77. package/dist/server/visualize.js +299 -0
  78. package/dist/server/visualize.js.map +1 -0
  79. package/dist/test-phase1.d.ts +13 -0
  80. package/dist/test-phase1.d.ts.map +1 -0
  81. package/dist/test-phase1.js +90 -0
  82. package/dist/test-phase1.js.map +1 -0
  83. package/dist/test-phase2.d.ts +13 -0
  84. package/dist/test-phase2.d.ts.map +1 -0
  85. package/dist/test-phase2.js +110 -0
  86. package/dist/test-phase2.js.map +1 -0
  87. package/dist/test-phase3.d.ts +12 -0
  88. package/dist/test-phase3.d.ts.map +1 -0
  89. package/dist/test-phase3.js +85 -0
  90. package/dist/test-phase3.js.map +1 -0
  91. package/dist/types.d.ts +73 -0
  92. package/dist/types.d.ts.map +1 -0
  93. package/dist/types.js +3 -0
  94. package/dist/types.js.map +1 -0
  95. package/dist/utils/cache.d.ts +12 -0
  96. package/dist/utils/cache.d.ts.map +1 -0
  97. package/dist/utils/cache.js +45 -0
  98. package/dist/utils/cache.js.map +1 -0
  99. package/dist/utils/logger.d.ts +16 -0
  100. package/dist/utils/logger.d.ts.map +1 -0
  101. package/dist/utils/logger.js +52 -0
  102. package/dist/utils/logger.js.map +1 -0
  103. package/dist/utils/scoring.d.ts +15 -0
  104. package/dist/utils/scoring.d.ts.map +1 -0
  105. package/dist/utils/scoring.js +17 -0
  106. package/dist/utils/scoring.js.map +1 -0
  107. package/dist/verify-parser.d.ts +6 -0
  108. package/dist/verify-parser.d.ts.map +1 -0
  109. package/dist/verify-parser.js +105 -0
  110. package/dist/verify-parser.js.map +1 -0
  111. package/package.json +52 -0
@@ -0,0 +1,62 @@
1
+ import { db } from "./connection.js";
2
+ const stmtUpsertMemory = db.prepare(`
3
+ INSERT INTO memories (id, title, summary, key_points, tags, embedding, created_at)
4
+ VALUES (@id, @title, @summary, @key_points, @tags, @embedding, @created_at)
5
+ ON CONFLICT(id) DO UPDATE SET
6
+ title = excluded.title,
7
+ summary = excluded.summary,
8
+ key_points = excluded.key_points,
9
+ tags = excluded.tags,
10
+ embedding = excluded.embedding
11
+ `);
12
+ export function upsertMemory(m) {
13
+ stmtUpsertMemory.run({
14
+ id: m.id,
15
+ title: m.title,
16
+ summary: m.summary,
17
+ key_points: JSON.stringify(m.key_points),
18
+ tags: JSON.stringify(m.tags),
19
+ embedding: m.embedding ? JSON.stringify(m.embedding) : null,
20
+ created_at: m.created_at,
21
+ });
22
+ }
23
+ const stmtGetMemory = db.prepare(`
24
+ SELECT * FROM memories WHERE id = ?
25
+ `);
26
+ export function getMemory(id) {
27
+ const row = stmtGetMemory.get(id);
28
+ if (!row)
29
+ return undefined;
30
+ return {
31
+ ...row,
32
+ key_points: JSON.parse(row.key_points),
33
+ tags: JSON.parse(row.tags),
34
+ embedding: row.embedding ? JSON.parse(row.embedding) : undefined,
35
+ };
36
+ }
37
+ const stmtGetAllMemories = db.prepare(`
38
+ SELECT * FROM memories ORDER BY created_at DESC
39
+ `);
40
+ export function getAllMemories() {
41
+ return stmtGetAllMemories.all().map((row) => ({
42
+ ...row,
43
+ key_points: JSON.parse(row.key_points),
44
+ tags: JSON.parse(row.tags),
45
+ embedding: row.embedding ? JSON.parse(row.embedding) : undefined,
46
+ }));
47
+ }
48
+ const stmtSearchMemories = db.prepare(`
49
+ SELECT * FROM memories
50
+ WHERE title LIKE ? OR summary LIKE ? OR tags LIKE ?
51
+ ORDER BY created_at DESC
52
+ `);
53
+ export function searchMemories(query) {
54
+ const term = `%${query}%`;
55
+ return stmtSearchMemories.all(term, term, term).map((row) => ({
56
+ ...row,
57
+ key_points: JSON.parse(row.key_points),
58
+ tags: JSON.parse(row.tags),
59
+ embedding: row.embedding ? JSON.parse(row.embedding) : undefined,
60
+ }));
61
+ }
62
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/db/memory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAGrC,MAAM,gBAAgB,GAAG,EAAE,CAAC,OAAO,CAAC;;;;;;;;;CASnC,CAAC,CAAC;AAEH,MAAM,UAAU,YAAY,CAAC,CAAqB;IAChD,gBAAgB,CAAC,GAAG,CAAC;QACnB,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC;QACxC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5B,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI;QAC3D,UAAU,EAAE,CAAC,CAAC,UAAU;KACzB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,aAAa,GAAG,EAAE,CAAC,OAAO,CAAC;;CAEhC,CAAC,CAAC;AAEH,MAAM,UAAU,SAAS,CAAC,EAAU;IAClC,MAAM,GAAG,GAAG,aAAa,CAAC,GAAG,CAAC,EAAE,CAAQ,CAAC;IACzC,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,OAAO;QACL,GAAG,GAAG;QACN,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC;QACtC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;QAC1B,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;KACjE,CAAC;AACJ,CAAC;AAED,MAAM,kBAAkB,GAAG,EAAE,CAAC,OAAO,CAAC;;CAErC,CAAC,CAAC;AAEH,MAAM,UAAU,cAAc;IAC5B,OAAO,kBAAkB,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,CAAC;QACjD,GAAG,GAAG;QACN,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC;QACtC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;QAC1B,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;KACjE,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,kBAAkB,GAAG,EAAE,CAAC,OAAO,CAAC;;;;CAIrC,CAAC,CAAC;AAEH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,IAAI,GAAG,IAAI,KAAK,GAAG,CAAC;IAC1B,OAAO,kBAAkB,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,CAAC;QACjE,GAAG,GAAG;QACN,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC;QACtC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;QAC1B,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;KACjE,CAAC,CAAC,CAAC;AACN,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { GraphNode, GraphEdge, ParsedEdge } from "../types.js";
2
+ export declare function upsertNode(node: Partial<GraphNode> & Pick<GraphNode, "id" | "type" | "name" | "file_path">): void;
3
+ export declare function getNode(id: string): GraphNode | undefined;
4
+ export declare function getNodesByName(name: string): GraphNode[];
5
+ export declare function getNodesByType(type: string): GraphNode[];
6
+ export declare function getNodesByNameAndType(name: string, type: string): GraphNode[];
7
+ export declare function getNodesWithEmbeddings(type?: string): GraphNode[];
8
+ export declare function deleteNodesByFile(filePath: string): void;
9
+ export declare function getAllNodes(): GraphNode[];
10
+ export declare function updateNodeEmbedding(id: string, embedding: number[]): void;
11
+ export declare function updateNodeImportance(id: string, score: number): void;
12
+ export declare function upsertEdge(edge: GraphEdge): void;
13
+ export declare const upsertEdgesBatch: (edges: ParsedEdge[]) => void;
14
+ export declare function deleteEdgesByFile(filePath: string): void;
15
+ export declare function getNeighbors(nodeId: string): GraphEdge[];
16
+ export declare function getConnectedNodes(nodeId: string): GraphNode[];
17
+ export declare function getInDegree(nodeId: string): number;
18
+ export declare function getOutDegree(nodeId: string): number;
19
+ export declare function getTopNodes(limit?: number): GraphNode[];
20
+ /**
21
+ * Compute all importance scores in a single SQL query + batch update.
22
+ * Replaces the O(N) individual getInDegree/getOutDegree calls.
23
+ */
24
+ export declare function batchComputeImportance(): {
25
+ updated: number;
26
+ };
27
+ export declare function getNodesByMeta(key: "role" | "tab" | "feature", value: string): GraphNode[];
28
+ export declare function searchNodesExtended(query: string): GraphNode[];
29
+ //# sourceMappingURL=queries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../src/db/queries.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAc,UAAU,EAAE,MAAM,aAAa,CAAC;AAoBhF,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,CAAC,GAAG,IAAI,CAUjH;AAMD,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAEzD;AAMD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,CAExD;AAMD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,CAExD;AAQD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,CAE7E;AAYD,wBAAgB,sBAAsB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,CAGjE;AAMD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAExD;AAMD,wBAAgB,WAAW,IAAI,SAAS,EAAE,CAEzC;AAMD,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAEzE;AAMD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAEpE;AASD,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAEhD;AAED,eAAO,MAAM,gBAAgB,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,IAItD,CAAC;AAQH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAExD;AAMD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,CAExD;AAUD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,CAE7D;AAYD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnD;AAMD,wBAAgB,WAAW,CAAC,KAAK,SAAK,GAAG,SAAS,EAAE,CAEnD;AAmCD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAe5D;AAgBD,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,EAC/B,KAAK,EAAE,MAAM,GACZ,SAAS,EAAE,CAMb;AAaD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,CAE9D"}
@@ -0,0 +1,215 @@
1
+ import { db } from "./connection.js";
2
+ // ───── Node Queries ───────────────────────────────────────────────────────────
3
+ const stmtUpsertNode = db.prepare(`
4
+ INSERT INTO nodes (id, type, name, file_path, summary, code_snippet, embedding, meta, hash, importance, updated_at)
5
+ VALUES (@id, @type, @name, @file_path, @summary, @code_snippet, @embedding, @meta, @hash, @importance, CURRENT_TIMESTAMP)
6
+ ON CONFLICT(id) DO UPDATE SET
7
+ type = excluded.type,
8
+ name = excluded.name,
9
+ file_path = excluded.file_path,
10
+ summary = excluded.summary,
11
+ code_snippet = excluded.code_snippet,
12
+ embedding = excluded.embedding,
13
+ meta = excluded.meta,
14
+ hash = excluded.hash,
15
+ importance = excluded.importance,
16
+ updated_at = CURRENT_TIMESTAMP
17
+ `);
18
+ export function upsertNode(node) {
19
+ stmtUpsertNode.run({
20
+ summary: null,
21
+ code_snippet: null,
22
+ embedding: null,
23
+ meta: null,
24
+ hash: null,
25
+ importance: 0,
26
+ ...node,
27
+ });
28
+ }
29
+ const stmtGetNode = db.prepare(`
30
+ SELECT * FROM nodes WHERE id = ?
31
+ `);
32
+ export function getNode(id) {
33
+ return stmtGetNode.get(id);
34
+ }
35
+ const stmtGetNodesByName = db.prepare(`
36
+ SELECT * FROM nodes WHERE name LIKE ? ORDER BY importance DESC
37
+ `);
38
+ export function getNodesByName(name) {
39
+ return stmtGetNodesByName.all(`%${name}%`);
40
+ }
41
+ const stmtGetNodesByType = db.prepare(`
42
+ SELECT * FROM nodes WHERE type = ? ORDER BY importance DESC
43
+ `);
44
+ export function getNodesByType(type) {
45
+ return stmtGetNodesByType.all(type);
46
+ }
47
+ // ── AND filter: name LIKE + type = (fixes OR bug in text search) ──────────────
48
+ const stmtGetNodesByNameAndType = db.prepare(`
49
+ SELECT * FROM nodes WHERE name LIKE ? AND type = ? ORDER BY importance DESC
50
+ `);
51
+ export function getNodesByNameAndType(name, type) {
52
+ return stmtGetNodesByNameAndType.all(`%${name}%`, type);
53
+ }
54
+ // ── Nodes with embeddings (for semantic search — avoids loading ALL nodes) ────
55
+ const stmtGetNodesWithEmbeddings = db.prepare(`
56
+ SELECT * FROM nodes WHERE embedding IS NOT NULL
57
+ `);
58
+ const stmtGetNodesWithEmbeddingsByType = db.prepare(`
59
+ SELECT * FROM nodes WHERE embedding IS NOT NULL AND type = ?
60
+ `);
61
+ export function getNodesWithEmbeddings(type) {
62
+ if (type)
63
+ return stmtGetNodesWithEmbeddingsByType.all(type);
64
+ return stmtGetNodesWithEmbeddings.all();
65
+ }
66
+ const stmtDeleteNodesByFile = db.prepare(`
67
+ DELETE FROM nodes WHERE file_path = ?
68
+ `);
69
+ export function deleteNodesByFile(filePath) {
70
+ stmtDeleteNodesByFile.run(filePath);
71
+ }
72
+ const stmtGetAllNodes = db.prepare(`
73
+ SELECT * FROM nodes
74
+ `);
75
+ export function getAllNodes() {
76
+ return stmtGetAllNodes.all();
77
+ }
78
+ const stmtUpdateEmbedding = db.prepare(`
79
+ UPDATE nodes SET embedding = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?
80
+ `);
81
+ export function updateNodeEmbedding(id, embedding) {
82
+ stmtUpdateEmbedding.run(JSON.stringify(embedding), id);
83
+ }
84
+ const stmtUpdateImportance = db.prepare(`
85
+ UPDATE nodes SET importance = ? WHERE id = ?
86
+ `);
87
+ export function updateNodeImportance(id, score) {
88
+ stmtUpdateImportance.run(score, id);
89
+ }
90
+ // ───── Edge Queries ───────────────────────────────────────────────────────────
91
+ const stmtUpsertEdge = db.prepare(`
92
+ INSERT OR IGNORE INTO edges (from_node, to_node, type)
93
+ VALUES (@from_node, @to_node, @type)
94
+ `);
95
+ export function upsertEdge(edge) {
96
+ stmtUpsertEdge.run(edge);
97
+ }
98
+ export const upsertEdgesBatch = db.transaction((edges) => {
99
+ for (const edge of edges) {
100
+ stmtUpsertEdge.run(edge);
101
+ }
102
+ });
103
+ const stmtDeleteEdgesByFile = db.prepare(`
104
+ DELETE FROM edges
105
+ WHERE from_node IN (SELECT id FROM nodes WHERE file_path = ?)
106
+ OR to_node IN (SELECT id FROM nodes WHERE file_path = ?)
107
+ `);
108
+ export function deleteEdgesByFile(filePath) {
109
+ stmtDeleteEdgesByFile.run(filePath, filePath);
110
+ }
111
+ const stmtGetNeighbors = db.prepare(`
112
+ SELECT * FROM edges WHERE from_node = ? OR to_node = ?
113
+ `);
114
+ export function getNeighbors(nodeId) {
115
+ return stmtGetNeighbors.all(nodeId, nodeId);
116
+ }
117
+ const stmtGetConnectedNodes = db.prepare(`
118
+ SELECT n.*
119
+ FROM nodes n
120
+ JOIN edges e ON n.id = e.to_node
121
+ WHERE e.from_node = ?
122
+ ORDER BY n.importance DESC
123
+ `);
124
+ export function getConnectedNodes(nodeId) {
125
+ return stmtGetConnectedNodes.all(nodeId);
126
+ }
127
+ // ───── Graph-wide helpers ────────────────────────────────────────────────────
128
+ const stmtInDegree = db.prepare(`
129
+ SELECT COUNT(*) as count FROM edges WHERE to_node = ?
130
+ `);
131
+ const stmtOutDegree = db.prepare(`
132
+ SELECT COUNT(*) as count FROM edges WHERE from_node = ?
133
+ `);
134
+ export function getInDegree(nodeId) {
135
+ return stmtInDegree.get(nodeId)?.count ?? 0;
136
+ }
137
+ export function getOutDegree(nodeId) {
138
+ return stmtOutDegree.get(nodeId)?.count ?? 0;
139
+ }
140
+ const stmtTopNodes = db.prepare(`
141
+ SELECT * FROM nodes ORDER BY importance DESC LIMIT ?
142
+ `);
143
+ export function getTopNodes(limit = 20) {
144
+ return stmtTopNodes.all(limit);
145
+ }
146
+ const stmtBatchDegrees = db.prepare(`
147
+ SELECT
148
+ n.id,
149
+ n.type,
150
+ COALESCE(i.cnt, 0) AS in_degree,
151
+ COALESCE(o.cnt, 0) AS out_degree
152
+ FROM nodes n
153
+ LEFT JOIN (SELECT to_node, COUNT(*) AS cnt FROM edges GROUP BY to_node) i ON i.to_node = n.id
154
+ LEFT JOIN (SELECT from_node, COUNT(*) AS cnt FROM edges GROUP BY from_node) o ON o.from_node = n.id
155
+ `);
156
+ const TYPE_WEIGHT = {
157
+ class: 3,
158
+ component: 2.5, // primary UI building blocks
159
+ function: 2,
160
+ route: 1.5, // Next.js route entries
161
+ interface: 1.5, // structural contracts
162
+ enum: 1.5, // structural enumerations
163
+ file: 1.5,
164
+ type_alias: 1, // usually simple aliases
165
+ variable: 1,
166
+ import: 0.5,
167
+ };
168
+ /**
169
+ * Compute all importance scores in a single SQL query + batch update.
170
+ * Replaces the O(N) individual getInDegree/getOutDegree calls.
171
+ */
172
+ export function batchComputeImportance() {
173
+ const rows = stmtBatchDegrees.all();
174
+ let updated = 0;
175
+ const batchUpdate = db.transaction(() => {
176
+ for (const row of rows) {
177
+ const typeWeight = TYPE_WEIGHT[row.type] ?? 1;
178
+ const score = row.in_degree * 2 + row.out_degree + typeWeight;
179
+ stmtUpdateImportance.run(score, row.id);
180
+ updated++;
181
+ }
182
+ });
183
+ batchUpdate();
184
+ return { updated };
185
+ }
186
+ // ── Meta queries (semantic role / tab / feature search) ───────────────────────
187
+ const stmtGetNodesByMetaRole = db.prepare(`
188
+ SELECT * FROM nodes WHERE json_extract(meta, '$.role') = ? ORDER BY importance DESC
189
+ `);
190
+ const stmtGetNodesByMetaTab = db.prepare(`
191
+ SELECT * FROM nodes WHERE json_extract(meta, '$.tab') = ? ORDER BY importance DESC
192
+ `);
193
+ const stmtGetNodesByMetaFeature = db.prepare(`
194
+ SELECT * FROM nodes WHERE json_extract(meta, '$.feature') = ? ORDER BY importance DESC
195
+ `);
196
+ export function getNodesByMeta(key, value) {
197
+ switch (key) {
198
+ case "role": return stmtGetNodesByMetaRole.all(value);
199
+ case "tab": return stmtGetNodesByMetaTab.all(value);
200
+ case "feature": return stmtGetNodesByMetaFeature.all(value);
201
+ }
202
+ }
203
+ // ── Combined name + meta search (searches name LIKE query OR meta contains query) ─
204
+ const stmtSearchNameOrMeta = db.prepare(`
205
+ SELECT * FROM nodes
206
+ WHERE name LIKE ?
207
+ OR json_extract(meta, '$.role') = ?
208
+ OR json_extract(meta, '$.tab') = ?
209
+ OR json_extract(meta, '$.feature') = ?
210
+ ORDER BY importance DESC
211
+ `);
212
+ export function searchNodesExtended(query) {
213
+ return stmtSearchNameOrMeta.all(`%${query}%`, query, query, query);
214
+ }
215
+ //# sourceMappingURL=queries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.js","sourceRoot":"","sources":["../../src/db/queries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAGrC,iFAAiF;AAEjF,MAAM,cAAc,GAAG,EAAE,CAAC,OAAO,CAAY;;;;;;;;;;;;;;CAc5C,CAAC,CAAC;AAEH,MAAM,UAAU,UAAU,CAAC,IAAgF;IACzG,cAAc,CAAC,GAAG,CAAC;QACjB,OAAO,EAAE,IAAI;QACb,YAAY,EAAE,IAAI;QAClB,SAAS,EAAE,IAAI;QACf,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,IAAI;QACV,UAAU,EAAE,CAAC;QACb,GAAG,IAAI;KACK,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,WAAW,GAAG,EAAE,CAAC,OAAO,CAAsB;;CAEnD,CAAC,CAAC;AAEH,MAAM,UAAU,OAAO,CAAC,EAAU;IAChC,OAAO,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,kBAAkB,GAAG,EAAE,CAAC,OAAO,CAAsB;;CAE1D,CAAC,CAAC;AAEH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,kBAAkB,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,kBAAkB,GAAG,EAAE,CAAC,OAAO,CAAsB;;CAE1D,CAAC,CAAC;AAEH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,iFAAiF;AAEjF,MAAM,yBAAyB,GAAG,EAAE,CAAC,OAAO,CAA8B;;CAEzE,CAAC,CAAC;AAEH,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,IAAY;IAC9D,OAAO,yBAAyB,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,EAAE,IAAI,CAAC,CAAC;AAC1D,CAAC;AAED,iFAAiF;AAEjF,MAAM,0BAA0B,GAAG,EAAE,CAAC,OAAO,CAAgB;;CAE5D,CAAC,CAAC;AAEH,MAAM,gCAAgC,GAAG,EAAE,CAAC,OAAO,CAAsB;;CAExE,CAAC,CAAC;AAEH,MAAM,UAAU,sBAAsB,CAAC,IAAa;IAClD,IAAI,IAAI;QAAE,OAAO,gCAAgC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5D,OAAO,0BAA0B,CAAC,GAAG,EAAE,CAAC;AAC1C,CAAC;AAED,MAAM,qBAAqB,GAAG,EAAE,CAAC,OAAO,CAAW;;CAElD,CAAC,CAAC;AAEH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,qBAAqB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,eAAe,GAAG,EAAE,CAAC,OAAO,CAAgB;;CAEjD,CAAC,CAAC;AAEH,MAAM,UAAU,WAAW;IACzB,OAAO,eAAe,CAAC,GAAG,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,mBAAmB,GAAG,EAAE,CAAC,OAAO,CAAmB;;CAExD,CAAC,CAAC;AAEH,MAAM,UAAU,mBAAmB,CAAC,EAAU,EAAE,SAAmB;IACjE,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,oBAAoB,GAAG,EAAE,CAAC,OAAO,CAAmB;;CAEzD,CAAC,CAAC;AAEH,MAAM,UAAU,oBAAoB,CAAC,EAAU,EAAE,KAAa;IAC5D,oBAAoB,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACtC,CAAC;AAED,iFAAiF;AAEjF,MAAM,cAAc,GAAG,EAAE,CAAC,OAAO,CAAY;;;CAG5C,CAAC,CAAC;AAEH,MAAM,UAAU,UAAU,CAAC,IAAe;IACxC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAkC,EAAE,CAAC,WAAW,CAAC,CAAC,KAAmB,EAAE,EAAE;IACpG,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,qBAAqB,GAAG,EAAE,CAAC,OAAO,CAAmB;;;;CAI1D,CAAC,CAAC;AAEH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,qBAAqB,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,gBAAgB,GAAG,EAAE,CAAC,OAAO,CAA8B;;CAEhE,CAAC,CAAC;AAEH,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,OAAO,gBAAgB,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,qBAAqB,GAAG,EAAE,CAAC,OAAO,CAAsB;;;;;;CAM7D,CAAC,CAAC;AAEH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,OAAO,qBAAqB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAC3C,CAAC;AAED,gFAAgF;AAEhF,MAAM,YAAY,GAAG,EAAE,CAAC,OAAO,CAA8B;;CAE5D,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,EAAE,CAAC,OAAO,CAA8B;;CAE7D,CAAC,CAAC;AAEH,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,OAAO,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,OAAO,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,YAAY,GAAG,EAAE,CAAC,OAAO,CAAsB;;CAEpD,CAAC,CAAC;AAEH,MAAM,UAAU,WAAW,CAAC,KAAK,GAAG,EAAE;IACpC,OAAO,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC;AAWD,MAAM,gBAAgB,GAAG,EAAE,CAAC,OAAO,CAAoB;;;;;;;;;CAStD,CAAC,CAAC;AAEH,MAAM,WAAW,GAA2B;IAC1C,KAAK,EAAE,CAAC;IACR,SAAS,EAAE,GAAG,EAAO,6BAA6B;IAClD,QAAQ,EAAE,CAAC;IACX,KAAK,EAAE,GAAG,EAAY,wBAAwB;IAC9C,SAAS,EAAE,GAAG,EAAO,uBAAuB;IAC5C,IAAI,EAAE,GAAG,EAAY,0BAA0B;IAC/C,IAAI,EAAE,GAAG;IACT,UAAU,EAAE,CAAC,EAAQ,yBAAyB;IAC9C,QAAQ,EAAE,CAAC;IACX,MAAM,EAAE,GAAG;CACZ,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,sBAAsB;IACpC,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,EAAE,CAAC;IACpC,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;QACtC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9C,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,GAAG,CAAC,GAAG,GAAG,CAAC,UAAU,GAAG,UAAU,CAAC;YAC9D,oBAAoB,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACxC,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,WAAW,EAAE,CAAC;IACd,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC;AAED,iFAAiF;AAEjF,MAAM,sBAAsB,GAAG,EAAE,CAAC,OAAO,CAAsB;;CAE9D,CAAC,CAAC;AAEH,MAAM,qBAAqB,GAAG,EAAE,CAAC,OAAO,CAAsB;;CAE7D,CAAC,CAAC;AAEH,MAAM,yBAAyB,GAAG,EAAE,CAAC,OAAO,CAAsB;;CAEjE,CAAC,CAAC;AAEH,MAAM,UAAU,cAAc,CAC5B,GAA+B,EAC/B,KAAa;IAEb,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM,CAAC,CAAC,OAAO,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtD,KAAK,KAAK,CAAC,CAAC,OAAO,qBAAqB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpD,KAAK,SAAS,CAAC,CAAC,OAAO,yBAAyB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC;AAED,qFAAqF;AAErF,MAAM,oBAAoB,GAAG,EAAE,CAAC,OAAO,CAA8C;;;;;;;CAOpF,CAAC,CAAC;AAEH,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,OAAO,oBAAoB,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AACrE,CAAC"}
@@ -0,0 +1,16 @@
1
+ export declare function checkOllama(): Promise<boolean>;
2
+ export declare function buildEmbeddingInput(params: {
3
+ name: string;
4
+ type: string;
5
+ summary?: string;
6
+ codeSnippet?: string;
7
+ meta?: {
8
+ role?: string;
9
+ tab?: string;
10
+ feature?: string;
11
+ route?: string;
12
+ };
13
+ }): string;
14
+ export declare function generateEmbedding(text: string): Promise<number[] | null>;
15
+ export declare function clearEmbeddingCache(): void;
16
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/embeddings/client.ts"],"names":[],"mappings":"AASA,wBAAsB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CASpD;AAGD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1E,GAAG,MAAM,CAST;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CA+B9E;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C"}
@@ -0,0 +1,70 @@
1
+ import { LRUCache } from "../utils/cache.js";
2
+ import { config } from "../config.js";
3
+ import { logger } from "../utils/logger.js";
4
+ // Cache embeddings to avoid re-calling Ollama for unchanged text
5
+ const embeddingCache = new LRUCache(1000, 30 * 60 * 1000); // 30min TTL
6
+ let ollamaAvailable = null;
7
+ export async function checkOllama() {
8
+ if (ollamaAvailable !== null)
9
+ return ollamaAvailable;
10
+ try {
11
+ const res = await fetch(`${config.ollama.url}/api/tags`, { signal: AbortSignal.timeout(2000) });
12
+ ollamaAvailable = res.ok;
13
+ }
14
+ catch {
15
+ ollamaAvailable = false;
16
+ }
17
+ return ollamaAvailable;
18
+ }
19
+ // Build the embedding input string — enriched with semantic metadata
20
+ export function buildEmbeddingInput(params) {
21
+ const parts = [`[NAME] ${params.name}`, `[TYPE] ${params.type}`];
22
+ if (params.meta?.role)
23
+ parts.push(`[ROLE] ${params.meta.role}`);
24
+ if (params.meta?.tab)
25
+ parts.push(`[TAB] ${params.meta.tab}`);
26
+ if (params.meta?.feature)
27
+ parts.push(`[FEATURE] ${params.meta.feature}`);
28
+ if (params.meta?.route)
29
+ parts.push(`[ROUTE] ${params.meta.route}`);
30
+ if (params.summary)
31
+ parts.push(`[SUMMARY] ${params.summary}`);
32
+ if (params.codeSnippet)
33
+ parts.push(`[CODE] ${params.codeSnippet.slice(0, 300)}`);
34
+ return parts.join("\n");
35
+ }
36
+ export async function generateEmbedding(text) {
37
+ // Return from cache if available
38
+ const cached = embeddingCache.get(text);
39
+ if (cached)
40
+ return cached;
41
+ // Skip if Ollama is offline
42
+ if (!(await checkOllama()))
43
+ return null;
44
+ try {
45
+ const res = await fetch(`${config.ollama.url}/api/embeddings`, {
46
+ method: "POST",
47
+ headers: { "Content-Type": "application/json" },
48
+ body: JSON.stringify({ model: config.ollama.model, prompt: text }),
49
+ signal: AbortSignal.timeout(10_000),
50
+ });
51
+ if (!res.ok) {
52
+ logger.error("embeddings", `Ollama returned ${res.status}`);
53
+ return null;
54
+ }
55
+ const data = (await res.json());
56
+ const embedding = data.embedding;
57
+ embeddingCache.set(text, embedding);
58
+ return embedding;
59
+ }
60
+ catch (err) {
61
+ logger.error("embeddings", "failed:", err);
62
+ ollamaAvailable = null; // Reset health check — may be transient
63
+ return null;
64
+ }
65
+ }
66
+ export function clearEmbeddingCache() {
67
+ embeddingCache.clear();
68
+ ollamaAvailable = null;
69
+ }
70
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/embeddings/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,iEAAiE;AACjE,MAAM,cAAc,GAAG,IAAI,QAAQ,CAAmB,IAAI,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,YAAY;AAEzF,IAAI,eAAe,GAAmB,IAAI,CAAC;AAE3C,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,eAAe,KAAK,IAAI;QAAE,OAAO,eAAe,CAAC;IACrD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,WAAW,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChG,eAAe,GAAG,GAAG,CAAC,EAAE,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,eAAe,GAAG,KAAK,CAAC;IAC1B,CAAC;IACD,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,mBAAmB,CAAC,MAMnC;IACC,MAAM,KAAK,GAAG,CAAC,UAAU,MAAM,CAAC,IAAI,EAAE,EAAE,UAAU,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IACjE,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,UAAU,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAChE,IAAI,MAAM,CAAC,IAAI,EAAE,GAAG;QAAE,KAAK,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7D,IAAI,MAAM,CAAC,IAAI,EAAE,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IACzE,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,WAAW,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACnE,IAAI,MAAM,CAAC,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9D,IAAI,MAAM,CAAC,WAAW;QAAE,KAAK,CAAC,IAAI,CAAC,UAAU,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACjF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY;IAClD,iCAAiC;IACjC,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,4BAA4B;IAC5B,IAAI,CAAC,CAAC,MAAM,WAAW,EAAE,CAAC;QAAE,OAAO,IAAI,CAAC;IAExC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,iBAAiB,EAAE;YAC7D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;YAClE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC;SACpC,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,CAAC,KAAK,CAAC,YAAY,EAAE,mBAAmB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YAC5D,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA4B,CAAC;QAC3D,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QAEjC,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACpC,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,YAAY,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;QAC3C,eAAe,GAAG,IAAI,CAAC,CAAC,wCAAwC;QAChE,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,cAAc,CAAC,KAAK,EAAE,CAAC;IACvB,eAAe,GAAG,IAAI,CAAC;AACzB,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Generate and store embeddings for all nodes that don't yet have them.
3
+ * Call after a full initial index to back-fill embeddings.
4
+ */
5
+ export declare function backfillEmbeddings(): Promise<{
6
+ updated: number;
7
+ skipped: number;
8
+ }>;
9
+ export { generateEmbedding, buildEmbeddingInput, checkOllama } from "./client.js";
10
+ export { rankBySimilarity, cosineSimilarity } from "./similarity.js";
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/embeddings/index.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA8BxF;AAED,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { getAllNodes, updateNodeEmbedding } from "../db/index.js";
2
+ import { generateEmbedding, buildEmbeddingInput } from "./client.js";
3
+ /**
4
+ * Generate and store embeddings for all nodes that don't yet have them.
5
+ * Call after a full initial index to back-fill embeddings.
6
+ */
7
+ export async function backfillEmbeddings() {
8
+ const nodes = getAllNodes();
9
+ let updated = 0;
10
+ let skipped = 0;
11
+ for (const node of nodes) {
12
+ if (node.embedding) {
13
+ skipped++;
14
+ continue;
15
+ }
16
+ const text = buildEmbeddingInput({
17
+ name: node.name,
18
+ type: node.type,
19
+ summary: node.summary ?? undefined,
20
+ codeSnippet: node.code_snippet ?? undefined,
21
+ meta: node.meta ? JSON.parse(node.meta) : undefined,
22
+ });
23
+ const embedding = await generateEmbedding(text);
24
+ if (embedding) {
25
+ updateNodeEmbedding(node.id, embedding);
26
+ updated++;
27
+ }
28
+ else {
29
+ // Ollama unavailable — stop early
30
+ break;
31
+ }
32
+ }
33
+ return { updated, skipped };
34
+ }
35
+ export { generateEmbedding, buildEmbeddingInput, checkOllama } from "./client.js";
36
+ export { rankBySimilarity, cosineSimilarity } from "./similarity.js";
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/embeddings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGrE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,KAAK,GAAG,WAAW,EAAE,CAAC;IAC5B,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,OAAO,EAAE,CAAC;YACV,SAAS;QACX,CAAC;QAED,MAAM,IAAI,GAAG,mBAAmB,CAAC;YAC/B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,SAAS;YAClC,WAAW,EAAE,IAAI,CAAC,YAAY,IAAI,SAAS;YAC3C,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;SACpD,CAAC,CAAC;QAEH,MAAM,SAAS,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,SAAS,EAAE,CAAC;YACd,mBAAmB,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;YACxC,OAAO,EAAE,CAAC;QACZ,CAAC;aAAM,CAAC;YACN,kCAAkC;YAClC,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC9B,CAAC;AAED,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { GraphNode } from "../types.js";
2
+ export declare function cosineSimilarity(a: number[], b: number[]): number;
3
+ export interface RankedNode extends GraphNode {
4
+ similarity: number;
5
+ }
6
+ export declare function rankBySimilarity(queryVector: number[], candidates: GraphNode[], topK?: number): RankedNode[];
7
+ //# sourceMappingURL=similarity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"similarity.d.ts","sourceRoot":"","sources":["../../src/embeddings/similarity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAejE;AAED,MAAM,WAAW,UAAW,SAAQ,SAAS;IAC3C,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EAAE,EACrB,UAAU,EAAE,SAAS,EAAE,EACvB,IAAI,SAAK,GACR,UAAU,EAAE,CAad"}
@@ -0,0 +1,31 @@
1
+ export function cosineSimilarity(a, b) {
2
+ if (a.length !== b.length || a.length === 0)
3
+ return 0;
4
+ let dot = 0;
5
+ let magA = 0;
6
+ let magB = 0;
7
+ for (let i = 0; i < a.length; i++) {
8
+ dot += a[i] * b[i];
9
+ magA += a[i] * a[i];
10
+ magB += b[i] * b[i];
11
+ }
12
+ const denom = Math.sqrt(magA) * Math.sqrt(magB);
13
+ return denom === 0 ? 0 : dot / denom;
14
+ }
15
+ export function rankBySimilarity(queryVector, candidates, topK = 10) {
16
+ return candidates
17
+ .flatMap((node) => {
18
+ if (!node.embedding)
19
+ return [];
20
+ try {
21
+ const vec = JSON.parse(node.embedding);
22
+ return [{ ...node, similarity: cosineSimilarity(queryVector, vec) }];
23
+ }
24
+ catch {
25
+ return [];
26
+ }
27
+ })
28
+ .sort((a, b) => b.similarity - a.similarity)
29
+ .slice(0, topK);
30
+ }
31
+ //# sourceMappingURL=similarity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"similarity.js","sourceRoot":"","sources":["../../src/embeddings/similarity.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,gBAAgB,CAAC,CAAW,EAAE,CAAW;IACvD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAEtD,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,IAAI,GAAG,CAAC,CAAC;IAEb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACtB,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC;AACvC,CAAC;AAMD,MAAM,UAAU,gBAAgB,CAC9B,WAAqB,EACrB,UAAuB,EACvB,IAAI,GAAG,EAAE;IAET,OAAO,UAAU;SACd,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAChB,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAa,CAAC;YACnD,OAAO,CAAC,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,gBAAgB,CAAC,WAAW,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACvE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;SACD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;SAC3C,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AACpB,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * CLI — index a directory using the configured TokenOS parser.
3
+ *
4
+ * Usage: npm run index -- /path/to/project
5
+ * (Path arg overrides tokenos.config.json watchPath for one-shot indexing)
6
+ */
7
+ import "../db/index.js";
8
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/indexer/cli.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,gBAAgB,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * CLI — index a directory using the configured TokenOS parser.
3
+ *
4
+ * Usage: npm run index -- /path/to/project
5
+ * (Path arg overrides tokenos.config.json watchPath for one-shot indexing)
6
+ */
7
+ // Import db first to ensure the schema (tables, indexes) is initialized
8
+ import "../db/index.js";
9
+ import { indexDirectory } from "./indexer.js";
10
+ import { computeAllImportance } from "../utils/scoring.js";
11
+ import { logger } from "../utils/logger.js";
12
+ const targetPath = process.argv[2];
13
+ if (!targetPath) {
14
+ logger.error("cli", "Usage: npm run index -- /path/to/project");
15
+ process.exit(1);
16
+ }
17
+ logger.info("cli", `indexing: ${targetPath}`);
18
+ const totals = await indexDirectory(targetPath);
19
+ const { updated } = computeAllImportance();
20
+ logger.success("cli", `done: ${totals.files} files · ${totals.nodes} nodes · ${totals.edges} edges · ${updated} importance scores`);
21
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/indexer/cli.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,wEAAwE;AACxE,OAAO,gBAAgB,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACnC,IAAI,CAAC,UAAU,EAAE,CAAC;IAChB,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,0CAA0C,CAAC,CAAC;IAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,aAAa,UAAU,EAAE,CAAC,CAAC;AAC9C,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,CAAC;AAChD,MAAM,EAAE,OAAO,EAAE,GAAG,oBAAoB,EAAE,CAAC;AAC3C,MAAM,CAAC,OAAO,CACZ,KAAK,EACL,SAAS,MAAM,CAAC,KAAK,YAAY,MAAM,CAAC,KAAK,YAAY,MAAM,CAAC,KAAK,YAAY,OAAO,oBAAoB,CAC7G,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { type Ignore } from "ignore";
2
+ export declare function createIgnorer(workspacePath: string): Ignore;
3
+ export declare function isIgnored(ig: Ignore, workspacePath: string, testPath: string): boolean;
4
+ //# sourceMappingURL=ignore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ignore.d.ts","sourceRoot":"","sources":["../../src/indexer/ignore.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAK7C,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAmB3D;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAMtF"}
@@ -0,0 +1,30 @@
1
+ import ignore from "ignore";
2
+ import { logger } from "../utils/logger.js";
3
+ import { readFileSync, existsSync } from "fs";
4
+ import { join, relative } from "path";
5
+ export function createIgnorer(workspacePath) {
6
+ const ig = ignore();
7
+ // Default hardcoded ignores
8
+ ig.add(["node_modules", ".git", "dist", ".next", "build", "*.d.ts"]);
9
+ // Read .gitignore if it exists
10
+ const gitignorePath = join(workspacePath, ".gitignore");
11
+ if (existsSync(gitignorePath)) {
12
+ try {
13
+ const content = readFileSync(gitignorePath, "utf-8");
14
+ ig.add(content);
15
+ logger.success("indexer", "applied .gitignore rules");
16
+ }
17
+ catch (err) {
18
+ logger.error("indexer", "failed to read .gitignore:", err);
19
+ }
20
+ }
21
+ return ig;
22
+ }
23
+ export function isIgnored(ig, workspacePath, testPath) {
24
+ const relPath = relative(workspacePath, testPath);
25
+ if (!relPath || relPath === "." || relPath.startsWith("..")) {
26
+ return false; // Root directory or outside scope
27
+ }
28
+ return ig.ignores(relPath);
29
+ }
30
+ //# sourceMappingURL=ignore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ignore.js","sourceRoot":"","sources":["../../src/indexer/ignore.ts"],"names":[],"mappings":"AAAA,OAAO,MAAuB,MAAM,QAAQ,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAEtC,MAAM,UAAU,aAAa,CAAC,aAAqB;IACjD,MAAM,EAAE,GAAG,MAAM,EAAY,CAAC;IAE9B,4BAA4B;IAC5B,EAAE,CAAC,GAAG,CAAC,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IAErE,+BAA+B;IAC/B,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IACxD,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;YACrD,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAChB,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,0BAA0B,CAAC,CAAC;QACxD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,4BAA4B,EAAE,GAAG,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,EAAU,EAAE,aAAqB,EAAE,QAAgB;IAC3E,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IAClD,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5D,OAAO,KAAK,CAAC,CAAC,kCAAkC;IAClD,CAAC;IACD,OAAO,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { parseFile, removeFile, hashContent } from "./parser.js";
2
+ export type { ParseResult } from "./parser.js";
3
+ export { indexFile, removeFile as removeIndexedFile, indexDirectory } from "./indexer.js";
4
+ export { startWatcher } from "./watcher.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACjE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,UAAU,IAAI,iBAAiB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC1F,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { parseFile, removeFile, hashContent } from "./parser.js";
2
+ export { indexFile, removeFile as removeIndexedFile, indexDirectory } from "./indexer.js";
3
+ export { startWatcher } from "./watcher.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEjE,OAAO,EAAE,SAAS,EAAE,UAAU,IAAI,iBAAiB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC1F,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}