usemint-cli 0.2.0-beta.1 → 0.2.0-beta.2

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.
package/dist/cli/index.js CHANGED
@@ -44,7 +44,7 @@ function getConfigPath() {
44
44
  function getGatewayUrl() {
45
45
  return conf.get("apiBaseUrl") ?? "https://api.usemint.dev";
46
46
  }
47
- var configSchema, conf, config;
47
+ var configSchema, conf, config2;
48
48
  var init_config = __esm({
49
49
  "src/utils/config.ts"() {
50
50
  "use strict";
@@ -97,7 +97,7 @@ var init_config = __esm({
97
97
  apiBaseUrl: { type: "string", default: "https://api.usemint.dev" }
98
98
  }
99
99
  });
100
- config = {
100
+ config2 = {
101
101
  get,
102
102
  set,
103
103
  setAll,
@@ -547,7 +547,7 @@ var init_anthropic = __esm({
547
547
  client = null;
548
548
  getClient() {
549
549
  if (this.client) return this.client;
550
- const apiKey = config.get("providers")?.anthropic;
550
+ const apiKey = config2.get("providers")?.anthropic;
551
551
  if (!apiKey) {
552
552
  throw new Error("Anthropic API key not configured. Run: axon config:set providers.anthropic <key>");
553
553
  }
@@ -709,7 +709,7 @@ var init_deepseek = __esm({
709
709
  client = null;
710
710
  getClient() {
711
711
  if (this.client) return this.client;
712
- const apiKey = config.get("providers")?.deepseek;
712
+ const apiKey = config2.get("providers")?.deepseek;
713
713
  if (!apiKey) {
714
714
  throw new Error("DeepSeek API key not configured. Run: axon config:set providers.deepseek <key>");
715
715
  }
@@ -981,7 +981,7 @@ var init_openai_compatible = __esm({
981
981
  getClient() {
982
982
  if (this.client) return this.client;
983
983
  const [section, key] = this.cfg.apiKeyConfigPath.split(".");
984
- const sectionData = config.get(section);
984
+ const sectionData = config2.get(section);
985
985
  const apiKey = sectionData?.[key];
986
986
  if (!apiKey) {
987
987
  throw new Error(
@@ -1209,7 +1209,7 @@ var init_gemini = __esm({
1209
1209
  sdk = null;
1210
1210
  getSDK() {
1211
1211
  if (this.sdk) return this.sdk;
1212
- const sectionData = config.get("providers");
1212
+ const sectionData = config2.get("providers");
1213
1213
  const apiKey = sectionData?.["gemini"];
1214
1214
  if (!apiKey) throw new Error("Gemini API key not configured. Run: axon config:set providers.gemini <key>");
1215
1215
  this.sdk = new GoogleGenerativeAI(apiKey);
@@ -1307,12 +1307,12 @@ var init_gemini = __esm({
1307
1307
 
1308
1308
  // src/providers/gateway.ts
1309
1309
  function getGatewayUrl2() {
1310
- return process.env.MINT_GATEWAY_URL ?? config.getGatewayUrl();
1310
+ return process.env.MINT_GATEWAY_URL ?? config2.getGatewayUrl();
1311
1311
  }
1312
1312
  function getToken() {
1313
- const gatewayToken = config.get("gatewayToken");
1313
+ const gatewayToken = config2.get("gatewayToken");
1314
1314
  if (gatewayToken) return gatewayToken;
1315
- const userToken = config.get("apiKey");
1315
+ const userToken = config2.get("apiKey");
1316
1316
  if (userToken) return userToken;
1317
1317
  const envToken = process.env.MINT_GATEWAY_TOKEN ?? process.env.MINT_API_TOKEN ?? "";
1318
1318
  if (envToken) return envToken;
@@ -1639,7 +1639,7 @@ function getProvider(modelId) {
1639
1639
  }
1640
1640
  function hasProviderKey(providerId) {
1641
1641
  try {
1642
- const providerKeys = config.get("providers");
1642
+ const providerKeys = config2.get("providers");
1643
1643
  return !!providerKeys?.[providerId];
1644
1644
  } catch {
1645
1645
  return false;
@@ -4334,10 +4334,10 @@ var init_SlashAutocomplete = __esm({
4334
4334
  SLASH_COMMANDS = [
4335
4335
  { name: "help", description: "show commands + keyboard shortcuts" },
4336
4336
  { name: "clear", description: "clear chat history" },
4337
- { name: "model", description: "show/switch model" },
4338
- { name: "models", description: "list all models with tiers" },
4339
- { name: "agent", description: "show/switch agent mode" },
4340
- { name: "usage", description: "session stats + totals" }
4337
+ { name: "model", description: "show current model" },
4338
+ { name: "auto", description: "toggle auto mode (skip approvals)" },
4339
+ { name: "yolo", description: "toggle yolo mode (full autonomy)" },
4340
+ { name: "usage", description: "session stats + savings" }
4341
4341
  ];
4342
4342
  }
4343
4343
  });
@@ -7663,13 +7663,13 @@ async function executeTool2(toolName, input, toolCallId, options) {
7663
7663
  async function generateDiffPreview(toolName, input, cwd) {
7664
7664
  const { createTwoFilesPatch: createTwoFilesPatch2 } = await import("diff");
7665
7665
  const { readFile: readFile10 } = await import("fs/promises");
7666
- const { join: join20 } = await import("path");
7666
+ const { join: join21 } = await import("path");
7667
7667
  if (toolName === "write_file") {
7668
7668
  const path = String(input.path ?? "");
7669
7669
  const newContent = String(input.content ?? "");
7670
7670
  let oldContent = "";
7671
7671
  try {
7672
- oldContent = await readFile10(join20(cwd, path), "utf-8");
7672
+ oldContent = await readFile10(join21(cwd, path), "utf-8");
7673
7673
  } catch {
7674
7674
  }
7675
7675
  return createTwoFilesPatch2(path, path, oldContent, newContent, "old", "new");
@@ -7679,7 +7679,7 @@ async function generateDiffPreview(toolName, input, cwd) {
7679
7679
  const oldStr = String(input.old_text ?? "");
7680
7680
  const newStr = String(input.new_text ?? "");
7681
7681
  try {
7682
- const current = await readFile10(join20(cwd, path), "utf-8");
7682
+ const current = await readFile10(join21(cwd, path), "utf-8");
7683
7683
  const firstMatch = current.indexOf(oldStr);
7684
7684
  const secondMatch = firstMatch === -1 ? -1 : current.indexOf(oldStr, firstMatch + oldStr.length);
7685
7685
  if (firstMatch !== -1 && secondMatch === -1) {
@@ -7692,7 +7692,7 @@ async function generateDiffPreview(toolName, input, cwd) {
7692
7692
  }
7693
7693
  if (toolName === "search_replace") {
7694
7694
  const path = String(input.path ?? "");
7695
- const current = await readFile10(join20(cwd, path), "utf-8");
7695
+ const current = await readFile10(join21(cwd, path), "utf-8");
7696
7696
  const { buildSearchReplacePlan: buildSearchReplacePlan2, buildSearchReplacePreview: buildSearchReplacePreview2 } = await Promise.resolve().then(() => (init_search_replace(), search_replace_exports));
7697
7697
  const plan = buildSearchReplacePlan2(current, {
7698
7698
  path,
@@ -10994,39 +10994,210 @@ var init_pipeline = __esm({
10994
10994
  });
10995
10995
 
10996
10996
  // src/orchestrator/prompts.ts
10997
- var ORCHESTRATOR_PROMPT;
10997
+ var ORCHESTRATOR_PROMPT, MEMORY_INSTRUCTION;
10998
10998
  var init_prompts = __esm({
10999
10999
  "src/orchestrator/prompts.ts"() {
11000
11000
  "use strict";
11001
- ORCHESTRATOR_PROMPT = `You are Mint CLI, a code editing assistant. You help developers fix bugs, add features, and refactor code.
11002
-
11003
- You have these tools:
11004
- - search_files: find relevant files in the project by keywords
11005
- - read_file: read a file's contents
11006
- - list_files: list directory contents
11007
- - edit_file: replace exact text in a file (most reliable for edits)
11008
- - write_file: create a new file or overwrite entirely
11009
- - write_code: dispatch a coding task to a fast coding model (for complex multi-file changes)
11010
- - run_command: execute a shell command (npm test, npm run build, etc)
11011
-
11012
- Your workflow:
11013
- 1. Search for relevant files using search_files
11014
- 2. Read the files you need with read_file
11015
- 3. For simple edits (changing text, fixing a line): use edit_file directly \u2014 find the exact text to replace and provide the replacement
11016
- 4. For complex changes (new features, multi-file refactors): use write_code to generate the code, then apply with edit_file or write_file
11017
- 5. For new files: use write_file
11018
- 6. If you want to verify, run tests with run_command
11001
+ ORCHESTRATOR_PROMPT = `You are Mint CLI, an AI coding assistant running in a terminal. You help developers fix bugs, add features, refactor code, and answer questions about codebases.
11002
+
11003
+ # Tools
11004
+
11005
+ ## Search & Read (free, no LLM cost)
11006
+ - **search_files**: Find relevant files by keywords. Use this first to locate files related to the task.
11007
+ - **read_file**: Read a file's contents. Large files are truncated at 200 lines \u2014 use grep_file for specifics. You MUST read a file before editing it.
11008
+ - **grep_file**: Search inside a file for specific text. Returns matching lines with line numbers and 2 lines of context. Use this for large files or to find exact text before edit_file.
11009
+ - **list_files**: List directory contents. Ignores node_modules and hidden files.
11010
+
11011
+ ## Edit & Write (requires user approval)
11012
+ - **edit_file**: Replace exact text in a file. The old_text must match EXACTLY including whitespace and indentation. If it fails, use grep_file to get the precise text, then retry. Prefer this over write_file for existing files.
11013
+ - **write_file**: Create a new file or overwrite entirely. Use for new files only \u2014 prefer edit_file for changes to existing files.
11014
+ - **write_code**: Dispatch a coding task to a fast coding model. Provide a precise task description + relevant file contents. Returns generated code. Use for complex multi-file changes or large code generation. YOU do the planning \u2014 write_code does the typing.
11015
+
11016
+ ## Verify & Execute (free, no LLM cost)
11017
+ - **run_command**: Execute a shell command with 30s timeout. Use for build, lint, curl, etc. NEVER run destructive commands (rm -rf, sudo, chmod 777, kill, shutdown).
11018
+ - **run_tests**: Detect and run the project's test suite. Checks package.json for test script.
11019
+ - **git_diff**: Show all uncommitted changes (status + staged + unstaged diffs).
11020
+ - **git_commit**: Stage all changes and commit with a message.
11021
+ - **undo**: Revert the last edit to a specific file. Only the most recent change per file can be undone.
11022
+
11023
+ # How to work
11024
+
11025
+ ## Questions vs changes
11026
+ - If the user asks a QUESTION (can you see, what does, how does, show me, explain, suggest, review) \u2014 read the relevant files and ANSWER. Do NOT edit anything.
11027
+ - Only edit files when the user explicitly asks for a change (fix, add, change, update, create, remove, rename, build).
11028
+
11029
+ ## Before editing
11030
+ - Always read before writing. Never assume file contents.
11031
+ - For large files (truncated at 200 lines): use grep_file to find the exact line, then edit_file with the exact text from grep_file output.
11032
+ - For edit_file: copy the EXACT text from the file as shown by read_file or grep_file. Whitespace and indentation matter. If it fails, the text didn't match \u2014 use grep_file to get the real content.
11033
+
11034
+ ## Making changes
11035
+ - Keep changes minimal and focused on the task. A bug fix doesn't need surrounding code cleaned up. A simple feature doesn't need extra configurability.
11036
+ - Don't add features, refactor code, or make "improvements" beyond what was asked.
11037
+ - Don't add error handling, fallbacks, or validation for scenarios that can't happen. Only validate at system boundaries (user input, external APIs).
11038
+ - Don't add comments, docstrings, or type annotations to code you didn't change.
11039
+ - Don't create helpers or abstractions for one-time operations. Three similar lines is better than a premature abstraction.
11040
+ - Match existing code patterns \u2014 read 2-3 nearby files first to understand naming, imports, error handling, and style.
11041
+
11042
+ ## For complex changes
11043
+ - Use write_code for multi-file features or large code generation. Pass only the relevant files (max 4-8).
11044
+ - write_code dispatches to a fast coding model \u2014 YOU do the planning and thinking, write_code does the typing.
11045
+
11046
+ ## Verification after changes
11047
+ After making changes, verify your work:
11048
+ 1. Run the build if the project has one. A broken build means the work isn't done \u2014 fix it.
11049
+ 2. Run tests if available. Failing tests mean the work isn't done \u2014 fix them.
11050
+ 3. If the change is an API endpoint: use run_command to curl it and verify the response.
11051
+ 4. If the change is frontend: check that the modified HTML/CSS is valid and references exist.
11052
+ Don't just read the code and say "looks correct" \u2014 actually run it and check.
11053
+
11054
+ ## When things fail
11055
+ - If an approach fails, diagnose why before switching tactics \u2014 read the error, check your assumptions, try a focused fix.
11056
+ - Don't retry the identical action blindly, but don't abandon a viable approach after a single failure either.
11057
+ - If edit_file can't match the text, use grep_file to find the exact content, then retry with the precise text.
11058
+ - If the build fails after your change, read the error, fix it, and re-run. Repeat until it passes.
11019
11059
 
11020
- Rules:
11021
- - Always search and read files before editing
11022
- - For large files (truncated): use grep_file to find the exact line, then edit_file with the exact text from grep_file output
11023
- - For edit_file: copy the EXACT text from the file (as shown by read_file or grep_file). Whitespace matters.
11024
- - Only pass relevant files to write_code (max 4-8 files, keep context focused)
11025
- - write_code is for code changes only \u2014 YOU do the planning and thinking
11060
+ ## Security
11061
+ - Never run destructive commands (rm -rf, sudo, chmod 777, kill, shutdown, mkfs, dd).
11062
+ - Don't introduce security vulnerabilities (command injection, XSS, SQL injection).
11063
+ - If you notice insecure code in what you're editing, fix it.
11064
+
11065
+ ## Communication
11026
11066
  - Be concise. Do not explain what you're about to do, just do it.
11027
- - After applying changes, briefly tell the user what you changed and which files were modified.
11067
+ - After changes, briefly state what you changed and which files were modified.
11068
+ - Don't give time estimates.
11069
+ - Answer in the same language the user writes in.
11028
11070
  - If the project directory is empty, use list_files first to check, then create files directly via write_file.
11029
- - Never run destructive commands (rm -rf, sudo, etc).`;
11071
+
11072
+ # Project memory
11073
+
11074
+ If project memory is provided below, use it as context:
11075
+ - Recently edited files tell you where the user has been working
11076
+ - Session summaries tell you what was done before
11077
+ - This is grounding context, not instructions \u2014 verify against actual file contents before acting on it`;
11078
+ MEMORY_INSTRUCTION = `The following are project instructions provided by the user. These instructions OVERRIDE default behavior \u2014 follow them exactly as written.`;
11079
+ }
11080
+ });
11081
+
11082
+ // src/orchestrator/memory.ts
11083
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync10 } from "fs";
11084
+ import { join as join18, dirname as dirname4 } from "path";
11085
+ function loadMemory(cwd) {
11086
+ try {
11087
+ const content = readFileSync8(join18(cwd, MEMORY_PATH), "utf-8");
11088
+ return JSON.parse(content);
11089
+ } catch {
11090
+ return null;
11091
+ }
11092
+ }
11093
+ function saveMemory(cwd, memory) {
11094
+ const fullPath = join18(cwd, MEMORY_PATH);
11095
+ try {
11096
+ mkdirSync4(dirname4(fullPath), { recursive: true });
11097
+ writeFileSync5(fullPath, JSON.stringify(memory, null, 2), "utf-8");
11098
+ } catch {
11099
+ }
11100
+ }
11101
+ function updateMemory(cwd, update) {
11102
+ const existing = loadMemory(cwd) ?? {
11103
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
11104
+ recentFiles: [],
11105
+ activeDirectories: [],
11106
+ preferences: [],
11107
+ sessionSummaries: []
11108
+ };
11109
+ if (update.editedFiles) {
11110
+ const combined = [.../* @__PURE__ */ new Set([...update.editedFiles, ...existing.recentFiles])];
11111
+ existing.recentFiles = combined.slice(0, MAX_RECENT_FILES);
11112
+ const dirs = new Set(existing.activeDirectories);
11113
+ for (const f of update.editedFiles) {
11114
+ const parts = f.split("/");
11115
+ if (parts.length > 1) dirs.add(parts.slice(0, -1).join("/"));
11116
+ }
11117
+ existing.activeDirectories = [...dirs].slice(0, 10);
11118
+ }
11119
+ if (update.sessionSummary) {
11120
+ existing.sessionSummaries = [
11121
+ update.sessionSummary,
11122
+ ...existing.sessionSummaries
11123
+ ].slice(0, MAX_SUMMARIES);
11124
+ }
11125
+ if (update.projectDescription) existing.projectDescription = update.projectDescription;
11126
+ if (update.language) existing.language = update.language;
11127
+ existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
11128
+ saveMemory(cwd, existing);
11129
+ }
11130
+ function formatMemoryForPrompt(memory) {
11131
+ const parts = [];
11132
+ if (memory.projectDescription) {
11133
+ parts.push(`Project: ${memory.projectDescription}`);
11134
+ }
11135
+ if (memory.language) {
11136
+ parts.push(`Language: ${memory.language}`);
11137
+ }
11138
+ if (memory.recentFiles.length > 0) {
11139
+ parts.push(`Recently edited files: ${memory.recentFiles.slice(0, 10).join(", ")}`);
11140
+ }
11141
+ if (memory.activeDirectories.length > 0) {
11142
+ parts.push(`Active directories: ${memory.activeDirectories.join(", ")}`);
11143
+ }
11144
+ if (memory.sessionSummaries.length > 0) {
11145
+ parts.push(`Recent session: ${memory.sessionSummaries[0]}`);
11146
+ }
11147
+ return parts.length > 0 ? `
11148
+ <project_memory>
11149
+ ${parts.join("\n")}
11150
+ </project_memory>` : "";
11151
+ }
11152
+ function loadProjectInstructions(cwd) {
11153
+ const candidates = [
11154
+ "MINT.md",
11155
+ ".mint/MINT.md",
11156
+ "CLAUDE.md",
11157
+ ".claude/CLAUDE.md"
11158
+ ];
11159
+ const parts = [];
11160
+ for (const candidate of candidates) {
11161
+ const fullPath = join18(cwd, candidate);
11162
+ if (existsSync10(fullPath)) {
11163
+ try {
11164
+ const content = readFileSync8(fullPath, "utf-8").trim();
11165
+ if (content.length > 0 && content.length < 4e4) {
11166
+ parts.push(`# ${candidate}
11167
+ ${content}`);
11168
+ }
11169
+ } catch {
11170
+ }
11171
+ }
11172
+ }
11173
+ const rulesDir = join18(cwd, ".mint", "rules");
11174
+ if (existsSync10(rulesDir)) {
11175
+ try {
11176
+ const { readdirSync: readdirSync4 } = __require("fs");
11177
+ const files = readdirSync4(rulesDir);
11178
+ for (const file of files) {
11179
+ if (!file.endsWith(".md")) continue;
11180
+ try {
11181
+ const content = readFileSync8(join18(rulesDir, file), "utf-8").trim();
11182
+ if (content.length > 0 && content.length < 1e4) {
11183
+ parts.push(`# .mint/rules/${file}
11184
+ ${content}`);
11185
+ }
11186
+ } catch {
11187
+ }
11188
+ }
11189
+ } catch {
11190
+ }
11191
+ }
11192
+ return parts.length > 0 ? parts.join("\n\n") : null;
11193
+ }
11194
+ var MEMORY_PATH, MAX_RECENT_FILES, MAX_SUMMARIES;
11195
+ var init_memory = __esm({
11196
+ "src/orchestrator/memory.ts"() {
11197
+ "use strict";
11198
+ MEMORY_PATH = ".mint/memory.json";
11199
+ MAX_RECENT_FILES = 20;
11200
+ MAX_SUMMARIES = 10;
11030
11201
  }
11031
11202
  });
11032
11203
 
@@ -11109,15 +11280,24 @@ Include 3 context lines around each change. One diff block per file.`;
11109
11280
  });
11110
11281
 
11111
11282
  // src/orchestrator/tools.ts
11112
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, readdirSync as readdirSync3, mkdirSync as mkdirSync4 } from "fs";
11283
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, readdirSync as readdirSync3, existsSync as existsSync11, mkdirSync as mkdirSync5 } from "fs";
11113
11284
  import { execSync } from "child_process";
11114
- import { join as join18, dirname as dirname4 } from "path";
11285
+ import { join as join19, dirname as dirname5 } from "path";
11115
11286
  function getWriteCodeCost() {
11116
11287
  return sessionWriteCodeCost;
11117
11288
  }
11118
11289
  function resetWriteCodeCost() {
11119
11290
  sessionWriteCodeCost = 0;
11120
11291
  }
11292
+ function isToolSafe(toolName, input) {
11293
+ if (SAFE_TOOLS.has(toolName)) return true;
11294
+ if (toolName === "run_command") {
11295
+ const cmd = String(input.command ?? "").trim().toLowerCase();
11296
+ const readOnlyPrefixes = ["git status", "git log", "git diff", "ls", "cat ", "head ", "tail ", "npm run build", "npm test", "npx tsc", "curl ", "echo ", "pwd", "which ", "node -e", "wc "];
11297
+ return readOnlyPrefixes.some((prefix) => cmd.startsWith(prefix));
11298
+ }
11299
+ return false;
11300
+ }
11121
11301
  async function executeOrchestratorTool(toolName, input, ctx) {
11122
11302
  switch (toolName) {
11123
11303
  case "search_files":
@@ -11147,6 +11327,14 @@ async function executeOrchestratorTool(toolName, input, ctx) {
11147
11327
  return toolWriteFile(String(input.path ?? ""), String(input.content ?? ""), ctx);
11148
11328
  case "run_command":
11149
11329
  return toolRunCommand(String(input.command ?? ""), ctx);
11330
+ case "git_diff":
11331
+ return toolGitDiff(ctx);
11332
+ case "git_commit":
11333
+ return toolGitCommit(String(input.message ?? ""), ctx);
11334
+ case "run_tests":
11335
+ return toolRunTests(ctx);
11336
+ case "undo":
11337
+ return toolUndo(String(input.path ?? ""), ctx);
11150
11338
  default:
11151
11339
  return `Unknown tool: ${toolName}`;
11152
11340
  }
@@ -11172,10 +11360,10 @@ async function toolSearchFiles(query, ctx) {
11172
11360
  }
11173
11361
  function toolReadFile(filePath, ctx) {
11174
11362
  ctx.onLog?.(`reading ${filePath}`);
11175
- const fullPath = join18(ctx.cwd, filePath);
11363
+ const fullPath = join19(ctx.cwd, filePath);
11176
11364
  if (!fullPath.startsWith(ctx.cwd)) return "Error: path outside project directory";
11177
11365
  try {
11178
- const content = readFileSync8(fullPath, "utf-8");
11366
+ const content = readFileSync9(fullPath, "utf-8");
11179
11367
  if (content.length > 32e3) {
11180
11368
  const lines = content.split("\n");
11181
11369
  const preview = lines.slice(0, 200).map((l, i) => `${i + 1}: ${l}`).join("\n");
@@ -11190,10 +11378,10 @@ function toolReadFile(filePath, ctx) {
11190
11378
  }
11191
11379
  function toolGrepFile(filePath, pattern, ctx) {
11192
11380
  ctx.onLog?.(`grep ${filePath}: ${pattern}`);
11193
- const fullPath = join18(ctx.cwd, filePath);
11381
+ const fullPath = join19(ctx.cwd, filePath);
11194
11382
  if (!fullPath.startsWith(ctx.cwd)) return "Error: path outside project directory";
11195
11383
  try {
11196
- const content = readFileSync8(fullPath, "utf-8");
11384
+ const content = readFileSync9(fullPath, "utf-8");
11197
11385
  const lines = content.split("\n");
11198
11386
  const matches = [];
11199
11387
  const patternLower = pattern.toLowerCase();
@@ -11215,7 +11403,7 @@ function toolGrepFile(filePath, pattern, ctx) {
11215
11403
  }
11216
11404
  function toolListFiles(dirPath, ctx) {
11217
11405
  ctx.onLog?.(`listing ${dirPath}`);
11218
- const fullPath = join18(ctx.cwd, dirPath);
11406
+ const fullPath = join19(ctx.cwd, dirPath);
11219
11407
  if (!fullPath.startsWith(ctx.cwd) && fullPath !== ctx.cwd) return "Error: path outside project directory";
11220
11408
  try {
11221
11409
  const entries = readdirSync3(fullPath, { withFileTypes: true });
@@ -11233,7 +11421,7 @@ async function toolWriteCode(task, files, ctx) {
11233
11421
  resolvedFiles[path] = content;
11234
11422
  } else {
11235
11423
  try {
11236
- resolvedFiles[path] = readFileSync8(join18(ctx.cwd, path), "utf-8");
11424
+ resolvedFiles[path] = readFileSync9(join19(ctx.cwd, path), "utf-8");
11237
11425
  } catch {
11238
11426
  resolvedFiles[path] = "(file does not exist \u2014 create it)";
11239
11427
  }
@@ -11257,17 +11445,18 @@ async function toolEditFile(filePath, oldText, newText, ctx) {
11257
11445
  const approved = await ctx.onApprovalNeeded(preview);
11258
11446
  if (!approved) return "User rejected this edit.";
11259
11447
  }
11260
- const fullPath = join18(ctx.cwd, filePath);
11448
+ const fullPath = join19(ctx.cwd, filePath);
11261
11449
  if (!fullPath.startsWith(ctx.cwd)) return "Error: path outside project directory";
11262
11450
  try {
11263
- const content = readFileSync8(fullPath, "utf-8");
11451
+ const content = readFileSync9(fullPath, "utf-8");
11264
11452
  if (content.includes(oldText)) {
11265
11453
  const count = content.split(oldText).length - 1;
11266
11454
  if (count > 1) {
11267
11455
  return `Error: old_text matches ${count} locations in ${filePath}. Make it more specific by including more surrounding context.`;
11268
11456
  }
11457
+ undoBackups.set(filePath, content);
11269
11458
  const updated = content.replace(oldText, newText);
11270
- writeFileSync5(fullPath, updated, "utf-8");
11459
+ writeFileSync6(fullPath, updated, "utf-8");
11271
11460
  return `Edited ${filePath}: replaced ${oldText.length} chars with ${newText.length} chars.`;
11272
11461
  }
11273
11462
  const normalize = (s) => s.replace(/\s+/g, " ").trim();
@@ -11281,7 +11470,7 @@ async function toolEditFile(filePath, oldText, newText, ctx) {
11281
11470
  newText
11282
11471
  ));
11283
11472
  if (updated !== content) {
11284
- writeFileSync5(fullPath, updated, "utf-8");
11473
+ writeFileSync6(fullPath, updated, "utf-8");
11285
11474
  return `Edited ${filePath} (fuzzy match on line ${i + 1}): replaced text.`;
11286
11475
  }
11287
11476
  }
@@ -11289,7 +11478,7 @@ async function toolEditFile(filePath, oldText, newText, ctx) {
11289
11478
  const window = lines.slice(i, i + windowSize).join("\n");
11290
11479
  if (normalize(window).includes(normalizedOld)) {
11291
11480
  const replacement = lines.slice(0, i).join("\n") + "\n" + newText + "\n" + lines.slice(i + windowSize).join("\n");
11292
- writeFileSync5(fullPath, replacement, "utf-8");
11481
+ writeFileSync6(fullPath, replacement, "utf-8");
11293
11482
  return `Edited ${filePath} (fuzzy match lines ${i + 1}-${i + windowSize}): replaced text.`;
11294
11483
  }
11295
11484
  }
@@ -11303,6 +11492,75 @@ Tip: use read_file to see the exact content, then copy-paste the exact text you
11303
11492
  return `Error editing ${filePath}: ${err instanceof Error ? err.message : String(err)}`;
11304
11493
  }
11305
11494
  }
11495
+ function toolGitDiff(ctx) {
11496
+ ctx.onLog?.("git diff");
11497
+ try {
11498
+ const diff = execSync("git diff", { cwd: ctx.cwd, encoding: "utf-8", timeout: 1e4 });
11499
+ const staged = execSync("git diff --cached", { cwd: ctx.cwd, encoding: "utf-8", timeout: 1e4 });
11500
+ const status = execSync("git status --short", { cwd: ctx.cwd, encoding: "utf-8", timeout: 1e4 });
11501
+ const parts = [
11502
+ status.trim() ? `Status:
11503
+ ${status.trim()}` : "No changes.",
11504
+ diff.trim() ? `
11505
+ Unstaged changes:
11506
+ ${diff.trim().slice(0, MAX_OUTPUT4)}` : "",
11507
+ staged.trim() ? `
11508
+ Staged changes:
11509
+ ${staged.trim().slice(0, MAX_OUTPUT4)}` : ""
11510
+ ].filter(Boolean);
11511
+ return parts.join("\n") || "Working tree clean.";
11512
+ } catch (err) {
11513
+ return `Git error: ${err instanceof Error ? err.message : String(err)}`;
11514
+ }
11515
+ }
11516
+ function toolGitCommit(message, ctx) {
11517
+ ctx.onLog?.(`git commit: ${message.slice(0, 40)}`);
11518
+ try {
11519
+ execSync("git add -A", { cwd: ctx.cwd, timeout: 1e4 });
11520
+ const result = execSync(`git commit -m "${message.replace(/"/g, '\\"')}"`, {
11521
+ cwd: ctx.cwd,
11522
+ encoding: "utf-8",
11523
+ timeout: 1e4
11524
+ });
11525
+ return result.trim() || "Committed.";
11526
+ } catch (err) {
11527
+ const msg = err instanceof Error ? err.message : String(err);
11528
+ if (msg.includes("nothing to commit")) return "Nothing to commit \u2014 working tree clean.";
11529
+ return `Git commit error: ${msg}`;
11530
+ }
11531
+ }
11532
+ function toolRunTests(ctx) {
11533
+ ctx.onLog?.("running tests");
11534
+ try {
11535
+ const pkgPath = join19(ctx.cwd, "package.json");
11536
+ if (existsSync11(pkgPath)) {
11537
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
11538
+ const testScript = pkg.scripts?.test;
11539
+ if (testScript && testScript !== 'echo "Error: no test specified" && exit 1') {
11540
+ const output = execSync("npm test", { cwd: ctx.cwd, encoding: "utf-8", timeout: 6e4, maxBuffer: 1024 * 1024 });
11541
+ return output.length > MAX_OUTPUT4 ? output.slice(0, MAX_OUTPUT4) + "\n... (truncated)" : output;
11542
+ }
11543
+ }
11544
+ return "No test script found in package.json.";
11545
+ } catch (err) {
11546
+ const execErr = err;
11547
+ const out = [execErr.stdout, execErr.stderr].filter(Boolean).join("\n");
11548
+ return out.length > MAX_OUTPUT4 ? out.slice(0, MAX_OUTPUT4) + "\n... (truncated)" : out || "Tests failed.";
11549
+ }
11550
+ }
11551
+ function toolUndo(filePath, ctx) {
11552
+ ctx.onLog?.(`undo ${filePath}`);
11553
+ const backup = undoBackups.get(filePath);
11554
+ if (!backup) return `No undo history for ${filePath}. Only the most recent edit can be undone.`;
11555
+ const fullPath = join19(ctx.cwd, filePath);
11556
+ try {
11557
+ writeFileSync6(fullPath, backup, "utf-8");
11558
+ undoBackups.delete(filePath);
11559
+ return `Reverted ${filePath} to previous state.`;
11560
+ } catch (err) {
11561
+ return `Undo error: ${err instanceof Error ? err.message : String(err)}`;
11562
+ }
11563
+ }
11306
11564
  function escapeRegex(s) {
11307
11565
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
11308
11566
  }
@@ -11314,11 +11572,15 @@ async function toolWriteFile(filePath, content, ctx) {
11314
11572
  const approved = await ctx.onApprovalNeeded(preview);
11315
11573
  if (!approved) return "User rejected this file creation.";
11316
11574
  }
11317
- const fullPath = join18(ctx.cwd, filePath);
11575
+ const fullPath = join19(ctx.cwd, filePath);
11318
11576
  if (!fullPath.startsWith(ctx.cwd)) return "Error: path outside project directory";
11319
11577
  try {
11320
- mkdirSync4(dirname4(fullPath), { recursive: true });
11321
- writeFileSync5(fullPath, content, "utf-8");
11578
+ mkdirSync5(dirname5(fullPath), { recursive: true });
11579
+ try {
11580
+ undoBackups.set(filePath, readFileSync9(fullPath, "utf-8"));
11581
+ } catch {
11582
+ }
11583
+ writeFileSync6(fullPath, content, "utf-8");
11322
11584
  return `Created ${filePath} (${content.length} chars).`;
11323
11585
  } catch (err) {
11324
11586
  return `Error writing ${filePath}: ${err instanceof Error ? err.message : String(err)}`;
@@ -11369,7 +11631,7 @@ function toolRunCommand(command, ctx) {
11369
11631
  return out.length > MAX_OUTPUT4 ? out.slice(0, MAX_OUTPUT4) + "\n... (truncated)" : out || "Command failed";
11370
11632
  }
11371
11633
  }
11372
- var sessionWriteCodeCost, ORCHESTRATOR_TOOL_DEFINITIONS, DANGEROUS_COMMANDS, MAX_OUTPUT4;
11634
+ var sessionWriteCodeCost, undoBackups, SAFE_TOOLS, ORCHESTRATOR_TOOL_DEFINITIONS, DANGEROUS_COMMANDS, MAX_OUTPUT4;
11373
11635
  var init_tools3 = __esm({
11374
11636
  "src/orchestrator/tools.ts"() {
11375
11637
  "use strict";
@@ -11378,6 +11640,16 @@ var init_tools3 = __esm({
11378
11640
  init_diff_apply();
11379
11641
  init_write_code();
11380
11642
  sessionWriteCodeCost = 0;
11643
+ undoBackups = /* @__PURE__ */ new Map();
11644
+ SAFE_TOOLS = /* @__PURE__ */ new Set([
11645
+ "search_files",
11646
+ "read_file",
11647
+ "grep_file",
11648
+ "list_files",
11649
+ "git_diff",
11650
+ "run_tests",
11651
+ "write_code"
11652
+ ]);
11381
11653
  ORCHESTRATOR_TOOL_DEFINITIONS = [
11382
11654
  {
11383
11655
  name: "search_files",
@@ -11482,6 +11754,46 @@ var init_tools3 = __esm({
11482
11754
  },
11483
11755
  required: ["command"]
11484
11756
  }
11757
+ },
11758
+ {
11759
+ name: "git_diff",
11760
+ description: "Show what changed since the last commit. Returns unified diff of all modified files.",
11761
+ input_schema: {
11762
+ type: "object",
11763
+ properties: {},
11764
+ required: []
11765
+ }
11766
+ },
11767
+ {
11768
+ name: "git_commit",
11769
+ description: "Stage all changes and commit with a message.",
11770
+ input_schema: {
11771
+ type: "object",
11772
+ properties: {
11773
+ message: { type: "string", description: "Commit message" }
11774
+ },
11775
+ required: ["message"]
11776
+ }
11777
+ },
11778
+ {
11779
+ name: "run_tests",
11780
+ description: "Detect and run the project test suite. Tries npm test, then looks for common test runners.",
11781
+ input_schema: {
11782
+ type: "object",
11783
+ properties: {},
11784
+ required: []
11785
+ }
11786
+ },
11787
+ {
11788
+ name: "undo",
11789
+ description: "Revert the last file change made by edit_file or write_file. Restores the file to its previous state.",
11790
+ input_schema: {
11791
+ type: "object",
11792
+ properties: {
11793
+ path: { type: "string", description: "File path to revert" }
11794
+ },
11795
+ required: ["path"]
11796
+ }
11485
11797
  }
11486
11798
  ];
11487
11799
  DANGEROUS_COMMANDS = /\b(rm\s+-rf|sudo|chmod\s+777|mkfs|dd\s+if|shutdown|reboot|kill\s+-9|pkill)\b/;
@@ -11497,6 +11809,15 @@ __export(loop_exports, {
11497
11809
  async function runOrchestrator(task, cwd, callbacks, signal, previousMessages) {
11498
11810
  const startTime = Date.now();
11499
11811
  resetWriteCodeCost();
11812
+ const memory = loadMemory(cwd);
11813
+ const memoryBlock = memory ? formatMemoryForPrompt(memory) : "";
11814
+ const projectInstructions = loadProjectInstructions(cwd);
11815
+ const instructionsBlock = projectInstructions ? `
11816
+
11817
+ ${MEMORY_INSTRUCTION}
11818
+
11819
+ ${projectInstructions}` : "";
11820
+ const systemPrompt = ORCHESTRATOR_PROMPT + memoryBlock + instructionsBlock;
11500
11821
  const messages = [
11501
11822
  ...previousMessages ?? [],
11502
11823
  { role: "user", content: task }
@@ -11513,13 +11834,14 @@ async function runOrchestrator(task, cwd, callbacks, signal, previousMessages) {
11513
11834
  for (let i = 0; i < MAX_ITERATIONS; i++) {
11514
11835
  if (signal?.aborted) break;
11515
11836
  iterations = i + 1;
11837
+ compactMessagesIfNeeded(messages);
11516
11838
  let responseText = "";
11517
11839
  const toolCalls = [];
11518
11840
  try {
11519
11841
  for await (const chunk of streamAgent({
11520
11842
  model: ORCHESTRATOR_MODEL,
11521
11843
  messages,
11522
- systemPrompt: ORCHESTRATOR_PROMPT,
11844
+ systemPrompt,
11523
11845
  tools: ORCHESTRATOR_TOOL_DEFINITIONS,
11524
11846
  maxTokens: 4096,
11525
11847
  signal,
@@ -11562,8 +11884,24 @@ ${errMsg2}`;
11562
11884
  }))
11563
11885
  });
11564
11886
  const toolResults = [];
11565
- for (const tc of toolCalls) {
11566
- callbacks?.onLog?.(`tool: ${tc.name}`);
11887
+ const writeTools = /* @__PURE__ */ new Set(["edit_file", "write_file", "git_commit", "apply_diff"]);
11888
+ const parallelCalls = toolCalls.filter((tc) => !writeTools.has(tc.name));
11889
+ const sequentialCalls = toolCalls.filter((tc) => writeTools.has(tc.name));
11890
+ if (parallelCalls.length > 0) {
11891
+ const results = await Promise.all(
11892
+ parallelCalls.map(async (tc) => {
11893
+ callbacks?.onToolCall?.(tc.name, tc.input);
11894
+ const result = await executeOrchestratorTool(tc.name, tc.input, toolCtx);
11895
+ callbacks?.onToolResult?.(tc.name, result.slice(0, 200));
11896
+ return { toolCallId: tc.id, content: result };
11897
+ })
11898
+ );
11899
+ toolResults.push(...results);
11900
+ }
11901
+ for (const tc of sequentialCalls) {
11902
+ callbacks?.onToolCall?.(tc.name, tc.input);
11903
+ if (!isToolSafe(tc.name, tc.input) && callbacks?.onApprovalNeeded) {
11904
+ }
11567
11905
  const result = await executeOrchestratorTool(tc.name, tc.input, toolCtx);
11568
11906
  callbacks?.onToolResult?.(tc.name, result.slice(0, 200));
11569
11907
  toolResults.push({ toolCallId: tc.id, content: result });
@@ -11574,6 +11912,23 @@ ${errMsg2}`;
11574
11912
  toolResults
11575
11913
  });
11576
11914
  }
11915
+ const editedFiles = [];
11916
+ for (const msg of messages) {
11917
+ const tc = msg.toolCalls;
11918
+ if (tc) {
11919
+ for (const call of tc) {
11920
+ if ((call.name === "edit_file" || call.name === "write_file") && call.input.path) {
11921
+ editedFiles.push(String(call.input.path));
11922
+ }
11923
+ }
11924
+ }
11925
+ }
11926
+ if (editedFiles.length > 0 || fullOutput) {
11927
+ updateMemory(cwd, {
11928
+ editedFiles: [...new Set(editedFiles)],
11929
+ sessionSummary: fullOutput.slice(0, 200)
11930
+ });
11931
+ }
11577
11932
  const modelInfo = MODELS[ORCHESTRATOR_MODEL];
11578
11933
  const orchestratorCost = modelInfo ? orchestratorInputTokens / 1e6 * modelInfo.inputPrice + orchestratorOutputTokens / 1e6 * modelInfo.outputPrice : 0;
11579
11934
  const writeCodeCost = getWriteCodeCost();
@@ -11588,6 +11943,52 @@ ${errMsg2}`;
11588
11943
  messages
11589
11944
  };
11590
11945
  }
11946
+ function compactMessagesIfNeeded(messages) {
11947
+ const totalChars = messages.reduce((sum, m) => {
11948
+ const content = typeof m.content === "string" ? m.content : "";
11949
+ const toolContent = m.toolResults ? JSON.stringify(m.toolResults) : "";
11950
+ return sum + content.length + toolContent.length;
11951
+ }, 0);
11952
+ if (totalChars < MAX_CONTEXT_CHARS) return;
11953
+ if (messages.length <= 8) return;
11954
+ const first = messages[0];
11955
+ const recent = messages.slice(-6);
11956
+ const middle = messages.slice(1, -6);
11957
+ const filesEdited = /* @__PURE__ */ new Set();
11958
+ const filesRead = /* @__PURE__ */ new Set();
11959
+ const userRequests = [];
11960
+ const actionsCompleted = [];
11961
+ for (const msg of middle) {
11962
+ if (msg.role === "user" && msg.content) {
11963
+ userRequests.push(msg.content.slice(0, 150));
11964
+ }
11965
+ if (msg.role === "assistant" && msg.content) {
11966
+ const text = msg.content.trim();
11967
+ if (text) actionsCompleted.push(text.slice(0, 200));
11968
+ }
11969
+ const tc = msg.toolCalls;
11970
+ if (tc) {
11971
+ for (const call of tc) {
11972
+ const path = String(call.input?.path ?? "");
11973
+ if (call.name === "edit_file" || call.name === "write_file") filesEdited.add(path);
11974
+ if (call.name === "read_file" || call.name === "grep_file") filesRead.add(path);
11975
+ }
11976
+ }
11977
+ }
11978
+ const summaryParts = [
11979
+ "[Conversation compacted to save context]",
11980
+ userRequests.length > 0 ? `User requests: ${userRequests.join(" \u2192 ")}` : "",
11981
+ filesRead.size > 0 ? `Files examined: ${[...filesRead].join(", ")}` : "",
11982
+ filesEdited.size > 0 ? `Files modified: ${[...filesEdited].join(", ")}` : "",
11983
+ actionsCompleted.length > 0 ? `Actions: ${actionsCompleted.join(" | ")}` : ""
11984
+ ].filter(Boolean);
11985
+ const summary = {
11986
+ role: "assistant",
11987
+ content: summaryParts.join("\n")
11988
+ };
11989
+ messages.length = 0;
11990
+ messages.push(first, summary, ...recent);
11991
+ }
11591
11992
  function formatError(err) {
11592
11993
  const raw = err instanceof Error ? err.message : typeof err === "object" && err !== null ? JSON.stringify(err) : String(err);
11593
11994
  if (raw.includes("401") || raw.includes("Unauthorized")) return "Gateway auth failed. Run `mint login` or check your API keys.";
@@ -11599,16 +12000,19 @@ function formatError(err) {
11599
12000
  if (raw.includes("No provider")) return "No API key configured. Run `mint config:set providers.deepseek <key>` or `mint login`.";
11600
12001
  return raw.length > 200 ? raw.slice(0, 200) + "..." : raw;
11601
12002
  }
11602
- var ORCHESTRATOR_MODEL, MAX_ITERATIONS;
12003
+ var ORCHESTRATOR_MODEL, MAX_ITERATIONS, MAX_CONTEXT_CHARS;
11603
12004
  var init_loop2 = __esm({
11604
12005
  "src/orchestrator/loop.ts"() {
11605
12006
  "use strict";
11606
12007
  init_providers();
11607
12008
  init_prompts();
12009
+ init_memory();
12010
+ init_prompts();
11608
12011
  init_tools3();
11609
12012
  init_types();
11610
12013
  ORCHESTRATOR_MODEL = "grok-4.1-fast";
11611
12014
  MAX_ITERATIONS = 20;
12015
+ MAX_CONTEXT_CHARS = 1e5;
11612
12016
  }
11613
12017
  });
11614
12018
 
@@ -11694,11 +12098,12 @@ function estimateInputAreaHeight(input, isBusy, isRouting, contextChips, termina
11694
12098
  }
11695
12099
  return lines;
11696
12100
  }
11697
- function App({ initialPrompt, modelPreference, agentMode, useOrchestrator = true }) {
12101
+ function App({ initialPrompt, modelPreference, agentMode: initialAgentMode, useOrchestrator = true }) {
11698
12102
  const { exit } = useApp();
11699
12103
  const [messages, setMessages] = useState5([]);
11700
12104
  const [input, setInput] = useState5("");
11701
12105
  const [isBusy, setIsBusy] = useState5(false);
12106
+ const [agentMode, setAgentMode] = useState5(initialAgentMode);
11702
12107
  const [isRouting, setIsRouting] = useState5(false);
11703
12108
  const [currentModel, setCurrentModel] = useState5(null);
11704
12109
  const [sessionTokens, setSessionTokens] = useState5(0);
@@ -11833,8 +12238,8 @@ function App({ initialPrompt, modelPreference, agentMode, useOrchestrator = true
11833
12238
  " /help \u2014 this help",
11834
12239
  " /clear \u2014 clear chat",
11835
12240
  " /model \u2014 current model",
11836
- " /models \u2014 list all models",
11837
- " /agent \u2014 agent mode",
12241
+ " /auto \u2014 toggle auto mode (skip approvals)",
12242
+ " /yolo \u2014 toggle yolo mode (full autonomy)",
11838
12243
  " /usage \u2014 session stats",
11839
12244
  "",
11840
12245
  "Keyboard:",
@@ -11860,6 +12265,28 @@ function App({ initialPrompt, modelPreference, agentMode, useOrchestrator = true
11860
12265
  resetPhases();
11861
12266
  return;
11862
12267
  }
12268
+ if (trimmed === "/auto") {
12269
+ const newMode = agentMode === "auto" ? void 0 : "auto";
12270
+ setAgentMode(newMode);
12271
+ setMessages((prev) => [...prev, {
12272
+ id: nextId(),
12273
+ role: "assistant",
12274
+ content: newMode === "auto" ? "Auto mode ON \u2014 changes apply without asking." : "Auto mode OFF \u2014 you'll be asked before changes."
12275
+ }]);
12276
+ setInput("");
12277
+ return;
12278
+ }
12279
+ if (trimmed === "/yolo") {
12280
+ const newMode = agentMode === "yolo" ? void 0 : "yolo";
12281
+ setAgentMode(newMode);
12282
+ setMessages((prev) => [...prev, {
12283
+ id: nextId(),
12284
+ role: "assistant",
12285
+ content: newMode === "yolo" ? "YOLO mode ON \u2014 full autonomy, no approvals." : "YOLO mode OFF \u2014 back to normal approvals."
12286
+ }]);
12287
+ setInput("");
12288
+ return;
12289
+ }
11863
12290
  if (trimmed === "/model") {
11864
12291
  setScrollOffset(0);
11865
12292
  setMessages((prev) => [
@@ -11931,27 +12358,33 @@ function App({ initialPrompt, modelPreference, agentMode, useOrchestrator = true
11931
12358
  try {
11932
12359
  const { runOrchestrator: runOrchestrator2 } = await Promise.resolve().then(() => (init_loop2(), loop_exports));
11933
12360
  let responseText = "";
11934
- let currentToolLine = "";
12361
+ const steps = [];
11935
12362
  const result = await runOrchestrator2(trimmed, process.cwd(), {
11936
12363
  onLog: () => {
11937
12364
  },
11938
12365
  onText: (text) => {
11939
12366
  responseText += text;
11940
- currentToolLine = "";
11941
- streamRef.current = responseText;
11942
- setStreamingContent(responseText);
12367
+ const stepsBlock2 = steps.length > 0 ? steps.map((s) => ` \x1B[32m\u2713\x1B[0m ${s}`).join("\n") + "\n\n" : "";
12368
+ streamRef.current = stepsBlock2 + responseText;
12369
+ setStreamingContent(streamRef.current);
11943
12370
  },
11944
12371
  onToolCall: (name, input2) => {
11945
- const preview = name === "write_code" ? `writing code...` : name === "read_file" ? `reading ${String(input2.path ?? "")}` : name === "grep_file" ? `searching in ${String(input2.path ?? "")}` : name === "search_files" ? `searching "${String(input2.query ?? "")}"` : name === "edit_file" ? `editing ${String(input2.path ?? "")}` : name === "write_file" ? `creating ${String(input2.path ?? "")}` : name === "run_command" ? `running ${String(input2.command ?? "").slice(0, 40)}` : name;
11946
- currentToolLine = `> ${preview}`;
11947
- streamRef.current = responseText + (responseText ? "\n" : "") + currentToolLine;
12372
+ const preview = name === "write_code" ? "writing code..." : name === "read_file" ? `reading ${String(input2.path ?? "")}` : name === "grep_file" ? `searching in ${String(input2.path ?? "")}` : name === "search_files" ? `searching "${String(input2.query ?? "").slice(0, 40)}"` : name === "edit_file" ? `editing ${String(input2.path ?? "")}` : name === "write_file" ? `creating ${String(input2.path ?? "")}` : name === "run_command" ? `running ${String(input2.command ?? "").slice(0, 40)}` : name;
12373
+ const completedBlock = steps.map((s) => ` \x1B[32m\u2713\x1B[0m ${s}`).join("\n");
12374
+ const activeStep = ` \x1B[36m\u25CF\x1B[0m ${preview}`;
12375
+ streamRef.current = (completedBlock ? completedBlock + "\n" : "") + activeStep;
11948
12376
  setStreamingContent(streamRef.current);
11949
12377
  },
11950
- onApprovalNeeded: async (description) => {
12378
+ onToolResult: (name, result2) => {
12379
+ const label = name === "write_code" ? "code generated" : name === "search_files" ? "files found" : name === "read_file" ? "file read" : name === "grep_file" ? "pattern found" : name === "edit_file" ? result2.startsWith("Edited") ? result2.split(":")[0] : "edit failed" : name === "write_file" ? result2.startsWith("Created") ? result2.split("(")[0].trim() : "write failed" : name === "run_command" ? "command done" : name === "apply_diff" ? "diff applied" : name === "git_diff" ? "changes shown" : name === "git_commit" ? "committed" : name === "run_tests" ? result2.includes("passing") ? "tests passed" : "tests done" : name === "undo" ? "reverted" : name;
12380
+ steps.push(label);
12381
+ },
12382
+ onApprovalNeeded: agentMode === "auto" || agentMode === "yolo" ? void 0 : async (description) => {
11951
12383
  return new Promise((resolve12) => {
11952
- responseText += `
11953
- ${description}
11954
- `;
12384
+ const stepsBlock2 = steps.map((s) => ` \x1B[32m\u2713\x1B[0m ${s}`).join("\n");
12385
+ responseText = (stepsBlock2 ? stepsBlock2 + "\n\n" : "") + `${description}
12386
+
12387
+ \x1B[36mApply this change? [Y/n]\x1B[0m`;
11955
12388
  streamRef.current = responseText;
11956
12389
  setStreamingContent(responseText);
11957
12390
  setIsBusy(false);
@@ -11967,9 +12400,10 @@ ${description}
11967
12400
  }
11968
12401
  }, controller.signal, orchestratorMessagesRef.current);
11969
12402
  orchestratorMessagesRef.current = result.messages;
12403
+ const stepsBlock = steps.length > 0 ? steps.map((s) => ` \x1B[32m\u2713\x1B[0m ${s}`).join("\n") + "\n\n" : "";
11970
12404
  const costLine = `
11971
- Cost: $${result.totalCost.toFixed(4)} \xB7 ${(result.duration / 1e3).toFixed(1)}s \xB7 ${result.iterations} steps`;
11972
- responseText += costLine;
12405
+ Cost: $${result.totalCost.toFixed(4)} \xB7 ${(result.duration / 1e3).toFixed(1)}s`;
12406
+ responseText = stepsBlock + responseText + costLine;
11973
12407
  setMessages(
11974
12408
  (prev) => prev.map(
11975
12409
  (m) => m.id === assistantMsgIdRef.current ? { ...m, content: responseText, isStreaming: false } : m
@@ -12303,10 +12737,10 @@ ${diffDisplay}
12303
12737
  }
12304
12738
  async function loadContextChips() {
12305
12739
  try {
12306
- const { readFileSync: readFileSync10 } = await import("fs");
12307
- const { join: join20 } = await import("path");
12308
- const indexPath = join20(process.cwd(), ".mint", "context.json");
12309
- const raw = readFileSync10(indexPath, "utf-8");
12740
+ const { readFileSync: readFileSync11 } = await import("fs");
12741
+ const { join: join21 } = await import("path");
12742
+ const indexPath = join21(process.cwd(), ".mint", "context.json");
12743
+ const raw = readFileSync11(indexPath, "utf-8");
12310
12744
  const index = JSON.parse(raw);
12311
12745
  const chips = [];
12312
12746
  if (index.language) chips.push({ label: index.language, color: "green" });
@@ -12497,8 +12931,8 @@ var init_dashboard = __esm({
12497
12931
  // src/cli/index.ts
12498
12932
  import { Command } from "commander";
12499
12933
  import chalk10 from "chalk";
12500
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5 } from "fs";
12501
- import { dirname as dirname5, resolve as resolve11, sep as sep9 } from "path";
12934
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6 } from "fs";
12935
+ import { dirname as dirname6, resolve as resolve11, sep as sep9 } from "path";
12502
12936
 
12503
12937
  // src/cli/commands/auth.ts
12504
12938
  init_config();
@@ -12544,7 +12978,7 @@ function promptHidden(question) {
12544
12978
  });
12545
12979
  }
12546
12980
  async function signup() {
12547
- if (config.isAuthenticated()) {
12981
+ if (config2.isAuthenticated()) {
12548
12982
  console.log(chalk.yellow("Already logged in. Run `mint logout` first."));
12549
12983
  return;
12550
12984
  }
@@ -12560,7 +12994,7 @@ async function signup() {
12560
12994
  console.log(chalk.red("\n Password must be at least 8 characters."));
12561
12995
  return;
12562
12996
  }
12563
- const gatewayUrl = config.getGatewayUrl();
12997
+ const gatewayUrl = config2.getGatewayUrl();
12564
12998
  try {
12565
12999
  const res = await fetch(`${gatewayUrl}/auth/signup`, {
12566
13000
  method: "POST",
@@ -12573,7 +13007,7 @@ async function signup() {
12573
13007
  Signup failed: ${data.error || res.statusText}`));
12574
13008
  return;
12575
13009
  }
12576
- config.setAll({
13010
+ config2.setAll({
12577
13011
  apiKey: data.api_token,
12578
13012
  userId: data.user.id,
12579
13013
  email: data.user.email
@@ -12593,8 +13027,8 @@ ${chalk.dim("Token saved. You can now use mint commands.")}`,
12593
13027
  }
12594
13028
  }
12595
13029
  async function login() {
12596
- if (config.isAuthenticated()) {
12597
- const email2 = config.get("email");
13030
+ if (config2.isAuthenticated()) {
13031
+ const email2 = config2.get("email");
12598
13032
  console.log(chalk.yellow(`Already logged in as ${email2}`));
12599
13033
  console.log(chalk.dim("Run `mint logout` to switch accounts"));
12600
13034
  return;
@@ -12606,7 +13040,7 @@ async function login() {
12606
13040
  console.log(chalk.red("\n Email and password are required."));
12607
13041
  return;
12608
13042
  }
12609
- const gatewayUrl = config.getGatewayUrl();
13043
+ const gatewayUrl = config2.getGatewayUrl();
12610
13044
  try {
12611
13045
  const res = await fetch(`${gatewayUrl}/auth/login`, {
12612
13046
  method: "POST",
@@ -12633,7 +13067,7 @@ async function login() {
12633
13067
  Failed to create API token: ${tokenData.error}`));
12634
13068
  return;
12635
13069
  }
12636
- config.setAll({
13070
+ config2.setAll({
12637
13071
  apiKey: tokenData.token,
12638
13072
  userId: data.user.id,
12639
13073
  email: data.user.email
@@ -12646,22 +13080,22 @@ async function login() {
12646
13080
  }
12647
13081
  }
12648
13082
  async function logout() {
12649
- if (!config.isAuthenticated()) {
13083
+ if (!config2.isAuthenticated()) {
12650
13084
  console.log(chalk.yellow("Not currently logged in"));
12651
13085
  return;
12652
13086
  }
12653
- const email = config.get("email");
12654
- config.clear();
13087
+ const email = config2.get("email");
13088
+ config2.clear();
12655
13089
  console.log(chalk.green(`Logged out from ${email}`));
12656
13090
  }
12657
13091
  async function whoami() {
12658
- if (!config.isAuthenticated()) {
13092
+ if (!config2.isAuthenticated()) {
12659
13093
  console.log(chalk.yellow("Not logged in"));
12660
13094
  console.log(chalk.dim("Run `mint login` or `mint signup` to authenticate"));
12661
13095
  return;
12662
13096
  }
12663
- const email = config.get("email");
12664
- const configPath = config.getConfigPath();
13097
+ const email = config2.get("email");
13098
+ const configPath = config2.getConfigPath();
12665
13099
  console.log(boxen(
12666
13100
  `${chalk.bold("Current User")}
12667
13101
 
@@ -12677,8 +13111,8 @@ import chalk2 from "chalk";
12677
13111
  import boxen2 from "boxen";
12678
13112
  import Table from "cli-table3";
12679
13113
  async function showConfig() {
12680
- const currentConfig = config.getConfig();
12681
- const configPath = config.getConfigPath();
13114
+ const currentConfig = config2.getConfig();
13115
+ const configPath = config2.getConfigPath();
12682
13116
  console.log(boxen2(
12683
13117
  chalk2.bold("Mint Configuration") + "\n\n" + chalk2.dim(`Path: ${configPath}`),
12684
13118
  { padding: 1, borderColor: "cyan", borderStyle: "round" }
@@ -12720,8 +13154,8 @@ async function showConfig() {
12720
13154
  async function setConfig(key, value) {
12721
13155
  if (key.startsWith("providers.")) {
12722
13156
  const provider = key.split(".")[1];
12723
- const currentProviders = config.get("providers") || {};
12724
- config.set("providers", {
13157
+ const currentProviders = config2.get("providers") || {};
13158
+ config2.set("providers", {
12725
13159
  ...currentProviders,
12726
13160
  [provider]: value
12727
13161
  });
@@ -12729,16 +13163,16 @@ async function setConfig(key, value) {
12729
13163
  return;
12730
13164
  }
12731
13165
  if (value === "true" || value === "false") {
12732
- config.set(key, value === "true");
13166
+ config2.set(key, value === "true");
12733
13167
  console.log(chalk2.green(`\u2713 Set ${key} = ${value}`));
12734
13168
  return;
12735
13169
  }
12736
13170
  if (!isNaN(Number(value))) {
12737
- config.set(key, Number(value));
13171
+ config2.set(key, Number(value));
12738
13172
  console.log(chalk2.green(`\u2713 Set ${key} = ${value}`));
12739
13173
  return;
12740
13174
  }
12741
- config.set(key, value);
13175
+ config2.set(key, value);
12742
13176
  console.log(chalk2.green(`\u2713 Set ${key} = ${value}`));
12743
13177
  }
12744
13178
 
@@ -13004,14 +13438,14 @@ import chalk4 from "chalk";
13004
13438
  import boxen3 from "boxen";
13005
13439
  import Table3 from "cli-table3";
13006
13440
  async function showUsage(options) {
13007
- if (!config.isAuthenticated()) {
13441
+ if (!config2.isAuthenticated()) {
13008
13442
  console.log(chalk4.yellow("Not authenticated. Usage tracking requires login."));
13009
13443
  console.log(chalk4.dim("Run `axon login` to authenticate"));
13010
13444
  return;
13011
13445
  }
13012
13446
  const days = parseInt(options.days, 10) || 7;
13013
- const apiBaseUrl = config.get("apiBaseUrl") || "https://api.axon.dev";
13014
- const apiKey = config.get("apiKey");
13447
+ const apiBaseUrl = config2.get("apiBaseUrl") || "https://api.axon.dev";
13448
+ const apiKey = config2.get("apiKey");
13015
13449
  console.log(chalk4.dim(`Fetching usage for last ${days} days...
13016
13450
  `));
13017
13451
  try {
@@ -13268,9 +13702,9 @@ program.command("init").description("Scan project and build search index").actio
13268
13702
  const topLangs = [...languages.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([lang, count]) => `${lang} (${count})`).join(", ");
13269
13703
  let depCount = 0;
13270
13704
  try {
13271
- const { readFileSync: readFileSync10 } = await import("fs");
13272
- const { join: join20 } = await import("path");
13273
- const pkg = JSON.parse(readFileSync10(join20(cwd, "package.json"), "utf-8"));
13705
+ const { readFileSync: readFileSync11 } = await import("fs");
13706
+ const { join: join21 } = await import("path");
13707
+ const pkg = JSON.parse(readFileSync11(join21(cwd, "package.json"), "utf-8"));
13274
13708
  depCount = Object.keys(pkg.dependencies ?? {}).length + Object.keys(pkg.devDependencies ?? {}).length;
13275
13709
  } catch {
13276
13710
  }
@@ -13283,6 +13717,16 @@ program.command("init").description("Scan project and build search index").actio
13283
13717
  console.log(chalk10.dim(`
13284
13718
  Run ${chalk10.cyan("mint")} to start editing.
13285
13719
  `));
13720
+ try {
13721
+ const gatewayUrl = config.get("apiBaseUrl") ?? "https://api.usemint.dev";
13722
+ fetch(`${gatewayUrl}/track`, {
13723
+ method: "POST",
13724
+ headers: { "Content-Type": "application/json" },
13725
+ body: JSON.stringify({ event: "init", files_indexed: index.totalFiles, language: index.language })
13726
+ }).catch(() => {
13727
+ });
13728
+ } catch {
13729
+ }
13286
13730
  });
13287
13731
  program.command("skills").description("List all skills in .mint/skills/").action(async () => {
13288
13732
  const { loadSkills: loadSkills2 } = await Promise.resolve().then(() => (init_skills(), skills_exports));
@@ -13491,13 +13935,13 @@ function applyDiffs(diffs, cwd) {
13491
13935
  }
13492
13936
  try {
13493
13937
  if (diff.oldContent === "") {
13494
- mkdirSync5(dirname5(fullPath), { recursive: true });
13938
+ mkdirSync6(dirname6(fullPath), { recursive: true });
13495
13939
  const newContent = diff.hunks.flatMap((h) => h.lines.filter((l) => l.type !== "remove").map((l) => l.content)).join("\n");
13496
- writeFileSync6(fullPath, newContent + "\n", "utf-8");
13940
+ writeFileSync7(fullPath, newContent + "\n", "utf-8");
13497
13941
  console.log(chalk10.green(` + Created ${diff.filePath}`));
13498
13942
  continue;
13499
13943
  }
13500
- const current = readFileSync9(fullPath, "utf-8");
13944
+ const current = readFileSync10(fullPath, "utf-8");
13501
13945
  let updated = current;
13502
13946
  for (const hunk of diff.hunks) {
13503
13947
  const removeLines = hunk.lines.filter((l) => l.type === "remove").map((l) => l.content);
@@ -13527,7 +13971,7 @@ function applyDiffs(diffs, cwd) {
13527
13971
  }
13528
13972
  }
13529
13973
  if (updated !== current) {
13530
- writeFileSync6(fullPath, updated, "utf-8");
13974
+ writeFileSync7(fullPath, updated, "utf-8");
13531
13975
  console.log(chalk10.green(` ~ Modified ${diff.filePath}`));
13532
13976
  } else {
13533
13977
  console.log(chalk10.yellow(` ? Could not apply diff to ${diff.filePath} (text not found)`));