@swarmvaultai/cli 0.7.30 → 0.7.31
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/dist/index.js +49 -7
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
listSchedules,
|
|
43
43
|
loadVaultConfig,
|
|
44
44
|
pathGraphVault,
|
|
45
|
+
previewCandidatePromotions,
|
|
45
46
|
promoteCandidate,
|
|
46
47
|
pushGraphNeo4j,
|
|
47
48
|
queryGraphVault,
|
|
@@ -52,6 +53,7 @@ import {
|
|
|
52
53
|
resumeSourceSession,
|
|
53
54
|
reviewManagedSource,
|
|
54
55
|
reviewSourceScope,
|
|
56
|
+
runAutoPromotion,
|
|
55
57
|
runSchedule,
|
|
56
58
|
runWatchCycle,
|
|
57
59
|
serveSchedules,
|
|
@@ -276,9 +278,9 @@ program.name("swarmvault").description("SwarmVault is a local-first knowledge co
|
|
|
276
278
|
function readCliVersion() {
|
|
277
279
|
try {
|
|
278
280
|
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
279
|
-
return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "0.7.
|
|
281
|
+
return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "0.7.31";
|
|
280
282
|
} catch {
|
|
281
|
-
return "0.7.
|
|
283
|
+
return "0.7.31";
|
|
282
284
|
}
|
|
283
285
|
}
|
|
284
286
|
function parsePositiveInt(value, fallback) {
|
|
@@ -435,7 +437,7 @@ program.command("init").description("Initialize a SwarmVault workspace in the cu
|
|
|
435
437
|
log("Initialized SwarmVault workspace.");
|
|
436
438
|
}
|
|
437
439
|
});
|
|
438
|
-
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("--guide", "Stage a guided source integration bundle after ingest and compile (default: from config)").option("--no-guide", "Skip guided mode even if enabled in config").option("--answers-file <path>", "JSON file with guided-session answers keyed by question id or listed in prompt order").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").option("--commit", "Auto-commit wiki and state changes after ingest").action(
|
|
440
|
+
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("--guide", "Stage a guided source integration bundle after ingest and compile (default: from config)").option("--no-guide", "Skip guided mode even if enabled in config").option("--answers-file <path>", "JSON file with guided-session answers keyed by question id or listed in prompt order").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").option("--resume <run-id>", "Re-run only the failed files from a prior ingest run id").option("--commit", "Auto-commit wiki and state changes after ingest").action(
|
|
439
441
|
async (input, options) => {
|
|
440
442
|
const guideAnswers = readGuideAnswersFile(options.answersFile);
|
|
441
443
|
const vaultConfig = await loadVaultConfig(process2.cwd()).catch(() => null);
|
|
@@ -456,7 +458,8 @@ program.command("ingest").description("Ingest a local file path, directory path,
|
|
|
456
458
|
exclude: options.exclude,
|
|
457
459
|
maxFiles,
|
|
458
460
|
gitignore: options.gitignore,
|
|
459
|
-
extractClasses
|
|
461
|
+
extractClasses,
|
|
462
|
+
resume: options.resume
|
|
460
463
|
};
|
|
461
464
|
const directoryResult = !/^https?:\/\//i.test(input) ? await import("fs/promises").then(
|
|
462
465
|
(fs) => fs.stat(input).then((stat) => stat.isDirectory() ? ingestDirectory(process2.cwd(), input, commonOptions) : null).catch(() => null)
|
|
@@ -494,9 +497,18 @@ program.command("ingest").description("Ingest a local file path, directory path,
|
|
|
494
497
|
completedGuide2 ? { ingest: directoryResult, guide: completedGuide2 } : review3 ? { ingest: directoryResult, review: review3 } : directoryResult
|
|
495
498
|
);
|
|
496
499
|
} else {
|
|
500
|
+
const failedCount = directoryResult.failed?.length ?? 0;
|
|
497
501
|
log(
|
|
498
|
-
`Imported ${directoryResult.imported.length} file(s), updated ${directoryResult.updated.length}, skipped ${directoryResult.skipped.length}.`
|
|
502
|
+
`Imported ${directoryResult.imported.length} file(s), updated ${directoryResult.updated.length}, skipped ${directoryResult.skipped.length}, failed ${failedCount}.`
|
|
499
503
|
);
|
|
504
|
+
if (failedCount && directoryResult.runId) {
|
|
505
|
+
log(`Run id: ${directoryResult.runId}`);
|
|
506
|
+
log(`Retry with: swarmvault ingest ${input} --resume ${directoryResult.runId}`);
|
|
507
|
+
for (const failure of (directoryResult.failed ?? []).slice(0, 5)) {
|
|
508
|
+
log(` failed ${failure.stage}: ${failure.path}: ${failure.error}`);
|
|
509
|
+
}
|
|
510
|
+
if (failedCount > 5) log(` ... ${failedCount - 5} more`);
|
|
511
|
+
}
|
|
500
512
|
if (review3) {
|
|
501
513
|
log(`Staged source review at ${review3.reviewPath}.`);
|
|
502
514
|
}
|
|
@@ -849,8 +861,9 @@ graph.command("serve").description("Serve the local graph viewer.").option("--po
|
|
|
849
861
|
});
|
|
850
862
|
graph.command("export").description(
|
|
851
863
|
"Export the graph as HTML, report, SVG, GraphML, Cypher, JSON, Obsidian vault, or Obsidian canvas. Combine flags to write multiple formats in one run."
|
|
852
|
-
).option("--html <output>", "Output HTML file path").option("--html-standalone <output>", "Output lightweight standalone HTML file path (vis.js, no build tooling)").option("--report <output>", "Output self-contained HTML report (graph stats, key nodes, communities)").option("--svg <output>", "Output SVG file path").option("--graphml <output>", "Output GraphML file path").option("--cypher <output>", "Output Cypher file path").option("--json <output>", "Output JSON file path").option("--obsidian <output>", "Output Obsidian vault directory path").option("--canvas <output>", "Output Obsidian canvas file path").option("--full", "
|
|
864
|
+
).option("--html <output>", "Output HTML file path").option("--html-standalone <output>", "Output lightweight standalone HTML file path (vis.js, no build tooling)").option("--report <output>", "Output self-contained HTML report (graph stats, key nodes, communities)").option("--svg <output>", "Output SVG file path").option("--graphml <output>", "Output GraphML file path").option("--cypher <output>", "Output Cypher file path").option("--json <output>", "Output JSON file path").option("--obsidian <output>", "Output Obsidian vault directory path").option("--canvas <output>", "Output Obsidian canvas file path").option("--full", "Include the full graph in HTML export (default; queries traverse complete graph)", true).option("--overview", "Use overview sampling for HTML export (smaller file, queries limited to sampled nodes)", false).action(
|
|
853
865
|
async (options) => {
|
|
866
|
+
const useFullGraph = options.overview ? false : options.full ?? true;
|
|
854
867
|
const targets = [
|
|
855
868
|
options.html ? { format: "html", outputPath: options.html } : null,
|
|
856
869
|
options.htmlStandalone ? { format: "html-standalone", outputPath: options.htmlStandalone } : null,
|
|
@@ -870,7 +883,7 @@ graph.command("export").description(
|
|
|
870
883
|
const results = [];
|
|
871
884
|
for (const target of targets) {
|
|
872
885
|
if (target.format === "html") {
|
|
873
|
-
const outputPath = await exportGraphHtml(process2.cwd(), target.outputPath, { full:
|
|
886
|
+
const outputPath = await exportGraphHtml(process2.cwd(), target.outputPath, { full: useFullGraph });
|
|
874
887
|
results.push({ format: target.format, outputPath });
|
|
875
888
|
} else if (target.format === "report") {
|
|
876
889
|
const result = await exportGraphReportHtml(process2.cwd(), target.outputPath);
|
|
@@ -1032,6 +1045,35 @@ candidate.command("archive").description("Archive a candidate by removing it fro
|
|
|
1032
1045
|
log(`Archived ${result.pageId}`);
|
|
1033
1046
|
}
|
|
1034
1047
|
});
|
|
1048
|
+
candidate.command("auto-promote").description("Apply configured auto-promotion rules to staged candidates. Requires candidate.autoPromote.enabled in config.").option("--dry-run", "Score candidates without moving files", false).action(async (options) => {
|
|
1049
|
+
const result = await runAutoPromotion(process2.cwd(), { dryRun: options.dryRun ?? false });
|
|
1050
|
+
if (isJson()) {
|
|
1051
|
+
emitJson(result);
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
log(
|
|
1055
|
+
`${result.dryRun ? "Dry-run" : "Promoted"} ${result.promotedPageIds.length} of ${result.decisions.length} candidates. Session: ${result.sessionPath ?? "none"}`
|
|
1056
|
+
);
|
|
1057
|
+
for (const decision of result.decisions) {
|
|
1058
|
+
const mark = decision.promote ? result.promotedPageIds.includes(decision.pageId) ? "+" : "~" : "-";
|
|
1059
|
+
log(` ${mark} ${decision.pageId} score=${decision.score.toFixed(2)} ${decision.reasons.join("; ")}`);
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
candidate.command("preview-scores").description("Show promotion scores for staged candidates without promoting.").action(async () => {
|
|
1063
|
+
const decisions = await previewCandidatePromotions(process2.cwd());
|
|
1064
|
+
if (isJson()) {
|
|
1065
|
+
emitJson(decisions);
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
if (!decisions.length) {
|
|
1069
|
+
log("No candidates to score.");
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
for (const decision of decisions) {
|
|
1073
|
+
const verdict = decision.promote ? "promote" : "skip";
|
|
1074
|
+
log(`${verdict} ${decision.pageId} score=${decision.score.toFixed(2)} ${decision.reasons.join("; ")}`);
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
1035
1077
|
var watch = program.command("watch").description("Watch the inbox directory and optionally tracked repos, or run one refresh cycle immediately.").option("--lint", "Run lint after each compile cycle", false).option("--repo", "Also refresh tracked repo sources and watch their repo roots", false).option("--once", "Run one import/refresh cycle immediately instead of starting a watcher", false).option("--code-only", "Only re-extract code sources (AST-only, no LLM re-analysis)", false).option("--debounce <ms>", "Debounce window in milliseconds", "900").action(async (options) => {
|
|
1036
1078
|
const debounceMs = parsePositiveInt(options.debounce, 900);
|
|
1037
1079
|
if (options.once) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmvaultai/cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.31",
|
|
4
4
|
"description": "Global CLI for SwarmVault.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"prepublishOnly": "node ../../scripts/check-release-sync.mjs && node ../../scripts/check-published-manifests.mjs"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@swarmvaultai/engine": "0.7.
|
|
47
|
+
"@swarmvaultai/engine": "0.7.31",
|
|
48
48
|
"commander": "^14.0.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|