@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.
Files changed (148) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +80 -0
  3. package/dist/cli/colors.d.ts +48 -0
  4. package/dist/cli/colors.js +48 -0
  5. package/dist/cli/commands/ask.d.ts +7 -0
  6. package/dist/cli/commands/ask.js +97 -0
  7. package/dist/cli/commands/auth.d.ts +10 -0
  8. package/dist/cli/commands/auth.js +484 -0
  9. package/dist/cli/commands/daemon.d.ts +22 -0
  10. package/dist/cli/commands/daemon.js +244 -0
  11. package/dist/cli/commands/docs.d.ts +7 -0
  12. package/dist/cli/commands/docs.js +188 -0
  13. package/dist/cli/commands/extensions.d.ts +7 -0
  14. package/dist/cli/commands/extensions.js +204 -0
  15. package/dist/cli/commands/misc.d.ts +7 -0
  16. package/dist/cli/commands/misc.js +172 -0
  17. package/dist/cli/commands/pending.d.ts +7 -0
  18. package/dist/cli/commands/pending.js +63 -0
  19. package/dist/cli/commands/projects.d.ts +7 -0
  20. package/dist/cli/commands/projects.js +136 -0
  21. package/dist/cli/commands/search.d.ts +7 -0
  22. package/dist/cli/commands/search.js +102 -0
  23. package/dist/cli/commands/skills.d.ts +24 -0
  24. package/dist/cli/commands/skills.js +447 -0
  25. package/dist/cli/commands/sources.d.ts +7 -0
  26. package/dist/cli/commands/sources.js +121 -0
  27. package/dist/cli/commands/sync.d.ts +31 -0
  28. package/dist/cli/commands/sync.js +768 -0
  29. package/dist/cli/helpers.d.ts +30 -0
  30. package/dist/cli/helpers.js +119 -0
  31. package/dist/core/auth.d.ts +62 -0
  32. package/dist/core/auth.js +330 -0
  33. package/dist/core/config.d.ts +41 -0
  34. package/dist/core/config.js +96 -0
  35. package/dist/core/data-repo.d.ts +31 -0
  36. package/dist/core/data-repo.js +146 -0
  37. package/dist/core/embedder.d.ts +22 -0
  38. package/dist/core/embedder.js +104 -0
  39. package/dist/core/git.d.ts +37 -0
  40. package/dist/core/git.js +140 -0
  41. package/dist/core/index.d.ts +4 -0
  42. package/dist/core/index.js +5 -0
  43. package/dist/core/insight-extractor.d.ts +26 -0
  44. package/dist/core/insight-extractor.js +114 -0
  45. package/dist/core/local-search.d.ts +43 -0
  46. package/dist/core/local-search.js +221 -0
  47. package/dist/core/themes.d.ts +15 -0
  48. package/dist/core/themes.js +77 -0
  49. package/dist/core/types.d.ts +177 -0
  50. package/dist/core/types.js +9 -0
  51. package/dist/core/user-settings.d.ts +15 -0
  52. package/dist/core/user-settings.js +42 -0
  53. package/dist/core/vector-store-lance.d.ts +98 -0
  54. package/dist/core/vector-store-lance.js +384 -0
  55. package/dist/core/vector-store-supabase.d.ts +89 -0
  56. package/dist/core/vector-store-supabase.js +295 -0
  57. package/dist/core/vector-store.d.ts +131 -0
  58. package/dist/core/vector-store.js +503 -0
  59. package/dist/daemon-runner.d.ts +8 -0
  60. package/dist/daemon-runner.js +246 -0
  61. package/dist/extensions/config.d.ts +22 -0
  62. package/dist/extensions/config.js +102 -0
  63. package/dist/extensions/proposals.d.ts +30 -0
  64. package/dist/extensions/proposals.js +178 -0
  65. package/dist/extensions/registry.d.ts +35 -0
  66. package/dist/extensions/registry.js +309 -0
  67. package/dist/extensions/sandbox.d.ts +16 -0
  68. package/dist/extensions/sandbox.js +17 -0
  69. package/dist/extensions/types.d.ts +114 -0
  70. package/dist/extensions/types.js +4 -0
  71. package/dist/extensions/worker.d.ts +1 -0
  72. package/dist/extensions/worker.js +49 -0
  73. package/dist/index.d.ts +17 -0
  74. package/dist/index.js +105 -0
  75. package/dist/mcp/handlers/archive-project.d.ts +51 -0
  76. package/dist/mcp/handlers/archive-project.js +112 -0
  77. package/dist/mcp/handlers/get-quotes.d.ts +27 -0
  78. package/dist/mcp/handlers/get-quotes.js +61 -0
  79. package/dist/mcp/handlers/get-source.d.ts +9 -0
  80. package/dist/mcp/handlers/get-source.js +40 -0
  81. package/dist/mcp/handlers/ingest.d.ts +25 -0
  82. package/dist/mcp/handlers/ingest.js +305 -0
  83. package/dist/mcp/handlers/list-projects.d.ts +4 -0
  84. package/dist/mcp/handlers/list-projects.js +16 -0
  85. package/dist/mcp/handlers/list-sources.d.ts +11 -0
  86. package/dist/mcp/handlers/list-sources.js +20 -0
  87. package/dist/mcp/handlers/research-agent.d.ts +21 -0
  88. package/dist/mcp/handlers/research-agent.js +369 -0
  89. package/dist/mcp/handlers/research.d.ts +22 -0
  90. package/dist/mcp/handlers/research.js +225 -0
  91. package/dist/mcp/handlers/retain.d.ts +18 -0
  92. package/dist/mcp/handlers/retain.js +92 -0
  93. package/dist/mcp/handlers/search.d.ts +52 -0
  94. package/dist/mcp/handlers/search.js +145 -0
  95. package/dist/mcp/handlers/sync.d.ts +47 -0
  96. package/dist/mcp/handlers/sync.js +211 -0
  97. package/dist/mcp/server.d.ts +10 -0
  98. package/dist/mcp/server.js +268 -0
  99. package/dist/mcp/tools.d.ts +16 -0
  100. package/dist/mcp/tools.js +297 -0
  101. package/dist/sync/config.d.ts +26 -0
  102. package/dist/sync/config.js +140 -0
  103. package/dist/sync/discover.d.ts +51 -0
  104. package/dist/sync/discover.js +190 -0
  105. package/dist/sync/index.d.ts +11 -0
  106. package/dist/sync/index.js +11 -0
  107. package/dist/sync/process.d.ts +50 -0
  108. package/dist/sync/process.js +285 -0
  109. package/dist/sync/processors.d.ts +24 -0
  110. package/dist/sync/processors.js +351 -0
  111. package/dist/tui/browse-handlers-ask.d.ts +30 -0
  112. package/dist/tui/browse-handlers-ask.js +372 -0
  113. package/dist/tui/browse-handlers-autocomplete.d.ts +49 -0
  114. package/dist/tui/browse-handlers-autocomplete.js +270 -0
  115. package/dist/tui/browse-handlers-extensions.d.ts +18 -0
  116. package/dist/tui/browse-handlers-extensions.js +107 -0
  117. package/dist/tui/browse-handlers-pending.d.ts +22 -0
  118. package/dist/tui/browse-handlers-pending.js +100 -0
  119. package/dist/tui/browse-handlers-research.d.ts +32 -0
  120. package/dist/tui/browse-handlers-research.js +363 -0
  121. package/dist/tui/browse-handlers-tools.d.ts +42 -0
  122. package/dist/tui/browse-handlers-tools.js +289 -0
  123. package/dist/tui/browse-handlers.d.ts +239 -0
  124. package/dist/tui/browse-handlers.js +1944 -0
  125. package/dist/tui/browse-render-extensions.d.ts +14 -0
  126. package/dist/tui/browse-render-extensions.js +114 -0
  127. package/dist/tui/browse-render-tools.d.ts +18 -0
  128. package/dist/tui/browse-render-tools.js +259 -0
  129. package/dist/tui/browse-render.d.ts +51 -0
  130. package/dist/tui/browse-render.js +599 -0
  131. package/dist/tui/browse-types.d.ts +142 -0
  132. package/dist/tui/browse-types.js +70 -0
  133. package/dist/tui/browse-ui.d.ts +10 -0
  134. package/dist/tui/browse-ui.js +432 -0
  135. package/dist/tui/browse.d.ts +17 -0
  136. package/dist/tui/browse.js +625 -0
  137. package/dist/tui/markdown.d.ts +22 -0
  138. package/dist/tui/markdown.js +223 -0
  139. package/package.json +71 -0
  140. package/plugins/claude-code/.claude-plugin/plugin.json +10 -0
  141. package/plugins/claude-code/.mcp.json +6 -0
  142. package/plugins/claude-code/skills/lore/SKILL.md +63 -0
  143. package/plugins/codex/SKILL.md +36 -0
  144. package/plugins/codex/agents/openai.yaml +10 -0
  145. package/plugins/gemini/GEMINI.md +31 -0
  146. package/plugins/gemini/gemini-extension.json +11 -0
  147. package/skills/generic-agent.md +99 -0
  148. package/skills/openclaw.md +67 -0
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Miscellaneous CLI Commands
3
+ *
4
+ * browse, research, init, serve
5
+ */
6
+ import path from 'path';
7
+ export function registerMiscCommands(program, defaultDataDir) {
8
+ // Browse command (TUI)
9
+ program
10
+ .command('browse')
11
+ .description('Browse documents in an interactive terminal UI')
12
+ .option('-p, --project <project>', 'Filter by project')
13
+ .option('-t, --type <type>', 'Filter by source type')
14
+ .option('-l, --limit <limit>', 'Max documents to load', '100')
15
+ .option('-d, --data-dir <dir>', 'Data directory', defaultDataDir)
16
+ .action(async (options) => {
17
+ const { startBrowser } = await import('../../tui/browse.js');
18
+ await startBrowser({
19
+ project: options.project,
20
+ sourceType: options.type,
21
+ limit: parseInt(options.limit, 10),
22
+ dataDir: options.dataDir,
23
+ });
24
+ });
25
+ // Research command (top-level for discoverability)
26
+ program
27
+ .command('research')
28
+ .description('Deep AI-powered research on a topic')
29
+ .argument('<query>', 'Research query')
30
+ .option('-p, --project <project>', 'Focus on specific project')
31
+ .option('-d, --data-dir <dir>', 'Data directory', defaultDataDir)
32
+ .option('--simple', 'Use simple mode (single-pass, faster)')
33
+ .action(async (query, options) => {
34
+ const { handleResearch } = await import('../../mcp/handlers/research.js');
35
+ const dataDir = options.dataDir;
36
+ const dbPath = path.join(dataDir, 'lore.lance');
37
+ if (options.simple) {
38
+ process.env.LORE_RESEARCH_MODE = 'simple';
39
+ }
40
+ console.log(`\nResearching: "${query}"\n`);
41
+ console.log('This may take a moment...\n');
42
+ const result = await handleResearch(dbPath, dataDir, {
43
+ task: query,
44
+ project: options.project,
45
+ include_sources: true,
46
+ }, {
47
+ hookContext: { mode: 'cli' },
48
+ });
49
+ console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
50
+ console.log(`📊 Research Results`);
51
+ console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
52
+ console.log(`Summary:\n${result.summary}\n`);
53
+ if (result.key_findings && result.key_findings.length > 0) {
54
+ console.log(`Key Findings:`);
55
+ for (const finding of result.key_findings) {
56
+ console.log(` • ${finding}`);
57
+ }
58
+ console.log('');
59
+ }
60
+ if (result.conflicts_resolved && result.conflicts_resolved.length > 0) {
61
+ console.log(`Conflicts Resolved:`);
62
+ for (const conflict of result.conflicts_resolved) {
63
+ console.log(` ⚡ ${conflict}`);
64
+ }
65
+ console.log('');
66
+ }
67
+ if (result.supporting_quotes && result.supporting_quotes.length > 0) {
68
+ console.log(`Supporting Quotes (${result.supporting_quotes.length}):`);
69
+ for (const quote of result.supporting_quotes.slice(0, 5)) {
70
+ const speaker = quote.speaker === 'user' ? '[You]' : '[Participant]';
71
+ console.log(` ${speaker} "${quote.text.substring(0, 80)}${quote.text.length > 80 ? '...' : ''}"`);
72
+ }
73
+ if (result.supporting_quotes.length > 5) {
74
+ console.log(` ... and ${result.supporting_quotes.length - 5} more`);
75
+ }
76
+ console.log('');
77
+ }
78
+ if (result.sources_consulted && result.sources_consulted.length > 0) {
79
+ console.log(`Sources Consulted (${result.sources_consulted.length}):`);
80
+ for (const source of result.sources_consulted.slice(0, 5)) {
81
+ const relevance = source.relevance ? ` (${(source.relevance * 100).toFixed(0)}%)` : '';
82
+ console.log(` • ${source.title}${relevance}`);
83
+ }
84
+ if (result.sources_consulted.length > 5) {
85
+ console.log(` ... and ${result.sources_consulted.length - 5} more`);
86
+ }
87
+ console.log('');
88
+ }
89
+ if (result.gaps_identified && result.gaps_identified.length > 0) {
90
+ console.log(`Gaps Identified:`);
91
+ for (const gap of result.gaps_identified) {
92
+ console.log(` ? ${gap}`);
93
+ }
94
+ console.log('');
95
+ }
96
+ if (result.suggested_queries && result.suggested_queries.length > 0) {
97
+ console.log(`Suggested Follow-up Queries:`);
98
+ for (const q of result.suggested_queries) {
99
+ console.log(` → ${q}`);
100
+ }
101
+ console.log('');
102
+ }
103
+ });
104
+ // Init command
105
+ program
106
+ .command('init')
107
+ .description('Initialize a new Lore data repository')
108
+ .argument('[path]', 'Path for the data repository', '~/.lore')
109
+ .option('--remote <url>', 'Git remote URL for cross-machine sync')
110
+ .action(async (targetPath, options) => {
111
+ const { initDataRepo, getGitRemoteUrl } = await import('../../core/data-repo.js');
112
+ const { execSync } = await import('child_process');
113
+ // Expand ~ to home directory
114
+ const expandedPath = targetPath.replace(/^~/, process.env.HOME || '~');
115
+ console.log(`\nLore Init`);
116
+ console.log(`=========`);
117
+ console.log(`Creating data repository at: ${expandedPath}\n`);
118
+ await initDataRepo(expandedPath);
119
+ console.log('✓ Created data repository');
120
+ // Add remote if provided
121
+ if (options.remote) {
122
+ try {
123
+ execSync(`git remote add origin ${options.remote}`, { cwd: expandedPath, stdio: 'pipe' });
124
+ console.log(`✓ Added remote: ${options.remote}`);
125
+ try {
126
+ execSync('git push -u origin main', { cwd: expandedPath, stdio: 'pipe' });
127
+ console.log('✓ Pushed to remote');
128
+ }
129
+ catch {
130
+ console.log('⚠ Could not push to remote (you may need to push manually)');
131
+ }
132
+ }
133
+ catch {
134
+ // Remote may already exist
135
+ const existing = getGitRemoteUrl(expandedPath);
136
+ if (existing) {
137
+ console.log(`✓ Remote already set: ${existing}`);
138
+ }
139
+ }
140
+ }
141
+ console.log(`
142
+ Done! To use this data repository:
143
+
144
+ 1. Set the environment variable:
145
+ export LORE_DATA_DIR=${expandedPath}
146
+
147
+ 2. Or add to your MCP config:
148
+ "env": { "LORE_DATA_DIR": "${expandedPath}" }
149
+
150
+ 3. Add sync sources:
151
+ lore sync add --name "My Notes" --path ~/notes --glob "**/*.md" -p myproject
152
+
153
+ Tip: Run 'lore setup' for the full guided experience (config + login + data repo).
154
+ `);
155
+ });
156
+ // MCP server command
157
+ program
158
+ .command('mcp')
159
+ .description('Start the MCP server')
160
+ .option('-w, --watch', 'Watch extensions and auto-reload')
161
+ .option('--sandbox', 'Run extension tools in worker thread sandbox')
162
+ .action(async (options) => {
163
+ if (options.watch) {
164
+ process.env.LORE_EXTENSION_WATCH = 'true';
165
+ }
166
+ if (options.sandbox) {
167
+ process.env.LORE_EXTENSION_SANDBOX = 'true';
168
+ }
169
+ // Dynamic import to start MCP server
170
+ await import('../../mcp/server.js');
171
+ });
172
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Pending proposals CLI commands
3
+ *
4
+ * lore pending list|show|approve|reject
5
+ */
6
+ import type { Command } from 'commander';
7
+ export declare function registerPendingCommand(extensionCmd: Command, defaultDataDir: string): void;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Pending proposals CLI commands
3
+ *
4
+ * lore pending list|show|approve|reject
5
+ */
6
+ import path from 'path';
7
+ import { listPendingProposals, getProposal, approveProposal, rejectProposal, } from '../../extensions/proposals.js';
8
+ function formatProposalSummary(entry) {
9
+ const date = new Date(entry.createdAt).toLocaleString();
10
+ return `${entry.id} ${entry.status} ${entry.extensionName} ${entry.change.type} ${date}`;
11
+ }
12
+ export function registerPendingCommand(extensionCmd, defaultDataDir) {
13
+ const pending = extensionCmd
14
+ .command('pending')
15
+ .description('Review and approve extension write proposals');
16
+ pending
17
+ .command('list')
18
+ .description('List pending proposals')
19
+ .action(async () => {
20
+ const proposals = await listPendingProposals();
21
+ if (proposals.length === 0) {
22
+ console.log('No pending proposals.');
23
+ return;
24
+ }
25
+ console.log('ID Status Extension Type Created');
26
+ console.log('──────────────────────────────────────────────');
27
+ for (const proposal of proposals) {
28
+ console.log(formatProposalSummary(proposal));
29
+ }
30
+ });
31
+ pending
32
+ .command('show')
33
+ .description('Show proposal details')
34
+ .argument('<id>', 'Proposal ID')
35
+ .action(async (id) => {
36
+ const proposal = await getProposal(id);
37
+ if (!proposal) {
38
+ console.error(`Proposal not found: ${id}`);
39
+ process.exit(1);
40
+ }
41
+ console.log(JSON.stringify(proposal, null, 2));
42
+ });
43
+ pending
44
+ .command('approve')
45
+ .description('Approve and apply a proposal')
46
+ .argument('<id>', 'Proposal ID')
47
+ .option('-d, --data-dir <dir>', 'Data directory', defaultDataDir)
48
+ .action(async (id, options) => {
49
+ const dataDir = options.dataDir;
50
+ const dbPath = path.join(dataDir, 'lore.lance');
51
+ await approveProposal(id, dbPath, dataDir);
52
+ console.log(`Approved proposal ${id}`);
53
+ });
54
+ pending
55
+ .command('reject')
56
+ .description('Reject a proposal')
57
+ .argument('<id>', 'Proposal ID')
58
+ .option('-r, --reason <reason>', 'Rejection reason', 'Rejected by user')
59
+ .action(async (id, options) => {
60
+ await rejectProposal(id, options.reason);
61
+ console.log(`Rejected proposal ${id}`);
62
+ });
63
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Projects CLI Commands
3
+ *
4
+ * Project management: list, archive, delete
5
+ */
6
+ import type { Command } from 'commander';
7
+ export declare function registerProjectsCommand(program: Command, defaultDataDir: string): void;
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Projects CLI Commands
3
+ *
4
+ * Project management: list, archive, delete
5
+ */
6
+ import path from 'path';
7
+ import { indexExists } from '../../core/vector-store.js';
8
+ export function registerProjectsCommand(program, defaultDataDir) {
9
+ const projectsCmd = program
10
+ .command('projects')
11
+ .description('Project management');
12
+ // List projects (default action)
13
+ projectsCmd
14
+ .command('list', { isDefault: true })
15
+ .description('List all projects')
16
+ .option('-d, --data-dir <dir>', 'Data directory', defaultDataDir)
17
+ .action(async (options) => {
18
+ const dataDir = options.dataDir;
19
+ const dbPath = path.join(dataDir, 'lore.lance');
20
+ if (!(await indexExists(dbPath))) {
21
+ console.log('No index found. Run "lore sync" first.');
22
+ process.exit(1);
23
+ }
24
+ const { getProjectStats } = await import('../../core/vector-store.js');
25
+ const projects = await getProjectStats(dbPath);
26
+ console.log(`\nProjects (${projects.length}):`);
27
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
28
+ if (projects.length === 0) {
29
+ console.log('No projects found.');
30
+ return;
31
+ }
32
+ for (const p of projects) {
33
+ console.log(`\n📁 ${p.project}`);
34
+ console.log(` Sources: ${p.source_count} | Quotes: ${p.quote_count}`);
35
+ console.log(` Latest: ${new Date(p.latest_activity).toLocaleDateString()}`);
36
+ }
37
+ console.log('');
38
+ });
39
+ // Archive project
40
+ projectsCmd
41
+ .command('archive')
42
+ .description('Archive a project (hide from search)')
43
+ .argument('<name>', 'Project name to archive')
44
+ .option('-r, --reason <reason>', 'Reason for archiving')
45
+ .option('-s, --successor <project>', 'Successor project name')
46
+ .option('-d, --data-dir <dir>', 'Data directory', defaultDataDir)
47
+ .option('--no-push', 'Skip git push')
48
+ .action(async (projectName, options) => {
49
+ const { handleArchiveProject } = await import('../../mcp/handlers/archive-project.js');
50
+ const dataDir = options.dataDir;
51
+ const dbPath = path.join(dataDir, 'lore.lance');
52
+ const result = await handleArchiveProject(dbPath, dataDir, {
53
+ project: projectName,
54
+ reason: options.reason,
55
+ successor_project: options.successor,
56
+ }, { autoPush: options.push !== false });
57
+ if (result.success) {
58
+ console.log(`\n✓ Archived project "${result.project}"`);
59
+ console.log(` Sources affected: ${result.sources_affected}`);
60
+ if (result.reason)
61
+ console.log(` Reason: ${result.reason}`);
62
+ if (result.successor_project)
63
+ console.log(` Successor: ${result.successor_project}`);
64
+ console.log(` Synced: ${result.synced ? 'yes' : 'no'}`);
65
+ }
66
+ else {
67
+ console.error(`\nFailed to archive: ${result.error}`);
68
+ process.exit(1);
69
+ }
70
+ });
71
+ // Delete project
72
+ projectsCmd
73
+ .command('delete')
74
+ .description('Delete a project and all its documents')
75
+ .argument('<name>', 'Project name to delete')
76
+ .option('-d, --data-dir <dir>', 'Data directory', defaultDataDir)
77
+ .option('--force', 'Skip confirmation')
78
+ .action(async (projectName, options) => {
79
+ const dataDir = options.dataDir;
80
+ const dbPath = path.join(dataDir, 'lore.lance');
81
+ if (!(await indexExists(dbPath))) {
82
+ console.log('No index found. Run "lore sync" first.');
83
+ process.exit(1);
84
+ }
85
+ // Get project stats first
86
+ const { getProjectStats, getAllSources, deleteSource } = await import('../../core/vector-store.js');
87
+ const projects = await getProjectStats(dbPath);
88
+ const project = projects.find(p => p.project === projectName);
89
+ if (!project) {
90
+ console.error(`Project not found: ${projectName}`);
91
+ process.exit(1);
92
+ }
93
+ if (!options.force) {
94
+ const readline = await import('readline');
95
+ const rl = readline.createInterface({
96
+ input: process.stdin,
97
+ output: process.stdout,
98
+ });
99
+ console.log(`\nThis will delete project "${projectName}" and all ${project.source_count} documents.`);
100
+ const answer = await new Promise((resolve) => {
101
+ rl.question('Are you sure? (type "yes" to confirm) ', resolve);
102
+ });
103
+ rl.close();
104
+ if (answer !== 'yes') {
105
+ console.log('Cancelled.');
106
+ return;
107
+ }
108
+ }
109
+ // Get all sources in this project
110
+ const sources = await getAllSources(dbPath, { project: projectName });
111
+ // Delete each source
112
+ const { rm } = await import('fs/promises');
113
+ const { deleteFileAndCommit, gitCommitAndPush } = await import('../../core/git.js');
114
+ let deleted = 0;
115
+ for (const source of sources) {
116
+ const { sourcePath: originalPath } = await deleteSource(dbPath, source.id);
117
+ // Delete from disk (lore-data copy)
118
+ const loreSourcePath = path.join(dataDir, 'sources', source.id);
119
+ try {
120
+ await rm(loreSourcePath, { recursive: true });
121
+ }
122
+ catch {
123
+ // File may not exist on disk
124
+ }
125
+ // Delete original source file from sync directory (and commit to its repo)
126
+ if (originalPath) {
127
+ await deleteFileAndCommit(originalPath, `Delete: ${source.title.slice(0, 50)}`);
128
+ }
129
+ deleted++;
130
+ }
131
+ // Git commit the lore-data changes
132
+ await gitCommitAndPush(dataDir, `Delete project: ${projectName} (${deleted} documents)`);
133
+ console.log(`\n✓ Deleted project "${projectName}"`);
134
+ console.log(` Documents deleted: ${deleted}`);
135
+ });
136
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Search Command
3
+ *
4
+ * Search the knowledge repository with various modes.
5
+ */
6
+ import type { Command } from 'commander';
7
+ export declare function registerSearchCommand(program: Command, defaultDataDir: string): void;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Search Command
3
+ *
4
+ * Search the knowledge repository with various modes.
5
+ */
6
+ import path from 'path';
7
+ import { indexExists, searchSources, getSourceById } from '../../core/vector-store.js';
8
+ import { generateEmbedding } from '../../core/embedder.js';
9
+ export function registerSearchCommand(program, defaultDataDir) {
10
+ program
11
+ .command('search')
12
+ .description('Search the knowledge repository')
13
+ .argument('<query>', 'Search query')
14
+ .option('-p, --project <project>', 'Filter by project')
15
+ .option('-l, --limit <limit>', 'Max results', '5')
16
+ .option('-m, --mode <mode>', 'Search mode: semantic, keyword, hybrid (default), regex', 'hybrid')
17
+ .option('-d, --data-dir <dir>', 'Data directory', defaultDataDir)
18
+ .action(async (query, options) => {
19
+ const dataDir = options.dataDir;
20
+ const dbPath = path.join(dataDir, 'lore.lance');
21
+ const searchMode = options.mode;
22
+ try {
23
+ if (!(await indexExists(dbPath))) {
24
+ console.log('No index found. Run "lore ingest" first.');
25
+ process.exit(1);
26
+ }
27
+ console.log(`\nSearching for: "${query}" (mode: ${searchMode})\n`);
28
+ // Handle regex mode separately
29
+ if (searchMode === 'regex') {
30
+ const { searchLocalFiles, getMatchSnippet } = await import('../../core/local-search.js');
31
+ const results = await searchLocalFiles(dataDir, query, {
32
+ maxTotalResults: parseInt(options.limit),
33
+ maxMatchesPerFile: 5,
34
+ });
35
+ if (results.length === 0) {
36
+ console.log('No regex matches found.');
37
+ return;
38
+ }
39
+ for (const result of results) {
40
+ const sourceData = await getSourceById(dbPath, result.source_id);
41
+ console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
42
+ console.log(`📄 ${sourceData?.title || result.source_id}`);
43
+ if (sourceData) {
44
+ console.log(` Type: ${sourceData.source_type} | ${sourceData.content_type}`);
45
+ console.log(` Projects: ${sourceData.projects.join(', ') || '(none)'}`);
46
+ }
47
+ console.log(` Matches: ${result.matches.length}`);
48
+ console.log('');
49
+ for (const match of result.matches.slice(0, 5)) {
50
+ const snippet = getMatchSnippet(match.line_content, match.match_start, match.match_end, 80);
51
+ console.log(` Line ${match.line_number}: ${snippet}`);
52
+ }
53
+ if (result.matches.length > 5) {
54
+ console.log(` ... and ${result.matches.length - 5} more matches`);
55
+ }
56
+ console.log('');
57
+ }
58
+ return;
59
+ }
60
+ // Semantic/keyword/hybrid search
61
+ const queryVector = await generateEmbedding(query);
62
+ const results = await searchSources(dbPath, queryVector, {
63
+ limit: parseInt(options.limit),
64
+ project: options.project,
65
+ mode: searchMode,
66
+ queryText: query,
67
+ });
68
+ if (results.length === 0) {
69
+ console.log('No results found.');
70
+ return;
71
+ }
72
+ for (const result of results) {
73
+ console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
74
+ console.log(`📄 ${result.title}`);
75
+ console.log(` Type: ${result.source_type} | ${result.content_type}`);
76
+ console.log(` Projects: ${result.projects.join(', ') || '(none)'}`);
77
+ // Show score and ranks for hybrid mode
78
+ if (searchMode === 'hybrid' && (result.semantic_rank || result.keyword_rank)) {
79
+ const semRank = result.semantic_rank ? `sem=#${result.semantic_rank}` : '';
80
+ const kwRank = result.keyword_rank ? `kw=#${result.keyword_rank}` : '';
81
+ console.log(` Score: ${(result.score * 100).toFixed(1)}% [${[semRank, kwRank].filter(Boolean).join(' ')}]`);
82
+ }
83
+ else {
84
+ console.log(` Score: ${(result.score * 100).toFixed(1)}%`);
85
+ }
86
+ console.log(`\n ${result.summary}\n`);
87
+ if (result.quotes.length > 0) {
88
+ console.log(` Key Quotes:`);
89
+ for (const quote of result.quotes.slice(0, 3)) {
90
+ const speaker = quote.speaker === 'user' ? '[You]' : '[Participant]';
91
+ console.log(` • ${speaker} "${quote.text.substring(0, 100)}..."`);
92
+ }
93
+ }
94
+ console.log('');
95
+ }
96
+ }
97
+ catch (error) {
98
+ console.error(`Error: ${error instanceof Error ? error.message : error}`);
99
+ process.exit(1);
100
+ }
101
+ });
102
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Skills Command
3
+ *
4
+ * Install Lore agent instruction files into the correct locations
5
+ * for each supported agent platform, using native plugin/extension formats.
6
+ *
7
+ * Each platform gets:
8
+ * 1. MCP server configuration (so Lore auto-starts)
9
+ * 2. Instruction/skill files (so the agent knows how to use Lore)
10
+ *
11
+ * lore skills list — show available skills
12
+ * lore skills install claude-code — install Claude Code plugin (MCP + skill)
13
+ * lore skills install gemini — install Gemini CLI extension (MCP + GEMINI.md)
14
+ * lore skills install codex — install Codex CLI skill (MCP + SKILL.md)
15
+ * lore skills install openclaw — install OpenClaw skill (SKILL.md)
16
+ * lore skills install generic — print generic instructions to stdout
17
+ */
18
+ import type { Command } from 'commander';
19
+ export declare function registerSkillsCommand(program: Command): void;
20
+ /**
21
+ * Interactive skill installation — used by the setup wizard.
22
+ * Returns a summary of what was installed.
23
+ */
24
+ export declare function interactiveSkillInstall(): Promise<string[]>;