claude-eidetic 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +333 -0
- package/dist/config.d.ts +25 -0
- package/dist/config.js +29 -14
- package/dist/core/cleanup.d.ts +8 -0
- package/dist/core/cleanup.js +41 -0
- package/dist/core/doc-indexer.d.ts +13 -0
- package/dist/core/doc-indexer.js +76 -0
- package/dist/core/doc-searcher.d.ts +13 -0
- package/dist/core/doc-searcher.js +65 -0
- package/dist/core/file-category.d.ts +7 -0
- package/dist/core/file-category.js +75 -0
- package/dist/core/indexer.js +12 -4
- package/dist/core/preview.d.ts +1 -2
- package/dist/core/preview.js +2 -5
- package/dist/core/repo-map.d.ts +33 -0
- package/dist/core/repo-map.js +144 -0
- package/dist/core/searcher.d.ts +1 -13
- package/dist/core/searcher.js +20 -24
- package/dist/core/snapshot-io.js +2 -2
- package/dist/core/sync.d.ts +5 -25
- package/dist/core/sync.js +90 -65
- package/dist/core/targeted-indexer.d.ts +19 -0
- package/dist/core/targeted-indexer.js +127 -0
- package/dist/embedding/factory.d.ts +0 -13
- package/dist/embedding/factory.js +0 -17
- package/dist/embedding/openai.d.ts +2 -14
- package/dist/embedding/openai.js +7 -20
- package/dist/errors.d.ts +2 -0
- package/dist/errors.js +2 -0
- package/dist/format.d.ts +12 -0
- package/dist/format.js +160 -31
- package/dist/hooks/post-tool-use.d.ts +13 -0
- package/dist/hooks/post-tool-use.js +113 -0
- package/dist/hooks/stop-hook.d.ts +11 -0
- package/dist/hooks/stop-hook.js +121 -0
- package/dist/hooks/targeted-runner.d.ts +11 -0
- package/dist/hooks/targeted-runner.js +66 -0
- package/dist/index.js +102 -24
- package/dist/infra/qdrant-bootstrap.js +14 -12
- package/dist/memory/history.d.ts +19 -0
- package/dist/memory/history.js +40 -0
- package/dist/memory/llm.d.ts +2 -0
- package/dist/memory/llm.js +56 -0
- package/dist/memory/prompts.d.ts +5 -0
- package/dist/memory/prompts.js +36 -0
- package/dist/memory/reconciler.d.ts +12 -0
- package/dist/memory/reconciler.js +36 -0
- package/dist/memory/store.d.ts +20 -0
- package/dist/memory/store.js +206 -0
- package/dist/memory/types.d.ts +28 -0
- package/dist/memory/types.js +2 -0
- package/dist/paths.d.ts +3 -4
- package/dist/paths.js +14 -4
- package/dist/precompact/hook.d.ts +9 -0
- package/dist/precompact/hook.js +170 -0
- package/dist/precompact/index-runner.d.ts +9 -0
- package/dist/precompact/index-runner.js +52 -0
- package/dist/precompact/note-writer.d.ts +15 -0
- package/dist/precompact/note-writer.js +109 -0
- package/dist/precompact/session-indexer.d.ts +13 -0
- package/dist/precompact/session-indexer.js +31 -0
- package/dist/precompact/tier0-inject.d.ts +16 -0
- package/dist/precompact/tier0-inject.js +88 -0
- package/dist/precompact/tier0-writer.d.ts +16 -0
- package/dist/precompact/tier0-writer.js +74 -0
- package/dist/precompact/transcript-parser.d.ts +10 -0
- package/dist/precompact/transcript-parser.js +148 -0
- package/dist/precompact/types.d.ts +93 -0
- package/dist/precompact/types.js +5 -0
- package/dist/precompact/utils.d.ts +29 -0
- package/dist/precompact/utils.js +95 -0
- package/dist/setup-message.d.ts +3 -0
- package/dist/setup-message.js +42 -0
- package/dist/splitter/ast.js +84 -22
- package/dist/splitter/line.d.ts +0 -4
- package/dist/splitter/line.js +1 -7
- package/dist/splitter/symbol-extract.d.ts +16 -0
- package/dist/splitter/symbol-extract.js +61 -0
- package/dist/splitter/types.d.ts +5 -0
- package/dist/splitter/types.js +1 -1
- package/dist/state/doc-metadata.d.ts +18 -0
- package/dist/state/doc-metadata.js +59 -0
- package/dist/state/registry.d.ts +1 -3
- package/dist/state/snapshot.d.ts +0 -1
- package/dist/state/snapshot.js +3 -19
- package/dist/tool-schemas.d.ts +251 -1
- package/dist/tool-schemas.js +307 -0
- package/dist/tools.d.ts +69 -0
- package/dist/tools.js +286 -17
- package/dist/vectordb/milvus.d.ts +7 -5
- package/dist/vectordb/milvus.js +116 -19
- package/dist/vectordb/qdrant.d.ts +8 -10
- package/dist/vectordb/qdrant.js +105 -33
- package/dist/vectordb/types.d.ts +20 -0
- package/messages.yaml +50 -0
- package/package.json +31 -6
package/dist/splitter/ast.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
|
+
import { MAX_CHUNK_CHARS } from './types.js';
|
|
3
|
+
import { extractSymbolInfo, isContainerType } from './symbol-extract.js';
|
|
2
4
|
// tree-sitter and language parsers are native CommonJS modules
|
|
3
5
|
const require = createRequire(import.meta.url);
|
|
4
6
|
const Parser = require('tree-sitter');
|
|
@@ -23,29 +25,67 @@ const languageParsers = {
|
|
|
23
25
|
};
|
|
24
26
|
// AST node types that represent logical code units per language
|
|
25
27
|
const SPLITTABLE_TYPES = {
|
|
26
|
-
javascript: ['function_declaration', 'arrow_function', 'class_declaration', 'method_definition'
|
|
27
|
-
typescript: [
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
javascript: ['function_declaration', 'arrow_function', 'class_declaration', 'method_definition'],
|
|
29
|
+
typescript: [
|
|
30
|
+
'function_declaration',
|
|
31
|
+
'arrow_function',
|
|
32
|
+
'class_declaration',
|
|
33
|
+
'method_definition',
|
|
34
|
+
'interface_declaration',
|
|
35
|
+
'type_alias_declaration',
|
|
36
|
+
],
|
|
37
|
+
tsx: [
|
|
38
|
+
'function_declaration',
|
|
39
|
+
'arrow_function',
|
|
40
|
+
'class_declaration',
|
|
41
|
+
'method_definition',
|
|
42
|
+
'interface_declaration',
|
|
43
|
+
'type_alias_declaration',
|
|
44
|
+
],
|
|
45
|
+
python: [
|
|
46
|
+
'function_definition',
|
|
47
|
+
'class_definition',
|
|
48
|
+
'decorated_definition',
|
|
49
|
+
'async_function_definition',
|
|
50
|
+
],
|
|
51
|
+
java: [
|
|
52
|
+
'method_declaration',
|
|
53
|
+
'class_declaration',
|
|
54
|
+
'interface_declaration',
|
|
55
|
+
'constructor_declaration',
|
|
56
|
+
],
|
|
31
57
|
cpp: ['function_definition', 'class_specifier', 'namespace_definition', 'declaration'],
|
|
32
|
-
go: [
|
|
58
|
+
go: [
|
|
59
|
+
'function_declaration',
|
|
60
|
+
'method_declaration',
|
|
61
|
+
'type_declaration',
|
|
62
|
+
'var_declaration',
|
|
63
|
+
'const_declaration',
|
|
64
|
+
],
|
|
33
65
|
rust: ['function_item', 'impl_item', 'struct_item', 'enum_item', 'trait_item', 'mod_item'],
|
|
34
|
-
csharp: [
|
|
66
|
+
csharp: [
|
|
67
|
+
'method_declaration',
|
|
68
|
+
'class_declaration',
|
|
69
|
+
'interface_declaration',
|
|
70
|
+
'struct_declaration',
|
|
71
|
+
'enum_declaration',
|
|
72
|
+
],
|
|
35
73
|
};
|
|
36
|
-
// Map aliases to canonical language names for node type lookup
|
|
37
74
|
const LANG_CANONICAL = {
|
|
38
|
-
js: 'javascript',
|
|
39
|
-
|
|
75
|
+
js: 'javascript',
|
|
76
|
+
ts: 'typescript',
|
|
77
|
+
py: 'python',
|
|
78
|
+
rs: 'rust',
|
|
79
|
+
'c++': 'cpp',
|
|
80
|
+
c: 'cpp',
|
|
81
|
+
cs: 'csharp',
|
|
40
82
|
};
|
|
41
|
-
const MAX_CHUNK_CHARS = 2500;
|
|
42
83
|
export class AstSplitter {
|
|
43
84
|
parser = new Parser();
|
|
44
85
|
currentLang = '';
|
|
45
86
|
// Shared across all AstSplitter instances — one cache per process
|
|
46
87
|
static langCache = new Map();
|
|
47
88
|
static resolveLanguage(lang) {
|
|
48
|
-
// Resolve alias to canonical name first — prevents duplicate cache entries
|
|
49
89
|
const canonical = LANG_CANONICAL[lang] ?? lang;
|
|
50
90
|
const cached = AstSplitter.langCache.get(canonical);
|
|
51
91
|
if (cached)
|
|
@@ -59,7 +99,7 @@ export class AstSplitter {
|
|
|
59
99
|
return mod;
|
|
60
100
|
}
|
|
61
101
|
catch (err) {
|
|
62
|
-
console.warn(`Failed to load tree-sitter parser for "${lang}"
|
|
102
|
+
console.warn(`Failed to load tree-sitter parser for "${lang}":`, err);
|
|
63
103
|
return null;
|
|
64
104
|
}
|
|
65
105
|
}
|
|
@@ -68,10 +108,9 @@ export class AstSplitter {
|
|
|
68
108
|
const canonical = LANG_CANONICAL[lang] ?? lang;
|
|
69
109
|
const langModule = AstSplitter.resolveLanguage(lang);
|
|
70
110
|
if (!langModule) {
|
|
71
|
-
return [];
|
|
111
|
+
return [];
|
|
72
112
|
}
|
|
73
113
|
try {
|
|
74
|
-
// Skip setLanguage() if parser is already configured for this language
|
|
75
114
|
if (canonical !== this.currentLang) {
|
|
76
115
|
this.parser.setLanguage(langModule);
|
|
77
116
|
this.currentLang = canonical;
|
|
@@ -81,14 +120,13 @@ export class AstSplitter {
|
|
|
81
120
|
return [];
|
|
82
121
|
const nodeTypes = SPLITTABLE_TYPES[canonical] ?? [];
|
|
83
122
|
const rawChunks = this.extractChunks(tree.rootNode, code, nodeTypes, language, filePath);
|
|
84
|
-
// If no meaningful chunks found, return empty (caller will use line splitter)
|
|
85
123
|
if (rawChunks.length === 0)
|
|
86
124
|
return [];
|
|
87
125
|
return this.refineChunks(rawChunks);
|
|
88
126
|
}
|
|
89
127
|
catch (err) {
|
|
90
|
-
console.warn(`AST parse failed for "${filePath}" (${language})
|
|
91
|
-
return [];
|
|
128
|
+
console.warn(`AST parse failed for "${filePath}" (${language}):`, err);
|
|
129
|
+
return [];
|
|
92
130
|
}
|
|
93
131
|
}
|
|
94
132
|
static isSupported(language) {
|
|
@@ -96,21 +134,37 @@ export class AstSplitter {
|
|
|
96
134
|
}
|
|
97
135
|
extractChunks(node, code, splittableTypes, language, filePath) {
|
|
98
136
|
const chunks = [];
|
|
99
|
-
const traverse = (current) => {
|
|
137
|
+
const traverse = (current, parentName) => {
|
|
100
138
|
if (splittableTypes.includes(current.type)) {
|
|
101
139
|
const text = code.slice(current.startIndex, current.endIndex);
|
|
102
140
|
if (text.trim().length > 0) {
|
|
103
|
-
|
|
141
|
+
const symbolInfo = extractSymbolInfo(current, code, parentName);
|
|
142
|
+
const chunk = {
|
|
104
143
|
content: text,
|
|
105
144
|
startLine: current.startPosition.row + 1,
|
|
106
145
|
endLine: current.endPosition.row + 1,
|
|
107
146
|
language,
|
|
108
147
|
filePath,
|
|
109
|
-
}
|
|
148
|
+
};
|
|
149
|
+
if (symbolInfo) {
|
|
150
|
+
chunk.symbolName = symbolInfo.name;
|
|
151
|
+
chunk.symbolKind = symbolInfo.kind;
|
|
152
|
+
chunk.symbolSignature = symbolInfo.signature;
|
|
153
|
+
if (parentName)
|
|
154
|
+
chunk.parentSymbol = parentName;
|
|
155
|
+
}
|
|
156
|
+
chunks.push(chunk);
|
|
157
|
+
// If this is a container, pass its name as parentName to children
|
|
158
|
+
if (isContainerType(current.type) && symbolInfo?.name) {
|
|
159
|
+
for (const child of current.children) {
|
|
160
|
+
traverse(child, symbolInfo.name);
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
110
164
|
}
|
|
111
165
|
}
|
|
112
166
|
for (const child of current.children) {
|
|
113
|
-
traverse(child);
|
|
167
|
+
traverse(child, parentName);
|
|
114
168
|
}
|
|
115
169
|
};
|
|
116
170
|
traverse(node);
|
|
@@ -144,6 +198,10 @@ export class AstSplitter {
|
|
|
144
198
|
endLine: startLine + lineCount - 1,
|
|
145
199
|
language: chunk.language,
|
|
146
200
|
filePath: chunk.filePath,
|
|
201
|
+
symbolName: chunk.symbolName,
|
|
202
|
+
symbolKind: chunk.symbolKind,
|
|
203
|
+
symbolSignature: chunk.symbolSignature,
|
|
204
|
+
parentSymbol: chunk.parentSymbol,
|
|
147
205
|
});
|
|
148
206
|
current = addition;
|
|
149
207
|
startLine = chunk.startLine + i;
|
|
@@ -161,6 +219,10 @@ export class AstSplitter {
|
|
|
161
219
|
endLine: startLine + lineCount - 1,
|
|
162
220
|
language: chunk.language,
|
|
163
221
|
filePath: chunk.filePath,
|
|
222
|
+
symbolName: chunk.symbolName,
|
|
223
|
+
symbolKind: chunk.symbolKind,
|
|
224
|
+
symbolSignature: chunk.symbolSignature,
|
|
225
|
+
parentSymbol: chunk.parentSymbol,
|
|
164
226
|
});
|
|
165
227
|
}
|
|
166
228
|
return subChunks;
|
package/dist/splitter/line.d.ts
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import type { Splitter, CodeChunk } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Simple line-based splitter. Used as fallback when tree-sitter
|
|
4
|
-
* doesn't support the language or fails to parse.
|
|
5
|
-
*/
|
|
6
2
|
export declare class LineSplitter implements Splitter {
|
|
7
3
|
private chunkLines;
|
|
8
4
|
private overlapLines;
|
package/dist/splitter/line.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
+
import { MAX_CHUNK_CHARS } from './types.js';
|
|
1
2
|
const DEFAULT_CHUNK_LINES = 60;
|
|
2
3
|
const OVERLAP_LINES = 5;
|
|
3
|
-
const MAX_CHUNK_CHARS = 2500;
|
|
4
|
-
/**
|
|
5
|
-
* Simple line-based splitter. Used as fallback when tree-sitter
|
|
6
|
-
* doesn't support the language or fails to parse.
|
|
7
|
-
*/
|
|
8
4
|
export class LineSplitter {
|
|
9
5
|
chunkLines;
|
|
10
6
|
overlapLines;
|
|
@@ -72,9 +68,7 @@ export class LineSplitter {
|
|
|
72
68
|
startLine = chunk.startLine + i;
|
|
73
69
|
lineCount = 0;
|
|
74
70
|
}
|
|
75
|
-
// If a single line exceeds the limit, hard-split it by characters
|
|
76
71
|
if (addition.length > MAX_CHUNK_CHARS) {
|
|
77
|
-
// Flush anything accumulated before this line
|
|
78
72
|
if (current.length > 0) {
|
|
79
73
|
flush();
|
|
80
74
|
current = '';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface SymbolInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
kind: string;
|
|
4
|
+
signature: string;
|
|
5
|
+
}
|
|
6
|
+
interface AstNode {
|
|
7
|
+
type: string;
|
|
8
|
+
startIndex: number;
|
|
9
|
+
endIndex: number;
|
|
10
|
+
children: AstNode[];
|
|
11
|
+
text?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function extractSymbolInfo(node: AstNode, code: string, parentName?: string): SymbolInfo | undefined;
|
|
14
|
+
export declare function isContainerType(nodeType: string): boolean;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=symbol-extract.d.ts.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const CONTAINER_TYPES = new Set(['class_declaration', 'class_definition', 'interface_declaration']);
|
|
2
|
+
const KIND_MAP = {
|
|
3
|
+
function_declaration: 'function',
|
|
4
|
+
arrow_function: 'function',
|
|
5
|
+
async_function_definition: 'function',
|
|
6
|
+
function_definition: 'function',
|
|
7
|
+
class_declaration: 'class',
|
|
8
|
+
class_definition: 'class',
|
|
9
|
+
interface_declaration: 'interface',
|
|
10
|
+
method_definition: 'method',
|
|
11
|
+
method_declaration: 'method',
|
|
12
|
+
type_alias_declaration: 'type',
|
|
13
|
+
enum_declaration: 'enum',
|
|
14
|
+
// Java
|
|
15
|
+
constructor_declaration: 'constructor',
|
|
16
|
+
// Rust
|
|
17
|
+
function_item: 'function',
|
|
18
|
+
impl_item: 'impl',
|
|
19
|
+
struct_item: 'struct',
|
|
20
|
+
enum_item: 'enum',
|
|
21
|
+
trait_item: 'trait',
|
|
22
|
+
// C#
|
|
23
|
+
struct_declaration: 'struct',
|
|
24
|
+
};
|
|
25
|
+
function getIdentifier(node, code) {
|
|
26
|
+
const identChild = node.children.find((c) => c.type === 'identifier' || c.type === 'type_identifier' || c.type === 'name');
|
|
27
|
+
if (identChild)
|
|
28
|
+
return code.slice(identChild.startIndex, identChild.endIndex);
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
function extractSignature(node, code) {
|
|
32
|
+
const text = code.slice(node.startIndex, node.endIndex);
|
|
33
|
+
// Take first line up to opening brace or end of line
|
|
34
|
+
const firstLine = text.split('\n')[0];
|
|
35
|
+
const upToBrace = firstLine.split('{')[0].trimEnd();
|
|
36
|
+
const sig = upToBrace || firstLine;
|
|
37
|
+
return sig.length > 200 ? sig.slice(0, 200) + '…' : sig;
|
|
38
|
+
}
|
|
39
|
+
export function extractSymbolInfo(node, code, parentName) {
|
|
40
|
+
const nodeType = node.type;
|
|
41
|
+
// Handle export_statement: recurse into declaration child
|
|
42
|
+
if (nodeType === 'export_statement') {
|
|
43
|
+
const declChild = node.children.find((c) => c.type !== 'export' && c.type !== 'default' && c.type !== ';' && c.type !== 'identifier');
|
|
44
|
+
if (declChild)
|
|
45
|
+
return extractSymbolInfo(declChild, code, parentName);
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const kind = KIND_MAP[nodeType];
|
|
49
|
+
if (!kind)
|
|
50
|
+
return undefined;
|
|
51
|
+
const name = getIdentifier(node, code);
|
|
52
|
+
if (!name)
|
|
53
|
+
return undefined;
|
|
54
|
+
const signature = extractSignature(node, code);
|
|
55
|
+
void parentName; // consumed by caller to set chunk.parentSymbol
|
|
56
|
+
return { name, kind, signature };
|
|
57
|
+
}
|
|
58
|
+
export function isContainerType(nodeType) {
|
|
59
|
+
return CONTAINER_TYPES.has(nodeType);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=symbol-extract.js.map
|
package/dist/splitter/types.d.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
export declare const MAX_CHUNK_CHARS = 2500;
|
|
1
2
|
export interface CodeChunk {
|
|
2
3
|
content: string;
|
|
3
4
|
startLine: number;
|
|
4
5
|
endLine: number;
|
|
5
6
|
language: string;
|
|
6
7
|
filePath: string;
|
|
8
|
+
symbolName?: string;
|
|
9
|
+
symbolKind?: string;
|
|
10
|
+
symbolSignature?: string;
|
|
11
|
+
parentSymbol?: string;
|
|
7
12
|
}
|
|
8
13
|
export interface Splitter {
|
|
9
14
|
split(code: string, language: string, filePath: string): CodeChunk[];
|
package/dist/splitter/types.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export const MAX_CHUNK_CHARS = 2500;
|
|
2
2
|
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface DocEntry {
|
|
2
|
+
library: string;
|
|
3
|
+
topic: string;
|
|
4
|
+
source: string;
|
|
5
|
+
collectionName: string;
|
|
6
|
+
indexedAt: string;
|
|
7
|
+
ttlDays: number;
|
|
8
|
+
totalChunks: number;
|
|
9
|
+
}
|
|
10
|
+
export type DocMetadata = Record<string, DocEntry>;
|
|
11
|
+
export declare function loadDocMetadata(): DocMetadata;
|
|
12
|
+
export declare function saveDocMetadata(metadata: DocMetadata): void;
|
|
13
|
+
export declare function upsertDocEntry(entry: DocEntry): void;
|
|
14
|
+
export declare function removeDocEntry(library: string, topic: string): boolean;
|
|
15
|
+
export declare function findDocEntries(library: string): DocEntry[];
|
|
16
|
+
export declare function isStale(entry: DocEntry): boolean;
|
|
17
|
+
export declare function listDocLibraries(): string[];
|
|
18
|
+
//# sourceMappingURL=doc-metadata.d.ts.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getDocMetadataPath } from '../paths.js';
|
|
4
|
+
function metadataKey(library, topic) {
|
|
5
|
+
return `${library.toLowerCase()}::${topic.toLowerCase()}`;
|
|
6
|
+
}
|
|
7
|
+
export function loadDocMetadata() {
|
|
8
|
+
const metadataPath = getDocMetadataPath();
|
|
9
|
+
try {
|
|
10
|
+
const data = fs.readFileSync(metadataPath, 'utf-8');
|
|
11
|
+
return JSON.parse(data);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function saveDocMetadata(metadata) {
|
|
18
|
+
const metadataPath = getDocMetadataPath();
|
|
19
|
+
const dir = path.dirname(metadataPath);
|
|
20
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
21
|
+
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2) + '\n', 'utf-8');
|
|
22
|
+
}
|
|
23
|
+
export function upsertDocEntry(entry) {
|
|
24
|
+
const metadata = loadDocMetadata();
|
|
25
|
+
const key = metadataKey(entry.library, entry.topic);
|
|
26
|
+
metadata[key] = entry;
|
|
27
|
+
saveDocMetadata(metadata);
|
|
28
|
+
}
|
|
29
|
+
export function removeDocEntry(library, topic) {
|
|
30
|
+
const metadata = loadDocMetadata();
|
|
31
|
+
const key = metadataKey(library, topic);
|
|
32
|
+
if (!(key in metadata))
|
|
33
|
+
return false;
|
|
34
|
+
Reflect.deleteProperty(metadata, key);
|
|
35
|
+
saveDocMetadata(metadata);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
export function findDocEntries(library) {
|
|
39
|
+
const metadata = loadDocMetadata();
|
|
40
|
+
const prefix = `${library.toLowerCase()}::`;
|
|
41
|
+
return Object.entries(metadata)
|
|
42
|
+
.filter(([key]) => key.startsWith(prefix))
|
|
43
|
+
.map(([, entry]) => entry);
|
|
44
|
+
}
|
|
45
|
+
export function isStale(entry) {
|
|
46
|
+
const indexedAt = new Date(entry.indexedAt).getTime();
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
const ttlMs = entry.ttlDays * 24 * 60 * 60 * 1000;
|
|
49
|
+
return now - indexedAt > ttlMs;
|
|
50
|
+
}
|
|
51
|
+
export function listDocLibraries() {
|
|
52
|
+
const metadata = loadDocMetadata();
|
|
53
|
+
const libs = new Set();
|
|
54
|
+
for (const entry of Object.values(metadata)) {
|
|
55
|
+
libs.add(entry.library);
|
|
56
|
+
}
|
|
57
|
+
return [...libs].sort();
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=doc-metadata.js.map
|
package/dist/state/registry.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
[project: string]: string;
|
|
3
|
-
}
|
|
1
|
+
type Registry = Record<string, string>;
|
|
4
2
|
export declare function registerProject(absolutePath: string): void;
|
|
5
3
|
export declare function resolveProject(project: string): string | undefined;
|
|
6
4
|
export declare function listProjects(): Registry;
|
package/dist/state/snapshot.d.ts
CHANGED
|
@@ -20,7 +20,6 @@ export declare class StateManager {
|
|
|
20
20
|
setIndexed(normalizedPath: string, totalFiles: number, totalChunks: number): void;
|
|
21
21
|
setError(normalizedPath: string, error: string): void;
|
|
22
22
|
remove(normalizedPath: string): void;
|
|
23
|
-
markExisting(normalizedPath: string, collectionName: string): void;
|
|
24
23
|
}
|
|
25
24
|
export declare function cleanupOrphanedSnapshots(vectordb: VectorDB): Promise<number>;
|
|
26
25
|
//# sourceMappingURL=snapshot.d.ts.map
|
package/dist/state/snapshot.js
CHANGED
|
@@ -46,14 +46,6 @@ export class StateManager {
|
|
|
46
46
|
remove(normalizedPath) {
|
|
47
47
|
this.states.delete(normalizedPath);
|
|
48
48
|
}
|
|
49
|
-
markExisting(normalizedPath, collectionName) {
|
|
50
|
-
this.states.set(normalizedPath, {
|
|
51
|
-
path: normalizedPath,
|
|
52
|
-
collectionName,
|
|
53
|
-
status: 'indexed',
|
|
54
|
-
lastIndexed: 'unknown (pre-existing)',
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
49
|
}
|
|
58
50
|
export async function cleanupOrphanedSnapshots(vectordb) {
|
|
59
51
|
const snapshotDir = getSnapshotDir();
|
|
@@ -61,16 +53,10 @@ export async function cleanupOrphanedSnapshots(vectordb) {
|
|
|
61
53
|
try {
|
|
62
54
|
if (!fs.existsSync(snapshotDir))
|
|
63
55
|
return 0;
|
|
64
|
-
const files = fs.readdirSync(snapshotDir).filter(f => f.endsWith('.json'));
|
|
56
|
+
const files = fs.readdirSync(snapshotDir).filter((f) => f.endsWith('.json'));
|
|
65
57
|
if (files.length === 0)
|
|
66
58
|
return 0;
|
|
67
|
-
// Connectivity probe: use a name unlikely to exist. If hasCollection throws
|
|
68
|
-
// or the vector DB is unreachable (Qdrant's hasCollection returns false on
|
|
69
|
-
// network error), we must not proceed -- deleting snapshots when we cannot
|
|
70
|
-
// confirm collection absence would destroy valid state.
|
|
71
59
|
const probeResult = await vectordb.hasCollection('__eidetic_connectivity_probe__');
|
|
72
|
-
// If the probe returns true for a name that should never exist, something is
|
|
73
|
-
// wrong -- skip cleanup to be safe.
|
|
74
60
|
if (probeResult) {
|
|
75
61
|
console.warn('Orphan cleanup skipped: connectivity probe returned unexpected result.');
|
|
76
62
|
return 0;
|
|
@@ -87,14 +73,12 @@ export async function cleanupOrphanedSnapshots(vectordb) {
|
|
|
87
73
|
}
|
|
88
74
|
}
|
|
89
75
|
catch (err) {
|
|
90
|
-
|
|
91
|
-
console.warn(`Skipping orphan check for ${collectionName}: ${err}`);
|
|
76
|
+
console.warn(`Skipping orphan check for ${collectionName}:`, err);
|
|
92
77
|
}
|
|
93
78
|
}
|
|
94
79
|
}
|
|
95
80
|
catch (err) {
|
|
96
|
-
|
|
97
|
-
console.warn(`Orphan cleanup skipped: ${err}`);
|
|
81
|
+
console.warn('Orphan cleanup skipped:', err);
|
|
98
82
|
}
|
|
99
83
|
return cleaned;
|
|
100
84
|
}
|