bluera-knowledge 0.10.0 → 0.10.1
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/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +12 -0
- package/README.md +73 -2
- package/commands/sync.md +96 -0
- package/dist/{chunk-ITH6FWQY.js → chunk-6U45VP5Z.js} +24 -3
- package/dist/{chunk-ITH6FWQY.js.map → chunk-6U45VP5Z.js.map} +1 -1
- package/dist/{chunk-CUHYSPRV.js → chunk-DP5XBPQV.js} +372 -2
- package/dist/chunk-DP5XBPQV.js.map +1 -0
- package/dist/{chunk-DWAIT2OD.js → chunk-UE4ZIJYA.js} +74 -5
- package/dist/{chunk-DWAIT2OD.js.map → chunk-UE4ZIJYA.js.map} +1 -1
- package/dist/index.js +213 -5
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +2 -2
- package/dist/workers/background-worker-cli.js +2 -2
- package/package.json +1 -1
- package/src/cli/commands/sync.test.ts +54 -0
- package/src/cli/commands/sync.ts +264 -0
- package/src/cli/index.ts +1 -0
- package/src/crawl/claude-client.test.ts +56 -0
- package/src/crawl/claude-client.ts +27 -1
- package/src/index.ts +2 -0
- package/src/mcp/commands/index.ts +2 -0
- package/src/mcp/commands/sync.commands.test.ts +283 -0
- package/src/mcp/commands/sync.commands.ts +233 -0
- package/src/services/gitignore.service.test.ts +157 -0
- package/src/services/gitignore.service.ts +132 -0
- package/src/services/store-definition.service.test.ts +440 -0
- package/src/services/store-definition.service.ts +198 -0
- package/src/services/store.service.test.ts +279 -1
- package/src/services/store.service.ts +101 -4
- package/src/types/index.ts +18 -0
- package/src/types/store-definition.test.ts +492 -0
- package/src/types/store-definition.ts +129 -0
- package/dist/chunk-CUHYSPRV.js.map +0 -1
package/dist/mcp/server.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
IntelligentCrawler
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-6U45VP5Z.js";
|
|
5
5
|
import {
|
|
6
6
|
JobService,
|
|
7
7
|
createDocumentId,
|
|
8
8
|
createServices,
|
|
9
9
|
createStoreId
|
|
10
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-UE4ZIJYA.js";
|
|
11
11
|
import "../chunk-6FHWC36B.js";
|
|
12
12
|
|
|
13
13
|
// src/workers/background-worker.ts
|
package/package.json
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { createSyncCommand } from './sync.js';
|
|
3
|
+
import type { GlobalOptions } from '../program.js';
|
|
4
|
+
|
|
5
|
+
describe('createSyncCommand', () => {
|
|
6
|
+
function createTestOptions(): GlobalOptions {
|
|
7
|
+
return {
|
|
8
|
+
dataDir: '/tmp/test-data',
|
|
9
|
+
projectRoot: '/tmp/test-project',
|
|
10
|
+
format: 'table',
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
it('creates sync command with correct name and description', () => {
|
|
15
|
+
const cmd = createSyncCommand(createTestOptions);
|
|
16
|
+
expect(cmd.name()).toBe('sync');
|
|
17
|
+
expect(cmd.description()).toContain('Sync');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('has --dry-run option', () => {
|
|
21
|
+
const cmd = createSyncCommand(createTestOptions);
|
|
22
|
+
const options = cmd.options;
|
|
23
|
+
const dryRunOpt = options.find((o) => o.long === '--dry-run');
|
|
24
|
+
expect(dryRunOpt).toBeDefined();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('has --prune option', () => {
|
|
28
|
+
const cmd = createSyncCommand(createTestOptions);
|
|
29
|
+
const options = cmd.options;
|
|
30
|
+
const pruneOpt = options.find((o) => o.long === '--prune');
|
|
31
|
+
expect(pruneOpt).toBeDefined();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('has --reindex option', () => {
|
|
35
|
+
const cmd = createSyncCommand(createTestOptions);
|
|
36
|
+
const options = cmd.options;
|
|
37
|
+
const reindexOpt = options.find((o) => o.long === '--reindex');
|
|
38
|
+
expect(reindexOpt).toBeDefined();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('has correct option descriptions', () => {
|
|
42
|
+
const cmd = createSyncCommand(createTestOptions);
|
|
43
|
+
const options = cmd.options;
|
|
44
|
+
|
|
45
|
+
const dryRunOpt = options.find((o) => o.long === '--dry-run');
|
|
46
|
+
expect(dryRunOpt?.description).toContain('without making changes');
|
|
47
|
+
|
|
48
|
+
const pruneOpt = options.find((o) => o.long === '--prune');
|
|
49
|
+
expect(pruneOpt?.description).toContain('Remove');
|
|
50
|
+
|
|
51
|
+
const reindexOpt = options.find((o) => o.long === '--reindex');
|
|
52
|
+
expect(reindexOpt?.description).toContain('index');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { createServices, destroyServices } from '../../services/index.js';
|
|
3
|
+
import { StoreDefinitionService } from '../../services/store-definition.service.js';
|
|
4
|
+
import {
|
|
5
|
+
isFileStoreDefinition,
|
|
6
|
+
isRepoStoreDefinition,
|
|
7
|
+
isWebStoreDefinition,
|
|
8
|
+
} from '../../types/store-definition.js';
|
|
9
|
+
import type { StoreService } from '../../services/store.service.js';
|
|
10
|
+
import type { StoreDefinition } from '../../types/store-definition.js';
|
|
11
|
+
import type { GlobalOptions } from '../program.js';
|
|
12
|
+
|
|
13
|
+
interface SyncResult {
|
|
14
|
+
created: string[];
|
|
15
|
+
skipped: string[];
|
|
16
|
+
failed: Array<{ name: string; error: string }>;
|
|
17
|
+
orphans: string[];
|
|
18
|
+
pruned: string[];
|
|
19
|
+
dryRun: boolean;
|
|
20
|
+
wouldCreate: string[];
|
|
21
|
+
wouldPrune: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create a store from a definition
|
|
26
|
+
*/
|
|
27
|
+
async function createStoreFromDefinition(
|
|
28
|
+
def: StoreDefinition,
|
|
29
|
+
defService: StoreDefinitionService,
|
|
30
|
+
storeService: StoreService
|
|
31
|
+
): Promise<{ success: true } | { success: false; error: string }> {
|
|
32
|
+
try {
|
|
33
|
+
if (isFileStoreDefinition(def)) {
|
|
34
|
+
const resolvedPath = defService.resolvePath(def.path);
|
|
35
|
+
const createResult = await storeService.create(
|
|
36
|
+
{
|
|
37
|
+
name: def.name,
|
|
38
|
+
type: 'file',
|
|
39
|
+
path: resolvedPath,
|
|
40
|
+
description: def.description,
|
|
41
|
+
tags: def.tags,
|
|
42
|
+
},
|
|
43
|
+
{ skipDefinitionSync: true }
|
|
44
|
+
);
|
|
45
|
+
if (!createResult.success) {
|
|
46
|
+
return { success: false, error: createResult.error.message };
|
|
47
|
+
}
|
|
48
|
+
return { success: true };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (isRepoStoreDefinition(def)) {
|
|
52
|
+
const createResult = await storeService.create(
|
|
53
|
+
{
|
|
54
|
+
name: def.name,
|
|
55
|
+
type: 'repo',
|
|
56
|
+
url: def.url,
|
|
57
|
+
branch: def.branch,
|
|
58
|
+
depth: def.depth,
|
|
59
|
+
description: def.description,
|
|
60
|
+
tags: def.tags,
|
|
61
|
+
},
|
|
62
|
+
{ skipDefinitionSync: true }
|
|
63
|
+
);
|
|
64
|
+
if (!createResult.success) {
|
|
65
|
+
return { success: false, error: createResult.error.message };
|
|
66
|
+
}
|
|
67
|
+
return { success: true };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (isWebStoreDefinition(def)) {
|
|
71
|
+
const createResult = await storeService.create(
|
|
72
|
+
{
|
|
73
|
+
name: def.name,
|
|
74
|
+
type: 'web',
|
|
75
|
+
url: def.url,
|
|
76
|
+
depth: def.depth,
|
|
77
|
+
description: def.description,
|
|
78
|
+
tags: def.tags,
|
|
79
|
+
},
|
|
80
|
+
{ skipDefinitionSync: true }
|
|
81
|
+
);
|
|
82
|
+
if (!createResult.success) {
|
|
83
|
+
return { success: false, error: createResult.error.message };
|
|
84
|
+
}
|
|
85
|
+
return { success: true };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { success: false, error: 'Unknown store definition type' };
|
|
89
|
+
} catch (error) {
|
|
90
|
+
return {
|
|
91
|
+
success: false,
|
|
92
|
+
error: error instanceof Error ? error.message : String(error),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function createSyncCommand(getOptions: () => GlobalOptions): Command {
|
|
98
|
+
const sync = new Command('sync').description(
|
|
99
|
+
'Sync stores from definitions config (bootstrap on fresh clone)'
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
sync
|
|
103
|
+
.option('--dry-run', 'Show what would happen without making changes')
|
|
104
|
+
.option('--prune', 'Remove stores not in definitions')
|
|
105
|
+
.option('--reindex', 'Re-index existing stores after sync')
|
|
106
|
+
.action(async (options: { dryRun?: boolean; prune?: boolean; reindex?: boolean }) => {
|
|
107
|
+
const globalOpts = getOptions();
|
|
108
|
+
const projectRoot = globalOpts.projectRoot ?? process.cwd();
|
|
109
|
+
|
|
110
|
+
const defService = new StoreDefinitionService(projectRoot);
|
|
111
|
+
const services = await createServices(globalOpts.config, globalOpts.dataDir, projectRoot);
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const config = await defService.load();
|
|
115
|
+
const existingStores = await services.store.list();
|
|
116
|
+
const existingNames = new Set(existingStores.map((s) => s.name));
|
|
117
|
+
const definedNames = new Set(config.stores.map((d) => d.name));
|
|
118
|
+
|
|
119
|
+
const result: SyncResult = {
|
|
120
|
+
created: [],
|
|
121
|
+
skipped: [],
|
|
122
|
+
failed: [],
|
|
123
|
+
orphans: [],
|
|
124
|
+
pruned: [],
|
|
125
|
+
dryRun: options.dryRun === true,
|
|
126
|
+
wouldCreate: [],
|
|
127
|
+
wouldPrune: [],
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Process each definition
|
|
131
|
+
for (const def of config.stores) {
|
|
132
|
+
if (existingNames.has(def.name)) {
|
|
133
|
+
result.skipped.push(def.name);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (options.dryRun === true) {
|
|
138
|
+
result.wouldCreate.push(def.name);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const createResult = await createStoreFromDefinition(def, defService, services.store);
|
|
143
|
+
if (createResult.success) {
|
|
144
|
+
result.created.push(def.name);
|
|
145
|
+
} else {
|
|
146
|
+
result.failed.push({ name: def.name, error: createResult.error });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Find orphans
|
|
151
|
+
for (const store of existingStores) {
|
|
152
|
+
if (!definedNames.has(store.name)) {
|
|
153
|
+
result.orphans.push(store.name);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Prune orphans if requested
|
|
158
|
+
if (options.prune === true && result.orphans.length > 0) {
|
|
159
|
+
if (options.dryRun === true) {
|
|
160
|
+
result.wouldPrune = [...result.orphans];
|
|
161
|
+
} else {
|
|
162
|
+
for (const orphanName of result.orphans) {
|
|
163
|
+
const store = await services.store.getByName(orphanName);
|
|
164
|
+
if (store !== undefined) {
|
|
165
|
+
const deleteResult = await services.store.delete(store.id, {
|
|
166
|
+
skipDefinitionSync: true,
|
|
167
|
+
});
|
|
168
|
+
if (deleteResult.success) {
|
|
169
|
+
result.pruned.push(orphanName);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Output result
|
|
177
|
+
if (globalOpts.format === 'json') {
|
|
178
|
+
console.log(JSON.stringify(result, null, 2));
|
|
179
|
+
} else {
|
|
180
|
+
printHumanReadable(result, globalOpts.quiet === true);
|
|
181
|
+
}
|
|
182
|
+
} finally {
|
|
183
|
+
await destroyServices(services);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
return sync;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function printHumanReadable(result: SyncResult, quiet: boolean): void {
|
|
191
|
+
if (quiet) {
|
|
192
|
+
// Just print created/pruned store names
|
|
193
|
+
for (const name of result.created) {
|
|
194
|
+
console.log(`created: ${name}`);
|
|
195
|
+
}
|
|
196
|
+
for (const name of result.pruned) {
|
|
197
|
+
console.log(`pruned: ${name}`);
|
|
198
|
+
}
|
|
199
|
+
for (const name of result.wouldCreate) {
|
|
200
|
+
console.log(`would create: ${name}`);
|
|
201
|
+
}
|
|
202
|
+
for (const name of result.wouldPrune) {
|
|
203
|
+
console.log(`would prune: ${name}`);
|
|
204
|
+
}
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (result.dryRun) {
|
|
209
|
+
console.log('\n[DRY RUN] No changes made.\n');
|
|
210
|
+
} else {
|
|
211
|
+
console.log('\nSync completed.\n');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (result.created.length > 0) {
|
|
215
|
+
console.log(`Created (${String(result.created.length)}):`);
|
|
216
|
+
for (const name of result.created) {
|
|
217
|
+
console.log(` + ${name}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (result.wouldCreate.length > 0) {
|
|
222
|
+
console.log(`Would create (${String(result.wouldCreate.length)}):`);
|
|
223
|
+
for (const name of result.wouldCreate) {
|
|
224
|
+
console.log(` + ${name}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (result.skipped.length > 0) {
|
|
229
|
+
console.log(`Skipped (already exist) (${String(result.skipped.length)}):`);
|
|
230
|
+
for (const name of result.skipped) {
|
|
231
|
+
console.log(` - ${name}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (result.failed.length > 0) {
|
|
236
|
+
console.log(`Failed (${String(result.failed.length)}):`);
|
|
237
|
+
for (const { name, error } of result.failed) {
|
|
238
|
+
console.log(` ! ${name}: ${error}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (result.orphans.length > 0) {
|
|
243
|
+
console.log(`Orphans (not in definitions) (${String(result.orphans.length)}):`);
|
|
244
|
+
for (const name of result.orphans) {
|
|
245
|
+
console.log(` ? ${name}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (result.pruned.length > 0) {
|
|
250
|
+
console.log(`Pruned (${String(result.pruned.length)}):`);
|
|
251
|
+
for (const name of result.pruned) {
|
|
252
|
+
console.log(` x ${name}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (result.wouldPrune.length > 0) {
|
|
257
|
+
console.log(`Would prune (${String(result.wouldPrune.length)}):`);
|
|
258
|
+
for (const name of result.wouldPrune) {
|
|
259
|
+
console.log(` x ${name}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
console.log('');
|
|
264
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -5,3 +5,4 @@ export { createIndexCommand } from './commands/index-cmd.js';
|
|
|
5
5
|
export { createServeCommand } from './commands/serve.js';
|
|
6
6
|
export { createCrawlCommand } from './commands/crawl.js';
|
|
7
7
|
export { createSetupCommand } from './commands/setup.js';
|
|
8
|
+
export { createSyncCommand } from './commands/sync.js';
|
|
@@ -106,6 +106,62 @@ describe('ClaudeClient', () => {
|
|
|
106
106
|
expect(result.reasoning).toBe('Found documentation pages');
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
+
it('should extract structured_output from Claude CLI wrapper format', async () => {
|
|
110
|
+
const promise = client.determineCrawlUrls(
|
|
111
|
+
'https://example.com',
|
|
112
|
+
'<html>test</html>',
|
|
113
|
+
'Find all docs'
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// Claude CLI with --json-schema returns this wrapper format
|
|
117
|
+
setTimeout(() => {
|
|
118
|
+
mockProcess.stdout.emit(
|
|
119
|
+
'data',
|
|
120
|
+
Buffer.from(
|
|
121
|
+
JSON.stringify({
|
|
122
|
+
type: 'result',
|
|
123
|
+
subtype: 'success',
|
|
124
|
+
result: '',
|
|
125
|
+
structured_output: {
|
|
126
|
+
urls: ['https://example.com/page1', 'https://example.com/page2'],
|
|
127
|
+
reasoning: 'Found documentation pages',
|
|
128
|
+
},
|
|
129
|
+
})
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
mockProcess.emit('close', 0);
|
|
133
|
+
}, 10);
|
|
134
|
+
|
|
135
|
+
const result = await promise;
|
|
136
|
+
expect(result.urls).toEqual(['https://example.com/page1', 'https://example.com/page2']);
|
|
137
|
+
expect(result.reasoning).toBe('Found documentation pages');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should fall back to raw response when structured_output is not an object', async () => {
|
|
141
|
+
const promise = client.determineCrawlUrls(
|
|
142
|
+
'https://example.com',
|
|
143
|
+
'<html>test</html>',
|
|
144
|
+
'Find all docs'
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// When structured_output is not an object, use the raw response
|
|
148
|
+
// (which will fail validation if it doesn't have urls/reasoning)
|
|
149
|
+
setTimeout(() => {
|
|
150
|
+
mockProcess.stdout.emit(
|
|
151
|
+
'data',
|
|
152
|
+
Buffer.from(
|
|
153
|
+
JSON.stringify({
|
|
154
|
+
type: 'result',
|
|
155
|
+
structured_output: 'not an object',
|
|
156
|
+
})
|
|
157
|
+
)
|
|
158
|
+
);
|
|
159
|
+
mockProcess.emit('close', 0);
|
|
160
|
+
}, 10);
|
|
161
|
+
|
|
162
|
+
await expect(promise).rejects.toThrow('invalid crawl strategy');
|
|
163
|
+
});
|
|
164
|
+
|
|
109
165
|
it('should call spawn with correct arguments for determineCrawlUrls', async () => {
|
|
110
166
|
const promise = client.determineCrawlUrls(
|
|
111
167
|
'https://example.com',
|
|
@@ -96,7 +96,11 @@ Return only URLs that are relevant to the instruction. If the instruction mentio
|
|
|
96
96
|
|
|
97
97
|
try {
|
|
98
98
|
const result = await this.callClaude(prompt, CRAWL_STRATEGY_SCHEMA);
|
|
99
|
-
const
|
|
99
|
+
const rawParsed: unknown = JSON.parse(result);
|
|
100
|
+
|
|
101
|
+
// Claude CLI with --json-schema returns wrapper: {type, result, structured_output: {...}}
|
|
102
|
+
// Extract structured_output if present, otherwise use raw response
|
|
103
|
+
const parsed = this.extractStructuredOutput(rawParsed);
|
|
100
104
|
|
|
101
105
|
// Validate and narrow type
|
|
102
106
|
if (
|
|
@@ -232,4 +236,26 @@ ${this.truncateMarkdown(markdown, 100000)}`;
|
|
|
232
236
|
|
|
233
237
|
return `${markdown.substring(0, maxLength)}\n\n[... content truncated ...]`;
|
|
234
238
|
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Type guard to check if value is a record (plain object)
|
|
242
|
+
*/
|
|
243
|
+
private isRecord(value: unknown): value is Record<string, unknown> {
|
|
244
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Extract structured_output from Claude CLI wrapper format if present.
|
|
249
|
+
* Claude CLI with --json-schema returns: {type, result, structured_output: {...}}
|
|
250
|
+
* This method extracts the inner structured_output, or returns the raw value if not wrapped.
|
|
251
|
+
*/
|
|
252
|
+
private extractStructuredOutput(rawParsed: unknown): unknown {
|
|
253
|
+
if (this.isRecord(rawParsed) && 'structured_output' in rawParsed) {
|
|
254
|
+
const structuredOutput = rawParsed['structured_output'];
|
|
255
|
+
if (typeof structuredOutput === 'object') {
|
|
256
|
+
return structuredOutput;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return rawParsed;
|
|
260
|
+
}
|
|
235
261
|
}
|
package/src/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { createSearchCommand } from './cli/commands/search.js';
|
|
|
16
16
|
import { createServeCommand } from './cli/commands/serve.js';
|
|
17
17
|
import { createSetupCommand } from './cli/commands/setup.js';
|
|
18
18
|
import { createStoreCommand } from './cli/commands/store.js';
|
|
19
|
+
import { createSyncCommand } from './cli/commands/sync.js';
|
|
19
20
|
import { createProgram, getGlobalOptions } from './cli/program.js';
|
|
20
21
|
|
|
21
22
|
// Default paths
|
|
@@ -105,6 +106,7 @@ program.addCommand(createIndexCommand(() => getGlobalOptions(program)));
|
|
|
105
106
|
program.addCommand(createServeCommand(() => getGlobalOptions(program)));
|
|
106
107
|
program.addCommand(createCrawlCommand(() => getGlobalOptions(program)));
|
|
107
108
|
program.addCommand(createSetupCommand(() => getGlobalOptions(program)));
|
|
109
|
+
program.addCommand(createSyncCommand(() => getGlobalOptions(program)));
|
|
108
110
|
program.addCommand(createMCPCommand(() => getGlobalOptions(program)));
|
|
109
111
|
|
|
110
112
|
// Show comprehensive help when no arguments provided
|
|
@@ -9,11 +9,13 @@ import { jobCommands } from './job.commands.js';
|
|
|
9
9
|
import { metaCommands } from './meta.commands.js';
|
|
10
10
|
import { commandRegistry } from './registry.js';
|
|
11
11
|
import { storeCommands } from './store.commands.js';
|
|
12
|
+
import { syncCommands } from './sync.commands.js';
|
|
12
13
|
|
|
13
14
|
// Register all commands
|
|
14
15
|
commandRegistry.registerAll(storeCommands);
|
|
15
16
|
commandRegistry.registerAll(jobCommands);
|
|
16
17
|
commandRegistry.registerAll(metaCommands);
|
|
18
|
+
commandRegistry.registerAll(syncCommands);
|
|
17
19
|
|
|
18
20
|
// Re-export for convenience
|
|
19
21
|
export { commandRegistry, executeCommand, generateHelp } from './registry.js';
|