@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.
- package/README.md +47 -37
- package/dist/agent-YR6YK26C.js +4 -0
- package/dist/anthropic-5GRR5JCT.js +4 -0
- package/dist/checker-N5TIJEN5.js +4 -0
- package/dist/chunk-D34FUVGU.js +4 -0
- package/dist/{chunk-UGJVMFGH.js → chunk-K5ZK27BX.js} +1 -1
- package/dist/chunk-LIMEBKCY.js +408 -0
- package/dist/chunk-OTVZGPHD.js +121 -0
- package/dist/chunk-POHIQUFP.js +301 -0
- package/dist/chunk-QCKJLO6X.js +4 -0
- package/dist/chunk-QFSCPD63.js +4 -0
- package/dist/chunk-TMEX7RFA.js +4 -0
- package/dist/chunk-Y6SQB5FG.js +4 -0
- package/dist/lib/agent-5WEKVRTD.js +11 -0
- package/dist/lib/{anthropic-RYPJDHQM.js → anthropic-KWO3KLNV.js} +4 -5
- package/dist/lib/{checker-6BW4222R.js → checker-AXHPKVBY.js} +2 -2
- package/dist/lib/{chunk-HK2Z6WP4.js → chunk-2IVEVG5N.js} +5 -1
- package/dist/lib/{chunk-XFSN4LMA.js → chunk-3FBS5NB7.js} +71 -18
- package/dist/lib/{chunk-GNI7YX6F.js → chunk-DJ6AILPN.js} +70 -24
- package/dist/lib/chunk-GCY44T7S.js +2346 -0
- package/dist/lib/{chunk-PZ42NAFA.js → chunk-KG4MJGKQ.js} +19 -21
- package/dist/lib/{chunk-2AUSNVIB.js → chunk-KZSBR4FO.js} +44 -37
- package/dist/lib/chunk-PIL7M52F.js +127 -0
- package/dist/lib/chunk-Z4CYHTII.js +392 -0
- package/dist/lib/index.d.ts +227 -132
- package/dist/lib/index.js +2195 -1602
- package/dist/lib/{openai-VX5VBXZ4.js → openai-PBF7EWNJ.js} +2 -3
- package/dist/lib/{tool-filter-VF7TZRE5.js → tool-filter-R6HDDJHE.js} +1 -1
- package/dist/main.js +203 -194
- package/dist/{openai-5FDSLJNM.js → openai-QQ2K7GPF.js} +14 -14
- package/dist/{server-F666APPN.js → server-G23HCFLI.js} +21 -21
- package/dist/{tool-filter-NO7I3HFD.js → tool-filter-VBP6WOGO.js} +1 -1
- package/package.json +2 -2
- package/dist/agent-OMC6YIMW.js +0 -4
- package/dist/anthropic-T5K4BQPB.js +0 -4
- package/dist/checker-TOQVEG3P.js +0 -4
- package/dist/chunk-5I2WXSGV.js +0 -90
- package/dist/chunk-A2VR3BC4.js +0 -4
- package/dist/chunk-A57FEYSN.js +0 -4
- package/dist/chunk-BRC5L644.js +0 -130
- package/dist/chunk-FZPTNGTU.js +0 -4
- package/dist/chunk-G6YIQW3Z.js +0 -238
- package/dist/chunk-I2XG2PXV.js +0 -4
- package/dist/chunk-LVSHXIBK.js +0 -389
- package/dist/chunk-SULWNDNC.js +0 -4
- package/dist/chunk-WO6DL7OB.js +0 -35
- package/dist/lib/agent-U7MIUCKT.js +0 -9
- package/dist/lib/chunk-2QILP24G.js +0 -1120
- package/dist/lib/chunk-3LU4APFB.js +0 -339
- package/dist/lib/chunk-OO2CLOEE.js +0 -88
- 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
|
|
71
|
+
Swifty reads a single global YAML configuration file:
|
|
72
72
|
|
|
73
|
-
|
|
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-
|
|
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 |
|
|
122
|
-
| context_window | no |
|
|
123
|
-
| max_output_tokens | no |
|
|
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
|
|
150
|
-
swifty --remote :9000
|
|
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
|
-
| /
|
|
162
|
-
| /
|
|
163
|
-
| /
|
|
164
|
-
| /memory
|
|
165
|
-
| /
|
|
166
|
-
| /skills
|
|
167
|
-
| /
|
|
168
|
-
| /
|
|
169
|
-
| /
|
|
170
|
-
| /
|
|
171
|
-
| /
|
|
172
|
-
| /
|
|
173
|
-
| /
|
|
174
|
-
| /
|
|
175
|
-
| /
|
|
176
|
-
| /
|
|
177
|
-
| /
|
|
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
|
-
|
|
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-
|
|
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};
|