@getlore/cli 0.2.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.
- package/LICENSE +13 -0
- package/README.md +80 -0
- package/dist/cli/colors.d.ts +48 -0
- package/dist/cli/colors.js +48 -0
- package/dist/cli/commands/ask.d.ts +7 -0
- package/dist/cli/commands/ask.js +97 -0
- package/dist/cli/commands/auth.d.ts +10 -0
- package/dist/cli/commands/auth.js +484 -0
- package/dist/cli/commands/daemon.d.ts +22 -0
- package/dist/cli/commands/daemon.js +244 -0
- package/dist/cli/commands/docs.d.ts +7 -0
- package/dist/cli/commands/docs.js +188 -0
- package/dist/cli/commands/extensions.d.ts +7 -0
- package/dist/cli/commands/extensions.js +204 -0
- package/dist/cli/commands/misc.d.ts +7 -0
- package/dist/cli/commands/misc.js +172 -0
- package/dist/cli/commands/pending.d.ts +7 -0
- package/dist/cli/commands/pending.js +63 -0
- package/dist/cli/commands/projects.d.ts +7 -0
- package/dist/cli/commands/projects.js +136 -0
- package/dist/cli/commands/search.d.ts +7 -0
- package/dist/cli/commands/search.js +102 -0
- package/dist/cli/commands/skills.d.ts +24 -0
- package/dist/cli/commands/skills.js +447 -0
- package/dist/cli/commands/sources.d.ts +7 -0
- package/dist/cli/commands/sources.js +121 -0
- package/dist/cli/commands/sync.d.ts +31 -0
- package/dist/cli/commands/sync.js +768 -0
- package/dist/cli/helpers.d.ts +30 -0
- package/dist/cli/helpers.js +119 -0
- package/dist/core/auth.d.ts +62 -0
- package/dist/core/auth.js +330 -0
- package/dist/core/config.d.ts +41 -0
- package/dist/core/config.js +96 -0
- package/dist/core/data-repo.d.ts +31 -0
- package/dist/core/data-repo.js +146 -0
- package/dist/core/embedder.d.ts +22 -0
- package/dist/core/embedder.js +104 -0
- package/dist/core/git.d.ts +37 -0
- package/dist/core/git.js +140 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +5 -0
- package/dist/core/insight-extractor.d.ts +26 -0
- package/dist/core/insight-extractor.js +114 -0
- package/dist/core/local-search.d.ts +43 -0
- package/dist/core/local-search.js +221 -0
- package/dist/core/themes.d.ts +15 -0
- package/dist/core/themes.js +77 -0
- package/dist/core/types.d.ts +177 -0
- package/dist/core/types.js +9 -0
- package/dist/core/user-settings.d.ts +15 -0
- package/dist/core/user-settings.js +42 -0
- package/dist/core/vector-store-lance.d.ts +98 -0
- package/dist/core/vector-store-lance.js +384 -0
- package/dist/core/vector-store-supabase.d.ts +89 -0
- package/dist/core/vector-store-supabase.js +295 -0
- package/dist/core/vector-store.d.ts +131 -0
- package/dist/core/vector-store.js +503 -0
- package/dist/daemon-runner.d.ts +8 -0
- package/dist/daemon-runner.js +246 -0
- package/dist/extensions/config.d.ts +22 -0
- package/dist/extensions/config.js +102 -0
- package/dist/extensions/proposals.d.ts +30 -0
- package/dist/extensions/proposals.js +178 -0
- package/dist/extensions/registry.d.ts +35 -0
- package/dist/extensions/registry.js +309 -0
- package/dist/extensions/sandbox.d.ts +16 -0
- package/dist/extensions/sandbox.js +17 -0
- package/dist/extensions/types.d.ts +114 -0
- package/dist/extensions/types.js +4 -0
- package/dist/extensions/worker.d.ts +1 -0
- package/dist/extensions/worker.js +49 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +105 -0
- package/dist/mcp/handlers/archive-project.d.ts +51 -0
- package/dist/mcp/handlers/archive-project.js +112 -0
- package/dist/mcp/handlers/get-quotes.d.ts +27 -0
- package/dist/mcp/handlers/get-quotes.js +61 -0
- package/dist/mcp/handlers/get-source.d.ts +9 -0
- package/dist/mcp/handlers/get-source.js +40 -0
- package/dist/mcp/handlers/ingest.d.ts +25 -0
- package/dist/mcp/handlers/ingest.js +305 -0
- package/dist/mcp/handlers/list-projects.d.ts +4 -0
- package/dist/mcp/handlers/list-projects.js +16 -0
- package/dist/mcp/handlers/list-sources.d.ts +11 -0
- package/dist/mcp/handlers/list-sources.js +20 -0
- package/dist/mcp/handlers/research-agent.d.ts +21 -0
- package/dist/mcp/handlers/research-agent.js +369 -0
- package/dist/mcp/handlers/research.d.ts +22 -0
- package/dist/mcp/handlers/research.js +225 -0
- package/dist/mcp/handlers/retain.d.ts +18 -0
- package/dist/mcp/handlers/retain.js +92 -0
- package/dist/mcp/handlers/search.d.ts +52 -0
- package/dist/mcp/handlers/search.js +145 -0
- package/dist/mcp/handlers/sync.d.ts +47 -0
- package/dist/mcp/handlers/sync.js +211 -0
- package/dist/mcp/server.d.ts +10 -0
- package/dist/mcp/server.js +268 -0
- package/dist/mcp/tools.d.ts +16 -0
- package/dist/mcp/tools.js +297 -0
- package/dist/sync/config.d.ts +26 -0
- package/dist/sync/config.js +140 -0
- package/dist/sync/discover.d.ts +51 -0
- package/dist/sync/discover.js +190 -0
- package/dist/sync/index.d.ts +11 -0
- package/dist/sync/index.js +11 -0
- package/dist/sync/process.d.ts +50 -0
- package/dist/sync/process.js +285 -0
- package/dist/sync/processors.d.ts +24 -0
- package/dist/sync/processors.js +351 -0
- package/dist/tui/browse-handlers-ask.d.ts +30 -0
- package/dist/tui/browse-handlers-ask.js +372 -0
- package/dist/tui/browse-handlers-autocomplete.d.ts +49 -0
- package/dist/tui/browse-handlers-autocomplete.js +270 -0
- package/dist/tui/browse-handlers-extensions.d.ts +18 -0
- package/dist/tui/browse-handlers-extensions.js +107 -0
- package/dist/tui/browse-handlers-pending.d.ts +22 -0
- package/dist/tui/browse-handlers-pending.js +100 -0
- package/dist/tui/browse-handlers-research.d.ts +32 -0
- package/dist/tui/browse-handlers-research.js +363 -0
- package/dist/tui/browse-handlers-tools.d.ts +42 -0
- package/dist/tui/browse-handlers-tools.js +289 -0
- package/dist/tui/browse-handlers.d.ts +239 -0
- package/dist/tui/browse-handlers.js +1944 -0
- package/dist/tui/browse-render-extensions.d.ts +14 -0
- package/dist/tui/browse-render-extensions.js +114 -0
- package/dist/tui/browse-render-tools.d.ts +18 -0
- package/dist/tui/browse-render-tools.js +259 -0
- package/dist/tui/browse-render.d.ts +51 -0
- package/dist/tui/browse-render.js +599 -0
- package/dist/tui/browse-types.d.ts +142 -0
- package/dist/tui/browse-types.js +70 -0
- package/dist/tui/browse-ui.d.ts +10 -0
- package/dist/tui/browse-ui.js +432 -0
- package/dist/tui/browse.d.ts +17 -0
- package/dist/tui/browse.js +625 -0
- package/dist/tui/markdown.d.ts +22 -0
- package/dist/tui/markdown.js +223 -0
- package/package.json +71 -0
- package/plugins/claude-code/.claude-plugin/plugin.json +10 -0
- package/plugins/claude-code/.mcp.json +6 -0
- package/plugins/claude-code/skills/lore/SKILL.md +63 -0
- package/plugins/codex/SKILL.md +36 -0
- package/plugins/codex/agents/openai.yaml +10 -0
- package/plugins/gemini/GEMINI.md +31 -0
- package/plugins/gemini/gemini-extension.json +11 -0
- package/skills/generic-agent.md +99 -0
- package/skills/openclaw.md +67 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Search - Regex/pattern search in local source files
|
|
3
|
+
*
|
|
4
|
+
* Provides grep-like functionality for searching source content
|
|
5
|
+
* stored in the data directory. Tries ripgrep first for speed,
|
|
6
|
+
* falls back to native JavaScript regex.
|
|
7
|
+
*/
|
|
8
|
+
import { spawn } from 'child_process';
|
|
9
|
+
import { readdir, readFile } from 'fs/promises';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { existsSync } from 'fs';
|
|
12
|
+
/**
|
|
13
|
+
* Check if ripgrep is available
|
|
14
|
+
*/
|
|
15
|
+
async function hasRipgrep() {
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
const proc = spawn('rg', ['--version']);
|
|
18
|
+
proc.on('error', () => resolve(false));
|
|
19
|
+
proc.on('close', (code) => resolve(code === 0));
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Search using ripgrep (fast)
|
|
24
|
+
*/
|
|
25
|
+
async function searchWithRipgrep(sourcesDir, pattern, options) {
|
|
26
|
+
const { maxMatchesPerFile = 10, maxTotalResults = 100, ignoreCase = false, contextBefore = 0, contextAfter = 0, } = options;
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const args = [
|
|
29
|
+
'--json',
|
|
30
|
+
'--max-count', String(maxMatchesPerFile),
|
|
31
|
+
'--glob', 'content.md',
|
|
32
|
+
];
|
|
33
|
+
if (ignoreCase) {
|
|
34
|
+
args.push('--ignore-case');
|
|
35
|
+
}
|
|
36
|
+
if (contextBefore > 0) {
|
|
37
|
+
args.push('-B', String(contextBefore));
|
|
38
|
+
}
|
|
39
|
+
if (contextAfter > 0) {
|
|
40
|
+
args.push('-A', String(contextAfter));
|
|
41
|
+
}
|
|
42
|
+
args.push(pattern, sourcesDir);
|
|
43
|
+
const proc = spawn('rg', args);
|
|
44
|
+
let stdout = '';
|
|
45
|
+
let stderr = '';
|
|
46
|
+
proc.stdout.on('data', (data) => {
|
|
47
|
+
stdout += data.toString();
|
|
48
|
+
});
|
|
49
|
+
proc.stderr.on('data', (data) => {
|
|
50
|
+
stderr += data.toString();
|
|
51
|
+
});
|
|
52
|
+
proc.on('error', (err) => {
|
|
53
|
+
reject(err);
|
|
54
|
+
});
|
|
55
|
+
proc.on('close', (code) => {
|
|
56
|
+
// ripgrep returns 1 if no matches, 0 if matches, 2 if error
|
|
57
|
+
if (code === 2) {
|
|
58
|
+
reject(new Error(`ripgrep error: ${stderr}`));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const results = [];
|
|
62
|
+
const resultMap = new Map();
|
|
63
|
+
// Parse JSON lines output
|
|
64
|
+
const lines = stdout.split('\n').filter((l) => l.trim());
|
|
65
|
+
for (const line of lines) {
|
|
66
|
+
try {
|
|
67
|
+
const entry = JSON.parse(line);
|
|
68
|
+
if (entry.type === 'match') {
|
|
69
|
+
const filePath = entry.data.path.text;
|
|
70
|
+
// Extract source_id from path (e.g., /path/sources/{source_id}/content.md)
|
|
71
|
+
const parts = filePath.split(path.sep);
|
|
72
|
+
const contentIdx = parts.findIndex((p) => p === 'content.md');
|
|
73
|
+
const sourceId = contentIdx > 0 ? parts[contentIdx - 1] : path.basename(path.dirname(filePath));
|
|
74
|
+
let result = resultMap.get(sourceId);
|
|
75
|
+
if (!result) {
|
|
76
|
+
result = {
|
|
77
|
+
source_id: sourceId,
|
|
78
|
+
file_path: filePath,
|
|
79
|
+
matches: [],
|
|
80
|
+
};
|
|
81
|
+
resultMap.set(sourceId, result);
|
|
82
|
+
}
|
|
83
|
+
// Process submatches
|
|
84
|
+
for (const submatch of entry.data.submatches || []) {
|
|
85
|
+
result.matches.push({
|
|
86
|
+
line_number: entry.data.line_number,
|
|
87
|
+
line_content: entry.data.lines.text.replace(/\n$/, ''),
|
|
88
|
+
match_start: submatch.start,
|
|
89
|
+
match_end: submatch.end,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Skip invalid JSON lines
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Convert map to array and limit total results
|
|
99
|
+
for (const result of resultMap.values()) {
|
|
100
|
+
if (results.length >= maxTotalResults)
|
|
101
|
+
break;
|
|
102
|
+
results.push(result);
|
|
103
|
+
}
|
|
104
|
+
resolve(results);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Search using native JavaScript regex (fallback)
|
|
110
|
+
*/
|
|
111
|
+
async function searchWithNative(sourcesDir, pattern, options) {
|
|
112
|
+
const { maxMatchesPerFile = 10, maxTotalResults = 100, ignoreCase = false, } = options;
|
|
113
|
+
const results = [];
|
|
114
|
+
// Create regex
|
|
115
|
+
let regex;
|
|
116
|
+
try {
|
|
117
|
+
regex = new RegExp(pattern, ignoreCase ? 'gi' : 'g');
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
throw new Error(`Invalid regex pattern: ${pattern}`);
|
|
121
|
+
}
|
|
122
|
+
// Read all source directories
|
|
123
|
+
if (!existsSync(sourcesDir)) {
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
const entries = await readdir(sourcesDir, { withFileTypes: true });
|
|
127
|
+
for (const entry of entries) {
|
|
128
|
+
if (results.length >= maxTotalResults)
|
|
129
|
+
break;
|
|
130
|
+
if (!entry.isDirectory())
|
|
131
|
+
continue;
|
|
132
|
+
const contentPath = path.join(sourcesDir, entry.name, 'content.md');
|
|
133
|
+
if (!existsSync(contentPath))
|
|
134
|
+
continue;
|
|
135
|
+
try {
|
|
136
|
+
const content = await readFile(contentPath, 'utf-8');
|
|
137
|
+
const lines = content.split('\n');
|
|
138
|
+
const matches = [];
|
|
139
|
+
for (let i = 0; i < lines.length && matches.length < maxMatchesPerFile; i++) {
|
|
140
|
+
const line = lines[i];
|
|
141
|
+
regex.lastIndex = 0; // Reset regex state
|
|
142
|
+
let match;
|
|
143
|
+
while ((match = regex.exec(line)) !== null && matches.length < maxMatchesPerFile) {
|
|
144
|
+
matches.push({
|
|
145
|
+
line_number: i + 1,
|
|
146
|
+
line_content: line,
|
|
147
|
+
match_start: match.index,
|
|
148
|
+
match_end: match.index + match[0].length,
|
|
149
|
+
});
|
|
150
|
+
// Prevent infinite loop on zero-length matches
|
|
151
|
+
if (match[0].length === 0) {
|
|
152
|
+
regex.lastIndex++;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (matches.length > 0) {
|
|
157
|
+
results.push({
|
|
158
|
+
source_id: entry.name,
|
|
159
|
+
file_path: contentPath,
|
|
160
|
+
matches,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// Skip files that can't be read
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return results;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Search local source files using regex pattern
|
|
172
|
+
*
|
|
173
|
+
* @param dataDir - The lore data directory
|
|
174
|
+
* @param pattern - Regex pattern to search for
|
|
175
|
+
* @param options - Search options
|
|
176
|
+
* @returns Array of search results with matches
|
|
177
|
+
*/
|
|
178
|
+
export async function searchLocalFiles(dataDir, pattern, options = {}) {
|
|
179
|
+
const sourcesDir = path.join(dataDir, 'sources');
|
|
180
|
+
if (!existsSync(sourcesDir)) {
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
// Try ripgrep first, fall back to native
|
|
184
|
+
const useRipgrep = await hasRipgrep();
|
|
185
|
+
if (useRipgrep) {
|
|
186
|
+
try {
|
|
187
|
+
return await searchWithRipgrep(sourcesDir, pattern, options);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// Fall back to native on error
|
|
191
|
+
return await searchWithNative(sourcesDir, pattern, options);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return await searchWithNative(sourcesDir, pattern, options);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Get a snippet of text around a match for display
|
|
198
|
+
*/
|
|
199
|
+
export function getMatchSnippet(lineContent, matchStart, matchEnd, maxLength = 100) {
|
|
200
|
+
const matchText = lineContent.slice(matchStart, matchEnd);
|
|
201
|
+
// Calculate window around match
|
|
202
|
+
const halfWindow = Math.floor((maxLength - matchText.length) / 2);
|
|
203
|
+
let start = Math.max(0, matchStart - halfWindow);
|
|
204
|
+
let end = Math.min(lineContent.length, matchEnd + halfWindow);
|
|
205
|
+
// Adjust if we're near edges
|
|
206
|
+
if (start === 0) {
|
|
207
|
+
end = Math.min(lineContent.length, maxLength);
|
|
208
|
+
}
|
|
209
|
+
else if (end === lineContent.length) {
|
|
210
|
+
start = Math.max(0, lineContent.length - maxLength);
|
|
211
|
+
}
|
|
212
|
+
let snippet = lineContent.slice(start, end);
|
|
213
|
+
// Add ellipses
|
|
214
|
+
if (start > 0) {
|
|
215
|
+
snippet = '...' + snippet;
|
|
216
|
+
}
|
|
217
|
+
if (end < lineContent.length) {
|
|
218
|
+
snippet = snippet + '...';
|
|
219
|
+
}
|
|
220
|
+
return snippet;
|
|
221
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lore - Theme Definitions
|
|
3
|
+
*
|
|
4
|
+
* Pre-defined themes for insight extraction from sources.
|
|
5
|
+
* Adapted from granola-extractor.
|
|
6
|
+
*/
|
|
7
|
+
export interface ThemeDefinition {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
prompt: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const THEMES: ThemeDefinition[];
|
|
13
|
+
export declare function getThemeById(id: string): ThemeDefinition | undefined;
|
|
14
|
+
export declare function getThemeNames(): string[];
|
|
15
|
+
export declare function getThemePromptList(): string;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lore - Theme Definitions
|
|
3
|
+
*
|
|
4
|
+
* Pre-defined themes for insight extraction from sources.
|
|
5
|
+
* Adapted from granola-extractor.
|
|
6
|
+
*/
|
|
7
|
+
export const THEMES = [
|
|
8
|
+
{
|
|
9
|
+
id: 'pain-points',
|
|
10
|
+
name: 'Pain Points',
|
|
11
|
+
prompt: 'Problems, frustrations, difficulties, blockers, challenges, risks identified',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: 'feature-requests',
|
|
15
|
+
name: 'Feature Requests',
|
|
16
|
+
prompt: 'Desired features, wishlist items, suggestions, improvements, capabilities needed',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: 'positive-feedback',
|
|
20
|
+
name: 'Positive Feedback',
|
|
21
|
+
prompt: 'Strengths, what works well, advantages, praise, value delivered',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'pricing',
|
|
25
|
+
name: 'Pricing & Value',
|
|
26
|
+
prompt: 'Cost, pricing, value perception, ROI, budget, willingness to pay, financial considerations',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'competition',
|
|
30
|
+
name: 'Competition',
|
|
31
|
+
prompt: 'Competitors, alternatives, market comparison, competitive advantages/disadvantages',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: 'workflow',
|
|
35
|
+
name: 'Workflow & Process',
|
|
36
|
+
prompt: 'How things work, processes, workarounds, current state, tools and methods used',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'decisions',
|
|
40
|
+
name: 'Decisions',
|
|
41
|
+
prompt: 'Key decisions, conclusions, action items, next steps, agreements, choices made',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'questions',
|
|
45
|
+
name: 'Open Questions',
|
|
46
|
+
prompt: 'Unanswered questions, uncertainties, things needing clarification, follow-ups',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 'requirements',
|
|
50
|
+
name: 'Requirements',
|
|
51
|
+
prompt: 'Must-haves, specifications, constraints, acceptance criteria, needs, dependencies',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'insights',
|
|
55
|
+
name: 'Key Insights',
|
|
56
|
+
prompt: 'Important learnings, discoveries, observations, patterns, strategic insights',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: 'data-points',
|
|
60
|
+
name: 'Data & Metrics',
|
|
61
|
+
prompt: 'Statistics, numbers, measurements, KPIs, benchmarks, quantitative findings',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'opportunities',
|
|
65
|
+
name: 'Opportunities',
|
|
66
|
+
prompt: 'Market opportunities, growth areas, untapped potential, strategic openings',
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
export function getThemeById(id) {
|
|
70
|
+
return THEMES.find((t) => t.id === id);
|
|
71
|
+
}
|
|
72
|
+
export function getThemeNames() {
|
|
73
|
+
return THEMES.map((t) => t.id);
|
|
74
|
+
}
|
|
75
|
+
export function getThemePromptList() {
|
|
76
|
+
return THEMES.map((t) => `- ${t.id}: ${t.prompt}`).join('\n');
|
|
77
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lore - Core Types
|
|
3
|
+
*
|
|
4
|
+
* A three-layer knowledge architecture:
|
|
5
|
+
* 1. Source Documents - Immutable original content (interviews, transcripts, docs)
|
|
6
|
+
* 2. Extracted Insights - Quotes, themes, decisions with links back to sources
|
|
7
|
+
* 3. Working Memory - Synthesized context for agent consumption
|
|
8
|
+
*/
|
|
9
|
+
export type SourceType = string;
|
|
10
|
+
export type SearchMode = 'semantic' | 'keyword' | 'hybrid' | 'regex';
|
|
11
|
+
export type ContentType = 'interview' | 'meeting' | 'conversation' | 'document' | 'note' | 'analysis' | 'survey' | 'research' | 'decision' | 'insight' | 'requirement';
|
|
12
|
+
export interface SourceDocument {
|
|
13
|
+
id: string;
|
|
14
|
+
source_type: SourceType;
|
|
15
|
+
source_id: string;
|
|
16
|
+
source_path?: string;
|
|
17
|
+
title: string;
|
|
18
|
+
content: string;
|
|
19
|
+
content_type: ContentType;
|
|
20
|
+
created_at: string;
|
|
21
|
+
imported_at: string;
|
|
22
|
+
participants?: string[];
|
|
23
|
+
projects: string[];
|
|
24
|
+
tags: string[];
|
|
25
|
+
}
|
|
26
|
+
export interface Citation {
|
|
27
|
+
source_id: string;
|
|
28
|
+
location?: string;
|
|
29
|
+
context?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface Quote {
|
|
32
|
+
id: string;
|
|
33
|
+
text: string;
|
|
34
|
+
speaker?: 'user' | 'participant' | 'ai' | 'unknown';
|
|
35
|
+
speaker_name?: string;
|
|
36
|
+
timestamp?: string;
|
|
37
|
+
theme?: string;
|
|
38
|
+
citation: Citation;
|
|
39
|
+
}
|
|
40
|
+
export interface Theme {
|
|
41
|
+
name: string;
|
|
42
|
+
evidence: Quote[];
|
|
43
|
+
summary?: string;
|
|
44
|
+
}
|
|
45
|
+
export type ThemeName = 'pain-points' | 'feature-requests' | 'positive-feedback' | 'pricing' | 'competition' | 'workflow' | 'decisions' | 'requirements' | 'insights';
|
|
46
|
+
export interface Decision {
|
|
47
|
+
id: string;
|
|
48
|
+
decision: string;
|
|
49
|
+
rationale: string;
|
|
50
|
+
alternatives_considered?: string[];
|
|
51
|
+
made_at: string;
|
|
52
|
+
citation: Citation;
|
|
53
|
+
project_id: string;
|
|
54
|
+
}
|
|
55
|
+
export interface Requirement {
|
|
56
|
+
id: string;
|
|
57
|
+
description: string;
|
|
58
|
+
priority: 'must' | 'should' | 'could' | 'wont';
|
|
59
|
+
source_quotes: Quote[];
|
|
60
|
+
project_id: string;
|
|
61
|
+
}
|
|
62
|
+
export type ProjectStatus = 'active' | 'paused' | 'completed' | 'archived';
|
|
63
|
+
export interface Project {
|
|
64
|
+
id: string;
|
|
65
|
+
name: string;
|
|
66
|
+
description: string;
|
|
67
|
+
status: ProjectStatus;
|
|
68
|
+
created_at: string;
|
|
69
|
+
updated_at: string;
|
|
70
|
+
parent_id?: string;
|
|
71
|
+
source_count?: number;
|
|
72
|
+
quote_count?: number;
|
|
73
|
+
decision_count?: number;
|
|
74
|
+
}
|
|
75
|
+
export type LineageEventType = 'created' | 'decision' | 'pivot' | 'milestone' | 'insight' | 'delegation';
|
|
76
|
+
export interface LineageEvent {
|
|
77
|
+
id: string;
|
|
78
|
+
project_id: string;
|
|
79
|
+
event_type: LineageEventType;
|
|
80
|
+
title: string;
|
|
81
|
+
description: string;
|
|
82
|
+
timestamp: string;
|
|
83
|
+
source_ids: string[];
|
|
84
|
+
}
|
|
85
|
+
export interface SourceRecord {
|
|
86
|
+
id: string;
|
|
87
|
+
title: string;
|
|
88
|
+
source_type: SourceType;
|
|
89
|
+
content_type: ContentType;
|
|
90
|
+
projects: string;
|
|
91
|
+
tags: string;
|
|
92
|
+
created_at: string;
|
|
93
|
+
summary: string;
|
|
94
|
+
themes_json: string;
|
|
95
|
+
quotes_json: string;
|
|
96
|
+
has_full_content: boolean;
|
|
97
|
+
vector: number[];
|
|
98
|
+
source_url?: string;
|
|
99
|
+
source_name?: string;
|
|
100
|
+
}
|
|
101
|
+
export interface ChunkRecord {
|
|
102
|
+
id: string;
|
|
103
|
+
source_id: string;
|
|
104
|
+
content: string;
|
|
105
|
+
type: 'quote' | 'theme' | 'summary' | 'decision' | 'requirement' | 'note' | 'insight';
|
|
106
|
+
theme_name?: string;
|
|
107
|
+
speaker?: string;
|
|
108
|
+
timestamp?: string;
|
|
109
|
+
vector: number[];
|
|
110
|
+
}
|
|
111
|
+
export interface SearchResult {
|
|
112
|
+
source_id: string;
|
|
113
|
+
title: string;
|
|
114
|
+
source_type: SourceType;
|
|
115
|
+
content_type: ContentType;
|
|
116
|
+
projects: string[];
|
|
117
|
+
summary: string;
|
|
118
|
+
relevance_score: number;
|
|
119
|
+
matching_quotes: Quote[];
|
|
120
|
+
matching_themes: string[];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Extended search result with ranking information from hybrid search
|
|
124
|
+
*/
|
|
125
|
+
export interface SearchResultWithRanks extends SearchResult {
|
|
126
|
+
/** Rank in semantic (vector) search results, null if not in semantic results */
|
|
127
|
+
semantic_rank?: number;
|
|
128
|
+
/** Rank in keyword (full-text) search results, null if not in keyword results */
|
|
129
|
+
keyword_rank?: number;
|
|
130
|
+
}
|
|
131
|
+
export interface ResearchPackage {
|
|
132
|
+
query: string;
|
|
133
|
+
project?: string;
|
|
134
|
+
generated_at: string;
|
|
135
|
+
summary: string;
|
|
136
|
+
key_findings: string[];
|
|
137
|
+
conflicts_resolved?: string[];
|
|
138
|
+
supporting_quotes: Quote[];
|
|
139
|
+
related_decisions: Decision[];
|
|
140
|
+
sources_consulted: Array<{
|
|
141
|
+
id: string;
|
|
142
|
+
title: string;
|
|
143
|
+
source_type: SourceType;
|
|
144
|
+
relevance: number;
|
|
145
|
+
}>;
|
|
146
|
+
gaps_identified?: string[];
|
|
147
|
+
suggested_queries?: string[];
|
|
148
|
+
}
|
|
149
|
+
export interface SearchArgs {
|
|
150
|
+
query: string;
|
|
151
|
+
project?: string;
|
|
152
|
+
source_type?: SourceType;
|
|
153
|
+
content_type?: ContentType;
|
|
154
|
+
limit?: number;
|
|
155
|
+
mode?: SearchMode;
|
|
156
|
+
}
|
|
157
|
+
export interface RetainArgs {
|
|
158
|
+
content: string;
|
|
159
|
+
project: string;
|
|
160
|
+
type: 'insight' | 'decision' | 'requirement' | 'note';
|
|
161
|
+
source_context?: string;
|
|
162
|
+
tags?: string[];
|
|
163
|
+
}
|
|
164
|
+
export interface ResearchArgs {
|
|
165
|
+
task: string;
|
|
166
|
+
project?: string;
|
|
167
|
+
depth?: 'quick' | 'thorough' | 'exhaustive';
|
|
168
|
+
include_sources?: boolean;
|
|
169
|
+
}
|
|
170
|
+
export interface IngestArgs {
|
|
171
|
+
path: string;
|
|
172
|
+
source_type: SourceType;
|
|
173
|
+
project?: string;
|
|
174
|
+
tags?: string[];
|
|
175
|
+
source_url?: string;
|
|
176
|
+
source_name?: string;
|
|
177
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lore - Core Types
|
|
3
|
+
*
|
|
4
|
+
* A three-layer knowledge architecture:
|
|
5
|
+
* 1. Source Documents - Immutable original content (interviews, transcripts, docs)
|
|
6
|
+
* 2. Extracted Insights - Quotes, themes, decisions with links back to sources
|
|
7
|
+
* 3. Working Memory - Synthesized context for agent consumption
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lore - User Settings (Supabase key-value store)
|
|
3
|
+
*
|
|
4
|
+
* Generic per-user settings stored in Supabase for cross-machine discovery.
|
|
5
|
+
* Uses the user_settings table with RLS for data isolation.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SETTING_DATA_REPO_URL = "data_repo_url";
|
|
8
|
+
/**
|
|
9
|
+
* Get a user setting by key. Returns null if not found.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getUserSetting(key: string): Promise<string | null>;
|
|
12
|
+
/**
|
|
13
|
+
* Set a user setting (upsert). Creates or updates the value for the given key.
|
|
14
|
+
*/
|
|
15
|
+
export declare function setUserSetting(key: string, value: string): Promise<void>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lore - User Settings (Supabase key-value store)
|
|
3
|
+
*
|
|
4
|
+
* Generic per-user settings stored in Supabase for cross-machine discovery.
|
|
5
|
+
* Uses the user_settings table with RLS for data isolation.
|
|
6
|
+
*/
|
|
7
|
+
import { getSupabase } from './vector-store.js';
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Well-known setting keys
|
|
10
|
+
// ============================================================================
|
|
11
|
+
export const SETTING_DATA_REPO_URL = 'data_repo_url';
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// CRUD
|
|
14
|
+
// ============================================================================
|
|
15
|
+
/**
|
|
16
|
+
* Get a user setting by key. Returns null if not found.
|
|
17
|
+
*/
|
|
18
|
+
export async function getUserSetting(key) {
|
|
19
|
+
const client = await getSupabase();
|
|
20
|
+
const { data, error } = await client
|
|
21
|
+
.from('user_settings')
|
|
22
|
+
.select('value')
|
|
23
|
+
.eq('key', key)
|
|
24
|
+
.single();
|
|
25
|
+
if (error || !data) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return data.value;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Set a user setting (upsert). Creates or updates the value for the given key.
|
|
32
|
+
*/
|
|
33
|
+
export async function setUserSetting(key, value) {
|
|
34
|
+
const client = await getSupabase();
|
|
35
|
+
const { error } = await client
|
|
36
|
+
.from('user_settings')
|
|
37
|
+
.upsert({ key, value, updated_at: new Date().toISOString() }, { onConflict: 'user_id,key' });
|
|
38
|
+
if (error) {
|
|
39
|
+
console.error(`[setUserSetting] Error setting '${key}':`, error);
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lore - Vector Store
|
|
3
|
+
*
|
|
4
|
+
* LanceDB-based vector storage for semantic search across sources and chunks.
|
|
5
|
+
* Adapted from granola-extractor with expanded schema for projects and citations.
|
|
6
|
+
*/
|
|
7
|
+
import * as lancedb from '@lancedb/lancedb';
|
|
8
|
+
import type { SourceRecord, ChunkRecord, Quote, Theme, SourceType, ContentType } from './types.js';
|
|
9
|
+
export declare function getDatabase(dbPath: string): Promise<lancedb.Connection>;
|
|
10
|
+
/**
|
|
11
|
+
* Reset the database connection to force a fresh read
|
|
12
|
+
*/
|
|
13
|
+
export declare function resetDatabaseConnection(): void;
|
|
14
|
+
export declare function closeDatabase(): Promise<void>;
|
|
15
|
+
export declare function indexExists(dbPath: string): Promise<boolean>;
|
|
16
|
+
export declare function initializeTables(dbPath: string): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Add a single source to the existing index (for retain operations)
|
|
19
|
+
*/
|
|
20
|
+
export declare function addSource(dbPath: string, source: SourceRecord, vector: number[]): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Add chunks to the existing index
|
|
23
|
+
*/
|
|
24
|
+
export declare function addChunks(dbPath: string, chunks: ChunkRecord[]): Promise<void>;
|
|
25
|
+
export declare function storeSources(dbPath: string, sources: Array<{
|
|
26
|
+
source: SourceRecord;
|
|
27
|
+
vector: number[];
|
|
28
|
+
}>): Promise<void>;
|
|
29
|
+
export declare function storeChunks(dbPath: string, chunks: ChunkRecord[]): Promise<void>;
|
|
30
|
+
export declare function searchSources(dbPath: string, queryVector: number[], options?: {
|
|
31
|
+
limit?: number;
|
|
32
|
+
project?: string;
|
|
33
|
+
source_type?: SourceType;
|
|
34
|
+
content_type?: ContentType;
|
|
35
|
+
recency_boost?: number;
|
|
36
|
+
}): Promise<Array<{
|
|
37
|
+
id: string;
|
|
38
|
+
title: string;
|
|
39
|
+
source_type: SourceType;
|
|
40
|
+
content_type: ContentType;
|
|
41
|
+
projects: string[];
|
|
42
|
+
tags: string[];
|
|
43
|
+
created_at: string;
|
|
44
|
+
summary: string;
|
|
45
|
+
themes: Theme[];
|
|
46
|
+
quotes: Quote[];
|
|
47
|
+
score: number;
|
|
48
|
+
}>>;
|
|
49
|
+
export declare function searchChunks(dbPath: string, queryVector: number[], options?: {
|
|
50
|
+
limit?: number;
|
|
51
|
+
type?: ChunkRecord['type'];
|
|
52
|
+
theme_name?: string;
|
|
53
|
+
source_id?: string;
|
|
54
|
+
}): Promise<Array<{
|
|
55
|
+
id: string;
|
|
56
|
+
source_id: string;
|
|
57
|
+
content: string;
|
|
58
|
+
type: string;
|
|
59
|
+
theme_name: string;
|
|
60
|
+
speaker: string;
|
|
61
|
+
timestamp: string;
|
|
62
|
+
score: number;
|
|
63
|
+
}>>;
|
|
64
|
+
export declare function getAllSources(dbPath: string, options?: {
|
|
65
|
+
project?: string;
|
|
66
|
+
source_type?: SourceType;
|
|
67
|
+
limit?: number;
|
|
68
|
+
}): Promise<Array<{
|
|
69
|
+
id: string;
|
|
70
|
+
title: string;
|
|
71
|
+
source_type: SourceType;
|
|
72
|
+
content_type: ContentType;
|
|
73
|
+
projects: string[];
|
|
74
|
+
created_at: string;
|
|
75
|
+
summary: string;
|
|
76
|
+
}>>;
|
|
77
|
+
export declare function getSourceById(dbPath: string, sourceId: string): Promise<{
|
|
78
|
+
id: string;
|
|
79
|
+
title: string;
|
|
80
|
+
source_type: SourceType;
|
|
81
|
+
content_type: ContentType;
|
|
82
|
+
projects: string[];
|
|
83
|
+
tags: string[];
|
|
84
|
+
created_at: string;
|
|
85
|
+
summary: string;
|
|
86
|
+
themes: Theme[];
|
|
87
|
+
quotes: Quote[];
|
|
88
|
+
} | null>;
|
|
89
|
+
export declare function getThemeStats(dbPath: string, project?: string): Promise<Map<string, {
|
|
90
|
+
source_count: number;
|
|
91
|
+
quote_count: number;
|
|
92
|
+
}>>;
|
|
93
|
+
export declare function getProjectStats(dbPath: string): Promise<Array<{
|
|
94
|
+
project: string;
|
|
95
|
+
source_count: number;
|
|
96
|
+
quote_count: number;
|
|
97
|
+
latest_activity: string;
|
|
98
|
+
}>>;
|