@skein-code/cli 0.2.1 → 0.2.3
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 +247 -19
- package/dist/cli.js.map +1 -1
- package/docs/NEXT_STEPS.md +4 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/cli.tsx
|
|
4
4
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
5
5
|
import { stdin as input, stdout as output } from "node:process";
|
|
6
|
-
import { writeFile as
|
|
6
|
+
import { writeFile as writeFile3 } from "node:fs/promises";
|
|
7
7
|
import { basename as basename12, resolve as resolve22 } from "node:path";
|
|
8
8
|
import { Command, Option } from "commander";
|
|
9
9
|
import chalk3 from "chalk";
|
|
@@ -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.
|
|
223
|
+
version: "0.2.3",
|
|
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
|
|
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
|
|
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
|
|
@@ -9752,10 +9763,11 @@ async function runDoctor(config, options = {}) {
|
|
|
9752
9763
|
let legacyCompatibility;
|
|
9753
9764
|
try {
|
|
9754
9765
|
namespace = await inspectProjectNamespace(root);
|
|
9766
|
+
const activeNamespace = resolveProjectNamespaceSync(root);
|
|
9755
9767
|
checks.push({
|
|
9756
9768
|
name: "Storage namespace",
|
|
9757
9769
|
ok: namespace.status !== "conflict",
|
|
9758
|
-
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 ${
|
|
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}`,
|
|
9759
9771
|
required: false
|
|
9760
9772
|
});
|
|
9761
9773
|
} catch (error) {
|
|
@@ -11123,6 +11135,9 @@ function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = fals
|
|
|
11123
11135
|
if (item.kind === "banner") {
|
|
11124
11136
|
return /* @__PURE__ */ jsx(Banner, { model: item.model, engine: item.engine, workspace: item.workspace, version: item.version, width, glyphs }, item.id);
|
|
11125
11137
|
}
|
|
11138
|
+
if (item.kind === "update") {
|
|
11139
|
+
return /* @__PURE__ */ jsx(UpdateNotice, { current: item.current, latest: item.latest, command: item.command, width, glyphs }, item.id);
|
|
11140
|
+
}
|
|
11126
11141
|
const color = item.tone === "error" ? theme.error : item.tone === "success" ? theme.success : theme.muted;
|
|
11127
11142
|
const noticeGlyph = item.tone === "error" ? glyphs.error : item.tone === "success" ? glyphs.success : glyphs.info;
|
|
11128
11143
|
return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { color, wrap: "wrap", children: `${noticeGlyph} ${sanitizeTerminalText(item.text)}` }) }, item.id);
|
|
@@ -11696,6 +11711,20 @@ function Banner({ model, engine, workspace, version, width, glyphs }) {
|
|
|
11696
11711
|
/* @__PURE__ */ jsx(Text, { color: theme.dim, children: truncateDisplay(`type a request ${glyphs.separator} /help for commands ${glyphs.separator} @ to attach files`, innerWidth) })
|
|
11697
11712
|
] });
|
|
11698
11713
|
}
|
|
11714
|
+
function UpdateNotice({ current, latest, command: command2, width, glyphs }) {
|
|
11715
|
+
const theme = useTheme();
|
|
11716
|
+
const parts = [
|
|
11717
|
+
{ text: glyphs.up, color: theme.accent, bold: true },
|
|
11718
|
+
{ text: " a new version is available ", color: theme.text, bold: false },
|
|
11719
|
+
{ text: `v${current}`, color: theme.dim, bold: false },
|
|
11720
|
+
{ text: ` ${glyphs.arrow} `, color: theme.muted, bold: false },
|
|
11721
|
+
{ text: `v${latest}`, color: theme.success, bold: true },
|
|
11722
|
+
{ text: ` ${command2}`, color: theme.dim, bold: false }
|
|
11723
|
+
];
|
|
11724
|
+
const rendered = truncateDisplay(parts.map((part) => part.text).join(""), safeWidth(width));
|
|
11725
|
+
const truncated = rendered.length < parts.map((part) => part.text).join("").length;
|
|
11726
|
+
return /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: truncated ? /* @__PURE__ */ jsx(Text, { color: theme.muted, children: rendered }) : parts.map((part, index) => /* @__PURE__ */ jsx(Text, { color: part.color, bold: part.bold, children: part.text }, index)) });
|
|
11727
|
+
}
|
|
11699
11728
|
function RichText({ value, glyphs }) {
|
|
11700
11729
|
const theme = useTheme();
|
|
11701
11730
|
let inCode = false;
|
|
@@ -11788,6 +11817,141 @@ function safeWidth(width) {
|
|
|
11788
11817
|
return Math.max(1, Math.floor(Number.isFinite(width) ? width : 80));
|
|
11789
11818
|
}
|
|
11790
11819
|
|
|
11820
|
+
// src/utils/update-check.ts
|
|
11821
|
+
import { mkdir as mkdir9, readFile as readFile15, writeFile as writeFile2 } from "node:fs/promises";
|
|
11822
|
+
import { join as join16 } from "node:path";
|
|
11823
|
+
var PACKAGE_NAME = "@skein-code/cli";
|
|
11824
|
+
var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
11825
|
+
var CACHE_FILE = "update-check.json";
|
|
11826
|
+
var CHECK_INTERVAL_MS = 1e3 * 60 * 60 * 24;
|
|
11827
|
+
var FETCH_TIMEOUT_MS = 3e3;
|
|
11828
|
+
var MAX_BODY_BYTES = 1e6;
|
|
11829
|
+
function truthyEnv(value) {
|
|
11830
|
+
return value !== void 0 && value !== "" && value !== "false" && value !== "0";
|
|
11831
|
+
}
|
|
11832
|
+
function isCi(env) {
|
|
11833
|
+
return truthyEnv(env.CI) || truthyEnv(env.CONTINUOUS_INTEGRATION) || truthyEnv(env.GITHUB_ACTIONS) || truthyEnv(env.GITLAB_CI) || truthyEnv(env.BUILD_NUMBER);
|
|
11834
|
+
}
|
|
11835
|
+
function isUpdateCheckDisabled(env = process.env) {
|
|
11836
|
+
if (truthyEnv(env.SKEIN_NO_UPDATE_CHECK) || truthyEnv(env.MOSAIC_NO_UPDATE_CHECK) || truthyEnv(env.NO_UPDATE_NOTIFIER)) {
|
|
11837
|
+
return true;
|
|
11838
|
+
}
|
|
11839
|
+
if (env.NODE_ENV === "test") return true;
|
|
11840
|
+
return isCi(env);
|
|
11841
|
+
}
|
|
11842
|
+
function parseVersion(value) {
|
|
11843
|
+
const cleaned = value.trim().replace(/^v/iu, "");
|
|
11844
|
+
const buildAt = cleaned.indexOf("+");
|
|
11845
|
+
const noBuild = buildAt === -1 ? cleaned : cleaned.slice(0, buildAt);
|
|
11846
|
+
const dashAt = noBuild.indexOf("-");
|
|
11847
|
+
const mainStr = dashAt === -1 ? noBuild : noBuild.slice(0, dashAt);
|
|
11848
|
+
const preStr = dashAt === -1 ? "" : noBuild.slice(dashAt + 1);
|
|
11849
|
+
const parts = mainStr.split(".");
|
|
11850
|
+
if (parts.length !== 3) return null;
|
|
11851
|
+
const [major, minor, patch] = parts.map((part) => Number(part));
|
|
11852
|
+
if ([major, minor, patch].some((n) => n === void 0 || !Number.isInteger(n) || n < 0)) return null;
|
|
11853
|
+
return { main: [major, minor, patch], pre: preStr ? preStr.split(".") : [] };
|
|
11854
|
+
}
|
|
11855
|
+
function compareVersions(a, b) {
|
|
11856
|
+
const pa = parseVersion(a);
|
|
11857
|
+
const pb = parseVersion(b);
|
|
11858
|
+
if (!pa || !pb) return a === b ? 0 : a < b ? -1 : 1;
|
|
11859
|
+
for (let i = 0; i < 3; i++) {
|
|
11860
|
+
const x = pa.main[i];
|
|
11861
|
+
const y = pb.main[i];
|
|
11862
|
+
if (x !== y) return x < y ? -1 : 1;
|
|
11863
|
+
}
|
|
11864
|
+
if (pa.pre.length === 0 && pb.pre.length === 0) return 0;
|
|
11865
|
+
if (pa.pre.length === 0) return 1;
|
|
11866
|
+
if (pb.pre.length === 0) return -1;
|
|
11867
|
+
const len = Math.max(pa.pre.length, pb.pre.length);
|
|
11868
|
+
for (let i = 0; i < len; i++) {
|
|
11869
|
+
const x = pa.pre[i];
|
|
11870
|
+
const y = pb.pre[i];
|
|
11871
|
+
if (x === void 0) return -1;
|
|
11872
|
+
if (y === void 0) return 1;
|
|
11873
|
+
const xNum = /^\d+$/u.test(x);
|
|
11874
|
+
const yNum = /^\d+$/u.test(y);
|
|
11875
|
+
if (xNum && yNum) {
|
|
11876
|
+
const nx = Number(x);
|
|
11877
|
+
const ny = Number(y);
|
|
11878
|
+
if (nx !== ny) return nx < ny ? -1 : 1;
|
|
11879
|
+
} else if (xNum !== yNum) {
|
|
11880
|
+
return xNum ? -1 : 1;
|
|
11881
|
+
} else if (x !== y) {
|
|
11882
|
+
return x < y ? -1 : 1;
|
|
11883
|
+
}
|
|
11884
|
+
}
|
|
11885
|
+
return 0;
|
|
11886
|
+
}
|
|
11887
|
+
function updateCachePath(env = process.env) {
|
|
11888
|
+
return join16(resolveHomeNamespace(env), CACHE_FILE);
|
|
11889
|
+
}
|
|
11890
|
+
function upgradeCommand() {
|
|
11891
|
+
return `npm i -g ${PACKAGE_NAME}`;
|
|
11892
|
+
}
|
|
11893
|
+
function updateNoticeText(notice) {
|
|
11894
|
+
return `Update available ${notice.current} \u2192 ${notice.latest} \xB7 run ${notice.command}`;
|
|
11895
|
+
}
|
|
11896
|
+
function noticeIfNewer(latest, current) {
|
|
11897
|
+
if (latest && compareVersions(latest, current) > 0) {
|
|
11898
|
+
return { current, latest, command: upgradeCommand() };
|
|
11899
|
+
}
|
|
11900
|
+
return void 0;
|
|
11901
|
+
}
|
|
11902
|
+
async function readUpdateCache(env = process.env) {
|
|
11903
|
+
try {
|
|
11904
|
+
const raw = await readFile15(updateCachePath(env), "utf8");
|
|
11905
|
+
const parsed = JSON.parse(raw);
|
|
11906
|
+
if (typeof parsed?.checkedAt === "number" && (typeof parsed.latest === "string" || parsed.latest === null)) {
|
|
11907
|
+
return { checkedAt: parsed.checkedAt, latest: parsed.latest };
|
|
11908
|
+
}
|
|
11909
|
+
} catch {
|
|
11910
|
+
}
|
|
11911
|
+
return null;
|
|
11912
|
+
}
|
|
11913
|
+
async function writeUpdateCache(cache, env) {
|
|
11914
|
+
try {
|
|
11915
|
+
const dir = resolveHomeNamespace(env);
|
|
11916
|
+
await mkdir9(dir, { recursive: true, mode: 448 });
|
|
11917
|
+
await writeFile2(updateCachePath(env), JSON.stringify(cache), "utf8");
|
|
11918
|
+
} catch {
|
|
11919
|
+
}
|
|
11920
|
+
}
|
|
11921
|
+
async function fetchLatestVersion(fetchImpl, url = REGISTRY_URL) {
|
|
11922
|
+
try {
|
|
11923
|
+
const response = await fetchImpl(url, {
|
|
11924
|
+
headers: { accept: "application/json" },
|
|
11925
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
11926
|
+
});
|
|
11927
|
+
if (!response.ok) return null;
|
|
11928
|
+
const body = await response.text();
|
|
11929
|
+
if (body.length > MAX_BODY_BYTES) return null;
|
|
11930
|
+
const parsed = JSON.parse(body);
|
|
11931
|
+
return typeof parsed.version === "string" ? parsed.version : null;
|
|
11932
|
+
} catch {
|
|
11933
|
+
return null;
|
|
11934
|
+
}
|
|
11935
|
+
}
|
|
11936
|
+
async function resolveCachedUpdateNotice(currentVersion, env = process.env) {
|
|
11937
|
+
if (isUpdateCheckDisabled(env)) return void 0;
|
|
11938
|
+
const cache = await readUpdateCache(env);
|
|
11939
|
+
return noticeIfNewer(cache?.latest ?? null, currentVersion);
|
|
11940
|
+
}
|
|
11941
|
+
async function refreshUpdateCache(currentVersion, options = {}) {
|
|
11942
|
+
const env = options.env ?? process.env;
|
|
11943
|
+
if (isUpdateCheckDisabled(env)) return void 0;
|
|
11944
|
+
const cache = await readUpdateCache(env);
|
|
11945
|
+
const now = Date.now();
|
|
11946
|
+
if (!options.force && cache && now - cache.checkedAt < CHECK_INTERVAL_MS) {
|
|
11947
|
+
return noticeIfNewer(cache.latest, currentVersion);
|
|
11948
|
+
}
|
|
11949
|
+
const latest = await fetchLatestVersion(options.fetchImpl ?? fetch);
|
|
11950
|
+
const effectiveLatest = latest ?? cache?.latest ?? null;
|
|
11951
|
+
await writeUpdateCache({ checkedAt: now, latest: effectiveLatest }, env);
|
|
11952
|
+
return noticeIfNewer(effectiveLatest, currentVersion);
|
|
11953
|
+
}
|
|
11954
|
+
|
|
11791
11955
|
// src/ui/composer.tsx
|
|
11792
11956
|
import { useEffect, useRef, useState } from "react";
|
|
11793
11957
|
import { Text as Text2, useInput, usePaste } from "ink";
|
|
@@ -12710,6 +12874,29 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
12710
12874
|
useEffect2(() => {
|
|
12711
12875
|
setSuggestionIndex(0);
|
|
12712
12876
|
}, [input2]);
|
|
12877
|
+
useEffect2(() => {
|
|
12878
|
+
let cancelled = false;
|
|
12879
|
+
const showNotice = (notice) => {
|
|
12880
|
+
if (cancelled || !notice) return;
|
|
12881
|
+
setTimeline((items) => {
|
|
12882
|
+
const bannerIndex = items.findIndex((item) => item.kind === "banner");
|
|
12883
|
+
if (bannerIndex === -1) return items;
|
|
12884
|
+
const existing = items.find((item) => item.kind === "update");
|
|
12885
|
+
if (existing) {
|
|
12886
|
+
if (existing.kind === "update" && existing.latest === notice.latest) return items;
|
|
12887
|
+
return items.map((item) => item === existing ? { id: item.id, kind: "update", current: notice.current, latest: notice.latest, command: notice.command } : item);
|
|
12888
|
+
}
|
|
12889
|
+
const next = items.slice();
|
|
12890
|
+
next.splice(bannerIndex + 1, 0, { id: nextId(), kind: "update", current: notice.current, latest: notice.latest, command: notice.command });
|
|
12891
|
+
return next;
|
|
12892
|
+
});
|
|
12893
|
+
};
|
|
12894
|
+
void resolveCachedUpdateNotice(package_default.version).then(showNotice);
|
|
12895
|
+
void refreshUpdateCache(package_default.version).then(showNotice);
|
|
12896
|
+
return () => {
|
|
12897
|
+
cancelled = true;
|
|
12898
|
+
};
|
|
12899
|
+
}, []);
|
|
12713
12900
|
useEffect2(() => {
|
|
12714
12901
|
if (suggestionMode !== "mention" || !mentionToken) {
|
|
12715
12902
|
mentionRequest.current += 1;
|
|
@@ -14901,9 +15088,9 @@ function scopeKey(scope, context) {
|
|
|
14901
15088
|
}
|
|
14902
15089
|
|
|
14903
15090
|
// src/skills/catalog.ts
|
|
14904
|
-
import { lstat as lstat19, readFile as
|
|
15091
|
+
import { lstat as lstat19, readFile as readFile16, readdir as readdir7, realpath as realpath8 } from "node:fs/promises";
|
|
14905
15092
|
import { homedir as homedir4 } from "node:os";
|
|
14906
|
-
import { basename as basename11, join as
|
|
15093
|
+
import { basename as basename11, join as join17, resolve as resolve20 } from "node:path";
|
|
14907
15094
|
import { parse as parseYaml3 } from "yaml";
|
|
14908
15095
|
var SkillCatalog = class {
|
|
14909
15096
|
constructor(workspace, config) {
|
|
@@ -14923,7 +15110,7 @@ var SkillCatalog = class {
|
|
|
14923
15110
|
for (const location of locations) {
|
|
14924
15111
|
const entries = await safeDirectories(location.path);
|
|
14925
15112
|
for (const entry of entries) {
|
|
14926
|
-
const skillPath =
|
|
15113
|
+
const skillPath = join17(location.path, entry, "SKILL.md");
|
|
14927
15114
|
const metadata = await readMetadata(skillPath);
|
|
14928
15115
|
if (!metadata) continue;
|
|
14929
15116
|
const descriptor = {
|
|
@@ -14974,10 +15161,10 @@ function discoveryLocations(workspace, configured) {
|
|
|
14974
15161
|
const home = homedir4();
|
|
14975
15162
|
const workspaceRoot = resolve20(workspace);
|
|
14976
15163
|
return [
|
|
14977
|
-
{ path:
|
|
14978
|
-
{ path:
|
|
14979
|
-
{ path:
|
|
14980
|
-
{ path:
|
|
15164
|
+
{ path: join17(home, ".agents", "skills"), scope: "user", trusted: true },
|
|
15165
|
+
{ path: join17(home, ".claude", "skills"), scope: "user", trusted: true },
|
|
15166
|
+
{ path: join17(home, ".augment", "skills"), scope: "user", trusted: true },
|
|
15167
|
+
{ path: join17(resolveHomeNamespace(), "skills"), scope: "user", trusted: true },
|
|
14981
15168
|
...configured.map((path) => {
|
|
14982
15169
|
const resolved = resolve20(workspaceRoot, path);
|
|
14983
15170
|
return {
|
|
@@ -14986,10 +15173,10 @@ function discoveryLocations(workspace, configured) {
|
|
|
14986
15173
|
trusted: !isInside(workspaceRoot, resolved)
|
|
14987
15174
|
};
|
|
14988
15175
|
}),
|
|
14989
|
-
{ path:
|
|
14990
|
-
{ path:
|
|
14991
|
-
{ path:
|
|
14992
|
-
{ path:
|
|
15176
|
+
{ path: join17(workspace, ".agents", "skills"), scope: "workspace", trusted: false },
|
|
15177
|
+
{ path: join17(workspace, ".claude", "skills"), scope: "workspace", trusted: false },
|
|
15178
|
+
{ path: join17(workspace, ".augment", "skills"), scope: "workspace", trusted: false },
|
|
15179
|
+
{ path: join17(resolveProjectNamespaceSync(workspaceRoot).active, "skills"), scope: "workspace", trusted: false }
|
|
14993
15180
|
];
|
|
14994
15181
|
}
|
|
14995
15182
|
async function safeDirectories(path) {
|
|
@@ -15033,7 +15220,7 @@ async function safeRead(path, maxBytes) {
|
|
|
15033
15220
|
const resolvedParent = await realpath8(parent);
|
|
15034
15221
|
const resolvedPath = await realpath8(path);
|
|
15035
15222
|
if (!resolvedPath.startsWith(`${resolvedParent}/`)) return void 0;
|
|
15036
|
-
return await
|
|
15223
|
+
return await readFile16(path, "utf8");
|
|
15037
15224
|
} catch {
|
|
15038
15225
|
return void 0;
|
|
15039
15226
|
}
|
|
@@ -15462,7 +15649,14 @@ program.command("status").description("Show model, context, workspace, and index
|
|
|
15462
15649
|
const config = await runtimeConfig(workspaceOption(options.workspace), runtimeOptions(options));
|
|
15463
15650
|
const engine = new ContextEngine(config);
|
|
15464
15651
|
const status = await engine.status();
|
|
15465
|
-
|
|
15652
|
+
const namespace = resolveProjectNamespaceSync(config.workspaceRoots[0] ?? process.cwd());
|
|
15653
|
+
const update = await refreshUpdateCache(package_default.version).catch(() => void 0);
|
|
15654
|
+
if (options.json === true) {
|
|
15655
|
+
const updateJson = update ? { current: update.current, latest: update.latest, command: update.command } : { current: package_default.version, latest: null, command: upgradeCommand() };
|
|
15656
|
+
printObject({ config: configSummary(config), context: status, namespace, update: updateJson }, true);
|
|
15657
|
+
} else {
|
|
15658
|
+
printStatusSummary(config, status, namespace, update);
|
|
15659
|
+
}
|
|
15466
15660
|
});
|
|
15467
15661
|
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) => {
|
|
15468
15662
|
const config = await runtimeConfig(workspaceOption(options.workspace), runtimeOptions(options));
|
|
@@ -15564,7 +15758,7 @@ sessionCommand.command("export <id>").description("Export a session as Markdown"
|
|
|
15564
15758
|
const store = new SessionStore(workspaceOption(options.workspace));
|
|
15565
15759
|
const session = await requireSessionSelector(store, id);
|
|
15566
15760
|
const markdown = sessionMarkdown(session);
|
|
15567
|
-
if (options.output) await
|
|
15761
|
+
if (options.output) await writeFile3(resolve22(options.output), markdown, "utf8");
|
|
15568
15762
|
else process.stdout.write(markdown);
|
|
15569
15763
|
});
|
|
15570
15764
|
var checkpointCommand = program.command("checkpoint").description("Inspect and restore pre-mutation snapshots");
|
|
@@ -16202,6 +16396,40 @@ function printObject(value, json) {
|
|
|
16202
16396
|
else process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
16203
16397
|
`);
|
|
16204
16398
|
}
|
|
16399
|
+
function printStatusSummary(config, context, namespace, update) {
|
|
16400
|
+
const glyphs = cliGlyphs;
|
|
16401
|
+
const dim = (text) => chalk3.dim(text);
|
|
16402
|
+
const line = (level, name, detail) => {
|
|
16403
|
+
const icon = level === "ok" ? chalk3.green(glyphs.success) : level === "error" ? chalk3.red(glyphs.error) : chalk3.yellow("!");
|
|
16404
|
+
process.stdout.write(`${icon} ${name.padEnd(16)} ${dim(detail)}
|
|
16405
|
+
`);
|
|
16406
|
+
};
|
|
16407
|
+
const keyReady = Boolean(config.model.apiKey) || config.model.provider === "compatible";
|
|
16408
|
+
const endpoint = redactEndpoint(config.model.baseUrl);
|
|
16409
|
+
const local = context.local ?? {};
|
|
16410
|
+
const selected = String(context.selected ?? "local");
|
|
16411
|
+
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";
|
|
16412
|
+
const indexFiles = local.files ?? 0;
|
|
16413
|
+
const indexReady = Boolean(local.available) && indexFiles > 0;
|
|
16414
|
+
const indexDetail = indexReady ? `${indexFiles} files ${glyphs.separator} ${local.chunks ?? 0} chunks` : `not built ${glyphs.separator} run ${PRODUCT_COMMAND} index`;
|
|
16415
|
+
process.stdout.write(`${chalk3.hex("#A78BFA").bold(`${glyphs.brand} ${PRODUCT_NAME.toUpperCase()} STATUS`)}
|
|
16416
|
+
|
|
16417
|
+
`);
|
|
16418
|
+
line("ok", "Model", `${config.model.provider}/${config.model.model}`);
|
|
16419
|
+
line("ok", "Endpoint", endpoint);
|
|
16420
|
+
line(keyReady ? "ok" : "warn", "API key", keyReady ? "configured" : `missing ${glyphs.separator} set it, then run ${PRODUCT_COMMAND} doctor to verify`);
|
|
16421
|
+
line(selected === "unavailable" ? "error" : "ok", "Context engine", engineDetail);
|
|
16422
|
+
line(indexReady ? "ok" : "warn", "Code index", indexDetail);
|
|
16423
|
+
line("ok", "Workspace", config.workspaceRoots.join(` ${glyphs.separator} `));
|
|
16424
|
+
const namespaceName = namespace.activeKind === "canonical" ? ".skein" : ".mosaic";
|
|
16425
|
+
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)`;
|
|
16426
|
+
const storageReady = namespace.activeKind === "canonical" || namespace.phase === "active";
|
|
16427
|
+
line(storageReady ? "ok" : "warn", "Storage", storageDetail);
|
|
16428
|
+
line(update ? "warn" : "ok", "Version", update ? updateNoticeText(update) : `v${package_default.version} (up to date)`);
|
|
16429
|
+
process.stdout.write(`
|
|
16430
|
+
${dim(`Run ${PRODUCT_COMMAND} status --json for the full machine-readable record.`)}
|
|
16431
|
+
`);
|
|
16432
|
+
}
|
|
16205
16433
|
function printSessionList(sessions) {
|
|
16206
16434
|
if (!sessions.length) {
|
|
16207
16435
|
process.stdout.write("No saved sessions.\n");
|