@swifty.js/swifty 0.0.28 → 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 +39 -37
- package/dist/{agent-3NON2EXE.js → agent-YR6YK26C.js} +1 -1
- package/dist/anthropic-5GRR5JCT.js +4 -0
- package/dist/chunk-LIMEBKCY.js +408 -0
- package/dist/{chunk-LZBOXJX2.js → chunk-POHIQUFP.js} +25 -25
- package/dist/{chunk-JGSIQXEN.js → chunk-QFSCPD63.js} +1 -1
- package/dist/chunk-TMEX7RFA.js +4 -0
- package/dist/glob.wasm +0 -0
- package/dist/lib/{agent-XYO2MXXK.js → agent-5WEKVRTD.js} +3 -3
- package/dist/lib/{anthropic-TGJGAP5R.js → anthropic-KWO3KLNV.js} +3 -3
- package/dist/lib/{chunk-P4F46PEF.js → chunk-3FBS5NB7.js} +30 -6
- package/dist/lib/{chunk-DV6RV5WU.js → chunk-GCY44T7S.js} +7 -6
- package/dist/lib/{chunk-ALJAVRRX.js → chunk-KZSBR4FO.js} +22 -9
- package/dist/lib/{chunk-IB5IQK2S.js → chunk-PIL7M52F.js} +2 -11
- package/dist/lib/{chunk-FBSPZQGC.js → chunk-Z4CYHTII.js} +69 -69
- package/dist/lib/glob.wasm +0 -0
- package/dist/lib/index.d.ts +84 -11
- package/dist/lib/index.js +273 -143
- package/dist/lib/{openai-5UCIIDPO.js → openai-PBF7EWNJ.js} +2 -2
- package/dist/main.js +163 -163
- package/dist/{openai-YLS2LUAI.js → openai-QQ2K7GPF.js} +14 -14
- package/dist/{server-GX72MJQF.js → server-G23HCFLI.js} +16 -16
- package/package.json +2 -2
- package/dist/anthropic-BJ5GN2VT.js +0 -4
- package/dist/chunk-R63ASIIW.js +0 -407
- package/dist/chunk-WX3B64R4.js +0 -4
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 | Context window in tokens (default: 1000000; no model-name inference)
|
|
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,9 +135,9 @@ swifty
|
|
|
134
135
|
|
|
135
136
|
Launches the terminal interface. If multiple providers are configured, a provider selection screen appears first.
|
|
136
137
|
|
|
137
|
-
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 thinking, Enter to save, and Esc to cancel. Duplicate names receive numeric suffixes (`name2`, `name3`, …).
|
|
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`, …).
|
|
138
139
|
|
|
139
|
-
The form saves to
|
|
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.
|
|
140
141
|
|
|
141
142
|
### Print Mode (Non-Interactive)
|
|
142
143
|
|
|
@@ -161,26 +162,27 @@ Starts a Koa HTTP server and WebSocket bridge. The bundled React frontend is ser
|
|
|
161
162
|
|
|
162
163
|
Inside the TUI, these commands are available:
|
|
163
164
|
|
|
164
|
-
| Command | Description
|
|
165
|
-
| ----------------------- |
|
|
166
|
-
| /login | Configure, save, and activate an LLM provider
|
|
167
|
-
| /status | Show current session status (model, tokens, tools, sandbox, memories, skills, MCP)
|
|
168
|
-
| /permission mode <mode> | Change permission mode (default, acceptEdits, plan, bypassPermissions)
|
|
169
|
-
| /memory | List stored memories
|
|
170
|
-
| /memory clear | Clear all memories
|
|
171
|
-
| /skills | List available skills
|
|
172
|
-
| /skills reload | Hot-reload skills from disk
|
|
173
|
-
| /skill <name> [args] | Run a skill by name
|
|
174
|
-
| /plan | Enter plan mode (read-only investigation)
|
|
175
|
-
| /do | Exit plan mode and execute the approved plan
|
|
176
|
-
| /compact | Force conversation compaction
|
|
177
|
-
| /clear | Reset the session and clear the terminal
|
|
178
|
-
| /resume [id] | List or restore a previous session
|
|
179
|
-
| /rewind | Open checkpoint rewind dialog
|
|
180
|
-
| /sandbox [1/2/3] | Configure sandbox (1=on+auto, 2=on+manual, 3=off)
|
|
181
|
-
| /worktree | List git worktrees
|
|
182
|
-
| /mcp | Show MCP server status
|
|
183
|
-
| /
|
|
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 |
|
|
184
186
|
|
|
185
187
|
### Keyboard Shortcuts
|
|
186
188
|
|
|
@@ -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{
|
|
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};
|