gnosys 5.9.2 → 5.9.4
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 +311 -147
- package/dist/cli.js.map +1 -1
- package/dist/lib/chat/index.d.ts.map +1 -1
- package/dist/lib/chat/index.js +5 -0
- package/dist/lib/chat/index.js.map +1 -1
- package/dist/lib/cleanup.d.ts +52 -0
- package/dist/lib/cleanup.d.ts.map +1 -0
- package/dist/lib/cleanup.js +168 -0
- package/dist/lib/cleanup.js.map +1 -0
- package/dist/lib/config.d.ts +7 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +41 -2
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/dream.d.ts.map +1 -1
- package/dist/lib/dream.js +13 -2
- package/dist/lib/dream.js.map +1 -1
- package/dist/lib/paths.d.ts +8 -0
- package/dist/lib/paths.d.ts.map +1 -1
- package/dist/lib/paths.js +15 -0
- package/dist/lib/paths.js.map +1 -1
- package/dist/lib/remote.d.ts +11 -0
- package/dist/lib/remote.d.ts.map +1 -1
- package/dist/lib/remote.js +26 -2
- package/dist/lib/remote.js.map +1 -1
- package/dist/lib/remoteWizard.d.ts.map +1 -1
- package/dist/lib/remoteWizard.js +112 -52
- package/dist/lib/remoteWizard.js.map +1 -1
- package/dist/lib/setup/coldStart.d.ts +71 -0
- package/dist/lib/setup/coldStart.d.ts.map +1 -0
- package/dist/lib/setup/coldStart.js +122 -0
- package/dist/lib/setup/coldStart.js.map +1 -0
- package/dist/lib/setup/configSetRender.d.ts +26 -0
- package/dist/lib/setup/configSetRender.d.ts.map +1 -0
- package/dist/lib/setup/configSetRender.js +103 -0
- package/dist/lib/setup/configSetRender.js.map +1 -0
- package/dist/lib/setup/dreamRender.d.ts +44 -0
- package/dist/lib/setup/dreamRender.d.ts.map +1 -0
- package/dist/lib/setup/dreamRender.js +55 -0
- package/dist/lib/setup/dreamRender.js.map +1 -0
- package/dist/lib/setup/dreamState.d.ts +50 -0
- package/dist/lib/setup/dreamState.d.ts.map +1 -0
- package/dist/lib/setup/dreamState.js +68 -0
- package/dist/lib/setup/dreamState.js.map +1 -0
- package/dist/lib/setup/modelsRender.d.ts +25 -0
- package/dist/lib/setup/modelsRender.d.ts.map +1 -0
- package/dist/lib/setup/modelsRender.js +33 -0
- package/dist/lib/setup/modelsRender.js.map +1 -0
- package/dist/lib/setup/remoteRender.d.ts +43 -0
- package/dist/lib/setup/remoteRender.d.ts.map +1 -0
- package/dist/lib/setup/remoteRender.js +65 -0
- package/dist/lib/setup/remoteRender.js.map +1 -0
- package/dist/lib/setup/routingRender.d.ts +48 -0
- package/dist/lib/setup/routingRender.d.ts.map +1 -0
- package/dist/lib/setup/routingRender.js +114 -0
- package/dist/lib/setup/routingRender.js.map +1 -0
- package/dist/lib/setup/sections/ides.d.ts +8 -0
- package/dist/lib/setup/sections/ides.d.ts.map +1 -1
- package/dist/lib/setup/sections/ides.js +88 -33
- package/dist/lib/setup/sections/ides.js.map +1 -1
- package/dist/lib/setup/sections/preferences.d.ts +8 -0
- package/dist/lib/setup/sections/preferences.d.ts.map +1 -1
- package/dist/lib/setup/sections/preferences.js +54 -20
- package/dist/lib/setup/sections/preferences.js.map +1 -1
- package/dist/lib/setup/sections/routing.d.ts +0 -1
- package/dist/lib/setup/sections/routing.d.ts.map +1 -1
- package/dist/lib/setup/sections/routing.js +80 -38
- package/dist/lib/setup/sections/routing.js.map +1 -1
- package/dist/lib/setup/storePath.d.ts +30 -0
- package/dist/lib/setup/storePath.d.ts.map +1 -0
- package/dist/lib/setup/storePath.js +47 -0
- package/dist/lib/setup/storePath.js.map +1 -0
- package/dist/lib/setup/summary.d.ts +33 -19
- package/dist/lib/setup/summary.d.ts.map +1 -1
- package/dist/lib/setup/summary.js +148 -113
- package/dist/lib/setup/summary.js.map +1 -1
- package/dist/lib/setup/syncProjectsRender.d.ts +57 -0
- package/dist/lib/setup/syncProjectsRender.d.ts.map +1 -0
- package/dist/lib/setup/syncProjectsRender.js +184 -0
- package/dist/lib/setup/syncProjectsRender.js.map +1 -0
- package/dist/lib/setup/ui/diff.d.ts +19 -0
- package/dist/lib/setup/ui/diff.d.ts.map +1 -0
- package/dist/lib/setup/ui/diff.js +34 -0
- package/dist/lib/setup/ui/diff.js.map +1 -0
- package/dist/lib/setup/ui/footer.d.ts +11 -0
- package/dist/lib/setup/ui/footer.d.ts.map +1 -0
- package/dist/lib/setup/ui/footer.js +21 -0
- package/dist/lib/setup/ui/footer.js.map +1 -0
- package/dist/lib/setup/ui/header.d.ts +24 -0
- package/dist/lib/setup/ui/header.d.ts.map +1 -0
- package/dist/lib/setup/ui/header.js +49 -0
- package/dist/lib/setup/ui/header.js.map +1 -0
- package/dist/lib/setup/ui/index.d.ts +28 -0
- package/dist/lib/setup/ui/index.d.ts.map +1 -0
- package/dist/lib/setup/ui/index.js +19 -0
- package/dist/lib/setup/ui/index.js.map +1 -0
- package/dist/lib/setup/ui/menu.d.ts +27 -0
- package/dist/lib/setup/ui/menu.d.ts.map +1 -0
- package/dist/lib/setup/ui/menu.js +75 -0
- package/dist/lib/setup/ui/menu.js.map +1 -0
- package/dist/lib/setup/ui/panel.d.ts +23 -0
- package/dist/lib/setup/ui/panel.d.ts.map +1 -0
- package/dist/lib/setup/ui/panel.js +60 -0
- package/dist/lib/setup/ui/panel.js.map +1 -0
- package/dist/lib/setup/ui/prompt.d.ts +27 -0
- package/dist/lib/setup/ui/prompt.d.ts.map +1 -0
- package/dist/lib/setup/ui/prompt.js +38 -0
- package/dist/lib/setup/ui/prompt.js.map +1 -0
- package/dist/lib/setup/ui/safePrompt.d.ts +24 -0
- package/dist/lib/setup/ui/safePrompt.d.ts.map +1 -0
- package/dist/lib/setup/ui/safePrompt.js +71 -0
- package/dist/lib/setup/ui/safePrompt.js.map +1 -0
- package/dist/lib/setup/ui/spinner.d.ts +28 -0
- package/dist/lib/setup/ui/spinner.d.ts.map +1 -0
- package/dist/lib/setup/ui/spinner.js +87 -0
- package/dist/lib/setup/ui/spinner.js.map +1 -0
- package/dist/lib/setup/ui/status.d.ts +21 -0
- package/dist/lib/setup/ui/status.d.ts.map +1 -0
- package/dist/lib/setup/ui/status.js +49 -0
- package/dist/lib/setup/ui/status.js.map +1 -0
- package/dist/lib/setup/ui/table.d.ts +37 -0
- package/dist/lib/setup/ui/table.d.ts.map +1 -0
- package/dist/lib/setup/ui/table.js +82 -0
- package/dist/lib/setup/ui/table.js.map +1 -0
- package/dist/lib/setup/ui/title.d.ts +14 -0
- package/dist/lib/setup/ui/title.d.ts.map +1 -0
- package/dist/lib/setup/ui/title.js +24 -0
- package/dist/lib/setup/ui/title.js.map +1 -0
- package/dist/lib/setup/ui/tokens.d.ts +66 -0
- package/dist/lib/setup/ui/tokens.d.ts.map +1 -0
- package/dist/lib/setup/ui/tokens.js +87 -0
- package/dist/lib/setup/ui/tokens.js.map +1 -0
- package/dist/lib/setup.d.ts +38 -0
- package/dist/lib/setup.d.ts.map +1 -1
- package/dist/lib/setup.js +440 -308
- package/dist/lib/setup.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -72,48 +72,21 @@ const pkgPath = path.resolve(__dirname, "..", "package.json");
|
|
|
72
72
|
const pkg = JSON.parse(await fs.readFile(pkgPath, "utf-8"));
|
|
73
73
|
const program = new Command();
|
|
74
74
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* Why stderr: doesn't pollute stdout consumers (agents parsing JSON, scripts
|
|
80
|
-
* piping the output). Visible to humans, invisible to most parsers.
|
|
81
|
-
*
|
|
82
|
-
* Why central DB meta: survives across CLI invocations and works whether
|
|
83
|
-
* gnosys is installed globally, locally, or via npx. Single source of truth
|
|
84
|
-
* for "what was the last version this user saw."
|
|
85
|
-
*
|
|
86
|
-
* Skipped when:
|
|
87
|
-
* - GNOSYS_SKIP_UPGRADE_NUDGE=1
|
|
88
|
-
* - Running `gnosys upgrade` itself (would be redundant)
|
|
89
|
-
* - Central DB unavailable (graceful — never blocks the actual command)
|
|
75
|
+
* Phase F: True if the CLI process is running inside a test harness.
|
|
76
|
+
* Any code path that would otherwise OPEN the central DB (which
|
|
77
|
+
* implicitly creates ~/.gnosys/gnosys.db) MUST short-circuit on this.
|
|
90
78
|
*/
|
|
91
|
-
function
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (process.argv[2] === "upgrade")
|
|
96
|
-
return;
|
|
97
|
-
try {
|
|
98
|
-
const db = GnosysDB.openLocal();
|
|
99
|
-
if (!db.isAvailable() || !db.isMigrated()) {
|
|
100
|
-
db.close();
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
const seen = db.getMeta("last_seen_version");
|
|
104
|
-
const current = pkg.version;
|
|
105
|
-
if (seen !== current) {
|
|
106
|
-
const prefix = seen ? `upgraded to v${current} (from v${seen})` : `installed v${current}`;
|
|
107
|
-
process.stderr.write(`gnosys: ${prefix}. Run 'gnosys upgrade' to sync registered projects.\n`);
|
|
108
|
-
db.setMeta("last_seen_version", current);
|
|
109
|
-
}
|
|
110
|
-
db.close();
|
|
111
|
-
}
|
|
112
|
-
catch {
|
|
113
|
-
// Never block actual commands — silently skip on any error.
|
|
114
|
-
}
|
|
79
|
+
function isTestEnv() {
|
|
80
|
+
return (process.env.VITEST === "true" ||
|
|
81
|
+
process.env.NODE_ENV === "test" ||
|
|
82
|
+
process.env.CI === "true");
|
|
115
83
|
}
|
|
116
|
-
maybePrintUpgradeNudge()
|
|
84
|
+
// v5.9.3 Phase H: `maybePrintUpgradeNudge` (cli.ts:92-118 in v5.9.2) was
|
|
85
|
+
// the second of two upgrade-nag mechanisms — both fired on every CLI
|
|
86
|
+
// invocation and both opened the central DB. It is now deleted; the
|
|
87
|
+
// post-install block at the BOTTOM of this file is the single source of
|
|
88
|
+
// truth, runs on stderr only, and is downgrade-aware (`reverted`/
|
|
89
|
+
// `upgraded`).
|
|
117
90
|
/**
|
|
118
91
|
* v5.6.0 back-compat shim: rewrite `gnosys export --to <dir>` →
|
|
119
92
|
* `gnosys export vault --to <dir>` before commander parses argv. The v5.6.0
|
|
@@ -193,6 +166,10 @@ Run 'gnosys <command> --help' for command-specific help.
|
|
|
193
166
|
// "you just installed a newer gnosys but haven't run sync-projects yet"
|
|
194
167
|
// case, which produced a misleading "Run: npm install -g gnosys"
|
|
195
168
|
// banner on every command.
|
|
169
|
+
//
|
|
170
|
+
// v5.9.3 (Phase F): skip in tests so we don't auto-create the central DB.
|
|
171
|
+
if (isTestEnv())
|
|
172
|
+
return;
|
|
196
173
|
try {
|
|
197
174
|
const centralDb = GnosysDB.openCentral();
|
|
198
175
|
if (centralDb.isAvailable()) {
|
|
@@ -1669,6 +1646,27 @@ program
|
|
|
1669
1646
|
catch {
|
|
1670
1647
|
cliConfig = (await import("./lib/config.js")).DEFAULT_CONFIG;
|
|
1671
1648
|
}
|
|
1649
|
+
// v5.9.3 Phase G: fail-fast on missing API key BEFORE any TUI render.
|
|
1650
|
+
// Done here so we exit before ink + react + the chat renderer pull in
|
|
1651
|
+
// 100+ ms of dependencies. Provider for the chat task may be the
|
|
1652
|
+
// default provider OR an explicit chat override in taskModels.chat.
|
|
1653
|
+
{
|
|
1654
|
+
const { resolveTaskModel: resolveTask } = await import("./lib/config.js");
|
|
1655
|
+
const chatTask = resolveTask(cliConfig, "chat");
|
|
1656
|
+
const provider = opts.provider ?? chatTask.provider;
|
|
1657
|
+
if (provider !== "ollama" && provider !== "lmstudio") {
|
|
1658
|
+
const { getApiKeyForProvider } = await import("./lib/setup.js");
|
|
1659
|
+
const key = await getApiKeyForProvider(provider);
|
|
1660
|
+
if (!key) {
|
|
1661
|
+
const { Status } = await import("./lib/setup/ui/status.js");
|
|
1662
|
+
const envVar = `${provider.toUpperCase()}_API_KEY`;
|
|
1663
|
+
process.stderr.write(`${Status("fail", `no API key for ${provider} (the configured chat provider)`)}\n`);
|
|
1664
|
+
process.stderr.write(` fix: gnosys setup pick a provider with a key, or add one\n`);
|
|
1665
|
+
process.stderr.write(` export ${envVar}=...\n`);
|
|
1666
|
+
process.exit(1);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1672
1670
|
await chat.startChat({
|
|
1673
1671
|
config: cliConfig,
|
|
1674
1672
|
resume: opts.resume,
|
|
@@ -2871,7 +2869,8 @@ const configCmd = program
|
|
|
2871
2869
|
configCmd
|
|
2872
2870
|
.command("show")
|
|
2873
2871
|
.description("Show current LLM configuration")
|
|
2874
|
-
.
|
|
2872
|
+
.option("--json", "Dump the raw effective config as JSON")
|
|
2873
|
+
.action(async (opts) => {
|
|
2875
2874
|
const resolver = await getResolver();
|
|
2876
2875
|
const stores = resolver.getStores();
|
|
2877
2876
|
if (stores.length === 0) {
|
|
@@ -2879,6 +2878,12 @@ configCmd
|
|
|
2879
2878
|
process.exit(1);
|
|
2880
2879
|
}
|
|
2881
2880
|
const cfg = await loadConfig(stores[0].path);
|
|
2881
|
+
if (opts.json) {
|
|
2882
|
+
// v5.9.3 (design §12): --json keeps the old machine-readable
|
|
2883
|
+
// dump for scripts. Default output is the human-friendly view.
|
|
2884
|
+
process.stdout.write(`${JSON.stringify(cfg, null, 2)}\n`);
|
|
2885
|
+
return;
|
|
2886
|
+
}
|
|
2882
2887
|
console.log("System of Cognition (SOC) — LLM Configuration:");
|
|
2883
2888
|
console.log(` Default provider: ${cfg.llm.defaultProvider}`);
|
|
2884
2889
|
console.log("");
|
|
@@ -2904,27 +2909,47 @@ configCmd
|
|
|
2904
2909
|
.command("set <key> <value> [extra...]")
|
|
2905
2910
|
.description("Set a config value. Keys: provider, model, ollama-url, groq-model, openai-model, lmstudio-url, task <task> <provider> <model>")
|
|
2906
2911
|
.action(async (key, value, extra) => {
|
|
2912
|
+
// v5.9.3 Screen 13 — schema-validate the top-level key against the
|
|
2913
|
+
// known set BEFORE any work, render a `did you mean X?` hint on
|
|
2914
|
+
// typo. The diff + store-source label fire after the switch.
|
|
2915
|
+
const { suggestConfigKey, classifyStore, KNOWN_CONFIG_KEYS } = await import("./lib/setup/configSetRender.js");
|
|
2916
|
+
const { Header } = await import("./lib/setup/ui/header.js");
|
|
2917
|
+
const { printStatus } = await import("./lib/setup/ui/status.js");
|
|
2918
|
+
const { printDiff } = await import("./lib/setup/ui/diff.js");
|
|
2919
|
+
if (!KNOWN_CONFIG_KEYS.includes(key)) {
|
|
2920
|
+
const suggestion = suggestConfigKey(key);
|
|
2921
|
+
const meta = suggestion ? `did you mean \`${suggestion}\` ?` : undefined;
|
|
2922
|
+
printStatus("fail", `unknown config key ${key}`, meta);
|
|
2923
|
+
process.exit(1);
|
|
2924
|
+
}
|
|
2907
2925
|
const resolver = await getResolver();
|
|
2908
2926
|
const writeTarget = resolver.getWriteTarget();
|
|
2909
2927
|
if (!writeTarget) {
|
|
2910
|
-
|
|
2928
|
+
printStatus("fail", "no writable store found");
|
|
2911
2929
|
process.exit(1);
|
|
2912
2930
|
}
|
|
2913
2931
|
const storePath = writeTarget.store.getStorePath();
|
|
2914
2932
|
const cfg = await loadConfig(storePath);
|
|
2915
2933
|
const validProviders = ALL_PROVIDERS.join(", ");
|
|
2934
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "/";
|
|
2935
|
+
const storeLabel = classifyStore(storePath, homeDir);
|
|
2936
|
+
// Each branch populates this row so the trailing Diff print picks up
|
|
2937
|
+
// a single before/after summary. Recall has three sub-fields and
|
|
2938
|
+
// sets the row inside its own switch.
|
|
2939
|
+
let diffRow = null;
|
|
2916
2940
|
switch (key) {
|
|
2917
2941
|
case "provider":
|
|
2918
2942
|
if (!ALL_PROVIDERS.includes(value)) {
|
|
2919
|
-
|
|
2943
|
+
printStatus("fail", `invalid provider \`${value}\``, `supported: ${validProviders}`);
|
|
2920
2944
|
process.exit(1);
|
|
2921
2945
|
}
|
|
2946
|
+
diffRow = { label: "provider", from: cfg.llm.defaultProvider, to: value };
|
|
2922
2947
|
cfg.llm.defaultProvider = value;
|
|
2923
|
-
console.log(`Default provider set to: ${value}`);
|
|
2924
2948
|
break;
|
|
2925
2949
|
case "model": {
|
|
2926
2950
|
// Set model for current default provider
|
|
2927
2951
|
const p = cfg.llm.defaultProvider;
|
|
2952
|
+
const fromModel = providerModel(cfg, p) ?? "(unset)";
|
|
2928
2953
|
if (p === "anthropic")
|
|
2929
2954
|
cfg.llm.anthropic.model = value;
|
|
2930
2955
|
else if (p === "ollama")
|
|
@@ -2945,7 +2970,7 @@ configCmd
|
|
|
2945
2970
|
else
|
|
2946
2971
|
cfg.llm.custom.model = value;
|
|
2947
2972
|
}
|
|
2948
|
-
|
|
2973
|
+
diffRow = { label: `${p}.model`, from: fromModel, to: value };
|
|
2949
2974
|
break;
|
|
2950
2975
|
}
|
|
2951
2976
|
case "task": {
|
|
@@ -2954,83 +2979,87 @@ configCmd
|
|
|
2954
2979
|
const taskProvider = extra[0];
|
|
2955
2980
|
const taskModel = extra[1];
|
|
2956
2981
|
if (!taskName || !taskProvider || !taskModel) {
|
|
2957
|
-
|
|
2982
|
+
printStatus("fail", "usage", "gnosys config set task <structuring|synthesis> <provider> <model>");
|
|
2958
2983
|
process.exit(1);
|
|
2959
2984
|
}
|
|
2960
2985
|
if (taskName !== "structuring" && taskName !== "synthesis") {
|
|
2961
|
-
|
|
2986
|
+
printStatus("fail", `invalid task \`${taskName}\``, "valid: structuring, synthesis");
|
|
2962
2987
|
process.exit(1);
|
|
2963
2988
|
}
|
|
2964
2989
|
if (!ALL_PROVIDERS.includes(taskProvider)) {
|
|
2965
|
-
|
|
2990
|
+
printStatus("fail", `invalid provider \`${taskProvider}\``, `supported: ${validProviders}`);
|
|
2966
2991
|
process.exit(1);
|
|
2967
2992
|
}
|
|
2968
2993
|
if (!cfg.taskModels)
|
|
2969
2994
|
cfg.taskModels = {};
|
|
2970
|
-
cfg.taskModels
|
|
2971
|
-
|
|
2995
|
+
const taskMap = cfg.taskModels;
|
|
2996
|
+
const prev = taskMap[taskName];
|
|
2997
|
+
const fromStr = prev ? `${prev.provider}/${prev.model}` : "(unset)";
|
|
2998
|
+
taskMap[taskName] = { provider: taskProvider, model: taskModel };
|
|
2999
|
+
diffRow = { label: `task.${taskName}`, from: fromStr, to: `${taskProvider}/${taskModel}` };
|
|
2972
3000
|
break;
|
|
2973
3001
|
}
|
|
2974
3002
|
case "ollama-url":
|
|
3003
|
+
diffRow = { label: "ollama.baseUrl", from: cfg.llm.ollama.baseUrl ?? "(unset)", to: value };
|
|
2975
3004
|
cfg.llm.ollama.baseUrl = value;
|
|
2976
|
-
console.log(`Ollama base URL set to: ${value}`);
|
|
2977
3005
|
break;
|
|
2978
3006
|
case "ollama-model":
|
|
3007
|
+
diffRow = { label: "ollama.model", from: cfg.llm.ollama.model ?? "(unset)", to: value };
|
|
2979
3008
|
cfg.llm.ollama.model = value;
|
|
2980
|
-
console.log(`Ollama model set to: ${value}`);
|
|
2981
3009
|
break;
|
|
2982
3010
|
case "anthropic-model":
|
|
3011
|
+
diffRow = { label: "anthropic.model", from: cfg.llm.anthropic.model ?? "(unset)", to: value };
|
|
2983
3012
|
cfg.llm.anthropic.model = value;
|
|
2984
|
-
console.log(`Anthropic model set to: ${value}`);
|
|
2985
3013
|
break;
|
|
2986
3014
|
case "groq-model":
|
|
3015
|
+
diffRow = { label: "groq.model", from: cfg.llm.groq.model ?? "(unset)", to: value };
|
|
2987
3016
|
cfg.llm.groq.model = value;
|
|
2988
|
-
console.log(`Groq model set to: ${value}`);
|
|
2989
3017
|
break;
|
|
2990
3018
|
case "openai-model":
|
|
3019
|
+
diffRow = { label: "openai.model", from: cfg.llm.openai.model ?? "(unset)", to: value };
|
|
2991
3020
|
cfg.llm.openai.model = value;
|
|
2992
|
-
console.log(`OpenAI model set to: ${value}`);
|
|
2993
3021
|
break;
|
|
2994
3022
|
case "openai-url":
|
|
3023
|
+
diffRow = { label: "openai.baseUrl", from: cfg.llm.openai.baseUrl ?? "(unset)", to: value };
|
|
2995
3024
|
cfg.llm.openai.baseUrl = value;
|
|
2996
|
-
console.log(`OpenAI base URL set to: ${value}`);
|
|
2997
3025
|
break;
|
|
2998
3026
|
case "lmstudio-url":
|
|
3027
|
+
diffRow = { label: "lmstudio.baseUrl", from: cfg.llm.lmstudio.baseUrl ?? "(unset)", to: value };
|
|
2999
3028
|
cfg.llm.lmstudio.baseUrl = value;
|
|
3000
|
-
console.log(`LM Studio base URL set to: ${value}`);
|
|
3001
3029
|
break;
|
|
3002
3030
|
case "lmstudio-model":
|
|
3031
|
+
diffRow = { label: "lmstudio.model", from: cfg.llm.lmstudio.model ?? "(unset)", to: value };
|
|
3003
3032
|
cfg.llm.lmstudio.model = value;
|
|
3004
|
-
console.log(`LM Studio model set to: ${value}`);
|
|
3005
3033
|
break;
|
|
3006
3034
|
case "xai-model":
|
|
3035
|
+
diffRow = { label: "xai.model", from: cfg.llm.xai.model ?? "(unset)", to: value };
|
|
3007
3036
|
cfg.llm.xai.model = value;
|
|
3008
|
-
console.log(`xAI model set to: ${value}`);
|
|
3009
3037
|
break;
|
|
3010
3038
|
case "mistral-model":
|
|
3039
|
+
diffRow = { label: "mistral.model", from: cfg.llm.mistral.model ?? "(unset)", to: value };
|
|
3011
3040
|
cfg.llm.mistral.model = value;
|
|
3012
|
-
console.log(`Mistral model set to: ${value}`);
|
|
3013
3041
|
break;
|
|
3014
3042
|
case "custom-url":
|
|
3043
|
+
diffRow = { label: "custom.baseUrl", from: cfg.llm.custom?.baseUrl ?? "(unset)", to: value };
|
|
3015
3044
|
if (!cfg.llm.custom)
|
|
3016
3045
|
cfg.llm.custom = { model: "", baseUrl: value };
|
|
3017
3046
|
else
|
|
3018
3047
|
cfg.llm.custom.baseUrl = value;
|
|
3019
|
-
console.log(`Custom provider base URL set to: ${value}`);
|
|
3020
3048
|
break;
|
|
3021
3049
|
case "custom-model":
|
|
3050
|
+
diffRow = { label: "custom.model", from: cfg.llm.custom?.model ?? "(unset)", to: value };
|
|
3022
3051
|
if (!cfg.llm.custom)
|
|
3023
3052
|
cfg.llm.custom = { model: value, baseUrl: "" };
|
|
3024
3053
|
else
|
|
3025
3054
|
cfg.llm.custom.model = value;
|
|
3026
|
-
console.log(`Custom provider model set to: ${value}`);
|
|
3027
3055
|
break;
|
|
3028
3056
|
case "custom-key":
|
|
3057
|
+
// Sensitive — don't echo the key in the diff, just mark as redacted.
|
|
3058
|
+
diffRow = { label: "custom.apiKey", from: cfg.llm.custom?.apiKey ? "(set)" : "(unset)", to: "(set)" };
|
|
3029
3059
|
if (!cfg.llm.custom)
|
|
3030
3060
|
cfg.llm.custom = { model: "", baseUrl: "", apiKey: value };
|
|
3031
3061
|
else
|
|
3032
3062
|
cfg.llm.custom.apiKey = value;
|
|
3033
|
-
console.log(`Custom provider API key set.`);
|
|
3034
3063
|
break;
|
|
3035
3064
|
case "recall": {
|
|
3036
3065
|
// gnosys config set recall <field> <value>
|
|
@@ -3038,7 +3067,7 @@ configCmd
|
|
|
3038
3067
|
const recallField = value;
|
|
3039
3068
|
const recallValue = extra[0];
|
|
3040
3069
|
if (!recallField || !recallValue) {
|
|
3041
|
-
|
|
3070
|
+
printStatus("fail", "usage", "gnosys config set recall <aggressive|maxMemories|minRelevance> <value>");
|
|
3042
3071
|
process.exit(1);
|
|
3043
3072
|
}
|
|
3044
3073
|
if (!cfg.recall)
|
|
@@ -3046,49 +3075,88 @@ configCmd
|
|
|
3046
3075
|
switch (recallField) {
|
|
3047
3076
|
case "aggressive":
|
|
3048
3077
|
if (recallValue !== "true" && recallValue !== "false") {
|
|
3049
|
-
|
|
3078
|
+
printStatus("fail", `invalid value \`${recallValue}\``, "use `true` or `false`");
|
|
3050
3079
|
process.exit(1);
|
|
3051
3080
|
}
|
|
3081
|
+
diffRow = { label: "recall.aggressive", from: String(cfg.recall.aggressive), to: recallValue };
|
|
3052
3082
|
cfg.recall.aggressive = recallValue === "true";
|
|
3053
|
-
console.log(`Recall aggressive mode: ${cfg.recall.aggressive ? "enabled" : "disabled"}`);
|
|
3054
3083
|
break;
|
|
3055
3084
|
case "maxMemories": {
|
|
3056
3085
|
const n = parseInt(recallValue, 10);
|
|
3057
3086
|
if (isNaN(n) || n < 1 || n > 20) {
|
|
3058
|
-
|
|
3087
|
+
printStatus("fail", "maxMemories must be between 1 and 20");
|
|
3059
3088
|
process.exit(1);
|
|
3060
3089
|
}
|
|
3090
|
+
diffRow = { label: "recall.maxMemories", from: String(cfg.recall.maxMemories), to: String(n) };
|
|
3061
3091
|
cfg.recall.maxMemories = n;
|
|
3062
|
-
console.log(`Recall maxMemories set to: ${n}`);
|
|
3063
3092
|
break;
|
|
3064
3093
|
}
|
|
3065
3094
|
case "minRelevance": {
|
|
3066
3095
|
const f = parseFloat(recallValue);
|
|
3067
3096
|
if (isNaN(f) || f < 0 || f > 1) {
|
|
3068
|
-
|
|
3097
|
+
printStatus("fail", "minRelevance must be between 0 and 1");
|
|
3069
3098
|
process.exit(1);
|
|
3070
3099
|
}
|
|
3100
|
+
diffRow = { label: "recall.minRelevance", from: String(cfg.recall.minRelevance), to: String(f) };
|
|
3071
3101
|
cfg.recall.minRelevance = f;
|
|
3072
|
-
console.log(`Recall minRelevance set to: ${f}`);
|
|
3073
3102
|
break;
|
|
3074
3103
|
}
|
|
3075
3104
|
default:
|
|
3076
|
-
|
|
3105
|
+
printStatus("fail", `unknown recall field \`${recallField}\``, "valid: aggressive, maxMemories, minRelevance");
|
|
3077
3106
|
process.exit(1);
|
|
3078
3107
|
}
|
|
3079
3108
|
break;
|
|
3080
3109
|
}
|
|
3081
|
-
default:
|
|
3082
|
-
console.error(`Unknown config key: "${key}". Valid: provider, model, task, ollama-url, ollama-model, anthropic-model, groq-model, openai-model, openai-url, lmstudio-url, lmstudio-model, recall`);
|
|
3083
|
-
process.exit(1);
|
|
3084
3110
|
}
|
|
3085
3111
|
await writeConfig(storePath, cfg);
|
|
3086
|
-
|
|
3112
|
+
// v5.9.3 Screen 13 — print Header + Diff + ✓ saved with store label.
|
|
3113
|
+
console.log("");
|
|
3114
|
+
console.log(Header(["gnosys", "config", "set"]));
|
|
3115
|
+
console.log("");
|
|
3116
|
+
if (diffRow) {
|
|
3117
|
+
// Append store source to the `to` column so the diff line says
|
|
3118
|
+
// both the new value and where it landed (project vs global).
|
|
3119
|
+
printDiff([{ ...diffRow, to: `${diffRow.to} (${storeLabel})` }]);
|
|
3120
|
+
}
|
|
3121
|
+
printStatus("ok", `saved · ${path.join(storePath, "gnosys.json")}`, `(${storeLabel})`);
|
|
3087
3122
|
});
|
|
3123
|
+
/** Resolve the current `.model` field for the active LLM provider. */
|
|
3124
|
+
function providerModel(cfg, p) {
|
|
3125
|
+
switch (p) {
|
|
3126
|
+
case "anthropic": return cfg.llm.anthropic.model;
|
|
3127
|
+
case "ollama": return cfg.llm.ollama.model;
|
|
3128
|
+
case "groq": return cfg.llm.groq.model;
|
|
3129
|
+
case "openai": return cfg.llm.openai.model;
|
|
3130
|
+
case "lmstudio": return cfg.llm.lmstudio.model;
|
|
3131
|
+
case "xai": return cfg.llm.xai.model;
|
|
3132
|
+
case "mistral": return cfg.llm.mistral.model;
|
|
3133
|
+
case "custom": return cfg.llm.custom?.model;
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3088
3136
|
configCmd
|
|
3089
3137
|
.command("init")
|
|
3090
|
-
.description("Generate a
|
|
3091
|
-
.
|
|
3138
|
+
.description("Generate a blank gnosys.json template (deprecated — prefer `gnosys setup`)")
|
|
3139
|
+
.option("--force", "Skip the deprecation warning and write the template")
|
|
3140
|
+
.action(async (opts) => {
|
|
3141
|
+
// v5.9.3 (design handoff §14, deci-050): `config init` is being
|
|
3142
|
+
// folded into `gnosys setup`. Without --force we print a warning
|
|
3143
|
+
// pointing to `gnosys setup` and exit. With --force we write the
|
|
3144
|
+
// (now-blank-provider) template anyway for muscle-memory use.
|
|
3145
|
+
if (!opts.force) {
|
|
3146
|
+
const { Header } = await import("./lib/setup/ui/header.js");
|
|
3147
|
+
const { Status } = await import("./lib/setup/ui/status.js");
|
|
3148
|
+
const { Footer } = await import("./lib/setup/ui/footer.js");
|
|
3149
|
+
console.log("");
|
|
3150
|
+
console.log(Header(["gnosys", "config", "init"], { version: `v${pkg.version}` }));
|
|
3151
|
+
console.log("");
|
|
3152
|
+
console.log(Status("warn", "writing a blank template means the next run of `gnosys setup`"));
|
|
3153
|
+
console.log(Status("warn", "will walk you through the same choices anyway"));
|
|
3154
|
+
console.log("");
|
|
3155
|
+
console.log(" try gnosys setup interactive walkthrough (recommended)");
|
|
3156
|
+
console.log("");
|
|
3157
|
+
console.log(Footer("re-run with --force to write the template anyway"));
|
|
3158
|
+
process.exit(0);
|
|
3159
|
+
}
|
|
3092
3160
|
const resolver = await getResolver();
|
|
3093
3161
|
const writeTarget = resolver.getWriteTarget();
|
|
3094
3162
|
if (!writeTarget) {
|
|
@@ -3224,7 +3292,12 @@ program
|
|
|
3224
3292
|
// `syncProjectsAction`, called from the new `setup sync-projects` command.
|
|
3225
3293
|
async function syncProjectsAction(opts) {
|
|
3226
3294
|
const currentVersion = pkg.version;
|
|
3227
|
-
|
|
3295
|
+
// v5.9.3 Screen 10 — Header + leading spinner + hierarchical sections.
|
|
3296
|
+
const { renderSyncHeader, renderUpgradedSection, renderSkippedSection, renderFailedSection, renderMachinesSection, renderDivider, renderDoneLine, renderDashboardSummary, } = await import("./lib/setup/syncProjectsRender.js");
|
|
3297
|
+
const { Spinner } = await import("./lib/setup/ui/spinner.js");
|
|
3298
|
+
console.log("");
|
|
3299
|
+
console.log(renderSyncHeader(currentVersion));
|
|
3300
|
+
console.log("");
|
|
3228
3301
|
// 1. Read registered projects from file registry AND central DB
|
|
3229
3302
|
const home = process.env.HOME || process.env.USERPROFILE || "/tmp";
|
|
3230
3303
|
const registryPath = path.join(home, ".config", "gnosys", "projects.json");
|
|
@@ -3235,13 +3308,18 @@ async function syncProjectsAction(opts) {
|
|
|
3235
3308
|
catch {
|
|
3236
3309
|
// No file registry yet
|
|
3237
3310
|
}
|
|
3238
|
-
// Also check central DB for projects not in the file registry
|
|
3311
|
+
// Also check central DB for projects not in the file registry. Also
|
|
3312
|
+
// capture project titles so the Screen 10 row labels can use the
|
|
3313
|
+
// human-readable name where available.
|
|
3239
3314
|
let dbProjects = [];
|
|
3315
|
+
const titleByDir = new Map();
|
|
3240
3316
|
try {
|
|
3241
3317
|
const centralDb = GnosysDB.openCentral();
|
|
3242
3318
|
if (centralDb.isAvailable()) {
|
|
3243
3319
|
const allProjects = centralDb.getAllProjects();
|
|
3244
3320
|
dbProjects = allProjects.map((p) => p.working_directory);
|
|
3321
|
+
for (const p of allProjects)
|
|
3322
|
+
titleByDir.set(p.working_directory, p.name);
|
|
3245
3323
|
centralDb.close();
|
|
3246
3324
|
}
|
|
3247
3325
|
}
|
|
@@ -3259,9 +3337,13 @@ async function syncProjectsAction(opts) {
|
|
|
3259
3337
|
}
|
|
3260
3338
|
}
|
|
3261
3339
|
if (projects.length === 0) {
|
|
3262
|
-
console.log("
|
|
3340
|
+
console.log(" no registered projects found");
|
|
3341
|
+
console.log(" run `gnosys init` in each project first");
|
|
3263
3342
|
return;
|
|
3264
3343
|
}
|
|
3344
|
+
// Lead-in spinner: shows we're churning through the registry. Resolves
|
|
3345
|
+
// to ✓ summary after the iteration loop completes (or fail on hard error).
|
|
3346
|
+
const syncSpinner = Spinner(`syncing ${projects.length} registered projects…`);
|
|
3265
3347
|
// Sync the merged list back to file registry
|
|
3266
3348
|
try {
|
|
3267
3349
|
await fs.mkdir(path.dirname(registryPath), { recursive: true });
|
|
@@ -3325,6 +3407,9 @@ async function syncProjectsAction(opts) {
|
|
|
3325
3407
|
failed.push(`${projectDir} (${err.message})`);
|
|
3326
3408
|
}
|
|
3327
3409
|
}
|
|
3410
|
+
// Stop the lead-in spinner now that the iteration is done. Resolved
|
|
3411
|
+
// before any per-section output so the cursor is on a fresh line.
|
|
3412
|
+
syncSpinner.ok(`synced ${projects.length} registered projects`, `${upgraded.length} upgraded · ${skipped.length} skipped · ${failed.length} failed`);
|
|
3328
3413
|
// 3. Update global agent rules
|
|
3329
3414
|
try {
|
|
3330
3415
|
let centralDb = null;
|
|
@@ -3340,11 +3425,13 @@ async function syncProjectsAction(opts) {
|
|
|
3340
3425
|
const { syncToTarget } = await import("./lib/rulesGen.js");
|
|
3341
3426
|
await syncToTarget(centralDb, process.cwd(), "global", null);
|
|
3342
3427
|
centralDb.close();
|
|
3343
|
-
|
|
3428
|
+
const { printStatus } = await import("./lib/setup/ui/status.js");
|
|
3429
|
+
printStatus("ok", "global agent rules updated", "~/.claude/CLAUDE.md");
|
|
3344
3430
|
}
|
|
3345
3431
|
}
|
|
3346
3432
|
catch {
|
|
3347
|
-
|
|
3433
|
+
const { printStatus } = await import("./lib/setup/ui/status.js");
|
|
3434
|
+
printStatus("warn", "could not update global agent rules");
|
|
3348
3435
|
}
|
|
3349
3436
|
// 4. Stamp the central DB with current version and machine info
|
|
3350
3437
|
try {
|
|
@@ -3370,25 +3457,36 @@ async function syncProjectsAction(opts) {
|
|
|
3370
3457
|
catch {
|
|
3371
3458
|
// non-critical
|
|
3372
3459
|
}
|
|
3373
|
-
// 5. Report
|
|
3460
|
+
// 5. Report — v5.9.3 Screen 10 hierarchical layout. Section helpers
|
|
3461
|
+
// turn the raw path arrays into ProjectRow lists (title + fullPath)
|
|
3462
|
+
// and emit dividers between groups.
|
|
3463
|
+
function rowFor(p) {
|
|
3464
|
+
const title = titleByDir.get(p) ?? titleByDir.get(path.resolve(p)) ?? path.basename(p);
|
|
3465
|
+
return { title, fullPath: p };
|
|
3466
|
+
}
|
|
3467
|
+
const upgradedRows = upgraded.map(rowFor);
|
|
3468
|
+
const skippedRows = skipped.map(rowFor);
|
|
3469
|
+
const failedRows = failed.map((f) => {
|
|
3470
|
+
// failed entries are "<path> (<err>)" — extract path for the title.
|
|
3471
|
+
const match = f.match(/^(.+?)\s\((.+)\)$/);
|
|
3472
|
+
const projectPath = match ? match[1] : f;
|
|
3473
|
+
return { title: titleByDir.get(projectPath) ?? path.basename(projectPath), fullPath: f };
|
|
3474
|
+
});
|
|
3374
3475
|
console.log("");
|
|
3375
|
-
|
|
3376
|
-
console.log(
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
}
|
|
3380
|
-
|
|
3381
|
-
console.log(
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
}
|
|
3390
|
-
console.log(`\nDone. Central DB stamped with v${currentVersion}.`);
|
|
3391
|
-
// Show machine status from shared DB
|
|
3476
|
+
for (const line of renderUpgradedSection(upgradedRows))
|
|
3477
|
+
console.log(line);
|
|
3478
|
+
if (upgradedRows.length > 0 && (skippedRows.length > 0 || failedRows.length > 0)) {
|
|
3479
|
+
console.log("");
|
|
3480
|
+
}
|
|
3481
|
+
for (const line of renderSkippedSection(skippedRows))
|
|
3482
|
+
console.log(line);
|
|
3483
|
+
if (failedRows.length > 0) {
|
|
3484
|
+
console.log("");
|
|
3485
|
+
for (const line of renderFailedSection(failedRows))
|
|
3486
|
+
console.log(line);
|
|
3487
|
+
}
|
|
3488
|
+
// Connected-machines callout (separate divider per design spec).
|
|
3489
|
+
let machineLines = [];
|
|
3392
3490
|
try {
|
|
3393
3491
|
const centralDb = GnosysDB.openCentral();
|
|
3394
3492
|
if (centralDb.isAvailable()) {
|
|
@@ -3396,18 +3494,14 @@ async function syncProjectsAction(opts) {
|
|
|
3396
3494
|
if (raw) {
|
|
3397
3495
|
const machines = JSON.parse(raw);
|
|
3398
3496
|
const entries = Object.entries(machines);
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
if (behind.length > 0) {
|
|
3408
|
-
console.log(`\n ${behind.length} machine(s) need upgrading. Run 'npm install -g gnosys && gnosys upgrade' on each.`);
|
|
3409
|
-
}
|
|
3410
|
-
}
|
|
3497
|
+
const currentHost = os.hostname();
|
|
3498
|
+
const machineRows = entries.map(([host, info]) => ({
|
|
3499
|
+
hostname: host,
|
|
3500
|
+
version: info.version,
|
|
3501
|
+
lastSeen: info.lastSeen,
|
|
3502
|
+
isCurrent: host === currentHost,
|
|
3503
|
+
}));
|
|
3504
|
+
machineLines = renderMachinesSection(machineRows, currentVersion);
|
|
3411
3505
|
}
|
|
3412
3506
|
centralDb.close();
|
|
3413
3507
|
}
|
|
@@ -3415,9 +3509,36 @@ async function syncProjectsAction(opts) {
|
|
|
3415
3509
|
catch {
|
|
3416
3510
|
// non-critical
|
|
3417
3511
|
}
|
|
3418
|
-
if (
|
|
3419
|
-
console.log(
|
|
3420
|
-
console.log(
|
|
3512
|
+
if (machineLines.length > 0) {
|
|
3513
|
+
console.log("");
|
|
3514
|
+
console.log(renderDivider());
|
|
3515
|
+
console.log("");
|
|
3516
|
+
for (const line of machineLines)
|
|
3517
|
+
console.log(line);
|
|
3518
|
+
}
|
|
3519
|
+
console.log("");
|
|
3520
|
+
console.log(renderDivider());
|
|
3521
|
+
console.log("");
|
|
3522
|
+
console.log(renderDoneLine(currentVersion));
|
|
3523
|
+
if (skippedRows.length > 0) {
|
|
3524
|
+
// v5.9.3 Phase H: offer one-keystroke cleanup. Stays interactive
|
|
3525
|
+
// by default; users on a TTY get the prompt, non-TTY runs silently
|
|
3526
|
+
// (sync-projects is sometimes invoked from CI).
|
|
3527
|
+
console.log("");
|
|
3528
|
+
if (process.stdout.isTTY) {
|
|
3529
|
+
try {
|
|
3530
|
+
const { cleanupRegistry } = await import("./lib/cleanup.js");
|
|
3531
|
+
await cleanupRegistry({ interactive: true });
|
|
3532
|
+
}
|
|
3533
|
+
catch (err) {
|
|
3534
|
+
const { printStatus } = await import("./lib/setup/ui/status.js");
|
|
3535
|
+
printStatus("warn", "cleanup skipped", err instanceof Error ? err.message : String(err));
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
else {
|
|
3539
|
+
const { printStatus } = await import("./lib/setup/ui/status.js");
|
|
3540
|
+
printStatus("progress", "tip", "run `gnosys cleanup` to remove stale entries");
|
|
3541
|
+
}
|
|
3421
3542
|
}
|
|
3422
3543
|
// 6. Regenerate portfolio dashboard
|
|
3423
3544
|
if (!opts.skipDashboard) {
|
|
@@ -3432,13 +3553,16 @@ async function syncProjectsAction(opts) {
|
|
|
3432
3553
|
await fs.writeFile(dashboardPath, generatePortfolioHtml(report, dashboardPath), "utf-8");
|
|
3433
3554
|
await fs.writeFile(dashboardMdPath, formatPortfolioMarkdown(report), "utf-8");
|
|
3434
3555
|
centralDb.close();
|
|
3435
|
-
console.log(
|
|
3436
|
-
|
|
3437
|
-
|
|
3556
|
+
console.log("");
|
|
3557
|
+
for (const line of renderDashboardSummary(dashboardPath, dashboardMdPath)) {
|
|
3558
|
+
console.log(line);
|
|
3559
|
+
}
|
|
3438
3560
|
}
|
|
3439
3561
|
}
|
|
3440
3562
|
catch {
|
|
3441
|
-
|
|
3563
|
+
const { printStatus } = await import("./lib/setup/ui/status.js");
|
|
3564
|
+
console.log("");
|
|
3565
|
+
printStatus("warn", "could not regenerate portfolio dashboard");
|
|
3442
3566
|
}
|
|
3443
3567
|
}
|
|
3444
3568
|
}
|
|
@@ -3449,6 +3573,25 @@ setupCmd
|
|
|
3449
3573
|
.description("Re-initialize all registered projects after upgrading gnosys: refresh agent rules, project registry, central DB stamp, and portfolio dashboard.")
|
|
3450
3574
|
.option("--skip-dashboard", "Skip regenerating the portfolio dashboard")
|
|
3451
3575
|
.action(syncProjectsAction);
|
|
3576
|
+
// `gnosys cleanup` — prune dead/temp entries from the project registry.
|
|
3577
|
+
// Standalone top-level command per Phase H. Also reusable from inside
|
|
3578
|
+
// `setup sync-projects` when the skipped list is non-empty (see
|
|
3579
|
+
// road-015).
|
|
3580
|
+
program
|
|
3581
|
+
.command("cleanup")
|
|
3582
|
+
.description("Remove dead and temp-dir entries from the project registry")
|
|
3583
|
+
.option("--yes", "Non-interactive, remove without prompting")
|
|
3584
|
+
.option("--dry-run", "Show what would be removed without writing")
|
|
3585
|
+
.action(async (opts) => {
|
|
3586
|
+
const { cleanupRegistry } = await import("./lib/cleanup.js");
|
|
3587
|
+
const result = await cleanupRegistry({
|
|
3588
|
+
interactive: !opts.yes && !opts.dryRun,
|
|
3589
|
+
yes: opts.yes,
|
|
3590
|
+
});
|
|
3591
|
+
if (opts.yes || opts.dryRun) {
|
|
3592
|
+
console.log(JSON.stringify(result, null, 2));
|
|
3593
|
+
}
|
|
3594
|
+
});
|
|
3452
3595
|
// `gnosys upgrade` — upgrade the gnosys CLI/MCP itself, then prompt the
|
|
3453
3596
|
// user to run sync-projects. Writes ~/.gnosys/last-upgrade-at so running
|
|
3454
3597
|
// MCP servers exit cleanly and the host respawns them against the new
|
|
@@ -6265,36 +6408,57 @@ webCmd
|
|
|
6265
6408
|
// you" which is a separate concern handled by the preAction warning.
|
|
6266
6409
|
// Also avoids re-nudging once per command for the same version-pair by
|
|
6267
6410
|
// honoring a per-session env-var sentinel.
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
const
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6411
|
+
//
|
|
6412
|
+
// v5.9.3 Phase H consolidation lives BELOW this block; Phase F guards us
|
|
6413
|
+
// from touching the DB at all in tests.
|
|
6414
|
+
if (!isTestEnv()) {
|
|
6415
|
+
try {
|
|
6416
|
+
const centralDb = GnosysDB.openCentral();
|
|
6417
|
+
if (centralDb.isAvailable()) {
|
|
6418
|
+
const lastVersion = centralDb.getMeta("app_version");
|
|
6419
|
+
const currentVersion = pkg.version;
|
|
6420
|
+
const isUpgradeCmd = process.argv.slice(2).some(a => a === "upgrade");
|
|
6421
|
+
const isSetupSyncCmd = process.argv.slice(2).join(" ").includes("setup sync-projects");
|
|
6422
|
+
// CRITICAL: `serve` writes JSON-RPC to stdout for MCP transport. Any
|
|
6423
|
+
// console.log during boot corrupts the protocol and the host (Grok, Codex,
|
|
6424
|
+
// etc.) sees the server as [unavailable]. Suppress the nag in serve mode.
|
|
6425
|
+
const isServeCmd = process.argv.slice(2).some(a => a === "serve");
|
|
6426
|
+
// v5.9.3 Phase H: fire on any mismatch (upgrade OR downgrade).
|
|
6427
|
+
const mismatch = lastVersion !== null && lastVersion !== undefined &&
|
|
6428
|
+
compareSemver(currentVersion, lastVersion) !== 0;
|
|
6429
|
+
if (mismatch && !isUpgradeCmd && !isSetupSyncCmd && !isServeCmd) {
|
|
6430
|
+
// v5.9.3 Phase H: emit on STDERR (was stdout). Safer invariant per
|
|
6431
|
+
// deci-045 — stdout is reserved for command output.
|
|
6432
|
+
const isMajorOrMinor = (() => {
|
|
6433
|
+
if (!lastVersion)
|
|
6434
|
+
return false;
|
|
6435
|
+
const oldParts = lastVersion.split(".").map(Number);
|
|
6436
|
+
const newParts = currentVersion.split(".").map(Number);
|
|
6437
|
+
return (oldParts[0] ?? 0) !== (newParts[0] ?? 0) || (oldParts[1] ?? 0) !== (newParts[1] ?? 0);
|
|
6438
|
+
})();
|
|
6439
|
+
const direction = compareSemver(currentVersion, lastVersion ?? "0.0.0") > 0 ? "upgraded" : "reverted";
|
|
6440
|
+
process.stderr.write("\n");
|
|
6441
|
+
process.stderr.write(` ⬢ gnosys ${direction} · v${lastVersion} → v${currentVersion}\n`);
|
|
6442
|
+
process.stderr.write("\n");
|
|
6443
|
+
if (direction === "upgraded") {
|
|
6444
|
+
process.stderr.write(" sync registered projects gnosys upgrade\n");
|
|
6445
|
+
if (isMajorOrMinor) {
|
|
6446
|
+
process.stderr.write(" restart mcp cursor → MCP: restart all servers\n");
|
|
6447
|
+
process.stderr.write(" claude code → /mcp → restart gnosys\n");
|
|
6448
|
+
process.stderr.write(" codex → start new session\n");
|
|
6449
|
+
}
|
|
6450
|
+
}
|
|
6451
|
+
else {
|
|
6452
|
+
process.stderr.write(" if this was unintentional, run gnosys upgrade\n");
|
|
6453
|
+
}
|
|
6454
|
+
process.stderr.write("\n");
|
|
6455
|
+
}
|
|
6456
|
+
centralDb.close();
|
|
6292
6457
|
}
|
|
6293
|
-
centralDb.close();
|
|
6294
6458
|
}
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6459
|
+
catch {
|
|
6460
|
+
// non-critical — don't block CLI startup
|
|
6461
|
+
}
|
|
6298
6462
|
}
|
|
6299
6463
|
program.parse();
|
|
6300
6464
|
//# sourceMappingURL=cli.js.map
|