min-agent 0.5.1 → 0.6.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 (169) hide show
  1. package/README.md +18 -18
  2. package/dist/cli.js +538 -12
  3. package/docs/API.md +6 -6
  4. package/package.json +6 -6
  5. package/skills/self-config/SKILL.md +4 -2
  6. package/skills/self-config/reference.md +4 -1
  7. package/dist/agent.js +0 -1388
  8. package/dist/assistant-stream.js +0 -131
  9. package/dist/cli/commands/chat.js +0 -13
  10. package/dist/cli/commands/ctx.js +0 -7
  11. package/dist/cli/commands/exec.js +0 -35
  12. package/dist/cli/commands/history.js +0 -58
  13. package/dist/cli/commands/index.js +0 -249
  14. package/dist/cli/commands/init.js +0 -18
  15. package/dist/cli/commands/mcp.js +0 -173
  16. package/dist/cli/commands/memory.js +0 -87
  17. package/dist/cli/commands/models.js +0 -21
  18. package/dist/cli/commands/permission.js +0 -12
  19. package/dist/cli/commands/rules.js +0 -33
  20. package/dist/cli/commands/sandbox.js +0 -13
  21. package/dist/cli/commands/serve.js +0 -9
  22. package/dist/cli/commands/setup.js +0 -56
  23. package/dist/cli/commands/shared.js +0 -25
  24. package/dist/cli/commands/skills.js +0 -119
  25. package/dist/cli/commands/think.js +0 -12
  26. package/dist/cli/commands/update.js +0 -7
  27. package/dist/cli/commands/write-config.js +0 -52
  28. package/dist/cli/errors.js +0 -36
  29. package/dist/cli/exec-prompt.js +0 -26
  30. package/dist/cli/option-helpers.js +0 -65
  31. package/dist/cli/program.js +0 -223
  32. package/dist/cli/setup/detect.js +0 -17
  33. package/dist/cli/setup/flags.js +0 -12
  34. package/dist/cli/setup/flow.js +0 -108
  35. package/dist/cli/setup/provider-form.js +0 -102
  36. package/dist/cli/setup/ui.js +0 -534
  37. package/dist/clipboard.js +0 -142
  38. package/dist/code-mode.js +0 -184
  39. package/dist/compaction.js +0 -644
  40. package/dist/config.js +0 -363
  41. package/dist/confirm.js +0 -172
  42. package/dist/context-window.js +0 -256
  43. package/dist/ctx-cli.js +0 -30
  44. package/dist/ctx.js +0 -80
  45. package/dist/doom-loop.js +0 -278
  46. package/dist/fetch-timeout.js +0 -152
  47. package/dist/http-approvals.js +0 -60
  48. package/dist/http.js +0 -119
  49. package/dist/instructions.js +0 -196
  50. package/dist/logger.js +0 -95
  51. package/dist/markdown.js +0 -259
  52. package/dist/mcp.js +0 -1036
  53. package/dist/memory-cli.js +0 -33
  54. package/dist/memory.js +0 -295
  55. package/dist/model-catalog.js +0 -285
  56. package/dist/ollama-model.js +0 -234
  57. package/dist/ollama-openai-bridge.js +0 -383
  58. package/dist/output.js +0 -76
  59. package/dist/paste-handler.js +0 -41
  60. package/dist/permission-cli.js +0 -40
  61. package/dist/plugins.js +0 -131
  62. package/dist/pricing.js +0 -119
  63. package/dist/provider.js +0 -63
  64. package/dist/question-format.js +0 -60
  65. package/dist/reasoning-stream.js +0 -158
  66. package/dist/sandbox-cli.js +0 -79
  67. package/dist/sandbox.js +0 -403
  68. package/dist/save-throttle.js +0 -45
  69. package/dist/scope.js +0 -23
  70. package/dist/serve/common.js +0 -425
  71. package/dist/serve/routes-chat.js +0 -367
  72. package/dist/serve/routes-mcp.js +0 -212
  73. package/dist/serve/routes-memory.js +0 -95
  74. package/dist/serve/routes-meta.js +0 -268
  75. package/dist/serve/routes-sessions.js +0 -61
  76. package/dist/serve/routes-skills.js +0 -70
  77. package/dist/serve.js +0 -138
  78. package/dist/sessions.js +0 -265
  79. package/dist/skills.js +0 -599
  80. package/dist/structured-output.js +0 -29
  81. package/dist/synthetic.js +0 -7
  82. package/dist/think-cli.js +0 -36
  83. package/dist/thinking-wire.js +0 -239
  84. package/dist/thinking.js +0 -166
  85. package/dist/title-gen.js +0 -55
  86. package/dist/token-display.js +0 -39
  87. package/dist/tool-display.js +0 -178
  88. package/dist/tool-output.js +0 -126
  89. package/dist/tools/apply_patch.js +0 -265
  90. package/dist/tools/atomic-file.js +0 -35
  91. package/dist/tools/backend.js +0 -61
  92. package/dist/tools/bash.js +0 -231
  93. package/dist/tools/code_search.js +0 -98
  94. package/dist/tools/edit.js +0 -68
  95. package/dist/tools/explore.js +0 -232
  96. package/dist/tools/glob.js +0 -40
  97. package/dist/tools/grep.js +0 -174
  98. package/dist/tools/index.js +0 -34
  99. package/dist/tools/question.js +0 -54
  100. package/dist/tools/read.js +0 -109
  101. package/dist/tools/search-searxng.js +0 -223
  102. package/dist/tools/search-serper.js +0 -189
  103. package/dist/tools/task.js +0 -165
  104. package/dist/tools/todo.js +0 -213
  105. package/dist/tools/web_fetch.js +0 -195
  106. package/dist/tools/web_search.js +0 -228
  107. package/dist/tools/write.js +0 -55
  108. package/dist/tui/App.js +0 -138
  109. package/dist/tui/ConfirmBar.js +0 -66
  110. package/dist/tui/CtxPicker.js +0 -68
  111. package/dist/tui/InputBar.js +0 -618
  112. package/dist/tui/MessageList.js +0 -718
  113. package/dist/tui/ModelPicker.js +0 -113
  114. package/dist/tui/QuestionBar.js +0 -136
  115. package/dist/tui/SessionPicker.js +0 -79
  116. package/dist/tui/Spinner.js +0 -6
  117. package/dist/tui/StatusBar.js +0 -22
  118. package/dist/tui/ThinkPicker.js +0 -75
  119. package/dist/tui/agent-runner.js +0 -223
  120. package/dist/tui/bracketed-paste.js +0 -37
  121. package/dist/tui/caret-pos.js +0 -179
  122. package/dist/tui/caret.js +0 -69
  123. package/dist/tui/click-count.js +0 -13
  124. package/dist/tui/diff-view.js +0 -61
  125. package/dist/tui/drag-state.js +0 -49
  126. package/dist/tui/hydrate.js +0 -129
  127. package/dist/tui/index.js +0 -264
  128. package/dist/tui/input-history.js +0 -125
  129. package/dist/tui/layout.js +0 -105
  130. package/dist/tui/mouse.js +0 -46
  131. package/dist/tui/overlay-input.js +0 -12
  132. package/dist/tui/paste-draft.js +0 -173
  133. package/dist/tui/prompt-queue.js +0 -24
  134. package/dist/tui/selection.js +0 -232
  135. package/dist/tui/session-switch.js +0 -28
  136. package/dist/tui/slash-commands.js +0 -129
  137. package/dist/tui/slash-handler.js +0 -615
  138. package/dist/tui/text-width.js +0 -113
  139. package/dist/tui/theme.js +0 -12
  140. package/dist/tui/token-info.js +0 -7
  141. package/dist/tui/tool-children.js +0 -19
  142. package/dist/tui/types.js +0 -1
  143. package/dist/tui/undo-stack.js +0 -14
  144. package/dist/tui/use-sgr-mouse.js +0 -29
  145. package/dist/tui-chat.js +0 -478
  146. package/dist/updater.js +0 -116
  147. package/dist/xml-search.js +0 -194
  148. package/docs/superpowers/plans/2026-08-16-batch1-tui-improvements.md +0 -1510
  149. package/docs/superpowers/plans/2026-08-16-batch2-cli-tools-api.md +0 -2105
  150. package/docs/superpowers/plans/2026-08-16-batch3-config-engineering.md +0 -1595
  151. package/docs/superpowers/plans/2026-08-16-input-caret.md +0 -782
  152. package/docs/superpowers/plans/2026-08-20-tui-completeness.md +0 -873
  153. package/docs/superpowers/plans/2026-08-20-unified-tui-default.md +0 -631
  154. package/docs/superpowers/plans/2026-08-23-cli-setup.md +0 -501
  155. package/docs/superpowers/plans/2026-08-23-input-paste-attachments.md +0 -475
  156. package/docs/superpowers/plans/2026-08-23-thinking-wire-profile.md +0 -450
  157. package/docs/superpowers/specs/2026-08-16-batch1-tui-improvements-design.md +0 -183
  158. package/docs/superpowers/specs/2026-08-16-batch2-cli-tools-api-design.md +0 -220
  159. package/docs/superpowers/specs/2026-08-16-batch3-config-engineering-design.md +0 -196
  160. package/docs/superpowers/specs/2026-08-16-input-caret-design.md +0 -63
  161. package/docs/superpowers/specs/2026-08-17-mouse-selection-design.md +0 -116
  162. package/docs/superpowers/specs/2026-08-20-config-http-alignment-design.md +0 -47
  163. package/docs/superpowers/specs/2026-08-20-mcp-plugins-alignment-design.md +0 -37
  164. package/docs/superpowers/specs/2026-08-20-sandbox-permissions-design.md +0 -68
  165. package/docs/superpowers/specs/2026-08-20-tui-completeness-design.md +0 -273
  166. package/docs/superpowers/specs/2026-08-20-unified-tui-default-design.md +0 -165
  167. package/docs/superpowers/specs/2026-08-23-cli-setup-design.md +0 -282
  168. package/docs/superpowers/specs/2026-08-23-input-paste-attachments-design.md +0 -174
  169. package/docs/superpowers/specs/2026-08-23-thinking-wire-profile-design.md +0 -140
package/dist/config.js DELETED
@@ -1,363 +0,0 @@
1
- import { readFileSync, mkdirSync, existsSync, statSync } from "fs";
2
- import path from "path";
3
- import os from "os";
4
- import { atomicWriteFileSync } from "./tools/atomic-file.js";
5
- import { getEffectiveSandboxPolicy, mergeSandboxConfig, parseSandboxConfig, sandboxLaunchSource, sandboxStatusLabel, } from "./sandbox.js";
6
- const DEFAULT_CONFIG_DIR = path.join(os.homedir(), ".min-agent");
7
- const CONFIG_FILE_NAME = "config.json";
8
- const RULES_FILE_NAME = "rules.md";
9
- export function getConfigDir() {
10
- return process.env.MIN_AGENT_CONFIG_DIR ?? DEFAULT_CONFIG_DIR;
11
- }
12
- export function getRulesFile() {
13
- return path.join(getConfigDir(), RULES_FILE_NAME);
14
- }
15
- let cachedConfig = null;
16
- const projectCache = new Map();
17
- export function getProjectConfigDir() {
18
- return path.join(process.cwd(), ".min-agent");
19
- }
20
- export function getProjectConfigFile() {
21
- return path.join(getProjectConfigDir(), CONFIG_FILE_NAME);
22
- }
23
- export function loadProjectConfig() {
24
- const file = getProjectConfigFile();
25
- let st;
26
- try {
27
- st = statSync(file);
28
- }
29
- catch {
30
- return {};
31
- }
32
- const cached = projectCache.get(file);
33
- if (cached && cached.mtimeMs === st.mtimeMs && cached.size === st.size)
34
- return cached.config;
35
- let config;
36
- try {
37
- config = JSON.parse(readFileSync(file, "utf-8"));
38
- }
39
- catch {
40
- console.warn(`\x1b[33m ⚠ Project config ${file} is not valid JSON; ignoring\x1b[0m`);
41
- return {};
42
- }
43
- projectCache.set(file, { mtimeMs: st.mtimeMs, size: st.size, config });
44
- return config;
45
- }
46
- export function saveProjectConfig(config) {
47
- const dir = getProjectConfigDir();
48
- mkdirSync(dir, { recursive: true });
49
- // Keep unknown keys, drop empty skill arrays so the file stays minimal.
50
- const { disabledSkills, enabledSkills, ...rest } = config;
51
- atomicWriteFileSync(getProjectConfigFile(), JSON.stringify({
52
- ...rest,
53
- ...(disabledSkills?.length ? { disabledSkills } : {}),
54
- ...(enabledSkills?.length ? { enabledSkills } : {}),
55
- }, null, 2));
56
- projectCache.delete(getProjectConfigFile());
57
- }
58
- export function loadConfigFile(configFile) {
59
- let st;
60
- try {
61
- st = statSync(configFile);
62
- }
63
- catch {
64
- return {};
65
- }
66
- if (cachedConfig &&
67
- cachedConfig.file === configFile &&
68
- cachedConfig.mtimeMs === st.mtimeMs &&
69
- cachedConfig.size === st.size) {
70
- return cachedConfig.config;
71
- }
72
- let config;
73
- try {
74
- config = JSON.parse(readFileSync(configFile, "utf-8"));
75
- }
76
- catch {
77
- console.warn(`\x1b[33m ⚠ Config file ${configFile} is not valid JSON; using defaults\x1b[0m`);
78
- return {};
79
- }
80
- cachedConfig = { file: configFile, mtimeMs: st.mtimeMs, size: st.size, config };
81
- return config;
82
- }
83
- export function loadConfig() {
84
- const raw = loadConfigFile(path.join(getConfigDir(), CONFIG_FILE_NAME));
85
- const { config, migrated } = migrateLegacyConfig(raw);
86
- if (migrated) {
87
- try {
88
- saveConfig(config);
89
- }
90
- catch { }
91
- }
92
- return config;
93
- }
94
- export function migrateLegacyConfig(config) {
95
- const legacy = config.provider;
96
- if (!legacy)
97
- return { config, migrated: false };
98
- const { provider, ...rest } = config;
99
- return {
100
- config: { ...rest, providers: [{ name: "default", ...provider }], activeProvider: "default" },
101
- migrated: true,
102
- };
103
- }
104
- export function mergeProjectOverGlobal(global, project) {
105
- const activeProvider = project.activeProvider ?? global.activeProvider;
106
- const sampling = project.sampling ? { ...global.sampling, ...project.sampling } : global.sampling;
107
- const budget = project.budget ? { ...global.budget, ...project.budget } : global.budget;
108
- const permission = parsePermissionMode(project.permission) ?? parsePermissionMode(global.permission);
109
- const thinking = parseThinkingEffort(project.thinking) ?? parseThinkingEffort(global.thinking);
110
- const memory = parseMemoryMode(project.memory) ?? parseMemoryMode(global.memory);
111
- const sandbox = mergeSandboxConfig(parseSandboxConfig(global.sandbox), parseSandboxConfig(project.sandbox));
112
- const compaction = project.compaction ? { ...global.compaction, ...project.compaction } : global.compaction;
113
- const agent = project.agent ? { ...global.agent, ...project.agent } : global.agent;
114
- let providers = global.providers;
115
- if (project.defaultModel && providers && providers.length > 0) {
116
- const idx = activeProvider ? providers.findIndex((p) => p.name === activeProvider) : 0;
117
- const target = idx >= 0 ? idx : 0;
118
- providers = providers.map((p, i) => (i === target ? { ...p, defaultModel: project.defaultModel } : p));
119
- }
120
- return {
121
- ...global,
122
- activeProvider,
123
- sampling,
124
- budget,
125
- permission,
126
- ...(thinking ? { thinking } : {}),
127
- ...(memory ? { memory } : {}),
128
- ...(sandbox ? { sandbox } : {}),
129
- compaction,
130
- agent,
131
- ...(providers ? { providers } : {}),
132
- };
133
- }
134
- export function getEffectiveConfig() {
135
- return mergeProjectOverGlobal(loadConfig(), loadProjectConfig());
136
- }
137
- export function getBudgetSnapshot() {
138
- const global = loadConfig();
139
- const project = loadProjectConfig();
140
- const maxCostUSD = mergeProjectOverGlobal(global, project).budget?.maxCostUSD;
141
- const source = project.budget?.maxCostUSD != null ? "project" : global.budget?.maxCostUSD != null ? "global" : null;
142
- return { maxCostUSD, source };
143
- }
144
- export function parsePermissionMode(value) {
145
- if (value === "ask" || value === "accept-edits" || value === "allow-all")
146
- return value;
147
- return undefined;
148
- }
149
- const THINKING_ALIASES = {
150
- off: "off",
151
- none: "off",
152
- low: "low",
153
- minimal: "low",
154
- medium: "medium",
155
- high: "high",
156
- max: "max",
157
- xhigh: "max",
158
- "extra-high": "max",
159
- extra_high: "max",
160
- extrahigh: "max",
161
- };
162
- export function parseThinkingEffort(value) {
163
- if (typeof value !== "string")
164
- return undefined;
165
- return THINKING_ALIASES[value.trim().toLowerCase()];
166
- }
167
- const MEMORY_ALIASES = {
168
- on: "on",
169
- true: "on",
170
- enable: "on",
171
- enabled: "on",
172
- off: "off",
173
- false: "off",
174
- disable: "off",
175
- disabled: "off",
176
- };
177
- export function parseMemoryMode(value) {
178
- if (value === true)
179
- return "on";
180
- if (value === false)
181
- return "off";
182
- if (typeof value !== "string")
183
- return undefined;
184
- return MEMORY_ALIASES[value.trim().toLowerCase()];
185
- }
186
- export function permissionModeLabel(mode) {
187
- if (mode === "allow-all")
188
- return "allow all";
189
- if (mode === "accept-edits")
190
- return "accept edits";
191
- return "ask";
192
- }
193
- export function getPermissionSnapshot() {
194
- const global = loadConfig();
195
- const project = loadProjectConfig();
196
- const permission = parsePermissionMode(project.permission) ?? parsePermissionMode(global.permission) ?? "ask";
197
- const source = parsePermissionMode(project.permission) != null
198
- ? "project"
199
- : parsePermissionMode(global.permission) != null
200
- ? "global"
201
- : null;
202
- return { permission, source };
203
- }
204
- export function setPermissionMode(permission, scope) {
205
- if (scope === "project") {
206
- const project = loadProjectConfig();
207
- saveProjectConfig({ ...project, permission });
208
- return;
209
- }
210
- saveConfig({ ...loadConfig(), permission });
211
- }
212
- export function setSandboxConfig(partial, scope) {
213
- if (scope === "project") {
214
- const project = loadProjectConfig();
215
- const current = parseSandboxConfig(project.sandbox) ?? {};
216
- saveProjectConfig({ ...project, sandbox: { ...current, ...partial } });
217
- return;
218
- }
219
- const cfg = loadConfig();
220
- const current = parseSandboxConfig(cfg.sandbox) ?? {};
221
- saveConfig({ ...cfg, sandbox: { ...current, ...partial } });
222
- }
223
- export function getSandboxSnapshot() {
224
- const policy = getEffectiveSandboxPolicy();
225
- const launch = sandboxLaunchSource();
226
- const project = parseSandboxConfig(loadProjectConfig().sandbox);
227
- const global = parseSandboxConfig(loadConfig().sandbox);
228
- const projectSet = Boolean(project?.mode || project?.network || project?.extraWriteRoots?.length || project?.extraReadRoots?.length);
229
- const globalSet = Boolean(global?.mode || global?.network || global?.extraWriteRoots?.length || global?.extraReadRoots?.length);
230
- const source = launch ?? (projectSet ? "project" : globalSet ? "global" : null);
231
- return {
232
- mode: policy.mode,
233
- network: policy.network,
234
- extraWriteRoots: policy.extraWriteRoots,
235
- extraReadRoots: policy.extraReadRoots,
236
- source,
237
- label: sandboxStatusLabel(policy),
238
- };
239
- }
240
- export function setBudgetMaxCost(maxCostUSD, scope) {
241
- if (scope === "project") {
242
- const project = loadProjectConfig();
243
- saveProjectConfig({ ...project, budget: { ...project.budget, maxCostUSD } });
244
- return;
245
- }
246
- const cfg = loadConfig();
247
- saveConfig({ ...cfg, budget: { ...cfg.budget, maxCostUSD } });
248
- }
249
- export function getActiveProvider(config) {
250
- if (!config.providers || config.providers.length === 0)
251
- return undefined;
252
- if (config.activeProvider) {
253
- const found = config.providers.find((p) => p.name === config.activeProvider);
254
- if (found)
255
- return found;
256
- }
257
- return config.providers[0];
258
- }
259
- /** Appends /v1 to an Ollama base URL unless already present */
260
- export function normalizeOllamaBaseURL(baseURL) {
261
- const trimmed = baseURL.replace(/\/$/, "");
262
- return trimmed.endsWith("/v1") ? trimmed : `${trimmed}/v1`;
263
- }
264
- export function saveConfig(config) {
265
- const dir = getConfigDir();
266
- mkdirSync(dir, { recursive: true });
267
- atomicWriteFileSync(path.join(dir, CONFIG_FILE_NAME), JSON.stringify(config, null, 2));
268
- cachedConfig = null;
269
- }
270
- export function isConfigured() {
271
- const config = loadConfig();
272
- const provider = getActiveProvider(config);
273
- return !!(provider?.baseURL && provider?.apiKey);
274
- }
275
- async function fetchModelsFromURL(url, apiKey) {
276
- const noCacheURL = new URL(url);
277
- noCacheURL.searchParams.set("_t", Date.now().toString());
278
- const response = await fetch(noCacheURL.toString(), {
279
- headers: {
280
- Authorization: `Bearer ${apiKey}`,
281
- "Cache-Control": "no-cache, no-store, must-revalidate",
282
- Pragma: "no-cache",
283
- Expires: "0",
284
- },
285
- cache: "no-store",
286
- signal: AbortSignal.timeout(10000),
287
- });
288
- if (!response.ok)
289
- return { models: [], ok: false, status: response.status };
290
- const data = await response.json();
291
- return { models: parseModelIds(data), ok: true, status: response.status };
292
- }
293
- function parseModelIds(data) {
294
- const list = Array.isArray(data)
295
- ? data
296
- : data !== null && typeof data === "object" && "data" in data && Array.isArray(data.data)
297
- ? data.data
298
- : [];
299
- return list
300
- .map((item) => item !== null && typeof item === "object" && "id" in item && typeof item.id === "string" ? item.id : null)
301
- .filter((id) => id != null)
302
- .sort();
303
- }
304
- const MODELS_CACHE_FILE = "models-cache.json";
305
- function modelsCachePath() {
306
- return path.join(getConfigDir(), MODELS_CACHE_FILE);
307
- }
308
- function modelsCacheKey(baseURL) {
309
- return baseURL.replace(/\/$/, "");
310
- }
311
- function loadModelsCacheMap() {
312
- const file = modelsCachePath();
313
- if (!existsSync(file))
314
- return {};
315
- try {
316
- const parsed = JSON.parse(readFileSync(file, "utf-8"));
317
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
318
- return {};
319
- return Object.fromEntries(Object.entries(parsed).filter((entry) => Array.isArray(entry[1]) && entry[1].every((item) => typeof item === "string")));
320
- }
321
- catch {
322
- return {};
323
- }
324
- }
325
- function loadModelsCacheBucket(baseURL) {
326
- return loadModelsCacheMap()[modelsCacheKey(baseURL)] ?? [];
327
- }
328
- function saveModelsCacheBucket(baseURL, models) {
329
- const map = loadModelsCacheMap();
330
- map[modelsCacheKey(baseURL)] = models;
331
- mkdirSync(getConfigDir(), { recursive: true });
332
- atomicWriteFileSync(modelsCachePath(), JSON.stringify(map));
333
- }
334
- export async function fetchModelsLive(baseURL, apiKey) {
335
- try {
336
- const trimmed = modelsCacheKey(baseURL);
337
- let result = await fetchModelsFromURL(`${trimmed}/models`, apiKey);
338
- if (result.models.length === 0 && !trimmed.endsWith("/v1")) {
339
- result = await fetchModelsFromURL(`${trimmed}/v1/models`, apiKey);
340
- }
341
- if (result.ok && result.models.length > 0) {
342
- saveModelsCacheBucket(trimmed, result.models);
343
- return { models: result.models, ok: true, status: result.status };
344
- }
345
- if (!result.ok)
346
- return { models: [], ok: false, status: result.status };
347
- return { models: [], ok: true, status: result.status };
348
- }
349
- catch {
350
- return { models: [], ok: false };
351
- }
352
- }
353
- export async function fetchModels(baseURL, apiKey) {
354
- const live = await fetchModelsLive(baseURL, apiKey);
355
- if (live.models.length > 0)
356
- return live.models;
357
- const cached = loadModelsCacheBucket(baseURL);
358
- if (cached.length > 0) {
359
- const suffix = live.status == null && !live.ok ? " — network unavailable" : "";
360
- console.log(`\x1b[90m (using cached model list${suffix})\x1b[0m`);
361
- }
362
- return cached;
363
- }
package/dist/confirm.js DELETED
@@ -1,172 +0,0 @@
1
- import { AsyncLocalStorage } from "async_hooks";
2
- import { getEffectiveConfig } from "./config.js";
3
- import { normalizeQuestionOptions, normalizeQuestionPrompt } from "./question-format.js";
4
- import readline from "readline";
5
- let autoApprove = false;
6
- export function setAutoApprove(value) {
7
- autoApprove = value;
8
- }
9
- export function isAutoApprove() {
10
- const ctx = confirmAls.getStore();
11
- if (ctx)
12
- return ctx.autoApprove;
13
- if (autoApprove)
14
- return true;
15
- return getPermissionMode() === "allow-all";
16
- }
17
- let permissionOverride;
18
- export function setPermissionOverride(mode) {
19
- permissionOverride = mode;
20
- }
21
- export function getPermissionOverride() {
22
- return permissionOverride;
23
- }
24
- export function getPermissionMode() {
25
- if (permissionOverride)
26
- return permissionOverride;
27
- const config = getEffectiveConfig();
28
- return config.permission === "allow-all" || config.permission === "accept-edits" || config.permission === "ask"
29
- ? config.permission
30
- : "ask";
31
- }
32
- export function isEditAutoApprove() {
33
- if (isAutoApprove())
34
- return true;
35
- return getPermissionMode() === "accept-edits";
36
- }
37
- const confirmAls = new AsyncLocalStorage();
38
- export function runWithConfirmContext(ctx, fn) {
39
- return confirmAls.run(ctx, fn);
40
- }
41
- /** Optional readline interface to pause/resume during confirmation prompts. */
42
- let _rl = null;
43
- export function setConfirmReadline(rl) {
44
- _rl = rl;
45
- }
46
- /** Optional TUI confirm handler — when set, confirm() delegates to the TUI overlay. */
47
- let _tuiConfirm = null;
48
- export function setTuiConfirm(handler) {
49
- _tuiConfirm = handler;
50
- }
51
- /** Optional TUI question handler — when set, askQuestion() delegates to the TUI. Returns null on cancel. */
52
- let _tuiQuestion = null;
53
- export function setTuiQuestion(handler) {
54
- _tuiQuestion = handler;
55
- }
56
- /** Ask the user a free-form question. Returns the answer, or null if cancelled. */
57
- export async function askQuestion(prompt, options) {
58
- const safePrompt = normalizeQuestionPrompt(prompt);
59
- const choices = normalizeQuestionOptions(options);
60
- const safeOptions = choices.length > 0 ? choices : undefined;
61
- const ctx = confirmAls.getStore();
62
- if (ctx) {
63
- if (ctx.autoApprove)
64
- return null;
65
- if (ctx.onQuestion)
66
- return ctx.onQuestion(safePrompt, safeOptions);
67
- return null;
68
- }
69
- if (_tuiQuestion)
70
- return _tuiQuestion(safePrompt, safeOptions);
71
- if (!process.stdin.isTTY) {
72
- console.log(`\n\x1b[33m❓ ${safePrompt} (no interactive terminal — skipped)\x1b[0m`);
73
- return null;
74
- }
75
- console.log();
76
- console.log(`\x1b[33m❓ ${safePrompt}\x1b[0m`);
77
- if (safeOptions) {
78
- for (let i = 0; i < safeOptions.length; i++) {
79
- console.log(`\x1b[90m ${i + 1}. ${safeOptions[i]}\x1b[0m`);
80
- }
81
- }
82
- return new Promise((resolve) => {
83
- const rl = readline.createInterface({
84
- input: process.stdin,
85
- output: process.stdout,
86
- });
87
- rl.question("\x1b[36m → \x1b[0m", (answer) => {
88
- rl.close();
89
- resolve(answer.trim() || "(no answer)");
90
- });
91
- });
92
- }
93
- /**
94
- * Ask user for confirmation. Returns true if approved.
95
- * Display order: detail first, then [y/N] prompt at the bottom.
96
- */
97
- export async function confirm(message) {
98
- const ctx = confirmAls.getStore();
99
- if (ctx) {
100
- if (ctx.autoApprove)
101
- return true;
102
- if (ctx.onConfirm)
103
- return ctx.onConfirm(message);
104
- return false;
105
- }
106
- if (isAutoApprove())
107
- return true;
108
- // If TUI mode is active, delegate to the TUI overlay
109
- if (_tuiConfirm)
110
- return _tuiConfirm(message);
111
- // Non-interactive stdin can never answer — reject instead of hanging
112
- if (!process.stdin.isTTY) {
113
- console.log(`\n\x1b[33m⚠ Rejected (no interactive terminal): ${message}\x1b[0m`);
114
- return false;
115
- }
116
- // Pause readline so it doesn't consume/echo the keystroke
117
- _rl?.pause();
118
- // Detail on top, prompt at the bottom
119
- process.stdout.write(`\n\x1b[90m┌─ 即将执行 ─────────────────────────────────\x1b[0m\n`);
120
- process.stdout.write(`\x1b[90m│\x1b[0m ${message}\n`);
121
- process.stdout.write(`\x1b[90m└────────────────────────────────────────────\x1b[0m\n`);
122
- process.stdout.write(`\n\x1b[1;34m? 是否允许执行? [y/N] \x1b[0m`);
123
- return new Promise((resolve) => {
124
- const wasRaw = process.stdin.isRaw;
125
- if (process.stdin.isTTY)
126
- process.stdin.setRawMode(true);
127
- // Ensure stdin is flowing so we can receive data even after readline pause
128
- process.stdin.resume();
129
- const onData = (buf) => {
130
- const ch = buf.toString();
131
- process.stdin.removeListener("data", onData);
132
- if (process.stdin.isTTY)
133
- process.stdin.setRawMode(wasRaw ?? false);
134
- // Echo the character and newline
135
- process.stdout.write(ch === "\r" || ch === "\n" ? "\n\n" : `${ch}\n\n`);
136
- // Resume readline after confirmation
137
- _rl?.resume();
138
- const answer = ch.trim().toLowerCase();
139
- resolve(answer === "y");
140
- };
141
- process.stdin.on("data", onData);
142
- });
143
- }
144
- /** Check if a shell command is potentially dangerous */
145
- export function isDangerousCommand(command) {
146
- const dangerous = [
147
- /\brm\s+(-[a-z]*[rf][a-z]*|--recursive)\s/,
148
- /\bsudo\b/,
149
- /\bmkfs\b/,
150
- /\bdd\s+/,
151
- /\b(shutdown|reboot|halt|poweroff)\b/,
152
- /\bgit\s+(push|reset\s+--hard|clean\s+-[a-z]*f|rebase)/,
153
- /\bnpm\s+(publish|unpublish)\b/,
154
- /\b(drop|truncate)\s+(table|database)\b/i,
155
- /\bformat\b.*\b[a-z]:\b/i,
156
- /\bfind\b[^|;&]*\s+-delete\b/,
157
- /\|\s*(ba|z|fi)?sh\b/,
158
- /\bchmod\s+(-[a-zA-Z]+\s+)?[0-7]{3,4}\b/,
159
- /\bchown\s+-R\b/,
160
- /\bkill(all)?\s+(-9|-[a-z]*9)/,
161
- /\b(pkill|killall)\b/,
162
- /\bgit\s+checkout\s+\./,
163
- /\bgit\s+stash\b/,
164
- /\b(docker|podman)\s+(rm|prune|system)\b/,
165
- /\b(shred|wipefs)\b/,
166
- /\b(cp|mv)\s+-[a-z]*f\b/,
167
- /[^<>=!&]\s*>>?\s*[^<>=&]|^\s*>>?\s*[^<>=&]/,
168
- /:\(\)\s*\{/,
169
- />+\s*\/dev\/(sd[a-z]+|nvme\d|disk\d)/,
170
- ];
171
- return dangerous.some((re) => re.test(command));
172
- }