@skein-code/cli 0.2.0 → 0.2.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/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.2.0",
223
+ version: "0.2.2",
224
224
  description: "A context-first, model-agnostic coding agent with an auditable terminal workspace.",
225
225
  type: "module",
226
226
  license: "MIT",
@@ -383,13 +383,15 @@ async function resolveProjectNamespace(workspace) {
383
383
  const { workspace: root, canonical, legacy } = projectNamespacePaths(workspace);
384
384
  await assertNamespacePathsSeparated(legacy, canonical);
385
385
  const [canonicalExists, legacyExists] = await Promise.all([isDirectory(canonical), isDirectory(legacy)]);
386
- const activeKind = canonicalExists ? "canonical" : legacyExists ? "legacy" : "legacy";
386
+ const phase = legacyCompatibilityStatus().phase;
387
+ const activeKind = activeProjectNamespaceKind(canonicalExists, legacyExists, phase);
387
388
  return {
388
389
  workspace: root,
389
390
  canonical,
390
391
  legacy,
391
392
  active: activeKind === "canonical" ? canonical : legacy,
392
393
  activeKind,
394
+ phase,
393
395
  canonicalExists,
394
396
  legacyExists,
395
397
  conflict: canonicalExists && legacyExists
@@ -399,18 +401,25 @@ function resolveProjectNamespaceSync(workspace) {
399
401
  const { workspace: root, canonical, legacy } = projectNamespacePaths(workspace);
400
402
  const canonicalExists = isDirectorySync(canonical);
401
403
  const legacyExists = isDirectorySync(legacy);
402
- const activeKind = canonicalExists ? "canonical" : "legacy";
404
+ const phase = legacyCompatibilityStatus().phase;
405
+ const activeKind = activeProjectNamespaceKind(canonicalExists, legacyExists, phase);
403
406
  return {
404
407
  workspace: root,
405
408
  canonical,
406
409
  legacy,
407
410
  active: activeKind === "canonical" ? canonical : legacy,
408
411
  activeKind,
412
+ phase,
409
413
  canonicalExists,
410
414
  legacyExists,
411
415
  conflict: canonicalExists && legacyExists
412
416
  };
413
417
  }
418
+ function activeProjectNamespaceKind(canonicalExists, legacyExists, phase) {
419
+ if (canonicalExists) return "canonical";
420
+ if (legacyExists) return "legacy";
421
+ return phase === "active" ? "legacy" : "canonical";
422
+ }
414
423
  function resolveHomeNamespace(environment = process.env) {
415
424
  const paths = homeNamespacePaths(environment);
416
425
  if (environment.SKEIN_HOME?.trim()) return paths.canonical;
@@ -421,12 +430,14 @@ async function resolveHomeStorageNamespace(environment = process.env) {
421
430
  await assertNamespacePathsSeparated(legacy, canonical);
422
431
  const [canonicalExists, legacyExists] = await Promise.all([isDirectory(canonical), isDirectory(legacy)]);
423
432
  const activeKind = canonicalExists ? "canonical" : "legacy";
433
+ const phase = legacyCompatibilityStatus({ environment }).phase;
424
434
  return {
425
435
  workspace: root,
426
436
  canonical,
427
437
  legacy,
428
438
  active: activeKind === "canonical" ? canonical : legacy,
429
439
  activeKind,
440
+ phase,
430
441
  canonicalExists,
431
442
  legacyExists,
432
443
  conflict: canonicalExists && legacyExists
@@ -1966,6 +1977,9 @@ var envKeysForProvider = {
1966
1977
  gemini: ["GEMINI_API_KEY"],
1967
1978
  compatible: ["SKEIN_API_KEY", "MOSAIC_API_KEY"]
1968
1979
  };
1980
+ function providerApiKeyEnv(provider) {
1981
+ return envKeysForProvider[provider][0];
1982
+ }
1969
1983
  var defaultPermissions = {
1970
1984
  read: "allow",
1971
1985
  write: "ask",
@@ -9749,10 +9763,11 @@ async function runDoctor(config, options = {}) {
9749
9763
  let legacyCompatibility;
9750
9764
  try {
9751
9765
  namespace = await inspectProjectNamespace(root);
9766
+ const activeNamespace = resolveProjectNamespaceSync(root);
9752
9767
  checks.push({
9753
9768
  name: "Storage namespace",
9754
9769
  ok: namespace.status !== "conflict",
9755
- detail: namespace.status === "ready" ? `legacy .mosaic detected; migrate to ${namespace.destination}` : namespace.status === "conflict" ? `conflict in ${namespace.conflicts.length} path(s); migration paused` : !namespace.sourceExists && !namespace.destinationExists ? `no durable state yet; first write uses ${namespace.source}` : `canonical .skein namespace active at ${namespace.destination}`,
9770
+ detail: namespace.status === "ready" ? `legacy .mosaic detected; migrate to ${namespace.destination}` : namespace.status === "conflict" ? `conflict in ${namespace.conflicts.length} path(s); migration paused` : !namespace.sourceExists && !namespace.destinationExists ? `no durable state yet; first write uses ${activeNamespace.active}` : `canonical .skein namespace active at ${namespace.destination}`,
9756
9771
  required: false
9757
9772
  });
9758
9773
  } catch (error) {
@@ -10420,8 +10435,9 @@ function command(name, description, usage, aliases) {
10420
10435
  import stringWidth from "string-width";
10421
10436
  import stripAnsi2 from "strip-ansi";
10422
10437
  var controlCharacters = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]/gu;
10438
+ var escapeSequences = /[[\]][0-9;?=>!]*[ -/]*[@-~]|[[\]][?=>][0-9;?=>!]*[a-zA-Z~]/gu;
10423
10439
  function sanitizeTerminalText(value) {
10424
- return stripAnsi2(value).replace(/\r\n?/g, "\n").replace(controlCharacters, "");
10440
+ return stripAnsi2(value).replace(escapeSequences, "").replace(/\r\n?/g, "\n").replace(controlCharacters, "");
10425
10441
  }
10426
10442
  function terminalEllipsis() {
10427
10443
  return process.env.SKEIN_GLYPHS === "ascii" || process.env.MOSAIC_GLYPHS === "ascii" ? "..." : "\u2026";
@@ -11678,12 +11694,7 @@ function ThemePreview({ name, width, glyphs }) {
11678
11694
  function Banner({ model, engine, workspace, version, width, glyphs }) {
11679
11695
  const theme = useTheme();
11680
11696
  const innerWidth = Math.max(1, safeWidth(width) - 2);
11681
- const art = [
11682
- "\u2591\u2588\u2580\u2580\u2591\u2588\u2591\u2588\u2591\u2588\u2580\u2580\u2591\u2580\u2588\u2580\u2591\u2588\u2580\u2588",
11683
- "\u2591\u2580\u2580\u2588\u2591\u2588\u2580\u2584\u2591\u2588\u2580\u2580\u2591\u2591\u2588\u2591\u2591\u2588\u2591\u2588",
11684
- "\u2591\u2580\u2580\u2580\u2591\u2580\u2591\u2580\u2591\u2580\u2580\u2580\u2591\u2580\u2580\u2580\u2591\u2580\u2591\u2580"
11685
- ];
11686
- const wordmarkFits = safeWidth(width) >= 44 && glyphs.borderStyle !== "classic";
11697
+ const wordmark = `${glyphs.brand} ${PRODUCT_NAME.toUpperCase().split("").join(" ")}`;
11687
11698
  const meta = [
11688
11699
  `model ${sanitizeInlineTerminalText(model)}`,
11689
11700
  `engine ${sanitizeInlineTerminalText(engine)}`,
@@ -11691,8 +11702,8 @@ function Banner({ model, engine, workspace, version, width, glyphs }) {
11691
11702
  ].join(` ${glyphs.separator} `);
11692
11703
  const tagline = "a terminal coding agent you can see through";
11693
11704
  return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [
11694
- wordmarkFits ? /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: art.map((line, index) => /* @__PURE__ */ jsx(Text, { bold: true, color: theme.accent, children: line }, index)) }) : /* @__PURE__ */ jsx(Text, { bold: true, color: theme.accent, children: `${glyphs.brand} ${PRODUCT_NAME.toUpperCase()}` }),
11695
- /* @__PURE__ */ jsx(Text, { color: theme.muted, children: truncateDisplay(`${glyphs.brand} ${PRODUCT_NAME} v${version} ${glyphs.separator} ${tagline}`, innerWidth) }),
11705
+ /* @__PURE__ */ jsx(Text, { bold: true, color: theme.accent, children: truncateDisplay(wordmark, innerWidth) }),
11706
+ /* @__PURE__ */ jsx(Text, { color: theme.muted, children: truncateDisplay(`v${version} ${glyphs.separator} ${tagline}`, innerWidth) }),
11696
11707
  /* @__PURE__ */ jsx(Text, { color: theme.dim, children: truncateDisplay(meta, innerWidth) }),
11697
11708
  /* @__PURE__ */ jsx(Text, { color: theme.dim, children: truncateDisplay(`type a request ${glyphs.separator} /help for commands ${glyphs.separator} @ to attach files`, innerWidth) })
11698
11709
  ] });
@@ -12638,7 +12649,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
12638
12649
  const separator = ` ${glyphs.separator} `;
12639
12650
  const ellipsis = terminalEllipsis();
12640
12651
  const initialSession = runner.getSession();
12641
- const setupProblem = config.model.provider !== "compatible" && !config.model.apiKey ? `No ${config.model.provider} API key configured. Run ${PRODUCT_COMMAND} doctor for setup guidance.` : config.model.provider === "compatible" && !config.model.baseUrl ? "No compatible model endpoint configured. Set model.baseUrl or pass --base-url." : void 0;
12652
+ const setupProblem = config.model.provider !== "compatible" && !config.model.apiKey ? `No ${config.model.provider} API key found. Set it and restart: export ${providerApiKeyEnv(config.model.provider)}=<your-key>${separator}then run ${PRODUCT_COMMAND} again. Use ${PRODUCT_COMMAND} doctor to verify, or --model to switch provider.` : config.model.provider === "compatible" && !config.model.baseUrl ? `No model endpoint configured. Set one and restart: export SKEIN_BASE_URL=<endpoint>${separator}or pass --base-url <endpoint>. Run ${PRODUCT_COMMAND} doctor to verify.` : void 0;
12642
12653
  const colorEnabled = config.ui.color && !process.env.NO_COLOR;
12643
12654
  const [theme, setTheme] = useState2(() => resolveThemeWithColor(config.ui.theme, colorEnabled));
12644
12655
  const [themeCatalogRevision, setThemeCatalogRevision] = useState2(0);
@@ -15463,7 +15474,12 @@ program.command("status").description("Show model, context, workspace, and index
15463
15474
  const config = await runtimeConfig(workspaceOption(options.workspace), runtimeOptions(options));
15464
15475
  const engine = new ContextEngine(config);
15465
15476
  const status = await engine.status();
15466
- printObject({ config: configSummary(config), context: status }, options.json === true);
15477
+ const namespace = resolveProjectNamespaceSync(config.workspaceRoots[0] ?? process.cwd());
15478
+ if (options.json === true) {
15479
+ printObject({ config: configSummary(config), context: status, namespace }, true);
15480
+ } else {
15481
+ printStatusSummary(config, status, namespace);
15482
+ }
15467
15483
  });
15468
15484
  program.command("doctor").description("Diagnose prerequisites and safe fallbacks").option("-w, --workspace <path>", "workspace root").option("--config <path>", "explicit config file").option("--json", "print JSON").option("--visual", "inspect terminal rendering, glyphs, and keyboard support").action(async (options) => {
15469
15485
  const config = await runtimeConfig(workspaceOption(options.workspace), runtimeOptions(options));
@@ -16203,6 +16219,39 @@ function printObject(value, json) {
16203
16219
  else process.stdout.write(`${JSON.stringify(value, null, 2)}
16204
16220
  `);
16205
16221
  }
16222
+ function printStatusSummary(config, context, namespace) {
16223
+ const glyphs = cliGlyphs;
16224
+ const dim = (text) => chalk3.dim(text);
16225
+ const line = (level, name, detail) => {
16226
+ const icon = level === "ok" ? chalk3.green(glyphs.success) : level === "error" ? chalk3.red(glyphs.error) : chalk3.yellow("!");
16227
+ process.stdout.write(`${icon} ${name.padEnd(16)} ${dim(detail)}
16228
+ `);
16229
+ };
16230
+ const keyReady = Boolean(config.model.apiKey) || config.model.provider === "compatible";
16231
+ const endpoint = redactEndpoint(config.model.baseUrl);
16232
+ const local = context.local ?? {};
16233
+ const selected = String(context.selected ?? "local");
16234
+ const engineDetail = selected === "contextengine" ? "ContextEngine (external)" : selected === "unindexed" ? `ContextEngine available; index not built ${glyphs.separator} run ${PRODUCT_COMMAND} index` : selected === "unavailable" ? `ContextEngine required but unavailable ${glyphs.separator} run ${PRODUCT_COMMAND} doctor` : "local index";
16235
+ const indexFiles = local.files ?? 0;
16236
+ const indexReady = Boolean(local.available) && indexFiles > 0;
16237
+ const indexDetail = indexReady ? `${indexFiles} files ${glyphs.separator} ${local.chunks ?? 0} chunks` : `not built ${glyphs.separator} run ${PRODUCT_COMMAND} index`;
16238
+ process.stdout.write(`${chalk3.hex("#A78BFA").bold(`${glyphs.brand} ${PRODUCT_NAME.toUpperCase()} STATUS`)}
16239
+
16240
+ `);
16241
+ line("ok", "Model", `${config.model.provider}/${config.model.model}`);
16242
+ line("ok", "Endpoint", endpoint);
16243
+ line(keyReady ? "ok" : "warn", "API key", keyReady ? "configured" : `missing ${glyphs.separator} set it, then run ${PRODUCT_COMMAND} doctor to verify`);
16244
+ line(selected === "unavailable" ? "error" : "ok", "Context engine", engineDetail);
16245
+ line(indexReady ? "ok" : "warn", "Code index", indexDetail);
16246
+ line("ok", "Workspace", config.workspaceRoots.join(` ${glyphs.separator} `));
16247
+ const namespaceName = namespace.activeKind === "canonical" ? ".skein" : ".mosaic";
16248
+ const storageDetail = namespace.activeKind === "canonical" ? `${namespaceName} (canonical)` : namespace.phase === "active" ? `${namespaceName} (legacy; new projects switch to .skein from 0.3.0)` : `${namespaceName} (legacy; run ${PRODUCT_COMMAND} migrate --yes before removal)`;
16249
+ const storageReady = namespace.activeKind === "canonical" || namespace.phase === "active";
16250
+ line(storageReady ? "ok" : "warn", "Storage", storageDetail);
16251
+ process.stdout.write(`
16252
+ ${dim(`Run ${PRODUCT_COMMAND} status --json for the full machine-readable record.`)}
16253
+ `);
16254
+ }
16206
16255
  function printSessionList(sessions) {
16207
16256
  if (!sessions.length) {
16208
16257
  process.stdout.write("No saved sessions.\n");