flintmaker 0.2.6 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,38 @@
1
+ # Changelog
2
+
3
+ ## 0.5.0 — 2026-10-25
4
+
5
+ - Owned background jobs: `start_background`, `read_job`, and `stop_job` run long-lived dev servers and watchers. Inspect or stop jobs with `/jobs` and `/jobs stop <id>`. Capped at 4 concurrent running jobs with the last 16 KB of output retained per job. Subject to command approvals and hard refusals; plan mode refuses job actions. Entire process trees are cleaned up on stop, interrupt, session exit, or session reset.
6
+ - Stdio MCP client: connect local Model Context Protocol servers configured in `.flint/mcp.json` or `~/.flintmaker/mcp.json`, with status and reconnect via `/mcp`. Exposes dynamic tools as `mcp__<server>__<tool>`. Capped at 4 servers, 32 dynamic tools, and 64 KB total schema. Server launches require interactive approval cached by command hash; hard command refusals cannot be launched. Tool calls validate arguments against command policy, secret files, project boundaries, and Read/Edit rules; commands and code execution arguments always require approval. Process output is bounded (256 KB message buffer, 8 MB total stderr/stdout, 10 s timeout) and schema context is reserved in auto-compaction budgets.
7
+ - Multimodal image input: attach PNG and JPEG images via `@path` or interactive terminal paste (rendered as `[Image: mime WxH]` instead of raw base64). Requires authenticated capability discovery (`GET /api/agent/capabilities`) and model support. Capped at 4 images per request and 2 MB per image, with max dimensions of 8,192×8,192 (16M pixels). Complete local validation of PNG chunks/CRC and JPEG markers/frames before dispatch; secret file protections and Read permissions apply to image paths. Conversation history retains the 4 most recent images, omitting older attachments with a placeholder.
8
+ - Depth-one read-only subagents: delegate targeted investigation tasks to child agents via `delegate`. Children run with separate context, an isolated read-only tool set (`read_file`, `list_files`, `search`, `repo_map`), and strict depth-one limits (cannot edit, run bash, use MCP, search the web, or delegate). Capped at 2 sequential delegations per parent turn, with at most 3 steps and a 30-second timeout per child. Child model requests count toward `--max-turns`. Children return bounded findings summaries (up to 4,000 characters) and discard intermediate history on completion.
9
+ - No new dependencies.
10
+
11
+ ## 0.4.0 — 2026-10-25
12
+
13
+ - Project and user permission rules in `.flint/settings.json` and `~/.flintmaker/settings.json`: allow/ask/deny for Bash, Read, Edit and local Tool requests. Deny wins across scopes. Hard refusals cannot be overridden; risky commands still ask. Rules cover implicit memory reads, attachments and indexing too.
14
+ - Visible task checklists, updated by `update_todos`. Includes an isolated, opt-in API schema and prompt change; needs that server update to be made available.
15
+ - Explicit headless mode: `-p` / `--print`, text/JSON/NDJSON output, strict 0/1 exit codes and `--max-turns`. Never prompts, opens login or auto-updates. Refused tools and exhausted limits fail the run.
16
+ - Markdown slash commands from project and user command directories, `$ARGUMENTS`, project overrides, help and completion.
17
+ - File and conversation checkpoints before patches, saved with sessions. `/rewind` restores creates/deletes/renames and original bytes/modes, refusing conflicts with later edits. Formatter changes to patched files are included. Privacy mode keeps snapshots in memory.
18
+ - Approved PreToolUse/PostToolUse/Stop shell hooks with JSON stdin, bounded output, timeouts and exit-2 blocking feedback. Plan mode skips hooks. Web tools are suppressed when local rules/hooks cannot be enforced around server execution.
19
+ - Automatic context summarization near the API budget, retaining the current request and complete tool-call groups. Summary tool requests are never executed; failed summaries preserve history. Headless turn budgets include summaries and retries.
20
+ - Revalidate planned writes before applying; reject stale plans and secret/outside-project symlink aliases. Agent file tools cannot rewrite `.flint/` configuration.
21
+ - No new dependencies. MCP, background commands and image input are deferred; API requirements and remaining work are recorded in the repo's `docs/cli-0.4-SERVER.md` and `docs/cli-0.4-PLAN.md`.
22
+
23
+ ## 0.3.0 — 2026-10-22
24
+
25
+ flint now works in your real repository.
26
+
27
+ - **Codebase map** (`repo_map`): files that git does not ignore, with their top-level functions, classes and exports, and the detected test/build commands (npm/pnpm/yarn/bun scripts, Make, Cargo, Go, Python, Gradle, Maven, Swift, Flutter; also one level down in monorepos). Capped at 300 files by default (max 1,000) and 48 KB.
28
+ - **Multi-file patches** (`apply_patch`): one unified diff for a change across files, including new, deleted and moved files. Reviewed file by file (yes / yes to the rest / skip / feedback); `--yes` and auto-accept mode apply it. Hunks are found by their content, so slightly wrong line numbers still apply; if any part does not fit, nothing changes and flint is told why.
29
+ - **git** tool: read-only commands run at once, changing ones ask, `push` only with `--allow-push` (still asks, never forced); `reset --hard`, `rebase`, `clean`, forced options, `git config` and option injection (`-c`, `--output`, `--ext-diff`…) are refused. Diffs of secret files are hidden.
30
+ - **Commands**: a deny list that applies even with `--yes` (sudo, deleting `/` or home, download-and-run, publishing, environment dumps, credential folders, secret files, git push/reset --hard), and an allow list of test/build/lint/read-only commands that run without asking in auto-accept mode. `--timeout <seconds>` (default 120).
31
+ - **Sessions**: conversations are saved locally after every turn (atomic writes, also on Ctrl+C); `flint -c`, `flint --resume [id]` and `/resume` continue them. Cut-off tool calls are repaired on load.
32
+ - **Privacy mode**: `--privacy` or `FLINT_PRIVACY=1` saves no history or sessions on this computer and turns web tools off (it also sends `x-flint-privacy: 1` for the server).
33
+ - Secret files are skipped everywhere (listing, search, map, patches, git, commands): `.env*` (not `.env.example`), keys, `*.pem`, certificates, credentials, service accounts. Without git, `.gitignore` files (nested too) are honoured.
34
+ - Needs the FlintMaker API with the repo tools; against an older API flint keeps working with the 0.2 tools.
35
+
36
+ ## 0.2.6
37
+
38
+ - Last 0.2 release: interactive session, one-shot mode, web search/fetch, auto-update, Windows support.
package/README.md CHANGED
@@ -1,18 +1,20 @@
1
1
  # flint — FlintMaker for your terminal
2
2
 
3
- `flint` is an AI coding agent from [FlintMaker](https://flintmaker.com). It reads your project, edits files and runs commands — always showing you the diff or command first.
3
+ `flint` is an AI coding agent from [FlintMaker](https://flintmaker.com). It works in your real repository: it indexes the codebase, changes several files in one reviewable patch, runs your tests and uses git — always showing you the diff or command first.
4
4
 
5
5
  ```sh
6
6
  npm install -g flintmaker
7
7
  flint login # connect your FlintMaker account (opens the browser)
8
8
  cd my-project
9
9
  flint # interactive session
10
- flint "add a dark mode toggle" # one task, then exit (plain output, fine for scripts)
10
+ flint "add a dark mode toggle" # one task, then exit (asks before changes in a terminal)
11
+ flint -p "check the project" --output-format json # scripts and CI; never prompts
12
+ flint -c "now add a test for it" # continue the last conversation in this folder
11
13
  ```
12
14
 
13
15
  ```text
14
16
  ╭────────────────────────────────────────────────────────────╮
15
- │ ✦ FlintMaker flint v0.2.0 │
17
+ │ ✦ FlintMaker flint v0.5.0 │
16
18
  │ ▟█▙ you@example.com · Blaze · 1,200 credits left │
17
19
  │ ▟███▙ model Standard · ~/code/my-app │
18
20
  ╰────────────────────────────────────────────────────────────╯
@@ -38,15 +40,153 @@ flint "add a dark mode toggle" # one task, then exit (plain output, fine for
38
40
  - `@path` attaches a file (tab completes paths) · `↑/↓` history (kept in `~/.flintmaker/history`)
39
41
  - `esc` interrupts the current step · `ctrl+c` twice exits
40
42
  - `shift+tab` switches mode: **ask before edits** → **auto-accept edits** → **plan only (no changes)**
41
- - `/help` `/model` `/mode` `/clear` `/compact` `/credits` `/init` `/login` `/logout` `/exit`
43
+ - `/help` `/model` `/mode` `/clear` `/resume` `/rewind` `/jobs` `/mcp` `/compact` `/credits` `/init` `/login` `/logout` `/exit`
42
44
  - `/init` writes `FLINT.md`, a short project memory flint reads at the start of every conversation
43
45
  - flint can search the web and read docs pages (current versions, error messages) and cites the URLs. These run on FlintMaker's servers, never touch your machine, and cost 2 credits per search and 1 per page (at most 5 searches and 8 pages per message). Page text is treated as information, never as instructions.
44
46
 
47
+ **Working in a real repo**
48
+
49
+ - **Codebase map**: flint starts a task by indexing the repo (files that git does not ignore, their top-level functions and classes, and the test/build commands it finds in `package.json`, `Makefile`, `Cargo.toml`, `go.mod`, Gradle and more), then reads only what it needs.
50
+ - **Multi-file patches**: a change across files arrives as one patch. You review it file by file: yes, yes to the rest of the patch, skip this file, or tell flint what to do instead. flint is told exactly which files were applied. If any part of a patch does not fit the current files, nothing is changed.
51
+ - **Tests**: flint runs the project's test command after changing code and fixes what fails. Commands time out after 120 s (`--timeout <seconds>`); long output is cut in the middle.
52
+ - **git**: `status`, `diff`, `log`, `show` and `branch` run at once; `add`, `commit`, `switch`, `checkout -b` and `stash` ask first. flint never pushes unless you start it with `--allow-push` (and then still asks, and never force-pushes). `reset --hard`, `rebase`, `clean`, forced options and `git config` are refused.
53
+ - **Tasks**: for multi-step work, flint shows a checklist and updates it as steps start and finish. Needs the API checklist capability described below.
54
+ - **Context**: long conversations are automatically summarized before the context budget is reached. The current request is kept intact, tool-call groups stay together, and the terminal reports compaction. Summaries use credits. `/compact` is still available.
55
+ - **Sessions**: every conversation is saved on this computer after each turn (also when you press Ctrl+C), in `~/.flintmaker/sessions`. `flint -c` continues the last one in this folder, `flint --resume` or `/resume` lets you pick one.
56
+
57
+ **Permission rules**
58
+
59
+ Put settings in `.flint/settings.json` for this project or `~/.flintmaker/settings.json` for all projects. Both files are loaded at startup. Restart flint after changing settings. Invalid settings stop startup.
60
+
61
+ ```json
62
+ {
63
+ "allow": ["Bash(npm run lint)", "Edit(src/*)"],
64
+ "ask": ["Edit(src/migrations/*)"],
65
+ "deny": ["Bash(npm run release)", "Read(private/*)", "Edit(vendor/*)"]
66
+ }
67
+ ```
68
+
69
+ Rules match the complete request; `*` matches any characters, including `/`. Paths are relative to the project root, with `/` separators. Deny wins across both files, then ask, then allow. `Tool(read_file)` or `Tool(git)` matches a whole local tool.
70
+
71
+ Read/Edit rules apply to file tools, attachments, project memory and indexing. Denied reads are omitted from search and the repo map. Reads requiring approval are omitted from automatic attachments and indexing; request `read_file` explicitly to approve one. Bash rules apply to commands. An allow rule suppresses approval for commands on the built-in test/build/lint/read-only list. Other commands still ask, including chained commands and changing git operations. Ask rules override auto-accept and session approvals; `--yes` explicitly approves asks. Deny and the hard refusals always remain in force. Agent file tools cannot edit `.flint/` configuration, including through a symlink.
72
+
73
+ Server-run web tools are omitted when a Tool rule requires a refusal or approval for either web tool, or a matching local pre/post hook would need to run around them. Both web tools are disabled together because the current API runs them before returning to the CLI.
74
+
75
+ **Scripts and CI**
76
+
77
+ ```sh
78
+ flint -p "explain the failing test" --output-format text
79
+ flint -p "fix the failing test" --yes --max-turns 12 --output-format json
80
+ flint -p "check this patch" --plan --output-format stream-json
81
+ ```
82
+
83
+ `-p` / `--print` never prompts, including when connected to a terminal. Anything needing approval is refused unless allowed by settings or approved with `--yes`. It does not open login or update prompts. Sign in beforehand with `flint login`.
84
+
85
+ Exit code is 0 when the run finishes without tool failures, and 1 for a refused or failed tool, failed request, interrupt, or exhausted turn limit. A later answer does not erase a tool failure. `--max-turns` counts model requests, including automatic summaries and retries (default 25, maximum 1,000).
86
+
87
+ - `text`: model text on stdout; checklist, hook, compaction, child and MCP notices on stderr.
88
+ - `json`: one final object with `type: "result"`, `success`, `result`, `turns`, `credits`, `duration_ms`, and `error` when available.
89
+ - `stream-json`: one JSON object per line: `delta`, `tool_start`, `tool_result`, `hook_start`, `hook_result`, `compact`, `child`, `mcp_status`, ending with the same `result` object. No terminal decoration.
90
+
91
+ Startup errors (invalid flags/settings or missing login) go to stderr and exit 1. Saved sessions work as usual; use `--privacy` to keep the run in memory.
92
+
93
+ **Custom commands**
94
+
95
+ Create `.flint/commands/review.md` or `~/.flintmaker/commands/review.md`:
96
+
97
+ ```md
98
+ Review the changes for correctness and missing tests. Focus on $ARGUMENTS.
99
+ ```
100
+
101
+ Run `/review date parsing`. `$ARGUMENTS` is replaced literally with the supplied text. Markdown becomes a prompt; loading it does not execute a shell command. Project commands override user commands of the same name. Built-ins and aliases are reserved. Commands appear in `/help` and slash completion and also work with `flint -p "/review date parsing"`. Oversized files and symlink files are skipped; project read rules apply. Restart an interactive session to discover newly added commands.
102
+
103
+ **Rewind**
104
+
105
+ `/rewind` lists snapshots from before applied patches; `/rewind 2` selects one directly. After confirmation, flint restores that patch and every later patch, including created, deleted and renamed files, and returns the conversation to that point. Later manual edits cause a refusal before any file is restored. Interrupted tool calls in the restored conversation are closed off consistently.
106
+
107
+ Snapshots keep original bytes and permissions. Up to 20 checkpoints and about 12 MB are kept per conversation, with a 2 MB file limit; patches too large to snapshot are refused. They are saved with sessions and kept only in memory in privacy mode. Formatter changes to files in the patch are included. Shell/git effects and hook changes to other files are not restored.
108
+
109
+ **Hooks**
110
+
111
+ Settings can include `PreToolUse`, `PostToolUse` and `Stop` shell hooks:
112
+
113
+ ```json
114
+ {
115
+ "hooks": {
116
+ "PostToolUse": [
117
+ { "matcher": "write_file|edit_file|apply_patch", "command": "npm run format", "timeout": 20 }
118
+ ]
119
+ }
120
+ }
121
+ ```
122
+
123
+ A matcher is an exact tool name, `*`, or names separated by `|`; omitting it matches every local tool. Stop hooks normally omit the matcher. User hooks run before project hooks. Each command uses the usual command approval and hard refusals; untrusted project hooks do not run silently. In headless mode, commands requiring approval need `--yes`. Plan mode skips hooks.
124
+
125
+ Hooks receive JSON on stdin with `hook_event_name`, `tool_name`, `tool_input`, `tool_response` (for post hooks), and `cwd`. Values are never substituted into shell code. Output is bounded; timeout defaults to 10 seconds (maximum 60). Exit 2 blocks a pre-tool call and returns its output, including stderr, to the model. A post-tool rejection reports that effects already occurred. Stop rejection asks the model to continue, within the turn limit. Other nonzero exits are shown but do not block. Timeout or interrupted hooks block.
126
+
127
+ **Background jobs**
128
+
129
+ flint can run long-running dev servers and watchers in the background using `start_background`, read recent output with `read_job`, and stop them with `stop_job`. Use `/jobs` to inspect running jobs or `/jobs stop <id>` to stop one.
130
+
131
+ At most 4 background jobs can run concurrently, and flint retains the last 16 KB of output for each job. Background commands obey the usual command approval rules and hard refusals (`sudo`, package publishing, etc. remain blocked); plan mode refuses starting or stopping jobs. Background jobs are owned by the current session and terminate immediately on interrupt (Ctrl+C), exit, `/clear`, or `/resume`. Nothing survives session exit.
132
+
133
+ **MCP (Model Context Protocol)**
134
+
135
+ flint connects to local MCP servers over stdio. Configure servers in `.flint/mcp.json` for the current project or `~/.flintmaker/mcp.json` across all projects:
136
+
137
+ ```json
138
+ {
139
+ "mcpServers": {
140
+ "memory": {
141
+ "command": "node",
142
+ "args": ["server.js"]
143
+ }
144
+ }
145
+ }
146
+ ```
147
+
148
+ Use `/mcp` to inspect connection status and registered tools, or `/mcp retry` to reconnect. Tools are exposed to the model as `mcp__<server>__<tool>`. flint supports up to 4 servers, 32 dynamic tools, and 64 KB total tool schema.
149
+
150
+ Launching an MCP server runs with your account access and requires interactive approval (remembered by command hash in `~/.flintmaker/mcp-trust.json`); hard command refusals cannot be launched. Plan mode skips launching MCP servers. Individual tool calls follow `Tool(...)` settings rules and prompt for approval unless allowed. Tool calls with commands or executable arguments (`cmd`, `script`, `code`, `expression`) always ask. File paths and arguments are validated: secret files, `file:` URIs, and outside paths are blocked, and project files respect `Read`/`Edit` rules. Server stdout/stderr are bounded (256 KB message buffer, 8 MB total output), requests time out after 10 s (max 30 s), and processes are killed when flint exits.
151
+
152
+ **Images**
153
+
154
+ You can attach PNG and JPEG screenshots or design mockups by referencing `@path/to/image.png` in your prompt or pasting image data into an interactive session (which renders `[Image: mime WxH]` instead of printing base64).
155
+
156
+ Image input is capability-gated: flint verifies multimodal support with the API before sending images, and gracefully refuses unsupported models or servers. Attachments are capped at 2 MB per file, at most 4 images per turn, and a maximum resolution of 8,192×8,192 pixels (16 million pixels total). Flint validates PNG chunk structure/CRC and JPEG markers locally before making network requests or using credits. Image paths must obey project boundaries, secret file rules, and `Read` permissions. In longer sessions, conversation history automatically retains the 4 most recent images and replaces earlier ones with a notice to conserve the context budget.
157
+
158
+ **Subagents**
159
+
160
+ flint can delegate targeted, read-only exploration and code location tasks to a child agent using `delegate`. Child agents investigate the project with their own clean context window and return a concise findings summary (up to 4,000 characters) to the parent agent.
161
+
162
+ Delegation is strictly depth one: a child agent cannot delegate to further subagents, modify files, run bash commands, manage background jobs, connect to MCP servers, or search the web. Children only have access to read-only tools (`read_file`, `list_files`, `search`, `repo_map`) and obey project read rules. flint allows at most one child to run at a time and at most 2 sequential delegations per turn. Each child is bounded to 3 steps and a 30-second timeout. In headless mode, child model requests count against the `--max-turns` limit. Child session history is discarded after returning findings to keep the main conversation focused.
163
+
164
+ **API support and next steps**
165
+
166
+ The checklist, background jobs, MCP tools, image input, and child subagents require the additive API capabilities described in [the server notes](../docs/cli-0.5-SERVER.md). Older APIs keep working with their supported tools. The server changes have not been deployed. The VS Code extension remains a next step.
167
+
168
+ **Options**
169
+
170
+ | Option | What it does |
171
+ | --- | --- |
172
+ | `-p`, `--print` | headless run; never prompts |
173
+ | `--output-format <format>` | `text`, `json` or `stream-json` (requires `-p`) |
174
+ | `--max-turns <n>` | maximum model requests in a headless run (default 25) |
175
+ | `--yes`, `-y` | apply every change and patch and run every command without asking, except the refused ones below (careful) |
176
+ | `--plan` | plan only: read the project, change nothing |
177
+ | `--smart` | the Smart model (paid plans) |
178
+ | `-c`, `--continue` | continue the last conversation in this folder |
179
+ | `--resume [id]` | pick a saved conversation, or continue the one with this id |
180
+ | `--privacy` | privacy mode (also `FLINT_PRIVACY=1`): no prompt history or sessions saved on this computer, and web tools off |
181
+ | `--allow-push` | allow `git push` after you approve it (never forced) |
182
+ | `--timeout <s>` | time limit per command (default 120) |
183
+
45
184
  **Safety**
46
185
 
47
- - Every file change is shown as a full diff and needs your approval (1 = yes, 2 = yes for the rest of the session, 3 = no and say what to do instead).
48
- - Commands run only after you approve them; chained or risky commands are always asked about.
49
- - Secret files such as `.env` and private keys are never read, and nothing outside the project folder is touched.
186
+ - In interactive and legacy one-shot sessions, file changes are shown as a full diff. Unless allowed by settings or the selected mode, they need your approval (1 = yes, 2 = yes for the rest of the session, 3 = no and say what to do instead).
187
+ - Commands run only after you approve them; chained or risky commands are always asked about. In auto-accept mode, test, build, lint and read-only commands (`npm test`, `pytest`, `go test`, `cargo build`, `git status`…) run without asking; everything else still asks.
188
+ - Never run, whatever the mode: `sudo`, deleting `/` or your home folder, piping a download into a shell, publishing packages (`npm publish`…), printing all environment variables, touching `~/.ssh` and other credential folders, and `git push` without `--allow-push`.
189
+ - Secret files (`.env*` except `.env.example`, private keys, `*.pem`, certificates, credentials, service-account JSON…) are never read, indexed, listed, patched, staged or named in a command, and their diffs are hidden from git output. Dependency and build folders (`node_modules`, `dist`, `build`, `target`, `.venv`…) are never indexed. Nothing outside the project folder is touched.
50
190
 
51
191
  **Windows**
52
192
 
@@ -54,7 +194,7 @@ flint "add a dark mode toggle" # one task, then exit (plain output, fine for
54
194
  - Commands run in `cmd.exe` by default; set `FLINT_SHELL=powershell` (or `pwsh`) to use PowerShell. flint tells the model which OS and shell you use, so it suggests commands that work there.
55
195
  - `flint` is not recognized? Add `%APPDATA%\npm` to your PATH (Settings → Environment variables), then open a new terminal.
56
196
  - PowerShell says running scripts is disabled? Run `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned` once.
57
- - Files keep their line endings (CRLF stays CRLF). In the old console window, symbols fall back to plain ASCII (`FLINT_ASCII=1` / `FLINT_UNICODE=1` to force).
197
+ - Files keep their line endings (CRLF stays CRLF), also when a patch is applied. git runs directly (no shell), so the git tool works the same in cmd, PowerShell and Git Bash. In the old console window, symbols fall back to plain ASCII (`FLINT_ASCII=1` / `FLINT_UNICODE=1` to force).
58
198
 
59
199
  **Updates**
60
200
 
@@ -0,0 +1,2 @@
1
+ var E=Array.from({length:256},(e,t)=>{for(let n=0;n<8;n++)t=t&1?3988292384^t>>>1:t>>>1;return t>>>0}),d=e=>{let t=4294967295;for(let n of e)t=E[(t^n)&255]^t>>>8;return(t^4294967295)>>>0};function c(e){if(!e.length||e.length>2e6)throw new Error("Image exceeds the 2 MB limit");let t=0,n=0,g;if(e.subarray(0,8).equals(Buffer.from([137,80,78,71,13,10,26,10]))){g="image/png";let r=8,o=!1,s=!1,i=!1,f=0;for(;r+12<=e.length&&++f<=4096;){let a=e.readUInt32BE(r),h=e.toString("ascii",r+4,r+8);if(a>2e6||r+12+a>e.length||d(e.subarray(r+4,r+8+a))!==e.readUInt32BE(r+8+a))throw new Error("Invalid PNG chunk");if(!o&&h!=="IHDR")throw new Error("PNG must start with IHDR");if(h==="IHDR"){if(o||a!==13)throw new Error("Invalid PNG header");t=e.readUInt32BE(r+8),n=e.readUInt32BE(r+12);let l=e[r+16],m=e[r+17];if(!{0:[1,2,4,8,16],2:[8,16],3:[1,2,4,8],4:[8,16],6:[8,16]}[m]?.includes(l)||e[r+18]!==0||e[r+19]!==0||e[r+20]>1)throw new Error("Unsupported PNG header");o=!0}if(h==="IDAT"&&a&&(s=!0),r+=a+12,h==="IEND"){if(a!==0||r!==e.length)throw new Error("Invalid PNG end");i=!0;break}}if(!o||!s||!i)throw new Error("Incomplete PNG")}else if(e[0]===255&&e[1]===216){g="image/jpeg";let r=2,o=!1,s=!1;for(;r<e.length;){if(e[r++]!==255)throw new Error("Invalid JPEG marker");for(;e[r]===255;)r++;let i=e[r++];if(i===217){s=r===e.length;break}if(!i||i===216||r+2>e.length)throw new Error("Invalid JPEG marker");let f=e.readUInt16BE(r);if(f<2||r+f>e.length)throw new Error("Invalid JPEG segment");if([192,193,194].includes(i)){if(f<8||e[r+2]!==8||t)throw new Error("Unsupported JPEG frame");if(n=e.readUInt16BE(r+3),t=e.readUInt16BE(r+5),![1,3,4].includes(e[r+7])||f!==8+3*e[r+7])throw new Error("Invalid JPEG frame")}if(r+=f,i===218){if(!t)throw new Error("JPEG scan has no frame");let a=r;for(;r<e.length&&!(e[r]===255&&e[r+1]!==0&&!(e[r+1]>=208&&e[r+1]<=215));)r+=e[r]===255?2:1;o||=r>a}}if(!t||!o||!s)throw new Error("Incomplete JPEG")}else throw new Error("Expected a PNG or JPEG image signature");if(!t||!n||t>8192||n>8192||t*n>16e6)throw new Error("Image dimensions exceed 8192 per side or 16 million pixels");return{mime:g,width:t,height:n}}function I(e){if(typeof e!="string"||e.length>Math.ceil(2e6/3)*4+40)throw new Error("Image data exceeds limit");let t=/^data:(image\/(?:png|jpeg));base64,([A-Za-z0-9+/]+={0,2})$/.exec(e);if(!t)throw new Error("Images must be PNG/JPEG base64 data URLs");let n=Buffer.from(t[2],"base64");if(n.toString("base64")!==t[2]||c(n).mime!==t[1])throw new Error("Image MIME/signature mismatch or invalid base64");return e}function u(e){let t=4;return[...e].reverse().map(n=>{if(n.role!=="user"||!n.images?.length)return n;if(n.images.length>4)throw new Error("At most 4 images per message");if(n.images.length>t){let{images:g,...r}=n;return{...r,content:r.content+`
2
+ [Earlier images omitted; attach them again if needed.]`}}return t-=n.images.length,n}).reverse()}export{c as a,I as b,u as c};
@@ -0,0 +1 @@
1
+ var g=Object.create;var f=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var i=Object.getOwnPropertyNames;var j=Object.getPrototypeOf,k=Object.prototype.hasOwnProperty;var m=(b,a)=>()=>{try{return a||b((a={exports:{}}).exports,a),a.exports}catch(c){throw a=0,c}};var l=(b,a,c,e)=>{if(a&&typeof a=="object"||typeof a=="function")for(let d of i(a))!k.call(b,d)&&d!==c&&f(b,d,{get:()=>a[d],enumerable:!(e=h(a,d))||e.enumerable});return b};var n=(b,a,c)=>(c=b!=null?g(j(b)):{},l(a||!b||!b.__esModule?f(c,"default",{value:b,enumerable:!0}):c,b));export{m as a,n as b};
@@ -0,0 +1 @@
1
+ import{p}from"./chunk-FEVGFFVL.js";import{spawn as h}from"node:child_process";var d=t=>/^https:\/\/[\w.-]+\/device(\?[\w=&-]*)?$/.test(t);function I(t){if(!d(t))return;let n=(f,m,l,c)=>{try{let s=h(f,m,{stdio:"ignore",detached:!0,...l});s.on("error",()=>c?.()),s.unref()}catch{c?.()}},{file:i,args:u,options:a}=p(t);n(i,u,a,process.platform==="linux"&&i==="cmd.exe"?()=>n("xdg-open",[t],{}):void 0)}import{chmodSync as g,existsSync as x,mkdirSync as y,readFileSync as A,rmSync as S,writeFileSync as v}from"node:fs";import{homedir as k}from"node:os";import{join as e}from"node:path";var w="https://flintmaker.com",r=()=>e(process.env.XDG_CONFIG_HOME||e(k(),".config"),"flintmaker"),o=(t=r())=>e(t,"auth.json");function P(t=r()){try{let n=JSON.parse(A(o(t),"utf8"));return typeof n?.token=="string"?n:null}catch{return null}}function B(t,n=r()){y(n,{recursive:!0,mode:448}),v(o(n),JSON.stringify(t,null,2),{mode:384});try{g(o(n),384)}catch{}}function C(t=r()){x(o(t))&&S(o(t))}function E(t){return(process.env.FLINT_API||t?.api||w).replace(/\/$/,"")}export{I as a,P as b,B as c,C as d,E as e};
@@ -0,0 +1,6 @@
1
+ import{spawn as f}from"node:child_process";import{release as u}from"node:os";import l from"node:path";var n=(e=process.platform)=>e==="win32",p=(e=process.platform)=>n(e)?l.win32:l.posix;function E(e,r=process.platform){if(p(r).isAbsolute(e))return`${e} is outside the project`;if(n(r)){if(/^[a-zA-Z]:/.test(e)||/^[\\/]/.test(e))return`${e} is outside the project`;if(e.includes(":"))return`${e} is not a valid path on Windows`}return null}function N(e,r,t=process.platform){let o=p(t),s=o.normalize(e),i=o.normalize(r);return n(t)&&(s=s.toLowerCase(),i=i.toLowerCase()),s.endsWith(o.sep)&&(s=s.slice(0,-1)),i===s||i.startsWith(s+o.sep)}function I(e,r=process.platform){return n(r)?e.replace(/[. ]+$/,""):e}function L(e,r=process.platform){return n(r)?e.replace(/\\/g,"/"):e}function R(e){let r=(e.match(/\n/g)??[]).length;return r===0?"lf":(e.match(/\r\n/g)??[]).length===r?"crlf":"lf"}var d=e=>e.replace(/\r\n/g,`
2
+ `),T=e=>d(e).replace(/\n/g,`\r
3
+ `);function c(e=process.platform,r=process.env){if(!n(e))return"sh";let t=(r.FLINT_SHELL??"").trim().toLowerCase();return t==="powershell"||t==="powershell.exe"?"powershell":t==="pwsh"||t==="pwsh.exe"?"pwsh":"cmd"}function C(e,r=process.platform,t=process.env){let o=c(r,t);if(o==="sh")return{file:"/bin/sh",args:["-c",e],options:{detached:!0}};if(o==="cmd")return{file:t.ComSpec||t.COMSPEC||"cmd.exe",args:["/d","/s","/c",`"${e}"`],options:{windowsVerbatimArguments:!0,windowsHide:!0}};let s=Buffer.from(e,"utf16le").toString("base64");return{file:o==="pwsh"?"pwsh.exe":"powershell.exe",args:["-NoLogo","-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-EncodedCommand",s],options:{windowsHide:!0}}}function A(e,r=process.platform,t){if(!e)return t?.();if(n(r)){try{f("taskkill",["/pid",String(e),"/T","/F"],{stdio:"ignore",windowsHide:!0}).on("error",()=>t?.())}catch{t?.()}return}try{process.kill(-e,"SIGKILL")}catch{t?.()}}var m={sh:"/bin/sh",cmd:"cmd.exe (Windows Command Prompt)",powershell:"Windows PowerShell (powershell.exe)",pwsh:"PowerShell 7 (pwsh.exe)"};function w(e=process.platform,r=process.env){return e==="win32"?"Windows":e==="darwin"?"macOS":e==="linux"?r.WSL_DISTRO_NAME?`Linux (WSL, ${r.WSL_DISTRO_NAME})`:"Linux":e}function _(e=process.platform,r=process.env){let t=c(e,r),o=[`OS: ${w(e,r)}`,`run_command shell: ${m[t]}`];return t==="cmd"?o.push("Use Windows cmd syntax: dir, type, copy, move, del, mkdir, findstr, set VAR=x, && to chain. No ls/cat/grep/rm/export, no single-quote quoting."):t!=="sh"&&o.push("Use PowerShell syntax (Get-ChildItem, Get-Content, $env:VAR, ; to chain)."),n(e)&&o.push("File tool paths are project-relative; forward slashes are fine."),`<environment>
4
+ ${o.join(`
5
+ `)}
6
+ </environment>`}function y(e=process.platform,r=process.env){return r.FLINT_ASCII&&r.FLINT_ASCII!=="0"?!1:r.FLINT_UNICODE&&r.FLINT_UNICODE!=="0"?!0:n(e)?!!r.WT_SESSION||!!r.TERMINUS_SUBLIME||r.ConEmuTask==="{cmd::Cmder}"||r.TERM_PROGRAM==="vscode"||r.TERM_PROGRAM==="Terminus-Sublime"||r.TERMINAL_EMULATOR==="JetBrains-JediTerm"||r.TERM==="xterm-256color"||r.TERM==="alacritty"||r.TERM==="rxvt-unicode"||r.TERM==="rxvt-unicode-256color":r.TERM!=="linux"}var a={"\u2726":"*","\u25CF":"*","\u23BF":"`","\u276F":">","\u270E":">","\u23F5":">","\u23F8":"|","\u2387":"@","\u23CE":"/","\u2713":"v","\u2717":"x","\u2722":"*","\u2733":"*","\u2736":"*","\u273B":"*","\u273D":"*","\u259F":"/","\u2599":"\\","\u2588":"#","\u256D":"+","\u256E":"+","\u2570":"+","\u256F":"+","\u250C":"+","\u2510":"+","\u2514":"+","\u2518":"+","\u251C":"+","\u2524":"+","\u252C":"+","\u2534":"+","\u253C":"+","\u2500":"-","\u2502":"|","\u2191":"^","\u2193":"v","\u2192":">","\u2190":"<","\u2026":".","\u2248":"~","\u2013":"-","\u2014":"-","\u25B8":">","\u258C":"|","\u258E":"|","\u2022":"*","\u23F9":"#","\u2714":"v","\u2610":"o","\u2506":":","\u22EE":":","\xD7":"x"},h=new RegExp(`[${Object.keys(a).join("").replace(/[\\\]^-]/g,"\\$&")}]`,"g"),g=e=>e.replace(h,r=>a[r]??r);function M(e){let r=e.write.bind(e);e.write=((t,...o)=>r(typeof t=="string"?g(t):t,...o))}function b(e=process.platform,r=u()){if(!n(e))return 0;let t=r.split(".");return Number(t[0])>=10&&Number(t[2])||0}function O(e,r=process.platform,t=process.env){return r==="darwin"?{file:"open",args:[e],options:{}}:n(r)?{file:t.ComSpec||"cmd.exe",args:["/d","/s","/c",`start "" "${e}"`],options:{windowsVerbatimArguments:!0,windowsHide:!0}}:t.WSL_DISTRO_NAME?{file:"cmd.exe",args:["/d","/c","start",'""',e.replace(/&/g,"^&")],options:{}}:{file:"xdg-open",args:[e],options:{}}}export{n as a,p as b,E as c,N as d,I as e,L as f,R as g,d as h,T as i,C as j,A as k,_ as l,y as m,M as n,b as o,O as p};
@@ -0,0 +1,84 @@
1
+ import{a as He,b as ge,c as De}from"./chunk-4IWYTYSQ.js";import{a as I,c as Ue,d as _,e as we,f as Ge,g as ye,h as R,i as be,j as G,k as A,l as ze}from"./chunk-FEVGFFVL.js";import{readdirSync as kt}from"node:fs";import{join as vt,sep as xt}from"node:path";var O=[{name:"mcp",description:"Show configured MCP servers and connection status"},{name:"jobs",args:"[stop <id>]",description:"List and stop background jobs owned by this session"},{name:"help",description:"Show commands and keyboard shortcuts"},{name:"model",args:"[standard|smart]",description:"Switch model and see credit costs"},{name:"mode",args:"[ask|auto|plan]",description:"Change when flint asks before acting (or Shift+Tab)"},{name:"clear",description:"Start a new conversation",aliases:["new","reset"]},{name:"resume",description:"Continue a saved conversation from this folder"},{name:"rewind",args:"[number]",description:"Restore files and conversation to an earlier patch"},{name:"compact",description:"Summarise the conversation to free up context"},{name:"credits",description:"Show credit usage and plans",aliases:["usage"]},{name:"init",description:"Create FLINT.md, the project memory flint reads first"},{name:"update",description:"Update flint to the newest version now"},{name:"login",description:"Connect another FlintMaker account"},{name:"logout",description:"Disconnect this computer"},{name:"exit",description:"Quit flint",aliases:["quit","q"]}];function Je(r){let n=/^\/([a-zA-Z][\w-]*)(?:\s+([\s\S]*))?$/.exec(r.trim());if(!n)return null;let e=n[1].toLowerCase(),t=O.find(s=>s.name===e||s.aliases?.includes(e));return!t&&(e==="smart"||e==="standard")?{name:"model",args:e,command:O.find(s=>s.name==="model")}:{name:t?.name??e,args:(n[2]??"").trim(),command:t}}function $t(r,n){let e=Array.from({length:n.length+1},(t,s)=>s);for(let t=1;t<=r.length;t++){let s=e[0];e[0]=t;for(let i=1;i<=n.length;i++){let o=e[i];e[i]=Math.min(e[i]+1,e[i-1]+1,s+(r[t-1]===n[i-1]?0:1)),s=o}}return e[n.length]}function Vn(r){let n=null,e=1/0;for(let t of O)for(let s of[t.name,...t.aliases??[]]){let i=s.startsWith(r)?0:$t(r,s);i<e&&(e=i,n=t.name)}return e<=Math.max(2,Math.floor(r.length/3))?n:null}function Zn(r,n=O){let e=r.replace(/^\//,"").toLowerCase();return n.filter(t=>t.name.startsWith(e)||t.aliases?.some(s=>s.startsWith(e)))}function Yn(r){if(r.length===0)return"";let n=r[0];for(let e of r.slice(1))for(;!e.startsWith(n);)n=n.slice(0,-1);return n}var Et=new Set(["node_modules",".git","dist","build",".next","coverage",".venv","__pycache__","DerivedData","Pods"]);function Kn(r,n){let e=n.lastIndexOf("/"),t=e>=0?n.slice(0,e+1):"",s=e>=0?n.slice(e+1):n;if(t.split("/").includes("..")||t.startsWith("/"))return[];let i;try{i=kt(vt(r,t.split("/").join(xt)),{withFileTypes:!0})}catch{return[]}return i.filter(o=>o.name.startsWith(s)&&!Et.has(o.name)&&(s.startsWith(".")||!o.name.startsWith(".")||o.name===".github")).map(o=>t+o.name+(o.isDirectory()?"/":"")).sort((o,l)=>Number(l.endsWith("/"))-Number(o.endsWith("/"))||o.localeCompare(l)).slice(0,50)}function Y(r){let n=new Set;for(let e of r.matchAll(/(?:^|\s)@(?:"([^"\r\n]+)"|'([^'\r\n]+)'|((?:[\w.-]+[\\/])*[\w.-]+))/g))n.add(e[1]??e[2]??e[3]);return[...n]}import{existsSync as Mt,lstatSync as Tt,readdirSync as jt,readFileSync as It,realpathSync as Q}from"node:fs";import{homedir as Rt}from"node:os";import{join as ee}from"node:path";import{existsSync as Pt,lstatSync as K,readFileSync as St,realpathSync as Be}from"node:fs";import{homedir as Ct}from"node:os";import{join as ke}from"node:path";var _t=()=>({allow:[],ask:[],deny:[]});function z(r,n){if(!Pt(r)){if(At(r))throw new Error(`Refusing broken configuration link: ${r}`);return}if(!_(Be(n),Be(r))||!K(r).isFile()||K(r).isSymbolicLink())throw new Error(`Configuration must be a regular file inside ${n}`);if(K(r).size>128e3)throw new Error(`Configuration is too large: ${r}`);try{return JSON.parse(St(r,"utf8"))}catch{throw new Error(`Invalid JSON in ${r}`)}}function At(r){try{return K(r),!0}catch{return!1}}function qe(r,n=ke(Ct(),".flintmaker")){let e=_t();for(let[t,s]of[[ke(n,"settings.json"),n],[ke(r,".flint","settings.json"),r]]){let i=z(t,s);if(i===void 0)continue;if(!i||typeof i!="object"||Array.isArray(i))throw new Error(`Invalid settings: ${t}`);for(let l of["allow","ask","deny"]){let a=i[l];if(a!==void 0){if(!Array.isArray(a)||a.length>200||a.some(c=>typeof c!="string"||c.length>2e3||!/^(Bash|Read|Edit|Tool)\([^\r\n]+\)$/.test(c)))throw new Error(`Invalid ${l} rules in ${t}`);e[l].push(...a)}}let o=i.hooks;if(o!==void 0){if(!o||typeof o!="object"||Array.isArray(o))throw new Error(`Invalid hooks in ${t}`);e.hooks??={};for(let[l,a]of Object.entries(o)){if(!["PreToolUse","PostToolUse","Stop"].includes(l)||!Array.isArray(a)||a.length>20)throw new Error(`Invalid hook event in ${t}`);for(let c of a){if(!c||typeof c.command!="string"||!c.command.trim()||c.command.length>4e3||c.matcher!==void 0&&(typeof c.matcher!="string"||!/^[\w*|_-]{1,200}$/.test(c.matcher))||c.timeout!==void 0&&(!Number.isInteger(c.timeout)||c.timeout<1||c.timeout>60))throw new Error(`Invalid hook in ${t}`);(e.hooks[l]??=[]).push({command:c.command,matcher:c.matcher,timeout:c.timeout})}}}}return e}function v(r,n){for(let e of["deny","ask","allow"])if(r[e].some(t=>{let s=new RegExp(`^${t.split("*").map(i=>i.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join(".*")}$`);return n.some(i=>s.test(i))}))return e}function L(r){return/^(?:\.flint(?:\/|$))/i.test(r.replace(/\\/g,"/"))}function ve(r,n=ee(Rt(),".flintmaker")){let e=new Map,t=new Set([...O.flatMap(s=>[s.name,...s.aliases??[]]),"smart","standard"]);for(let[s,i,o]of[[n,ee(n,"commands"),!1],[r.root,ee(r.root,".flint","commands"),!0]])if(Mt(i)){if(!_(Q(s),Q(i)))throw new Error("Command directory is outside its configuration folder");for(let l of jt(i,{withFileTypes:!0}).sort((a,c)=>a.name.localeCompare(c.name))){if(!l.isFile()||!/^[a-zA-Z][\w-]*\.md$/.test(l.name))continue;let a=l.name.slice(0,-3).toLowerCase();if(t.has(a))continue;let c=ee(i,l.name);if(o&&["deny","ask"].includes(v(r.settings,[`Read(${r.rel(c)})`,"Tool(read_file)"])??""))continue;let u=Tt(c);if(u.isSymbolicLink()||u.size>32e3||!_(Q(s),Q(c)))continue;let d=It(c,"utf8").trim();d&&e.set(a,{name:a,args:"[arguments]",body:d,description:d.split(`
2
+ `)[0].replace(/^#+\s*/,"").slice(0,100)})}}return[...e.values()]}function xe(r,n){let e=Je(r);if(!e||e.command)return r;let t=n.find(s=>s.name===e.name);return t?t.body.replace(/\$ARGUMENTS/g,()=>e.args):r}import{spawn as qt}from"node:child_process";import{createHash as Xt}from"node:crypto";import{existsSync as Vt,mkdirSync as Zt,renameSync as Yt,writeFileSync as Kt}from"node:fs";import{homedir as Ke}from"node:os";import{join as N}from"node:path";import{StringDecoder as Qt}from"node:string_decoder";var te=new Set(["node_modules",".git",".hg",".svn","dist","build","out",".next",".nuxt",".svelte-kit",".output",".vercel",".wrangler",".expo","coverage",".venv","venv","__pycache__",".pytest_cache",".mypy_cache",".ruff_cache",".tox",".turbo",".cache",".parcel-cache",".gradle",".terraform","target","Pods","DerivedData","bower_components"]),Ot=/^(\.env(\..+)?|\.envrc|\.dev\.vars|\.netrc|\.git-credentials|\.npmrc|\.pypirc|\.htpasswd|.*\.(pem|key|p12|pfx|keystore|jks|tfvars|tfstate|tfstate\.backup|ppk|mobileprovision|p8|cer|crt|der|asc|gpg|kdbx)|id_(rsa|dsa|ecdsa|ed25519)(\.pub)?|credentials(\.json)?|secrets?\.(json|ya?ml|toml)|.*service[-_]?account.*\.json|.*-credentials\.json|google-services\.json|GoogleService-Info\.plist)$/i,Lt=/^\.env\.(example|sample|template|dist|defaults)$/i;function E(r){return Ot.test(r)&&!Lt.test(r)}function re(r){let n=r.split(/[\\/]+/).filter(t=>t&&t!==".");if(n.length===0)return!1;if(n.slice(0,-1).some(t=>te.has(t)))return!0;let e=n[n.length-1];return E(e)||te.has(e)}function Nt(r,n){let e="";for(let t=0;t<r.length;t++){let s=r[t];if(s==="*")r[t+1]==="*"?r[t+2]==="/"?(e+="(?:.*/)?",t+=2):(e+=".*",t+=1):e+="[^/]*";else if(s==="?")e+="[^/]";else if(s==="["){let i=r.indexOf("]",t+1);if(i===-1)e+="\\[";else{let o=r.slice(t+1,i).replace(/^!/,"^").replace(/\\/g,"\\\\");e+=`[${o}]`,t=i}}else s==="\\"&&t+1<r.length?e+=r[++t].replace(/[.*+?^${}()|[\]\\/]/g,"\\$&"):e+=s.replace(/[.*+?^${}()|[\]\\/]/g,"\\$&")}return new RegExp(`^${n?"":"(?:.*/)?"}${e}(?:/.*)?$`)}var ne=class{rules=[];add(n,e=""){for(let t of n.split(/\r?\n/)){if(!t.trim()||t.startsWith("#"))continue;t=t.replace(/(?<!\\)\s+$/,"");let s=!1;t.startsWith("!")?(s=!0,t=t.slice(1)):(t.startsWith("\\!")||t.startsWith("\\#"))&&(t=t.slice(1));let i=t.endsWith("/");i&&(t=t.slice(0,-1));let o=t.includes("/");t.startsWith("/")&&(t=t.slice(1)),t&&this.rules.push({base:e,rule:{re:Nt(t,o),negate:s,dirOnly:i}})}}ignores(n,e=!1){let t=!1;for(let{base:s,rule:i}of this.rules){let o=n;if(s){if(!n.startsWith(s+"/"))continue;o=n.slice(s.length+1)}if(i.dirOnly&&!e){let l=o.split("/").slice(0,-1),a=!1;for(let c=1;c<=l.length&&!a;c++)a=i.re.test(l.slice(0,c).join("/"));if(!a)continue}else if(!i.re.test(o))continue;t=!i.negate}return t}};var x=r=>({kind:"deny",reason:r}),Xe=new Set(["status","diff","log","show","blame","shortlog","rev-parse","ls-files","describe","grep","whatchanged","merge-base","rev-list","cherry"]),Ft=new Set(["add","commit","switch","mv","rm","restore","merge","cherry-pick","revert","init","fetch","pull","reset","notes"]),Ve={rebase:"rebase rewrites history; run it yourself",clean:"git clean deletes untracked files; run it yourself","filter-branch":"rewrites history; run it yourself","filter-repo":"rewrites history; run it yourself",gc:"run it yourself",prune:"run it yourself","update-ref":"run it yourself",config:"git config can run programs (aliases, hooks); change it yourself",credential:"credentials are off limits",submodule:"run it yourself",daemon:"not allowed","send-email":"not allowed","upload-pack":"not allowed","receive-pack":"not allowed",am:"use apply_patch instead",apply:"use apply_patch instead",archive:"not allowed",bundle:"not allowed"},Wt=/^(--(exec|upload-pack|receive-pack|ext-diff|output|config|git-dir|work-tree|namespace|super-prefix|exec-path|template|open-files-in-pager|no-index|contents|file)(=|$)|-F)/,Ze=/^(-f|--force|--force-with-lease(=.*)?|--force-if-includes|--mirror|--prune|--delete|-D)$/;function Ht(r){let e=(r.includes(":")&&!r.startsWith(":")?r.slice(r.indexOf(":")+1):r.replace(/^:(\([^)]*\))?/,"")).split(/[\\/]/).pop()??"";return!!e&&E(e)}function J(r,n={}){if(!Array.isArray(r)||r.length===0)return x('args are required, e.g. ["status","--short"]');if(r.some(o=>typeof o!="string"))return x("every argument must be a string");let[e,...t]=r;if(e.startsWith("-"))return x(`global git options (${e}) are not allowed; start with the subcommand`);if(r.some(o=>/[\0\n\r]/.test(o)))return x("arguments may not contain newlines");let s=t.find(o=>Wt.test(o));if(s)return x(`${s.split("=")[0]} is not allowed`);if(t.some(Ht))return x("that names a secret file (.env, keys, credentials); flint never reads or stages those");if(e==="push"){if(!n.allowPush)return x("flint never pushes unless started with --allow-push; ask the user to push");let o=t.find(l=>Ze.test(l)||/^\+/.test(l)||/^:/.test(l)||/^-[a-zA-Z]*f/.test(l));return o?x(`force or delete pushes (${o}) are never allowed`):{kind:"change"}}if(e in Ve)return x(Ve[e]);let i=t.find(o=>Ze.test(o)||/^-[a-zA-Z]*f[a-zA-Z]*$/.test(o));if(i&&!Xe.has(e))return x(`${i} (force) is not allowed`);if(Xe.has(e))return{kind:"read"};switch(e){case"branch":return t.every(l=>/^(-a|-r|-v|-vv|--list|-l|--all|--remotes|--show-current|--merged|--no-merged|--contains|--sort=.*|--format=.*|--no-color|--color)$/.test(l))?{kind:"read"}:{kind:"change"};case"stash":{let o=t[0]??"push";return o==="list"||o==="show"?{kind:"read"}:o==="drop"||o==="clear"?x("dropping stashes loses work; run it yourself"):{kind:"change"}}case"tag":return t.length===0||t.every(o=>/^(-l|--list|-n\d*|--sort=.*|--contains)$/.test(o))?{kind:"read"}:{kind:"change"};case"remote":return t.length===0||t.length===1&&t[0]==="-v"||t[0]==="show"?{kind:"read"}:x("changing remotes is not allowed");case"reflog":return t.length===0||t[0]==="show"?{kind:"read"}:x("run it yourself");case"worktree":return t[0]==="list"?{kind:"read"}:x("run it yourself");case"checkout":return t[0]==="-b"&&t.length>=2&&t.length<=3?{kind:"change"}:x('use "switch <branch>" or "checkout -b <new-branch>"; checkout of files discards work');case"reset":return t.some(o=>/^--(hard|merge|keep)$/.test(o))?x("reset --hard discards work; run it yourself"):{kind:"change"};case"restore":return t.some(o=>o==="--staged"||o==="-S")&&!t.some(o=>o==="--worktree"||o==="-W")?{kind:"change"}:x("restore without --staged discards work; run it yourself");case"commit":return t.some(o=>/^(-m|--message(=.*)?|-C|--reuse-message(=.*)?|--no-edit|-[a-zA-Z]*m)$/.test(o)||/^-m./.test(o))?{kind:"change"}:x('commit needs a message: ["commit","-m","\u2026"]');case"pull":return t.some(o=>o==="--rebase"||o==="-r"||/^--rebase=/.test(o))?x("pull --rebase rewrites history; run it yourself"):{kind:"change"};default:return Ft.has(e)?{kind:"change"}:x(`git ${e} is not supported by the git tool`)}}var Dt=[[/\.flint(?:maker)?[\\/]mcp\.json\b/i,"MCP configuration must be changed by the user outside agent tools"],[/(^|[\s;&|(])(sudo|doas|su|runas)(\s|$)/i,"commands that need admin rights are not allowed; run them yourself"],[/(^|[\s;&|(])rm\s+(-[a-z]*\s+)*-[a-z]*[rf][a-z]*\s+(-[a-z]*\s+)*("|')?(\/|~|\$HOME|\*|\.\.?\/?)("|')?(\s|$|\/\*)/i,"that would delete the project, your home folder or the whole disk"],[/(^|[\s;&|(])(remove-item|rm|del|rd|rmdir)\b.*\s(-recurse|\/s)\b.*\s([a-z]:\\?|~|\$env:userprofile|\\)(\s|$)/i,"that would delete a whole drive or your home folder"],[/(^|[\s;&|(])(mkfs(\.\w+)?|fdisk|diskpart|format(\.com)?\s+[a-z]:|shutdown|reboot|halt|poweroff|bcdedit)(\s|$)/i,"system-level commands are not allowed"],[/(^|\s)dd\s+.*of=\/dev\//i,"writing to a disk device is not allowed"],[/>\s*\/dev\/(sd|nvme|disk|hd)/i,"writing to a disk device is not allowed"],[/:\(\)\s*\{\s*:\|:&\s*\};:/,"not allowed"],[/(curl|wget|iwr|invoke-webrequest|irm|invoke-restmethod)\b[^|]*\|\s*(sudo\s+)?(sh|bash|zsh|fish|python3?|node|perl|ruby|iex|invoke-expression|pwsh|powershell)\b/i,"downloading and running a script is not allowed"],[/(^|[\s;&|(])(iex|invoke-expression)(\s|$|\()/i,"Invoke-Expression is not allowed"],[/chmod\s+(-R\s+)?[0-7]*777\s+\//,"not allowed"],[/(^|[\s;&|(])(npm|pnpm|yarn|bun)\s+publish\b/i,"flint never publishes packages; run it yourself"],[/(^|[\s;&|(])(cargo\s+publish|gem\s+push|twine\s+upload|poetry\s+publish|dotnet\s+nuget\s+push|vsce\s+publish)\b/i,"flint never publishes packages; run it yourself"],[/(^|[\s;&|(])(printenv|env)\s*($|[;&|])/i,"printing all environment variables could expose secrets"],[/(^|[\s;&|(])(set)\s*($|[;&|])/i,"printing all environment variables could expose secrets"],[/(get-childitem|gci|dir|ls)\s+env:/i,"printing all environment variables could expose secrets"],[/(^|[\s;&|(])(history|fc\s+-l)(\s|$)/i,"shell history can contain secrets"],[/(^|[\s"'=/\\])\.ssh([\\/]|\s|$)|\.aws[\\/]credentials|\.config[\\/]gh\b|\.docker[\\/]config|\.kube[\\/]config/i,"that touches your credentials folder"]],Ut=[/^(npm|pnpm|yarn|bun)\s+(test|t)(\s+--?[\w-]+(=\S+)?)*(\s+--(\s+\S+)*)?$/i,/^(npm|pnpm|bun)\s+run\s+(test|build|lint|typecheck|type-check|types|check|format:check|fmt:check|compile|verify|smoke)([:-][\w:-]+)?(\s+--(\s+\S+)*)?$/i,/^yarn\s+(run\s+)?(test|build|lint|typecheck|type-check|types|check|compile|verify)([:-][\w:-]+)?$/i,/^(npx|pnpm\s+exec|yarn|bunx)\s+(--no-install\s+)?(tsc|vitest|jest|eslint|mocha|ava|tap|playwright\s+test|prettier\s+--check|biome\s+(check|lint))(\s+[^\s]+)*$/i,/^(tsc|vitest|jest|eslint|mocha)(\s+[\w./=:@-]+)*$/i,/^(python3?|py)\s+-m\s+(pytest|unittest|mypy|ruff\s+check|black\s+--check|flake8|pylint|compileall)(\s+[\w./=:@-]+)*$/i,/^(pytest|mypy|flake8|pylint|ruff\s+check|black\s+--check|tox)(\s+[\w./=:@-]+)*$/i,/^go\s+(test|build|vet|fmt\s+-l|list)(\s+[\w./=:@-]+)*$/i,/^cargo\s+(test|build|check|clippy|fmt\s+--check|bench\s+--no-run)(\s+[\w./=:@-]+)*$/i,/^make(\s+-[sk])*\s+(test|tests|check|build|lint|typecheck|all)$/i,/^(\.\/gradlew|gradlew(\.bat)?|gradle)\s+([\w:-]*(test|Test|build|assemble\w*|lint|check|compile\w*)[\w:-]*\s*)+(--\w[\w-]*\s*)*$/,/^(mvn|\.\/mvnw|mvnw(\.cmd)?)\s+(-q\s+)?(test|verify|compile|package)(\s+-[\w.=-]+)*$/i,/^dotnet\s+(test|build)(\s+[\w./=:@-]+)*$/i,/^swift\s+(test|build)(\s+[\w./=:@-]+)*$/i,/^deno\s+(test|lint|check|fmt\s+--check)(\s+[\w./=:@-]+)*$/i,/^(bundle\s+exec\s+)?(rspec|rake\s+test|rails\s+test)(\s+[\w./=:@-]+)*$/i,/^(vendor\/bin\/)?(phpunit|phpstan|pest)(\s+[\w./=:@-]+)*$/i,/^composer\s+(test|validate)$/i,/^flutter\s+(test|analyze)(\s+[\w./=:@-]+)*$/i,/^dart\s+(test|analyze)(\s+[\w./=:@-]+)*$/i,/^(ls|dir|pwd|whoami|date|uname(\s+-a)?|tree)(\s+-[\w-]+)*(\s+[\w./-]+)?$/i,/^(node|npm|pnpm|yarn|bun|python3?|py|go|cargo|rustc|java|javac|dotnet|deno|ruby|php|git|tsc)\s+(-v|-V|--version|version)$/i,/^(which|where|command\s+-v|get-command)\s+[\w.-]+$/i,/^git\s+(status|diff|log|show|branch|rev-parse|ls-files)(\s+[\w./=:@^~-]+)*$/i],Gt=/[;|<>`$(){}\n\r\\!*?#'"&]/;function zt(r){return r.trim().replace(/\s+2>&1\s*$/,"").split(/\s*&&\s*/).map(n=>n.trim())}function Jt(r){for(let n of r.split(/[\s;&|<>()'"`=,]+/)){let e=n.split(/[\\/]/).pop()??"";if(e&&E(e))return e}return null}function Bt(r){let n=[];for(let e of r.split(/&&|\|\||[;|\n]/)){let t=e.trim().split(/\s+/),s=t.findIndex(i=>/^git(\.exe)?$/i.test(i.split(/[\\/]/).pop()??""));s!==-1&&n.push(t.slice(s+1).map(i=>i.replace(/^["']|["']$/g,"")))}return n}function S(r,n={}){let e=r.trim();if(!e)return x("command is required");for(let[i,o]of Dt)if(i.test(e))return x(o);let t=Jt(e);if(t)return x(`it names ${t}, which looks like a secret file; flint never reads or sends secrets`);for(let i of Bt(e)){if(i.length===0)continue;let o=J(i,n);if(o.kind==="deny")return x(`git ${i[0]}: ${o.reason}`);if(o.kind==="change")return{kind:"ask"}}return Gt.test(e.replace(/\s+2>&1\s*$/,"").replace(/&&/g," "))||I(n.platform)&&/[%^]/.test(e)?{kind:"ask"}:zt(e).every(i=>i&&Ut.some(o=>o.test(i)))?{kind:"allow"}:{kind:"ask"}}var Qe=r=>/^[a-zA-Z0-9_-]{1,60}$/.test(r)&&!r.includes("__"),Ye=r=>{let n=/^mcp__([a-zA-Z0-9_-]{1,24})__([a-zA-Z0-9_-]{1,60})$/.exec(r);return!!n&&!n[1].includes("__")&&!n[2].includes("__")};function se(r,n=0,e={n:0}){return n>12||++e.n>4e3?!1:typeof r=="string"?r.length<=8e3:r===null||typeof r=="number"||typeof r=="boolean"?!0:!r||typeof r!="object"?!1:Array.isArray(r)?r.length<=128&&r.every(t=>se(t,n+1,e)):Object.entries(r).every(([t,s])=>t.length<=200&&(t!=="$ref"||typeof s=="string"&&s.startsWith("#"))&&se(s,n+1,e))}var en=r=>Object.fromEntries(Object.entries({PATH:process.env.PATH,SystemRoot:process.env.SystemRoot,ComSpec:process.env.ComSpec,PATHEXT:process.env.PATHEXT,TEMP:process.env.TEMP,TMP:process.env.TMP,TMPDIR:process.env.TMPDIR,HOME:r,USERPROFILE:r}).filter(n=>typeof n[1]=="string"));function tn(r,n=N(Ke(),".flintmaker")){let e=new Map;for(let[t,s,i]of[[n,N(n,"mcp.json"),!1],[r,N(r,".flint/mcp.json"),!0]]){let o=z(s,t);if(o!==void 0){if(!o||typeof o!="object"||!o.mcpServers||Array.isArray(o.mcpServers)||typeof o.mcpServers!="object")throw new Error("mcp.json needs an mcpServers object");for(let[l,a]of Object.entries(o.mcpServers)){let c=a;if(!Qe(l)||l.length>24||!c||typeof c.command!="string"||!c.command||c.command.length>1e3||/[\0\r\n]/.test(c.command)||!Array.isArray(c.args??[])||(c.args??[]).some(u=>typeof u!="string"||u.length>2e3||/[\0\r\n]/.test(u)))throw new Error(`Invalid MCP server ${l}`);if((c.args??[]).length>40)throw new Error("Too many MCP arguments");e.set(l,{name:l,command:c.command,args:c.args??[],project:i})}}}if(e.size>4)throw new Error("At most 4 MCP servers are supported");return[...e.values()]}var $e=class{constructor(n,e,t,s=1e4){this.timeout=s;this.child=qt(n,e,{cwd:t,env:en(t),detached:process.platform!=="win32",stdio:["pipe","pipe","pipe"],windowsHide:!0});let i=new Qt("utf8");this.child.stdout.on("data",o=>{if(this.total+=o.length,this.buffer+=i.write(o),this.buffer.length>262144||this.total>8e6)return this.close("MCP output limit exceeded");for(;this.buffer.includes(`
3
+ `);){let l=this.buffer.indexOf(`
4
+ `),a=this.buffer.slice(0,l);this.buffer=this.buffer.slice(l+1);try{let c=JSON.parse(a);if(!c||Array.isArray(c)||c.jsonrpc!=="2.0")throw new Error("Invalid JSON-RPC");if(typeof c.method=="string")c.id!==void 0&&this.send({jsonrpc:"2.0",id:c.id,...c.method==="ping"?{result:{}}:{error:{code:-32601,message:"Client method not supported"}}});else{let u=this.pending.get(c.id);if(!u)continue;clearTimeout(u.timer),this.pending.delete(c.id),c.error?u.reject(new Error(String(c.error.message??"MCP request failed").slice(0,2e3))):Object.hasOwn(c,"result")?u.resolve(c.result):u.reject(new Error("Invalid MCP response"))}}catch{this.close("Invalid MCP JSON-RPC output");break}}}),this.child.stderr.on("data",o=>{this.total+=o.length,this.stderr=(this.stderr+String(o)).slice(-8e3),this.total>8e6&&this.close("MCP output limit exceeded")}),this.child.stdin.on("error",()=>this.close("MCP input closed")),this.child.on("error",()=>this.close("MCP process could not start")),this.child.on("exit",()=>this.close("MCP process exited")),process.on("exit",this.onExit),process.on("SIGINT",this.onExit),process.on("SIGTERM",this.onTerminate),process.on("SIGHUP",this.onTerminate)}timeout;child;next=1;pending=new Map;buffer="";total=0;closed=!1;stderr="";onExit=()=>this.close("MCP session ended");onTerminate=()=>{this.close("MCP session ended"),process.exit(1)};get alive(){return!this.closed}send(n){this.closed||this.child.stdin.write(JSON.stringify(n)+`
5
+ `)}request(n,e,t){if(this.closed||t?.aborted)return Promise.reject(new Error("MCP connection unavailable"));if(this.pending.size>=4||JSON.stringify(e).length>128e3)return Promise.reject(new Error("MCP request limit exceeded"));let s=this.next++,i=()=>this.close("MCP interrupted");return t?.addEventListener("abort",i,{once:!0}),new Promise((o,l)=>{let a=setTimeout(()=>this.close("MCP request timed out"),this.timeout);this.pending.set(s,{resolve:o,reject:l,timer:a}),this.send({jsonrpc:"2.0",id:s,method:n,params:e})}).finally(()=>t?.removeEventListener("abort",i))}async initialize(n){let e=await this.request("initialize",{protocolVersion:"2025-03-26",capabilities:{},clientInfo:{name:"flint",version:"0.5.0"}},n);if(!["2025-03-26","2024-11-05"].includes(e?.protocolVersion)||!e?.capabilities?.tools)throw new Error("Unsupported MCP version or tools capability");this.send({jsonrpc:"2.0",method:"notifications/initialized"})}close(n="MCP connection closed"){if(!this.closed){this.closed=!0,this.child.stdin.end(),A(this.child.pid,process.platform,()=>this.child.kill("SIGKILL"));for(let e of this.pending.values())clearTimeout(e.timer),e.reject(new Error(n));this.pending.clear(),process.removeListener("exit",this.onExit),process.removeListener("SIGINT",this.onExit),process.removeListener("SIGTERM",this.onTerminate),process.removeListener("SIGHUP",this.onTerminate)}}},oe=class{constructor(n,e=N(Ke(),".flintmaker")){this.ws=n;this.userDir=e;this.servers=tn(n.root,e);try{let t=z(N(e,"mcp-trust.json"),e);if(Array.isArray(t))for(let s of t)typeof s=="string"&&/^[a-f0-9]{64}$/.test(s)&&this.trusted.add(s)}catch{}}ws;userDir;servers;clients=new Map;states=new Map;tools=new Map;routes=new Map;trusted=new Set;get registry(){return[...this.tools.values()]}get available(){return[...this.routes].filter(([,n])=>this.clients.get(n.server)?.alive).map(([n])=>n)}restore(n=[]){if(!Array.isArray(n)||n.length>32||JSON.stringify(n).length>64e3)throw new Error("Invalid saved MCP registry");for(let e of n){if(e?.type!=="function"||!Ye(e.function?.name)||typeof e.function.description!="string"||!e.function.parameters||e.function.parameters.type!=="object"||e.function.description.length>4e3||JSON.stringify(e.function.parameters).length>16e3||!se(e.function.parameters))throw new Error("Invalid saved MCP tool");this.tools.set(e.function.name,e)}}status(){return this.servers.map(n=>`${n.name}: ${this.clients.get(n.name)?.alive?"connected":(this.states.get(n.name)==="connected"?"disconnected":this.states.get(n.name))??"not started"}`).join(`
6
+ `)||"No MCP servers configured."}async connect(n,e={}){if(!e.plan)for(let t of this.servers){if(e.signal?.aborted)break;if(this.clients.get(t.name)?.alive||this.states.get(t.name)==="declined"||this.states.get(t.name)?.startsWith("error:"))continue;for(let[d,f]of this.routes)f.server===t.name&&this.routes.delete(d);let s=[t.command,...t.args].map(d=>/[^\w./:@=-]/.test(d)?JSON.stringify(d):d).join(" "),i=Xt("sha256").update(JSON.stringify([this.ws.root,t.name,t.command,t.args])).digest("hex"),o=v(this.ws.settings,[`Bash(${s})`,`Tool(mcp_launch_${t.name})`]),l=t.command.split(/[\\/]/).pop().replace(/\.(?:exe|cmd|bat)$/i,""),a=S(`${l} ${t.args.map(d=>JSON.stringify(d)).join(" ")}`,e.policy),c=S(s,e.policy),u;try{if(a.kind==="deny"||c.kind==="deny"||o==="deny")throw new Error("MCP launch refused by command policy");if(!this.trusted.has(i)||o==="ask"){if(await n.approveCommand(`Launch ${t.project?"project":"user"} MCP ${t.name}: ${s} (runs with your account access)`,"ask","ask")!==!0){this.states.set(t.name,"declined");continue}this.trusted.add(i),e.privacy||this.saveTrust()}u=new $e(t.command,t.args,this.ws.root),await u.initialize(e.signal);let d=[],f=[],p;for(let w=0;w<4;w++){let h=await u.request("tools/list",p?{cursor:p}:{},e.signal);if(!Array.isArray(h?.tools))throw new Error("Invalid MCP tool list");for(let y of h.tools){let g=`mcp__${t.name}__${y?.name}`;if(!Qe(y?.name??"")||!Ye(g)||typeof y?.description!="string"&&y?.description!==void 0||(y.description?.length??0)>4e3||y?.inputSchema?.type!=="object"||JSON.stringify(y.inputSchema).length>16e3||!se(y.inputSchema))throw new Error("Invalid MCP tool schema");if(f.some(([b])=>b===g))throw new Error("Duplicate MCP tool name");d.push({type:"function",function:{name:g,description:y.description??"",parameters:y.inputSchema}}),f.push([g,{server:t.name,tool:y.name}])}if(d.length>32||JSON.stringify(d).length>64e3)throw new Error("MCP tool registry limit exceeded");if(p=h.nextCursor,p===void 0)break;if(typeof p!="string"||p.length>1e3||w===3)throw new Error("MCP pagination limit exceeded")}let m=new Map(this.tools);for(let w of d)m.set(w.function.name,w);if(m.size>32||JSON.stringify([...m.values()]).length>64e3)throw new Error("MCP tool registry limit exceeded");this.tools=m;for(let[w,h]of f)this.routes.set(w,h);this.clients.set(t.name,u),this.states.set(t.name,"connected")}catch(d){u?.close(),this.states.set(t.name,`error: ${d.message}`)}n.onMCP?.(t.name,this.states.get(t.name)??"unavailable")}}async call(n,e,t,s){if(s.plan)throw new Error("Plan mode refuses external MCP tools");let i=this.routes.get(n),o=i&&this.clients.get(i.server);if(!i||!o?.alive)throw new Error("MCP tool unavailable; inspect /mcp");let l=v(this.ws.settings,[`Tool(${n})`]);if(l==="deny")throw new Error("MCP call refused by settings");let a={ask:!1};if(this.checkArguments(e,"",0,a,s.policy),typeof e.command=="string"){let d=S(e.command,s.policy),f=v(this.ws.settings,[`Bash(${e.command})`]);if(d.kind==="deny"||f==="deny")throw new Error("MCP command refused by policy");if(await t.approveCommand(e.command,d.kind,f==="allow"&&d.kind==="allow"?"allow":"ask")!==!0)throw new Error("MCP command permission declined")}let c=Object.keys(e).some(d=>/^(cmd|script|code|expression)$/i.test(d));if((l!=="allow"||a.ask||c)&&await t.approveCommand(`MCP ${n} ${JSON.stringify(e)}`,"ask","ask")!==!0)throw new Error("MCP permission declined");let u=await o.request("tools/call",{name:i.tool,arguments:e},s.signal);if(u?.isError)throw new Error(`MCP tool error: ${JSON.stringify(u.content).slice(0,12e3)}`);return JSON.stringify(u).slice(0,32e3)}checkArguments(n,e="",t=0,s={ask:!1},i){if(t>12)throw new Error("MCP argument nesting limit exceeded");if(typeof n=="string"){if(S(n,i).kind==="deny")throw new Error("MCP argument refused by hard command policy");for(let o of n.split(/[\s'"(),;=]+/))if(E(o.split(/[\\/]/).pop()??""))throw new Error("MCP argument names a secret file");if(/^file:/i.test(n))throw new Error("MCP file URIs are refused; use project-relative paths");if(!/^https?:\/\//.test(n)&&(/path|file|directory|cwd|root/i.test(e)||/^(?:[.~]?\.\/|\/|[A-Za-z]:[\\/]|~[\\/])/.test(n))){let o=this.ws.rel(this.ws.resolve(n)),l=this.ws.canonical(o);if(L(o)||L(l))throw new Error("MCP tools cannot access project configuration");let a=v(this.ws.settings,[`Read(${o})`,`Edit(${o})`,`Read(${l})`,`Edit(${l})`]);if(a==="deny")throw new Error("MCP path refused by settings");a==="ask"&&(s.ask=!0)}}else if(Array.isArray(n))for(let o of n)this.checkArguments(o,e,t+1,s,i);else if(n&&typeof n=="object")for(let[o,l]of Object.entries(n))this.checkArguments(l,o,t+1,s,i)}closeAll(){for(let[n,e]of this.clients)e.close(),this.states.set(n,"stopped");this.clients.clear(),this.routes.clear()}saveTrust(){try{Zt(this.userDir,{recursive:!0,mode:448});let n=N(this.userDir,"mcp-trust.json");Vt(n)&&z(n,this.userDir);let e=`${n}.${process.pid}.tmp`;Kt(e,JSON.stringify([...this.trusted].slice(-200)),{mode:384,flag:"wx"}),Yt(e,n)}catch{}}};import{spawn as nn}from"node:child_process";var ie=class{constructor(n){this.root=n}root;jobs=new Map;next=1;listening=!1;cleanup=()=>this.stopAll();terminate=()=>{this.stopAll(),process.exit(1)};start(n){if([...this.jobs.values()].filter(l=>l.state==="running").length>=4)throw new Error("Background job limit reached (4); stop a job first");for(;this.jobs.size>=20;){let l=[...this.jobs.values()].find(a=>a.state!=="running");if(!l)break;this.jobs.delete(l.id)}this.listening||(process.on("exit",this.cleanup),process.on("SIGINT",this.cleanup),process.on("SIGTERM",this.terminate),process.on("SIGHUP",this.terminate),this.listening=!0);let e=G(n),t=nn(e.file,e.args,{...e.options,cwd:this.root,env:{...process.env,FORCE_COLOR:"0",CI:"1"},stdio:["ignore","pipe","pipe"]}),s=`job-${this.next++}`,i={id:s,command:n,child:t,output:"",state:"running"};this.jobs.set(s,i);let o=l=>{i.output=(i.output+l.toString("utf8")).slice(-16e3)};return t.stdout.on("data",o),t.stderr.on("data",o),t.on("error",l=>{i.state="failed",i.output=(i.output+l.message).slice(-16e3)}),t.on("exit",l=>{i.state==="running"&&(i.state=`exited ${l}`),A(t.pid,process.platform,()=>t.kill("SIGKILL"))}),`Started ${s}: ${n}. Use read_job for output and stop_job to stop it. Jobs end with this CLI session.`}list(){return[...this.jobs.values()].map(n=>`${n.id} [${n.state}] ${n.command}`).join(`
7
+ `)||"No background jobs."}read(n){let e=this.get(n);return`${e.id} [${e.state}] ${e.command}
8
+ ${e.output||"(no output yet)"}`}stop(n){let e=this.get(n);return e.state==="running"&&(e.state="stopped",A(e.child.pid,process.platform,()=>e.child.kill("SIGKILL"))),`${e.id} [${e.state}]`}stopAll(){for(let n of this.jobs.keys())this.stop(n);this.listening&&(process.removeListener("exit",this.cleanup),process.removeListener("SIGINT",this.cleanup),process.removeListener("SIGTERM",this.terminate),process.removeListener("SIGHUP",this.terminate),this.listening=!1)}get(n){if(typeof n!="string"||!this.jobs.has(n))throw new Error("Unknown job id; use an id owned by this session");return this.jobs.get(n)}};import{spawn as st,execFileSync as cn}from"node:child_process";import{closeSync as dn,constants as Ae,existsSync as W,fstatSync as un,lstatSync as fn,openSync as pn,readSync as mn,readFileSync as ot,realpathSync as q}from"node:fs";import{mkdir as Me,readdir as hn,readFile as X,stat as Te,unlink as it,writeFile as je}from"node:fs/promises";import{basename as le,dirname as ce,join as de,relative as Ie,resolve as at,sep as lt}from"node:path";var B=class{diff(n,e,t={}){let s;typeof t=="function"?(s=t,t={}):"callback"in t&&(s=t.callback);let i=this.castInput(n,t),o=this.castInput(e,t),l=this.removeEmpty(this.tokenize(i,t)),a=this.removeEmpty(this.tokenize(o,t));return this.diffWithOptionsObj(l,a,t,s)}diffWithOptionsObj(n,e,t,s){var i;let o=g=>{if(g=this.postProcess(g,t),s){setTimeout(function(){s(g)},0);return}else return g},l=e.length,a=n.length,c=1,u=l+a;t.maxEditLength!=null&&(u=Math.min(u,t.maxEditLength));let d=(i=t.timeout)!==null&&i!==void 0?i:1/0,f=Date.now()+d,p=[{oldPos:-1,lastComponent:void 0}],m=this.extractCommon(p[0],e,n,0,t);if(p[0].oldPos+1>=a&&m+1>=l)return o(this.buildValues(p[0].lastComponent,e,n));let w=-1/0,h=1/0,y=()=>{for(let g=Math.max(w,-c);g<=Math.min(h,c);g+=2){let b,$=p[g-1],k=p[g+1];$&&(p[g-1]=void 0);let T=!1;if(k){let j=k.oldPos-g;T=k&&0<=j&&j<l}let C=$&&$.oldPos+1<a;if(!T&&!C){p[g]=void 0;continue}if(!C||T&&$.oldPos<k.oldPos?b=this.addToPath(k,!0,!1,0,t):b=this.addToPath($,!1,!0,1,t),m=this.extractCommon(b,e,n,g,t),b.oldPos+1>=a&&m+1>=l)return o(this.buildValues(b.lastComponent,e,n))||!0;p[g]=b,b.oldPos+1>=a&&(h=Math.min(h,g-1)),m+1>=l&&(w=Math.max(w,g+1))}c++};if(s)(function g(){setTimeout(function(){if(c>u||Date.now()>f)return s(void 0);y()||g()},0)})();else for(;c<=u&&Date.now()<=f;){let g=y();if(g)return g}}addToPath(n,e,t,s,i){let o=n.lastComponent;return o&&!i.oneChangePerToken&&o.added===e&&o.removed===t?{oldPos:n.oldPos+s,lastComponent:{count:o.count+1,added:e,removed:t,previousComponent:o.previousComponent}}:{oldPos:n.oldPos+s,lastComponent:{count:1,added:e,removed:t,previousComponent:o}}}extractCommon(n,e,t,s,i){let o=e.length,l=t.length,a=n.oldPos,c=a-s,u=0;for(;c+1<o&&a+1<l&&this.equals(t[a+1],e[c+1],i);)c++,a++,u++,i.oneChangePerToken&&(n.lastComponent={count:1,previousComponent:n.lastComponent,added:!1,removed:!1});return u&&!i.oneChangePerToken&&(n.lastComponent={count:u,previousComponent:n.lastComponent,added:!1,removed:!1}),n.oldPos=a,c}equals(n,e,t){return t.comparator?t.comparator(n,e):n===e||!!t.ignoreCase&&n.toLowerCase()===e.toLowerCase()}removeEmpty(n){let e=[];for(let t=0;t<n.length;t++)n[t]&&e.push(n[t]);return e}castInput(n,e){return n}tokenize(n,e){return Array.from(n)}join(n){return n.join("")}postProcess(n,e){return n}get useLongestToken(){return!1}buildValues(n,e,t){let s=[],i;for(;n;)s.push(n),i=n.previousComponent,delete n.previousComponent,n=i;s.reverse();let o=s.length,l=0,a=0,c=0;for(;l<o;l++){let u=s[l];if(u.removed)u.value=this.join(t.slice(c,c+u.count)),c+=u.count;else{if(!u.added&&this.useLongestToken){let d=e.slice(a,a+u.count);d=d.map(function(f,p){let m=t[c+p];return m.length>f.length?m:f}),u.value=this.join(d)}else u.value=this.join(e.slice(a,a+u.count));a+=u.count,u.added||(c+=u.count)}}return s}};var Ee=class extends B{constructor(){super(...arguments),this.tokenize=sn}equals(n,e,t){return t.ignoreWhitespace?((!t.newlineIsToken||!n.includes(`
9
+ `))&&(n=n.trim()),(!t.newlineIsToken||!e.includes(`
10
+ `))&&(e=e.trim())):t.ignoreNewlineAtEof&&!t.newlineIsToken&&(n.endsWith(`
11
+ `)&&(n=n.slice(0,-1)),e.endsWith(`
12
+ `)&&(e=e.slice(0,-1))),super.equals(n,e,t)}},rn=new Ee;function Pe(r,n,e){return rn.diff(r,n,e)}function sn(r,n){n.stripTrailingCr&&(r=r.replace(/\r\n/g,`
13
+ `));let e=[],t=r.split(/(\n|\r\n)/);t[t.length-1]||t.pop();for(let s=0;s<t.length;s++){let i=t[s];s%2&&!n.newlineIsToken?e[e.length-1]+=i:e.push(i)}return e}function on(r){for(let n=0;n<r.length;n++)if(r[n]<" "||r[n]>"~"||r[n]==='"'||r[n]==="\\")return!0;return!1}function M(r){if(!on(r))return r;let n='"',e=new TextEncoder().encode(r),t=0;for(;t<e.length;){let s=e[t];s===7?n+="\\a":s===8?n+="\\b":s===9?n+="\\t":s===10?n+="\\n":s===11?n+="\\v":s===12?n+="\\f":s===13?n+="\\r":s===34?n+='\\"':s===92?n+="\\\\":s>=32&&s<=126?n+=String.fromCharCode(s):n+="\\"+s.toString(8).padStart(3,"0"),t++}return n+='"',n}var Se={includeIndex:!0,includeUnderline:!0,includeFileHeaders:!0};function Ce(r,n,e,t,s,i,o){let l;o?typeof o=="function"?l={callback:o}:l=o:l={},typeof l.context>"u"&&(l.context=4);let a=l.context;if(l.newlineIsToken)throw new Error("newlineIsToken may not be used with patch-generation functions, only with diffing functions");if(l.callback){let{callback:u}=l;Pe(e,t,Object.assign(Object.assign({},l),{callback:d=>{let f=c(d);u(f)}}))}else return c(Pe(e,t,l));function c(u){if(!u)return;u.push({value:"",lines:[]});function d(g){return g.map(function(b){return" "+b})}let f=[],p=0,m=0,w=[],h=1,y=1;for(let g=0;g<u.length;g++){let b=u[g],$=b.lines||an(b.value);if(b.lines=$,b.added||b.removed){if(!p){let k=u[g-1];p=h,m=y,k&&(w=a>0?d(k.lines.slice(-a)):[],p-=w.length,m-=w.length)}for(let k of $)w.push((b.added?"+":"-")+k);b.added?y+=$.length:h+=$.length}else{if(p)if($.length<=a*2&&g<u.length-2)for(let k of d($))w.push(k);else{let k=Math.min($.length,a);for(let C of d($.slice(0,k)))w.push(C);let T={oldStart:p,oldLines:h-p+k,newStart:m,newLines:y-m+k,lines:w};f.push(T),p=0,m=0,w=[]}h+=$.length,y+=$.length}}for(let g of f)for(let b=0;b<g.lines.length;b++)g.lines[b].endsWith(`
14
+ `)?g.lines[b]=g.lines[b].slice(0,-1):(g.lines.splice(b+1,0,"\"),b++);return{oldFileName:r,newFileName:n,oldHeader:s,newHeader:i,hunks:f}}}function ae(r,n){var e,t,s,i,o,l;if(n||(n=Se),Array.isArray(r)){if(r.length>1&&!n.includeFileHeaders&&!r.every(u=>u.isGit))throw new Error("Cannot omit file headers on a multi-file patch. (The result would be unparseable; how would a tool trying to apply the patch know which changes are to which file?)");return r.map(u=>ae(u,n)).join(`
15
+ `)}let a=[];if(r.isGit){if(n=Se,!r.oldFileName)throw new Error("oldFileName must be specified for Git patches");if(!r.newFileName)throw new Error("newFileName must be specified for Git patches");let u=r.oldFileName,d=r.newFileName;r.isCreate&&u==="/dev/null"?u=d.replace(/^b\//,"a/"):r.isDelete&&d==="/dev/null"&&(d=u.replace(/^a\//,"b/")),a.push("diff --git "+M(u)+" "+M(d)),r.isDelete&&a.push("deleted file mode "+((e=r.oldMode)!==null&&e!==void 0?e:"100644")),r.isCreate&&a.push("new file mode "+((t=r.newMode)!==null&&t!==void 0?t:"100644")),r.oldMode&&r.newMode&&!r.isDelete&&!r.isCreate&&(a.push("old mode "+r.oldMode),a.push("new mode "+r.newMode)),r.isRename&&(a.push("rename from "+M(((s=r.oldFileName)!==null&&s!==void 0?s:"").replace(/^a\//,""))),a.push("rename to "+M(((i=r.newFileName)!==null&&i!==void 0?i:"").replace(/^b\//,"")))),r.isCopy&&(a.push("copy from "+M(((o=r.oldFileName)!==null&&o!==void 0?o:"").replace(/^a\//,""))),a.push("copy to "+M(((l=r.newFileName)!==null&&l!==void 0?l:"").replace(/^b\//,""))))}else n.includeIndex&&r.oldFileName==r.newFileName&&r.oldFileName!==void 0&&a.push("Index: "+r.oldFileName),n.includeUnderline&&a.push("===================================================================");let c=r.hunks.length>0;n.includeFileHeaders&&r.oldFileName!==void 0&&r.newFileName!==void 0&&(!r.isGit||c)&&(a.push("--- "+M(r.oldFileName)+(r.oldHeader?" "+r.oldHeader:"")),a.push("+++ "+M(r.newFileName)+(r.newHeader?" "+r.newHeader:"")));for(let u=0;u<r.hunks.length;u++){let d=r.hunks[u],f=d.oldLines===0?d.oldStart-1:d.oldStart,p=d.newLines===0?d.newStart-1:d.newStart;a.push("@@ -"+f+","+d.oldLines+" +"+p+","+d.newLines+" @@");for(let m of d.lines)a.push(m)}return a.join(`
16
+ `)+`
17
+ `}function F(r,n,e,t,s,i,o){if(typeof o=="function"&&(o={callback:o}),o?.callback){let{callback:l}=o;Ce(r,n,e,t,s,i,Object.assign(Object.assign({},o),{callback:a=>{l(a?ae(a,o.headerOptions):void 0)}}))}else{let l=Ce(r,n,e,t,s,i,o);return l?ae(l,o?.headerOptions):void 0}}function an(r){let n=r.endsWith(`
18
+ `),e=r.split(`
19
+ `).map(t=>t+`
20
+ `);return n?e.pop():e.push(e.pop().slice(0,-1)),e}function et(r,n){let e=r.replace(/\t.*$/,"").trim();return e.startsWith('"')&&e.endsWith('"')&&(e=e.slice(1,-1)),e==="/dev/null"||e===""?null:(e.startsWith(n)&&(e=e.slice(2)),e)}function nt(r){if(r.length>1e6)throw new Error(`patch is too large (${r.length} bytes; max 1000000)`);let e=r.replace(/\r\n/g,`
21
+ `).replace(/^\s*```[\w-]*\n/,"").replace(/\n```\s*$/,`
22
+ `).split(`
23
+ `),t=[],s=null,i=null,o=null,l=null,a=null,c=()=>{s={oldPath:null,newPath:null,hunks:[]},t.push(s),i=null};for(let d=0;d<e.length;d++){let f=e[d];if(f.startsWith("diff --git ")){let m=/^diff --git "?a\/(.+?)"? "?b\/(.+?)"?$/.exec(f);a=m?{a:m[1],b:m[2]}:null,o=l=null,c(),s.oldPath=a?.a??null,s.newPath=a?.b??null;continue}if(f.startsWith("--- ")&&e[d+1]?.startsWith("+++ ")){let m=s;(!m||m.hunks.length>0||i)&&c(),s.oldPath=et(f.slice(4),"a/"),s.newPath=et(e[d+1].slice(4),"b/"),o&&s.oldPath===null&&s.newPath===null&&(s.oldPath=o),d++,i=null;continue}if(s&&!i){if(f.startsWith("rename from ")){o=f.slice(12).trim(),s.oldPath=o;continue}if(f.startsWith("rename to ")){l=f.slice(10).trim(),s.newPath=l;continue}if(f.startsWith("new file mode")){s.oldPath=null;continue}if(f.startsWith("deleted file mode")){s.newPath=null;continue}if(f.startsWith("GIT binary patch")||f.startsWith("Binary files ")){s.binary=!0;continue}}let p=/^@@+ (?:-(\d+)(?:,\d+)? \+\d+(?:,\d+)? )?@@+/.exec(f)??(/^@@\s*$/.test(f)?["",void 0]:null);if(p){if(!s)throw new Error("the patch has a hunk (@@) before any ---/+++ file header");i={oldStart:p[1]!==void 0?Number(p[1]):0,lines:[]},s.hunks.push(i);continue}if(i){let m=i;if(f.startsWith("\\")){m.lines.at(-1)?.kind==="+"&&(m.noNewlineAtEnd=!0);continue}let w=f[0];w===" "||w==="-"||w==="+"?m.lines.push({kind:w,text:f.slice(1)}):f===""?m.lines.push({kind:" ",text:""}):i=null}}for(let d of t)for(let f of d.hunks)for(;f.lines.length&&f.lines.at(-1).kind===" "&&f.lines.at(-1).text==="";)f.lines.pop();let u=t.filter(d=>d.oldPath!==null||d.newPath!==null);if(u.length===0)throw new Error('no file diffs found: send a unified diff with "--- a/path" and "+++ b/path" headers and @@ hunks');if(u.length>60)throw new Error(`the patch touches ${u.length} files (max 60); split it`);return u}var ln=(r,n,e)=>e?r.trimEnd()===n.trimEnd():r===n;function tt(r,n,e,t,s){let i=r.length-n.length,o=-1,l=1/0;for(let a=e;a<=i;a++){let c=!0;for(let u=0;u<n.length&&c;u++)c=ln(r[a+u],n[u],s);if(c){let u=Math.abs(a-t);if(u<l&&(o=a,l=u),a>t)break}}return o}function _e(r,n,e){let t=r===""||r.endsWith(`
24
+ `),s=r===""?[]:(t?r.slice(0,-1):r).split(`
25
+ `),i=0,o=0,l=!1;return n.forEach((a,c)=>{let u=a.lines.filter(h=>h.kind!=="+").map(h=>h.text),d=a.lines.filter(h=>h.kind!=="-").map(h=>h.text),f=Math.max(i,(a.oldStart>0?a.oldStart-1:0)+o),p;if(u.length===0?p=Math.min(s.length,Math.max(i,a.oldStart+o)):(p=tt(s,u,i,f,!1),p===-1&&(p=tt(s,u,i,f,!0))),p===-1){let h=u.find(y=>y.trim())??u[0];throw new Error(`hunk ${c+1} of ${e} does not match the file${a.oldStart?` (expected near line ${a.oldStart})`:""}: could not find the lines starting with ${JSON.stringify(h.slice(0,80))}. Re-read the file and send a corrected patch.`)}let m=[],w=p;for(let h of a.lines)h.kind==="+"?m.push(h.text):h.kind===" "?m.push(s[w++]):w++;s.splice(p,u.length,...m),i=p+d.length,o+=d.length-u.length,c===n.length-1&&a.noNewlineAtEnd&&p+d.length===s.length&&(l=!0)}),s.length===0?"":s.join(`
26
+ `)+(l||!t?"":`
27
+ `)}var gn=1e5,wn=400,yn=12e3,H=class{mcp;jobs;root;settings;constructor(n,e){this.root=q(n),this.jobs=new ie(this.root),this.settings=e??qe(this.root),this.mcp=new oe(this)}resolve(n){if(typeof n!="string"||n.trim()==="")throw new Error("path is required");let e=n.trim(),t=Ue(e);if(t)throw new Error(t);let s=at(this.root,e);if(!_(this.root,s))throw new Error(`${e} is outside the project`);let i=this.root;for(let l of Ie(this.root,s).split(lt).filter(Boolean)){i=de(i,l);let a;try{a=fn(i)}catch{break}if(a.isSymbolicLink()){let c;try{c=q(i)}catch{throw new Error(`${e} goes through a broken symlink; refusing`)}if(!_(this.root,c))throw new Error(`${e} is outside the project`)}}let o=[we(le(s))];if(W(s)&&o.push(we(le(q(s)))),I())try{o.push(le(q.native(s)))}catch{}if(o.some(E))throw new Error(`${e} looks like a secret file; flint does not read or change it`);return s}canonical(n){let t=this.resolve(n),s=[];for(;!W(t);)s.unshift(le(t)),t=ce(t);return this.rel(at(q(t),...s))}rel(n){return Ge(Ie(this.root,n))||"."}async listFiles(n=".",e=wn){let t=this.resolve(n),s;try{s=cn("git",["ls-files","--cached","--others","--exclude-standard","--",this.rel(t)],{cwd:this.root,encoding:"utf8",stdio:["ignore","pipe","ignore"],windowsHide:!0,maxBuffer:16*1024*1024}).split(/\r?\n/).filter(Boolean)}catch{s=[],await this.walk(t,s,this.ignoreFor(t))}return s=s.filter(o=>{try{return!re(o)&&!["deny","ask"].includes(v(this.settings,[`Read(${o})`,`Read(${this.canonical(o)})`])??"")}catch{return!1}}),s.sort(),s.slice(0,e).join(`
28
+ `)+(s.length>e?`
29
+ \u2026 ${s.length-e} more files`:"")||"(empty)"}ignoreFor(n){let e=new ne,t=Ie(this.root,n),s=t?t.split(lt):[];for(let i=0;i<=s.length;i++){let o=s.slice(0,i).join("/");try{e.add(ot(de(this.root,...s.slice(0,i),".gitignore"),"utf8"),o)}catch{}}return e}async walk(n,e,t=this.ignoreFor(n),s=5e3){if(e.length>=s)return;let i;try{i=await hn(n,{withFileTypes:!0})}catch{return}if(n!==this.root&&i.some(o=>o.name===".gitignore"&&o.isFile()))try{t.add(await X(de(n,".gitignore"),"utf8"),this.rel(n))}catch{}i.sort((o,l)=>o.name.localeCompare(l.name));for(let o of i){if(e.length>=s)return;let l=de(n,o.name),a=this.rel(l);o.isDirectory()?!te.has(o.name)&&!t.ignores(a,!0)&&await this.walk(l,e,t,s):o.isFile()&&!t.ignores(a)&&e.push(a)}}readImage(n,e=!1){let t=this.resolve(n),s=this.canonical(n),i=v(this.settings,[`Read(${this.rel(t)})`,`Read(${s})`,"Tool(read_file)"]);if(i==="deny"||i==="ask"&&!e)throw new Error("Image read requires explicit Read permission");if(s!==this.rel(t))throw new Error("Image attachments refuse symlink aliases");let o=pn(t,Ae.O_RDONLY|(Ae.O_NOFOLLOW??0)|(Ae.O_NONBLOCK??0));try{let l=un(o);if(!l.isFile()||l.size>2e6)throw new Error("Image must be a regular PNG/JPEG file, at most 2 MB");let a=Buffer.alloc(Math.min(2000001,l.size+1)),c=0,u;for(;c<a.length&&(u=mn(o,a,c,a.length-c,null))>0;)c+=u;this.resolve(n);let d=a.subarray(0,c);return`data:${He(d).mime};base64,${d.toString("base64")}`}finally{dn(o)}}async readFile(n,e,t,s=!1){let i=this.resolve(n),o=v(this.settings,[`Read(${this.rel(i)})`,`Read(${this.canonical(n)})`]);if(o==="deny"||o==="ask"&&!s)throw new Error("refused by Read rule; explicit approval required");let l=await Te(i);if(!l.isFile())throw new Error(`${n} is not a file`);let a=await X(i);if(a.subarray(0,8e3).includes(0))return`[binary file, ${l.size} bytes]`;let c=a.toString("utf8").split(/\r?\n/);c.at(-1)===""&&c.pop();let u=Math.max(1,e??1),d=Math.min(c.length,t??c.length),f="";for(let p=u;p<=d;p++){let m=`${p} ${c[p-1]}`;if(f.length+m.length>gn){f+=`
30
+ [truncated at line ${p-1} of ${c.length}; read more with start_line]`;break}f+=(f?`
31
+ `:"")+m}return f}async search(n,e="."){let t;try{t=new RegExp(n)}catch{throw new Error("invalid regular expression")}let s=(await this.listFiles(e)).split(`
32
+ `).filter(o=>o&&!o.startsWith("\u2026 ")),i=[];for(let o of s){if(i.length>=200)break;let l;try{let a=this.resolve(o);if((await Te(a)).size>4e5)continue;let c=await X(a);if(c.subarray(0,8e3).includes(0))continue;l=c.toString("utf8")}catch{continue}l.split(/\r?\n/).forEach((a,c)=>{i.length<200&&t.test(a)&&i.push(`${o}:${c+1}:${a.slice(0,300)}`)})}return i.join(`
33
+ `)||"No matches."}assertUnchanged(n,e){let t=this.resolve(n);if(this.checkEdit(n),(W(t)?ot(t,"utf8"):null)!==e)throw new Error(`${n} changed after the patch was planned; read it again`)}checkEdit(n){if(L(n)||L(this.canonical(n)))throw new Error("Project configuration and memory must be edited by the user, outside agent tools");if(v(this.settings,[`Edit(${n})`,`Edit(${this.canonical(n)})`])==="deny")throw new Error("refused by Edit rule")}async plan(n,e){let t=this.resolve(e.path);this.checkEdit(this.rel(t));let s=this.rel(t),i=W(t)?await X(t,"utf8"):null,o=i!==null&&ye(i)==="crlf",l=i!==null&&o?R(i):i,a;if(n==="write_file"){if(typeof e.content!="string")throw new Error("content is required");a=o?R(e.content):e.content}else{if(l===null)throw new Error(`${s} does not exist`);if(typeof e.old_text!="string"||e.old_text===""||typeof e.new_text!="string")throw new Error("old_text and new_text are required");let d=o?R(e.old_text):e.old_text,f=o?R(e.new_text):e.new_text,p=l.split(d).length-1;if(p===0)throw new Error(`old_text not found in ${s}`);if(p>1)throw new Error(`old_text matches ${p} times in ${s}; include more context`);a=l.replace(d,()=>f)}let c=F(`a/${s}`,`b/${s}`,l??"",a,"","",{context:3}),u=o?be(a):a;return{path:s,diff:c,created:i===null,apply:async()=>{this.assertUnchanged(s,i),await Me(ce(t),{recursive:!0}),await je(t,u)}}}async planPatch(n){if(typeof n!="string"||!n.trim())throw new Error("patch is required");let e=nt(n),t=[],s=[],i=new Set;for(let o of e){let l=o.newPath??o.oldPath??"?";try{for(let c of[o.oldPath,o.newPath])c&&this.checkEdit(this.rel(this.resolve(c)));let a=await this.planFilePatch(o);for(let c of[a.path,a.renamedFrom].filter(u=>!!u)){if(i.has(c))throw new Error("this file appears twice in the patch; merge its hunks into one file diff");i.add(c)}t.push(a)}catch(a){s.push(`${l}: ${a.message}`)}}if(s.length)throw new Error(`The patch was not applied (no file was changed):
34
+ - ${s.join(`
35
+ - `)}`);return t}async planFilePatch(n){if(n.binary)throw new Error("binary patches are not supported");if(n.oldPath===null){let f=this.resolve(n.newPath),p=this.rel(f);if(W(f))throw new Error("already exists; send a diff against the current content instead of /dev/null");let m=_e("",n.hunks,p);return{path:p,created:!0,diff:F(`a/${p}`,`b/${p}`,"",m,"","",{context:3}),apply:async()=>{this.assertUnchanged(p,null),await Me(ce(f),{recursive:!0}),await je(f,m)}}}let e=this.resolve(n.oldPath),t=this.rel(e),s;try{if(!(await Te(e)).isFile())throw new Error("not a file");s=await X(e,"utf8")}catch{throw new Error("does not exist; use --- /dev/null to create it")}let i=ye(s)==="crlf",o=i?R(s):s;if(n.newPath===null)return{path:t,created:!1,deleted:!0,diff:F(`a/${t}`,`b/${t}`,o,"","","",{context:3}),apply:async()=>{this.assertUnchanged(t,s),await it(e)}};let l=this.resolve(n.newPath),a=this.rel(l),c=l!==e;if(c&&W(l))throw new Error(`cannot rename to ${a}: it already exists`);let u=n.hunks.length?_e(o,n.hunks,t):o;if(!c&&u===o)throw new Error("the hunks make no change");let d=i?be(u):u;return{path:a,created:!1,renamedFrom:c?t:void 0,diff:F(`a/${c?t:a}`,`b/${a}`,o,u,"","",{context:3}),apply:async()=>{this.assertUnchanged(t,s),c&&this.assertUnchanged(a,null),await Me(ce(l),{recursive:!0}),await je(l,d),c&&await it(e)}}}runHook(n,e,t=1e4,s){return new Promise(i=>{if(s?.aborted)return i({code:2,stdout:"",stderr:"Hook interrupted"});let o=G(n),l=st(o.file,o.args,{...o.options,cwd:this.root,env:{...process.env,FORCE_COLOR:"0",CI:"1"}}),a="",c="";l.stdout.on("data",m=>{a=(a+String(m)).slice(-8e3)}),l.stderr.on("data",m=>{c=(c+String(m)).slice(-8e3)});let u=!1,d=()=>{u=!0,A(l.pid,process.platform,()=>l.kill("SIGKILL"))},f=setTimeout(d,t);s?.addEventListener("abort",d,{once:!0});let p=(m,w="")=>{clearTimeout(f),s?.removeEventListener("abort",d),i({code:u?2:m,stdout:a,stderr:c+(u?`
36
+ Hook timed out or was interrupted`:w)})};l.on("error",m=>p(2,m.message)),l.on("close",m=>p(m)),l.stdin.on("error",()=>{}),l.stdin.end(JSON.stringify(e))})}runCommand(n,e=12e4,t){return new Promise(s=>{if(t?.aborted)return s("[interrupted by the user before it started]");let i=G(n),o;try{o=st(i.file,i.args,{...i.options,cwd:this.root,env:{...process.env,FORCE_COLOR:"0",CI:"1"}})}catch(h){return s(`[could not run command: ${h.message}]`)}let l="",a="",c=0,u=h=>{let y=h.toString("utf8");if(l.length<4e3){let g=4e3-l.length;l+=y.slice(0,g),a+=y.slice(g)}else a+=y;a.length>8e3&&(c+=a.length-8e3,a=a.slice(-8e3))};o.stdout.on("data",u),o.stderr.on("data",u);let d=!1,f=!1,p=()=>A(o.pid,process.platform,()=>o.kill("SIGKILL")),m=setTimeout(()=>{d=!0,p()},e),w=()=>{f=!0,p()};t?.addEventListener("abort",w,{once:!0}),o.on("close",h=>{clearTimeout(m),t?.removeEventListener("abort",w);let y=(l+(c?`
37
+ [\u2026 ${c} characters omitted \u2026]
38
+ `:"")+a).replace(/\r\n/g,`
39
+ `),g=f?"[interrupted by the user]":d?`[timed out after ${Math.round(e/1e3)}s]`:`[exit code ${h}]`;s(`${y.slice(0,yn)}
40
+ ${g}`.trim())}),o.on("error",h=>{clearTimeout(m),t?.removeEventListener("abort",w),s(`[could not run command: ${h.message}]`)})})}};import{isAbsolute as bn}from"node:path";import{fileURLToPath as kn}from"node:url";import{readFileSync as ct,realpathSync as vn,statSync as dt}from"node:fs";var D="FLINT.md",ut=2e4,V=r=>r.toLocaleString("en-US");function cs(r){return r.creditsLeftToday!==null?`${V(r.creditsLeftToday)} credits left today`:`${V(r.creditsLeftMonth)} credits left`}function ds(r){let n=t=>new Date(t).toLocaleString(void 0,{weekday:"short",day:"numeric",month:"short",hour:"2-digit",minute:"2-digit"}),e=[`Plan: ${r.plan.charAt(0).toUpperCase()+r.plan.slice(1)}`];return r.creditsLeftToday!==null&&e.push(`Today: ${V(r.creditsLeftToday)} credits left (resets ${n(r.resetAt)})`),e.push(`This period: ${V(r.creditsLeftMonth)} of ${V(r.monthlyCredits)} credits left (resets ${n(r.monthResetAt)})`),e}var us={standard:{label:"Standard",cost:"\u22482\u20133 credits per step",note:"fast, great for most tasks \xB7 all plans"},smart:{label:"Smart",cost:"\u22482\u20138 credits per step",note:"stronger reasoning, longer answers \xB7 paid plans"}},fs=`Create ${D} in the project root: a concise project memory that you will read at the start of every future conversation.
41
+ First explore the project (list files, read the README, package/config files and a few key sources). Then write ${D} (max ~60 lines) with:
42
+ - What the project is, in 1\u20132 sentences
43
+ - Tech stack and layout (key directories and entry points)
44
+ - How to install, build, run, test and lint (exact commands)
45
+ - Code style and conventions worth following
46
+ - Anything surprising (gotchas, generated files, things not to touch)
47
+ Do not include secrets. Keep it factual and short.`,ue=`Summarise our conversation so far so we can continue in a fresh context. Do not call any tools.
48
+ Include: the goal, decisions made, files changed (with what changed), commands run and their outcomes, open problems and next steps.
49
+ Be concise but keep exact file paths, names and error messages.`,xn="[Plan mode is on: do not change files or run commands. Explore with read-only tools (list_files, read_file, search) and reply with a clear, numbered plan. The user will switch modes to let you carry it out.]";function $n(r,n=new H(r)){try{let e=n.resolve(D);return["deny","ask"].includes(v(n.settings,[`Read(${D})`,`Read(${n.canonical(D)})`,"Tool(read_file)"])??"")||!dt(e).isFile()?null:dt(e).size>ut?ct(e,"utf8").slice(0,ut)+`
50
+ [truncated]`:ct(e,"utf8")}catch{return null}}async function pt(r,n,e){n=xe(n,ve(r));let t=(e.images??[]).map(ge);if(t.length>4)throw new Error("At most 4 images may be attached");let s=Y(n).filter(a=>/\.(png|jpe?g)$/i.test(a));if(t.length+s.length>4)throw new Error("At most 4 images may be attached");if((t.length||s.length)&&!e.imageInput)throw new Error("Image input is unavailable on this API or selected model");let i=[],o=[],l=!1;if(e.first){o.push(ze());let a=$n(r.root,r);a&&(l=!0,o.push(`<project-memory file="${D}">
51
+ ${a.trim()}
52
+ </project-memory>`))}e.mode==="plan"&&o.push(xn),o.push(n);for(let a of Y(n))try{if(/\.(png|jpe?g)$/i.test(a)){if(t.length>=4)throw new Error("At most 4 images may be attached");let d=r.rel(r.resolve(a)),f=r.canonical(a),m=v(r.settings,[`Read(${d})`,`Read(${f})`,"Tool(read_file)"])==="ask"?await e.approveRead?.(d)===!0:!1;t.push(r.readImage(a,m)),i.push({path:a,image:!0});continue}let c=await r.readFile(a),u=c.split(`
53
+ `).filter(d=>/^\d+\t/.test(d)).length;i.push({path:a,lines:u}),o.push(`<file path="${a}">
54
+ ${c}
55
+ </file>`)}catch(c){if(/\.(png|jpe?g)$/i.test(a))throw c;let u=c.message;i.push({path:a,error:/ENOENT|no such file/.test(u)?"not found":u.replace(`${a} `,"")})}return{content:o.join(`
56
+
57
+ `),attachments:i,memory:l,...t.length?{images:t}:{}}}var Oe=new Set(["rm","rmdir","sudo","su","doas","dd","mkfs","shred","curl","wget","ssh","scp","sh","bash","zsh","fish","eval","exec","xargs","env","chmod","chown","kill","killall","git push"]),En=new Set(["del","erase","rd","rmdir","format","cmd","powershell","pwsh","start","reg","regedit","taskkill","runas","icacls","takeown","attrib","cipher","diskpart","bcdedit","schtasks","sc","net","netsh","wsl","certutil","bitsadmin","mshta","rundll32","wscript","cscript","remove-item","invoke-expression","iex","invoke-webrequest","iwr","curl.exe","move","ren","rename","copy","xcopy","robocopy","mklink","setx","stop-process"]),ft=new Set(["node","py","python","python3","ruby","perl","php","deno","bun","npx","pnpx","bunx"]);function ps(r,n=process.platform){let e=r.trim();if(!e||/[;&|<>`$(){}\n\r\\*?!~#'"]/.test(e)||I(n)&&/[%^]/.test(e))return null;let[t,s]=e.split(/\s+/),i=I(n),o=i?t.split("/").pop().toLowerCase().replace(/\.(exe|cmd|bat|com|ps1)$/,""):t.split("/").pop();if(Oe.has(o)||i&&En.has(o))return null;let l=s&&/^[a-z][\w:-]*$/i.test(s)?`${t} ${s}`:t;return Oe.has(l)||i&&Oe.has(`${o} ${s?.toLowerCase()}`)||ft.has(o)&&l===t||ft.has(o)&&["npx","pnpx","bunx"].includes(o)?null:l}var Pn=r=>Y(r).some(n=>/\.(png|jpe?g)$/i.test(n));function ms(r,n){let e=n.trim();if(/^data:image\//i.test(e))return{image:ge(e)};if(/[\r\n]/.test(e)||e.startsWith("@"))return null;let t=e.replace(/^(?:"([^"\r\n]+)"|'([^'\r\n]+)')$/,(i,o,l)=>o??l);if(!/\.(png|jpe?g)$/i.test(t))return null;if(t.startsWith("file:")&&(t=kn(t)),bn(t)){try{t=vn(t)}catch{}t=r.rel(t)}let s=r.rel(r.resolve(t));return{reference:`@${/\s/.test(s)?JSON.stringify(s):s}`}}var hs=(r,n)=>Pn(xe(n,ve(r)));function U(r){let n=0,e=0,t=0,s=i=>{for(let o=0;o<i.length;o++)i.charCodeAt(o)<128?n++:e++};for(let i of r)if(s(i.content??""),i.role==="user"&&(t+=(i.images?.length??0)*85),i.role==="assistant")for(let o of i.tool_calls??[])s(o.function.arguments+o.function.name);return Math.ceil(n/4)+e+r.length*10+t}async function Le(r,n,e={}){if(!e.force&&r.length<240&&U(r)<(e.threshold??24e3))return r;let t=r.flatMap((d,f)=>f>1&&d.role!=="tool"?[f]:[]);if(r.length>2&&r.some(d=>d.role==="assistant")&&t.push(r.length),!t.length)return r;let s=t.find(d=>U(r.slice(d))<8e3&&r.length-d<=120)??t.at(-1),i=r.slice(0,s),o=r.slice(s),l=-1;for(let d=0;d<r.length;d++)r[d].role==="user"&&(l=d);e.onCompact?.(r.length);let a="",c=i;for(let d=0;d<4;d++){if(e.signal?.aborted)return r;try{let f=await n([...c,{role:"user",content:ue}],()=>{});if(f.toolCalls.length||!f.text.trim()||f.text.length>2e4)throw new Error("Automatic summary was invalid. Conversation retained; try /compact.");a=f.text.trim();break}catch(f){if(!(f instanceof Z))throw f;c=Ne(c)}}if(!a)throw new Error("Could not compact this conversation. Conversation retained; try /compact or /clear.");let u=[{role:"user",content:`Summary of earlier work (historical context; continue the current request):
58
+ ${a}`},{role:"assistant",content:"I will continue from this context."},...l>=0&&l<s?[r[l]]:[],...o];if(U(u)>=U(r)&&r.length<240)throw new Error("Automatic summary did not reduce context. Conversation retained; try /compact.");return e.onCompact?.(r.length,u.length),u}async function fe(r,n,e,t,s,i={}){if(i.plan||i.signal?.aborted)return null;for(let o of r.settings.hooks?.[n]??[]){if(o.matcher&&!o.matcher.split("|").some(u=>u==="*"||u===e))continue;s.onHook?.(n,o.command);let l=S(o.command,i.policy),a=v(r.settings,[`Bash(${o.command})`,"Tool(hook)"]),c=null;if(l.kind==="deny"||a==="deny")c=`Hook refused: ${l.kind==="deny"?l.reason:"settings deny rule"}`;else{let u=await s.approveCommand(o.command,l.kind,a==="allow"&&l.kind!=="allow"?"ask":a);if(u!==!0)c=typeof u=="object"?u.declined:"Hook permission declined.";else{let d=await r.runHook(o.command,{hook_event_name:n,tool_name:e,tool_input:t,tool_response:i.output,cwd:r.root},(o.timeout??10)*1e3,i.signal),f=[d.stdout,d.stderr].filter(Boolean).join(`
59
+ `);d.code===2||d.code===null?c=f||`Hook exited ${d.code}`:s.onHook?.(n,o.command,d.code===0,f||`Hook exited ${d.code}`)}}if(c)return s.onHook?.(n,o.command,!1,c),`${n} hook blocked${n==="PostToolUse"?" after the tool executed (its effects remain)":""}:
60
+ ${c}`}return null}function mt(r){if(!Array.isArray(r)||r.length>40)throw new Error("todos must be an array of at most 40 items");let n=r.map(e=>{if(!e||typeof e.text!="string"||!e.text.trim()||e.text.length>240||!["pending","in_progress","completed"].includes(e.status))throw new Error("Each todo needs text (1\u2013240 characters) and status pending, in_progress or completed");return{text:e.text.trim(),status:e.status}});if(n.filter(e=>e.status==="in_progress").length>1)throw new Error("Only one todo may be in_progress");return n.length?n.map(e=>`${e.status==="completed"?"[x]":e.status==="in_progress"?"[>]":"[ ]"} ${e.text}`).join(`
61
+ `):"Checklist cleared."}import{execFile as Cn}from"node:child_process";import{existsSync as pe,readFileSync as ht}from"node:fs";import{readFile as _n,stat as An}from"node:fs/promises";import{extname as Mn,join as me}from"node:path";var Tn=300,jn=1e3,In=256e3,Rn=12,On=48e3,Fe=16e3,P=[{re:/^export\s+(?:default\s+)?(?:declare\s+)?(?:abstract\s+)?(?:async\s+)?(?:function\*?|class|interface|type|enum|const|let|var|namespace)\s+([A-Za-z_$][\w$]*)/,group:1},{re:/^(?:async\s+)?function\*?\s+([A-Za-z_$][\w$]*)/,group:1},{re:/^(?:abstract\s+)?class\s+([A-Za-z_$][\w$]*)/,group:1},{re:/^(?:interface|type|enum)\s+([A-Za-z_$][\w$]*)/,group:1},{re:/^module\.exports\.([A-Za-z_$][\w$]*)\s*=/,group:1}],Ln={".ts":P,".tsx":P,".mts":P,".cts":P,".js":P,".jsx":P,".mjs":P,".cjs":P,".vue":P,".svelte":P,".py":[{re:/^(?:async\s+)?def\s+(\w+)/,group:1},{re:/^class\s+(\w+)/,group:1}],".go":[{re:/^func\s+(?:\([^)]*\)\s*)?(\w+)/,group:1},{re:/^type\s+(\w+)/,group:1}],".rs":[{re:/^(?:pub(?:\([^)]*\))?\s+)?(?:async\s+)?(?:fn|struct|enum|trait|mod|type|const|static)\s+(\w+)/,group:1}],".java":[{re:/^\s{0,4}(?:(?:public|private|protected|static|final|abstract|sealed)\s+)*(?:class|interface|enum|record)\s+(\w+)/,group:1}],".kt":[{re:/^\s{0,4}(?:(?:public|private|internal|open|data|sealed|abstract|suspend|inline)\s+)*(?:class|interface|object|fun|enum\s+class)\s+(?:<[^>]*>\s*)?([\w.]+)/,group:1}],".swift":[{re:/^\s{0,4}(?:(?:public|private|internal|open|final|fileprivate|static)\s+)*(?:class|struct|enum|protocol|extension|func|actor)\s+(\w+)/,group:1}],".cs":[{re:/^\s{0,8}(?:(?:public|private|internal|protected|static|sealed|abstract|partial)\s+)*(?:class|interface|enum|struct|record)\s+(\w+)/,group:1}],".rb":[{re:/^\s{0,2}(?:class|module|def)\s+([\w:.?!]+)/,group:1}],".php":[{re:/^(?:(?:abstract|final)\s+)?(?:class|interface|trait|function|enum)\s+(\w+)/,group:1}],".dart":[{re:/^(?:abstract\s+)?(?:class|mixin|enum|extension)\s+(\w+)/,group:1}],".c":[{re:/^(?:static\s+)?[\w\s*]+?\b(\w+)\s*\([^;]*$/,group:1}],".h":[{re:/^(?:typedef\s+)?struct\s+(\w+)/,group:1}]};function Nn(r,n){let e=Ln[Mn(r).toLowerCase()];if(!e)return[];let t=[];for(let s of n.split(/\r?\n/)){if(t.length>=Rn)break;for(let{re:i,group:o}of e){let l=i.exec(s);if(l&&l[o]&&!t.includes(l[o])){t.push(l[o]);break}}}return t}function gt(r,n="",e){let t=l=>me(r,n,l),s=l=>{try{let a=n?`${n}/${l}`:l;return e&&(e.resolve(a),["deny","ask"].includes(v(e.settings,[`Read(${a})`,`Read(${e.canonical(a)})`])??""))?!1:pe(t(l))}catch{return!1}},i=n?` (in ${n}/)`:"",o=[];if(s("package.json"))try{let l=JSON.parse(ht(t("package.json"),"utf8")),a=s("pnpm-lock.yaml")?"pnpm":s("yarn.lock")?"yarn":s("bun.lockb")||s("bun.lock")?"bun":"npm";for(let c of["test","typecheck","lint","build","check"])l.scripts?.[c]&&o.push(`${c}: ${c==="test"?`${a} test`:`${a} run ${c}`}${i}`)}catch{}if(s("Cargo.toml")&&o.push(`test: cargo test${i}`,`build: cargo build${i}`),s("go.mod")&&o.push(`test: go test ./...${i}`,`build: go build ./...${i}`),(s("pyproject.toml")||s("pytest.ini")||s("setup.py"))&&o.push(`test: python -m pytest${i}`),s("gradlew")||s("gradlew.bat")?o.push(`test: ./gradlew test${i}`,`build: ./gradlew build${i}`):s("pom.xml")&&o.push(`test: mvn test${i}`),s("Package.swift")&&o.push(`test: swift test${i}`),s("pubspec.yaml")&&o.push(`test: flutter test${i}`),s("Makefile"))try{let l=ht(t("Makefile"),"utf8");for(let a of["test","check","build","lint"])new RegExp(`^${a}:`,"m").test(l)&&o.push(`${a}: make ${a}${i}`)}catch{}return o}async function Fn(r,n="."){return(await r.listFiles(n,1/0)).split(`
62
+ `).filter(t=>t&&t!=="(empty)"&&!t.startsWith("\u2026 ")&&!re(t))}async function wt(r,n={}){let e=typeof n.path=="string"&&n.path.trim()?n.path.trim():".";r.resolve(e);let t=Math.max(1,Math.min(jn,Math.floor(Number(n.maxFiles)||Tn))),s=await Fn(r,e),i=gt(r.root,"",r),o=[...new Set(s.map(d=>d.split("/")).filter(d=>d.length>1).map(d=>d[0]))];for(let d of o)(pe(me(r.root,d,"package.json"))||pe(me(r.root,d,"Cargo.toml"))||pe(me(r.root,d,"go.mod")))&&i.push(...gt(r.root,d,r));let l=[],a=0,c=0;for(let d of s.slice(0,t)){let f="";try{let m=r.resolve(d),w=await An(m);if(w.size<=In){let h=await _n(m);if(h.subarray(0,8e3).includes(0))f=" [binary]";else{let y=h.toString("utf8"),g=y.split(`
63
+ `).length-(y.endsWith(`
64
+ `)?1:0),b=Nn(d,y);f=` [${g} lines]${b.length?`: ${b.join(", ")}`:""}`}}else f=` [${Math.round(w.size/1024)} KB]`}catch{continue}let p=`${d}${f}`;if(a+p.length>On)break;l.push(p),a+=p.length+1,c++}let u=[`Repository map of ${e==="."?"the project root":e}: ${s.length} file${s.length===1?"":"s"}${c<s.length?`, showing ${c} (pass path or max_files for more)`:""}.`];return u.push(i.length?`Commands: ${i.join(" \xB7 ")}`:"Commands: none detected."),u.push("Not indexed: .gitignored files, dependency/build folders and secret files (.env, keys)."),[...u,"",...l].join(`
65
+ `)}function Wn(r){return r.split(/(?=^diff --git )/m).map(e=>{let t=/^diff --git "?a\/(.+?)"? "?b\/(.+?)"?$/m.exec(e.split(`
66
+ `)[0]);return t&&[t[1],t[2]].map(i=>i.split("/").pop()??"").some(E)?`diff --git a/${t[1]} b/${t[2]}
67
+ [diff of a secret file hidden by flint]
68
+ `:e}).join("").split(`
69
+ `).filter(e=>{let t=/^([^:\s][^:]*):\d+:/.exec(e);return!(t&&E(t[1].split("/").pop()??""))}).join(`
70
+ `)}function Hn(r,n,e,t=6e4){return new Promise(s=>{let i={...process.env,GIT_PAGER:"cat",PAGER:"cat",GIT_TERMINAL_PROMPT:"0",GIT_EDITOR:"true",GIT_SEQUENCE_EDITOR:"true",GCM_INTERACTIVE:"never"},o=["-c","color.ui=false","-c","core.quotepath=false",...n];Cn("git",o,{cwd:r,env:i,timeout:t,maxBuffer:32*1024*1024,windowsHide:!0,signal:e,encoding:"utf8"},(a,c,u)=>{let d=Wn(`${c??""}${u?(c?`
71
+ `:"")+u:""}`).replace(/\r\n/g,`
72
+ `).trimEnd();if(d.length>Fe&&(d=`${d.slice(0,Fe)}
73
+ [\u2026 ${d.length-Fe} more characters; narrow it, e.g. with -- <path> or --stat]`),!a)return s({ok:!0,output:d||"(no output)"});let f=a;if(e?.aborted)return s({ok:!1,output:"[interrupted by the user]"});if(f.code==="ENOENT")return s({ok:!1,output:"git is not installed or not on PATH"});if(f.killed)return s({ok:!1,output:`${d}
74
+ [timed out after ${Math.round(t/1e3)}s]`.trim()});s({ok:!1,output:`${d}
75
+ [exit code ${typeof f.code=="number"?f.code:1}]`.trim()})}).stdin?.end()})}async function yt(r,n,e,t={}){let s=Array.isArray(n)?n:typeof n=="string"?n.trim().split(/\s+/):[],i=J(s,t);if(i.kind==="deny")throw new Error(`refused: ${i.reason}`);if(i.kind==="change"){let l=await e.approve(`git ${s.map(a=>/[\s"'$`\\]/.test(a)?JSON.stringify(a):a).join(" ")}`);if(l!==!0)return typeof l=="object"?l.declined:"The user declined this git command."}let o=await Hn(r.root,s,t.signal);if(!o.ok)throw new Error(o.output);return o.output}var he="Interrupted by the user before this finished.",Z=class extends Error{},Dn={list_files:r=>String(r.path??"."),read_file:r=>`${r.path}${r.start_line?`:${r.start_line}-${r.end_line??""}`:""}`,search:r=>`/${r.pattern}/ ${r.path??""}`.trim(),write_file:r=>String(r.path),edit_file:r=>String(r.path),start_background:r=>String(r.command),read_job:r=>String(r.id),stop_job:r=>String(r.id),run_command:r=>String(r.command),web_search:r=>`"${r.query??""}"`,web_fetch:r=>String(r.url??"").replace(/^https?:\/\//,"").replace(/\/$/,""),repo_map:r=>String(r.path??"."),apply_patch:r=>{let n=(String(r.patch??"").match(/^\+\+\+ /gm)??[]).length;return`${(typeof r.summary=="string"?r.summary.split(`
76
+ `)[0].slice(0,80):"")||"patch"}${n?` (${n} file${n===1?"":"s"})`:""}`},git:r=>Array.isArray(r.args)?r.args.map(String).join(" "):String(r.args??"")},bt=(r,n)=>typeof r=="object"?r.declined:n,Un=r=>`${r.path}${r.created?" (created)":r.deleted?" (deleted)":r.renamedFrom?` (moved from ${r.renamedFrom})`:""}`;async function Gn(r,n,e,t,s={}){let i=await r.planPatch(n.patch),o=typeof n.summary=="string"?n.summary:"",l;if(e.approvePatch)l=await e.approvePatch(i,o);else{l=[];for(let w of i)l.push(t?.aborted?!1:await e.approveChange(w))}if(t?.aborted)return he;let a=i.filter((w,h)=>l[h]===!0),c=a.length?s.checkpoints?.capture(a,s.messages??[]):void 0,u=[],d=[],f=[],p=[];for(let[w,h]of i.entries()){let y=l[w]??!1;if(y!==!0){d.push(h.path),typeof y=="object"&&!p.includes(y.declined)&&p.push(y.declined);continue}try{await h.apply(),u.push(Un(h))}catch(g){f.push(`${h.path}: ${g.message}`)}}c&&s.checkpoints?.finish(c);let m=[];return u.length&&m.push(`Applied ${u.length} of ${i.length} file${i.length===1?"":"s"}: ${u.join(", ")}.`),d.length&&m.push(`The user rejected ${d.length===i.length?"the whole patch":`these files: ${d.join(", ")}`}.${p.length?"":" Ask what they want instead, or try a different approach."}`),m.push(...p),f.length&&m.push(`Could not write: ${f.join("; ")}`),m.join(`
77
+ `)}async function zn(r,n,e,t,s={}){let i=s.signal,o=v(r.settings,[`Tool(${n})`]);if(o==="deny")throw new Error("refused by tool permission rule");let l=typeof e.path=="string"?r.rel(r.resolve(e.path)):".";if(["read_file","list_files","search","repo_map"].includes(n)){let c=v(r.settings,[`Read(${l})`,`Read(${r.canonical(l)})`,`Tool(${n})`]);if(c==="deny")throw new Error("refused by Read rule");if(c==="ask"&&await t.approveCommand(`Read ${l}`,"ask","ask")!==!0)throw new Error("Read permission declined")}if(o==="ask"&&!n.startsWith("mcp__")&&!["read_file","list_files","search","repo_map","write_file","edit_file","apply_patch","run_command","start_background","git"].includes(n)&&await t.approveCommand(`Tool ${n}`,"ask","ask")!==!0)throw new Error("Tool permission declined");if(s.plan&&["write_file","edit_file","apply_patch","run_command","start_background","stop_job","git"].includes(n))throw new Error("Plan mode is on; changes and commands are refused");if(n.startsWith("mcp__"))return r.mcp.call(n,e,t,s);let a=t;switch(t={...a,approveChange:async c=>{let u=v(r.settings,[`Edit(${c.path})`,`Edit(${r.canonical(c.path)})`,...c.renamedFrom?[`Edit(${c.renamedFrom})`]:[],`Tool(${n})`]);if(u==="deny")throw new Error("refused by Edit rule");return a.approveChange(c,u)},approveCommand:async(c,u)=>{let d=v(r.settings,[`Bash(${c})`,`Tool(${n})`]);if(d==="deny")throw new Error("refused by Bash rule");return a.approveCommand(c,u,d==="allow"&&u!=="allow"?"ask":d)},approvePatch:[...r.settings.allow,...r.settings.ask,...r.settings.deny].length?void 0:a.approvePatch},n){case"delegate":if(!s.delegate)throw new Error("Child delegation unavailable");if(typeof e.task!="string"||!e.task.trim()||e.task.length>4e3)throw new Error("Child task must be 1\u20134000 characters");return s.delegate(e.task);case"update_todos":return mt(e.todos);case"list_files":return r.listFiles(typeof e.path=="string"?e.path:".");case"read_file":return r.readFile(String(e.path),We(e.start_line),We(e.end_line),!0);case"search":return r.search(String(e.pattern??""),typeof e.path=="string"?e.path:".");case"repo_map":return wt(r,{path:typeof e.path=="string"?e.path:void 0,maxFiles:We(e.max_files)});case"write_file":case"edit_file":{let c=await r.plan(n,e),u=await t.approveChange(c);if(u!==!0)return bt(u,"The user declined this change. Ask what they want instead, or try a different approach.");if(i?.aborted)return he;let d=s.checkpoints?.capture([c],s.messages??[]);try{await c.apply()}finally{d&&s.checkpoints?.finish(d)}return`${c.created?"Created":"Updated"} ${c.path}.`}case"apply_patch":return Gn(r,e,t,i,s);case"git":{let c=`git ${Array.isArray(e.args)?e.args.join(" "):String(e.args??"")}`,u=v(r.settings,[`Bash(${c})`,"Tool(git)"]);if(u==="deny")throw new Error("refused by Bash rule");let d=Array.isArray(e.args)?e.args:String(e.args??"").trim().split(/\s+/),f=J(d,s.policy);if(f.kind==="deny")throw new Error(`refused: ${f.reason}`);if(f.kind==="read"&&u==="ask"&&await t.approveCommand(c,"allow")!==!0)throw new Error("Git permission declined");return yt(r,e.args,{approve:p=>t.approveCommand(p,"ask")},{...s.policy,signal:i})}case"read_job":return r.jobs.read(e.id);case"stop_job":return r.jobs.stop(e.id);case"start_background":case"run_command":{let c=String(e.command??"").trim();if(!c)throw new Error("command is required");let u=S(c,s.policy);if(u.kind==="deny")throw new Error(`refused: ${u.reason}`);let d=await t.approveCommand(c,u.kind);return d!==!0?bt(d,"The user declined to run this command."):i?.aborted?he:n==="start_background"?r.jobs.start(c):r.runCommand(c,s.policy?.timeoutMs,i)}default:throw new Error(`unknown tool ${n}`)}}var We=r=>typeof r=="number"&&Number.isFinite(r)?r:void 0;function Ne(r){let n=structuredClone(r),e=n.map((t,s)=>({m:t,i:s})).filter(t=>t.m.role==="tool"&&(t.m.content?.length??0)>400);for(let{m:t}of e.slice(0,Math.max(1,Math.ceil(e.length/2))))t.role==="tool"&&(t.content=`${t.content.slice(0,300)}
78
+ [output trimmed to save context; read again if needed]`);if(e.length===0&&n.length>4){let t=n.find(s=>s.role==="assistant"&&!s.tool_calls?.length&&(s.content?.length??0)>400);if(t)t.content=`${t.content.slice(0,300)}
79
+ [earlier answer trimmed]`;else{let s=n.findIndex((i,o)=>o>0&&i.role==="user");s>0&&n.splice(0,s)}}return n}async function Jn(r,n,e,t,s={}){let i=De(n),o=s.signal,l=(d,f)=>e(d,f,{readOnly:s.readOnly,signal:o}),a=0,c=async d=>{if(s.readOnly||a++>=2)throw new Error("Child depth/count limit reached (depth 1, at most 2 per parent turn)");return Bn(r,d,e,t,s)},u=()=>{r.jobs.stopAll(),r.mcp.closeAll()};o?.addEventListener("abort",u,{once:!0});try{await r.mcp.connect(t,s);let d=s.contextThreshold??Math.max(4e3,24e3-U([{role:"user",content:JSON.stringify(r.mcp.registry)}]));for(let f=0;f<(s.maxSteps??25);f++){if(o?.aborted||(i=await Le(i,l,{threshold:d,signal:o,onCompact:t.onCompact}),o?.aborted))return i;let p=null,m="",w=h=>{m+=h,t.onDelta(h)};for(let h=0;h<4&&!p;h++)try{m="",p=await l(i,w)}catch(y){if(o?.aborted)return m&&i.push({role:"assistant",content:m}),i;if(!(y instanceof Z))throw y;let g=await Le(i,l,{force:!0,signal:o,onCompact:t.onCompact});i=g===i?Ne(i):g}if(!p)throw new Error("This conversation is too long. Start a new one with /clear.");if(p.toolCalls.length===0){i.push({role:"assistant",content:p.text});let h=await fe(r,"Stop","",{},t,s);if(h){i.push({role:"user",content:`[Local Stop hook feedback]
80
+ ${h}`});continue}return i}i.push({role:"assistant",content:p.text||null,tool_calls:p.toolCalls.map(h=>({id:h.id,type:"function",function:{name:h.name,arguments:h.arguments}}))});for(let h of p.toolCalls){if(o?.aborted){i.push({role:"tool",tool_call_id:h.id,content:he});continue}let y={},g=!1;try{let k=JSON.parse(h.arguments||"{}");if(!k||typeof k!="object"||Array.isArray(k))throw new Error("invalid arguments");y=k}catch{g=!0}t.onToolStart(h.name,(Dn[h.name]??(()=>""))(y),y);let b;if(s.readOnly&&!["list_files","read_file","search","repo_map"].includes(h.name)){b="Error: Child agents may only read, list, search and index project files; delegation depth is limited to one.",t.onToolResult(h.name,!1,b),i.push({role:"tool",tool_call_id:h.id,content:b});continue}let $=p.serverResults?.[h.id];if($){b=$.content,t.onToolResult(h.name,$.ok,$.ok?b:b.replace(/^Error: /,"")),i.push({role:"tool",tool_call_id:h.id,content:b});continue}try{if(g)throw new Error("Tool arguments must be a JSON object");let k=await fe(r,"PreToolUse",h.name,y,t,s);if(k)throw new Error(k);let T=s.checkpoints?.entries.at(-1);b=await zn(r,h.name,y,t,{signal:o,policy:s.policy,plan:s.plan,checkpoints:s.checkpoints,messages:i,delegate:c});let C=s.checkpoints?.entries.at(-1),j;try{j=await fe(r,"PostToolUse",h.name,y,t,{...s,output:b})}finally{C&&C!==T&&s.checkpoints?.refresh(C)}j&&(b+=`
81
+ ${j}`),t.onToolResult(h.name,!j,b)}catch(k){b=`Error: ${k.message}`,t.onToolResult(h.name,!1,k.message)}i.push({role:"tool",tool_call_id:h.id,content:b})}}}catch(d){throw d&&typeof d=="object"&&Object.assign(d,{partial:i}),d}finally{o?.removeEventListener("abort",u),o?.aborted&&u()}return s.onLimit?.(),i.push({role:"assistant",content:"I stopped after many steps. Tell me how to continue."}),i}async function Bn(r,n,e,t,s){let i=s.signal?AbortSignal.any([s.signal,AbortSignal.timeout(3e4)]):AbortSignal.timeout(3e4),o=new H(r.root,r.settings),l=0,a=!1,c=(d,f,p)=>++l>3?Promise.reject(new Error("Child request limit reached")):e(d,f,{...p,readOnly:!0,signal:i}),u={onDelta(){},onToolStart:(d,f)=>t.onChild?.("progress",`${d} ${f}`),onToolResult:(d,f)=>t.onChild?.("progress",`${d}: ${f?"done":"refused or failed"}`),approveChange:async()=>!1,approveCommand:(d,f,p)=>t.approveCommand(`Child: ${d}`,f,p)};t.onChild?.("start",n);try{let f=[{role:"user",content:(await pt(o,`Read-only side task: ${n}
82
+ Return a concise summary with exact file paths and findings. Do not modify files or delegate.`,{first:!0,mode:"plan"})).content}];try{f=await Jn(o,f,c,u,{maxSteps:3,signal:i,policy:s.policy,privacy:!0,readOnly:!0,plan:!0,onLimit:()=>{a=!0}})}catch(m){if(m.message!=="Child request limit reached")throw m;f=m.partial??f,a=!0}if(i.aborted)throw new Error("Child interrupted or timed out");let p=f.at(-1)?.role==="assistant"?f.at(-1).content??"":"";if(a){let m=await e([...f,{role:"user",content:ue+`
83
+ Return only the side-task findings; no tools.`}],()=>{},{readOnly:!0,signal:i});if(m.toolCalls.length)throw new Error("Child summary requested tools; no tools were executed");p=m.text}if(i.aborted||!p.trim())throw new Error("Child did not return a summary");return`Child findings (read-only; verify before making changes):
84
+ ${p.slice(0,4e3)}`}finally{o.jobs.stopAll(),o.mcp.closeAll(),t.onChild?.("done","Side task ended")}}export{v as a,L as b,O as c,Je as d,Vn as e,Zn as f,Yn as g,Kn as h,ve as i,H as j,D as k,V as l,cs as m,ds as n,us as o,fs as p,ue as q,pt as r,ps as s,ms as t,hs as u,he as v,Z as w,Ne as x,Jn as y};
@@ -0,0 +1 @@
1
+ import{a as g,b as d}from"./chunk-IZWMUOBI.js";import{d as f}from"./chunk-NQ54OCJ5.js";import{existsSync as h,lstatSync as p,readFileSync as w,writeFileSync as u,mkdirSync as b,unlinkSync as y,chmodSync as S}from"node:fs";import{dirname as k}from"node:path";var l=2e6,c=12e6,m=class{constructor(e,r){this.ws=e;Array.isArray(r)&&JSON.stringify(r).length<=c&&(this.entries=structuredClone(r.slice(-20)))}ws;entries=[];path(e){let r=this.ws.resolve(e);if(d(e)||this.ws.canonical(e)!==this.ws.rel(r))throw new Error("Checkpoints refuse configuration paths and symlinks");if(g(this.ws.settings,[`Edit(${e})`,`Read(${e})`])==="deny")throw new Error("Checkpoint path is denied by settings");if(h(r)&&!p(r).isFile())throw new Error(`Checkpoint path is not a regular file: ${e}`);return r}state(e){let r=this.path(e);if(!h(r))return{data:null,mode:420};let i=p(r);if(i.size>l)throw new Error(`File too large for a checkpoint: ${e} (limit 2 MB)`);return{data:w(r).toString("base64"),mode:i.mode&511}}capture(e,r){let i=Object.create(null);if(e.some(t=>t.diff.length>l))throw new Error("Patch too large for a checkpoint (limit 2 MB per file)");for(let t of e)for(let s of[t.path,t.renamedFrom])s&&(i[s]=this.state(s));if(JSON.stringify(i).length>c/2)throw new Error("Patch is too large for a checkpoint (limit 6 MB)");let a={label:e.map(t=>t.path).join(", "),messages:f(r),before:i,after:Object.create(null)};if(JSON.stringify(a).length+JSON.stringify(i).length+e.reduce((t,s)=>t+s.diff.length*2,0)>c)throw new Error("Checkpoint exceeds the conversation snapshot limit; compact the conversation first");return a}refresh(e){for(let r of Object.keys(e.before))e.after[r]=this.state(r)}finish(e){if(this.refresh(e),JSON.stringify(e.before)!==JSON.stringify(e.after))for(this.entries.push(e);this.entries.length>20||JSON.stringify(this.entries).length>c;)this.entries.shift()}rewind(e){if(!Number.isInteger(e)||e<0||e>=this.entries.length)throw new Error("Unknown checkpoint");let r=Object.create(null),i=Object.create(null);for(let t of this.entries.slice(e)){if(!t||!t.before||!t.after||!Array.isArray(t.messages))throw new Error("Invalid saved checkpoint");for(let[s,n]of Object.entries(t.before)){if(Object.hasOwn(r,s)||(r[s]=n),!Object.hasOwn(t.after,s))throw new Error("Incomplete saved checkpoint");i[s]=t.after[s]}}for(let[t,s]of Object.entries(r)){for(let o of[s,i[t]])if(!o||o.data!==null&&(typeof o.data!="string"||o.data.length>l*1.4||Buffer.from(o.data,"base64").toString("base64")!==o.data)||!Number.isInteger(o.mode)||o.mode<0||o.mode>511)throw new Error("Invalid saved file snapshot");let n=this.state(t);if(n.data!==i[t].data||n.data!==null&&n.mode!==i[t].mode)throw new Error(`Cannot rewind: ${t} changed after the patch. Keep or move those changes first.`)}let a=f(this.entries[e].messages);for(let[t,s]of Object.entries(r)){let n=this.path(t);s.data===null?h(n)&&y(n):(b(k(n),{recursive:!0}),u(n,Buffer.from(s.data,"base64"),{mode:s.mode}),S(n,s.mode))}return this.entries.splice(e),a}};export{m as a};