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/README.md +66 -54
- package/dist/cli/index.js +710 -177
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
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,
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 ??
|
|
1310
|
+
return process.env.MINT_GATEWAY_URL ?? config2.getGatewayUrl();
|
|
1311
1311
|
}
|
|
1312
1312
|
function getToken() {
|
|
1313
|
-
const gatewayToken =
|
|
1313
|
+
const gatewayToken = config2.get("gatewayToken");
|
|
1314
1314
|
if (gatewayToken) return gatewayToken;
|
|
1315
|
-
const userToken =
|
|
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 =
|
|
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
|
|
4338
|
-
{ name: "
|
|
4339
|
-
{ name: "
|
|
4340
|
-
{ name: "usage", description: "session stats +
|
|
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 "
|
|
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, {
|
|
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:
|
|
4591
|
-
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "
|
|
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:
|
|
4595
|
-
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "
|
|
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:
|
|
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: "
|
|
4600
|
+
/* @__PURE__ */ jsx8(Text8, { dimColor: true, bold: true, children: "COMMANDS" }),
|
|
4605
4601
|
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
4606
|
-
/* @__PURE__ */ jsx8(Text8, { color: "cyan", children: "
|
|
4607
|
-
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2014
|
|
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: "/
|
|
4611
|
-
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2014
|
|
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: "/
|
|
4615
|
-
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2014
|
|
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: "
|
|
4634
|
-
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2192
|
|
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: "
|
|
4642
|
-
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2192
|
|
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:
|
|
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(
|
|
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(
|
|
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(
|
|
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,
|
|
11002
|
-
|
|
11003
|
-
|
|
11004
|
-
|
|
11005
|
-
|
|
11006
|
-
-
|
|
11007
|
-
-
|
|
11008
|
-
-
|
|
11009
|
-
-
|
|
11010
|
-
|
|
11011
|
-
|
|
11012
|
-
|
|
11013
|
-
|
|
11014
|
-
|
|
11015
|
-
|
|
11016
|
-
|
|
11017
|
-
|
|
11018
|
-
|
|
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
|
-
|
|
11021
|
-
-
|
|
11022
|
-
-
|
|
11023
|
-
-
|
|
11024
|
-
|
|
11025
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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] =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
11563
|
+
const fullPath = join19(ctx.cwd, filePath);
|
|
11318
11564
|
if (!fullPath.startsWith(ctx.cwd)) return "Error: path outside project directory";
|
|
11319
11565
|
try {
|
|
11320
|
-
|
|
11321
|
-
|
|
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
|
-
...
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
11566
|
-
|
|
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
|
-
" /
|
|
11837
|
-
" /
|
|
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
|
-
|
|
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
|
-
|
|
11941
|
-
streamRef.current = responseText;
|
|
11942
|
-
setStreamingContent(
|
|
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" ?
|
|
11946
|
-
|
|
11947
|
-
|
|
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
|
-
|
|
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
|
-
|
|
11953
|
-
|
|
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
|
|
11972
|
-
responseText
|
|
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:
|
|
12307
|
-
const { join:
|
|
12308
|
-
const indexPath =
|
|
12309
|
-
const raw =
|
|
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
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12814
|
+
try {
|
|
12815
|
+
const result = await runOrchestrator(task, cwd, {
|
|
12816
|
+
onLog: (msg) => {
|
|
12817
|
+
process.stdout.write(chalk9.dim(` ${msg}
|
|
12391
12818
|
`));
|
|
12392
|
-
|
|
12393
|
-
|
|
12394
|
-
|
|
12395
|
-
|
|
12396
|
-
|
|
12397
|
-
|
|
12398
|
-
|
|
12399
|
-
|
|
12400
|
-
|
|
12401
|
-
|
|
12402
|
-
|
|
12403
|
-
|
|
12404
|
-
|
|
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
|
|
12501
|
-
import { dirname as
|
|
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 (
|
|
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 =
|
|
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
|
-
|
|
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 (
|
|
12597
|
-
const email2 =
|
|
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 =
|
|
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
|
-
|
|
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 (!
|
|
13083
|
+
if (!config2.isAuthenticated()) {
|
|
12650
13084
|
console.log(chalk.yellow("Not currently logged in"));
|
|
12651
13085
|
return;
|
|
12652
13086
|
}
|
|
12653
|
-
const email =
|
|
12654
|
-
|
|
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 (!
|
|
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 =
|
|
12664
|
-
const configPath =
|
|
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 =
|
|
12681
|
-
const configPath =
|
|
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 =
|
|
12724
|
-
|
|
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
|
-
|
|
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
|
-
|
|
13171
|
+
config2.set(key, Number(value));
|
|
12738
13172
|
console.log(chalk2.green(`\u2713 Set ${key} = ${value}`));
|
|
12739
13173
|
return;
|
|
12740
13174
|
}
|
|
12741
|
-
|
|
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 (!
|
|
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 =
|
|
13014
|
-
const 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:
|
|
13272
|
-
const { join:
|
|
13273
|
-
const pkg = JSON.parse(
|
|
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
|
-
|
|
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
|
-
|
|
14029
|
+
writeFileSync7(fullPath, newContent + "\n", "utf-8");
|
|
13497
14030
|
console.log(chalk10.green(` + Created ${diff.filePath}`));
|
|
13498
14031
|
continue;
|
|
13499
14032
|
}
|
|
13500
|
-
const current =
|
|
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
|
-
|
|
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)`));
|