@swarmvaultai/cli 0.4.0 → 0.6.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.
- package/README.md +15 -5
- package/dist/index.js +177 -27
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -22,9 +22,11 @@ Installed commands:
|
|
|
22
22
|
```bash
|
|
23
23
|
mkdir my-vault
|
|
24
24
|
cd my-vault
|
|
25
|
-
swarmvault init --obsidian
|
|
25
|
+
swarmvault init --obsidian --profile personal-research
|
|
26
26
|
swarmvault source add https://github.com/karpathy/micrograd
|
|
27
27
|
swarmvault source add https://example.com/docs/getting-started
|
|
28
|
+
swarmvault source add ./exports/customer-call.srt --guide
|
|
29
|
+
swarmvault source session file-customer-call-srt-12345678
|
|
28
30
|
swarmvault source list
|
|
29
31
|
swarmvault source reload --all
|
|
30
32
|
sed -n '1,120p' swarmvault.schema.md
|
|
@@ -50,7 +52,7 @@ swarmvault graph push neo4j --dry-run
|
|
|
50
52
|
|
|
51
53
|
## Commands
|
|
52
54
|
|
|
53
|
-
### `swarmvault init [--obsidian]`
|
|
55
|
+
### `swarmvault init [--obsidian] [--profile personal-research]`
|
|
54
56
|
|
|
55
57
|
Create a workspace with:
|
|
56
58
|
|
|
@@ -67,25 +69,31 @@ Create a workspace with:
|
|
|
67
69
|
|
|
68
70
|
The schema file is the vault-specific instruction layer. Edit it to define naming rules, categories, grounding expectations, and exclusions before a serious compile.
|
|
69
71
|
|
|
70
|
-
### `swarmvault source add|list|reload|delete`
|
|
72
|
+
### `swarmvault source add|list|reload|review|guide|session|delete`
|
|
71
73
|
|
|
72
74
|
Manage recurring source roots through a registry-backed workflow.
|
|
73
75
|
|
|
74
|
-
- `source add <input>` supports local directories, public GitHub repo root URLs such as `https://github.com/karpathy/micrograd`, and docs/wiki/help/reference/tutorial hubs
|
|
76
|
+
- `source add <input>` supports local files, local directories, public GitHub repo root URLs such as `https://github.com/karpathy/micrograd`, and docs/wiki/help/reference/tutorial hubs
|
|
75
77
|
- by default `source add` registers the source, syncs it into the vault, runs one compile, and writes a source brief to `wiki/outputs/source-briefs/<source-id>.md`
|
|
78
|
+
- add `--guide` when you want a resumable source session, source brief, source review, source guide, and approval-bundled `wiki/insights/` updates for one-source-at-a-time integration
|
|
76
79
|
- `source list` shows every managed source with its kind, status, and current brief path
|
|
77
80
|
- `source reload [id]` re-syncs one source, or use `--all` to refresh everything in the registry and compile once
|
|
81
|
+
- `source review <id>` stages a lighter source-scoped review artifact
|
|
82
|
+
- `source guide <id>` remains a compatibility alias for the guided session flow
|
|
83
|
+
- `source session <id>` resumes the latest guided session for a managed source id, raw source id, source scope id, or session id
|
|
78
84
|
- `source delete <id>` unregisters the source and removes transient sync state under `state/sources/<id>/`, but leaves canonical `raw/`, `wiki/`, and saved output artifacts intact
|
|
79
85
|
|
|
80
86
|
Useful flags:
|
|
81
87
|
|
|
82
88
|
- `--all`
|
|
89
|
+
- `--guide`
|
|
90
|
+
- `--answers-file <path>`
|
|
83
91
|
- `--no-compile`
|
|
84
92
|
- `--no-brief`
|
|
85
93
|
- `--max-pages <n>`
|
|
86
94
|
- `--max-depth <n>`
|
|
87
95
|
|
|
88
|
-
Managed sources write registry state to `state/sources.json`. Local directory entries remain compatible with `watch --repo`; remote GitHub and docs-crawl sources are manual `source reload` sources in this release.
|
|
96
|
+
Managed sources write registry state to `state/sources.json`. Guided sessions write durable anchors to `wiki/outputs/source-sessions/` and session state to `state/source-sessions/`. In an interactive TTY, `--guide` can ask the session questions immediately; otherwise use `source session <id>` or `--answers-file <path>` to resume and stage the approval bundle later. Local directory entries remain compatible with `watch --repo`; remote GitHub and docs-crawl sources are manual `source reload` sources in this release.
|
|
89
97
|
|
|
90
98
|
### `swarmvault ingest <path-or-url>`
|
|
91
99
|
|
|
@@ -97,11 +105,13 @@ Ingest a local file path, directory path, or URL into immutable source storage a
|
|
|
97
105
|
- use `source add` instead when the same local directory, public GitHub repo root, or docs hub should stay registered and reloadable
|
|
98
106
|
- URL ingest still localizes remote image references by default
|
|
99
107
|
- local file ingest supports markdown, text, reStructuredText, HTML, PDF, DOCX, images, and code
|
|
108
|
+
- add `--guide` when you want a resumable source session, source brief, source review, source guide, and approval-bundled `wiki/insights/` updates after ingest
|
|
100
109
|
- code-aware directory ingest currently covers JavaScript, JSX, TypeScript, TSX, Python, Go, Rust, Java, Kotlin, Scala, Lua, Zig, C#, C, C++, PHP, Ruby, and PowerShell
|
|
101
110
|
|
|
102
111
|
Useful flags:
|
|
103
112
|
|
|
104
113
|
- `--repo-root <path>`
|
|
114
|
+
- `--answers-file <path>`
|
|
105
115
|
- `--include <glob...>`
|
|
106
116
|
- `--exclude <glob...>`
|
|
107
117
|
- `--max-files <n>`
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { readFileSync } from "fs";
|
|
5
5
|
import process2 from "process";
|
|
6
|
+
import { createInterface } from "readline/promises";
|
|
6
7
|
import {
|
|
7
8
|
acceptApproval,
|
|
8
9
|
addInput,
|
|
@@ -17,6 +18,8 @@ import {
|
|
|
17
18
|
exportGraphHtml,
|
|
18
19
|
getGitHookStatus,
|
|
19
20
|
getWatchStatus,
|
|
21
|
+
guideManagedSource,
|
|
22
|
+
guideSourceScope,
|
|
20
23
|
importInbox,
|
|
21
24
|
ingestDirectory,
|
|
22
25
|
ingestInputDetailed,
|
|
@@ -39,6 +42,7 @@ import {
|
|
|
39
42
|
readApproval,
|
|
40
43
|
rejectApproval,
|
|
41
44
|
reloadManagedSources,
|
|
45
|
+
resumeSourceSession,
|
|
42
46
|
reviewManagedSource,
|
|
43
47
|
reviewSourceScope,
|
|
44
48
|
runSchedule,
|
|
@@ -224,9 +228,9 @@ program.name("swarmvault").description("SwarmVault is a local-first knowledge co
|
|
|
224
228
|
function readCliVersion() {
|
|
225
229
|
try {
|
|
226
230
|
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
227
|
-
return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "0.
|
|
231
|
+
return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "0.6.0";
|
|
228
232
|
} catch {
|
|
229
|
-
return "0.
|
|
233
|
+
return "0.6.0";
|
|
230
234
|
}
|
|
231
235
|
}
|
|
232
236
|
function parsePositiveInt(value, fallback) {
|
|
@@ -234,6 +238,21 @@ function parsePositiveInt(value, fallback) {
|
|
|
234
238
|
const parsed = Number.parseInt(value, 10);
|
|
235
239
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
236
240
|
}
|
|
241
|
+
function sourceScopeFromManifests(input, manifests) {
|
|
242
|
+
if (!manifests.length) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
const primary = manifests[0];
|
|
246
|
+
return {
|
|
247
|
+
id: primary?.sourceGroupId ?? primary?.sourceId ?? slugForCli(input),
|
|
248
|
+
title: primary?.sourceGroupTitle ?? primary?.title ?? input,
|
|
249
|
+
sourceIds: manifests.map((manifest) => manifest.sourceId),
|
|
250
|
+
kind: primary?.sourceKind
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function slugForCli(value) {
|
|
254
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "source";
|
|
255
|
+
}
|
|
237
256
|
function isJson() {
|
|
238
257
|
return program.opts().json === true;
|
|
239
258
|
}
|
|
@@ -254,6 +273,57 @@ function emitNotice(message) {
|
|
|
254
273
|
process2.stderr.write(`[swarmvault] ${message}
|
|
255
274
|
`);
|
|
256
275
|
}
|
|
276
|
+
function canPromptGuide() {
|
|
277
|
+
return Boolean(process2.stdin.isTTY && process2.stdout.isTTY && !isJson());
|
|
278
|
+
}
|
|
279
|
+
function readGuideAnswersFile(filePath) {
|
|
280
|
+
if (!filePath) {
|
|
281
|
+
return void 0;
|
|
282
|
+
}
|
|
283
|
+
const raw = JSON.parse(readFileSync(filePath, "utf8"));
|
|
284
|
+
if (Array.isArray(raw)) {
|
|
285
|
+
return raw.filter((value) => typeof value === "string");
|
|
286
|
+
}
|
|
287
|
+
if (raw && typeof raw === "object") {
|
|
288
|
+
return Object.fromEntries(
|
|
289
|
+
Object.entries(raw).filter((entry) => typeof entry[0] === "string" && typeof entry[1] === "string")
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
throw new Error("Guide answers files must contain either a JSON object keyed by question id or a JSON array of answers.");
|
|
293
|
+
}
|
|
294
|
+
async function promptGuideAnswers(questions) {
|
|
295
|
+
const rl = createInterface({
|
|
296
|
+
input: process2.stdin,
|
|
297
|
+
output: process2.stdout
|
|
298
|
+
});
|
|
299
|
+
try {
|
|
300
|
+
const answers = {};
|
|
301
|
+
for (const question of questions) {
|
|
302
|
+
const promptLines = [question.prompt];
|
|
303
|
+
if (question.answer) {
|
|
304
|
+
promptLines.push(`Current: ${question.answer}`);
|
|
305
|
+
promptLines.push("Press Enter to keep the current answer.");
|
|
306
|
+
}
|
|
307
|
+
const answer = (await rl.question(`${promptLines.join("\n")}
|
|
308
|
+
> `)).trim();
|
|
309
|
+
if (answer) {
|
|
310
|
+
answers[question.id] = answer;
|
|
311
|
+
} else if (question.answer) {
|
|
312
|
+
answers[question.id] = question.answer;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return answers;
|
|
316
|
+
} finally {
|
|
317
|
+
rl.close();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
async function completeGuideInteractively(guide, fallbackTarget) {
|
|
321
|
+
if (!guide.awaitingInput || !canPromptGuide()) {
|
|
322
|
+
return guide;
|
|
323
|
+
}
|
|
324
|
+
const answers = await promptGuideAnswers(guide.questions);
|
|
325
|
+
return await resumeSourceSession(process2.cwd(), guide.sessionId || fallbackTarget, { answers });
|
|
326
|
+
}
|
|
257
327
|
function getCommandPath(command) {
|
|
258
328
|
const names = [];
|
|
259
329
|
let current = command;
|
|
@@ -276,16 +346,22 @@ program.hook("postAction", async (_thisCommand, actionCommand) => {
|
|
|
276
346
|
emitNotice(notice);
|
|
277
347
|
}
|
|
278
348
|
});
|
|
279
|
-
program.command("init").description("Initialize a SwarmVault workspace in the current directory.").option("--obsidian", "Generate a minimal .obsidian workspace alongside the vault", false).action(async (options) => {
|
|
280
|
-
await initVault(process2.cwd(), { obsidian: options.obsidian ?? false });
|
|
349
|
+
program.command("init").description("Initialize a SwarmVault workspace in the current directory.").option("--obsidian", "Generate a minimal .obsidian workspace alongside the vault", false).addOption(new Option("--profile <profile>", "Starter workspace profile").choices(["default", "personal-research"])).action(async (options) => {
|
|
350
|
+
await initVault(process2.cwd(), { obsidian: options.obsidian ?? false, profile: options.profile });
|
|
281
351
|
if (isJson()) {
|
|
282
|
-
emitJson({
|
|
352
|
+
emitJson({
|
|
353
|
+
status: "initialized",
|
|
354
|
+
rootDir: process2.cwd(),
|
|
355
|
+
obsidian: options.obsidian ?? false,
|
|
356
|
+
profile: options.profile ?? "default"
|
|
357
|
+
});
|
|
283
358
|
} else {
|
|
284
359
|
log("Initialized SwarmVault workspace.");
|
|
285
360
|
}
|
|
286
361
|
});
|
|
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(
|
|
362
|
+
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", false).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").action(
|
|
288
363
|
async (input, options) => {
|
|
364
|
+
const guideAnswers = readGuideAnswersFile(options.answersFile);
|
|
289
365
|
const maxAssetSize = typeof options.maxAssetSize === "string" && options.maxAssetSize.trim() ? parsePositiveInt(options.maxAssetSize, 0) || void 0 : void 0;
|
|
290
366
|
const maxFiles = typeof options.maxFiles === "string" && options.maxFiles.trim() ? parsePositiveInt(options.maxFiles, 0) || void 0 : void 0;
|
|
291
367
|
const extractClasses = [
|
|
@@ -308,8 +384,7 @@ program.command("ingest").description("Ingest a local file path, directory path,
|
|
|
308
384
|
(fs) => fs.stat(input).then((stat) => stat.isDirectory() ? ingestDirectory(process2.cwd(), input, commonOptions) : null).catch(() => null)
|
|
309
385
|
) : null;
|
|
310
386
|
if (directoryResult) {
|
|
311
|
-
const
|
|
312
|
-
await compileVault(process2.cwd(), {});
|
|
387
|
+
const scope2 = options.review || options.guide ? await (async () => {
|
|
313
388
|
const pathModule = await import("path");
|
|
314
389
|
const absoluteInput = pathModule.resolve(process2.cwd(), input);
|
|
315
390
|
const sourceIds = (await listManifests(process2.cwd())).filter((manifest) => {
|
|
@@ -319,14 +394,27 @@ program.command("ingest").description("Ingest a local file path, directory path,
|
|
|
319
394
|
const relative = pathModule.relative(absoluteInput, pathModule.resolve(manifest.originalPath));
|
|
320
395
|
return relative === "" || !relative.startsWith("..") && !pathModule.isAbsolute(relative);
|
|
321
396
|
}).map((manifest) => manifest.sourceId);
|
|
322
|
-
return sourceIds.length ?
|
|
397
|
+
return sourceIds.length ? {
|
|
323
398
|
id: `directory-${absoluteInput.split(pathModule.sep).pop() ?? "source"}`,
|
|
324
399
|
title: absoluteInput.split(pathModule.sep).pop() ?? absoluteInput,
|
|
325
|
-
sourceIds
|
|
326
|
-
|
|
400
|
+
sourceIds,
|
|
401
|
+
kind: "directory"
|
|
402
|
+
} : void 0;
|
|
327
403
|
})() : void 0;
|
|
404
|
+
const shouldStage = Boolean(scope2 && (directoryResult.imported.length || directoryResult.updated.length));
|
|
405
|
+
const review3 = shouldStage && options.review && !options.guide ? await (async () => {
|
|
406
|
+
await compileVault(process2.cwd(), {});
|
|
407
|
+
return await reviewSourceScope(process2.cwd(), scope2);
|
|
408
|
+
})() : void 0;
|
|
409
|
+
const guide2 = shouldStage && options.guide ? await (async () => {
|
|
410
|
+
await compileVault(process2.cwd(), {});
|
|
411
|
+
return await guideSourceScope(process2.cwd(), scope2, { answers: guideAnswers });
|
|
412
|
+
})() : void 0;
|
|
413
|
+
const completedGuide2 = guide2 && !options.answersFile ? await completeGuideInteractively(guide2, scope2?.id ?? input) : guide2;
|
|
328
414
|
if (isJson()) {
|
|
329
|
-
emitJson(
|
|
415
|
+
emitJson(
|
|
416
|
+
completedGuide2 ? { ingest: directoryResult, guide: completedGuide2 } : review3 ? { ingest: directoryResult, review: review3 } : directoryResult
|
|
417
|
+
);
|
|
330
418
|
} else {
|
|
331
419
|
log(
|
|
332
420
|
`Imported ${directoryResult.imported.length} file(s), updated ${directoryResult.updated.length}, skipped ${directoryResult.skipped.length}.`
|
|
@@ -334,21 +422,29 @@ program.command("ingest").description("Ingest a local file path, directory path,
|
|
|
334
422
|
if (review3) {
|
|
335
423
|
log(`Staged source review at ${review3.reviewPath}.`);
|
|
336
424
|
}
|
|
425
|
+
if (completedGuide2?.awaitingInput) {
|
|
426
|
+
log(
|
|
427
|
+
`Created guided session at ${completedGuide2.sessionPath}. Resume with \`swarmvault source session ${completedGuide2.sessionId}\`.`
|
|
428
|
+
);
|
|
429
|
+
} else if (completedGuide2?.guidePath) {
|
|
430
|
+
log(`Staged guided session at ${completedGuide2.guidePath}.`);
|
|
431
|
+
}
|
|
337
432
|
}
|
|
338
433
|
return;
|
|
339
434
|
}
|
|
340
435
|
const ingest = await ingestInputDetailed(process2.cwd(), input, commonOptions);
|
|
341
|
-
const
|
|
436
|
+
const scope = sourceScopeFromManifests(input, [...ingest.created, ...ingest.updated, ...ingest.unchanged]);
|
|
437
|
+
const review2 = options.review && !options.guide && scope && (ingest.created.length || ingest.updated.length || ingest.unchanged.length) ? await (async () => {
|
|
342
438
|
await compileVault(process2.cwd(), {});
|
|
343
|
-
|
|
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
|
-
});
|
|
439
|
+
return await reviewSourceScope(process2.cwd(), scope);
|
|
349
440
|
})() : void 0;
|
|
441
|
+
const guide = options.guide && scope && (ingest.created.length || ingest.updated.length || ingest.unchanged.length) ? await (async () => {
|
|
442
|
+
await compileVault(process2.cwd(), {});
|
|
443
|
+
return await guideSourceScope(process2.cwd(), scope, { answers: guideAnswers });
|
|
444
|
+
})() : void 0;
|
|
445
|
+
const completedGuide = guide && !options.answersFile ? await completeGuideInteractively(guide, scope?.id ?? input) : guide;
|
|
350
446
|
if (isJson()) {
|
|
351
|
-
emitJson(review2 ? { ingest, review: review2 } : ingest);
|
|
447
|
+
emitJson(completedGuide ? { ingest, guide: completedGuide } : review2 ? { ingest, review: review2 } : ingest);
|
|
352
448
|
} else {
|
|
353
449
|
const primary = [...ingest.created, ...ingest.updated, ...ingest.unchanged][0];
|
|
354
450
|
if (ingest.created.length + ingest.updated.length + ingest.removed.length <= 1 && primary) {
|
|
@@ -361,6 +457,13 @@ program.command("ingest").description("Ingest a local file path, directory path,
|
|
|
361
457
|
if (review2) {
|
|
362
458
|
log(`Staged source review at ${review2.reviewPath}.`);
|
|
363
459
|
}
|
|
460
|
+
if (completedGuide?.awaitingInput) {
|
|
461
|
+
log(
|
|
462
|
+
`Created guided session at ${completedGuide.sessionPath}. Resume with \`swarmvault source session ${completedGuide.sessionId}\`.`
|
|
463
|
+
);
|
|
464
|
+
} else if (completedGuide?.guidePath) {
|
|
465
|
+
log(`Staged guided session at ${completedGuide.guidePath}.`);
|
|
466
|
+
}
|
|
364
467
|
}
|
|
365
468
|
}
|
|
366
469
|
);
|
|
@@ -376,20 +479,26 @@ program.command("add").description("Capture supported URLs into normalized markd
|
|
|
376
479
|
}
|
|
377
480
|
});
|
|
378
481
|
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(
|
|
482
|
+
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("--guide", "Stage a guided source integration bundle after sync and compile", false).option("--answers-file <path>", "JSON file with guided-session answers keyed by question id or listed in prompt order").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
483
|
async (input, options) => {
|
|
484
|
+
const guideAnswers = readGuideAnswersFile(options.answersFile);
|
|
381
485
|
const result = await addManagedSource(process2.cwd(), input, {
|
|
382
486
|
compile: options.compile,
|
|
383
487
|
brief: options.brief,
|
|
384
488
|
review: options.review,
|
|
489
|
+
guide: options.guide,
|
|
490
|
+
guideAnswers,
|
|
385
491
|
maxPages: options.maxPages ? parsePositiveInt(options.maxPages, 0) || void 0 : void 0,
|
|
386
492
|
maxDepth: options.maxDepth ? parsePositiveInt(options.maxDepth, 0) || void 0 : void 0
|
|
387
493
|
});
|
|
494
|
+
if (result.guide && !options.answersFile) {
|
|
495
|
+
result.guide = await completeGuideInteractively(result.guide, result.source.id);
|
|
496
|
+
}
|
|
388
497
|
if (isJson()) {
|
|
389
498
|
emitJson(result);
|
|
390
499
|
} else {
|
|
391
500
|
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}` : ""}`
|
|
501
|
+
`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}` : ""}${result.guide?.awaitingInput ? ` Session: ${result.guide.sessionPath}. Resume with \`swarmvault source session ${result.guide.sessionId}\`.` : result.guide?.guidePath ? ` Guide: ${result.guide.guidePath}` : ""}`
|
|
393
502
|
);
|
|
394
503
|
}
|
|
395
504
|
}
|
|
@@ -406,21 +515,28 @@ source.command("list").description("List managed sources registered in this vaul
|
|
|
406
515
|
}
|
|
407
516
|
}
|
|
408
517
|
});
|
|
409
|
-
source.command("reload").description("Re-sync one managed source or all managed sources, then optionally compile and refresh briefs.").argument("[id]", "Managed source id").option("--all", "Reload all managed sources", false).option("--no-compile", "Re-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(
|
|
518
|
+
source.command("reload").description("Re-sync one managed source or all managed sources, then optionally compile and refresh briefs.").argument("[id]", "Managed source id").option("--all", "Reload all managed sources", false).option("--no-compile", "Re-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("--guide", "Stage a guided source integration bundle after sync and compile", false).option("--answers-file <path>", "JSON file with guided-session answers keyed by question id or listed in prompt order").option("--max-pages <n>", "Maximum number of pages to crawl for docs sources").option("--max-depth <n>", "Maximum crawl depth for docs sources").action(
|
|
410
519
|
async (id, options) => {
|
|
520
|
+
const guideAnswers = readGuideAnswersFile(options.answersFile);
|
|
411
521
|
const result = await reloadManagedSources(process2.cwd(), {
|
|
412
522
|
id,
|
|
413
523
|
all: options.all ?? false,
|
|
414
524
|
compile: options.compile,
|
|
415
525
|
brief: options.brief,
|
|
526
|
+
review: options.review,
|
|
527
|
+
guide: options.guide,
|
|
528
|
+
guideAnswers,
|
|
416
529
|
maxPages: options.maxPages ? parsePositiveInt(options.maxPages, 0) || void 0 : void 0,
|
|
417
530
|
maxDepth: options.maxDepth ? parsePositiveInt(options.maxDepth, 0) || void 0 : void 0
|
|
418
531
|
});
|
|
532
|
+
if (!options.answersFile && result.guides.length === 1) {
|
|
533
|
+
result.guides = [await completeGuideInteractively(result.guides[0], result.sources[0]?.id ?? id ?? "source")];
|
|
534
|
+
}
|
|
419
535
|
if (isJson()) {
|
|
420
536
|
emitJson(result);
|
|
421
537
|
} else {
|
|
422
538
|
log(
|
|
423
|
-
`Reloaded ${result.sources.length} source(s).${result.compile ? ` Compiled ${result.compile.sourceCount} source(s).` : ""}${result.briefPaths.length ? ` Briefs: ${result.briefPaths.length}.` : ""}`
|
|
539
|
+
`Reloaded ${result.sources.length} source(s).${result.compile ? ` Compiled ${result.compile.sourceCount} source(s).` : ""}${result.briefPaths.length ? ` Briefs: ${result.briefPaths.length}.` : ""}${result.reviews.length ? ` Reviews: ${result.reviews.length}.` : ""}${result.guides.length ? ` Guides/Sessions: ${result.guides.length}.` : ""}`
|
|
424
540
|
);
|
|
425
541
|
}
|
|
426
542
|
}
|
|
@@ -441,6 +557,36 @@ source.command("review").description("Stage a source review artifact for a manag
|
|
|
441
557
|
log(`Staged source review at ${result.reviewPath}.`);
|
|
442
558
|
}
|
|
443
559
|
});
|
|
560
|
+
source.command("guide").description("Create or resume a guided source session for a managed source id or raw source id.").argument("<id>", "Managed source id or raw source id").option("--answers-file <path>", "JSON file with guided-session answers keyed by question id or listed in prompt order").action(async (id, options) => {
|
|
561
|
+
const guideAnswers = readGuideAnswersFile(options.answersFile);
|
|
562
|
+
let result = await guideManagedSource(process2.cwd(), id, { answers: guideAnswers });
|
|
563
|
+
if (!options.answersFile) {
|
|
564
|
+
result = await completeGuideInteractively(result, id);
|
|
565
|
+
}
|
|
566
|
+
if (isJson()) {
|
|
567
|
+
emitJson(result);
|
|
568
|
+
} else {
|
|
569
|
+
if (result.awaitingInput) {
|
|
570
|
+
log(`Created guided session at ${result.sessionPath}. Resume with \`swarmvault source session ${result.sessionId}\`.`);
|
|
571
|
+
} else {
|
|
572
|
+
log(`Staged guided session at ${result.guidePath}.`);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
source.command("session").description("Resume the latest guided source session for a managed source id, raw source id, source scope id, or session id.").argument("<id>", "Managed source id, raw source id, source scope id, or guided session id").option("--answers-file <path>", "JSON file with guided-session answers keyed by question id or listed in prompt order").action(async (id, options) => {
|
|
577
|
+
const guideAnswers = readGuideAnswersFile(options.answersFile);
|
|
578
|
+
let result = await resumeSourceSession(process2.cwd(), id, { answers: guideAnswers });
|
|
579
|
+
if (!options.answersFile) {
|
|
580
|
+
result = await completeGuideInteractively(result, id);
|
|
581
|
+
}
|
|
582
|
+
if (isJson()) {
|
|
583
|
+
emitJson(result);
|
|
584
|
+
} else if (result.awaitingInput) {
|
|
585
|
+
log(`Updated guided session at ${result.sessionPath}. Resume with \`swarmvault source session ${result.sessionId}\` when ready.`);
|
|
586
|
+
} else {
|
|
587
|
+
log(`Staged guided session at ${result.guidePath}.`);
|
|
588
|
+
}
|
|
589
|
+
});
|
|
444
590
|
var inbox = program.command("inbox").description("Inbox and capture workflows.");
|
|
445
591
|
inbox.command("import").description("Import supported files from the configured inbox directory.").argument("[dir]", "Optional inbox directory override").action(async (dir) => {
|
|
446
592
|
const result = await importInbox(process2.cwd(), dir);
|
|
@@ -651,7 +797,7 @@ review.command("list").description("List staged approval bundles and their resol
|
|
|
651
797
|
}
|
|
652
798
|
for (const approval of approvals) {
|
|
653
799
|
log(
|
|
654
|
-
`${approval.approvalId} pending=${approval.pendingCount} accepted=${approval.acceptedCount} rejected=${approval.rejectedCount} created=${approval.createdAt}`
|
|
800
|
+
`${approval.approvalId}${approval.bundleType ? ` [${approval.bundleType}]` : ""}${approval.title ? ` ${approval.title}` : ""} pending=${approval.pendingCount} accepted=${approval.acceptedCount} rejected=${approval.rejectedCount} created=${approval.createdAt}`
|
|
655
801
|
);
|
|
656
802
|
}
|
|
657
803
|
});
|
|
@@ -661,9 +807,13 @@ review.command("show").description("Show the entries inside a staged approval bu
|
|
|
661
807
|
emitJson(approval);
|
|
662
808
|
return;
|
|
663
809
|
}
|
|
664
|
-
log(
|
|
810
|
+
log(
|
|
811
|
+
`${approval.approvalId}${approval.bundleType ? ` [${approval.bundleType}]` : ""}${approval.title ? ` ${approval.title}` : ""} pending=${approval.pendingCount} accepted=${approval.acceptedCount} rejected=${approval.rejectedCount}`
|
|
812
|
+
);
|
|
665
813
|
for (const entry of approval.entries) {
|
|
666
|
-
log(
|
|
814
|
+
log(
|
|
815
|
+
`- ${entry.status} ${entry.changeType}${entry.label ? ` [${entry.label}]` : ""} ${entry.pageId} ${entry.nextPath ?? entry.previousPath ?? ""}`.trim()
|
|
816
|
+
);
|
|
667
817
|
if (entry.changeSummary) log(` Summary: ${entry.changeSummary}`);
|
|
668
818
|
if (entry.diff) {
|
|
669
819
|
log("");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmvaultai/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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.
|
|
41
|
+
"@swarmvaultai/engine": "0.6.0",
|
|
42
42
|
"commander": "^14.0.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|