@skein-code/cli 0.3.1 → 0.3.2

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 CHANGED
@@ -85,6 +85,10 @@ To install the published package from npm:
85
85
  npm install -g @skein-code/cli
86
86
  ```
87
87
 
88
+ Once installed, upgrade in place with `skein update` (it detects your package
89
+ manager and runs the matching global install). Add `--check` to only report a
90
+ newer version, or `--yes` to skip the confirmation prompt.
91
+
88
92
  `skein` is the primary command. Existing installations can continue using
89
93
  `mosaic` or `mosaic-code`; the `.mosaic/` project state and `MOSAIC_*`
90
94
  environment variables remain compatible with this release.
@@ -224,6 +228,7 @@ skein --print [prompt] headless agent run
224
228
  skein init project setup
225
229
  skein doctor prerequisite and fallback checks
226
230
  skein doctor --visual terminal rendering and input calibration
231
+ skein update upgrade to the latest release
227
232
  skein config show resolved, redacted configuration
228
233
  skein index build/update the selected index
229
234
  skein search <query> ranked grounded spans
package/dist/cli.js CHANGED
@@ -220,7 +220,7 @@ function isSqliteBusy(error) {
220
220
  // package.json
221
221
  var package_default = {
222
222
  name: "@skein-code/cli",
223
- version: "0.3.1",
223
+ version: "0.3.2",
224
224
  description: "A context-first, model-agnostic coding agent with an auditable terminal workspace.",
225
225
  type: "module",
226
226
  license: "MIT",
@@ -236,8 +236,8 @@ var package_default = {
236
236
  },
237
237
  skein: {
238
238
  releaseNotes: [
239
- "Fix ghost characters left in /theme list and other list panels",
240
- "List rows now pad to a stable width so incremental repaints overwrite stale cells"
239
+ "New: skein update self-upgrades to the latest release",
240
+ "Detects npm/pnpm/yarn/bun; --check reports only, --yes skips the prompt"
241
241
  ]
242
242
  },
243
243
  bin: {
@@ -17151,6 +17151,61 @@ function escapeXml2(value) {
17151
17151
  })[character] ?? character);
17152
17152
  }
17153
17153
 
17154
+ // src/utils/self-update.ts
17155
+ import { spawn as spawn2 } from "node:child_process";
17156
+ import { realpathSync } from "node:fs";
17157
+ var GLOBAL_INSTALL_ARGS = {
17158
+ npm: (spec) => ["install", "-g", spec],
17159
+ pnpm: (spec) => ["add", "-g", spec],
17160
+ yarn: (spec) => ["global", "add", spec],
17161
+ bun: (spec) => ["add", "-g", spec]
17162
+ };
17163
+ function detectPackageManager(binaryPath = process.argv[1], env = process.env) {
17164
+ const fromAgent = parseUserAgent(env.npm_config_user_agent);
17165
+ if (fromAgent) return fromAgent;
17166
+ let resolved = binaryPath ?? "";
17167
+ try {
17168
+ if (resolved) resolved = realpathSync(resolved);
17169
+ } catch {
17170
+ }
17171
+ const path = resolved.replace(/\\/g, "/").toLowerCase();
17172
+ if (/(^|\/)\.bun\/|(^|\/)bun\//.test(path)) return "bun";
17173
+ if (/(^|\/)pnpm(\/|-global\/|$)/.test(path)) return "pnpm";
17174
+ if (/(^|\/)yarn\//.test(path)) return "yarn";
17175
+ return "npm";
17176
+ }
17177
+ function parseUserAgent(agent) {
17178
+ if (!agent) return null;
17179
+ const name = agent.trim().split("/")[0]?.toLowerCase();
17180
+ if (name === "pnpm" || name === "yarn" || name === "bun" || name === "npm") return name;
17181
+ return null;
17182
+ }
17183
+ function resolveUpgradePlan(options = {}) {
17184
+ const env = options.env ?? process.env;
17185
+ const spec = `${PACKAGE_NAME}@${options.version ?? "latest"}`;
17186
+ const manager = detectPackageManager(options.binaryPath, env);
17187
+ const args = GLOBAL_INSTALL_ARGS[manager](spec);
17188
+ return { command: manager, args, manager, display: `${manager} ${args.join(" ")}` };
17189
+ }
17190
+ function upgradeCommandOverride(env = process.env) {
17191
+ const raw = env.SKEIN_UPDATE_COMMAND ?? env.MOSAIC_UPDATE_COMMAND;
17192
+ const trimmed = raw?.trim();
17193
+ return trimmed ? trimmed : void 0;
17194
+ }
17195
+ function runUpgrade(plan) {
17196
+ return new Promise((resolve24) => {
17197
+ const child = spawn2(plan.command, plan.args, {
17198
+ stdio: "inherit",
17199
+ shell: plan.shell ?? false
17200
+ });
17201
+ child.on("error", () => resolve24({ ok: false, exitCode: 127, display: plan.display }));
17202
+ child.on("close", (code) => {
17203
+ const exitCode = code ?? 1;
17204
+ resolve24({ ok: exitCode === 0, exitCode, display: plan.display });
17205
+ });
17206
+ });
17207
+ }
17208
+
17154
17209
  // src/cli.tsx
17155
17210
  var cliGlyphs = resolveCliGlyphs();
17156
17211
  var defaultWarningListeners = process.listeners("warning");
@@ -17254,6 +17309,63 @@ program.command("doctor").description("Diagnose prerequisites and safe fallbacks
17254
17309
  const ok = await runDoctor(config, { json: options.json === true, visual: options.visual === true });
17255
17310
  if (!ok) process.exitCode = 1;
17256
17311
  });
17312
+ program.command("update").description("Update to the latest published release").option("--check", "only report whether a newer version is available").option("--yes", "skip the confirmation prompt and upgrade immediately").option("--json", "print the result as JSON").action(async (options) => {
17313
+ const json = options.json === true;
17314
+ const current = package_default.version;
17315
+ const notice = await refreshUpdateCache(current, { force: true }).catch(() => void 0);
17316
+ if (!notice) {
17317
+ if (json) printObject({ current, latest: current, upToDate: true }, true);
17318
+ else process.stdout.write(`${chalk4.green(cliGlyphs.success)} Already on the latest release (v${current}).
17319
+ `);
17320
+ return;
17321
+ }
17322
+ const override = upgradeCommandOverride();
17323
+ const plan = override ? { command: override, args: [], manager: "custom", display: override, shell: true } : { ...resolveUpgradePlan({ version: notice.latest }), shell: false };
17324
+ if (json && options.check === true) {
17325
+ printObject({ current, latest: notice.latest, upToDate: false, command: plan.display, ...notice.highlights ? { highlights: notice.highlights } : {} }, true);
17326
+ return;
17327
+ }
17328
+ process.stdout.write(`${chalk4.cyan(cliGlyphs.brand)} Update available ${chalk4.dim(`v${current}`)} ${cliGlyphs.separator} ${chalk4.green(`v${notice.latest}`)}
17329
+ `);
17330
+ for (const highlight of notice.highlights ?? []) {
17331
+ process.stdout.write(` ${cliGlyphs.separator} ${highlight}
17332
+ `);
17333
+ }
17334
+ if (options.check === true) {
17335
+ process.stdout.write(` Run ${chalk4.cyan(`${PRODUCT_COMMAND} update`)} to install ${chalk4.dim(`(${plan.display})`)}.
17336
+ `);
17337
+ return;
17338
+ }
17339
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
17340
+ if (options.yes !== true) {
17341
+ if (!interactive) {
17342
+ process.stdout.write(` Re-run with ${chalk4.cyan(`${PRODUCT_COMMAND} update --yes`)} to install ${chalk4.dim(`(${plan.display})`)}.
17343
+ `);
17344
+ return;
17345
+ }
17346
+ const rl = createInterface2({ input, output });
17347
+ try {
17348
+ const answer = (await rl.question(` Install ${chalk4.dim(plan.display)}? [Y/n] `)).trim().toLowerCase();
17349
+ if (answer === "n" || answer === "no") {
17350
+ process.stdout.write(" Update cancelled.\n");
17351
+ return;
17352
+ }
17353
+ } finally {
17354
+ rl.close();
17355
+ }
17356
+ }
17357
+ process.stdout.write(`${chalk4.dim(`${cliGlyphs.running} ${plan.display}`)}
17358
+ `);
17359
+ const result = await runUpgrade(plan);
17360
+ if (result.ok) {
17361
+ process.stdout.write(`${chalk4.green(cliGlyphs.success)} Updated to v${notice.latest}. Restart ${PRODUCT_COMMAND} to use it.
17362
+ `);
17363
+ } else {
17364
+ process.stdout.write(`${chalk4.red(cliGlyphs.error)} Update failed (exit ${result.exitCode}). Run ${chalk4.cyan(plan.display)} manually.
17365
+ `);
17366
+ process.exitCode = 1;
17367
+ }
17368
+ });
17257
17369
  program.command("migrate").description("Inspect or migrate legacy .mosaic state into .skein").option("-w, --workspace <path>", "workspace root").option("--json", "print a migration manifest as JSON").option("--yes", "perform the migration after conflict checks").option("--rollback", "verify and roll back a completed migration").option("--recover", "inspect or recover interrupted migration/rollback state").option("--home", "operate on the user-level Skein/Mosaic namespace").action(async (options) => {
17258
17370
  if (options.home && options.workspace) throw new Error("--workspace cannot be combined with --home.");
17259
17371
  if (options.recover && options.rollback) throw new Error("--recover and --rollback cannot be combined.");