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

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
  });
@@ -4474,7 +4474,7 @@ function InputBox({
4474
4474
  ),
4475
4475
  /* @__PURE__ */ jsxs6(Box6, { borderStyle: "single", borderColor: "cyan", paddingX: 1, flexDirection: "row", children: [
4476
4476
  /* @__PURE__ */ jsx6(Box6, { flexGrow: 1, children: value.length === 0 ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
4477
- 'Ask anything\u2026 or try "fix the auth bug"',
4477
+ 'Ask anything\u2026 or try "add a pricing section"',
4478
4478
  /* @__PURE__ */ jsx6(Text6, { inverse: true, children: " " })
4479
4479
  ] }) : /* @__PURE__ */ jsxs6(Text6, { children: [
4480
4480
  before,
@@ -4560,7 +4560,7 @@ function StatusBar({
4560
4560
  /* @__PURE__ */ jsxs7(Box7, { flexShrink: 0, gap: 0, children: [
4561
4561
  /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " \u2502 " }),
4562
4562
  /* @__PURE__ */ jsx7(Text7, { color: modeColor(agentMode), children: agentMode }),
4563
- /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " \u2502 v0.2.0" }),
4563
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " \u2502 v0.2.0-beta" }),
4564
4564
  inspectorHint && /* @__PURE__ */ jsxs7(Fragment3, { children: [
4565
4565
  /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " \u2502 " }),
4566
4566
  /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: inspectorHint })
@@ -4577,69 +4577,57 @@ var init_StatusBar = __esm({
4577
4577
  // src/tui/components/WelcomeScreen.tsx
4578
4578
  import { Box as Box8, Text as Text8 } from "ink";
4579
4579
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
4580
- function WelcomeScreen({
4581
- modelCount = 18,
4582
- agentCount = 4,
4583
- savingsLabel = "97%"
4584
- }) {
4580
+ function WelcomeScreen() {
4585
4581
  return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", alignItems: "center", flexGrow: 1, paddingTop: 1, children: [
4586
4582
  /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", alignItems: "center", children: MINT_LOGO.map((line, i) => /* @__PURE__ */ jsx8(Text8, { color: "cyan", children: line }, i)) }),
4587
- /* @__PURE__ */ jsx8(Box8, { marginTop: 0, children: /* @__PURE__ */ jsx8(Text8, { color: "cyan", dimColor: true, children: " AI CODING CLI" }) }),
4583
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 0, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " AI coding assistant \xB7 under a penny per task" }) }),
4588
4584
  /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, gap: 4, children: [
4589
4585
  /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", alignItems: "center", children: [
4590
- /* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: true, children: String(modelCount) }),
4591
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "models" })
4586
+ /* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: true, children: "13" }),
4587
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "tools" })
4592
4588
  ] }),
4593
4589
  /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", alignItems: "center", children: [
4594
- /* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: true, children: String(agentCount) }),
4595
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "agents" })
4590
+ /* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: true, children: "8" }),
4591
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "providers" })
4596
4592
  ] }),
4597
4593
  /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", alignItems: "center", children: [
4598
- /* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: true, children: savingsLabel }),
4594
+ /* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: true, children: "98%" }),
4599
4595
  /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "cheaper" })
4600
4596
  ] })
4601
4597
  ] }),
4602
4598
  /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, gap: 2, children: [
4603
4599
  /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, width: 30, children: [
4604
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, bold: true, children: "QUICK START" }),
4600
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, bold: true, children: "COMMANDS" }),
4605
4601
  /* @__PURE__ */ jsxs8(Text8, { children: [
4606
- /* @__PURE__ */ jsx8(Text8, { color: "cyan", children: "mint init" }),
4607
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2014 index project" })
4602
+ /* @__PURE__ */ jsx8(Text8, { color: "cyan", children: "/help " }),
4603
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2014 show all commands" })
4608
4604
  ] }),
4609
4605
  /* @__PURE__ */ jsxs8(Text8, { children: [
4610
- /* @__PURE__ */ jsx8(Text8, { color: "cyan", children: "/models " }),
4611
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2014 all models" })
4606
+ /* @__PURE__ */ jsx8(Text8, { color: "cyan", children: "/auto " }),
4607
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2014 skip approvals" })
4612
4608
  ] }),
4613
4609
  /* @__PURE__ */ jsxs8(Text8, { children: [
4614
- /* @__PURE__ */ jsx8(Text8, { color: "cyan", children: "/agent " }),
4615
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2014 switch mode" })
4610
+ /* @__PURE__ */ jsx8(Text8, { color: "cyan", children: "/yolo " }),
4611
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2014 full autonomy" })
4616
4612
  ] }),
4617
4613
  /* @__PURE__ */ jsxs8(Text8, { children: [
4618
- /* @__PURE__ */ jsx8(Text8, { color: "cyan", children: "/usage " }),
4614
+ /* @__PURE__ */ jsx8(Text8, { color: "cyan", children: "/usage " }),
4619
4615
  /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2014 session stats" })
4620
4616
  ] })
4621
4617
  ] }),
4622
4618
  /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, width: 30, children: [
4623
4619
  /* @__PURE__ */ jsx8(Text8, { dimColor: true, bold: true, children: "KEYBOARD" }),
4624
- /* @__PURE__ */ jsxs8(Text8, { children: [
4625
- /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "Esc " }),
4626
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2192 normal mode" })
4627
- ] }),
4628
4620
  /* @__PURE__ */ jsxs8(Text8, { children: [
4629
4621
  /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "i " }),
4630
4622
  /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2192 insert mode" })
4631
4623
  ] }),
4632
4624
  /* @__PURE__ */ jsxs8(Text8, { children: [
4633
- /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "Enter " }),
4634
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2192 send message" })
4635
- ] }),
4636
- /* @__PURE__ */ jsxs8(Text8, { children: [
4637
- /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "Tab " }),
4638
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2192 live inspector" })
4625
+ /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "Esc " }),
4626
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2192 normal mode" })
4639
4627
  ] }),
4640
4628
  /* @__PURE__ */ jsxs8(Text8, { children: [
4641
- /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "PgUp " }),
4642
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2192 scroll faster" })
4629
+ /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "Enter " }),
4630
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2192 send message" })
4643
4631
  ] }),
4644
4632
  /* @__PURE__ */ jsxs8(Text8, { children: [
4645
4633
  /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "Ctrl+C" }),
@@ -7663,13 +7651,13 @@ async function executeTool2(toolName, input, toolCallId, options) {
7663
7651
  async function generateDiffPreview(toolName, input, cwd) {
7664
7652
  const { createTwoFilesPatch: createTwoFilesPatch2 } = await import("diff");
7665
7653
  const { readFile: readFile10 } = await import("fs/promises");
7666
- const { join: join20 } = await import("path");
7654
+ const { join: join21 } = await import("path");
7667
7655
  if (toolName === "write_file") {
7668
7656
  const path = String(input.path ?? "");
7669
7657
  const newContent = String(input.content ?? "");
7670
7658
  let oldContent = "";
7671
7659
  try {
7672
- oldContent = await readFile10(join20(cwd, path), "utf-8");
7660
+ oldContent = await readFile10(join21(cwd, path), "utf-8");
7673
7661
  } catch {
7674
7662
  }
7675
7663
  return createTwoFilesPatch2(path, path, oldContent, newContent, "old", "new");
@@ -7679,7 +7667,7 @@ async function generateDiffPreview(toolName, input, cwd) {
7679
7667
  const oldStr = String(input.old_text ?? "");
7680
7668
  const newStr = String(input.new_text ?? "");
7681
7669
  try {
7682
- const current = await readFile10(join20(cwd, path), "utf-8");
7670
+ const current = await readFile10(join21(cwd, path), "utf-8");
7683
7671
  const firstMatch = current.indexOf(oldStr);
7684
7672
  const secondMatch = firstMatch === -1 ? -1 : current.indexOf(oldStr, firstMatch + oldStr.length);
7685
7673
  if (firstMatch !== -1 && secondMatch === -1) {
@@ -7692,7 +7680,7 @@ async function generateDiffPreview(toolName, input, cwd) {
7692
7680
  }
7693
7681
  if (toolName === "search_replace") {
7694
7682
  const path = String(input.path ?? "");
7695
- const current = await readFile10(join20(cwd, path), "utf-8");
7683
+ const current = await readFile10(join21(cwd, path), "utf-8");
7696
7684
  const { buildSearchReplacePlan: buildSearchReplacePlan2, buildSearchReplacePreview: buildSearchReplacePreview2 } = await Promise.resolve().then(() => (init_search_replace(), search_replace_exports));
7697
7685
  const plan = buildSearchReplacePlan2(current, {
7698
7686
  path,
@@ -10994,39 +10982,210 @@ var init_pipeline = __esm({
10994
10982
  });
10995
10983
 
10996
10984
  // src/orchestrator/prompts.ts
10997
- var ORCHESTRATOR_PROMPT;
10985
+ var ORCHESTRATOR_PROMPT, MEMORY_INSTRUCTION;
10998
10986
  var init_prompts = __esm({
10999
10987
  "src/orchestrator/prompts.ts"() {
11000
10988
  "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
10989
+ 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.
10990
+
10991
+ # Tools
10992
+
10993
+ ## Search & Read (free, no LLM cost)
10994
+ - **search_files**: Find relevant files by keywords. Use this first to locate files related to the task.
10995
+ - **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.
10996
+ - **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.
10997
+ - **list_files**: List directory contents. Ignores node_modules and hidden files.
10998
+
10999
+ ## Edit & Write (requires user approval)
11000
+ - **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.
11001
+ - **write_file**: Create a new file or overwrite entirely. Use for new files only \u2014 prefer edit_file for changes to existing files.
11002
+ - **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.
11003
+
11004
+ ## Verify & Execute (free, no LLM cost)
11005
+ - **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).
11006
+ - **run_tests**: Detect and run the project's test suite. Checks package.json for test script.
11007
+ - **git_diff**: Show all uncommitted changes (status + staged + unstaged diffs).
11008
+ - **git_commit**: Stage all changes and commit with a message.
11009
+ - **undo**: Revert the last edit to a specific file. Only the most recent change per file can be undone.
11010
+
11011
+ # How to work
11012
+
11013
+ ## Questions vs changes
11014
+ - 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.
11015
+ - Only edit files when the user explicitly asks for a change (fix, add, change, update, create, remove, rename, build).
11016
+
11017
+ ## Before editing
11018
+ - Always read before writing. Never assume file contents.
11019
+ - 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.
11020
+ - 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.
11021
+
11022
+ ## Making changes
11023
+ - 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.
11024
+ - Don't add features, refactor code, or make "improvements" beyond what was asked.
11025
+ - Don't add error handling, fallbacks, or validation for scenarios that can't happen. Only validate at system boundaries (user input, external APIs).
11026
+ - Don't add comments, docstrings, or type annotations to code you didn't change.
11027
+ - Don't create helpers or abstractions for one-time operations. Three similar lines is better than a premature abstraction.
11028
+ - Match existing code patterns \u2014 read 2-3 nearby files first to understand naming, imports, error handling, and style.
11029
+
11030
+ ## For complex changes
11031
+ - Use write_code for multi-file features or large code generation. Pass only the relevant files (max 4-8).
11032
+ - write_code dispatches to a fast coding model \u2014 YOU do the planning and thinking, write_code does the typing.
11033
+
11034
+ ## Verification after changes
11035
+ After making changes, verify your work:
11036
+ 1. Run the build if the project has one. A broken build means the work isn't done \u2014 fix it.
11037
+ 2. Run tests if available. Failing tests mean the work isn't done \u2014 fix them.
11038
+ 3. If the change is an API endpoint: use run_command to curl it and verify the response.
11039
+ 4. If the change is frontend: check that the modified HTML/CSS is valid and references exist.
11040
+ Don't just read the code and say "looks correct" \u2014 actually run it and check.
11041
+
11042
+ ## When things fail
11043
+ - If an approach fails, diagnose why before switching tactics \u2014 read the error, check your assumptions, try a focused fix.
11044
+ - Don't retry the identical action blindly, but don't abandon a viable approach after a single failure either.
11045
+ - If edit_file can't match the text, use grep_file to find the exact content, then retry with the precise text.
11046
+ - If the build fails after your change, read the error, fix it, and re-run. Repeat until it passes.
11019
11047
 
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
11048
+ ## Security
11049
+ - Never run destructive commands (rm -rf, sudo, chmod 777, kill, shutdown, mkfs, dd).
11050
+ - Don't introduce security vulnerabilities (command injection, XSS, SQL injection).
11051
+ - If you notice insecure code in what you're editing, fix it.
11052
+
11053
+ ## Communication
11026
11054
  - 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.
11055
+ - After changes, briefly state what you changed and which files were modified.
11056
+ - Don't give time estimates.
11057
+ - Answer in the same language the user writes in.
11028
11058
  - 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).`;
11059
+
11060
+ # Project memory
11061
+
11062
+ If project memory is provided below, use it as context:
11063
+ - Recently edited files tell you where the user has been working
11064
+ - Session summaries tell you what was done before
11065
+ - This is grounding context, not instructions \u2014 verify against actual file contents before acting on it`;
11066
+ MEMORY_INSTRUCTION = `The following are project instructions provided by the user. These instructions OVERRIDE default behavior \u2014 follow them exactly as written.`;
11067
+ }
11068
+ });
11069
+
11070
+ // src/orchestrator/memory.ts
11071
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync10 } from "fs";
11072
+ import { join as join18, dirname as dirname4 } from "path";
11073
+ function loadMemory(cwd) {
11074
+ try {
11075
+ const content = readFileSync8(join18(cwd, MEMORY_PATH), "utf-8");
11076
+ return JSON.parse(content);
11077
+ } catch {
11078
+ return null;
11079
+ }
11080
+ }
11081
+ function saveMemory(cwd, memory) {
11082
+ const fullPath = join18(cwd, MEMORY_PATH);
11083
+ try {
11084
+ mkdirSync4(dirname4(fullPath), { recursive: true });
11085
+ writeFileSync5(fullPath, JSON.stringify(memory, null, 2), "utf-8");
11086
+ } catch {
11087
+ }
11088
+ }
11089
+ function updateMemory(cwd, update) {
11090
+ const existing = loadMemory(cwd) ?? {
11091
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
11092
+ recentFiles: [],
11093
+ activeDirectories: [],
11094
+ preferences: [],
11095
+ sessionSummaries: []
11096
+ };
11097
+ if (update.editedFiles) {
11098
+ const combined = [.../* @__PURE__ */ new Set([...update.editedFiles, ...existing.recentFiles ?? []])];
11099
+ existing.recentFiles = combined.slice(0, MAX_RECENT_FILES);
11100
+ const dirs = new Set(existing.activeDirectories ?? []);
11101
+ for (const f of update.editedFiles) {
11102
+ const parts = f.split("/");
11103
+ if (parts.length > 1) dirs.add(parts.slice(0, -1).join("/"));
11104
+ }
11105
+ existing.activeDirectories = [...dirs].slice(0, 10);
11106
+ }
11107
+ if (update.sessionSummary) {
11108
+ existing.sessionSummaries = [
11109
+ update.sessionSummary,
11110
+ ...existing.sessionSummaries ?? []
11111
+ ].slice(0, MAX_SUMMARIES);
11112
+ }
11113
+ if (update.projectDescription) existing.projectDescription = update.projectDescription;
11114
+ if (update.language) existing.language = update.language;
11115
+ existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
11116
+ saveMemory(cwd, existing);
11117
+ }
11118
+ function formatMemoryForPrompt(memory) {
11119
+ const parts = [];
11120
+ if (memory.projectDescription) {
11121
+ parts.push(`Project: ${memory.projectDescription}`);
11122
+ }
11123
+ if (memory.language) {
11124
+ parts.push(`Language: ${memory.language}`);
11125
+ }
11126
+ if (memory.recentFiles?.length > 0) {
11127
+ parts.push(`Recently edited files: ${memory.recentFiles.slice(0, 10).join(", ")}`);
11128
+ }
11129
+ if (memory.activeDirectories?.length > 0) {
11130
+ parts.push(`Active directories: ${memory.activeDirectories.join(", ")}`);
11131
+ }
11132
+ if (memory.sessionSummaries?.length > 0) {
11133
+ parts.push(`Recent session: ${memory.sessionSummaries[0]}`);
11134
+ }
11135
+ return parts.length > 0 ? `
11136
+ <project_memory>
11137
+ ${parts.join("\n")}
11138
+ </project_memory>` : "";
11139
+ }
11140
+ async function loadProjectInstructions(cwd) {
11141
+ const candidates = [
11142
+ "MINT.md",
11143
+ ".mint/MINT.md",
11144
+ "CLAUDE.md",
11145
+ ".claude/CLAUDE.md"
11146
+ ];
11147
+ const parts = [];
11148
+ for (const candidate of candidates) {
11149
+ const fullPath = join18(cwd, candidate);
11150
+ if (existsSync10(fullPath)) {
11151
+ try {
11152
+ const content = readFileSync8(fullPath, "utf-8").trim();
11153
+ if (content.length > 0 && content.length < 4e4) {
11154
+ parts.push(`# ${candidate}
11155
+ ${content}`);
11156
+ }
11157
+ } catch {
11158
+ }
11159
+ }
11160
+ }
11161
+ const rulesDir = join18(cwd, ".mint", "rules");
11162
+ if (existsSync10(rulesDir)) {
11163
+ try {
11164
+ const { readdirSync: readdirSync4 } = await import("fs");
11165
+ const files = readdirSync4(rulesDir);
11166
+ for (const file of files) {
11167
+ if (!file.endsWith(".md")) continue;
11168
+ try {
11169
+ const content = readFileSync8(join18(rulesDir, file), "utf-8").trim();
11170
+ if (content.length > 0 && content.length < 1e4) {
11171
+ parts.push(`# .mint/rules/${file}
11172
+ ${content}`);
11173
+ }
11174
+ } catch {
11175
+ }
11176
+ }
11177
+ } catch {
11178
+ }
11179
+ }
11180
+ return parts.length > 0 ? parts.join("\n\n") : null;
11181
+ }
11182
+ var MEMORY_PATH, MAX_RECENT_FILES, MAX_SUMMARIES;
11183
+ var init_memory = __esm({
11184
+ "src/orchestrator/memory.ts"() {
11185
+ "use strict";
11186
+ MEMORY_PATH = ".mint/memory.json";
11187
+ MAX_RECENT_FILES = 20;
11188
+ MAX_SUMMARIES = 10;
11030
11189
  }
11031
11190
  });
11032
11191
 
@@ -11109,15 +11268,24 @@ Include 3 context lines around each change. One diff block per file.`;
11109
11268
  });
11110
11269
 
11111
11270
  // src/orchestrator/tools.ts
11112
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, readdirSync as readdirSync3, mkdirSync as mkdirSync4 } from "fs";
11271
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, readdirSync as readdirSync3, existsSync as existsSync11, mkdirSync as mkdirSync5 } from "fs";
11113
11272
  import { execSync } from "child_process";
11114
- import { join as join18, dirname as dirname4 } from "path";
11273
+ import { join as join19, dirname as dirname5 } from "path";
11115
11274
  function getWriteCodeCost() {
11116
11275
  return sessionWriteCodeCost;
11117
11276
  }
11118
11277
  function resetWriteCodeCost() {
11119
11278
  sessionWriteCodeCost = 0;
11120
11279
  }
11280
+ function isToolSafe(toolName, input) {
11281
+ if (SAFE_TOOLS.has(toolName)) return true;
11282
+ if (toolName === "run_command") {
11283
+ const cmd = String(input.command ?? "").trim().toLowerCase();
11284
+ 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 "];
11285
+ return readOnlyPrefixes.some((prefix) => cmd.startsWith(prefix));
11286
+ }
11287
+ return false;
11288
+ }
11121
11289
  async function executeOrchestratorTool(toolName, input, ctx) {
11122
11290
  switch (toolName) {
11123
11291
  case "search_files":
@@ -11147,6 +11315,14 @@ async function executeOrchestratorTool(toolName, input, ctx) {
11147
11315
  return toolWriteFile(String(input.path ?? ""), String(input.content ?? ""), ctx);
11148
11316
  case "run_command":
11149
11317
  return toolRunCommand(String(input.command ?? ""), ctx);
11318
+ case "git_diff":
11319
+ return toolGitDiff(ctx);
11320
+ case "git_commit":
11321
+ return toolGitCommit(String(input.message ?? ""), ctx);
11322
+ case "run_tests":
11323
+ return toolRunTests(ctx);
11324
+ case "undo":
11325
+ return toolUndo(String(input.path ?? ""), ctx);
11150
11326
  default:
11151
11327
  return `Unknown tool: ${toolName}`;
11152
11328
  }
@@ -11172,10 +11348,10 @@ async function toolSearchFiles(query, ctx) {
11172
11348
  }
11173
11349
  function toolReadFile(filePath, ctx) {
11174
11350
  ctx.onLog?.(`reading ${filePath}`);
11175
- const fullPath = join18(ctx.cwd, filePath);
11351
+ const fullPath = join19(ctx.cwd, filePath);
11176
11352
  if (!fullPath.startsWith(ctx.cwd)) return "Error: path outside project directory";
11177
11353
  try {
11178
- const content = readFileSync8(fullPath, "utf-8");
11354
+ const content = readFileSync9(fullPath, "utf-8");
11179
11355
  if (content.length > 32e3) {
11180
11356
  const lines = content.split("\n");
11181
11357
  const preview = lines.slice(0, 200).map((l, i) => `${i + 1}: ${l}`).join("\n");
@@ -11190,10 +11366,10 @@ function toolReadFile(filePath, ctx) {
11190
11366
  }
11191
11367
  function toolGrepFile(filePath, pattern, ctx) {
11192
11368
  ctx.onLog?.(`grep ${filePath}: ${pattern}`);
11193
- const fullPath = join18(ctx.cwd, filePath);
11369
+ const fullPath = join19(ctx.cwd, filePath);
11194
11370
  if (!fullPath.startsWith(ctx.cwd)) return "Error: path outside project directory";
11195
11371
  try {
11196
- const content = readFileSync8(fullPath, "utf-8");
11372
+ const content = readFileSync9(fullPath, "utf-8");
11197
11373
  const lines = content.split("\n");
11198
11374
  const matches = [];
11199
11375
  const patternLower = pattern.toLowerCase();
@@ -11215,7 +11391,7 @@ function toolGrepFile(filePath, pattern, ctx) {
11215
11391
  }
11216
11392
  function toolListFiles(dirPath, ctx) {
11217
11393
  ctx.onLog?.(`listing ${dirPath}`);
11218
- const fullPath = join18(ctx.cwd, dirPath);
11394
+ const fullPath = join19(ctx.cwd, dirPath);
11219
11395
  if (!fullPath.startsWith(ctx.cwd) && fullPath !== ctx.cwd) return "Error: path outside project directory";
11220
11396
  try {
11221
11397
  const entries = readdirSync3(fullPath, { withFileTypes: true });
@@ -11233,7 +11409,7 @@ async function toolWriteCode(task, files, ctx) {
11233
11409
  resolvedFiles[path] = content;
11234
11410
  } else {
11235
11411
  try {
11236
- resolvedFiles[path] = readFileSync8(join18(ctx.cwd, path), "utf-8");
11412
+ resolvedFiles[path] = readFileSync9(join19(ctx.cwd, path), "utf-8");
11237
11413
  } catch {
11238
11414
  resolvedFiles[path] = "(file does not exist \u2014 create it)";
11239
11415
  }
@@ -11257,17 +11433,18 @@ async function toolEditFile(filePath, oldText, newText, ctx) {
11257
11433
  const approved = await ctx.onApprovalNeeded(preview);
11258
11434
  if (!approved) return "User rejected this edit.";
11259
11435
  }
11260
- const fullPath = join18(ctx.cwd, filePath);
11436
+ const fullPath = join19(ctx.cwd, filePath);
11261
11437
  if (!fullPath.startsWith(ctx.cwd)) return "Error: path outside project directory";
11262
11438
  try {
11263
- const content = readFileSync8(fullPath, "utf-8");
11439
+ const content = readFileSync9(fullPath, "utf-8");
11264
11440
  if (content.includes(oldText)) {
11265
11441
  const count = content.split(oldText).length - 1;
11266
11442
  if (count > 1) {
11267
11443
  return `Error: old_text matches ${count} locations in ${filePath}. Make it more specific by including more surrounding context.`;
11268
11444
  }
11445
+ undoBackups.set(filePath, content);
11269
11446
  const updated = content.replace(oldText, newText);
11270
- writeFileSync5(fullPath, updated, "utf-8");
11447
+ writeFileSync6(fullPath, updated, "utf-8");
11271
11448
  return `Edited ${filePath}: replaced ${oldText.length} chars with ${newText.length} chars.`;
11272
11449
  }
11273
11450
  const normalize = (s) => s.replace(/\s+/g, " ").trim();
@@ -11281,7 +11458,7 @@ async function toolEditFile(filePath, oldText, newText, ctx) {
11281
11458
  newText
11282
11459
  ));
11283
11460
  if (updated !== content) {
11284
- writeFileSync5(fullPath, updated, "utf-8");
11461
+ writeFileSync6(fullPath, updated, "utf-8");
11285
11462
  return `Edited ${filePath} (fuzzy match on line ${i + 1}): replaced text.`;
11286
11463
  }
11287
11464
  }
@@ -11289,7 +11466,7 @@ async function toolEditFile(filePath, oldText, newText, ctx) {
11289
11466
  const window = lines.slice(i, i + windowSize).join("\n");
11290
11467
  if (normalize(window).includes(normalizedOld)) {
11291
11468
  const replacement = lines.slice(0, i).join("\n") + "\n" + newText + "\n" + lines.slice(i + windowSize).join("\n");
11292
- writeFileSync5(fullPath, replacement, "utf-8");
11469
+ writeFileSync6(fullPath, replacement, "utf-8");
11293
11470
  return `Edited ${filePath} (fuzzy match lines ${i + 1}-${i + windowSize}): replaced text.`;
11294
11471
  }
11295
11472
  }
@@ -11303,6 +11480,75 @@ Tip: use read_file to see the exact content, then copy-paste the exact text you
11303
11480
  return `Error editing ${filePath}: ${err instanceof Error ? err.message : String(err)}`;
11304
11481
  }
11305
11482
  }
11483
+ function toolGitDiff(ctx) {
11484
+ ctx.onLog?.("git diff");
11485
+ try {
11486
+ const diff = execSync("git diff", { cwd: ctx.cwd, encoding: "utf-8", timeout: 1e4 });
11487
+ const staged = execSync("git diff --cached", { cwd: ctx.cwd, encoding: "utf-8", timeout: 1e4 });
11488
+ const status = execSync("git status --short", { cwd: ctx.cwd, encoding: "utf-8", timeout: 1e4 });
11489
+ const parts = [
11490
+ status.trim() ? `Status:
11491
+ ${status.trim()}` : "No changes.",
11492
+ diff.trim() ? `
11493
+ Unstaged changes:
11494
+ ${diff.trim().slice(0, MAX_OUTPUT4)}` : "",
11495
+ staged.trim() ? `
11496
+ Staged changes:
11497
+ ${staged.trim().slice(0, MAX_OUTPUT4)}` : ""
11498
+ ].filter(Boolean);
11499
+ return parts.join("\n") || "Working tree clean.";
11500
+ } catch (err) {
11501
+ return `Git error: ${err instanceof Error ? err.message : String(err)}`;
11502
+ }
11503
+ }
11504
+ function toolGitCommit(message, ctx) {
11505
+ ctx.onLog?.(`git commit: ${message.slice(0, 40)}`);
11506
+ try {
11507
+ execSync("git add -A", { cwd: ctx.cwd, timeout: 1e4 });
11508
+ const result = execSync(`git commit -m "${message.replace(/"/g, '\\"')}"`, {
11509
+ cwd: ctx.cwd,
11510
+ encoding: "utf-8",
11511
+ timeout: 1e4
11512
+ });
11513
+ return result.trim() || "Committed.";
11514
+ } catch (err) {
11515
+ const msg = err instanceof Error ? err.message : String(err);
11516
+ if (msg.includes("nothing to commit")) return "Nothing to commit \u2014 working tree clean.";
11517
+ return `Git commit error: ${msg}`;
11518
+ }
11519
+ }
11520
+ function toolRunTests(ctx) {
11521
+ ctx.onLog?.("running tests");
11522
+ try {
11523
+ const pkgPath = join19(ctx.cwd, "package.json");
11524
+ if (existsSync11(pkgPath)) {
11525
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
11526
+ const testScript = pkg.scripts?.test;
11527
+ if (testScript && testScript !== 'echo "Error: no test specified" && exit 1') {
11528
+ const output = execSync("npm test", { cwd: ctx.cwd, encoding: "utf-8", timeout: 6e4, maxBuffer: 1024 * 1024 });
11529
+ return output.length > MAX_OUTPUT4 ? output.slice(0, MAX_OUTPUT4) + "\n... (truncated)" : output;
11530
+ }
11531
+ }
11532
+ return "No test script found in package.json.";
11533
+ } catch (err) {
11534
+ const execErr = err;
11535
+ const out = [execErr.stdout, execErr.stderr].filter(Boolean).join("\n");
11536
+ return out.length > MAX_OUTPUT4 ? out.slice(0, MAX_OUTPUT4) + "\n... (truncated)" : out || "Tests failed.";
11537
+ }
11538
+ }
11539
+ function toolUndo(filePath, ctx) {
11540
+ ctx.onLog?.(`undo ${filePath}`);
11541
+ const backup = undoBackups.get(filePath);
11542
+ if (!backup) return `No undo history for ${filePath}. Only the most recent edit can be undone.`;
11543
+ const fullPath = join19(ctx.cwd, filePath);
11544
+ try {
11545
+ writeFileSync6(fullPath, backup, "utf-8");
11546
+ undoBackups.delete(filePath);
11547
+ return `Reverted ${filePath} to previous state.`;
11548
+ } catch (err) {
11549
+ return `Undo error: ${err instanceof Error ? err.message : String(err)}`;
11550
+ }
11551
+ }
11306
11552
  function escapeRegex(s) {
11307
11553
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
11308
11554
  }
@@ -11314,11 +11560,15 @@ async function toolWriteFile(filePath, content, ctx) {
11314
11560
  const approved = await ctx.onApprovalNeeded(preview);
11315
11561
  if (!approved) return "User rejected this file creation.";
11316
11562
  }
11317
- const fullPath = join18(ctx.cwd, filePath);
11563
+ const fullPath = join19(ctx.cwd, filePath);
11318
11564
  if (!fullPath.startsWith(ctx.cwd)) return "Error: path outside project directory";
11319
11565
  try {
11320
- mkdirSync4(dirname4(fullPath), { recursive: true });
11321
- writeFileSync5(fullPath, content, "utf-8");
11566
+ mkdirSync5(dirname5(fullPath), { recursive: true });
11567
+ try {
11568
+ undoBackups.set(filePath, readFileSync9(fullPath, "utf-8"));
11569
+ } catch {
11570
+ }
11571
+ writeFileSync6(fullPath, content, "utf-8");
11322
11572
  return `Created ${filePath} (${content.length} chars).`;
11323
11573
  } catch (err) {
11324
11574
  return `Error writing ${filePath}: ${err instanceof Error ? err.message : String(err)}`;
@@ -11369,7 +11619,7 @@ function toolRunCommand(command, ctx) {
11369
11619
  return out.length > MAX_OUTPUT4 ? out.slice(0, MAX_OUTPUT4) + "\n... (truncated)" : out || "Command failed";
11370
11620
  }
11371
11621
  }
11372
- var sessionWriteCodeCost, ORCHESTRATOR_TOOL_DEFINITIONS, DANGEROUS_COMMANDS, MAX_OUTPUT4;
11622
+ var sessionWriteCodeCost, undoBackups, SAFE_TOOLS, ORCHESTRATOR_TOOL_DEFINITIONS, DANGEROUS_COMMANDS, MAX_OUTPUT4;
11373
11623
  var init_tools3 = __esm({
11374
11624
  "src/orchestrator/tools.ts"() {
11375
11625
  "use strict";
@@ -11378,6 +11628,16 @@ var init_tools3 = __esm({
11378
11628
  init_diff_apply();
11379
11629
  init_write_code();
11380
11630
  sessionWriteCodeCost = 0;
11631
+ undoBackups = /* @__PURE__ */ new Map();
11632
+ SAFE_TOOLS = /* @__PURE__ */ new Set([
11633
+ "search_files",
11634
+ "read_file",
11635
+ "grep_file",
11636
+ "list_files",
11637
+ "git_diff",
11638
+ "run_tests",
11639
+ "write_code"
11640
+ ]);
11381
11641
  ORCHESTRATOR_TOOL_DEFINITIONS = [
11382
11642
  {
11383
11643
  name: "search_files",
@@ -11482,6 +11742,46 @@ var init_tools3 = __esm({
11482
11742
  },
11483
11743
  required: ["command"]
11484
11744
  }
11745
+ },
11746
+ {
11747
+ name: "git_diff",
11748
+ description: "Show what changed since the last commit. Returns unified diff of all modified files.",
11749
+ input_schema: {
11750
+ type: "object",
11751
+ properties: {},
11752
+ required: []
11753
+ }
11754
+ },
11755
+ {
11756
+ name: "git_commit",
11757
+ description: "Stage all changes and commit with a message.",
11758
+ input_schema: {
11759
+ type: "object",
11760
+ properties: {
11761
+ message: { type: "string", description: "Commit message" }
11762
+ },
11763
+ required: ["message"]
11764
+ }
11765
+ },
11766
+ {
11767
+ name: "run_tests",
11768
+ description: "Detect and run the project test suite. Tries npm test, then looks for common test runners.",
11769
+ input_schema: {
11770
+ type: "object",
11771
+ properties: {},
11772
+ required: []
11773
+ }
11774
+ },
11775
+ {
11776
+ name: "undo",
11777
+ description: "Revert the last file change made by edit_file or write_file. Restores the file to its previous state.",
11778
+ input_schema: {
11779
+ type: "object",
11780
+ properties: {
11781
+ path: { type: "string", description: "File path to revert" }
11782
+ },
11783
+ required: ["path"]
11784
+ }
11485
11785
  }
11486
11786
  ];
11487
11787
  DANGEROUS_COMMANDS = /\b(rm\s+-rf|sudo|chmod\s+777|mkfs|dd\s+if|shutdown|reboot|kill\s+-9|pkill)\b/;
@@ -11497,8 +11797,20 @@ __export(loop_exports, {
11497
11797
  async function runOrchestrator(task, cwd, callbacks, signal, previousMessages) {
11498
11798
  const startTime = Date.now();
11499
11799
  resetWriteCodeCost();
11800
+ const memory = loadMemory(cwd);
11801
+ const memoryBlock = memory ? formatMemoryForPrompt(memory) : "";
11802
+ const projectInstructions = await loadProjectInstructions(cwd);
11803
+ const instructionsBlock = projectInstructions ? `
11804
+
11805
+ ${MEMORY_INSTRUCTION}
11806
+
11807
+ ${projectInstructions}` : "";
11808
+ const systemPrompt = ORCHESTRATOR_PROMPT + memoryBlock + instructionsBlock;
11809
+ const safeHistory = (previousMessages ?? []).filter(
11810
+ (m) => m && typeof m.role === "string" && (typeof m.content === "string" || m.content === null || m.content === void 0)
11811
+ );
11500
11812
  const messages = [
11501
- ...previousMessages ?? [],
11813
+ ...safeHistory,
11502
11814
  { role: "user", content: task }
11503
11815
  ];
11504
11816
  const toolCtx = {
@@ -11513,13 +11825,14 @@ async function runOrchestrator(task, cwd, callbacks, signal, previousMessages) {
11513
11825
  for (let i = 0; i < MAX_ITERATIONS; i++) {
11514
11826
  if (signal?.aborted) break;
11515
11827
  iterations = i + 1;
11828
+ compactMessagesIfNeeded(messages);
11516
11829
  let responseText = "";
11517
11830
  const toolCalls = [];
11518
11831
  try {
11519
11832
  for await (const chunk of streamAgent({
11520
11833
  model: ORCHESTRATOR_MODEL,
11521
11834
  messages,
11522
- systemPrompt: ORCHESTRATOR_PROMPT,
11835
+ systemPrompt,
11523
11836
  tools: ORCHESTRATOR_TOOL_DEFINITIONS,
11524
11837
  maxTokens: 4096,
11525
11838
  signal,
@@ -11539,7 +11852,8 @@ async function runOrchestrator(task, cwd, callbacks, signal, previousMessages) {
11539
11852
  }
11540
11853
  } catch (err) {
11541
11854
  const errMsg2 = formatError(err);
11542
- callbacks?.onLog?.(`${errMsg2}`);
11855
+ const stack = err instanceof Error ? err.stack?.split("\n").slice(0, 3).join("\n") : "";
11856
+ callbacks?.onLog?.(`${errMsg2}${stack ? "\n" + stack : ""}`);
11543
11857
  fullOutput += `
11544
11858
  ${errMsg2}`;
11545
11859
  break;
@@ -11562,8 +11876,24 @@ ${errMsg2}`;
11562
11876
  }))
11563
11877
  });
11564
11878
  const toolResults = [];
11565
- for (const tc of toolCalls) {
11566
- callbacks?.onLog?.(`tool: ${tc.name}`);
11879
+ const writeTools = /* @__PURE__ */ new Set(["edit_file", "write_file", "git_commit", "apply_diff"]);
11880
+ const parallelCalls = toolCalls.filter((tc) => !writeTools.has(tc.name));
11881
+ const sequentialCalls = toolCalls.filter((tc) => writeTools.has(tc.name));
11882
+ if (parallelCalls.length > 0) {
11883
+ const results = await Promise.all(
11884
+ parallelCalls.map(async (tc) => {
11885
+ callbacks?.onToolCall?.(tc.name, tc.input);
11886
+ const result = await executeOrchestratorTool(tc.name, tc.input, toolCtx);
11887
+ callbacks?.onToolResult?.(tc.name, result.slice(0, 200));
11888
+ return { toolCallId: tc.id, content: result };
11889
+ })
11890
+ );
11891
+ toolResults.push(...results);
11892
+ }
11893
+ for (const tc of sequentialCalls) {
11894
+ callbacks?.onToolCall?.(tc.name, tc.input);
11895
+ if (!isToolSafe(tc.name, tc.input) && callbacks?.onApprovalNeeded) {
11896
+ }
11567
11897
  const result = await executeOrchestratorTool(tc.name, tc.input, toolCtx);
11568
11898
  callbacks?.onToolResult?.(tc.name, result.slice(0, 200));
11569
11899
  toolResults.push({ toolCallId: tc.id, content: result });
@@ -11574,6 +11904,23 @@ ${errMsg2}`;
11574
11904
  toolResults
11575
11905
  });
11576
11906
  }
11907
+ const editedFiles = [];
11908
+ for (const msg of messages) {
11909
+ const tc = msg.toolCalls;
11910
+ if (tc) {
11911
+ for (const call of tc) {
11912
+ if ((call.name === "edit_file" || call.name === "write_file") && call.input.path) {
11913
+ editedFiles.push(String(call.input.path));
11914
+ }
11915
+ }
11916
+ }
11917
+ }
11918
+ if (editedFiles.length > 0 || fullOutput) {
11919
+ updateMemory(cwd, {
11920
+ editedFiles: [...new Set(editedFiles)],
11921
+ sessionSummary: fullOutput.slice(0, 200)
11922
+ });
11923
+ }
11577
11924
  const modelInfo = MODELS[ORCHESTRATOR_MODEL];
11578
11925
  const orchestratorCost = modelInfo ? orchestratorInputTokens / 1e6 * modelInfo.inputPrice + orchestratorOutputTokens / 1e6 * modelInfo.outputPrice : 0;
11579
11926
  const writeCodeCost = getWriteCodeCost();
@@ -11588,6 +11935,52 @@ ${errMsg2}`;
11588
11935
  messages
11589
11936
  };
11590
11937
  }
11938
+ function compactMessagesIfNeeded(messages) {
11939
+ const totalChars = messages.reduce((sum, m) => {
11940
+ const content = typeof m.content === "string" ? m.content : "";
11941
+ const toolContent = m.toolResults ? JSON.stringify(m.toolResults) : "";
11942
+ return sum + content.length + toolContent.length;
11943
+ }, 0);
11944
+ if (totalChars < MAX_CONTEXT_CHARS) return;
11945
+ if (messages.length <= 8) return;
11946
+ const first = messages[0];
11947
+ const recent = messages.slice(-6);
11948
+ const middle = messages.slice(1, -6);
11949
+ const filesEdited = /* @__PURE__ */ new Set();
11950
+ const filesRead = /* @__PURE__ */ new Set();
11951
+ const userRequests = [];
11952
+ const actionsCompleted = [];
11953
+ for (const msg of middle) {
11954
+ if (msg.role === "user" && msg.content) {
11955
+ userRequests.push(msg.content.slice(0, 150));
11956
+ }
11957
+ if (msg.role === "assistant" && msg.content) {
11958
+ const text = msg.content.trim();
11959
+ if (text) actionsCompleted.push(text.slice(0, 200));
11960
+ }
11961
+ const tc = msg.toolCalls;
11962
+ if (tc) {
11963
+ for (const call of tc) {
11964
+ const path = String(call.input?.path ?? "");
11965
+ if (call.name === "edit_file" || call.name === "write_file") filesEdited.add(path);
11966
+ if (call.name === "read_file" || call.name === "grep_file") filesRead.add(path);
11967
+ }
11968
+ }
11969
+ }
11970
+ const summaryParts = [
11971
+ "[Conversation compacted to save context]",
11972
+ userRequests.length > 0 ? `User requests: ${userRequests.join(" \u2192 ")}` : "",
11973
+ filesRead.size > 0 ? `Files examined: ${[...filesRead].join(", ")}` : "",
11974
+ filesEdited.size > 0 ? `Files modified: ${[...filesEdited].join(", ")}` : "",
11975
+ actionsCompleted.length > 0 ? `Actions: ${actionsCompleted.join(" | ")}` : ""
11976
+ ].filter(Boolean);
11977
+ const summary = {
11978
+ role: "assistant",
11979
+ content: summaryParts.join("\n")
11980
+ };
11981
+ messages.length = 0;
11982
+ messages.push(first, summary, ...recent);
11983
+ }
11591
11984
  function formatError(err) {
11592
11985
  const raw = err instanceof Error ? err.message : typeof err === "object" && err !== null ? JSON.stringify(err) : String(err);
11593
11986
  if (raw.includes("401") || raw.includes("Unauthorized")) return "Gateway auth failed. Run `mint login` or check your API keys.";
@@ -11599,16 +11992,19 @@ function formatError(err) {
11599
11992
  if (raw.includes("No provider")) return "No API key configured. Run `mint config:set providers.deepseek <key>` or `mint login`.";
11600
11993
  return raw.length > 200 ? raw.slice(0, 200) + "..." : raw;
11601
11994
  }
11602
- var ORCHESTRATOR_MODEL, MAX_ITERATIONS;
11995
+ var ORCHESTRATOR_MODEL, MAX_ITERATIONS, MAX_CONTEXT_CHARS;
11603
11996
  var init_loop2 = __esm({
11604
11997
  "src/orchestrator/loop.ts"() {
11605
11998
  "use strict";
11606
11999
  init_providers();
11607
12000
  init_prompts();
12001
+ init_memory();
12002
+ init_prompts();
11608
12003
  init_tools3();
11609
12004
  init_types();
11610
12005
  ORCHESTRATOR_MODEL = "grok-4.1-fast";
11611
12006
  MAX_ITERATIONS = 20;
12007
+ MAX_CONTEXT_CHARS = 1e5;
11612
12008
  }
11613
12009
  });
11614
12010
 
@@ -11694,11 +12090,12 @@ function estimateInputAreaHeight(input, isBusy, isRouting, contextChips, termina
11694
12090
  }
11695
12091
  return lines;
11696
12092
  }
11697
- function App({ initialPrompt, modelPreference, agentMode, useOrchestrator = true }) {
12093
+ function App({ initialPrompt, modelPreference, agentMode: initialAgentMode, useOrchestrator = true }) {
11698
12094
  const { exit } = useApp();
11699
12095
  const [messages, setMessages] = useState5([]);
11700
12096
  const [input, setInput] = useState5("");
11701
12097
  const [isBusy, setIsBusy] = useState5(false);
12098
+ const [agentMode, setAgentMode] = useState5(initialAgentMode);
11702
12099
  const [isRouting, setIsRouting] = useState5(false);
11703
12100
  const [currentModel, setCurrentModel] = useState5(null);
11704
12101
  const [sessionTokens, setSessionTokens] = useState5(0);
@@ -11833,8 +12230,8 @@ function App({ initialPrompt, modelPreference, agentMode, useOrchestrator = true
11833
12230
  " /help \u2014 this help",
11834
12231
  " /clear \u2014 clear chat",
11835
12232
  " /model \u2014 current model",
11836
- " /models \u2014 list all models",
11837
- " /agent \u2014 agent mode",
12233
+ " /auto \u2014 toggle auto mode (skip approvals)",
12234
+ " /yolo \u2014 toggle yolo mode (full autonomy)",
11838
12235
  " /usage \u2014 session stats",
11839
12236
  "",
11840
12237
  "Keyboard:",
@@ -11860,6 +12257,28 @@ function App({ initialPrompt, modelPreference, agentMode, useOrchestrator = true
11860
12257
  resetPhases();
11861
12258
  return;
11862
12259
  }
12260
+ if (trimmed === "/auto") {
12261
+ const newMode = agentMode === "auto" ? void 0 : "auto";
12262
+ setAgentMode(newMode);
12263
+ setMessages((prev) => [...prev, {
12264
+ id: nextId(),
12265
+ role: "assistant",
12266
+ content: newMode === "auto" ? "Auto mode ON \u2014 changes apply without asking." : "Auto mode OFF \u2014 you'll be asked before changes."
12267
+ }]);
12268
+ setInput("");
12269
+ return;
12270
+ }
12271
+ if (trimmed === "/yolo") {
12272
+ const newMode = agentMode === "yolo" ? void 0 : "yolo";
12273
+ setAgentMode(newMode);
12274
+ setMessages((prev) => [...prev, {
12275
+ id: nextId(),
12276
+ role: "assistant",
12277
+ content: newMode === "yolo" ? "YOLO mode ON \u2014 full autonomy, no approvals." : "YOLO mode OFF \u2014 back to normal approvals."
12278
+ }]);
12279
+ setInput("");
12280
+ return;
12281
+ }
11863
12282
  if (trimmed === "/model") {
11864
12283
  setScrollOffset(0);
11865
12284
  setMessages((prev) => [
@@ -11931,27 +12350,33 @@ function App({ initialPrompt, modelPreference, agentMode, useOrchestrator = true
11931
12350
  try {
11932
12351
  const { runOrchestrator: runOrchestrator2 } = await Promise.resolve().then(() => (init_loop2(), loop_exports));
11933
12352
  let responseText = "";
11934
- let currentToolLine = "";
12353
+ const steps = [];
11935
12354
  const result = await runOrchestrator2(trimmed, process.cwd(), {
11936
12355
  onLog: () => {
11937
12356
  },
11938
12357
  onText: (text) => {
11939
12358
  responseText += text;
11940
- currentToolLine = "";
11941
- streamRef.current = responseText;
11942
- setStreamingContent(responseText);
12359
+ const stepsBlock2 = steps.length > 0 ? steps.map((s) => ` \x1B[32m\u2713\x1B[0m ${s}`).join("\n") + "\n\n" : "";
12360
+ streamRef.current = stepsBlock2 + responseText;
12361
+ setStreamingContent(streamRef.current);
11943
12362
  },
11944
12363
  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;
12364
+ 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;
12365
+ const completedBlock = steps.map((s) => ` \x1B[32m\u2713\x1B[0m ${s}`).join("\n");
12366
+ const activeStep = ` \x1B[36m\u25CF\x1B[0m ${preview}`;
12367
+ streamRef.current = (completedBlock ? completedBlock + "\n" : "") + activeStep;
11948
12368
  setStreamingContent(streamRef.current);
11949
12369
  },
11950
- onApprovalNeeded: async (description) => {
12370
+ onToolResult: (name, result2) => {
12371
+ 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;
12372
+ steps.push(label);
12373
+ },
12374
+ onApprovalNeeded: agentMode === "auto" || agentMode === "yolo" ? void 0 : async (description) => {
11951
12375
  return new Promise((resolve12) => {
11952
- responseText += `
11953
- ${description}
11954
- `;
12376
+ const stepsBlock2 = steps.map((s) => ` \x1B[32m\u2713\x1B[0m ${s}`).join("\n");
12377
+ responseText = (stepsBlock2 ? stepsBlock2 + "\n\n" : "") + `${description}
12378
+
12379
+ \x1B[36mApply this change? [Y/n]\x1B[0m`;
11955
12380
  streamRef.current = responseText;
11956
12381
  setStreamingContent(responseText);
11957
12382
  setIsBusy(false);
@@ -11967,9 +12392,10 @@ ${description}
11967
12392
  }
11968
12393
  }, controller.signal, orchestratorMessagesRef.current);
11969
12394
  orchestratorMessagesRef.current = result.messages;
12395
+ const stepsBlock = steps.length > 0 ? steps.map((s) => ` \x1B[32m\u2713\x1B[0m ${s}`).join("\n") + "\n\n" : "";
11970
12396
  const costLine = `
11971
- Cost: $${result.totalCost.toFixed(4)} \xB7 ${(result.duration / 1e3).toFixed(1)}s \xB7 ${result.iterations} steps`;
11972
- responseText += costLine;
12397
+ Cost: $${result.totalCost.toFixed(4)} \xB7 ${(result.duration / 1e3).toFixed(1)}s`;
12398
+ responseText = stepsBlock + responseText + costLine;
11973
12399
  setMessages(
11974
12400
  (prev) => prev.map(
11975
12401
  (m) => m.id === assistantMsgIdRef.current ? { ...m, content: responseText, isStreaming: false } : m
@@ -12303,10 +12729,10 @@ ${diffDisplay}
12303
12729
  }
12304
12730
  async function loadContextChips() {
12305
12731
  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");
12732
+ const { readFileSync: readFileSync11 } = await import("fs");
12733
+ const { join: join21 } = await import("path");
12734
+ const indexPath = join21(process.cwd(), ".mint", "context.json");
12735
+ const raw = readFileSync11(indexPath, "utf-8");
12310
12736
  const index = JSON.parse(raw);
12311
12737
  const chips = [];
12312
12738
  if (index.language) chips.push({ label: index.language, color: "green" });
@@ -12385,34 +12811,42 @@ async function runOrchestratorCLI(task) {
12385
12811
  console.log(chalk9.dim(`
12386
12812
  Task: ${task}
12387
12813
  `));
12388
- const result = await runOrchestrator(task, cwd, {
12389
- onLog: (msg) => {
12390
- process.stdout.write(chalk9.dim(` ${msg}
12814
+ try {
12815
+ const result = await runOrchestrator(task, cwd, {
12816
+ onLog: (msg) => {
12817
+ process.stdout.write(chalk9.dim(` ${msg}
12391
12818
  `));
12392
- },
12393
- onText: (text) => {
12394
- process.stdout.write(text);
12395
- },
12396
- onToolCall: (name, input) => {
12397
- const preview = name === "write_code" ? `task: "${String(input.task ?? "").slice(0, 60)}..."` : name === "read_file" ? String(input.path ?? "") : name === "search_files" ? String(input.query ?? "") : name === "run_command" ? String(input.command ?? "").slice(0, 60) : name === "apply_diff" ? "(applying...)" : JSON.stringify(input).slice(0, 60);
12398
- console.log(chalk9.cyan(` > ${name}`) + chalk9.dim(` ${preview}`));
12399
- },
12400
- onToolResult: (name, result2) => {
12401
- if (name === "search_files" || name === "list_files") {
12402
- console.log(chalk9.dim(` ${result2.split("\n").length} results`));
12403
- } else if (name === "apply_diff") {
12404
- console.log(chalk9.green(` ${result2.slice(0, 100)}`));
12819
+ },
12820
+ onText: (text) => {
12821
+ process.stdout.write(text);
12822
+ },
12823
+ onToolCall: (name, input) => {
12824
+ const preview = name === "write_code" ? `task: "${String(input.task ?? "").slice(0, 60)}..."` : name === "read_file" ? String(input.path ?? "") : name === "search_files" ? String(input.query ?? "") : name === "run_command" ? String(input.command ?? "").slice(0, 60) : name === "apply_diff" ? "(applying...)" : JSON.stringify(input).slice(0, 60);
12825
+ console.log(chalk9.cyan(` > ${name}`) + chalk9.dim(` ${preview}`));
12826
+ },
12827
+ onToolResult: (name, result2) => {
12828
+ if (name === "search_files" || name === "list_files") {
12829
+ console.log(chalk9.dim(` ${result2.split("\n").length} results`));
12830
+ } else if (name === "apply_diff") {
12831
+ console.log(chalk9.green(` ${result2.slice(0, 100)}`));
12832
+ }
12405
12833
  }
12834
+ });
12835
+ const duration = (result.duration / 1e3).toFixed(1);
12836
+ const opusCost = result.totalCost * 50;
12837
+ console.log("");
12838
+ console.log(chalk9.dim(` ${result.iterations} steps \xB7 ${duration}s \xB7 $${result.totalCost.toFixed(4)} (orchestrator: $${result.orchestratorCost.toFixed(4)} + code: $${result.writeCodeCost.toFixed(4)})`));
12839
+ if (opusCost > result.totalCost * 2) {
12840
+ console.log(chalk9.dim(` Opus equivalent: $${opusCost.toFixed(2)} \u2014 saved ${Math.round((1 - result.totalCost / opusCost) * 100)}%`));
12841
+ }
12842
+ console.log("");
12843
+ } catch (err) {
12844
+ console.error(chalk9.red(`
12845
+ Error: ${err instanceof Error ? err.message : String(err)}`));
12846
+ if (err instanceof Error && err.stack) {
12847
+ console.error(chalk9.dim(err.stack.split("\n").slice(1, 5).join("\n")));
12406
12848
  }
12407
- });
12408
- const duration = (result.duration / 1e3).toFixed(1);
12409
- const opusCost = result.totalCost * 50;
12410
- console.log("");
12411
- console.log(chalk9.dim(` ${result.iterations} steps \xB7 ${duration}s \xB7 $${result.totalCost.toFixed(4)} (orchestrator: $${result.orchestratorCost.toFixed(4)} + code: $${result.writeCodeCost.toFixed(4)})`));
12412
- if (opusCost > result.totalCost * 2) {
12413
- console.log(chalk9.dim(` Opus equivalent: $${opusCost.toFixed(2)} \u2014 saved ${Math.round((1 - result.totalCost / opusCost) * 100)}%`));
12414
12849
  }
12415
- console.log("");
12416
12850
  }
12417
12851
  var init_orchestrator = __esm({
12418
12852
  "src/cli/commands/orchestrator.ts"() {
@@ -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,12 +13702,22 @@ 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
  }
13711
+ const { existsSync: existsSync12, readFileSync: readFs, writeFileSync: writeFs, mkdirSync: mkFs } = await import("fs");
13712
+ const { join: joinPath } = await import("path");
13713
+ const mintMdPath = joinPath(cwd, "MINT.md");
13714
+ if (!existsSync12(mintMdPath)) {
13715
+ const mintMd = await generateMintMd(cwd, index, topLangs, depCount);
13716
+ writeFs(mintMdPath, mintMd, "utf-8");
13717
+ console.log(chalk10.dim(` Generated MINT.md`));
13718
+ } else {
13719
+ console.log(chalk10.dim(` MINT.md already exists \u2014 skipped`));
13720
+ }
13277
13721
  console.log(chalk10.green(`
13278
13722
  Ready.`));
13279
13723
  console.log(chalk10.dim(` ${index.totalFiles} files \xB7 ${index.totalLOC.toLocaleString()} lines of code`));
@@ -13283,6 +13727,16 @@ program.command("init").description("Scan project and build search index").actio
13283
13727
  console.log(chalk10.dim(`
13284
13728
  Run ${chalk10.cyan("mint")} to start editing.
13285
13729
  `));
13730
+ try {
13731
+ const gatewayUrl = config.get("apiBaseUrl") ?? "https://api.usemint.dev";
13732
+ fetch(`${gatewayUrl}/track`, {
13733
+ method: "POST",
13734
+ headers: { "Content-Type": "application/json" },
13735
+ body: JSON.stringify({ event: "init", files_indexed: index.totalFiles, language: index.language })
13736
+ }).catch(() => {
13737
+ });
13738
+ } catch {
13739
+ }
13286
13740
  });
13287
13741
  program.command("skills").description("List all skills in .mint/skills/").action(async () => {
13288
13742
  const { loadSkills: loadSkills2 } = await Promise.resolve().then(() => (init_skills(), skills_exports));
@@ -13322,6 +13776,85 @@ function parseHumanInLoopEnv(raw) {
13322
13776
  if (["0", "false", "no", "off"].includes(normalized)) return false;
13323
13777
  return true;
13324
13778
  }
13779
+ async function generateMintMd(cwd, index, topLangs, depCount) {
13780
+ const fs = await import("fs");
13781
+ const path = await import("path");
13782
+ const lines = ["# Project Instructions for Mint CLI", ""];
13783
+ let framework = "";
13784
+ let buildCmd = "";
13785
+ let testCmd = "";
13786
+ let lintCmd = "";
13787
+ try {
13788
+ const pkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf-8"));
13789
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
13790
+ if (deps["next"]) {
13791
+ framework = "Next.js";
13792
+ buildCmd = "npm run build";
13793
+ } else if (deps["vite"]) {
13794
+ framework = "Vite";
13795
+ buildCmd = "npm run build";
13796
+ } else if (deps["react"]) {
13797
+ framework = "React";
13798
+ buildCmd = "npm run build";
13799
+ } else if (deps["vue"]) {
13800
+ framework = "Vue";
13801
+ buildCmd = "npm run build";
13802
+ } else if (deps["svelte"]) {
13803
+ framework = "Svelte";
13804
+ buildCmd = "npm run build";
13805
+ } else if (deps["express"] || deps["hono"] || deps["fastify"]) {
13806
+ framework = "Node.js server";
13807
+ buildCmd = "npm run build";
13808
+ }
13809
+ if (pkg.scripts?.build) buildCmd = "npm run build";
13810
+ if (pkg.scripts?.test && pkg.scripts.test !== 'echo "Error: no test specified" && exit 1') testCmd = "npm test";
13811
+ if (pkg.scripts?.lint) lintCmd = "npm run lint";
13812
+ if (deps["typescript"] || deps["tsup"] || deps["tsc"]) {
13813
+ if (!buildCmd) buildCmd = "npx tsc --noEmit";
13814
+ }
13815
+ lines.push(`## Project`);
13816
+ lines.push(`- **Name**: ${pkg.name ?? "unnamed"}`);
13817
+ if (framework) lines.push(`- **Framework**: ${framework}`);
13818
+ lines.push(`- **Language**: ${index.language}`);
13819
+ lines.push(`- **Files**: ${index.totalFiles} (${index.totalLOC.toLocaleString()} LOC)`);
13820
+ if (depCount > 0) lines.push(`- **Dependencies**: ${depCount}`);
13821
+ lines.push("");
13822
+ } catch {
13823
+ lines.push(`## Project`);
13824
+ lines.push(`- **Language**: ${index.language}`);
13825
+ lines.push(`- **Files**: ${index.totalFiles} (${index.totalLOC.toLocaleString()} LOC)`);
13826
+ lines.push("");
13827
+ }
13828
+ lines.push(`## Commands`);
13829
+ if (buildCmd) lines.push(`- **Build**: \`${buildCmd}\``);
13830
+ if (testCmd) lines.push(`- **Test**: \`${testCmd}\``);
13831
+ if (lintCmd) lines.push(`- **Lint**: \`${lintCmd}\``);
13832
+ if (!buildCmd && !testCmd && !lintCmd) lines.push("- No build/test/lint scripts detected");
13833
+ lines.push("");
13834
+ lines.push(`## Conventions`);
13835
+ lines.push(`- Match existing code style (indentation, naming, imports)`);
13836
+ if (index.language === "typescript") {
13837
+ lines.push(`- Use TypeScript types \u2014 no \`any\` unless necessary`);
13838
+ lines.push(`- Prefer \`const\` over \`let\``);
13839
+ }
13840
+ lines.push(`- Keep changes minimal and focused`);
13841
+ lines.push(`- Run build after changes to verify`);
13842
+ lines.push("");
13843
+ const dirs = /* @__PURE__ */ new Set();
13844
+ for (const filePath of Object.keys(index.files)) {
13845
+ const parts = filePath.split("/");
13846
+ if (parts.length > 1) dirs.add(parts[0]);
13847
+ }
13848
+ if (dirs.size > 0) {
13849
+ lines.push(`## Key Directories`);
13850
+ for (const dir of [...dirs].sort().slice(0, 10)) {
13851
+ const count = Object.keys(index.files).filter((f) => f.startsWith(dir + "/")).length;
13852
+ lines.push(`- \`${dir}/\` (${count} files)`);
13853
+ }
13854
+ lines.push("");
13855
+ }
13856
+ return lines.join("\n");
13857
+ }
13325
13858
  async function runOneShotPipeline(task, options) {
13326
13859
  const { runPipeline: runPipeline2, formatDiffs: formatDiffs2, formatCostSummary: formatCostSummary2 } = await Promise.resolve().then(() => (init_pipeline(), pipeline_exports));
13327
13860
  const { createUsageTracker: createUsageTracker2 } = await Promise.resolve().then(() => (init_tracker(), tracker_exports));
@@ -13491,13 +14024,13 @@ function applyDiffs(diffs, cwd) {
13491
14024
  }
13492
14025
  try {
13493
14026
  if (diff.oldContent === "") {
13494
- mkdirSync5(dirname5(fullPath), { recursive: true });
14027
+ mkdirSync6(dirname6(fullPath), { recursive: true });
13495
14028
  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");
14029
+ writeFileSync7(fullPath, newContent + "\n", "utf-8");
13497
14030
  console.log(chalk10.green(` + Created ${diff.filePath}`));
13498
14031
  continue;
13499
14032
  }
13500
- const current = readFileSync9(fullPath, "utf-8");
14033
+ const current = readFileSync10(fullPath, "utf-8");
13501
14034
  let updated = current;
13502
14035
  for (const hunk of diff.hunks) {
13503
14036
  const removeLines = hunk.lines.filter((l) => l.type === "remove").map((l) => l.content);
@@ -13527,7 +14060,7 @@ function applyDiffs(diffs, cwd) {
13527
14060
  }
13528
14061
  }
13529
14062
  if (updated !== current) {
13530
- writeFileSync6(fullPath, updated, "utf-8");
14063
+ writeFileSync7(fullPath, updated, "utf-8");
13531
14064
  console.log(chalk10.green(` ~ Modified ${diff.filePath}`));
13532
14065
  } else {
13533
14066
  console.log(chalk10.yellow(` ? Could not apply diff to ${diff.filePath} (text not found)`));