muonroi-cli 1.8.5 → 1.9.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 (206) hide show
  1. package/dist/packages/agent-harness-core/src/event-filter.js +11 -0
  2. package/dist/packages/agent-harness-core/src/event-redact.js +7 -0
  3. package/dist/packages/agent-harness-core/src/event-tee.d.ts +20 -4
  4. package/dist/packages/agent-harness-core/src/event-tee.js +32 -5
  5. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +14 -0
  6. package/dist/packages/agent-harness-core/src/mcp-server.js +86 -10
  7. package/dist/packages/agent-harness-core/src/predicate.d.ts +1 -1
  8. package/dist/packages/agent-harness-core/src/protocol.d.ts +24 -2
  9. package/dist/src/agent-harness/mock-model.d.ts +10 -0
  10. package/dist/src/agent-harness/mock-model.js +6 -2
  11. package/dist/src/chat/chat-keychain.d.ts +7 -12
  12. package/dist/src/chat/chat-keychain.js +19 -86
  13. package/dist/src/cli/keys.d.ts +8 -45
  14. package/dist/src/cli/keys.js +18 -324
  15. package/dist/src/council/clarifier.d.ts +4 -2
  16. package/dist/src/council/clarifier.js +158 -36
  17. package/dist/src/council/debate-planner.js +3 -2
  18. package/dist/src/council/debate.js +59 -5
  19. package/dist/src/council/index.d.ts +23 -0
  20. package/dist/src/council/index.js +37 -2
  21. package/dist/src/council/llm.d.ts +62 -0
  22. package/dist/src/council/llm.js +123 -23
  23. package/dist/src/council/panel-select.js +13 -3
  24. package/dist/src/council/planner.js +17 -0
  25. package/dist/src/council/preflight.d.ts +10 -0
  26. package/dist/src/council/preflight.js +36 -0
  27. package/dist/src/council/prompts.d.ts +9 -1
  28. package/dist/src/council/prompts.js +23 -6
  29. package/dist/src/council/types.d.ts +10 -0
  30. package/dist/src/ee/auth.d.ts +19 -0
  31. package/dist/src/ee/auth.js +39 -0
  32. package/dist/src/ee/client.js +28 -3
  33. package/dist/src/ee/ee-onboarding.js +6 -26
  34. package/dist/src/flow/compaction/compress.d.ts +2 -2
  35. package/dist/src/flow/compaction/compress.js +21 -8
  36. package/dist/src/flow/compaction/extract.d.ts +3 -3
  37. package/dist/src/flow/compaction/extract.js +6 -6
  38. package/dist/src/flow/compaction/index.d.ts +2 -1
  39. package/dist/src/flow/compaction/index.js +29 -3
  40. package/dist/src/flow/compaction/progress.d.ts +35 -0
  41. package/dist/src/flow/compaction/progress.js +35 -0
  42. package/dist/src/generated/version.d.ts +1 -1
  43. package/dist/src/generated/version.js +1 -1
  44. package/dist/src/gsd/flags.d.ts +11 -0
  45. package/dist/src/gsd/flags.js +19 -0
  46. package/dist/src/gsd/plan-council.js +104 -72
  47. package/dist/src/gsd/verdict-schema.d.ts +1 -1
  48. package/dist/src/headless/council-answers.js +4 -0
  49. package/dist/src/index.js +129 -260
  50. package/dist/src/lsp/builtins.js +3 -1
  51. package/dist/src/lsp/manager.d.ts +5 -1
  52. package/dist/src/lsp/manager.js +249 -3
  53. package/dist/src/lsp/npm-cache.d.ts +11 -1
  54. package/dist/src/lsp/npm-cache.js +17 -1
  55. package/dist/src/lsp/runtime.d.ts +6 -1
  56. package/dist/src/lsp/runtime.js +17 -1
  57. package/dist/src/lsp/types.d.ts +83 -1
  58. package/dist/src/lsp/types.js +10 -0
  59. package/dist/src/mcp/client-pool.js +43 -15
  60. package/dist/src/mcp/lsp-tools.d.ts +5 -1
  61. package/dist/src/mcp/lsp-tools.js +93 -2
  62. package/dist/src/mcp/mcp-keychain.d.ts +3 -5
  63. package/dist/src/mcp/mcp-keychain.js +9 -49
  64. package/dist/src/mcp/setup-guide-text.d.ts +1 -1
  65. package/dist/src/mcp/setup-guide-text.js +22 -2
  66. package/dist/src/mcp/tools-server.d.ts +10 -0
  67. package/dist/src/mcp/tools-server.js +10 -2
  68. package/dist/src/models/catalog.json +19 -19
  69. package/dist/src/orchestrator/ask-user.d.ts +61 -0
  70. package/dist/src/orchestrator/ask-user.js +65 -0
  71. package/dist/src/orchestrator/compaction.d.ts +2 -3
  72. package/dist/src/orchestrator/compaction.js +8 -8
  73. package/dist/src/orchestrator/council-manager.js +9 -8
  74. package/dist/src/orchestrator/council-request.d.ts +49 -0
  75. package/dist/src/orchestrator/council-request.js +62 -0
  76. package/dist/src/orchestrator/interactive-pause.d.ts +26 -0
  77. package/dist/src/orchestrator/interactive-pause.js +36 -0
  78. package/dist/src/orchestrator/message-processor.d.ts +4 -0
  79. package/dist/src/orchestrator/message-processor.js +26 -8
  80. package/dist/src/orchestrator/orchestrator.d.ts +25 -0
  81. package/dist/src/orchestrator/orchestrator.js +204 -50
  82. package/dist/src/orchestrator/preprocessor.js +2 -2
  83. package/dist/src/orchestrator/safety-askcard.d.ts +1 -1
  84. package/dist/src/orchestrator/safety-askcard.js +5 -2
  85. package/dist/src/orchestrator/safety-intercept.d.ts +5 -0
  86. package/dist/src/orchestrator/safety-intercept.js +7 -0
  87. package/dist/src/orchestrator/stall-watchdog.d.ts +8 -1
  88. package/dist/src/orchestrator/stall-watchdog.js +24 -3
  89. package/dist/src/orchestrator/stream-runner.d.ts +13 -3
  90. package/dist/src/orchestrator/stream-runner.js +54 -21
  91. package/dist/src/orchestrator/tool-engine.d.ts +19 -0
  92. package/dist/src/orchestrator/tool-engine.js +241 -25
  93. package/dist/src/orchestrator/turn-watchdog.d.ts +7 -0
  94. package/dist/src/orchestrator/turn-watchdog.js +38 -9
  95. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  96. package/dist/src/pil/agent-operating-contract.js +6 -4
  97. package/dist/src/pil/discovery.d.ts +1 -1
  98. package/dist/src/pil/discovery.js +2 -2
  99. package/dist/src/pil/layer1_5-complexity-size.d.ts +7 -0
  100. package/dist/src/pil/layer1_5-complexity-size.js +31 -5
  101. package/dist/src/pil/llm-classify.d.ts +78 -3
  102. package/dist/src/pil/llm-classify.js +351 -111
  103. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  104. package/dist/src/pil/native-capabilities-workbook.js +7 -0
  105. package/dist/src/pil/pipeline.js +2 -0
  106. package/dist/src/pil/repo-grounding-probe.d.ts +15 -0
  107. package/dist/src/pil/repo-grounding-probe.js +136 -0
  108. package/dist/src/pil/repo-structure-hints.d.ts +7 -0
  109. package/dist/src/pil/repo-structure-hints.js +45 -0
  110. package/dist/src/product-loop/artifact-io.js +4 -0
  111. package/dist/src/product-loop/criteria-seed.d.ts +51 -0
  112. package/dist/src/product-loop/criteria-seed.js +200 -0
  113. package/dist/src/product-loop/discovery-interview.d.ts +9 -0
  114. package/dist/src/product-loop/discovery-interview.js +37 -18
  115. package/dist/src/product-loop/discovery-recommender.js +2 -1
  116. package/dist/src/product-loop/discovery-schema.js +14 -1
  117. package/dist/src/product-loop/discovery-triage.d.ts +23 -0
  118. package/dist/src/product-loop/discovery-triage.js +109 -0
  119. package/dist/src/product-loop/gather.js +150 -2
  120. package/dist/src/product-loop/index.js +7 -0
  121. package/dist/src/product-loop/loop-driver.js +21 -8
  122. package/dist/src/product-loop/phase-plan.d.ts +16 -0
  123. package/dist/src/product-loop/phase-plan.js +42 -4
  124. package/dist/src/product-loop/phase-rituals.d.ts +3 -0
  125. package/dist/src/product-loop/phase-rituals.js +8 -3
  126. package/dist/src/product-loop/phase-runner.js +30 -11
  127. package/dist/src/product-loop/plan-adherence-review.d.ts +26 -0
  128. package/dist/src/product-loop/plan-adherence-review.js +144 -0
  129. package/dist/src/product-loop/sprint-runner.d.ts +62 -0
  130. package/dist/src/product-loop/sprint-runner.js +309 -8
  131. package/dist/src/product-loop/types.d.ts +25 -0
  132. package/dist/src/providers/anthropic.d.ts +9 -8
  133. package/dist/src/providers/anthropic.js +13 -47
  134. package/dist/src/providers/auth/grok-oauth.d.ts +1 -0
  135. package/dist/src/providers/auth/grok-oauth.js +30 -5
  136. package/dist/src/providers/auth/openai-oauth.d.ts +1 -0
  137. package/dist/src/providers/auth/openai-oauth.js +14 -0
  138. package/dist/src/providers/auth/token-store.d.ts +9 -9
  139. package/dist/src/providers/auth/token-store.js +8 -70
  140. package/dist/src/providers/auth/types.d.ts +8 -0
  141. package/dist/src/providers/env-store.d.ts +17 -0
  142. package/dist/src/providers/env-store.js +228 -0
  143. package/dist/src/providers/keychain.d.ts +21 -17
  144. package/dist/src/providers/keychain.js +124 -135
  145. package/dist/src/providers/runtime.d.ts +24 -9
  146. package/dist/src/providers/runtime.js +48 -37
  147. package/dist/src/providers/strategies/thinking-mode.js +9 -1
  148. package/dist/src/providers/strategies/xai.strategy.js +27 -0
  149. package/dist/src/providers/warm.d.ts +65 -0
  150. package/dist/src/providers/warm.js +145 -0
  151. package/dist/src/self-qa/agentic-loop.js +3 -2
  152. package/dist/src/storage/transcript.js +56 -2
  153. package/dist/src/tools/git-safety.d.ts +19 -0
  154. package/dist/src/tools/git-safety.js +168 -0
  155. package/dist/src/tools/native-tools.d.ts +1 -1
  156. package/dist/src/tools/native-tools.js +76 -1
  157. package/dist/src/tools/registry.d.ts +17 -0
  158. package/dist/src/tools/registry.js +116 -1
  159. package/dist/src/types/index.d.ts +29 -1
  160. package/dist/src/ui/app.js +67 -4
  161. package/dist/src/ui/components/agent-rail-activities.d.ts +26 -0
  162. package/dist/src/ui/components/agent-rail-activities.js +47 -0
  163. package/dist/src/ui/components/compact-progress-card.d.ts +24 -0
  164. package/dist/src/ui/components/compact-progress-card.js +42 -0
  165. package/dist/src/ui/components/council-phase-timeline.js +17 -2
  166. package/dist/src/ui/components/council-question-card.js +1 -0
  167. package/dist/src/ui/components/message-view.d.ts +15 -0
  168. package/dist/src/ui/components/message-view.js +50 -1
  169. package/dist/src/ui/components/tool-group.d.ts +15 -3
  170. package/dist/src/ui/components/tool-group.js +69 -11
  171. package/dist/src/ui/containers/modals-layer.d.ts +2 -1
  172. package/dist/src/ui/containers/modals-layer.js +2 -2
  173. package/dist/src/ui/council-harness-event.d.ts +57 -0
  174. package/dist/src/ui/council-harness-event.js +46 -0
  175. package/dist/src/ui/heartbeat-debug.d.ts +29 -0
  176. package/dist/src/ui/heartbeat-debug.js +45 -0
  177. package/dist/src/ui/modals/api-key-modal.js +1 -1
  178. package/dist/src/ui/modals/model-picker-modal.d.ts +8 -18
  179. package/dist/src/ui/modals/model-picker-modal.js +8 -10
  180. package/dist/src/ui/slash/ee.js +81 -0
  181. package/dist/src/ui/slash/menu-items.js +11 -2
  182. package/dist/src/ui/use-app-logic.js +354 -224
  183. package/dist/src/ui/utils/agent-activities.d.ts +39 -0
  184. package/dist/src/ui/utils/agent-activities.js +96 -0
  185. package/dist/src/ui/utils/group-tool-entries.d.ts +26 -0
  186. package/dist/src/ui/utils/group-tool-entries.js +111 -0
  187. package/dist/src/ui/utils/tool-summary.d.ts +21 -0
  188. package/dist/src/ui/utils/tool-summary.js +91 -0
  189. package/dist/src/utils/event-loop-monitor.d.ts +85 -0
  190. package/dist/src/utils/event-loop-monitor.js +107 -0
  191. package/dist/src/utils/llm-deadline.d.ts +14 -0
  192. package/dist/src/utils/llm-deadline.js +19 -0
  193. package/dist/src/utils/loop-profiler.d.ts +102 -0
  194. package/dist/src/utils/loop-profiler.js +202 -0
  195. package/dist/src/utils/settings.d.ts +27 -0
  196. package/dist/src/utils/settings.js +38 -2
  197. package/dist/src/utils/side-question.d.ts +1 -2
  198. package/dist/src/utils/side-question.js +2 -2
  199. package/dist/src/verify/entrypoint.js +51 -16
  200. package/dist/src/verify/orchestrator.d.ts +1 -1
  201. package/dist/src/verify/orchestrator.js +20 -3
  202. package/package.json +1 -2
  203. package/dist/src/cli/bw-vault.d.ts +0 -55
  204. package/dist/src/cli/bw-vault.js +0 -133
  205. package/dist/src/mcp/ee-tools.d.ts +0 -46
  206. package/dist/src/mcp/ee-tools.js +0 -194
@@ -1,62 +1,27 @@
1
1
  /**
2
2
  * `muonroi-cli keys` subcommand group.
3
3
  *
4
- * Manages provider API keys via the OS keychain (Windows Credential Manager,
5
- * macOS Keychain, libsecret on Linux via keytar). Keys are read by the CLI through the
6
- * keychain → env → settings.json priority chain in providers/keychain.ts.
7
- *
8
- * On Linux without libsecret + an active secret service (gnome-keyring etc.),
9
- * or in headless sessions, keychain writes return false and callers fall back
10
- * to advising environment variables (which are still honored at runtime).
4
+ * Manages provider API keys via the env-store. Keys are read by the CLI through
5
+ * the env-store env settings.json priority chain in providers/keychain.ts.
11
6
  *
12
7
  * Subcommands:
13
- * keys set <provider> — interactive prompt, stores in keychain
8
+ * keys set <provider> — interactive prompt, stores the key
14
9
  * keys list — show masked keys currently stored
15
10
  * keys delete <provider> — remove a stored key
16
- * keys import-bw [providers] pull from Bitwarden vault, store in keychain
17
- * keys cleanup-settings strip plaintext keys from user-settings.json
11
+ * keys export <file> export keys to an encrypted portable bundle
12
+ * keys import <file> import an encrypted bundle
18
13
  * keys login <provider> — OAuth login (openai, xai)
19
14
  * keys logout <provider> — OAuth logout (openai, xai)
20
15
  */
21
16
  import { type ChatSecretId } from "../chat/chat-keychain.js";
22
- export interface KeysSetOptions {
23
- bw?: boolean;
24
- itemPrefix?: string;
25
- }
26
- export declare function runKeysSet(provider: string, options?: KeysSetOptions): Promise<void>;
27
- export declare function runMcpKeysSet(id: string, options?: KeysSetOptions): Promise<void>;
17
+ export type KeysSetOptions = Record<string, never>;
18
+ export declare function runKeysSet(provider: string, _options?: KeysSetOptions): Promise<void>;
19
+ export declare function runMcpKeysSet(id: string, _options?: KeysSetOptions): Promise<void>;
28
20
  export declare function runKeysList(): Promise<void>;
29
21
  export declare function runKeysDelete(provider: string): Promise<void>;
30
22
  export declare function runKeysExport(filePath: string): Promise<void>;
31
23
  export declare function runKeysImport(filePath: string): Promise<void>;
32
- interface BwImportOptions {
33
- providers?: string[];
34
- itemPrefix?: string;
35
- }
36
- export declare function runKeysImportBw(opts?: BwImportOptions): Promise<void>;
37
- interface McpBwImportOptions {
38
- keys?: string[];
39
- itemPrefix?: string;
40
- }
41
- /**
42
- * Import MCP secrets (e.g. Tavily) from a Bitwarden vault into the OS
43
- * keychain via mcp-keychain. Vault items are expected at `<prefix><id>` —
44
- * default prefix `muonroi-cli/`. The key value is read from the item's
45
- * notes field, mirroring the provider import-bw flow.
46
- */
47
- export declare function runMcpImportBw(opts?: McpBwImportOptions): Promise<void>;
48
24
  export declare function runChatKeySet(id: ChatSecretId, value: string): Promise<void>;
49
- interface ChatBwImportOptions {
50
- ids?: ChatSecretId[];
51
- itemPrefix?: string;
52
- }
53
- /**
54
- * Import chat secrets (discord-token, discord-guild-id, slack-token, slack-team-id)
55
- * from a Bitwarden vault into the OS keychain. Vault items are expected at
56
- * `<prefix><id>` — default prefix `muonroi-cli/chat-`. The value is read from
57
- * the item's notes field, mirroring the provider/MCP import-bw flow.
58
- */
59
- export declare function runChatImportBw(opts?: ChatBwImportOptions): Promise<void>;
60
25
  /**
61
26
  * `muonroi-cli keys login <provider>`
62
27
  *
@@ -74,5 +39,3 @@ export declare function runKeysLogin(provider: string): Promise<void>;
74
39
  * through the OAuth registry — no provider branching.
75
40
  */
76
41
  export declare function runKeysLogout(provider: string): Promise<void>;
77
- export declare function runKeysCleanupSettings(): Promise<void>;
78
- export {};
@@ -1,26 +1,19 @@
1
1
  /**
2
2
  * `muonroi-cli keys` subcommand group.
3
3
  *
4
- * Manages provider API keys via the OS keychain (Windows Credential Manager,
5
- * macOS Keychain, libsecret on Linux via keytar). Keys are read by the CLI through the
6
- * keychain → env → settings.json priority chain in providers/keychain.ts.
7
- *
8
- * On Linux without libsecret + an active secret service (gnome-keyring etc.),
9
- * or in headless sessions, keychain writes return false and callers fall back
10
- * to advising environment variables (which are still honored at runtime).
4
+ * Manages provider API keys via the env-store. Keys are read by the CLI through
5
+ * the env-store env settings.json priority chain in providers/keychain.ts.
11
6
  *
12
7
  * Subcommands:
13
- * keys set <provider> — interactive prompt, stores in keychain
8
+ * keys set <provider> — interactive prompt, stores the key
14
9
  * keys list — show masked keys currently stored
15
10
  * keys delete <provider> — remove a stored key
16
- * keys import-bw [providers] pull from Bitwarden vault, store in keychain
17
- * keys cleanup-settings strip plaintext keys from user-settings.json
11
+ * keys export <file> export keys to an encrypted portable bundle
12
+ * keys import <file> import an encrypted bundle
18
13
  * keys login <provider> — OAuth login (openai, xai)
19
14
  * keys logout <provider> — OAuth logout (openai, xai)
20
15
  */
21
- import { spawnSync } from "node:child_process";
22
16
  import { promises as fs } from "node:fs";
23
- import * as os from "node:os";
24
17
  import * as path from "node:path";
25
18
  import { getChatSecret, listChatSecrets, setChatSecret } from "../chat/chat-keychain.js";
26
19
  import { setMcpKey } from "../mcp/mcp-keychain.js";
@@ -39,11 +32,6 @@ const MCP_KEY_IDS = ["tavily"];
39
32
  function isMcpKeyId(value) {
40
33
  return MCP_KEY_IDS.includes(value);
41
34
  }
42
- const CHAT_SECRET_IDS = ["discord-token", "discord-guild-id", "slack-token", "slack-team-id"];
43
- function isChatSecretId(value) {
44
- return CHAT_SECRET_IDS.includes(value);
45
- }
46
- const getSettingsPath = () => path.join(os.homedir(), ".muonroi-cli", "user-settings.json");
47
35
  function maskKey(key) {
48
36
  if (key.length <= 10)
49
37
  return "***";
@@ -110,7 +98,7 @@ async function promptHidden(question) {
110
98
  process.stdin.on("data", onData);
111
99
  });
112
100
  }
113
- export async function runKeysSet(provider, options = {}) {
101
+ export async function runKeysSet(provider, _options = {}) {
114
102
  const norm = normalizeProvider(provider);
115
103
  if (!norm) {
116
104
  console.error(`Unknown provider '${provider}'. Valid: ${KEYCHAIN_PROVIDER_IDS.join(", ")}`);
@@ -121,16 +109,6 @@ export async function runKeysSet(provider, options = {}) {
121
109
  console.error("Aborted (empty key).");
122
110
  process.exit(1);
123
111
  }
124
- if (options.bw) {
125
- const { writeBwSecureNote } = await import("./bw-vault.js");
126
- const itemName = `${options.itemPrefix ?? "muonroi-cli/"}${provider}`;
127
- const res = await writeBwSecureNote(itemName, key);
128
- if (!res.ok) {
129
- console.error(`Bitwarden write failed: ${res.error}`);
130
- process.exit(2);
131
- }
132
- console.log(`Bitwarden vault: ${res.action} '${itemName}'.`);
133
- }
134
112
  try {
135
113
  const ok = await setKeyForProvider(norm, key);
136
114
  if (!ok) {
@@ -150,7 +128,7 @@ export async function runKeysSet(provider, options = {}) {
150
128
  process.exit(1);
151
129
  }
152
130
  }
153
- export async function runMcpKeysSet(id, options = {}) {
131
+ export async function runMcpKeysSet(id, _options = {}) {
154
132
  if (!isMcpKeyId(id)) {
155
133
  console.error(`Unknown MCP key '${id}'. Valid: ${MCP_KEY_IDS.join(", ")}`);
156
134
  process.exit(1);
@@ -160,16 +138,6 @@ export async function runMcpKeysSet(id, options = {}) {
160
138
  console.error("Aborted (empty key).");
161
139
  process.exit(1);
162
140
  }
163
- if (options.bw) {
164
- const { writeBwSecureNote } = await import("./bw-vault.js");
165
- const itemName = `${options.itemPrefix ?? "muonroi-cli/"}${id}`;
166
- const res = await writeBwSecureNote(itemName, key);
167
- if (!res.ok) {
168
- console.error(`Bitwarden write failed: ${res.error}`);
169
- process.exit(2);
170
- }
171
- console.log(`Bitwarden vault: ${res.action} '${itemName}'.`);
172
- }
173
141
  try {
174
142
  const ok = await setMcpKey(id, key);
175
143
  if (!ok) {
@@ -211,7 +179,7 @@ export async function runKeysList() {
211
179
  const hasAnything = stored.length > 0 || chatStored.length > 0 || oauthRows.length > 0;
212
180
  if (!hasAnything) {
213
181
  console.log("No keys stored in OS keychain.");
214
- console.log("Run 'muonroi-cli keys set <provider>' or 'muonroi-cli keys import-bw' to add some.");
182
+ console.log("Run 'muonroi-cli keys set <provider>' to add some.");
215
183
  console.log("Run 'muonroi-cli keys login openai' to log in with your OpenAI subscription.");
216
184
  console.log("Run 'muonroi-cli keys login xai' to log in with your SuperGrok / X Premium+ subscription.");
217
185
  return;
@@ -365,161 +333,6 @@ export async function runKeysImport(filePath) {
365
333
  }
366
334
  console.log(`\nImported ${imported} key(s); ${skipped} skipped.`);
367
335
  }
368
- export async function runKeysImportBw(opts = {}) {
369
- const which = spawnSync("bw", ["--version"], { encoding: "utf8" });
370
- if (which.status !== 0) {
371
- console.error("Bitwarden CLI ('bw') not found in PATH.");
372
- console.error("Install: https://bitwarden.com/help/cli/");
373
- process.exit(2);
374
- }
375
- const session = process.env.BW_SESSION;
376
- if (!session) {
377
- console.error("BW_SESSION not set. Run:");
378
- console.error(" export BW_SESSION=$(bw unlock --raw)");
379
- process.exit(2);
380
- }
381
- const status = spawnSync("bw", ["status", "--session", session], { encoding: "utf8" });
382
- if (status.status !== 0) {
383
- console.error(`bw status failed: ${status.stderr || status.stdout}`);
384
- process.exit(2);
385
- }
386
- let parsed;
387
- try {
388
- parsed = JSON.parse(status.stdout);
389
- }
390
- catch {
391
- parsed = {};
392
- }
393
- if (parsed.status !== "unlocked") {
394
- console.error(`Bitwarden vault is not unlocked (status: ${parsed.status ?? "unknown"}).`);
395
- console.error("Run: export BW_SESSION=$(bw unlock --raw)");
396
- process.exit(2);
397
- }
398
- const requested = opts.providers && opts.providers.length > 0 ? opts.providers : KEYCHAIN_PROVIDER_IDS.slice();
399
- const prefix = opts.itemPrefix ?? "muonroi-cli/";
400
- let imported = 0;
401
- let skipped = 0;
402
- for (const provider of requested) {
403
- const norm = normalizeProvider(provider);
404
- if (!norm) {
405
- console.warn(`Skip unknown provider: ${provider}`);
406
- skipped++;
407
- continue;
408
- }
409
- const itemName = `${prefix}${provider}`;
410
- const got = spawnSync("bw", ["get", "notes", itemName, "--session", session], { encoding: "utf8" });
411
- if (got.status !== 0) {
412
- // bw prints "Not found." on stderr when the item is missing — treat as skip.
413
- skipped++;
414
- continue;
415
- }
416
- const key = got.stdout.trim();
417
- if (!key || key.length < 20) {
418
- console.warn(`Skip ${provider}: vault item '${itemName}' empty or too short.`);
419
- skipped++;
420
- continue;
421
- }
422
- try {
423
- const ok = await setKeyForProvider(norm, key);
424
- if (!ok) {
425
- console.warn(`! ${provider} — OS keychain unavailable (keytar or secret service backend).`);
426
- console.warn(" Linux fix: sudo dnf install libsecret (Fedora) or sudo apt-get install libsecret-1-0 (Ubuntu)");
427
- console.warn(` Fallback (works at runtime): export ${provider.toUpperCase()}_API_KEY=...`);
428
- skipped++;
429
- }
430
- else {
431
- console.log(`Imported ${provider} → keychain.`);
432
- imported++;
433
- }
434
- }
435
- catch (e) {
436
- console.warn(`Failed ${provider}: ${e.message}`);
437
- skipped++;
438
- }
439
- }
440
- console.log(`\nDone. Imported: ${imported}, skipped: ${skipped}.`);
441
- if (imported > 0) {
442
- console.log("Run 'muonroi-cli keys cleanup-settings' to strip any plaintext keys from settings.json.");
443
- }
444
- }
445
- /**
446
- * Import MCP secrets (e.g. Tavily) from a Bitwarden vault into the OS
447
- * keychain via mcp-keychain. Vault items are expected at `<prefix><id>` —
448
- * default prefix `muonroi-cli/`. The key value is read from the item's
449
- * notes field, mirroring the provider import-bw flow.
450
- */
451
- export async function runMcpImportBw(opts = {}) {
452
- const which = spawnSync("bw", ["--version"], { encoding: "utf8" });
453
- if (which.status !== 0) {
454
- console.error("Bitwarden CLI ('bw') not found in PATH.");
455
- console.error("Install: https://bitwarden.com/help/cli/");
456
- process.exit(2);
457
- }
458
- const session = process.env.BW_SESSION;
459
- if (!session) {
460
- console.error("BW_SESSION not set. Run:");
461
- console.error(" export BW_SESSION=$(bw unlock --raw)");
462
- process.exit(2);
463
- }
464
- const status = spawnSync("bw", ["status", "--session", session], { encoding: "utf8" });
465
- if (status.status !== 0) {
466
- console.error(`bw status failed: ${status.stderr || status.stdout}`);
467
- process.exit(2);
468
- }
469
- let parsed;
470
- try {
471
- parsed = JSON.parse(status.stdout);
472
- }
473
- catch {
474
- parsed = {};
475
- }
476
- if (parsed.status !== "unlocked") {
477
- console.error(`Bitwarden vault is not unlocked (status: ${parsed.status ?? "unknown"}).`);
478
- console.error("Run: export BW_SESSION=$(bw unlock --raw)");
479
- process.exit(2);
480
- }
481
- const requested = opts.keys && opts.keys.length > 0 ? opts.keys : MCP_KEY_IDS.slice();
482
- const prefix = opts.itemPrefix ?? "muonroi-cli/";
483
- let imported = 0;
484
- let skipped = 0;
485
- for (const id of requested) {
486
- if (!isMcpKeyId(id)) {
487
- console.warn(`Skip unknown MCP key: ${id}`);
488
- skipped++;
489
- continue;
490
- }
491
- const itemName = `${prefix}${id}`;
492
- const got = spawnSync("bw", ["get", "notes", itemName, "--session", session], { encoding: "utf8" });
493
- if (got.status !== 0) {
494
- skipped++;
495
- continue;
496
- }
497
- const key = got.stdout.trim();
498
- if (!key || key.length < 16) {
499
- console.warn(`Skip ${id}: vault item '${itemName}' empty or too short.`);
500
- skipped++;
501
- continue;
502
- }
503
- try {
504
- const ok = await setMcpKey(id, key);
505
- if (!ok) {
506
- console.warn(`! ${id} — OS keychain unavailable (keytar or secret service backend).`);
507
- console.warn(" Linux fix: sudo dnf install libsecret (Fedora) or sudo apt-get install libsecret-1-0 (Ubuntu)");
508
- console.warn(` Fallback (works at runtime): export ${id.toUpperCase()}_API_KEY=...`);
509
- skipped++;
510
- }
511
- else {
512
- console.log(`Imported MCP key '${id}' → keychain.`);
513
- imported++;
514
- }
515
- }
516
- catch (e) {
517
- console.warn(`Failed ${id}: ${e.message}`);
518
- skipped++;
519
- }
520
- }
521
- console.log(`\nDone. Imported: ${imported}, skipped: ${skipped}.`);
522
- }
523
336
  export async function runChatKeySet(id, value) {
524
337
  if (!value || value.length < 8) {
525
338
  console.error(`Value for chat secret '${id}' is too short (< 8 chars).`);
@@ -540,85 +353,6 @@ export async function runChatKeySet(id, value) {
540
353
  process.exit(1);
541
354
  }
542
355
  }
543
- /**
544
- * Import chat secrets (discord-token, discord-guild-id, slack-token, slack-team-id)
545
- * from a Bitwarden vault into the OS keychain. Vault items are expected at
546
- * `<prefix><id>` — default prefix `muonroi-cli/chat-`. The value is read from
547
- * the item's notes field, mirroring the provider/MCP import-bw flow.
548
- */
549
- export async function runChatImportBw(opts = {}) {
550
- const which = spawnSync("bw", ["--version"], { encoding: "utf8" });
551
- if (which.status !== 0) {
552
- console.error("Bitwarden CLI ('bw') not found in PATH.");
553
- console.error("Install: https://bitwarden.com/help/cli/");
554
- process.exit(2);
555
- }
556
- const session = process.env.BW_SESSION;
557
- if (!session) {
558
- console.error("BW_SESSION not set. Run:");
559
- console.error(" export BW_SESSION=$(bw unlock --raw)");
560
- process.exit(2);
561
- }
562
- const status = spawnSync("bw", ["status", "--session", session], { encoding: "utf8" });
563
- if (status.status !== 0) {
564
- console.error(`bw status failed: ${status.stderr || status.stdout}`);
565
- process.exit(2);
566
- }
567
- let parsed;
568
- try {
569
- parsed = JSON.parse(status.stdout);
570
- }
571
- catch {
572
- parsed = {};
573
- }
574
- if (parsed.status !== "unlocked") {
575
- console.error(`Bitwarden vault is not unlocked (status: ${parsed.status ?? "unknown"}).`);
576
- console.error("Run: export BW_SESSION=$(bw unlock --raw)");
577
- process.exit(2);
578
- }
579
- const requested = opts.ids && opts.ids.length > 0 ? opts.ids : CHAT_SECRET_IDS.slice();
580
- const prefix = opts.itemPrefix ?? "muonroi-cli/chat-";
581
- let imported = 0;
582
- let skipped = 0;
583
- for (const id of requested) {
584
- if (!isChatSecretId(id)) {
585
- console.warn(`Skip unknown chat secret: ${id}`);
586
- skipped++;
587
- continue;
588
- }
589
- const itemName = `${prefix}${id}`;
590
- const got = spawnSync("bw", ["get", "notes", itemName, "--session", session], { encoding: "utf8" });
591
- if (got.status !== 0) {
592
- // bw prints "Not found." on stderr when the item is missing — treat as skip.
593
- skipped++;
594
- continue;
595
- }
596
- const value = got.stdout.trim();
597
- if (!value || value.length < 8) {
598
- console.warn(`Skip ${id}: vault item '${itemName}' empty or too short.`);
599
- skipped++;
600
- continue;
601
- }
602
- try {
603
- const ok = await setChatSecret(id, value);
604
- if (!ok) {
605
- console.warn(`! ${id} — OS keychain unavailable (keytar or secret service backend).`);
606
- console.warn(" Linux fix: sudo dnf install libsecret (Fedora) or sudo apt-get install libsecret-1-0 (Ubuntu)");
607
- console.warn(` Fallback (works at runtime): export ${id.includes("token") ? "MUONROI_" : "MUONROI_"}${id.replace("-", "_").toUpperCase()}=...`);
608
- skipped++;
609
- }
610
- else {
611
- console.log(`Imported chat secret '${id}' → keychain.`);
612
- imported++;
613
- }
614
- }
615
- catch (e) {
616
- console.warn(`Failed ${id}: ${e.message}`);
617
- skipped++;
618
- }
619
- }
620
- console.log(`\nDone. Imported: ${imported}, skipped: ${skipped}.`);
621
- }
622
356
  // ---------------------------------------------------------------------------
623
357
  // OAuth login / logout
624
358
  // ---------------------------------------------------------------------------
@@ -667,6 +401,16 @@ export async function runKeysLogin(provider) {
667
401
  },
668
402
  });
669
403
  await saveTokens(norm, tokens);
404
+ // One auth mode per provider (OAuth XOR API key): logging in via OAuth clears
405
+ // any stored API key for this provider so a stale key can never shadow OAuth.
406
+ try {
407
+ const { clearEnvVar } = await import("../providers/env-store.js");
408
+ const { ENV_BY_PROVIDER } = await import("../providers/keychain.js");
409
+ clearEnvVar(ENV_BY_PROVIDER[norm]);
410
+ }
411
+ catch (err) {
412
+ console.error(`[keys] failed to clear API key after OAuth login for ${norm}: ${err instanceof Error ? err.message : String(err)}`);
413
+ }
670
414
  const emailDisplay = tokens.email ? ` (${tokens.email})` : "";
671
415
  const expiry = new Date(tokens.expiresAt).toLocaleString();
672
416
  console.log(`\nLogged in to ${name}${emailDisplay}. Token expires: ${expiry}`);
@@ -701,54 +445,4 @@ export async function runKeysLogout(provider) {
701
445
  const name = cfg?.displayName ?? provider;
702
446
  console.log(`Logged out of ${name}. OAuth tokens revoked and deleted.`);
703
447
  }
704
- export async function runKeysCleanupSettings() {
705
- let raw;
706
- try {
707
- raw = await fs.readFile(getSettingsPath(), "utf8");
708
- }
709
- catch (e) {
710
- if (e.code === "ENOENT") {
711
- console.log("No user-settings.json found — nothing to clean.");
712
- return;
713
- }
714
- throw e;
715
- }
716
- let json;
717
- try {
718
- json = JSON.parse(raw);
719
- }
720
- catch {
721
- console.error(`Settings file is not valid JSON: ${getSettingsPath()}`);
722
- process.exit(1);
723
- }
724
- const ts = new Date().toISOString().replace(/[:.]/g, "-");
725
- const backup = `${getSettingsPath()}.bak.${ts}`;
726
- await fs.writeFile(backup, raw, "utf8");
727
- let removed = 0;
728
- if ("apiKey" in json) {
729
- delete json.apiKey;
730
- removed++;
731
- }
732
- if (json.providers && typeof json.providers === "object") {
733
- const providers = json.providers;
734
- for (const [name, block] of Object.entries(providers)) {
735
- if (block && "apiKey" in block) {
736
- delete block.apiKey;
737
- removed++;
738
- if (Object.keys(block).length === 0) {
739
- delete providers[name];
740
- }
741
- }
742
- }
743
- if (Object.keys(providers).length === 0) {
744
- delete json.providers;
745
- }
746
- }
747
- await fs.writeFile(getSettingsPath(), `${JSON.stringify(json, null, 2)}\n`, "utf8");
748
- console.log(`Backed up to: ${backup}`);
749
- console.log(`Removed ${removed} plaintext key field(s) from: ${getSettingsPath()}`);
750
- if (removed === 0) {
751
- console.log("(File was already clean.)");
752
- }
753
- }
754
448
  //# sourceMappingURL=keys.js.map
@@ -46,6 +46,7 @@ export declare function judgeReadiness(spec: ClarifiedSpec, topic: string, qa: A
46
46
  confidence: number;
47
47
  gaps: string[];
48
48
  }>;
49
+ export declare function resolveInterviewLanguage(topic: string): string;
49
50
  export interface ClarifyOptionsResult {
50
51
  options: CouncilQuestionOption[];
51
52
  /**
@@ -68,7 +69,7 @@ export interface ClarifyOptionsResult {
68
69
  * returned `defaultIndex` points to it. Otherwise `defaultIndex` is omitted
69
70
  * so the UI knows to suppress the "(Recommended)" tag.
70
71
  */
71
- export declare function buildClarifyOptions(suggestions: string[] | undefined, recommended?: string): ClarifyOptionsResult;
72
+ export declare function buildClarifyOptions(suggestions: string[] | undefined, recommended?: string, lang?: string): ClarifyOptionsResult;
72
73
  /**
73
74
  * Model-designed option object: a choice the user picks between, carrying its
74
75
  * own explanation. `recommended:true` marks the pre-selected default. This is
@@ -88,7 +89,8 @@ export interface ClarifyOptionSpec {
88
89
  * "Chat about this" escape-hatches are appended as in `buildClarifyOptions`.
89
90
  * Pure + exported for unit testing.
90
91
  */
91
- export declare function buildClarifyOptionsRich(specs: ClarifyOptionSpec[] | undefined): ClarifyOptionsResult;
92
+ export declare function buildClarifyOptionsRich(specs: ClarifyOptionSpec[] | undefined, lang?: string): ClarifyOptionsResult;
93
+ export declare function researchScopeForClarification(topic: string, conversationContext: string, leaderModelId: string, llm: CouncilLLM, signal: AbortSignal | undefined, reachableModels: string[]): AsyncGenerator<StreamChunk, string, unknown>;
92
94
  export declare function runClarification(topic: string, leaderModelId: string, conversationContext: string, respondToQuestion: QuestionResponder, llm: CouncilLLM, signal?: AbortSignal, seedQuestions?: GrayAreaQuestion[], maxRounds?: number,
93
95
  /**
94
96
  * Optional pre-filled answers keyed by seed-question id. When the loop's