@knowsuchagency/fulcrum 1.12.1 → 1.13.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/bin/fulcrum.js CHANGED
@@ -935,10 +935,12 @@ var init_errors = __esm(() => {
935
935
  ExitCodes = {
936
936
  SUCCESS: 0,
937
937
  ERROR: 1,
938
+ GENERAL_ERROR: 1,
938
939
  INVALID_ARGS: 2,
939
940
  SERVER_UNREACHABLE: 3,
940
941
  NOT_FOUND: 4,
941
- VALIDATION_ERROR: 5
942
+ VALIDATION_ERROR: 5,
943
+ NETWORK_ERROR: 6
942
944
  };
943
945
  CliError = class CliError extends Error {
944
946
  code;
@@ -47082,7 +47084,7 @@ var marketplace_default = `{
47082
47084
  "name": "fulcrum",
47083
47085
  "source": "./",
47084
47086
  "description": "Task orchestration for Claude Code",
47085
- "version": "1.12.1",
47087
+ "version": "1.13.0",
47086
47088
  "skills": [
47087
47089
  "./skills/fulcrum"
47088
47090
  ],
@@ -47908,7 +47910,7 @@ var notifyCommand = defineCommand({
47908
47910
  });
47909
47911
 
47910
47912
  // cli/src/commands/up.ts
47911
- import { spawn } from "child_process";
47913
+ import { spawn as spawn2 } from "child_process";
47912
47914
  import { existsSync as existsSync5 } from "fs";
47913
47915
  import { dirname as dirname3, join as join5 } from "path";
47914
47916
  import { fileURLToPath } from "url";
@@ -48191,11 +48193,86 @@ function installUv() {
48191
48193
  return false;
48192
48194
  return installDependency(dep);
48193
48195
  }
48196
+
48197
+ // cli/src/commands/update.ts
48198
+ import { spawn, spawnSync as spawnSync3 } from "child_process";
48199
+ init_errors();
48200
+
48201
+ // shared/semver.ts
48202
+ function parseSemver(version) {
48203
+ const cleaned = version.trim().replace(/^v/, "");
48204
+ if (cleaned.length === 0)
48205
+ return null;
48206
+ const [mainAndPre] = cleaned.split("+", 1);
48207
+ const [main, preReleaseRaw] = mainAndPre.split("-", 2);
48208
+ const parts = main.split(".");
48209
+ if (parts.length > 3 || parts.length === 0)
48210
+ return null;
48211
+ if (parts.some((part) => part.length > 1 && part.startsWith("0")))
48212
+ return null;
48213
+ const major = Number(parts[0]);
48214
+ const minor = Number(parts[1] ?? "0");
48215
+ const patch = Number(parts[2] ?? "0");
48216
+ if (!Number.isFinite(major) || !Number.isFinite(minor) || !Number.isFinite(patch))
48217
+ return null;
48218
+ if (major < 0 || minor < 0 || patch < 0)
48219
+ return null;
48220
+ if (preReleaseRaw) {
48221
+ const preReleaseParts = preReleaseRaw.split(".");
48222
+ if (preReleaseParts.some((part) => /^\d+$/.test(part) && part.length > 1 && part.startsWith("0"))) {
48223
+ return null;
48224
+ }
48225
+ }
48226
+ const preRelease = preReleaseRaw ? preReleaseRaw.split(".").map((part) => /^\d+$/.test(part) ? Number(part) : part) : [];
48227
+ return { major, minor, patch, preRelease };
48228
+ }
48229
+ function compareIdentifiers(a2, b2) {
48230
+ if (typeof a2 === "number" && typeof b2 === "number")
48231
+ return a2 - b2;
48232
+ if (typeof a2 === "number")
48233
+ return -1;
48234
+ if (typeof b2 === "number")
48235
+ return 1;
48236
+ return a2.localeCompare(b2);
48237
+ }
48238
+ function compareVersions(v1, v2) {
48239
+ const parsed1 = parseSemver(v1);
48240
+ const parsed2 = parseSemver(v2);
48241
+ if (!parsed1 || !parsed2)
48242
+ return 0;
48243
+ if (parsed1.major !== parsed2.major)
48244
+ return parsed1.major - parsed2.major;
48245
+ if (parsed1.minor !== parsed2.minor)
48246
+ return parsed1.minor - parsed2.minor;
48247
+ if (parsed1.patch !== parsed2.patch)
48248
+ return parsed1.patch - parsed2.patch;
48249
+ const pre1 = parsed1.preRelease;
48250
+ const pre2 = parsed2.preRelease;
48251
+ if (pre1.length === 0 && pre2.length === 0)
48252
+ return 0;
48253
+ if (pre1.length === 0)
48254
+ return 1;
48255
+ if (pre2.length === 0)
48256
+ return -1;
48257
+ const maxLen = Math.max(pre1.length, pre2.length);
48258
+ for (let i2 = 0;i2 < maxLen; i2++) {
48259
+ const id1 = pre1[i2];
48260
+ const id2 = pre2[i2];
48261
+ if (id1 === undefined)
48262
+ return -1;
48263
+ if (id2 === undefined)
48264
+ return 1;
48265
+ const diff = compareIdentifiers(id1, id2);
48266
+ if (diff !== 0)
48267
+ return diff;
48268
+ }
48269
+ return 0;
48270
+ }
48194
48271
  // package.json
48195
48272
  var package_default = {
48196
48273
  name: "@knowsuchagency/fulcrum",
48197
48274
  private: true,
48198
- version: "1.12.1",
48275
+ version: "1.13.0",
48199
48276
  description: "Harness Attention. Orchestrate Agents. Ship.",
48200
48277
  license: "PolyForm-Perimeter-1.0.0",
48201
48278
  type: "module",
@@ -48286,6 +48363,168 @@ var package_default = {
48286
48363
  }
48287
48364
  };
48288
48365
 
48366
+ // cli/src/commands/update.ts
48367
+ var GITHUB_REPO = "knowsuchagency/fulcrum";
48368
+ var NPM_PACKAGE = "@knowsuchagency/fulcrum";
48369
+ async function fetchLatestVersionFromNpm() {
48370
+ try {
48371
+ const response = await fetch(`https://registry.npmjs.org/${NPM_PACKAGE}/latest`, {
48372
+ headers: { Accept: "application/json" }
48373
+ });
48374
+ if (!response.ok)
48375
+ return null;
48376
+ const data = await response.json();
48377
+ return data.version || null;
48378
+ } catch {
48379
+ return null;
48380
+ }
48381
+ }
48382
+ async function checkForUpdates() {
48383
+ const currentVersion = package_default.version;
48384
+ const latestVersion = await fetchLatestVersionFromNpm();
48385
+ let updateAvailable = false;
48386
+ if (latestVersion) {
48387
+ updateAvailable = compareVersions(latestVersion, currentVersion) > 0;
48388
+ }
48389
+ return { currentVersion, latestVersion, updateAvailable };
48390
+ }
48391
+ function isBunAvailable() {
48392
+ try {
48393
+ const result = spawnSync3("bun", ["--version"], { stdio: "pipe" });
48394
+ return result.status === 0;
48395
+ } catch {
48396
+ return false;
48397
+ }
48398
+ }
48399
+ function getPackageRunner() {
48400
+ if (isBunAvailable()) {
48401
+ return { command: "bunx", execCommand: "bunx" };
48402
+ }
48403
+ return { command: "npx", execCommand: "npx" };
48404
+ }
48405
+ function stopServer() {
48406
+ const { execCommand } = getPackageRunner();
48407
+ console.log("Stopping current server...");
48408
+ const result = spawnSync3(execCommand, [`${NPM_PACKAGE}@latest`, "down"], {
48409
+ stdio: "inherit",
48410
+ shell: true
48411
+ });
48412
+ if (result.status === 0) {
48413
+ console.log("Server stopped.");
48414
+ } else {
48415
+ console.log("Server was not running (or already stopped).");
48416
+ }
48417
+ }
48418
+ function installLatestVersion() {
48419
+ const { execCommand, command } = getPackageRunner();
48420
+ console.log(`
48421
+ Installing latest version via ${command}...`);
48422
+ const args = command === "bunx" ? ["--bun", `${NPM_PACKAGE}@latest`, "--version"] : ["--yes", "--ignore-scripts", `${NPM_PACKAGE}@latest`, "--version"];
48423
+ const result = spawnSync3(execCommand, args, {
48424
+ stdio: "inherit",
48425
+ shell: true
48426
+ });
48427
+ if (result.status === 0) {
48428
+ console.log("Latest version installed.");
48429
+ return true;
48430
+ }
48431
+ console.error("Failed to install latest version.");
48432
+ return false;
48433
+ }
48434
+ function startServer() {
48435
+ return new Promise((resolve) => {
48436
+ const { command, execCommand } = getPackageRunner();
48437
+ const args = [`${NPM_PACKAGE}@latest`, "up"];
48438
+ console.log(`
48439
+ Starting server: ${command} ${args.join(" ")}
48440
+ `);
48441
+ const child = spawn(execCommand, args, {
48442
+ stdio: "inherit",
48443
+ shell: true
48444
+ });
48445
+ child.on("close", (code) => {
48446
+ resolve(code ?? 0);
48447
+ });
48448
+ child.on("error", (err) => {
48449
+ console.error("Failed to start server:", err.message);
48450
+ resolve(1);
48451
+ });
48452
+ });
48453
+ }
48454
+ async function runUpdate() {
48455
+ stopServer();
48456
+ const installed = installLatestVersion();
48457
+ if (!installed) {
48458
+ console.error(`
48459
+ Update failed during installation. Your previous version may still work.`);
48460
+ console.log("Try running: fulcrum up");
48461
+ return 1;
48462
+ }
48463
+ console.log(`
48464
+ Starting updated server...`);
48465
+ return await startServer();
48466
+ }
48467
+ async function handleUpdateCommand(flags) {
48468
+ const checkOnly = flags.check === "true";
48469
+ const { command } = getPackageRunner();
48470
+ if (isJsonOutput()) {
48471
+ const result = await checkForUpdates();
48472
+ output({
48473
+ ...result,
48474
+ updateCommand: "fulcrum update",
48475
+ releaseUrl: `https://github.com/${GITHUB_REPO}/releases/latest`
48476
+ });
48477
+ return;
48478
+ }
48479
+ console.log("Checking for updates...");
48480
+ const { currentVersion, latestVersion, updateAvailable } = await checkForUpdates();
48481
+ if (!latestVersion) {
48482
+ throw new CliError("NETWORK_ERROR", "Could not check for updates. Please check your internet connection.", ExitCodes.NETWORK_ERROR);
48483
+ }
48484
+ console.log(`Current version: ${currentVersion}`);
48485
+ console.log(`Latest version: ${latestVersion}`);
48486
+ if (!updateAvailable) {
48487
+ console.log(`
48488
+ \u2713 You are running the latest version.`);
48489
+ return;
48490
+ }
48491
+ console.log(`
48492
+ \u2191 Update available: ${currentVersion} \u2192 ${latestVersion}`);
48493
+ if (checkOnly) {
48494
+ console.log(`
48495
+ To update, run: fulcrum update`);
48496
+ console.log(`Or manually: ${command} ${NPM_PACKAGE}@latest up`);
48497
+ return;
48498
+ }
48499
+ console.log(`
48500
+ Updating Fulcrum...`);
48501
+ console.log(`This will stop the current server, install the update, and restart.
48502
+ `);
48503
+ const exitCode = await runUpdate();
48504
+ if (exitCode !== 0) {
48505
+ throw new CliError("GENERAL_ERROR", "Update failed", ExitCodes.GENERAL_ERROR);
48506
+ }
48507
+ console.log(`
48508
+ \u2713 Fulcrum has been updated and restarted.`);
48509
+ }
48510
+ var updateCommand = defineCommand({
48511
+ meta: {
48512
+ name: "update",
48513
+ description: "Check for updates and update Fulcrum to the latest version"
48514
+ },
48515
+ args: {
48516
+ ...globalArgs,
48517
+ check: {
48518
+ type: "boolean",
48519
+ description: "Only check for updates, do not install"
48520
+ }
48521
+ },
48522
+ async run({ args }) {
48523
+ setupJsonOutput(args);
48524
+ await handleUpdateCommand(toFlags(args));
48525
+ }
48526
+ });
48527
+
48289
48528
  // cli/src/commands/up.ts
48290
48529
  function getPackageRoot() {
48291
48530
  const currentFile = fileURLToPath(import.meta.url);
@@ -48300,6 +48539,24 @@ function getPackageRoot() {
48300
48539
  }
48301
48540
  async function handleUpCommand(flags) {
48302
48541
  const autoYes = flags.yes === "true" || flags.y === "true";
48542
+ const shouldUpdate = flags.update === "true";
48543
+ if (shouldUpdate) {
48544
+ console.error("Checking for updates...");
48545
+ const { currentVersion, latestVersion, updateAvailable } = await checkForUpdates();
48546
+ if (latestVersion && updateAvailable) {
48547
+ console.error(`Update available: ${currentVersion} \u2192 ${latestVersion}`);
48548
+ console.error("Installing update...");
48549
+ const installed = installLatestVersion();
48550
+ if (!installed) {
48551
+ throw new CliError("UPDATE_FAILED", "Failed to install update", ExitCodes.ERROR);
48552
+ }
48553
+ console.error("Update installed successfully.");
48554
+ } else if (latestVersion) {
48555
+ console.error(`Already on latest version: ${currentVersion}`);
48556
+ } else {
48557
+ console.error("Could not check for updates, continuing with current version.");
48558
+ }
48559
+ }
48303
48560
  if (needsViboraMigration()) {
48304
48561
  const viboraDir = getLegacyViboraDir();
48305
48562
  console.error(`
@@ -48402,7 +48659,7 @@ Found existing Vibora data at ${viboraDir}`);
48402
48659
  const fulcrumDir = getFulcrumDir();
48403
48660
  const debug = flags.debug === "true";
48404
48661
  console.error(`Starting Fulcrum server${debug ? " (debug mode)" : ""}...`);
48405
- const serverProc = spawn("bun", [serverPath], {
48662
+ const serverProc = spawn2("bun", [serverPath], {
48406
48663
  detached: true,
48407
48664
  stdio: "ignore",
48408
48665
  env: {
@@ -48467,7 +48724,8 @@ var upCommand = defineCommand({
48467
48724
  args: {
48468
48725
  ...globalArgs,
48469
48726
  yes: { type: "boolean", alias: "y", description: "Auto-answer yes to prompts" },
48470
- host: { type: "boolean", description: "Bind to 0.0.0.0 (expose to network)" }
48727
+ host: { type: "boolean", description: "Bind to 0.0.0.0 (expose to network)" },
48728
+ update: { type: "boolean", description: "Check for and install updates before starting" }
48471
48729
  },
48472
48730
  async run({ args }) {
48473
48731
  setupJsonOutput(args);
@@ -48829,6 +49087,7 @@ var main = defineCommand({
48829
49087
  doctor: doctorCommand,
48830
49088
  dev: devCommand,
48831
49089
  mcp: mcpCommand,
49090
+ update: updateCommand,
48832
49091
  "migrate-from-vibora": migrateFromViboraCommand
48833
49092
  }
48834
49093
  });