@swifty.js/swifty 0.0.27 → 0.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +47 -37
  2. package/dist/agent-YR6YK26C.js +4 -0
  3. package/dist/anthropic-5GRR5JCT.js +4 -0
  4. package/dist/checker-N5TIJEN5.js +4 -0
  5. package/dist/chunk-D34FUVGU.js +4 -0
  6. package/dist/{chunk-UGJVMFGH.js → chunk-K5ZK27BX.js} +1 -1
  7. package/dist/chunk-LIMEBKCY.js +408 -0
  8. package/dist/chunk-OTVZGPHD.js +121 -0
  9. package/dist/chunk-POHIQUFP.js +301 -0
  10. package/dist/chunk-QCKJLO6X.js +4 -0
  11. package/dist/chunk-QFSCPD63.js +4 -0
  12. package/dist/chunk-TMEX7RFA.js +4 -0
  13. package/dist/chunk-Y6SQB5FG.js +4 -0
  14. package/dist/lib/agent-5WEKVRTD.js +11 -0
  15. package/dist/lib/{anthropic-RYPJDHQM.js → anthropic-KWO3KLNV.js} +4 -5
  16. package/dist/lib/{checker-6BW4222R.js → checker-AXHPKVBY.js} +2 -2
  17. package/dist/lib/{chunk-HK2Z6WP4.js → chunk-2IVEVG5N.js} +5 -1
  18. package/dist/lib/{chunk-XFSN4LMA.js → chunk-3FBS5NB7.js} +71 -18
  19. package/dist/lib/{chunk-GNI7YX6F.js → chunk-DJ6AILPN.js} +70 -24
  20. package/dist/lib/chunk-GCY44T7S.js +2346 -0
  21. package/dist/lib/{chunk-PZ42NAFA.js → chunk-KG4MJGKQ.js} +19 -21
  22. package/dist/lib/{chunk-2AUSNVIB.js → chunk-KZSBR4FO.js} +44 -37
  23. package/dist/lib/chunk-PIL7M52F.js +127 -0
  24. package/dist/lib/chunk-Z4CYHTII.js +392 -0
  25. package/dist/lib/index.d.ts +227 -132
  26. package/dist/lib/index.js +2195 -1602
  27. package/dist/lib/{openai-VX5VBXZ4.js → openai-PBF7EWNJ.js} +2 -3
  28. package/dist/lib/{tool-filter-VF7TZRE5.js → tool-filter-R6HDDJHE.js} +1 -1
  29. package/dist/main.js +203 -194
  30. package/dist/{openai-5FDSLJNM.js → openai-QQ2K7GPF.js} +14 -14
  31. package/dist/{server-F666APPN.js → server-G23HCFLI.js} +21 -21
  32. package/dist/{tool-filter-NO7I3HFD.js → tool-filter-VBP6WOGO.js} +1 -1
  33. package/package.json +2 -2
  34. package/dist/agent-OMC6YIMW.js +0 -4
  35. package/dist/anthropic-T5K4BQPB.js +0 -4
  36. package/dist/checker-TOQVEG3P.js +0 -4
  37. package/dist/chunk-5I2WXSGV.js +0 -90
  38. package/dist/chunk-A2VR3BC4.js +0 -4
  39. package/dist/chunk-A57FEYSN.js +0 -4
  40. package/dist/chunk-BRC5L644.js +0 -130
  41. package/dist/chunk-FZPTNGTU.js +0 -4
  42. package/dist/chunk-G6YIQW3Z.js +0 -238
  43. package/dist/chunk-I2XG2PXV.js +0 -4
  44. package/dist/chunk-LVSHXIBK.js +0 -389
  45. package/dist/chunk-SULWNDNC.js +0 -4
  46. package/dist/chunk-WO6DL7OB.js +0 -35
  47. package/dist/lib/agent-U7MIUCKT.js +0 -9
  48. package/dist/lib/chunk-2QILP24G.js +0 -1120
  49. package/dist/lib/chunk-3LU4APFB.js +0 -339
  50. package/dist/lib/chunk-OO2CLOEE.js +0 -88
  51. package/dist/lib/chunk-RKJYTYQM.js +0 -1285
package/README.md CHANGED
@@ -68,11 +68,9 @@ pnpm dev
68
68
 
69
69
  ## Configuration
70
70
 
71
- Swifty reads YAML configuration files from multiple locations, merged in order:
71
+ Swifty reads a single global YAML configuration file:
72
72
 
73
- 1. ~/.swifty/config.yaml
74
- 2. .swifty/config.yaml (project root)
75
- 3. .swifty/config.local.yaml (project root, gitignored)
73
+ - ~/.swifty/config.yaml
76
74
 
77
75
  At least one provider must be configured. Example config.yaml:
78
76
 
@@ -81,8 +79,9 @@ providers:
81
79
  - name: anthropic
82
80
  protocol: anthropic
83
81
  base_url: https://api.anthropic.com
84
- model: claude-sonnet-4-20250514
82
+ model: claude-sonnet-4-6
85
83
  # api_key defaults to $ANTHROPIC_API_KEY
84
+ thinking: high # off | minimal | low | medium | high | xhigh | max
86
85
 
87
86
  permission_mode: default
88
87
 
@@ -111,16 +110,18 @@ enable_coordinator_mode: false
111
110
 
112
111
  Provider fields:
113
112
 
114
- | Field | Required | Description |
115
- | ----------------- | -------- | -------------------------------------------------------------------- |
116
- | name | yes | Display name for the provider |
117
- | protocol | yes | One of: anthropic, openai, openai-compat |
118
- | base_url | yes | API base URL |
119
- | model | yes | Model identifier |
120
- | api_key | no | API key (falls back to environment variable) |
121
- | thinking | no | Enable extended thinking mode (increases max_output_tokens to 64000) |
122
- | context_window | no | Override auto-detected context window size |
123
- | max_output_tokens | no | Override default max output tokens |
113
+ | Field | Required | Description |
114
+ | ----------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
115
+ | name | yes | Display name for the provider |
116
+ | protocol | yes | One of: anthropic, openai, openai-compat |
117
+ | base_url | yes | API base URL |
118
+ | model | yes | Model identifier |
119
+ | api_key | no | API key (falls back to environment variable) |
120
+ | thinking | no | Thinking level: off, minimal, low, medium, high, xhigh, max. Defaults to `high` for `anthropic` and `off` for `openai`/`openai-compat` (reasoning parameters are only sent when configured). A legacy `true`/`false` is still accepted (`false` → off, `true` → the protocol default). |
121
+ | context_window | no | Context window in tokens (default: 1000000; no model-name inference) |
122
+ | max_output_tokens | no | Output cap for the model (default: 128000, never above `context_window`). Set this for models with a smaller output limit. |
123
+
124
+ The thinking level controls reasoning depth. For `anthropic` it maps to a thinking token budget (minimal 1024, low 2048, medium 8192, high 16384, xhigh 32768, max 65536); for `openai` and `openai-compat` it maps to the provider reasoning effort. The budget shares `max_output_tokens` and always leaves at least 1024 answer tokens, so lower `max_output_tokens` shrinks the thinking budget instead of disabling it (below a 2048-token cap no valid budget remains and thinking falls back to disabled). On `openai`/`openai-compat`, the effort string is passed through verbatim, and only levels supported by the model are accepted (`xhigh`/`max` are model-specific). Use `/thinking <level>` to change it at runtime (the change is applied to the active client and saved to `~/.swifty/config.yaml`), or `/thinking` to show the current level.
124
125
 
125
126
  API keys are resolved in this order: explicit api_key field, then environment variables (ANTHROPIC_API_KEY for anthropic, OPENAI_API_KEY for openai and openai-compat).
126
127
 
@@ -134,6 +135,10 @@ swifty
134
135
 
135
136
  Launches the terminal interface. If multiple providers are configured, a provider selection screen appears first.
136
137
 
138
+ Use `/login` to configure and activate a provider from the TUI. When no provider is configured, the login form opens automatically. Name, protocol, base URL, API key, and model are required in the form. Use ↑↓ or Tab to move between fields, ←→ to select protocol or cycle the thinking level, Enter to save, and Esc to cancel. Changing the protocol also moves an untouched thinking level to that protocol's default. Duplicate names receive numeric suffixes (`name2`, `name3`, …).
139
+
140
+ The form saves to `~/.swifty/config.yaml`, retaining existing providers and other settings. Context window accepts integers from 1000 to 10000000; max output accepts integers from 1 to 1000000 and must not exceed the context window. Empty optional fields use the defaults above.
141
+
137
142
  ### Print Mode (Non-Interactive)
138
143
 
139
144
  ```bash
@@ -146,8 +151,9 @@ The -p flag sends a single prompt, runs the agent loop, and prints the result to
146
151
  ### Remote Mode (Browser UI)
147
152
 
148
153
  ```bash
149
- swifty --remote # listens on :18888
150
- swifty --remote :9000 # custom address
154
+ swifty --remote # listens on 127.0.0.1:18888
155
+ swifty --remote :9000 # custom loopback port
156
+ swifty --remote 0.0.0.0:9000 # explicitly expose on all interfaces (no built-in authentication)
151
157
  ```
152
158
 
153
159
  Starts a Koa HTTP server and WebSocket bridge. The bundled React frontend is served at the configured address for browser-based interaction.
@@ -156,25 +162,27 @@ Starts a Koa HTTP server and WebSocket bridge. The bundled React frontend is ser
156
162
 
157
163
  Inside the TUI, these commands are available:
158
164
 
159
- | Command | Description |
160
- | ----------------------- | ---------------------------------------------------------------------------------- |
161
- | /status | Show current session status (model, tokens, tools, sandbox, memories, skills, MCP) |
162
- | /permission mode <mode> | Change permission mode (default, acceptEdits, plan, bypassPermissions) |
163
- | /memory | List stored memories |
164
- | /memory clear | Clear all memories |
165
- | /skills | List available skills |
166
- | /skills reload | Hot-reload skills from disk |
167
- | /skill <name> [args] | Run a skill by name |
168
- | /plan | Enter plan mode (read-only investigation) |
169
- | /do | Exit plan mode and execute the approved plan |
170
- | /compact | Force conversation compaction |
171
- | /clear | Reset the session and clear the terminal |
172
- | /resume [id] | List or restore a previous session |
173
- | /rewind | Open checkpoint rewind dialog |
174
- | /sandbox [1/2/3] | Configure sandbox (1=on+auto, 2=on+manual, 3=off) |
175
- | /worktree | List git worktrees |
176
- | /mcp | Show MCP server status |
177
- | /quit | Exit the application |
165
+ | Command | Description |
166
+ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------- |
167
+ | /login | Configure, save, and activate an LLM provider |
168
+ | /status | Show current session status (model, tokens, tools, sandbox, memories, skills, MCP) |
169
+ | /permission mode <mode> | Change permission mode (default, acceptEdits, plan, bypassPermissions) |
170
+ | /memory | List stored memories |
171
+ | /memory clear | Clear all memories |
172
+ | /skills | List available skills |
173
+ | /skills reload | Hot-reload skills from disk |
174
+ | /skill <name> [args] | Run a skill by name |
175
+ | /plan | Enter plan mode (read-only investigation) |
176
+ | /do | Exit plan mode and execute the approved plan |
177
+ | /compact | Force conversation compaction |
178
+ | /clear | Reset the session and clear the terminal |
179
+ | /resume [id] | List or restore a previous session |
180
+ | /rewind | Open checkpoint rewind dialog |
181
+ | /sandbox [1/2/3] | Configure sandbox (1=on+auto, 2=on+manual, 3=off) |
182
+ | /worktree | List git worktrees |
183
+ | /mcp | Show MCP server status |
184
+ | /thinking [level] | Show or set the thinking level (off, minimal, low, medium, high, xhigh, max); setting persists to `~/.swifty/config.yaml` |
185
+ | /quit | Exit the application |
178
186
 
179
187
  ### Keyboard Shortcuts
180
188
 
@@ -186,4 +194,6 @@ Inside the TUI, these commands are available:
186
194
  | Ctrl+V | Paste a clipboard image (Alt+V on Windows) |
187
195
  | Shift+Tab | Cycle permission modes |
188
196
 
189
- Pasting an image saves it as a PNG under `.swifty/file-history/<session-id>/` and inserts a workDir-relative `@` reference into the prompt; on submit it expands into an inline image block like any other `@image` mention. Linux requires `wl-clipboard` (Wayland) or `xclip` (X11).
197
+ Pastes longer than 10 lines or 1,000 characters collapse to `[paste #1 +124 lines]` or `[paste #1 1234 chars]`. Clipboard images appear as `[Image #1]`. Arrow keys move across each placeholder as a unit, and Backspace/Delete remove it as a unit. Placeholders survive dialog switches; submitting restores the full text and image attachments.
198
+
199
+ Pasting an image saves it as a PNG under `.swifty/file-history/<session-id>/`. Its placeholder expands to a workDir-relative `@` reference on submit and loads as an inline image block. Linux requires `wl-clipboard` (Wayland) or `xclip` (X11).
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __swiftyCreateRequire } from "node:module";
3
+ const require = __swiftyCreateRequire(import.meta.url);
4
+ import{s as a}from"./chunk-POHIQUFP.js";import"./chunk-QFSCPD63.js";import"./chunk-TMEX7RFA.js";import"./chunk-QCKJLO6X.js";import"./chunk-OTVZGPHD.js";import"./chunk-E55KH72M.js";import"./chunk-X3UA5OZL.js";export{a as Agent};
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __swiftyCreateRequire } from "node:module";
3
+ const require = __swiftyCreateRequire(import.meta.url);
4
+ import{a as D}from"./chunk-K5ZK27BX.js";import{a as q}from"./chunk-QFSCPD63.js";import{g as M}from"./chunk-C3WGLVVZ.js";import{A as z,m as N,n as R,q as O,r as L,u as F,v as I,w as H,x as G,y as W}from"./chunk-TMEX7RFA.js";import"./chunk-QCKJLO6X.js";import{D as P,I as S,J as B,K as j,L as U,M as d,N as K,R as $,g as C}from"./chunk-OTVZGPHD.js";import"./chunk-E55KH72M.js";import"./chunk-X3UA5OZL.js";var J=1024,V=1024;function Q(n){for(let t=n.length-1;t>=0;t--){let e=n[t];if(e.defer_loading!==!0){e.cache_control={type:"ephemeral"};return}}}function X(n){return n.some(t=>t.defer_loading)}var v=S({module:"llm"});var Z=3e3,ee=P.object({max_input_tokens:P.coerce.number()});async function he(n){if(n.protocol!=="anthropic")return 0;let t=L(n),o=`${n.base_url.replace(/\/+$/,"")}/v1/models/${encodeURIComponent(n.model)}`,s=new AbortController,r=setTimeout(()=>{s.abort()},Z);try{let c=await fetch(o,{method:"GET",headers:{"anthropic-version":"2023-06-01",...t?{"x-api-key":t}:{}},signal:s.signal});if(!c.ok)return 0;let k=await c.json(),{success:_,error:m,data:h}=await C(ee,k);if(!_)return v.warn({message:m.message},"model context window schema validation failed"),0;let g=h.max_input_tokens;return Math.max(g,0)}catch(c){return v.error({err:c},"failed to fetch model context window"),0}finally{clearTimeout(r)}}function T(n){return typeof n=="string"?[{type:"text",text:n}]:n.map(t=>{let e=q(t);if(!e||e.type==="tool_reference")throw new Error(`Unsupported user content block: ${$(t,"type","unknown")}`);return e})}function te(n){let t=[];for(let e of n)if(e.role==="assistant"){let o=[];if(e.thinkingBlocks)for(let r of e.thinkingBlocks)o.push({type:"thinking",thinking:r.thinking,signature:r.signature});let s=typeof e.content=="string"?e.content:B(e.content);if(s&&o.push({type:"text",text:s}),e.toolUses)for(let r of e.toolUses)o.push({type:"tool_use",id:r.toolUseId,name:r.toolName,input:r.arguments});o.length===0&&o.push({type:"text",text:""}),t.push({role:"assistant",content:o})}else if(e.toolResults&&e.toolResults.length>0){let o=[];for(let s of e.toolResults)o.push({type:"tool_result",tool_use_id:s.toolUseId,is_error:s.isError,content:s.contentBlocks?.length?s.contentBlocks:s.content});e.content.length>0&&o.push(...T(e.content)),t.push({role:"user",content:o})}else{if(t.length===0){t.push({role:"user",content:T(e.content)});continue}let o=!1,s=t[t.length-1],r=s.content;s.role==="user"&&(typeof r=="string"||Array.isArray(r)&&r.length>0&&(r[0].type==="text"||r[0].type==="image"))&&(o=!0),o?(typeof r=="string"&&(r=s.content=r.trim().length>0?[{type:"text",text:r}]:[]),r.push(...T(e.content))):t.push({role:"user",content:T(e.content)})}return t}var Y=class{client;model;thinkingLevel;systemPrompt;maxOutputTokens;constructor(t,e){let o=L(t);if(!o)throw new I("Anthropic API key not found, set ANTHROPIC_API_KEY in ~/.swifty/config.yaml, or via ANTHROPIC_API_KEY env variable.");this.client=new M({apiKey:o,baseURL:t.base_url}),this.model=t.model,this.thinkingLevel=N(t),this.systemPrompt=e,this.maxOutputTokens=O(t)}setSystemPrompt(t){this.systemPrompt=t}setMaxOutputTokens(t){this.maxOutputTokens=t}setThinkingLevel(t){this.thinkingLevel=t}getThinkingLevel(){return this.thinkingLevel}async*stream(t,e,o){let s=te(z(t.getMessages())),r=X(e),c=e.map(l=>{let p=l.input_schema,u={name:l.name,description:l.description,input_schema:{type:"object",properties:p.properties,required:p.required??[]}};return l.defer_loading===!0&&(u.defer_loading=!0),u});Q(c),ne(s);let k={model:this.model,max_tokens:this.maxOutputTokens,stream:!0,system:[{type:"text",text:this.systemPrompt,cache_control:{type:"ephemeral"}}],messages:s,...c.length>0?{tools:c}:{}},_=this.maxOutputTokens-V;k.thinking=this.thinkingLevel!=="off"&&_>=J?{type:"enabled",budget_tokens:Math.min(R(this.thinkingLevel),_)}:{type:"disabled"};let m=0,h=0,g=0,x=0,E="end_turn",b="",A="",w=!1;try{let l=this.client.messages.stream(k,{...o?{signal:o}:{},...r?{headers:{"anthropic-beta":D}}:{}}),p="",u="",f="";for await(let a of l)switch(a.type){case"content_block_start":{let i=a.content_block;i.type==="thinking"?(w=!0,b="",A=""):i.type==="tool_use"&&(u=i.id,p=i.name,f="",yield{type:"tool_call_start",toolName:p,toolId:u});break}case"content_block_delta":{let i=a.delta;i.type==="thinking_delta"?(b+=i.thinking,yield{type:"thinking_delta",text:i.thinking}):i.type==="signature_delta"?A+=i.signature:i.type==="text_delta"?yield{type:"text_delta",text:i.text}:i.type==="input_json_delta"&&(f+=i.partial_json,yield{type:"tool_call_delta",text:i.partial_json});break}case"content_block_stop":{if(w&&(yield{type:"thinking_complete",thinking:b,signature:A},w=!1),p){let i={};if(f)try{let y=JSON.parse(f);i=j(y)?U(y):{}}catch(y){v.error({err:y},"llm operation failed"),i={}}yield{type:"tool_call_complete",toolId:u,toolName:p,arguments:i},p="",u="",f=""}break}case"message_delta":{a.delta.stop_reason&&(E=a.delta.stop_reason),a.usage.output_tokens&&(h=a.usage.output_tokens,a.usage.input_tokens&&(m=a.usage.input_tokens),a.usage.cache_read_input_tokens&&(g=a.usage.cache_read_input_tokens),a.usage.cache_creation_input_tokens&&(x=a.usage.cache_creation_input_tokens));break}case"message_start":{m=a.message.usage.input_tokens,h=a.message.usage.output_tokens,g=a.message.usage.cache_read_input_tokens??0,x=a.message.usage.cache_creation_input_tokens??0;break}}yield{type:"stream_end",stopReason:E,usage:{inputTokens:m,outputTokens:h,cacheReadInputTokens:g,cacheCreationInputTokens:x}}}catch(l){throw v.error({err:l},"llm operation failed"),oe(l)}}};function ne(n){for(let t=n.length-1;t>=0;t--){if(n[t].role!=="user")continue;let e=n[t].content;if(typeof e=="string"&&e.length===0||Array.isArray(e)&&e.length===0)return;typeof e=="string"&&(e=n[t].content=[{type:"text",text:e}]);let o=e[e.length-1];for(let s=e.length-1;s>=0;s--)if(e[s].type!=="image"){o=e[s];break}Reflect.set(o,"cache_control",{type:"ephemeral"});return}}function oe(n){if(n instanceof M.APIError){if(n.status===413||/prompts?\s+too\s+long/i.test(n.message))return new W(`Prompt too long: ${n.message}`);if(n.status===401)return new I(`Invalid API key: ${n.message}`);if(n.status===429){let t=n.headers,e=t instanceof Headers?t.get("retry-after"):void 0,o="Rate Limited";if(e){let s=Number.parseInt(d(e));Number.isNaN(s)?o+=", please wait.":o+=`, retry after ${d(s)}s.`}else o+=", please wait.";return new H(o,e?d(e):void 0)}return new F(`Anthropic API error (${d(n.status)}): ${n.message}`)}return new G(`Network error: ${K(n)}`)}export{Y as AnthropicClient,te as buildAnthropicMessages,he as fetchModelContextWindow,ne as markLastUserTailForCache,Q as markToolsForCache,X as needsToolSearchBeta};
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __swiftyCreateRequire } from "node:module";
3
+ const require = __swiftyCreateRequire(import.meta.url);
4
+ import{c as a,d as b,e as c,f as d,g as e,h as f}from"./chunk-D34FUVGU.js";import"./chunk-QCKJLO6X.js";import"./chunk-OTVZGPHD.js";import"./chunk-E55KH72M.js";import"./chunk-X3UA5OZL.js";export{b as PathSandbox,f as PermissionChecker,d as RuleEngine,c as evaluateRules,a as extractContent,e as isSafeCommand};
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __swiftyCreateRequire } from "node:module";
3
+ const require = __swiftyCreateRequire(import.meta.url);
4
+ import{c as W}from"./chunk-QCKJLO6X.js";import{D as h,I as S,R as u,d as b,e as F}from"./chunk-OTVZGPHD.js";import{b as A}from"./chunk-E55KH72M.js";import{readFileSync as B,writeFileSync as G,mkdirSync as U,statSync as T}from"fs";import{homedir as J,tmpdir as X}from"os";import{join as p,resolve as c,dirname as C}from"path";import{realpathSync as M}from"fs";import{basename as L,dirname as O,isAbsolute as z,join as I,relative as N,resolve as $,sep as Y}from"path";function d(i){let e=$(i),t=[];for(;;)try{return I(M(e),...t)}catch(n){if(!(n instanceof Error)||!("code"in n)||n.code!=="ENOENT"&&n.code!=="ENOTDIR")throw n;let s=O(e);if(s===e)return $(i);t.unshift(L(e)),e=s}}function m(i,e){let t=N(i,e);return t===""||t!==".."&&!t.startsWith(`..${Y}`)&&!z(t)}var P=S({module:"permissions"}),q=[],H=["ls","pwd","echo","cat","head","tail","wc","date","whoami","uname","hostname","which","type","file","git status","git log","git diff","git branch","git show","git rev-parse","git remote","bun test","bun run","npm test","npm run","go test","go build","go vet","python -c","node -e"],K={Bash:"command",PowerShell:"command",ReadFile:"file_path",WriteFile:"file_path",EditFile:"file_path",Glob:"pattern",Grep:"pattern",InstallSkill:"source"},Q=[".swifty/config.yaml",".swifty/permissions.local.yaml",".swifty/skills/"];function R(i,e){if(i===A)return W(u(e,"server",""),u(e,"tool",""));let t=K[i];if(!t)return"";let n=e[t];return typeof n=="string"?n:""}var x=class{allowedRoots;denyWritePaths;projectDir;constructor(e){this.projectDir=c(e),this.allowedRoots=[this.projectDir,X()],this.denyWritePaths=Q.map(t=>p(this.projectDir,t))}addRoot(e){this.allowedRoots.push(c(e))}addDenyWrite(e){this.denyWritePaths.push(c(e))}checkDenyWrite(e){let t=c(this.projectDir,e),n=d(t);for(let s of this.denyWritePaths)if(m(s,t)||m(d(s),n))return{effect:"deny",reason:`Path ${e} is in deny-write list`};return null}check(e){let t=d(c(this.projectDir,e));for(let n of this.allowedRoots)if(m(d(n),t))return null;return{effect:"deny",reason:`Path ${e} is outside allowed directories`}}};function V(i,e){let t="^"+i.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*").replace(/\?/g,".")+"$";try{return new RegExp(t).test(e)}catch(n){return P.error({err:n},"permissions operation failed"),!1}}var Z=/^(\w+)\((.+)\)$/;function j(i){let e;try{e=B(i,"utf-8")}catch(r){return r.code!=="ENOENT"&&P.error({err:r},"permissions operation failed"),[]}let t=h.object({rule:h.string().optional(),effect:h.string().optional()}),n;try{let r=b.load(e);n=F(h.array(t),r)}catch(r){return P.error({err:r},"permissions operation failed"),[]}let s=[];for(let r of n){if(r.effect!=="allow"&&r.effect!=="deny"&&r.effect!=="ask")continue;let o=Z.exec((r.rule??"").trim());o&&s.push({tool:o[1],pattern:o[2],effect:r.effect})}return s}function g(i,e,t){let n=null;for(let s of i)if(!(s.tool!==e&&s.tool!=="*")&&V(s.pattern,t)){if(s.effect==="deny")return"deny";s.effect==="ask"?n="ask":n??="allow"}return n}var D=class{userPath;projectPath;localPath;cache=new Map;constructor(e){this.userPath=p(J(),".swifty","permissions.yaml"),this.projectPath=p(e,".swifty","permissions.yaml"),this.localPath=p(e,".swifty","permissions.local.yaml")}rulesFor(e){let t;try{t=T(e,{bigint:!0})}catch{return this.cache.delete(e),[]}let n=this.cache.get(e);if(n?.mtimeNs===t.mtimeNs&&n.size===t.size)return n.rules;let s=j(e);return this.cache.set(e,{mtimeNs:t.mtimeNs,size:t.size,rules:s}),s}snapshot(){return[this.userPath,this.projectPath,this.localPath].flatMap(e=>this.rulesFor(e))}evaluate(e,t){return g(this.snapshot(),e,t)}appendLocalRule(e){U(C(this.localPath),{recursive:!0});let t=j(this.localPath);if(t.some(r=>r.tool===e.tool&&r.pattern===e.pattern&&r.effect===e.effect))return;t.push(e);let s=t.map(r=>({rule:`${r.tool}(${r.pattern})`,effect:r.effect}));G(this.localPath,b.dump(s),"utf-8")}};function ee(i){for(let e of q)if(e.re.test(i))return e.reason;return""}function te(i){let e=i.trim();return e.includes(">")||e.includes("<")||/[\r\n&]/.test(e)||e.includes("|")||e.includes(";")||e.includes("&&")||e.includes("$(")||e.includes("`")?!1:H.some(t=>e===t||e.startsWith(t+" ")||e.startsWith(t+" "))}function ne(i,e){switch(i){case"bypassPermissions":return"allow";case"plan":return e==="read"?"allow":"ask";case"acceptEdits":return e==="command"?"ask":"allow";default:return e==="read"?"allow":"ask"}}var _=class i{constructor(e,t="default"){this.workDir=e;this.mode=t,this.sandbox=new x(e),this.ruleEngine=new D(e)}workDir;mode;planFilePath="";sandboxEnabled=!1;sandboxAutoAllow=!1;sandbox;ruleEngine;forWorkDir(e){let t=new i(e,this.mode);return t.ruleEngine=this.ruleEngine,t.sandboxEnabled=this.sandboxEnabled,t.sandboxAutoAllow=this.sandboxAutoAllow,t}check(e,t,n){let s=R(e,n),r=null,o=()=>r??=this.ruleEngine.snapshot(),a=g(o(),e,s);if(a==="deny"||a==="ask")return{effect:a,reason:`Permission rule: ${a}`};if(this.mode==="plan"&&(e==="WriteFile"||e==="EditFile")){let f=u(n,"file_path","");if(this.planFilePath&&d(c(this.workDir,f))===d(c(this.workDir,this.planFilePath))&&!this.sandbox.checkDenyWrite(f))return{effect:"allow",reason:"Plan file write allowed in plan mode"}}if(t==="command"&&te(s))return{effect:"allow",reason:"Safe read-only command"};let k=t==="command"?ee(s):"";if(k)return{effect:"deny",reason:`Dangerous command blocked: ${k}`};if(this.sandboxEnabled&&this.sandboxAutoAllow&&e==="Bash"){let f=u(n,"command").split(/\s*(?:&&|\|\||[;|])\s*/).map(E=>E.trim()).filter(Boolean),l=!1;for(let E of f){let v=g(o(),e,E);if(v==="deny")return{effect:"deny",reason:"Permission rule: deny"};v==="ask"&&(l=!0)}return l?{effect:"ask",reason:"Permission rule: ask (sandbox does not override)"}:{effect:"allow",reason:"Sandbox auto-allow: OS sandbox active"}}let w=u(n,"file_path",u(n,"path",""));if((t==="read"||t==="write")&&w){if(t==="write"){let l=this.sandbox.checkDenyWrite(w);if(l)return l}let f=this.sandbox.check(w);if(f&&this.mode!=="bypassPermissions"){let l=this.ruleEngine.evaluate(e,s);return l?{effect:l,reason:`Permission rule: ${l}`}:{effect:"ask",reason:f.reason}}}let y=g(o(),e,s);return y?{effect:y,reason:`Permission rule: ${y}`}:{effect:ne(this.mode,t),reason:`Mode: ${this.mode}`}}allowExtraRoot(e){this.sandbox.addRoot(e)}allowAlways(e,t){let n=R(e,t),s=e==="ReadFile"||e==="WriteFile"||e==="EditFile",r;if(s&&n){let o=c(this.workDir,n),a=!1;try{a=T(o).isDirectory()}catch{}r=p(a?o:C(o),"*")}else r=n.trim().split(/\s+/).slice(0,2).join(" ")+"*";this.ruleEngine.appendLocalRule({tool:e,pattern:r,effect:"allow"})}describeToolAction(e,t){let n=R(e,t);if(n)return n;let s=[];for(let[r,o]of Object.entries(t)){let a=String(o);a.length>80&&(a=a.slice(0,80)+"..."),s.push(`${r}: ${a}`)}return s.join(", ")}};export{d as a,m as b,R as c,x as d,g as e,D as f,te as g,_ as h};
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire as __swiftyCreateRequire } from "node:module";
3
3
  const require = __swiftyCreateRequire(import.meta.url);
4
- import{a as i,d as s}from"./chunk-A57FEYSN.js";var a=10,p=2.5,E="advanced-tool-use-2025-11-20",f=new Set(["api.anthropic.com"]),d="SWIFTY_MCP_LOADING";function u(o){if(!o)return!0;try{return f.has(new URL(o).hostname.toLowerCase())}catch{return!1}}function l(o){return Math.floor(o/p)}function m(o,t,e,n=a){let r=(process.env[d]??"").trim().toLowerCase();if(r==="eager"||r==="native"||r==="dispatch")return r;if(e<=0)return"eager";let c=t*n/100;return l(e)<c?"eager":u(o)?"native":"dispatch"}function g(o){let t=0;for(let e of o.listTools())if(e.name.startsWith(i))try{t+=JSON.stringify(e.schema()).length}catch{t+=e.name.length+(e.description?.length??0)}return t}function T(o,t){o.mcpLoadingMode=t;let e=t==="eager";for(let n of o.listTools())s(n)&&typeof n.setDeferLoading=="function"&&n.setDeferLoading(!e);o.exposeToolSearch=!e,o.exposeMcpCall=t==="dispatch"}function M(o,t,e){let n=m(t,e,g(o));return T(o,n),n}export{E as a,T as b,M as c};
4
+ import{a as i,d as s}from"./chunk-QCKJLO6X.js";var a=10,p=2.5,E="advanced-tool-use-2025-11-20",f=new Set(["api.anthropic.com"]),d="SWIFTY_MCP_LOADING";function u(o){if(!o)return!0;try{return f.has(new URL(o).hostname.toLowerCase())}catch{return!1}}function l(o){return Math.floor(o/p)}function m(o,t,e,n=a){let r=(process.env[d]??"").trim().toLowerCase();if(r==="eager"||r==="native"||r==="dispatch")return r;if(e<=0)return"eager";let c=t*n/100;return l(e)<c?"eager":u(o)?"native":"dispatch"}function g(o){let t=0;for(let e of o.listTools())if(e.name.startsWith(i))try{t+=JSON.stringify(e.schema()).length}catch{t+=e.name.length+(e.description?.length??0)}return t}function T(o,t){o.mcpLoadingMode=t;let e=t==="eager";for(let n of o.listTools())s(n)&&typeof n.setDeferLoading=="function"&&n.setDeferLoading(!e);o.exposeToolSearch=!e,o.exposeMcpCall=t==="dispatch"}function M(o,t,e){let n=m(t,e,g(o));return T(o,n),n}export{E as a,T as b,M as c};