min-agent 0.3.0 → 0.4.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 (120) hide show
  1. package/README.md +111 -28
  2. package/dist/agent.js +1119 -256
  3. package/dist/cli/commands/chat.js +10 -0
  4. package/dist/cli/commands/exec.js +32 -0
  5. package/dist/cli/commands/history.js +58 -0
  6. package/dist/cli/commands/index.js +224 -0
  7. package/dist/cli/commands/init.js +18 -0
  8. package/dist/cli/commands/mcp.js +173 -0
  9. package/dist/cli/commands/memory.js +69 -0
  10. package/dist/cli/commands/models.js +21 -0
  11. package/dist/cli/commands/permission.js +12 -0
  12. package/dist/cli/commands/rules.js +33 -0
  13. package/dist/cli/commands/sandbox.js +13 -0
  14. package/dist/cli/commands/serve.js +9 -0
  15. package/dist/cli/commands/setup.js +4 -0
  16. package/dist/cli/commands/shared.js +16 -0
  17. package/dist/cli/commands/skills.js +119 -0
  18. package/dist/cli/commands/update.js +7 -0
  19. package/dist/cli/commands/write-config.js +30 -0
  20. package/dist/cli/errors.js +36 -0
  21. package/dist/cli/exec-prompt.js +26 -0
  22. package/dist/cli/option-helpers.js +53 -0
  23. package/dist/cli/program.js +180 -0
  24. package/dist/cli.js +5 -888
  25. package/dist/code-mode.js +32 -14
  26. package/dist/compaction.js +347 -160
  27. package/dist/config.js +119 -10
  28. package/dist/confirm.js +56 -9
  29. package/dist/context-window.js +107 -39
  30. package/dist/doom-loop.js +264 -29
  31. package/dist/fetch-timeout.js +152 -0
  32. package/dist/http-approvals.js +60 -0
  33. package/dist/instructions.js +21 -0
  34. package/dist/logger.js +33 -4
  35. package/dist/markdown.js +37 -11
  36. package/dist/mcp.js +328 -30
  37. package/dist/memory.js +97 -56
  38. package/dist/output.js +7 -5
  39. package/dist/permission-cli.js +43 -0
  40. package/dist/plugins.js +46 -8
  41. package/dist/pricing.js +4 -4
  42. package/dist/provider.js +23 -6
  43. package/dist/question-format.js +60 -0
  44. package/dist/sandbox-cli.js +82 -0
  45. package/dist/sandbox.js +403 -0
  46. package/dist/save-throttle.js +45 -0
  47. package/dist/serve/common.js +404 -0
  48. package/dist/serve/routes-chat.js +347 -0
  49. package/dist/serve/routes-mcp.js +212 -0
  50. package/dist/serve/routes-memory.js +66 -0
  51. package/dist/serve/routes-meta.js +205 -0
  52. package/dist/serve/routes-sessions.js +61 -0
  53. package/dist/serve/routes-skills.js +70 -0
  54. package/dist/serve.js +33 -883
  55. package/dist/sessions.js +53 -9
  56. package/dist/skills.js +82 -18
  57. package/dist/title-gen.js +8 -2
  58. package/dist/token-display.js +36 -0
  59. package/dist/tool-display.js +5 -0
  60. package/dist/tool-output.js +1 -3
  61. package/dist/tools/apply_patch.js +85 -11
  62. package/dist/tools/atomic-file.js +35 -0
  63. package/dist/tools/backend.js +2 -2
  64. package/dist/tools/bash.js +57 -19
  65. package/dist/tools/code_search.js +7 -1
  66. package/dist/tools/edit.js +11 -10
  67. package/dist/tools/explore.js +74 -14
  68. package/dist/tools/glob.js +4 -0
  69. package/dist/tools/grep.js +17 -10
  70. package/dist/tools/index.js +6 -21
  71. package/dist/tools/question.js +28 -9
  72. package/dist/tools/read.js +6 -4
  73. package/dist/tools/search-searxng.js +223 -0
  74. package/dist/tools/search-serper.js +189 -0
  75. package/dist/tools/task.js +84 -30
  76. package/dist/tools/todo.js +120 -19
  77. package/dist/tools/web_fetch.js +11 -3
  78. package/dist/tools/web_search.js +66 -556
  79. package/dist/tools/write.js +23 -6
  80. package/dist/tui/App.js +63 -14
  81. package/dist/tui/ConfirmBar.js +45 -13
  82. package/dist/tui/InputBar.js +150 -35
  83. package/dist/tui/MessageList.js +266 -125
  84. package/dist/tui/ModelPicker.js +8 -3
  85. package/dist/tui/QuestionBar.js +51 -19
  86. package/dist/tui/SessionPicker.js +79 -0
  87. package/dist/tui/StatusBar.js +8 -14
  88. package/dist/tui/agent-runner.js +142 -22
  89. package/dist/tui/caret-pos.js +48 -5
  90. package/dist/tui/caret.js +1 -1
  91. package/dist/tui/click-count.js +13 -0
  92. package/dist/tui/drag-state.js +8 -3
  93. package/dist/tui/hydrate.js +129 -0
  94. package/dist/tui/index.js +42 -13
  95. package/dist/tui/input-history.js +92 -11
  96. package/dist/tui/layout.js +75 -4
  97. package/dist/tui/prompt-queue.js +24 -0
  98. package/dist/tui/selection.js +113 -21
  99. package/dist/tui/session-switch.js +28 -0
  100. package/dist/tui/slash-commands.js +22 -6
  101. package/dist/tui/slash-handler.js +233 -58
  102. package/dist/tui/text-width.js +38 -16
  103. package/dist/tui/token-info.js +7 -0
  104. package/dist/tui/tool-children.js +19 -0
  105. package/dist/tui/undo-stack.js +1 -1
  106. package/dist/tui/use-sgr-mouse.js +3 -1
  107. package/dist/tui-chat.js +276 -40
  108. package/dist/updater.js +88 -29
  109. package/dist/xml-search.js +194 -0
  110. package/docs/API.md +257 -25
  111. package/docs/superpowers/plans/2026-08-20-tui-completeness.md +873 -0
  112. package/docs/superpowers/plans/2026-08-20-unified-tui-default.md +631 -0
  113. package/docs/superpowers/specs/2026-08-20-config-http-alignment-design.md +47 -0
  114. package/docs/superpowers/specs/2026-08-20-mcp-plugins-alignment-design.md +37 -0
  115. package/docs/superpowers/specs/2026-08-20-sandbox-permissions-design.md +68 -0
  116. package/docs/superpowers/specs/2026-08-20-tui-completeness-design.md +273 -0
  117. package/docs/superpowers/specs/2026-08-20-unified-tui-default-design.md +165 -0
  118. package/package.json +6 -1
  119. package/skills/self-config/SKILL.md +90 -0
  120. package/skills/self-config/reference.md +149 -0
@@ -0,0 +1,9 @@
1
+ import { isConfigured } from "../../config.js";
2
+ import { CliError } from "../errors.js";
3
+ export async function runServeCommand(opts) {
4
+ if (!isConfigured()) {
5
+ throw new CliError("Not configured.", { hint: "Run: min-agent setup" });
6
+ }
7
+ const { runServe } = await import("../../serve.js");
8
+ await runServe({ port: opts.port, host: opts.host });
9
+ }
@@ -0,0 +1,4 @@
1
+ import { runSetup } from "../../config.js";
2
+ export async function runSetupCommand() {
3
+ await runSetup();
4
+ }
@@ -0,0 +1,16 @@
1
+ import { isConfigured } from "../../config.js";
2
+ import { CliError } from "../errors.js";
3
+ export async function startTuiSession(input) {
4
+ if (!isConfigured()) {
5
+ throw new CliError("Not configured.", { hint: "Run: min-agent setup" });
6
+ }
7
+ const { runTui } = await import("../../tui-chat.js");
8
+ const prompt = input.positionals.join(" ").trim();
9
+ await runTui({
10
+ modelId: input.model,
11
+ providerName: input.provider,
12
+ resumeSessionId: input.resume,
13
+ images: input.images,
14
+ initialPrompt: prompt || undefined,
15
+ });
16
+ }
@@ -0,0 +1,119 @@
1
+ import { discoverSkills } from "../../skills.js";
2
+ import { existsSync, writeFileSync, mkdirSync } from "fs";
3
+ import path from "path";
4
+ import os from "os";
5
+ import { CliError } from "../errors.js";
6
+ /** One-line, length-capped text for list output. */
7
+ function truncate(text, max) {
8
+ const flat = text.replace(/\s+/g, " ").trim();
9
+ return flat.length <= max ? flat : `${flat.slice(0, max - 1)}…`;
10
+ }
11
+ export async function runSkillsList(json) {
12
+ discoverSkills({ silent: true });
13
+ const { getAllSkills, getStaleDisabledSkills } = await import("../../skills.js");
14
+ const all = getAllSkills().sort((a, b) => a.name.localeCompare(b.name));
15
+ const stale = getStaleDisabledSkills();
16
+ if (json) {
17
+ console.log(JSON.stringify({
18
+ count: all.length,
19
+ enabled_count: all.filter((s) => s.enabled).length,
20
+ stale_disabled: stale,
21
+ skills: all.map((s) => ({
22
+ name: s.name,
23
+ description: s.description,
24
+ location: s.location,
25
+ enabled: s.enabled,
26
+ ...(s.disabledScope ? { disabled_scope: s.disabledScope } : {}),
27
+ ...(s.version ? { version: s.version } : {}),
28
+ ...(s.allowedTools.length ? { allowed_tools: s.allowedTools } : {}),
29
+ ...(s.requiredBins.length ? { requires_bins: s.requiredBins } : {}),
30
+ ...(s.builtin ? { builtin: true } : {}),
31
+ ...(s.alwaysLoad ? { always_load: true } : {}),
32
+ })),
33
+ }, null, 2));
34
+ return;
35
+ }
36
+ if (all.length === 0 && stale.length === 0) {
37
+ console.log("No skills found. Add SKILL.md files in .min-agent/skills/<name>/");
38
+ return;
39
+ }
40
+ console.log(`Skills (${all.filter((s) => s.enabled).length} enabled, ${all.filter((s) => !s.enabled).length} disabled):`);
41
+ for (const skill of all) {
42
+ const status = skill.enabled
43
+ ? "\x1b[32menabled \x1b[0m"
44
+ : `\x1b[90mdisabled(${skill.disabledScope === "project" ? "proj" : "glob"})\x1b[0m`;
45
+ const origin = skill.builtin ? " \x1b[90mbuiltin\x1b[0m" : "";
46
+ console.log(` ${skill.name} ${status}${origin} \x1b[90m${truncate(skill.description, 60)}\x1b[0m`);
47
+ console.log(` \x1b[90m${skill.location}\x1b[0m`);
48
+ }
49
+ if (stale.length > 0) {
50
+ console.log(`\n\x1b[33m⚠ disabledSkills/enabledSkills entries matching no skill: ${stale.join(", ")}\x1b[0m`);
51
+ console.log(`\x1b[33m Remove them with: min-agent skills enable ${stale.join(" ")} (add --project if the entry is project-scoped)\x1b[0m`);
52
+ }
53
+ console.log("\nUse: min-agent skills info <name> for details");
54
+ }
55
+ export async function runSkillsInfo(name, json) {
56
+ discoverSkills({ silent: true });
57
+ const { getSkill, getAllSkills } = await import("../../skills.js");
58
+ const skill = getSkill(name);
59
+ if (!skill) {
60
+ const names = getAllSkills().map((s) => s.name);
61
+ throw new CliError(`Skill "${name}" not found.${names.length ? ` Available: ${names.join(", ")}` : " No skills discovered."}`);
62
+ }
63
+ if (json) {
64
+ console.log(JSON.stringify(skill, null, 2));
65
+ return;
66
+ }
67
+ console.log(`Name: ${skill.name}`);
68
+ console.log(`Status: ${skill.enabled ? "enabled" : `disabled (${skill.disabledScope} scope)`}`);
69
+ if (skill.builtin)
70
+ console.log("Source: builtin");
71
+ if (skill.version)
72
+ console.log(`Version: ${skill.version}`);
73
+ console.log(`Description: ${skill.description}`);
74
+ console.log(`Location: ${skill.location}`);
75
+ if (skill.allowedTools.length > 0)
76
+ console.log(`Tools: ${skill.allowedTools.join(", ")}`);
77
+ if (skill.requiredBins.length > 0)
78
+ console.log(`Requires: ${skill.requiredBins.join(", ")}`);
79
+ if (skill.content.trim()) {
80
+ console.log("\n--- Content ---\n");
81
+ console.log(skill.content.trim());
82
+ }
83
+ }
84
+ export async function runSkillsToggle(names, enabled, scope) {
85
+ const verb = enabled ? "enable" : "disable";
86
+ if (names.length === 0)
87
+ throw new CliError(`Usage: min-agent skills ${verb} <name...> [--project]`);
88
+ discoverSkills({ silent: true });
89
+ const { getAllSkills, setSkillEnabled, getStaleDisabledSkills } = await import("../../skills.js");
90
+ const known = new Set(getAllSkills().map((s) => s.name));
91
+ const stale = new Set(getStaleDisabledSkills());
92
+ // `enable` must accept stale names so they can be cleaned up from config;
93
+ // `disable` on an unknown name is always an error.
94
+ const unknown = names.filter((n) => !known.has(n) && !(enabled && stale.has(n)));
95
+ if (unknown.length > 0) {
96
+ throw new CliError(`Skill${unknown.length > 1 ? "s" : ""} not found: ${unknown.join(", ")}`, {
97
+ hint: known.size ? `Available: ${[...known].join(", ")}` : "No skills discovered.",
98
+ });
99
+ }
100
+ for (const result of setSkillEnabled(names, enabled, scope)) {
101
+ console.log(`✓ Skill "${result.name}" ${verb}d (${scope} scope)`);
102
+ if (result.blockedBy) {
103
+ console.log(`\x1b[33m ⚠ still ${result.enabled ? "enabled" : "disabled"} — overridden by ${result.blockedBy} scope\x1b[0m`);
104
+ }
105
+ }
106
+ }
107
+ export async function runSkillsNew(name, global) {
108
+ if (!/^[\w-]+$/.test(name)) {
109
+ throw new CliError("Invalid skill name", { hint: "use letters, digits, _ or -" });
110
+ }
111
+ const root = global ? path.join(os.homedir(), ".agents", "skills") : path.join(process.cwd(), ".min-agent", "skills");
112
+ const dir = path.join(root, name);
113
+ if (existsSync(path.join(dir, "SKILL.md"))) {
114
+ throw new CliError(`Skill "${name}" already exists at ${dir}`);
115
+ }
116
+ mkdirSync(dir, { recursive: true });
117
+ writeFileSync(path.join(dir, "SKILL.md"), `---\nname: ${name}\ndescription: What this skill does\n# version: 1.0.0\n# allowed-tools: [read, grep]\n# metadata:\n# requires:\n# bins: ["some-cli"]\n---\n\n# ${name}\n\nWrite the skill instructions here.\n`, "utf-8");
118
+ console.log(`✓ Created skill "${name}" at ${path.join(dir, "SKILL.md")}`);
119
+ }
@@ -0,0 +1,7 @@
1
+ import { runUpdate } from "../../updater.js";
2
+ export async function runUpdateCommand() {
3
+ const result = await runUpdate();
4
+ if (result.status === "check-failed" || result.status === "install-failed") {
5
+ process.exitCode = 1;
6
+ }
7
+ }
@@ -0,0 +1,30 @@
1
+ import { runSandboxCli } from "../../sandbox-cli.js";
2
+ import { runPermissionCli } from "../../permission-cli.js";
3
+ export async function runWriteConfig(input) {
4
+ const lines = [];
5
+ let ok = true;
6
+ if (input.sandbox !== undefined || input.network !== undefined) {
7
+ const result = runSandboxCli({
8
+ positionals: [],
9
+ flagMode: input.sandbox,
10
+ flagNetwork: input.network,
11
+ scope: input.scope,
12
+ });
13
+ lines.push(...result.lines);
14
+ if (!result.ok)
15
+ ok = false;
16
+ }
17
+ if (input.permission !== undefined) {
18
+ const result = runPermissionCli({
19
+ positionals: [],
20
+ flagMode: input.permission,
21
+ scope: input.scope,
22
+ });
23
+ lines.push(...result.lines);
24
+ if (!result.ok)
25
+ ok = false;
26
+ }
27
+ console.log(lines.join("\n"));
28
+ if (!ok)
29
+ process.exitCode = 1;
30
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Unified CLI error contract.
3
+ *
4
+ * Exit code convention:
5
+ * 1 - user error (bad arguments, missing input, not found, validation failure)
6
+ * 2 - runtime error (network failure, subprocess failure, unexpected exception)
7
+ */
8
+ export class CliError extends Error {
9
+ exitCode;
10
+ hint;
11
+ constructor(message, options) {
12
+ super(message);
13
+ this.name = "CliError";
14
+ this.exitCode = options?.exitCode ?? 1;
15
+ this.hint = options?.hint;
16
+ }
17
+ }
18
+ export function userError(message, hint) {
19
+ return new CliError(message, { exitCode: 1, hint });
20
+ }
21
+ export function runtimeError(message, hint) {
22
+ return new CliError(message, { exitCode: 2, hint });
23
+ }
24
+ /** Render a CliError (or any error) as the standard `✖ message` block on stderr. */
25
+ export function formatCliError(err) {
26
+ if (err instanceof CliError) {
27
+ return err.hint ? `✖ ${err.message}\n → ${err.hint}` : `✖ ${err.message}`;
28
+ }
29
+ const message = err instanceof Error ? err.message : String(err);
30
+ return `✖ ${message}`;
31
+ }
32
+ export function exitCodeFor(err) {
33
+ if (err instanceof CliError)
34
+ return err.exitCode;
35
+ return 2;
36
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Resolve the final prompt text for `min-agent exec`, combining positional
3
+ * arguments with piped stdin according to these rules:
4
+ * - `exec -` reads the entire prompt from stdin.
5
+ * - `exec` (no args) requires piped stdin (non-TTY); errors otherwise.
6
+ * - `exec <msg>` + pipe appends piped stdin after the message.
7
+ * - `exec <msg>` uses the message as-is.
8
+ */
9
+ export function resolveExecPrompt(positionals, stdinText, stdinIsTty) {
10
+ const joined = positionals.join(" ");
11
+ const piped = stdinText.length > 0;
12
+ if (joined === "-") {
13
+ const prompt = stdinText;
14
+ if (!prompt.trim())
15
+ return { ok: false, error: "Usage: min-agent exec <message>" };
16
+ return { ok: true, prompt };
17
+ }
18
+ if (!joined) {
19
+ if (stdinIsTty || !piped)
20
+ return { ok: false, error: "Usage: min-agent exec <message>" };
21
+ return { ok: true, prompt: stdinText };
22
+ }
23
+ if (piped && !stdinIsTty)
24
+ return { ok: true, prompt: `${joined}\n\n${stdinText}` };
25
+ return { ok: true, prompt: joined };
26
+ }
@@ -0,0 +1,53 @@
1
+ import { Option } from "commander";
2
+ import { parseNetworkPolicy, parseSandboxMode } from "../sandbox.js";
3
+ import { parsePermissionMode } from "../config.js";
4
+ import { userError } from "./errors.js";
5
+ /** Reusable --project / --global mutually exclusive scope selector. */
6
+ export function scopeOptions() {
7
+ return [
8
+ new Option("--project", "Apply to this project only (.min-agent/config.json)").conflicts("global"),
9
+ new Option("--global", "Apply to every project (~/.min-agent/config.json)").conflicts("project"),
10
+ ];
11
+ }
12
+ /** Resolve --project/--global into a Scope, given an optional fallback. */
13
+ export function resolveScope(opts, fallback = "global") {
14
+ if (opts.project)
15
+ return "project";
16
+ if (opts.global)
17
+ return "global";
18
+ return fallback;
19
+ }
20
+ export function parseSandboxModeArg(value) {
21
+ const parsed = parseSandboxMode(value);
22
+ if (!parsed)
23
+ throw userError(`Invalid --sandbox value: "${value}"`, "use one of: off, workspace, strict");
24
+ return parsed;
25
+ }
26
+ export function parseNetworkPolicyArg(value) {
27
+ const parsed = parseNetworkPolicy(value);
28
+ if (!parsed)
29
+ throw userError(`Invalid --network value: "${value}"`, "use one of: allow, deny");
30
+ return parsed;
31
+ }
32
+ export function parsePermissionModeArg(value) {
33
+ const parsed = parsePermissionMode(value);
34
+ if (!parsed)
35
+ throw userError(`Invalid --permission value: "${value}"`, "use one of: ask, accept-edits, allow-all");
36
+ return parsed;
37
+ }
38
+ export function parsePortArg(value) {
39
+ const n = Number.parseInt(value, 10);
40
+ if (!Number.isFinite(n) || n <= 0 || n > 65535) {
41
+ throw userError(`Invalid --port value: "${value}"`, "use an integer between 1 and 65535");
42
+ }
43
+ return n;
44
+ }
45
+ export function parsePositiveIntArg(flagName) {
46
+ return (value) => {
47
+ const n = Number.parseInt(value, 10);
48
+ if (!Number.isFinite(n) || n <= 0) {
49
+ throw userError(`Invalid ${flagName} value: "${value}"`, "use a positive integer");
50
+ }
51
+ return n;
52
+ };
53
+ }
@@ -0,0 +1,180 @@
1
+ import { Command, Option } from "commander";
2
+ import { setAutoApprove } from "../confirm.js";
3
+ import { CliError, formatCliError, exitCodeFor } from "./errors.js";
4
+ import { parseNetworkPolicyArg, parsePermissionModeArg, parseSandboxModeArg } from "./option-helpers.js";
5
+ import { localVersion } from "../updater.js";
6
+ function collectImage(value, previous) {
7
+ return [...previous, value];
8
+ }
9
+ /** Options shared across the root command and `exec` (session-affecting flags). */
10
+ export function addSessionOptions(cmd) {
11
+ return cmd
12
+ .option("-m, --model <model>", "Override model for this request")
13
+ .option("--provider <name>", "Use a specific configured provider")
14
+ .option("--resume <id>", "Resume a previous session by id")
15
+ .option("-i, --image <path>", "Attach an image (repeatable)", collectImage, [])
16
+ .addOption(new Option("--sandbox <mode>", "Isolation mode (off, workspace, strict)").argParser(parseSandboxModeArg))
17
+ .addOption(new Option("--network <policy>", "Network policy (allow, deny)").argParser(parseNetworkPolicyArg))
18
+ .addOption(new Option("--permission <mode>", "Confirmation mode (ask, accept-edits, allow-all)").argParser(parsePermissionModeArg))
19
+ .option("--project", "Write --permission/--sandbox to this repo's config")
20
+ .option("--global", "Write --permission/--sandbox to the global config")
21
+ .option("-y, --yes", "This session only: same as --permission allow-all");
22
+ }
23
+ export function buildProgram() {
24
+ const program = new Command("min-agent");
25
+ program
26
+ .description("min-agent - Minimal AI coding agent")
27
+ .version(localVersion(), "-v, --version", "Show version number")
28
+ .helpOption("-h, --help", "Show help")
29
+ .allowUnknownOption()
30
+ .enablePositionalOptions()
31
+ .configureOutput({
32
+ writeErr: (str) => process.stderr.write(str),
33
+ outputError: (str) => process.stderr.write(str),
34
+ })
35
+ .exitOverride();
36
+ addSessionOptions(program);
37
+ program.argument("[prompt...]", "Prompt to send in an interactive session");
38
+ program.addHelpText("after", `
39
+ Session:
40
+ min-agent Start interactive session
41
+ min-agent <message> Start interactive session and send message
42
+ min-agent --resume <id> Resume a previous interactive session
43
+ min-agent chat | code Same as interactive session (compat)
44
+ min-agent exec <message> Run one message non-interactively
45
+ min-agent exec --resume <id> <msg> Continue a saved exec session
46
+
47
+ Setup & config:
48
+ min-agent setup Configure providers (interactively)
49
+ min-agent init Initialize .min-agent/ in current directory
50
+ min-agent models List available models
51
+ min-agent rules [edit] Show or edit instruction rules
52
+ min-agent memory <add|search|delete|list> Manage saved memories
53
+ min-agent sandbox [off|workspace|strict] Show or set isolation mode
54
+ min-agent sandbox network <allow|deny> Show or set network policy
55
+ min-agent permission [ask|accept-edits|allow-all] Show or set confirmation mode
56
+
57
+ Integrations:
58
+ min-agent mcp <list|add|remove|info|check|enable|disable> Manage MCP servers
59
+ min-agent skills <list|info|new|enable|disable> Manage skills
60
+
61
+ Operations:
62
+ min-agent serve [--host H] [--port P] Start the HTTP API (see docs/API.md)
63
+ min-agent history [delete|rename|export] Manage saved sessions
64
+ min-agent update Upgrade to the latest npm release
65
+
66
+ Confirmation modes (default: ask; independent of isolation):
67
+ ask Prompt before dangerous operations
68
+ accept-edits Auto-approve file writes; still prompt for dangerous commands
69
+ allow-all Auto-approve everything
70
+
71
+ Isolation modes (default: off; independent of confirmation):
72
+ off No sandboxing
73
+ workspace Restrict file writes to the project workspace
74
+ strict Restrict file writes and deny risky commands
75
+ Env: MIN_AGENT_SANDBOX, MIN_AGENT_SANDBOX_NETWORK
76
+
77
+ Rules (loaded as system instructions):
78
+ Global: ~/.min-agent/rules.md
79
+ Project: ./AGENTS.md or ./RULES.md or ./.min-agent/AGENTS.md
80
+ Config: "instructions" array in ~/.min-agent/config.json
81
+
82
+ Examples:
83
+ min-agent setup
84
+ min-agent
85
+ min-agent "hello"
86
+ min-agent exec "hello"
87
+ min-agent exec --resume <id> "continue"
88
+ min-agent --resume <id>
89
+ min-agent --permission ask
90
+ min-agent serve --port 8787
91
+ min-agent update
92
+ min-agent rules edit
93
+ min-agent mcp add filesystem npx -y @modelcontextprotocol/server-filesystem /tmp
94
+ min-agent mcp add remote --url https://example.com/mcp --token "$TOKEN"
95
+ `);
96
+ return program;
97
+ }
98
+ /** Apply --yes/-y for this process. Sandbox/permission overrides are applied per-invocation-kind. */
99
+ export function applySessionOverrides(opts) {
100
+ if (opts.yes)
101
+ setAutoApprove(true);
102
+ }
103
+ export function hasSessionIntent(opts, prompt) {
104
+ return (prompt.length > 0 || Boolean(opts.resume) || opts.image.length > 0 || Boolean(opts.model) || Boolean(opts.provider));
105
+ }
106
+ /** Extract `--unknown` style tokens left over in positionals (from allowUnknownOption) as errors. */
107
+ export function extractUnknownOptionErrors(positionals) {
108
+ const rest = [];
109
+ const errors = [];
110
+ for (const p of positionals) {
111
+ if (p.startsWith("--"))
112
+ errors.push(`Unknown option: ${p}`);
113
+ else
114
+ rest.push(p);
115
+ }
116
+ return { rest, errors };
117
+ }
118
+ async function runRootAction(promptTokens, opts) {
119
+ applySessionOverrides(opts);
120
+ const { setSandboxOverride } = await import("../sandbox.js");
121
+ const { setPermissionOverride } = await import("../confirm.js");
122
+ const { rest, errors } = extractUnknownOptionErrors(promptTokens);
123
+ const sessionIntent = hasSessionIntent(opts, rest);
124
+ const wantsConfigWrite = opts.sandbox !== undefined || opts.network !== undefined || opts.permission !== undefined;
125
+ if (wantsConfigWrite && !sessionIntent) {
126
+ const { runWriteConfig } = await import("./commands/write-config.js");
127
+ await runWriteConfig({
128
+ sandbox: opts.sandbox,
129
+ network: opts.network,
130
+ permission: opts.permission,
131
+ scope: opts.project ? "project" : opts.global ? "global" : undefined,
132
+ });
133
+ return;
134
+ }
135
+ if (opts.sandbox !== undefined)
136
+ setSandboxOverride({ mode: opts.sandbox });
137
+ if (opts.network !== undefined)
138
+ setSandboxOverride({ network: opts.network });
139
+ if (opts.permission !== undefined)
140
+ setPermissionOverride(opts.permission);
141
+ if (errors.length > 0) {
142
+ console.error(errors[0]);
143
+ process.exitCode = 1;
144
+ return;
145
+ }
146
+ const { startTuiSession } = await import("./commands/shared.js");
147
+ await startTuiSession({
148
+ model: opts.model,
149
+ provider: opts.provider,
150
+ resume: opts.resume,
151
+ images: opts.image,
152
+ positionals: rest,
153
+ });
154
+ }
155
+ export async function runProgram(argv, registerCommands) {
156
+ const program = buildProgram();
157
+ program.action((promptTokens, opts) => runRootAction(promptTokens, opts));
158
+ registerCommands(program);
159
+ try {
160
+ await program.parseAsync(argv, { from: "user" });
161
+ }
162
+ catch (err) {
163
+ if (err instanceof CliError) {
164
+ console.error(formatCliError(err));
165
+ process.exitCode = exitCodeFor(err);
166
+ return;
167
+ }
168
+ // Commander throws a CommanderError for --help/--version and for its own
169
+ // usage errors; those already printed output and set process.exitCode.
170
+ if (isCommanderExit(err)) {
171
+ process.exitCode = err.exitCode;
172
+ return;
173
+ }
174
+ console.error(formatCliError(err));
175
+ process.exitCode = 2;
176
+ }
177
+ }
178
+ function isCommanderExit(err) {
179
+ return typeof err === "object" && err !== null && "exitCode" in err && "code" in err;
180
+ }