gnosys 5.11.3 → 5.12.0

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 (244) hide show
  1. package/dist/cli.js +332 -5151
  2. package/dist/index.js +364 -235
  3. package/dist/lib/addCommand.d.ts +9 -0
  4. package/dist/lib/addCommand.js +103 -0
  5. package/dist/lib/addStructuredCommand.d.ts +16 -0
  6. package/dist/lib/addStructuredCommand.js +103 -0
  7. package/dist/lib/ambiguityCommand.d.ts +4 -0
  8. package/dist/lib/ambiguityCommand.js +36 -0
  9. package/dist/lib/apiKeyVault.d.ts +78 -0
  10. package/dist/lib/apiKeyVault.js +447 -0
  11. package/dist/lib/askCommand.d.ts +13 -0
  12. package/dist/lib/askCommand.js +145 -0
  13. package/dist/lib/audioExtract.js +4 -1
  14. package/dist/lib/auditCommand.d.ts +7 -0
  15. package/dist/lib/auditCommand.js +27 -0
  16. package/dist/lib/backupCommand.d.ts +6 -0
  17. package/dist/lib/backupCommand.js +54 -0
  18. package/dist/lib/bootstrapCommand.d.ts +15 -0
  19. package/dist/lib/bootstrapCommand.js +51 -0
  20. package/dist/lib/briefingCommand.d.ts +7 -0
  21. package/dist/lib/briefingCommand.js +92 -0
  22. package/dist/lib/centralizeCommand.d.ts +5 -0
  23. package/dist/lib/centralizeCommand.js +16 -0
  24. package/dist/lib/chatCommand.d.ts +12 -0
  25. package/dist/lib/chatCommand.js +46 -0
  26. package/dist/lib/checkCommand.d.ts +4 -0
  27. package/dist/lib/checkCommand.js +133 -0
  28. package/dist/lib/clientReadOverlay.d.ts +27 -0
  29. package/dist/lib/clientReadOverlay.js +73 -0
  30. package/dist/lib/clientReadResolve.d.ts +32 -0
  31. package/dist/lib/clientReadResolve.js +84 -0
  32. package/dist/lib/commitContextCommand.d.ts +9 -0
  33. package/dist/lib/commitContextCommand.js +142 -0
  34. package/dist/lib/config.d.ts +43 -3
  35. package/dist/lib/config.js +58 -57
  36. package/dist/lib/configCommand.d.ts +10 -0
  37. package/dist/lib/configCommand.js +321 -0
  38. package/dist/lib/connectCommand.d.ts +8 -0
  39. package/dist/lib/connectCommand.js +19 -0
  40. package/dist/lib/db.d.ts +52 -0
  41. package/dist/lib/db.js +169 -1
  42. package/dist/lib/dearchiveCommand.d.ts +7 -0
  43. package/dist/lib/dearchiveCommand.js +41 -0
  44. package/dist/lib/discoverCommand.d.ts +9 -0
  45. package/dist/lib/discoverCommand.js +87 -0
  46. package/dist/lib/doctorCommand.d.ts +6 -0
  47. package/dist/lib/doctorCommand.js +256 -0
  48. package/dist/lib/dream.d.ts +42 -2
  49. package/dist/lib/dream.js +290 -30
  50. package/dist/lib/dreamCommand.d.ts +10 -0
  51. package/dist/lib/dreamCommand.js +195 -0
  52. package/dist/lib/dreamLaunchd.d.ts +2 -0
  53. package/dist/lib/dreamLaunchd.js +72 -0
  54. package/dist/lib/dreamLogCommand.d.ts +10 -0
  55. package/dist/lib/dreamLogCommand.js +58 -0
  56. package/dist/lib/dreamReport.d.ts +7 -0
  57. package/dist/lib/dreamReport.js +114 -0
  58. package/dist/lib/dreamRunLog.d.ts +121 -0
  59. package/dist/lib/dreamRunLog.js +212 -0
  60. package/dist/lib/embeddings.js +3 -0
  61. package/dist/lib/exportCommand.d.ts +18 -0
  62. package/dist/lib/exportCommand.js +101 -0
  63. package/dist/lib/fsearchCommand.d.ts +8 -0
  64. package/dist/lib/fsearchCommand.js +44 -0
  65. package/dist/lib/graphCommand.d.ts +4 -0
  66. package/dist/lib/graphCommand.js +68 -0
  67. package/dist/lib/helperGenerateCommand.d.ts +5 -0
  68. package/dist/lib/helperGenerateCommand.js +27 -0
  69. package/dist/lib/historyCommand.d.ts +5 -0
  70. package/dist/lib/historyCommand.js +51 -0
  71. package/dist/lib/hybridSearchCommand.d.ts +12 -0
  72. package/dist/lib/hybridSearchCommand.js +95 -0
  73. package/dist/lib/ideMcpInstall.d.ts +30 -0
  74. package/dist/lib/ideMcpInstall.js +102 -0
  75. package/dist/lib/importCommand.d.ts +16 -0
  76. package/dist/lib/importCommand.js +89 -0
  77. package/dist/lib/importProjectCommand.d.ts +6 -0
  78. package/dist/lib/importProjectCommand.js +43 -0
  79. package/dist/lib/ingestCommand.d.ts +13 -0
  80. package/dist/lib/ingestCommand.js +95 -0
  81. package/dist/lib/installOutput.d.ts +36 -0
  82. package/dist/lib/installOutput.js +55 -0
  83. package/dist/lib/lensCommand.d.ts +20 -0
  84. package/dist/lib/lensCommand.js +61 -0
  85. package/dist/lib/lensing.d.ts +1 -0
  86. package/dist/lib/lensing.js +50 -9
  87. package/dist/lib/linksCommand.d.ts +7 -0
  88. package/dist/lib/linksCommand.js +48 -0
  89. package/dist/lib/listCommand.d.ts +8 -0
  90. package/dist/lib/listCommand.js +74 -0
  91. package/dist/lib/llm.d.ts +1 -1
  92. package/dist/lib/llm.js +26 -8
  93. package/dist/lib/localDiskCheck.d.ts +17 -0
  94. package/dist/lib/localDiskCheck.js +54 -0
  95. package/dist/lib/machineConfig.d.ts +11 -1
  96. package/dist/lib/machineConfig.js +16 -0
  97. package/dist/lib/machineRegistry.d.ts +61 -0
  98. package/dist/lib/machineRegistry.js +80 -0
  99. package/dist/lib/maintainCommand.d.ts +8 -0
  100. package/dist/lib/maintainCommand.js +34 -0
  101. package/dist/lib/masterLease.d.ts +20 -0
  102. package/dist/lib/masterLease.js +68 -0
  103. package/dist/lib/migrateCommand.d.ts +7 -0
  104. package/dist/lib/migrateCommand.js +158 -0
  105. package/dist/lib/migrateDbCommand.d.ts +9 -0
  106. package/dist/lib/migrateDbCommand.js +94 -0
  107. package/dist/lib/modelValidation.d.ts +5 -0
  108. package/dist/lib/modelValidation.js +27 -0
  109. package/dist/lib/openrouterTiers.d.ts +29 -0
  110. package/dist/lib/openrouterTiers.js +113 -0
  111. package/dist/lib/prefCommand.d.ts +10 -0
  112. package/dist/lib/prefCommand.js +118 -0
  113. package/dist/lib/projectsCommand.d.ts +8 -0
  114. package/dist/lib/projectsCommand.js +131 -0
  115. package/dist/lib/readCommand.d.ts +7 -0
  116. package/dist/lib/readCommand.js +62 -0
  117. package/dist/lib/recall.d.ts +3 -0
  118. package/dist/lib/recall.js +19 -4
  119. package/dist/lib/recallCommand.d.ts +11 -0
  120. package/dist/lib/recallCommand.js +112 -0
  121. package/dist/lib/reflectCommand.d.ts +8 -0
  122. package/dist/lib/reflectCommand.js +61 -0
  123. package/dist/lib/reindexCommand.d.ts +4 -0
  124. package/dist/lib/reindexCommand.js +34 -0
  125. package/dist/lib/reindexGraphCommand.d.ts +4 -0
  126. package/dist/lib/reindexGraphCommand.js +12 -0
  127. package/dist/lib/reinforceCommand.d.ts +8 -0
  128. package/dist/lib/reinforceCommand.js +40 -0
  129. package/dist/lib/remote.d.ts +5 -1
  130. package/dist/lib/remote.js +5 -1
  131. package/dist/lib/remoteWizard.d.ts +24 -5
  132. package/dist/lib/remoteWizard.js +308 -319
  133. package/dist/lib/restoreCommand.d.ts +5 -0
  134. package/dist/lib/restoreCommand.js +35 -0
  135. package/dist/lib/sandboxStartCommand.d.ts +6 -0
  136. package/dist/lib/sandboxStartCommand.js +25 -0
  137. package/dist/lib/sandboxStatusCommand.d.ts +4 -0
  138. package/dist/lib/sandboxStatusCommand.js +24 -0
  139. package/dist/lib/sandboxStopCommand.d.ts +4 -0
  140. package/dist/lib/sandboxStopCommand.js +21 -0
  141. package/dist/lib/searchCommand.d.ts +9 -0
  142. package/dist/lib/searchCommand.js +90 -0
  143. package/dist/lib/semanticSearchCommand.d.ts +8 -0
  144. package/dist/lib/semanticSearchCommand.js +52 -0
  145. package/dist/lib/setup/configSetRender.js +2 -0
  146. package/dist/lib/setup/providerGlyphs.d.ts +19 -0
  147. package/dist/lib/setup/providerGlyphs.js +42 -0
  148. package/dist/lib/setup/remoteRender.d.ts +31 -1
  149. package/dist/lib/setup/remoteRender.js +95 -4
  150. package/dist/lib/setup/sections/ides.d.ts +7 -0
  151. package/dist/lib/setup/sections/ides.js +24 -2
  152. package/dist/lib/setup/sections/providers.d.ts +17 -0
  153. package/dist/lib/setup/sections/providers.js +255 -0
  154. package/dist/lib/setup/sections/routing.d.ts +2 -6
  155. package/dist/lib/setup/sections/routing.js +33 -85
  156. package/dist/lib/setup/sections/taskRoutingEditor.d.ts +17 -0
  157. package/dist/lib/setup/sections/taskRoutingEditor.js +149 -0
  158. package/dist/lib/setup/summary.d.ts +9 -0
  159. package/dist/lib/setup/summary.js +51 -37
  160. package/dist/lib/setup/ui/status.d.ts +1 -0
  161. package/dist/lib/setup/ui/status.js +2 -0
  162. package/dist/lib/setup.d.ts +108 -3
  163. package/dist/lib/setup.js +813 -303
  164. package/dist/lib/setupKeys.d.ts +42 -0
  165. package/dist/lib/setupKeys.js +564 -0
  166. package/dist/lib/setupRemoteCommand.d.ts +4 -0
  167. package/dist/lib/setupRemoteCommand.js +28 -0
  168. package/dist/lib/setupRemotePullCommand.d.ts +5 -0
  169. package/dist/lib/setupRemotePullCommand.js +52 -0
  170. package/dist/lib/setupRemotePushCommand.d.ts +5 -0
  171. package/dist/lib/setupRemotePushCommand.js +57 -0
  172. package/dist/lib/setupRemoteResolveCommand.d.ts +4 -0
  173. package/dist/lib/setupRemoteResolveCommand.js +48 -0
  174. package/dist/lib/setupRemoteStatusCommand.d.ts +4 -0
  175. package/dist/lib/setupRemoteStatusCommand.js +73 -0
  176. package/dist/lib/setupRemoteSyncCommand.d.ts +6 -0
  177. package/dist/lib/setupRemoteSyncCommand.js +65 -0
  178. package/dist/lib/setupSyncProjectsCommand.d.ts +4 -0
  179. package/dist/lib/setupSyncProjectsCommand.js +292 -0
  180. package/dist/lib/staleCommand.d.ts +8 -0
  181. package/dist/lib/staleCommand.js +34 -0
  182. package/dist/lib/statsCommand.d.ts +6 -0
  183. package/dist/lib/statsCommand.js +142 -0
  184. package/dist/lib/statusCommand.d.ts +18 -0
  185. package/dist/lib/statusCommand.js +250 -0
  186. package/dist/lib/storesCommand.d.ts +2 -0
  187. package/dist/lib/storesCommand.js +4 -0
  188. package/dist/lib/syncClient.d.ts +47 -0
  189. package/dist/lib/syncClient.js +212 -0
  190. package/dist/lib/syncCommand.d.ts +6 -0
  191. package/dist/lib/syncCommand.js +57 -0
  192. package/dist/lib/syncDoctorCommand.d.ts +5 -0
  193. package/dist/lib/syncDoctorCommand.js +100 -0
  194. package/dist/lib/syncIngest.d.ts +19 -0
  195. package/dist/lib/syncIngest.js +152 -0
  196. package/dist/lib/syncIngestLaunchd.d.ts +8 -0
  197. package/dist/lib/syncIngestLaunchd.js +93 -0
  198. package/dist/lib/syncIngestStartup.d.ts +5 -0
  199. package/dist/lib/syncIngestStartup.js +29 -0
  200. package/dist/lib/syncIngestSystemd.d.ts +10 -0
  201. package/dist/lib/syncIngestSystemd.js +97 -0
  202. package/dist/lib/syncIngestTimer.d.ts +8 -0
  203. package/dist/lib/syncIngestTimer.js +27 -0
  204. package/dist/lib/syncIngestTimerCommand.d.ts +7 -0
  205. package/dist/lib/syncIngestTimerCommand.js +83 -0
  206. package/dist/lib/syncLock.d.ts +6 -0
  207. package/dist/lib/syncLock.js +74 -0
  208. package/dist/lib/syncSnapshot.d.ts +30 -0
  209. package/dist/lib/syncSnapshot.js +184 -0
  210. package/dist/lib/syncStaging.d.ts +81 -0
  211. package/dist/lib/syncStaging.js +239 -0
  212. package/dist/lib/tagsAddCommand.d.ts +8 -0
  213. package/dist/lib/tagsAddCommand.js +18 -0
  214. package/dist/lib/tagsCommand.d.ts +4 -0
  215. package/dist/lib/tagsCommand.js +16 -0
  216. package/dist/lib/timelineCommand.d.ts +7 -0
  217. package/dist/lib/timelineCommand.js +49 -0
  218. package/dist/lib/traceCommand.d.ts +6 -0
  219. package/dist/lib/traceCommand.js +39 -0
  220. package/dist/lib/traverseCommand.d.ts +6 -0
  221. package/dist/lib/traverseCommand.js +58 -0
  222. package/dist/lib/updateCommand.d.ts +13 -0
  223. package/dist/lib/updateCommand.js +67 -0
  224. package/dist/lib/updateStatusCommand.d.ts +5 -0
  225. package/dist/lib/updateStatusCommand.js +38 -0
  226. package/dist/lib/webAddCommand.d.ts +8 -0
  227. package/dist/lib/webAddCommand.js +55 -0
  228. package/dist/lib/webBuildCommand.d.ts +10 -0
  229. package/dist/lib/webBuildCommand.js +65 -0
  230. package/dist/lib/webBuildIndexCommand.d.ts +8 -0
  231. package/dist/lib/webBuildIndexCommand.js +37 -0
  232. package/dist/lib/webIngestCommand.d.ts +11 -0
  233. package/dist/lib/webIngestCommand.js +51 -0
  234. package/dist/lib/webInitCommand.d.ts +9 -0
  235. package/dist/lib/webInitCommand.js +167 -0
  236. package/dist/lib/webRemoveCommand.d.ts +5 -0
  237. package/dist/lib/webRemoveCommand.js +41 -0
  238. package/dist/lib/webStatusCommand.d.ts +5 -0
  239. package/dist/lib/webStatusCommand.js +94 -0
  240. package/dist/lib/webUpdateCommand.d.ts +7 -0
  241. package/dist/lib/webUpdateCommand.js +72 -0
  242. package/dist/lib/workingSetCommand.d.ts +6 -0
  243. package/dist/lib/workingSetCommand.js +37 -0
  244. package/package.json +2 -1
@@ -0,0 +1,255 @@
1
+ /**
2
+ * Setup: Providers — API keys and provider credentials.
3
+ *
4
+ * `gnosys setup providers` or summary menu row "providers".
5
+ */
6
+ import { loadConfig, updateConfig, } from "../../config.js";
7
+ import { apiKeyServiceName, deleteStoredSecret, listStoredKeySlots, maskKeySnippet, storeApiKeySecret, } from "../../apiKeyVault.js";
8
+ import { resolveActiveStorePath } from "../storePath.js";
9
+ import { safeQuestion } from "../ui/safePrompt.js";
10
+ import { Header } from "../ui/header.js";
11
+ import { Title } from "../ui/title.js";
12
+ import { Footer } from "../ui/footer.js";
13
+ import { printStatus } from "../ui/status.js";
14
+ import { c, color, glyph } from "../ui/tokens.js";
15
+ import { CLOUD_PROVIDERS_FOR_KEYS, LOCAL_PROVIDERS, renderProviderLabel, renderProviderMark, } from "../providerGlyphs.js";
16
+ async function ask(rl, prompt) {
17
+ return (await safeQuestion(rl, prompt)).trim();
18
+ }
19
+ async function askYesNo(rl, prompt, defaultYes = true) {
20
+ const hint = defaultYes ? " [Y/n] " : " [y/N] ";
21
+ const answer = (await ask(rl, prompt + hint)).toLowerCase();
22
+ if (!answer)
23
+ return defaultYes;
24
+ return answer === "y" || answer === "yes";
25
+ }
26
+ async function askChoice(rl, prompt, choices, defaultIdx = 0) {
27
+ console.log("");
28
+ if (prompt)
29
+ console.log(prompt);
30
+ choices.forEach((ch, i) => {
31
+ const marker = i === defaultIdx ? color(c.textDim, " (default)") : "";
32
+ console.log(` ${i + 1}. ${ch}${marker}`);
33
+ });
34
+ for (let attempts = 0; attempts < 5; attempts++) {
35
+ const answer = await ask(rl, ` ${color(c.accent, glyph.prompt)} `);
36
+ if (!answer)
37
+ return defaultIdx;
38
+ const n = parseInt(answer, 10);
39
+ if (!Number.isNaN(n) && n >= 1 && n <= choices.length)
40
+ return n - 1;
41
+ printStatus("warn", `pick a number 1–${choices.length}`);
42
+ }
43
+ return defaultIdx;
44
+ }
45
+ function slotScopeLabel(slot) {
46
+ if (slot.scope === "global")
47
+ return "global";
48
+ return "provider";
49
+ }
50
+ async function clearConfigProviderApiKey(storePath, provider) {
51
+ const cfg = await loadConfig(storePath);
52
+ const patch = {};
53
+ switch (provider) {
54
+ case "anthropic":
55
+ if (!cfg.llm.anthropic.apiKey)
56
+ return;
57
+ patch.anthropic = { ...cfg.llm.anthropic, apiKey: "" };
58
+ break;
59
+ case "openai":
60
+ if (!cfg.llm.openai.apiKey)
61
+ return;
62
+ patch.openai = { ...cfg.llm.openai, apiKey: "" };
63
+ break;
64
+ case "groq":
65
+ if (!cfg.llm.groq.apiKey)
66
+ return;
67
+ patch.groq = { ...cfg.llm.groq, apiKey: "" };
68
+ break;
69
+ case "xai":
70
+ if (!cfg.llm.xai.apiKey)
71
+ return;
72
+ patch.xai = { ...cfg.llm.xai, apiKey: "" };
73
+ break;
74
+ case "mistral":
75
+ if (!cfg.llm.mistral.apiKey)
76
+ return;
77
+ patch.mistral = { ...cfg.llm.mistral, apiKey: "" };
78
+ break;
79
+ case "openrouter":
80
+ if (!cfg.llm.openrouter.apiKey)
81
+ return;
82
+ patch.openrouter = { ...cfg.llm.openrouter, apiKey: "" };
83
+ break;
84
+ case "custom":
85
+ if (!cfg.llm.custom?.apiKey)
86
+ return;
87
+ patch.custom = { ...cfg.llm.custom, apiKey: "" };
88
+ break;
89
+ default:
90
+ return;
91
+ }
92
+ await updateConfig(storePath, { llm: patch });
93
+ }
94
+ function renderProviderRow(index, provider, cfg, local = false) {
95
+ const mark = renderProviderMark(provider);
96
+ const slots = listStoredKeySlots(cfg, provider);
97
+ const hasKey = slots.length > 0;
98
+ const status = local
99
+ ? color(c.textDim, "local · no key")
100
+ : hasKey
101
+ ? color(c.ok, `${glyph.ok} key`)
102
+ : color(c.textGhost, "no key");
103
+ const preview = hasKey && !local ? ` ${color(c.textDim, slots[0].preview ?? "")}` : "";
104
+ const num = color(c.textDim, String(index));
105
+ const name = color(c.text, provider.padEnd(12));
106
+ return ` ${num} ${mark} ${name} ${status}${preview}`;
107
+ }
108
+ async function manageProviderKeys(rl, storePath, cfg, provider) {
109
+ let changed = false;
110
+ while (true) {
111
+ const slots = listStoredKeySlots(cfg, provider);
112
+ console.log("");
113
+ console.log(Header(["gnosys", "setup", "providers", provider]));
114
+ console.log("");
115
+ console.log(Title(renderProviderLabel(provider), "stored credentials for this provider"));
116
+ console.log("");
117
+ if (slots.length === 0) {
118
+ console.log(` ${color(c.textDim, "No keys found for this provider.")}`);
119
+ console.log(` ${color(c.textDim, `Add one with rotate — saved as GNOSYS_GLOBAL_${provider.toUpperCase()}_KEY`)}`);
120
+ console.log("");
121
+ }
122
+ else {
123
+ for (const slot of slots) {
124
+ const scope = color(c.textMid, slotScopeLabel(slot).padEnd(14));
125
+ const src = color(c.textDim, slot.source.padEnd(22));
126
+ const prev = slot.preview ? color(c.text, slot.preview) : "";
127
+ console.log(` ${scope} ${src} ${prev}`);
128
+ console.log(` ${color(c.textGhost, " " + slot.service)}`);
129
+ }
130
+ console.log("");
131
+ }
132
+ const choice = await askChoice(rl, "Actions", [
133
+ slots.length === 0 ? "Add API key (global)" : "Rotate global API key",
134
+ "Delete one stored key…",
135
+ "Back",
136
+ ], 2);
137
+ if (choice === 2) {
138
+ return changed;
139
+ }
140
+ if (choice === 0) {
141
+ const service = apiKeyServiceName(provider, "global");
142
+ console.log("");
143
+ console.log(` ${color(c.textDim, `Keychain: ${service}`)}`);
144
+ const key = await ask(rl, ` ${color(c.accent, glyph.prompt)} Enter ${provider} API key: `);
145
+ if (!key) {
146
+ printStatus("warn", "skipped");
147
+ continue;
148
+ }
149
+ if (storeApiKeySecret(service, key, provider)) {
150
+ const store = process.platform === "darwin" ? "macOS Keychain" : "GNOME Keyring";
151
+ printStatus("ok", "key saved", `${store} · ${maskKeySnippet(key)}`);
152
+ changed = true;
153
+ }
154
+ else {
155
+ printStatus("fail", "could not write to secure store");
156
+ }
157
+ continue;
158
+ }
159
+ if (choice === 1) {
160
+ if (slots.length === 0) {
161
+ printStatus("warn", "nothing to delete");
162
+ continue;
163
+ }
164
+ const labels = slots.map((s, i) => `${i + 1}. ${slotScopeLabel(s)} · ${s.service}`);
165
+ const pick = await askChoice(rl, "Delete which key?", [...labels, "Cancel"], labels.length);
166
+ if (pick >= slots.length)
167
+ continue;
168
+ const slot = slots[pick];
169
+ if (slot.service === "gnosys.json") {
170
+ if (await askYesNo(rl, "Clear API key from gnosys.json?", false)) {
171
+ await clearConfigProviderApiKey(storePath, provider);
172
+ printStatus("ok", "cleared gnosys.json apiKey");
173
+ changed = true;
174
+ cfg = await loadConfig(storePath);
175
+ }
176
+ continue;
177
+ }
178
+ if (slot.source.startsWith("$")) {
179
+ printStatus("warn", `unset ${slot.service} in your shell or ~/.config/gnosys/.env`, "env vars cannot be removed from here");
180
+ continue;
181
+ }
182
+ if (await askYesNo(rl, `Delete ${slot.service} from secure store?`, false)) {
183
+ if (deleteStoredSecret(slot.service)) {
184
+ printStatus("ok", "deleted", slot.service);
185
+ changed = true;
186
+ }
187
+ else {
188
+ printStatus("fail", "delete failed or entry not found");
189
+ }
190
+ }
191
+ continue;
192
+ }
193
+ }
194
+ }
195
+ /**
196
+ * Providers + API key management screen.
197
+ */
198
+ export async function runProvidersSetup(opts) {
199
+ const storePath = resolveActiveStorePath(opts.directory);
200
+ let cfg = await loadConfig(storePath);
201
+ let anyChange = false;
202
+ const menuProviders = [
203
+ ...CLOUD_PROVIDERS_FOR_KEYS,
204
+ ...LOCAL_PROVIDERS,
205
+ ];
206
+ while (true) {
207
+ console.log("");
208
+ console.log(Header(["gnosys", "setup", "providers"]));
209
+ console.log("");
210
+ console.log(Title("Providers", "API keys live here · pick task routing separately for models per task"));
211
+ console.log("");
212
+ for (let i = 0; i < menuProviders.length; i++) {
213
+ const p = menuProviders[i];
214
+ const local = LOCAL_PROVIDERS.includes(p);
215
+ console.log(renderProviderRow(i + 1, p, cfg, local));
216
+ }
217
+ console.log("");
218
+ console.log(` ${color(c.textDim, "Keys: global = GNOSYS_GLOBAL_<PROVIDER>_KEY (shared across tasks)")}`);
219
+ console.log(Footer("number · manage enter · back"));
220
+ const answer = await ask(opts.rl, ` ${color(c.accent, glyph.prompt)} `);
221
+ if (!answer) {
222
+ return anyChange;
223
+ }
224
+ const n = parseInt(answer, 10);
225
+ if (Number.isNaN(n) || n < 1 || n > menuProviders.length) {
226
+ printStatus("warn", `enter 1–${menuProviders.length} or press Enter to go back`);
227
+ continue;
228
+ }
229
+ const provider = menuProviders[n - 1];
230
+ if (LOCAL_PROVIDERS.includes(provider)) {
231
+ console.log("");
232
+ printStatus("ok", `${provider} is local`, "no API key required");
233
+ continue;
234
+ }
235
+ const changed = await manageProviderKeys(opts.rl, storePath, cfg, provider);
236
+ if (changed) {
237
+ anyChange = true;
238
+ cfg = await loadConfig(storePath);
239
+ }
240
+ }
241
+ }
242
+ /** Summary line: how many providers have keys. */
243
+ export async function describeProvidersSummary(cfg) {
244
+ const named = [];
245
+ for (const p of CLOUD_PROVIDERS_FOR_KEYS) {
246
+ if (listStoredKeySlots(cfg, p).length > 0) {
247
+ named.push(p);
248
+ }
249
+ }
250
+ if (named.length === 0)
251
+ return "no keys stored";
252
+ const preview = named.slice(0, 3).join(", ");
253
+ const suffix = named.length > 3 ? ` +${named.length - 3}` : "";
254
+ return `${named.length} with keys · ${preview}${suffix}`;
255
+ }
@@ -2,9 +2,7 @@
2
2
  * Setup: Task Routing.
3
3
  *
4
4
  * Standalone wizard for configuring per-task LLM routing
5
- * (structuring / synthesis / vision / transcription / dream).
6
- * Extracted from the linear `runSetup` flow so it can be invoked
7
- * directly via `gnosys setup routing` or from the summary-first menu.
5
+ * (structuring / synthesis / vision / transcription / chat / dream).
8
6
  */
9
7
  import type { Interface as ReadlineInterface } from "readline/promises";
10
8
  export interface RoutingOptions {
@@ -12,8 +10,6 @@ export interface RoutingOptions {
12
10
  directory: string;
13
11
  }
14
12
  /**
15
- * Run the task-routing wizard. Reads current config, walks the 3-way choice
16
- * (keep defaults / customize individual / use same for all), writes any
17
- * overrides via updateConfig(). Returns true if config was changed.
13
+ * Run the task-routing wizard.
18
14
  */
19
15
  export declare function runRoutingSetup(opts: RoutingOptions): Promise<boolean>;
@@ -2,17 +2,18 @@
2
2
  * Setup: Task Routing.
3
3
  *
4
4
  * Standalone wizard for configuring per-task LLM routing
5
- * (structuring / synthesis / vision / transcription / dream).
6
- * Extracted from the linear `runSetup` flow so it can be invoked
7
- * directly via `gnosys setup routing` or from the summary-first menu.
5
+ * (structuring / synthesis / vision / transcription / chat / dream).
8
6
  */
9
- import { loadConfig, updateConfig, resolveTaskModel, getProviderModel, } from "../../config.js";
7
+ import { loadConfig, updateConfig, getProviderModel, } from "../../config.js";
8
+ import { buildApiKeyRequirementsFromConfig, buildEffectiveRouting, ensureApiKeys, } from "../../apiKeyVault.js";
10
9
  import { safeQuestion } from "../ui/safePrompt.js";
11
10
  import { Header } from "../ui/header.js";
12
11
  import { Title } from "../ui/title.js";
13
12
  import { Footer } from "../ui/footer.js";
14
13
  import { printStatus } from "../ui/status.js";
15
14
  import { classifyCost, renderRoutingTable, renderRoutingDiff, } from "../routingRender.js";
15
+ import { runCommaListRoutingEditor } from "./taskRoutingEditor.js";
16
+ import { resolveActiveStorePath } from "../storePath.js";
16
17
  const DIM = "\x1b[2m";
17
18
  const RESET = "\x1b[0m";
18
19
  const TASKS = ["structuring", "synthesis", "chat", "vision", "transcription"];
@@ -45,23 +46,6 @@ async function askChoice(rl, prompt, choices, defaultIdx = 0) {
45
46
  }
46
47
  return defaultIdx;
47
48
  }
48
- function buildEffectiveRouting(cfg) {
49
- const out = {};
50
- for (const t of TASKS) {
51
- const r = resolveTaskModel(cfg, t);
52
- out[t] = { provider: r.provider, model: r.model };
53
- }
54
- out.dream = {
55
- provider: cfg.dream?.provider ?? "ollama",
56
- model: cfg.dream?.model ?? getProviderModel(cfg, (cfg.dream?.provider ?? "ollama")),
57
- };
58
- return out;
59
- }
60
- /**
61
- * Build the table-row payload from current routing data. Marks any
62
- * task whose effective provider/model differs from the snapshot as
63
- * `changed` so the renderer can highlight it (▶ in accent-hi).
64
- */
65
49
  function buildTaskRows(routing, baseline, dreamEnabled) {
66
50
  const rows = [];
67
51
  for (const t of [...TASKS, "dream"]) {
@@ -75,72 +59,33 @@ function buildTaskRows(routing, baseline, dreamEnabled) {
75
59
  return rows;
76
60
  }
77
61
  /**
78
- * Run the task-routing wizard. Reads current config, walks the 3-way choice
79
- * (keep defaults / customize individual / use same for all), writes any
80
- * overrides via updateConfig(). Returns true if config was changed.
62
+ * Run the task-routing wizard.
81
63
  */
82
64
  export async function runRoutingSetup(opts) {
83
- const cfg = await loadConfig(opts.directory);
65
+ const storePath = resolveActiveStorePath(opts.directory);
66
+ const cfg = await loadConfig(storePath);
84
67
  const provider = cfg.llm.defaultProvider;
85
68
  const model = getProviderModel(cfg, provider);
86
69
  console.log("");
87
70
  console.log(Header(["gnosys", "setup", "routing"]));
88
71
  console.log("");
89
- console.log(Title("Task routing", "each task can use a different model overrides the default"));
72
+ console.log(Title("Task routing", "pick provider + model per taskset API keys under setup providers"));
90
73
  console.log("");
91
74
  const dreamEnabled = !!cfg.dream?.enabled;
92
75
  const baseline = buildEffectiveRouting(cfg);
93
- // Initial table: nothing has changed yet, so every row is `changed: false`.
94
76
  const initialRows = buildTaskRows(baseline, baseline, dreamEnabled);
95
77
  console.log(renderRoutingTable(initialRows));
96
78
  console.log("");
97
- // v5.9.4 Bug 5 — clearer option copy. Option 1 keeps current routing
98
- // (skip, no changes). Option 2 customises individual tasks. Option 3
99
- // CLEARS all task overrides so every task falls back to the default
100
- // provider; we show a Diff() of what's being removed before committing.
101
79
  const choice = await askChoice(opts.rl, "What would you like to do?", [
102
80
  "Keep current routing (no changes)",
103
- "Customize individual tasks",
81
+ "Edit tasks — pick by number, then provider + model for each",
104
82
  "Reset all task overrides to use default",
105
83
  ], 0);
106
84
  if (choice === 0) {
107
85
  console.log(`${DIM}No changes.${RESET}`);
108
86
  return false;
109
87
  }
110
- const newTaskModels = {
111
- ...(cfg.taskModels ?? {}),
112
- };
113
- let dreamProvider = (cfg.dream?.provider ?? "ollama");
114
- let dreamModel = cfg.dream?.model ?? "llama3.2";
115
- let dreamEnabledNew = dreamEnabled;
116
- if (choice === 1) {
117
- // Customize each task
118
- for (const t of TASKS) {
119
- const current = baseline[t];
120
- const keep = await askYesNo(opts.rl, `Keep ${t} → ${current.provider} / ${current.model}?`, true);
121
- if (!keep) {
122
- const p = await ask(opts.rl, ` Provider for ${t} (e.g. anthropic, openai, xai, ollama): `);
123
- const m = await ask(opts.rl, ` Model for ${t}: `);
124
- if (p && m)
125
- newTaskModels[t] = { provider: p, model: m };
126
- }
127
- }
128
- // Dream
129
- dreamEnabledNew = await askYesNo(opts.rl, "Enable dream mode?", dreamEnabled);
130
- if (dreamEnabledNew) {
131
- const keepDream = await askYesNo(opts.rl, `Keep dream → ${dreamProvider} / ${dreamModel}?`, true);
132
- if (!keepDream) {
133
- const p = (await ask(opts.rl, " Provider for dream: "));
134
- if (p)
135
- dreamProvider = p;
136
- dreamModel = (await ask(opts.rl, " Model for dream: ")) || dreamModel;
137
- }
138
- }
139
- }
140
- else {
141
- // v5.9.4 Bug 5 — choice === 2 now means "reset all task overrides to use
142
- // default". Show the user what's about to be cleared (the rows currently
143
- // pinned to non-default providers) before committing.
88
+ if (choice === 2) {
144
89
  const { Diff } = await import("../ui/diff.js");
145
90
  const overridesBeingCleared = Object.entries(cfg.taskModels ?? {})
146
91
  .filter(([, v]) => v.provider !== provider || v.model !== model)
@@ -162,26 +107,22 @@ export async function runRoutingSetup(opts) {
162
107
  console.log(`${DIM}Cancelled.${RESET}`);
163
108
  return false;
164
109
  }
165
- // Clear every overridden task back to default by deleting the keys.
166
- for (const t of TASKS)
167
- delete newTaskModels[t];
110
+ await updateConfig(storePath, { taskModels: {} });
111
+ printStatus("ok", "routing reset", "all tasks use default provider/model");
112
+ console.log(Footer("press enter to return"));
113
+ return true;
168
114
  }
169
- // Persist
170
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
171
- await updateConfig(opts.directory, {
172
- taskModels: newTaskModels,
173
- dream: {
174
- ...(cfg.dream ?? {}),
175
- enabled: dreamEnabledNew,
176
- provider: dreamProvider,
177
- model: dreamModel,
178
- },
115
+ const patch = await runCommaListRoutingEditor(opts.rl, storePath, cfg);
116
+ if (!patch) {
117
+ return false;
118
+ }
119
+ await updateConfig(storePath, {
120
+ taskModels: patch.taskModels,
121
+ dream: patch.dream,
179
122
  });
180
- // v5.9.3 Screen 4 — re-render the table with `▶` markers on changed rows
181
- // followed by a Diff() block summarizing what shipped. Then a final
182
- // status line that names the saved file.
183
- const updatedCfg = await loadConfig(opts.directory);
123
+ const updatedCfg = await loadConfig(storePath);
184
124
  const updatedRouting = buildEffectiveRouting(updatedCfg);
125
+ const dreamEnabledNew = !!updatedCfg.dream?.enabled;
185
126
  const finalRows = buildTaskRows(updatedRouting, baseline, dreamEnabledNew);
186
127
  console.log("");
187
128
  console.log(renderRoutingTable(finalRows));
@@ -196,8 +137,15 @@ export async function runRoutingSetup(opts) {
196
137
  }
197
138
  console.log(renderRoutingDiff(diffEntries));
198
139
  console.log("");
199
- printStatus("ok", "routing saved", `${opts.directory}/.gnosys/gnosys.json`);
200
- // Footer hint (right-aligned) for any follow-up navigation in the menu flow.
140
+ printStatus("ok", "routing saved", `${storePath}/gnosys.json`);
141
+ const keyReqs = buildApiKeyRequirementsFromConfig(updatedCfg);
142
+ if (keyReqs.length > 0) {
143
+ const askInput = async (r, prompt) => ask(r, `${prompt}: `);
144
+ await ensureApiKeys(opts.rl, keyReqs, askInput, {
145
+ dim: DIM,
146
+ reset: RESET,
147
+ });
148
+ }
201
149
  console.log(Footer("press enter to return"));
202
150
  return true;
203
151
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Comma-list task routing editor (provider + model per selected task).
3
+ */
4
+ import type { Interface as ReadlineInterface } from "readline/promises";
5
+ import { type GnosysConfig } from "../../config.js";
6
+ /**
7
+ * Let the user pick tasks by number, then provider + model for each.
8
+ * Returns a patch for taskModels + dream, or null if cancelled.
9
+ */
10
+ export declare function runCommaListRoutingEditor(rl: ReadlineInterface, storePath: string, cfg: GnosysConfig): Promise<{
11
+ taskModels: NonNullable<GnosysConfig["taskModels"]>;
12
+ dream: GnosysConfig["dream"];
13
+ } | null>;
14
+ export declare function applyRoutingPatch(projectDir: string, patch: {
15
+ taskModels: NonNullable<GnosysConfig["taskModels"]>;
16
+ dream: GnosysConfig["dream"];
17
+ }): Promise<GnosysConfig>;
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Comma-list task routing editor (provider + model per selected task).
3
+ */
4
+ import { loadConfig, updateConfig, } from "../../config.js";
5
+ import { ASSIGNABLE_TASK_LIST, fetchDynamicModels, getAssignableRouting, modelForTaskAssignment, parseCommaSeparatedTaskSelection, pickModel, pickProvider, TASK_DESCRIPTIONS, } from "../../setup.js";
6
+ import { resolveActiveStorePath } from "../storePath.js";
7
+ import { safeQuestion } from "../ui/safePrompt.js";
8
+ import { renderProviderMark } from "../providerGlyphs.js";
9
+ import { c, color, glyph } from "../ui/tokens.js";
10
+ import { printStatus } from "../ui/status.js";
11
+ const BOLD = "\x1b[1m";
12
+ const DIM = "\x1b[2m";
13
+ const RESET = "\x1b[0m";
14
+ async function askInput(rl, prompt, opts) {
15
+ const hint = opts?.default ? ` ${DIM}(${opts.default})${RESET}` : "";
16
+ const raw = await safeQuestion(rl, `${prompt}${hint} `);
17
+ const trimmed = raw.trim();
18
+ return trimmed || opts?.default || "";
19
+ }
20
+ async function askYesNo(rl, question, defaultYes = true) {
21
+ const hint = defaultYes ? "Y/n" : "y/N";
22
+ const answer = await safeQuestion(rl, `${question} [${hint}] `);
23
+ const trimmed = answer.trim().toLowerCase();
24
+ if (trimmed === "")
25
+ return defaultYes;
26
+ return trimmed === "y" || trimmed === "yes";
27
+ }
28
+ /**
29
+ * Let the user pick tasks by number, then provider + model for each.
30
+ * Returns a patch for taskModels + dream, or null if cancelled.
31
+ */
32
+ export async function runCommaListRoutingEditor(rl, storePath, cfg) {
33
+ const tasks = ASSIGNABLE_TASK_LIST;
34
+ const dreamEnabledBefore = !!cfg.dream?.enabled;
35
+ const currentByTask = {};
36
+ for (const task of tasks) {
37
+ currentByTask[task] = getAssignableRouting(cfg, task);
38
+ }
39
+ console.log();
40
+ console.log(`${BOLD}Which tasks should get a new provider and model?${RESET}`);
41
+ console.log(`${DIM}Enter numbers (comma-separated), ${BOLD}all${RESET}${DIM}, or ${BOLD}none${RESET}${DIM}. Unlisted tasks keep current routing.${RESET}`);
42
+ console.log();
43
+ for (let i = 0; i < tasks.length; i++) {
44
+ const task = tasks[i];
45
+ const cur = currentByTask[task];
46
+ const desc = TASK_DESCRIPTIONS[task] ?? "";
47
+ const mark = renderProviderMark(cur.provider);
48
+ const off = task === "dream" && !dreamEnabledBefore ? ` ${DIM}[off]${RESET}` : "";
49
+ console.log(` ${BOLD}${i + 1}.${RESET} ${mark} ${task.padEnd(14)} ${DIM}now:${RESET} ${cur.provider} / ${cur.model} ${DIM}(${desc})${RESET}${off}`);
50
+ }
51
+ console.log();
52
+ let selectedIndices = [];
53
+ while (true) {
54
+ const raw = await askInput(rl, "Tasks to edit (e.g. 1,3,6 or all)", { default: "all" });
55
+ const parsed = parseCommaSeparatedTaskSelection(raw, tasks.length);
56
+ if (parsed === "all") {
57
+ selectedIndices = tasks.map((_, i) => i);
58
+ break;
59
+ }
60
+ if (parsed === "none") {
61
+ selectedIndices = [];
62
+ break;
63
+ }
64
+ if (parsed && parsed.length > 0) {
65
+ selectedIndices = parsed;
66
+ break;
67
+ }
68
+ console.log(`${color(c.fail, "Enter numbers 1-" + tasks.length + ", comma-separated, or 'all'.")}`);
69
+ }
70
+ if (selectedIndices.length === 0) {
71
+ printStatus("warn", "no tasks selected");
72
+ return null;
73
+ }
74
+ const selectedSet = new Set(selectedIndices.map((i) => tasks[i]));
75
+ const planned = { ...currentByTask };
76
+ const dynamicModels = await fetchDynamicModels();
77
+ for (const task of tasks) {
78
+ if (!selectedSet.has(task))
79
+ continue;
80
+ console.log("");
81
+ console.log(`${BOLD}${task}${RESET} ${DIM}— ${TASK_DESCRIPTIONS[task] ?? ""}${RESET}`);
82
+ const cur = currentByTask[task];
83
+ const provider = await pickProvider(rl, dynamicModels, "Provider", cur.provider);
84
+ let model;
85
+ const tiers = dynamicModels[provider];
86
+ if (provider === "custom" || !tiers || tiers.length === 0) {
87
+ model = await askInput(rl, "Model name", { default: cur.model });
88
+ }
89
+ else {
90
+ model = await pickModel(rl, provider, dynamicModels, "Model", cur.model);
91
+ }
92
+ if (!model) {
93
+ printStatus("warn", `skipped ${task} — no model`);
94
+ continue;
95
+ }
96
+ planned[task] = {
97
+ provider: provider,
98
+ model: modelForTaskAssignment(task, provider, model),
99
+ };
100
+ }
101
+ console.log();
102
+ console.log(`${BOLD}Planned task routing${RESET}`);
103
+ console.log(` ${"Task".padEnd(16)}${"Provider / model".padEnd(42)}`);
104
+ console.log(` ${"\u2500".repeat(56)}`);
105
+ for (const task of tasks) {
106
+ const p = planned[task];
107
+ const marker = selectedSet.has(task) ? `${color(c.accentHi, glyph.selection)} ` : " ";
108
+ const off = task === "dream" && !dreamEnabledBefore
109
+ ? ` ${DIM}(dream off)${RESET}`
110
+ : "";
111
+ console.log(`${marker}${task.padEnd(14)}${p.provider} / ${p.model}${off}`);
112
+ }
113
+ console.log(`${DIM} ${glyph.selection} = will update${RESET}`);
114
+ console.log();
115
+ if (!(await askYesNo(rl, "Save this routing?", true))) {
116
+ printStatus("warn", "cancelled");
117
+ return null;
118
+ }
119
+ const taskModels = {
120
+ ...(cfg.taskModels ?? {}),
121
+ };
122
+ for (const task of ASSIGNABLE_TASK_LIST) {
123
+ if (task === "dream")
124
+ continue;
125
+ if (selectedSet.has(task)) {
126
+ taskModels[task] = planned[task];
127
+ }
128
+ }
129
+ let dream = { ...(cfg.dream ?? {}) };
130
+ if (selectedSet.has("dream")) {
131
+ dream = {
132
+ ...dream,
133
+ provider: planned.dream.provider,
134
+ model: planned.dream.model,
135
+ };
136
+ if (!dreamEnabledBefore && (await askYesNo(rl, "Enable dream mode?", true))) {
137
+ dream.enabled = true;
138
+ }
139
+ }
140
+ return { taskModels, dream };
141
+ }
142
+ export async function applyRoutingPatch(projectDir, patch) {
143
+ const storePath = resolveActiveStorePath(projectDir);
144
+ await updateConfig(storePath, {
145
+ taskModels: patch.taskModels,
146
+ dream: patch.dream,
147
+ });
148
+ return loadConfig(storePath);
149
+ }
@@ -47,9 +47,18 @@ export interface SummaryOptions {
47
47
  * (Enter or `done`). Returns true if any section was edited.
48
48
  */
49
49
  export declare function runSummaryWizard(opts?: SummaryOptions): Promise<boolean>;
50
+ /**
51
+ * Settings-panel value for multi-machine sync (v13 design).
52
+ * Shows the configured master folder path, or "NA (single-machine only)" when unset.
53
+ */
54
+ export declare function formatMultiMachineSyncSummary(remotePath: string | null): string;
55
+ /** Resolve multi-machine sync status for the summary panel row. */
56
+ export declare function describeMultiMachineSyncPanel(): Promise<string>;
50
57
  export declare const __test: {
51
58
  resolveActiveStorePath: typeof resolveActiveStorePath;
52
59
  renderPanelRows: typeof renderPanelRows;
53
60
  buildTrailingMap: typeof buildTrailingMap;
61
+ formatMultiMachineSyncSummary: typeof formatMultiMachineSyncSummary;
62
+ describeMultiMachineSyncPanel: typeof describeMultiMachineSyncPanel;
54
63
  };
55
64
  export {};