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.
Files changed (136) hide show
  1. package/dist/cli.js +311 -147
  2. package/dist/cli.js.map +1 -1
  3. package/dist/lib/chat/index.d.ts.map +1 -1
  4. package/dist/lib/chat/index.js +5 -0
  5. package/dist/lib/chat/index.js.map +1 -1
  6. package/dist/lib/cleanup.d.ts +52 -0
  7. package/dist/lib/cleanup.d.ts.map +1 -0
  8. package/dist/lib/cleanup.js +168 -0
  9. package/dist/lib/cleanup.js.map +1 -0
  10. package/dist/lib/config.d.ts +7 -0
  11. package/dist/lib/config.d.ts.map +1 -1
  12. package/dist/lib/config.js +41 -2
  13. package/dist/lib/config.js.map +1 -1
  14. package/dist/lib/dream.d.ts.map +1 -1
  15. package/dist/lib/dream.js +13 -2
  16. package/dist/lib/dream.js.map +1 -1
  17. package/dist/lib/paths.d.ts +8 -0
  18. package/dist/lib/paths.d.ts.map +1 -1
  19. package/dist/lib/paths.js +15 -0
  20. package/dist/lib/paths.js.map +1 -1
  21. package/dist/lib/remote.d.ts +11 -0
  22. package/dist/lib/remote.d.ts.map +1 -1
  23. package/dist/lib/remote.js +26 -2
  24. package/dist/lib/remote.js.map +1 -1
  25. package/dist/lib/remoteWizard.d.ts.map +1 -1
  26. package/dist/lib/remoteWizard.js +112 -52
  27. package/dist/lib/remoteWizard.js.map +1 -1
  28. package/dist/lib/setup/coldStart.d.ts +71 -0
  29. package/dist/lib/setup/coldStart.d.ts.map +1 -0
  30. package/dist/lib/setup/coldStart.js +122 -0
  31. package/dist/lib/setup/coldStart.js.map +1 -0
  32. package/dist/lib/setup/configSetRender.d.ts +26 -0
  33. package/dist/lib/setup/configSetRender.d.ts.map +1 -0
  34. package/dist/lib/setup/configSetRender.js +103 -0
  35. package/dist/lib/setup/configSetRender.js.map +1 -0
  36. package/dist/lib/setup/dreamRender.d.ts +44 -0
  37. package/dist/lib/setup/dreamRender.d.ts.map +1 -0
  38. package/dist/lib/setup/dreamRender.js +55 -0
  39. package/dist/lib/setup/dreamRender.js.map +1 -0
  40. package/dist/lib/setup/dreamState.d.ts +50 -0
  41. package/dist/lib/setup/dreamState.d.ts.map +1 -0
  42. package/dist/lib/setup/dreamState.js +68 -0
  43. package/dist/lib/setup/dreamState.js.map +1 -0
  44. package/dist/lib/setup/modelsRender.d.ts +25 -0
  45. package/dist/lib/setup/modelsRender.d.ts.map +1 -0
  46. package/dist/lib/setup/modelsRender.js +33 -0
  47. package/dist/lib/setup/modelsRender.js.map +1 -0
  48. package/dist/lib/setup/remoteRender.d.ts +43 -0
  49. package/dist/lib/setup/remoteRender.d.ts.map +1 -0
  50. package/dist/lib/setup/remoteRender.js +65 -0
  51. package/dist/lib/setup/remoteRender.js.map +1 -0
  52. package/dist/lib/setup/routingRender.d.ts +48 -0
  53. package/dist/lib/setup/routingRender.d.ts.map +1 -0
  54. package/dist/lib/setup/routingRender.js +114 -0
  55. package/dist/lib/setup/routingRender.js.map +1 -0
  56. package/dist/lib/setup/sections/ides.d.ts +8 -0
  57. package/dist/lib/setup/sections/ides.d.ts.map +1 -1
  58. package/dist/lib/setup/sections/ides.js +88 -33
  59. package/dist/lib/setup/sections/ides.js.map +1 -1
  60. package/dist/lib/setup/sections/preferences.d.ts +8 -0
  61. package/dist/lib/setup/sections/preferences.d.ts.map +1 -1
  62. package/dist/lib/setup/sections/preferences.js +54 -20
  63. package/dist/lib/setup/sections/preferences.js.map +1 -1
  64. package/dist/lib/setup/sections/routing.d.ts +0 -1
  65. package/dist/lib/setup/sections/routing.d.ts.map +1 -1
  66. package/dist/lib/setup/sections/routing.js +80 -38
  67. package/dist/lib/setup/sections/routing.js.map +1 -1
  68. package/dist/lib/setup/storePath.d.ts +30 -0
  69. package/dist/lib/setup/storePath.d.ts.map +1 -0
  70. package/dist/lib/setup/storePath.js +47 -0
  71. package/dist/lib/setup/storePath.js.map +1 -0
  72. package/dist/lib/setup/summary.d.ts +33 -19
  73. package/dist/lib/setup/summary.d.ts.map +1 -1
  74. package/dist/lib/setup/summary.js +148 -113
  75. package/dist/lib/setup/summary.js.map +1 -1
  76. package/dist/lib/setup/syncProjectsRender.d.ts +57 -0
  77. package/dist/lib/setup/syncProjectsRender.d.ts.map +1 -0
  78. package/dist/lib/setup/syncProjectsRender.js +184 -0
  79. package/dist/lib/setup/syncProjectsRender.js.map +1 -0
  80. package/dist/lib/setup/ui/diff.d.ts +19 -0
  81. package/dist/lib/setup/ui/diff.d.ts.map +1 -0
  82. package/dist/lib/setup/ui/diff.js +34 -0
  83. package/dist/lib/setup/ui/diff.js.map +1 -0
  84. package/dist/lib/setup/ui/footer.d.ts +11 -0
  85. package/dist/lib/setup/ui/footer.d.ts.map +1 -0
  86. package/dist/lib/setup/ui/footer.js +21 -0
  87. package/dist/lib/setup/ui/footer.js.map +1 -0
  88. package/dist/lib/setup/ui/header.d.ts +24 -0
  89. package/dist/lib/setup/ui/header.d.ts.map +1 -0
  90. package/dist/lib/setup/ui/header.js +49 -0
  91. package/dist/lib/setup/ui/header.js.map +1 -0
  92. package/dist/lib/setup/ui/index.d.ts +28 -0
  93. package/dist/lib/setup/ui/index.d.ts.map +1 -0
  94. package/dist/lib/setup/ui/index.js +19 -0
  95. package/dist/lib/setup/ui/index.js.map +1 -0
  96. package/dist/lib/setup/ui/menu.d.ts +27 -0
  97. package/dist/lib/setup/ui/menu.d.ts.map +1 -0
  98. package/dist/lib/setup/ui/menu.js +75 -0
  99. package/dist/lib/setup/ui/menu.js.map +1 -0
  100. package/dist/lib/setup/ui/panel.d.ts +23 -0
  101. package/dist/lib/setup/ui/panel.d.ts.map +1 -0
  102. package/dist/lib/setup/ui/panel.js +60 -0
  103. package/dist/lib/setup/ui/panel.js.map +1 -0
  104. package/dist/lib/setup/ui/prompt.d.ts +27 -0
  105. package/dist/lib/setup/ui/prompt.d.ts.map +1 -0
  106. package/dist/lib/setup/ui/prompt.js +38 -0
  107. package/dist/lib/setup/ui/prompt.js.map +1 -0
  108. package/dist/lib/setup/ui/safePrompt.d.ts +24 -0
  109. package/dist/lib/setup/ui/safePrompt.d.ts.map +1 -0
  110. package/dist/lib/setup/ui/safePrompt.js +71 -0
  111. package/dist/lib/setup/ui/safePrompt.js.map +1 -0
  112. package/dist/lib/setup/ui/spinner.d.ts +28 -0
  113. package/dist/lib/setup/ui/spinner.d.ts.map +1 -0
  114. package/dist/lib/setup/ui/spinner.js +87 -0
  115. package/dist/lib/setup/ui/spinner.js.map +1 -0
  116. package/dist/lib/setup/ui/status.d.ts +21 -0
  117. package/dist/lib/setup/ui/status.d.ts.map +1 -0
  118. package/dist/lib/setup/ui/status.js +49 -0
  119. package/dist/lib/setup/ui/status.js.map +1 -0
  120. package/dist/lib/setup/ui/table.d.ts +37 -0
  121. package/dist/lib/setup/ui/table.d.ts.map +1 -0
  122. package/dist/lib/setup/ui/table.js +82 -0
  123. package/dist/lib/setup/ui/table.js.map +1 -0
  124. package/dist/lib/setup/ui/title.d.ts +14 -0
  125. package/dist/lib/setup/ui/title.d.ts.map +1 -0
  126. package/dist/lib/setup/ui/title.js +24 -0
  127. package/dist/lib/setup/ui/title.js.map +1 -0
  128. package/dist/lib/setup/ui/tokens.d.ts +66 -0
  129. package/dist/lib/setup/ui/tokens.d.ts.map +1 -0
  130. package/dist/lib/setup/ui/tokens.js +87 -0
  131. package/dist/lib/setup/ui/tokens.js.map +1 -0
  132. package/dist/lib/setup.d.ts +38 -0
  133. package/dist/lib/setup.d.ts.map +1 -1
  134. package/dist/lib/setup.js +440 -308
  135. package/dist/lib/setup.js.map +1 -1
  136. 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
- * v5.4.3 upgrade nudge runs once per CLI invocation. If the installed
76
- * version differs from the last-seen one stored in central DB meta, print
77
- * a one-line stderr nudge so the user knows to run `gnosys upgrade`.
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 maybePrintUpgradeNudge() {
92
- if (process.env.GNOSYS_SKIP_UPGRADE_NUDGE === "1")
93
- return;
94
- // Avoid printing the nudge when the user is already running upgrade.
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
- .action(async () => {
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
- console.error("No writable store found.");
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
- console.error(`Invalid provider: "${value}". Supported: ${validProviders}`);
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
- console.log(`Model set to: ${value} (for ${p})`);
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
- console.error("Usage: gnosys config set task <structuring|synthesis> <provider> <model>");
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
- console.error(`Invalid task: "${taskName}". Valid: structuring, synthesis`);
2986
+ printStatus("fail", `invalid task \`${taskName}\``, "valid: structuring, synthesis");
2962
2987
  process.exit(1);
2963
2988
  }
2964
2989
  if (!ALL_PROVIDERS.includes(taskProvider)) {
2965
- console.error(`Invalid provider: "${taskProvider}". Supported: ${validProviders}`);
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[taskName] = { provider: taskProvider, model: taskModel };
2971
- console.log(`Task "${taskName}" routed to: ${taskProvider}/${taskModel}`);
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
- console.error("Usage: gnosys config set recall <aggressive|maxMemories|minRelevance> <value>");
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
- console.error(`Invalid value: "${recallValue}". Use "true" or "false".`);
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
- console.error("maxMemories must be between 1 and 20");
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
- console.error("minRelevance must be between 0 and 1");
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
- console.error(`Unknown recall field: "${recallField}". Valid: aggressive, maxMemories, minRelevance`);
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
- console.log("Configuration saved to gnosys.json");
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 default gnosys.json with LLM settings")
3091
- .action(async () => {
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
- console.log(`Gnosys v${currentVersion}upgrading registered projects...\n`);
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("No registered projects found. Run 'gnosys init' in each project first.");
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
- console.log(` ✓ Global agent rules updated (~/.claude/CLAUDE.md)`);
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
- console.log(` ⚠ Could not update global agent rules`);
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
- if (upgraded.length > 0) {
3376
- console.log(`Upgraded (${upgraded.length}):`);
3377
- for (const p of upgraded)
3378
- console.log(` ✓ ${path.basename(p)} — ${p}`);
3379
- }
3380
- if (skipped.length > 0) {
3381
- console.log(`\nSkipped — no .gnosys directory (${skipped.length}):`);
3382
- for (const p of skipped)
3383
- console.log(` ○ ${path.basename(p)} — ${p} (run 'gnosys init' in this directory)`);
3384
- }
3385
- if (failed.length > 0) {
3386
- console.log(`\nFailed (${failed.length}):`);
3387
- for (const f of failed)
3388
- console.log(` ✗ ${f}`);
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
- if (entries.length > 1) {
3400
- console.log(`\nConnected machines:`);
3401
- for (const [host, info] of entries) {
3402
- const isCurrent = host === os.hostname();
3403
- const status = info.version === currentVersion ? "✓" : `⚠ v${info.version}`;
3404
- console.log(` ${status} ${host}${isCurrent ? " (this machine)" : ""} last seen ${info.lastSeen.split("T")[0]}`);
3405
- }
3406
- const behind = entries.filter(([, info]) => info.version !== currentVersion);
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 (skipped.length > 0) {
3419
- console.log(`\nNote: ${skipped.length} project(s) skipped — no .gnosys directory found.`);
3420
- console.log(`Run 'gnosys init' in each directory to set them up, or remove them from the registry.`);
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(`\nPortfolio dashboard regenerated:`);
3436
- console.log(` HTML: ${dashboardPath}`);
3437
- console.log(` MD: ${dashboardMdPath}`);
3556
+ console.log("");
3557
+ for (const line of renderDashboardSummary(dashboardPath, dashboardMdPath)) {
3558
+ console.log(line);
3559
+ }
3438
3560
  }
3439
3561
  }
3440
3562
  catch {
3441
- console.log(`\n Could not regenerate portfolio dashboard`);
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
- try {
6269
- const centralDb = GnosysDB.openCentral();
6270
- if (centralDb.isAvailable()) {
6271
- const lastVersion = centralDb.getMeta("app_version");
6272
- const currentVersion = pkg.version;
6273
- const isUpgradeCmd = process.argv.slice(2).some(a => a === "upgrade");
6274
- const isSetupSyncCmd = process.argv.slice(2).join(" ").includes("setup sync-projects");
6275
- // CRITICAL: `serve` writes JSON-RPC to stdout for MCP transport. Any
6276
- // console.log during boot corrupts the protocol and the host (Grok, Codex,
6277
- // etc.) sees the server as [unavailable]. Suppress the nag in serve mode.
6278
- const isServeCmd = process.argv.slice(2).some(a => a === "serve");
6279
- const newer = lastVersion && compareSemver(currentVersion, lastVersion) > 0;
6280
- if (newer && !isUpgradeCmd && !isSetupSyncCmd && !isServeCmd) {
6281
- console.log("");
6282
- console.log(` Gnosys updated: v${lastVersion} v${currentVersion}`);
6283
- console.log("");
6284
- console.log(" Run now to refresh the central DB stamp and registered projects:");
6285
- console.log(" gnosys setup sync-projects");
6286
- console.log("");
6287
- console.log(" Then restart MCP servers:");
6288
- console.log(" Cursor: Cmd+Shift+P > MCP: Restart All Servers");
6289
- console.log(" Claude Code: /mcp > restart gnosys (or start new session)");
6290
- console.log(" Codex: start new session");
6291
- console.log("");
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
- catch {
6297
- // non-critical — don't block CLI startup
6459
+ catch {
6460
+ // non-critical — don't block CLI startup
6461
+ }
6298
6462
  }
6299
6463
  program.parse();
6300
6464
  //# sourceMappingURL=cli.js.map