@swarmvaultai/cli 0.2.2 → 0.4.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 (2) hide show
  1. package/dist/index.js +76 -23
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  getWatchStatus,
20
20
  importInbox,
21
21
  ingestDirectory,
22
- ingestInput,
22
+ ingestInputDetailed,
23
23
  initVault,
24
24
  installAgent,
25
25
  installGitHooks,
@@ -28,6 +28,7 @@ import {
28
28
  listCandidates,
29
29
  listGodNodes,
30
30
  listManagedSourceRecords,
31
+ listManifests,
31
32
  listSchedules,
32
33
  loadVaultConfig,
33
34
  pathGraphVault,
@@ -38,6 +39,8 @@ import {
38
39
  readApproval,
39
40
  rejectApproval,
40
41
  reloadManagedSources,
42
+ reviewManagedSource,
43
+ reviewSourceScope,
41
44
  runSchedule,
42
45
  runWatchCycle,
43
46
  serveSchedules,
@@ -221,9 +224,9 @@ program.name("swarmvault").description("SwarmVault is a local-first knowledge co
221
224
  function readCliVersion() {
222
225
  try {
223
226
  const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
224
- return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "0.2.2";
227
+ return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "0.4.0";
225
228
  } catch {
226
- return "0.2.2";
229
+ return "0.4.0";
227
230
  }
228
231
  }
229
232
  function parsePositiveInt(value, fallback) {
@@ -281,7 +284,7 @@ program.command("init").description("Initialize a SwarmVault workspace in the cu
281
284
  log("Initialized SwarmVault workspace.");
282
285
  }
283
286
  });
284
- program.command("ingest").description("Ingest a local file path, directory path, or URL into the raw SwarmVault workspace.").argument("<input>", "Local file path, directory path, or URL").option("--include-assets", "Download remote image assets when ingesting URLs", true).option("--no-include-assets", "Skip downloading remote image assets when ingesting URLs").option("--max-asset-size <bytes>", "Maximum number of bytes to fetch for a single remote image asset").option("--repo-root <path>", "Override the detected repo root when ingesting a directory").option("--include <glob...>", "Only ingest files matching one or more glob patterns").option("--exclude <glob...>", "Skip files matching one or more glob patterns").option("--max-files <n>", "Maximum number of files to ingest from a directory").option("--include-third-party", "Also ingest repo files classified as third-party", false).option("--include-resources", "Also ingest repo files classified as resources", false).option("--include-generated", "Also ingest repo files classified as generated output", false).option("--no-gitignore", "Ignore .gitignore rules when ingesting a directory").action(
287
+ program.command("ingest").description("Ingest a local file path, directory path, or URL into the raw SwarmVault workspace.").argument("<input>", "Local file path, directory path, or URL").option("--review", "Stage a source review artifact after ingest and compile", false).option("--include-assets", "Download remote image assets when ingesting URLs", true).option("--no-include-assets", "Skip downloading remote image assets when ingesting URLs").option("--max-asset-size <bytes>", "Maximum number of bytes to fetch for a single remote image asset").option("--repo-root <path>", "Override the detected repo root when ingesting a directory").option("--include <glob...>", "Only ingest files matching one or more glob patterns").option("--exclude <glob...>", "Skip files matching one or more glob patterns").option("--max-files <n>", "Maximum number of files to ingest from a directory").option("--include-third-party", "Also ingest repo files classified as third-party", false).option("--include-resources", "Also ingest repo files classified as resources", false).option("--include-generated", "Also ingest repo files classified as generated output", false).option("--no-gitignore", "Ignore .gitignore rules when ingesting a directory").action(
285
288
  async (input, options) => {
286
289
  const maxAssetSize = typeof options.maxAssetSize === "string" && options.maxAssetSize.trim() ? parsePositiveInt(options.maxAssetSize, 0) || void 0 : void 0;
287
290
  const maxFiles = typeof options.maxFiles === "string" && options.maxFiles.trim() ? parsePositiveInt(options.maxFiles, 0) || void 0 : void 0;
@@ -305,20 +308,59 @@ program.command("ingest").description("Ingest a local file path, directory path,
305
308
  (fs) => fs.stat(input).then((stat) => stat.isDirectory() ? ingestDirectory(process2.cwd(), input, commonOptions) : null).catch(() => null)
306
309
  ) : null;
307
310
  if (directoryResult) {
311
+ const review3 = options.review && (directoryResult.imported.length || directoryResult.updated.length) ? await (async () => {
312
+ await compileVault(process2.cwd(), {});
313
+ const pathModule = await import("path");
314
+ const absoluteInput = pathModule.resolve(process2.cwd(), input);
315
+ const sourceIds = (await listManifests(process2.cwd())).filter((manifest) => {
316
+ if (!manifest.originalPath) {
317
+ return false;
318
+ }
319
+ const relative = pathModule.relative(absoluteInput, pathModule.resolve(manifest.originalPath));
320
+ return relative === "" || !relative.startsWith("..") && !pathModule.isAbsolute(relative);
321
+ }).map((manifest) => manifest.sourceId);
322
+ return sourceIds.length ? await reviewSourceScope(process2.cwd(), {
323
+ id: `directory-${absoluteInput.split(pathModule.sep).pop() ?? "source"}`,
324
+ title: absoluteInput.split(pathModule.sep).pop() ?? absoluteInput,
325
+ sourceIds
326
+ }) : void 0;
327
+ })() : void 0;
308
328
  if (isJson()) {
309
- emitJson(directoryResult);
329
+ emitJson(review3 ? { ingest: directoryResult, review: review3 } : directoryResult);
310
330
  } else {
311
331
  log(
312
332
  `Imported ${directoryResult.imported.length} file(s), updated ${directoryResult.updated.length}, skipped ${directoryResult.skipped.length}.`
313
333
  );
334
+ if (review3) {
335
+ log(`Staged source review at ${review3.reviewPath}.`);
336
+ }
314
337
  }
315
338
  return;
316
339
  }
317
- const manifest = await ingestInput(process2.cwd(), input, commonOptions);
340
+ const ingest = await ingestInputDetailed(process2.cwd(), input, commonOptions);
341
+ const review2 = options.review && (ingest.created.length || ingest.updated.length || ingest.unchanged.length) ? await (async () => {
342
+ await compileVault(process2.cwd(), {});
343
+ const scopeSourceIds = [...ingest.created, ...ingest.updated, ...ingest.unchanged].map((manifest) => manifest.sourceId);
344
+ return await reviewSourceScope(process2.cwd(), {
345
+ id: ingest.created[0]?.sourceGroupId ?? ingest.updated[0]?.sourceGroupId ?? ingest.unchanged[0]?.sourceGroupId ?? scopeSourceIds[0],
346
+ title: [...ingest.created, ...ingest.updated, ...ingest.unchanged][0]?.sourceGroupTitle ?? [...ingest.created, ...ingest.updated, ...ingest.unchanged][0]?.title ?? input,
347
+ sourceIds: scopeSourceIds
348
+ });
349
+ })() : void 0;
318
350
  if (isJson()) {
319
- emitJson(manifest);
351
+ emitJson(review2 ? { ingest, review: review2 } : ingest);
320
352
  } else {
321
- log(manifest.sourceId);
353
+ const primary = [...ingest.created, ...ingest.updated, ...ingest.unchanged][0];
354
+ if (ingest.created.length + ingest.updated.length + ingest.removed.length <= 1 && primary) {
355
+ log(primary.sourceId);
356
+ } else {
357
+ log(
358
+ `Created ${ingest.created.length}, updated ${ingest.updated.length}, unchanged ${ingest.unchanged.length}, removed ${ingest.removed.length}.`
359
+ );
360
+ }
361
+ if (review2) {
362
+ log(`Staged source review at ${review2.reviewPath}.`);
363
+ }
322
364
  }
323
365
  }
324
366
  );
@@ -333,22 +375,25 @@ program.command("add").description("Capture supported URLs into normalized markd
333
375
  log(`${result.captureType}${result.fallback ? " (fallback)" : ""}: ${result.manifest.sourceId}`);
334
376
  }
335
377
  });
336
- var source = program.command("source").description("Manage recurring directory, public repo, and docs sources.");
337
- source.command("add").description("Register and sync a managed source from a directory, public GitHub repo root URL, or docs hub URL.").argument("<input>", "Directory path, public GitHub repo root URL, or docs hub URL").option("--no-compile", "Register and sync without compiling the vault").option("--no-brief", "Skip source brief generation after sync").option("--max-pages <n>", "Maximum number of pages to crawl for docs sources").option("--max-depth <n>", "Maximum crawl depth for docs sources").action(async (input, options) => {
338
- const result = await addManagedSource(process2.cwd(), input, {
339
- compile: options.compile,
340
- brief: options.brief,
341
- maxPages: options.maxPages ? parsePositiveInt(options.maxPages, 0) || void 0 : void 0,
342
- maxDepth: options.maxDepth ? parsePositiveInt(options.maxDepth, 0) || void 0 : void 0
343
- });
344
- if (isJson()) {
345
- emitJson(result);
346
- } else {
347
- log(
348
- `Registered ${result.source.kind} source ${result.source.id}. Status: ${result.source.status}.${result.compile ? ` Compiled ${result.compile.sourceCount} source(s).` : ""}${result.briefGenerated ? ` Brief: ${result.source.briefPath}` : ""}`
349
- );
378
+ var source = program.command("source").description("Manage recurring local files, directories, public repos, and docs sources.");
379
+ source.command("add").description("Register and sync a managed source from a local file, directory, public GitHub repo root URL, or docs hub URL.").argument("<input>", "Local file path, directory path, public GitHub repo root URL, or docs hub URL").option("--no-compile", "Register and sync without compiling the vault").option("--no-brief", "Skip source brief generation after sync").option("--review", "Stage a source review artifact after sync and compile", false).option("--max-pages <n>", "Maximum number of pages to crawl for docs sources").option("--max-depth <n>", "Maximum crawl depth for docs sources").action(
380
+ async (input, options) => {
381
+ const result = await addManagedSource(process2.cwd(), input, {
382
+ compile: options.compile,
383
+ brief: options.brief,
384
+ review: options.review,
385
+ maxPages: options.maxPages ? parsePositiveInt(options.maxPages, 0) || void 0 : void 0,
386
+ maxDepth: options.maxDepth ? parsePositiveInt(options.maxDepth, 0) || void 0 : void 0
387
+ });
388
+ if (isJson()) {
389
+ emitJson(result);
390
+ } else {
391
+ log(
392
+ `Registered ${result.source.kind} source ${result.source.id}. Status: ${result.source.status}.${result.compile ? ` Compiled ${result.compile.sourceCount} source(s).` : ""}${result.briefGenerated ? ` Brief: ${result.source.briefPath}` : ""}${result.review ? ` Review: ${result.review.reviewPath}` : ""}`
393
+ );
394
+ }
350
395
  }
351
- });
396
+ );
352
397
  source.command("list").description("List managed sources registered in this vault.").action(async () => {
353
398
  const sources = await listManagedSourceRecords(process2.cwd());
354
399
  if (isJson()) {
@@ -388,6 +433,14 @@ source.command("delete").description("Unregister a managed source and remove its
388
433
  log(`Deleted managed source ${result.removed.id}. Canonical vault content was left in place.`);
389
434
  }
390
435
  });
436
+ source.command("review").description("Stage a source review artifact for a managed source id or raw source id.").argument("<id>", "Managed source id or raw source id").action(async (id) => {
437
+ const result = await reviewManagedSource(process2.cwd(), id);
438
+ if (isJson()) {
439
+ emitJson(result);
440
+ } else {
441
+ log(`Staged source review at ${result.reviewPath}.`);
442
+ }
443
+ });
391
444
  var inbox = program.command("inbox").description("Inbox and capture workflows.");
392
445
  inbox.command("import").description("Import supported files from the configured inbox directory.").argument("[dir]", "Optional inbox directory override").action(async (dir) => {
393
446
  const result = await importInbox(process2.cwd(), dir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swarmvaultai/cli",
3
- "version": "0.2.2",
3
+ "version": "0.4.0",
4
4
  "description": "Global CLI for SwarmVault.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -38,7 +38,7 @@
38
38
  "node": ">=24.0.0"
39
39
  },
40
40
  "dependencies": {
41
- "@swarmvaultai/engine": "0.2.2",
41
+ "@swarmvaultai/engine": "0.4.0",
42
42
  "commander": "^14.0.1"
43
43
  },
44
44
  "devDependencies": {