@swarmvaultai/cli 1.0.0 → 1.1.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 -2
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  consolidateVault,
19
19
  createSupersessionEdge,
20
20
  deleteManagedSource,
21
+ downloadWhisperModel,
21
22
  explainGraphVault,
22
23
  exploreVault,
23
24
  exportGraphFormat,
@@ -52,6 +53,7 @@ import {
52
53
  queryGraphVault,
53
54
  queryVault,
54
55
  readApproval,
56
+ registerLocalWhisperProvider,
55
57
  rejectApproval,
56
58
  reloadManagedSources,
57
59
  removeWatchedRoot,
@@ -65,6 +67,7 @@ import {
65
67
  serveSchedules,
66
68
  startGraphServer,
67
69
  startMcpServer,
70
+ summarizeLocalWhisperSetup,
68
71
  uninstallGitHooks,
69
72
  watchVault
70
73
  } from "@swarmvaultai/engine";
@@ -284,9 +287,9 @@ program.name("swarmvault").description("SwarmVault is a local-first knowledge co
284
287
  function readCliVersion() {
285
288
  try {
286
289
  const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
287
- return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "1.0.0";
290
+ return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "1.1.0";
288
291
  } catch {
289
- return "1.0.0";
292
+ return "1.1.0";
290
293
  }
291
294
  }
292
295
  function parsePositiveInt(value, fallback) {
@@ -1141,6 +1144,77 @@ candidate.command("preview-scores").description("Show promotion scores for stage
1141
1144
  log(`${verdict} ${decision.pageId} score=${decision.score.toFixed(2)} ${decision.reasons.join("; ")}`);
1142
1145
  }
1143
1146
  });
1147
+ var provider = program.command("provider").description("Configure provider adapters.");
1148
+ provider.command("setup").description("Interactive setup for a provider (currently: local-whisper). Checks for required binaries and downloads models.").option("--local-whisper", "Set up the local Whisper (whisper.cpp) provider", false).option("--model <model>", "Whisper model to install (e.g. tiny.en, base.en, small.en)", "base.en").option("--apply", "Skip confirmation prompts and download/install automatically", false).option("--set-audio-provider", "Force tasks.audioProvider to local-whisper even if another provider is already configured", false).action(async (options) => {
1149
+ if (!options.localWhisper) {
1150
+ throw new Error("Specify a provider to set up (currently: --local-whisper).");
1151
+ }
1152
+ const modelName = (options.model ?? "base.en").trim();
1153
+ if (!modelName) {
1154
+ throw new Error("Model name cannot be empty.");
1155
+ }
1156
+ const status = await summarizeLocalWhisperSetup({ modelName });
1157
+ if (isJson()) {
1158
+ emitJson({ ...status, apply: Boolean(options.apply), configWrite: null });
1159
+ return;
1160
+ }
1161
+ log(`whisper.cpp binary: ${status.binary.found ? status.binary.path : "NOT FOUND"}`);
1162
+ if (!status.binary.found) {
1163
+ log(status.binary.installHint);
1164
+ log("Re-run once whisper.cpp is on $PATH.");
1165
+ process2.exitCode = 1;
1166
+ return;
1167
+ }
1168
+ log(`Model "${modelName}": ${status.model.exists ? status.model.expectedPath : "missing"}`);
1169
+ if (!status.model.exists) {
1170
+ const sizeHint = status.model.approximateSize ? ` (~${status.model.approximateSize})` : "";
1171
+ log(`Download plan: ${status.model.downloadUrl}${sizeHint} -> ${status.model.expectedPath}`);
1172
+ const proceed = options.apply === true || await confirmInteractive(`Download ggml-${modelName}.bin now?`);
1173
+ if (!proceed) {
1174
+ log("Skipped download. Run with --apply (or confirm y) to download.");
1175
+ process2.exitCode = 1;
1176
+ return;
1177
+ }
1178
+ const downloaded = await downloadWhisperModel({
1179
+ modelName,
1180
+ onProgress: (progress) => {
1181
+ if (progress.totalBytes) {
1182
+ const percent = Math.floor(progress.downloadedBytes / progress.totalBytes * 100);
1183
+ process2.stderr.write(`\r[swarmvault] downloading ggml-${modelName}.bin: ${percent}%`);
1184
+ }
1185
+ }
1186
+ });
1187
+ process2.stderr.write("\n");
1188
+ log(`Downloaded ${downloaded.bytes} bytes to ${downloaded.path}.`);
1189
+ }
1190
+ const registration = await registerLocalWhisperProvider({
1191
+ rootDir: process2.cwd(),
1192
+ model: modelName,
1193
+ setAsAudioProvider: options.setAudioProvider ? true : void 0
1194
+ });
1195
+ if (registration.providerWasAdded) {
1196
+ log(`Registered provider "local-whisper" in ${registration.configPath}.`);
1197
+ } else if (registration.providerWasUpdated) {
1198
+ log(`Updated existing "local-whisper" provider entry in ${registration.configPath}.`);
1199
+ } else {
1200
+ log(`Provider "local-whisper" already configured in ${registration.configPath}.`);
1201
+ }
1202
+ if (registration.audioProviderSet) {
1203
+ log(`Set tasks.audioProvider = "local-whisper".`);
1204
+ } else if (registration.previousAudioProvider && registration.previousAudioProvider !== "local-whisper") {
1205
+ log(`Left tasks.audioProvider = "${registration.previousAudioProvider}" untouched (use --set-audio-provider to override).`);
1206
+ }
1207
+ });
1208
+ async function confirmInteractive(message) {
1209
+ if (!process2.stdin.isTTY) return false;
1210
+ const rl = createInterface({ input: process2.stdin, output: process2.stderr });
1211
+ try {
1212
+ const answer = (await rl.question(`${message} [y/N] `)).trim().toLowerCase();
1213
+ return answer === "y" || answer === "yes";
1214
+ } finally {
1215
+ rl.close();
1216
+ }
1217
+ }
1144
1218
  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").option("--root <path>", "Watch this repo root instead of config/auto-discovery (repeat for multiple)", collectRepeated, []).action(async (options) => {
1145
1219
  const debounceMs = parsePositiveInt(options.debounce, 900);
1146
1220
  const overrideRoots = options.root && options.root.length > 0 ? options.root : void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swarmvaultai/cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
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": "1.0.0",
47
+ "@swarmvaultai/engine": "1.1.0",
48
48
  "commander": "^14.0.1"
49
49
  },
50
50
  "devDependencies": {