@getlore/cli 0.4.0 → 0.5.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.
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Ingest Command
3
+ *
4
+ * Push content directly into Lore from the CLI.
5
+ * Accepts inline text, a file path, or piped stdin.
6
+ */
7
+ import type { Command } from 'commander';
8
+ export declare function registerIngestCommand(program: Command, defaultDataDir: string): void;
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Ingest Command
3
+ *
4
+ * Push content directly into Lore from the CLI.
5
+ * Accepts inline text, a file path, or piped stdin.
6
+ */
7
+ import { readFileSync, existsSync } from 'fs';
8
+ import path from 'path';
9
+ export function registerIngestCommand(program, defaultDataDir) {
10
+ program
11
+ .command('ingest')
12
+ .description('Ingest content into the knowledge base')
13
+ .argument('[content]', 'Content to ingest (or use --file / stdin)')
14
+ .option('-f, --file <path>', 'Read content from a file')
15
+ .option('-t, --title <title>', 'Document title')
16
+ .option('-p, --project <project>', 'Project name', 'default')
17
+ .option('--type <type>', 'Source type (e.g. meeting, notes, article)')
18
+ .option('--url <url>', 'Source URL for citation linking')
19
+ .option('--name <name>', 'Human-readable source name')
20
+ .option('--tags <tags>', 'Comma-separated tags')
21
+ .option('-d, --data-dir <dir>', 'Data directory', defaultDataDir)
22
+ .action(async (contentArg, options) => {
23
+ const { handleIngest } = await import('../../mcp/handlers/ingest.js');
24
+ const dataDir = options.dataDir;
25
+ const dbPath = path.join(dataDir, 'lore.lance');
26
+ // Resolve content from argument, file, or stdin
27
+ let content;
28
+ if (options.file) {
29
+ const filePath = options.file.replace(/^~/, process.env.HOME || '~');
30
+ if (!existsSync(filePath)) {
31
+ console.error(`File not found: ${filePath}`);
32
+ process.exit(1);
33
+ }
34
+ content = readFileSync(filePath, 'utf-8');
35
+ }
36
+ else if (contentArg) {
37
+ content = contentArg;
38
+ }
39
+ else if (!process.stdin.isTTY) {
40
+ // Reading from pipe/stdin
41
+ content = readFileSync(0, 'utf-8');
42
+ }
43
+ else {
44
+ console.error('No content provided. Use one of:');
45
+ console.error(' lore ingest "Your content here"');
46
+ console.error(' lore ingest --file ./notes.md');
47
+ console.error(' echo "content" | lore ingest');
48
+ process.exit(1);
49
+ }
50
+ content = content.trim();
51
+ if (!content) {
52
+ console.error('Content is empty.');
53
+ process.exit(1);
54
+ }
55
+ // Derive title from file name or content
56
+ let title = options.title;
57
+ if (!title && options.file) {
58
+ title = path.basename(options.file, path.extname(options.file));
59
+ }
60
+ if (!title) {
61
+ // Use first line or first 60 chars
62
+ const firstLine = content.split('\n')[0].replace(/^#+\s*/, '');
63
+ title = firstLine.length > 60 ? firstLine.slice(0, 57) + '...' : firstLine;
64
+ }
65
+ const tags = options.tags ? options.tags.split(',').map((t) => t.trim()) : [];
66
+ console.log(`\nIngesting: ${title}`);
67
+ console.log(`Project: ${options.project}`);
68
+ if (options.type)
69
+ console.log(`Type: ${options.type}`);
70
+ console.log(`Content: ${content.length} chars`);
71
+ console.log('');
72
+ const result = await handleIngest(dbPath, dataDir, {
73
+ content,
74
+ title,
75
+ project: options.project,
76
+ source_type: options.type,
77
+ tags,
78
+ source_url: options.url,
79
+ source_name: options.name,
80
+ }, {
81
+ hookContext: { mode: 'cli' },
82
+ });
83
+ if (result.deduplicated) {
84
+ console.log('Already exists (identical content). Skipped.');
85
+ return;
86
+ }
87
+ if (result.success) {
88
+ console.log(`Ingested (ID: ${result.id})`);
89
+ if (result.indexed) {
90
+ console.log('Indexed and searchable.');
91
+ }
92
+ else {
93
+ console.log('Saved to disk. Run "lore sync" to index.');
94
+ }
95
+ if (result.synced) {
96
+ console.log('Pushed to git.');
97
+ }
98
+ }
99
+ else {
100
+ console.error('Ingestion failed.');
101
+ process.exit(1);
102
+ }
103
+ });
104
+ }
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ import { registerAskCommand } from './cli/commands/ask.js';
30
30
  import { registerAuthCommands } from './cli/commands/auth.js';
31
31
  import { registerSkillsCommand } from './cli/commands/skills.js';
32
32
  import { registerUpdateCommand } from './cli/commands/update.js';
33
+ import { registerIngestCommand } from './cli/commands/ingest.js';
33
34
  import { getExtensionRegistry, getLoreVersionString } from './extensions/registry.js';
34
35
  import { bridgeConfigToEnv } from './core/config.js';
35
36
  import { expandPath } from './sync/config.js';
@@ -78,6 +79,7 @@ registerAskCommand(program, DEFAULT_DATA_DIR);
78
79
  registerAuthCommands(program);
79
80
  registerSkillsCommand(program);
80
81
  registerUpdateCommand(program, DEFAULT_DATA_DIR);
82
+ registerIngestCommand(program, DEFAULT_DATA_DIR);
81
83
  // Extension system — hidden from top-level help for now
82
84
  const extensionCmd = registerExtensionCommands(program);
83
85
  extensionCmd._hidden = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getlore/cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Research knowledge repository with semantic search, citations, and project lineage tracking",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",