@swifty.js/swifty 0.0.1 → 0.0.2-alpha
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 +302 -0
- package/dist/anthropic-RNEEF6RX.js +4 -0
- package/dist/chunk-MQ5XOYLD.js +4 -0
- package/dist/{chunk-Z3E5YV3P.js → chunk-SLGPWJQF.js} +3 -3
- package/dist/chunk-YUOKPBUW.js +526 -0
- package/dist/{cleanup-4R3534Z3.js → cleanup-BQJDUOKA.js} +1 -1
- package/dist/glob_addon.node +0 -0
- package/dist/main.js +233 -233
- package/dist/openai-BUFKIEZO.js +26 -0
- package/dist/{server-XPKSROM2.js → server-4FIOX5PK.js} +17 -17
- package/package.json +17 -24
- package/dist/anthropic-737END4X.js +0 -4
- package/dist/chunk-NCPOPA4A.js +0 -4
- package/dist/chunk-SC34YHMX.js +0 -521
- package/dist/openai-4KX74QBZ.js +0 -27
- package/docs/ch1.md +0 -25
- package/docs/ch10.md +0 -122
- package/docs/ch11.md +0 -163
- package/docs/ch12.md +0 -289
- package/docs/ch13.md +0 -320
- package/docs/ch14.md +0 -152
- package/docs/ch15.md +0 -547
- package/docs/ch2.md +0 -273
- package/docs/ch3.md +0 -206
- package/docs/ch4.md +0 -125
- package/docs/ch5.md +0 -165
- package/docs/ch6.md +0 -201
- package/docs/ch7.md +0 -448
- package/docs/ch8.md +0 -217
- package/docs/ch9.md +0 -351
- package/docs/index.css +0 -23
- package/docs/index.md +0 -21
- package/docs/swifty.mdx +0 -7
package/README.md
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# Swifty
|
|
2
|
+
|
|
3
|
+
Swifty is a terminal-based AI coding agent. It provides an interactive TUI (terminal user interface) for conversing with large language models, executing code, manipulating files, and orchestrating multi-agent workflows, all from the command line.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Swifty runs as a single CLI binary that connects to configurable LLM providers (Anthropic, OpenAI, or any OpenAI-compatible endpoint). It renders a rich terminal interface using React and Ink, giving you streaming responses, tool execution feedback, permission prompts, and slash commands in a single pane.
|
|
8
|
+
|
|
9
|
+
Beyond interactive use, Swifty supports a non-interactive print mode for scripting, a remote mode that serves a browser-based chat UI over WebSocket, and a teammate mode that lets one lead agent coordinate multiple subagents working in parallel.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
### Core Capabilities
|
|
14
|
+
|
|
15
|
+
- Multi-provider LLM support with Anthropic, OpenAI, and OpenAI-compatible protocols
|
|
16
|
+
- Interactive terminal UI with streaming text, thinking indicators, and tool execution display
|
|
17
|
+
- Built-in tool set: ReadFile, WriteFile, EditFile, Bash, Glob, Grep, ToolSearch, EnterWorktree, ExitWorktree, ExitPlanMode
|
|
18
|
+
- MCP (Model Context Protocol) server integration for extending the tool set with external services
|
|
19
|
+
- Permission system with four modes: default, acceptEdits, plan (read-only), and bypassPermissions
|
|
20
|
+
- Sandbox support via bwrap (Linux) and seatbelt (macOS) for isolated command execution
|
|
21
|
+
- Dangerous command pattern detection with human-in-the-loop approval dialogs
|
|
22
|
+
|
|
23
|
+
### Conversation and Memory
|
|
24
|
+
|
|
25
|
+
- Session persistence with JSONL-based storage for cross-session resume
|
|
26
|
+
- Automatic context compaction when conversations approach the model's context window
|
|
27
|
+
- Long-term memory extraction and recall across sessions
|
|
28
|
+
- Instructions file support for persistent project-level guidance
|
|
29
|
+
|
|
30
|
+
### Skills and Commands
|
|
31
|
+
|
|
32
|
+
- Skill catalog with three-tier loading: built-in, user-global (~/.swifty/skills/), and project-level (.swifty/skills/)
|
|
33
|
+
- Hot-reload support for skills edited on disk
|
|
34
|
+
- Inline and fork execution modes for skills
|
|
35
|
+
- Slash command system with built-in commands and user-defined commands from .swifty/commands/
|
|
36
|
+
- Skill installation from URLs
|
|
37
|
+
|
|
38
|
+
### Agent Orchestration
|
|
39
|
+
|
|
40
|
+
- Subagent spawning with built-in agent types: general-purpose, plan (read-only architect), explore (read-only code explorer)
|
|
41
|
+
- Team coordination with file-based mailboxes and lead/member communication
|
|
42
|
+
- Coordinator mode for managing multi-agent workflows
|
|
43
|
+
- Git worktree isolation for parallel agent tasks
|
|
44
|
+
|
|
45
|
+
### Hooks
|
|
46
|
+
|
|
47
|
+
- Event-driven hook engine supporting: session_start, session_end, turn_start, turn_end, pre_send, post_receive, pre_tool_use, post_tool_use, shutdown
|
|
48
|
+
- Hook actions: shell commands, HTTP requests, prompt injection
|
|
49
|
+
- Conditional execution, reject-on-failure, and async options
|
|
50
|
+
|
|
51
|
+
### Remote Mode
|
|
52
|
+
|
|
53
|
+
- Koa HTTP server with WebSocket bridge for browser-based access
|
|
54
|
+
- React frontend served at a configurable address
|
|
55
|
+
- Bidirectional message streaming between browser and agent
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install -g @swifty.js/swifty
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Or run directly from the monorepo:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pnpm dev
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Configuration
|
|
70
|
+
|
|
71
|
+
Swifty reads YAML configuration files from multiple locations, merged in order:
|
|
72
|
+
|
|
73
|
+
1. ~/.swifty/config.yml or ~/.swifty/config.yaml
|
|
74
|
+
2. .swifty/config.yml or .swifty/config.yaml (project root)
|
|
75
|
+
3. .swifty/config.local.yml or .swifty/config.local.yaml (project root, gitignored)
|
|
76
|
+
|
|
77
|
+
At least one provider must be configured. Example config.yml:
|
|
78
|
+
|
|
79
|
+
```yaml
|
|
80
|
+
providers:
|
|
81
|
+
- name: anthropic
|
|
82
|
+
protocol: anthropic
|
|
83
|
+
base_url: https://api.anthropic.com
|
|
84
|
+
model: claude-sonnet-4-20250514
|
|
85
|
+
# api_key defaults to $ANTHROPIC_API_KEY
|
|
86
|
+
|
|
87
|
+
permission_mode: default
|
|
88
|
+
|
|
89
|
+
mcp_servers:
|
|
90
|
+
- name: my-server
|
|
91
|
+
command: npx
|
|
92
|
+
args: ["-y", "my-mcp-server"]
|
|
93
|
+
|
|
94
|
+
hooks:
|
|
95
|
+
- event: pre_tool_use
|
|
96
|
+
condition: "Bash"
|
|
97
|
+
action:
|
|
98
|
+
type: command
|
|
99
|
+
command: "echo tool about to run"
|
|
100
|
+
|
|
101
|
+
sandbox:
|
|
102
|
+
enabled: false
|
|
103
|
+
auto_allow: false
|
|
104
|
+
network_enabled: true
|
|
105
|
+
|
|
106
|
+
enable_coordinator_mode: false
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Provider fields:
|
|
110
|
+
|
|
111
|
+
| Field | Required | Description |
|
|
112
|
+
| ----------------- | -------- | -------------------------------------------------------------------- |
|
|
113
|
+
| name | yes | Display name for the provider |
|
|
114
|
+
| protocol | yes | One of: anthropic, openai, openai-compat |
|
|
115
|
+
| base_url | yes | API base URL |
|
|
116
|
+
| model | yes | Model identifier |
|
|
117
|
+
| api_key | no | API key (falls back to environment variable) |
|
|
118
|
+
| thinking | no | Enable extended thinking mode (increases max_output_tokens to 64000) |
|
|
119
|
+
| context_window | no | Override auto-detected context window size |
|
|
120
|
+
| max_output_tokens | no | Override default max output tokens |
|
|
121
|
+
|
|
122
|
+
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).
|
|
123
|
+
|
|
124
|
+
## Usage
|
|
125
|
+
|
|
126
|
+
### Interactive TUI Mode
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
swifty
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Launches the terminal interface. If multiple providers are configured, a provider selection screen appears first.
|
|
133
|
+
|
|
134
|
+
### Print Mode (Non-Interactive)
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
swifty -p "explain this codebase"
|
|
138
|
+
swifty -p "fix the failing test" --output-format stream-json
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The -p flag sends a single prompt, runs the agent loop, and prints the result to stdout. Useful for scripting and CI pipelines.
|
|
142
|
+
|
|
143
|
+
### Remote Mode (Browser UI)
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
swifty --remote # listens on :18888
|
|
147
|
+
swifty --remote :9000 # custom address
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Starts a Koa HTTP server and WebSocket bridge. The bundled React frontend is served at the configured address for browser-based interaction.
|
|
151
|
+
|
|
152
|
+
### Slash Commands
|
|
153
|
+
|
|
154
|
+
Inside the TUI, these commands are available:
|
|
155
|
+
|
|
156
|
+
| Command | Description |
|
|
157
|
+
| ----------------------- | ---------------------------------------------------------------------------------- |
|
|
158
|
+
| /status | Show current session status (model, tokens, tools, sandbox, memories, skills, MCP) |
|
|
159
|
+
| /permission mode <mode> | Change permission mode (default, acceptEdits, plan, bypassPermissions) |
|
|
160
|
+
| /memory | List stored memories |
|
|
161
|
+
| /memory clear | Clear all memories |
|
|
162
|
+
| /skills | List available skills |
|
|
163
|
+
| /skills reload | Hot-reload skills from disk |
|
|
164
|
+
| /skill <name> [args] | Run a skill by name |
|
|
165
|
+
| /plan | Enter plan mode (read-only investigation) |
|
|
166
|
+
| /do | Exit plan mode and execute the approved plan |
|
|
167
|
+
| /compact | Force conversation compaction |
|
|
168
|
+
| /clear | Reset the session and clear the terminal |
|
|
169
|
+
| /resume [id] | List or restore a previous session |
|
|
170
|
+
| /rewind | Open checkpoint rewind dialog |
|
|
171
|
+
| /sandbox [1/2/3] | Configure sandbox (1=on+auto, 2=on+manual, 3=off) |
|
|
172
|
+
| /worktree | List git worktrees |
|
|
173
|
+
| /mcp | Show MCP server status |
|
|
174
|
+
| /quit | Exit the application |
|
|
175
|
+
|
|
176
|
+
### Keyboard Shortcuts
|
|
177
|
+
|
|
178
|
+
| Key | Action |
|
|
179
|
+
| --------- | -------------------------------------------------------------------- |
|
|
180
|
+
| Ctrl+C | Interrupt streaming (first press), exit app (second press within 2s) |
|
|
181
|
+
| Ctrl+O | Toggle full vs. truncated tool output |
|
|
182
|
+
| Ctrl+T | Toggle Teams dialog overlay |
|
|
183
|
+
| Shift+Tab | Cycle permission modes |
|
|
184
|
+
|
|
185
|
+
## Development
|
|
186
|
+
|
|
187
|
+
Requires Node.js 20+ and pnpm 10+.
|
|
188
|
+
|
|
189
|
+
### Scripts
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
pnpm dev # Run in development mode via tsx
|
|
193
|
+
pnpm build # Build production bundle with tsup (runs prebuild first)
|
|
194
|
+
pnpm test # Run tests with Vitest
|
|
195
|
+
pnpm test:watch # Run tests in watch mode
|
|
196
|
+
pnpm lint # Lint source with ESLint
|
|
197
|
+
pnpm lint:fix # Lint and auto-fix
|
|
198
|
+
pnpm format # Format with oxfmt
|
|
199
|
+
pnpm typecheck # Type-check with tsc --noEmit
|
|
200
|
+
|
|
201
|
+
pnpm dev:docs # Run documentation site (Rspress)
|
|
202
|
+
pnpm build:docs # Build documentation
|
|
203
|
+
pnpm preview:docs # Preview built documentation
|
|
204
|
+
|
|
205
|
+
pnpm fe:dev # Run remote frontend in dev mode (Rsbuild)
|
|
206
|
+
pnpm fe:build # Build remote frontend
|
|
207
|
+
pnpm fe:preview # Preview built frontend
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Build Output
|
|
211
|
+
|
|
212
|
+
The tsup build produces a single ESM bundle at dist/main.js with a #!/usr/bin/env node shebang. The prebuild step compiles the glob-wasm module and the glob-addon native binary. These are copied into dist/ along with built-in skills:
|
|
213
|
+
|
|
214
|
+
- dist/main.js: CLI entry point
|
|
215
|
+
- dist/release.wasm: WASM glob matcher
|
|
216
|
+
- dist/glob_addon.node: Native C++ addon for glob (platform-specific)
|
|
217
|
+
- dist/builtin/: Built-in skill markdown files
|
|
218
|
+
|
|
219
|
+
### Testing
|
|
220
|
+
|
|
221
|
+
Tests live in tests/ and use Vitest with V8 coverage. The test timeout is set to 30 seconds.
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
pnpm test
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Dependencies and Tech Stack
|
|
228
|
+
|
|
229
|
+
### Runtime
|
|
230
|
+
|
|
231
|
+
| Package | Purpose |
|
|
232
|
+
| ------------------------- | -------------------------------------------- |
|
|
233
|
+
| @anthropic-ai/sdk | Anthropic Claude API client |
|
|
234
|
+
| openai | OpenAI API client |
|
|
235
|
+
| @modelcontextprotocol/sdk | MCP protocol support |
|
|
236
|
+
| ink + react | Terminal UI framework |
|
|
237
|
+
| zod | Schema validation (config, session, hooks) |
|
|
238
|
+
| js-yaml | YAML config parsing |
|
|
239
|
+
| koa + ws | Remote mode HTTP server and WebSocket bridge |
|
|
240
|
+
| pino | Structured logging |
|
|
241
|
+
| marked + dompurify | Markdown rendering and sanitization |
|
|
242
|
+
| fuse.js | Fuzzy search for tool/command lookup |
|
|
243
|
+
| chalk | Terminal color output |
|
|
244
|
+
|
|
245
|
+
### Build Toolchain
|
|
246
|
+
|
|
247
|
+
| Tool | Purpose |
|
|
248
|
+
| -------------------------- | ------------------------------------- |
|
|
249
|
+
| tsup | Bundle to single ESM file for Node.js |
|
|
250
|
+
| tsx | Development runner |
|
|
251
|
+
| typescript | Type checking |
|
|
252
|
+
| vitest | Testing |
|
|
253
|
+
| eslint + typescript-eslint | Linting |
|
|
254
|
+
| oxfmt | Code formatting |
|
|
255
|
+
| rspress | Documentation site |
|
|
256
|
+
| rsbuild | Remote frontend bundler |
|
|
257
|
+
| biome | Remote frontend linter/formatter |
|
|
258
|
+
| tailwindcss | Remote frontend styling |
|
|
259
|
+
|
|
260
|
+
## Project Structure
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
apps/swifty/
|
|
264
|
+
src/
|
|
265
|
+
main.tsx CLI entry point (TUI, remote, print, teammate routing)
|
|
266
|
+
agent/ Agent loop (ReAct pattern, streaming executor)
|
|
267
|
+
compact/ Context window compaction
|
|
268
|
+
commands/ Slash command registry and loader
|
|
269
|
+
config/ YAML config loading and validation
|
|
270
|
+
conversation/ Message management and conversation state
|
|
271
|
+
file-history/ File snapshot checkpoints for rewind
|
|
272
|
+
hooks/ Event-driven hook engine
|
|
273
|
+
llm/ LLM clients (Anthropic, OpenAI, OpenAI-compatible)
|
|
274
|
+
logger/ Structured logging (pino)
|
|
275
|
+
mcp/ MCP client, manager, and tool wrapper
|
|
276
|
+
memory/ Long-term memory extraction, consolidation, recall
|
|
277
|
+
permissions/ Permission checker with dangerous pattern detection
|
|
278
|
+
plan-file/ Plan mode file management
|
|
279
|
+
print-mode/ Non-interactive print mode (-p flag)
|
|
280
|
+
prompt/ System prompt builder and environment detection
|
|
281
|
+
remote/
|
|
282
|
+
server.ts Koa + WebSocket remote server
|
|
283
|
+
fe/ React frontend for remote mode (Rsbuild)
|
|
284
|
+
sandbox/ Sandbox implementations (bwrap, seatbelt)
|
|
285
|
+
session/ Session persistence and resume
|
|
286
|
+
skills/ Skill catalog, executor, load/install tools
|
|
287
|
+
subagent/ Subagent spawning and task management
|
|
288
|
+
teams/ Team coordination, file mailboxes, progress tracking
|
|
289
|
+
teammate.ts Teammate process entry point
|
|
290
|
+
todo/ Task list management tools
|
|
291
|
+
tool-result/ Tool output budgeting and reconstruction
|
|
292
|
+
tools/ Built-in tools (ReadFile, Bash, Glob, Grep, etc.)
|
|
293
|
+
tui/ Terminal UI components (Ink/React)
|
|
294
|
+
utils/ Shared utilities
|
|
295
|
+
worktree/ Git worktree management
|
|
296
|
+
tests/ Vitest test suite (25 test files)
|
|
297
|
+
docs/ Rspress documentation (15 chapters)
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## License
|
|
301
|
+
|
|
302
|
+
ISC
|
|
@@ -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{g as v}from"./chunk-H7VXSMJR.js";import{$,L as w,N as P,O as S,P as I,Q as k,R as N,X as O,Y as M,Z as j,_ as U,aa as K,ca as B,da as R,e as L,p as E}from"./chunk-SLGPWJQF.js";import"./chunk-MQ5XOYLD.js";import"./chunk-X3UA5OZL.js";var x=L({module:"llm"});var G=3e3,H=w.object({max_input_tokens:w.coerce.number()});async function tt(o){if(o.protocol!=="anthropic")return 0;let t=R(o),n=`${o.base_url.replace(/\/+$/,"")}/v1/models/${encodeURIComponent(o.model)}`,r=new AbortController,a=setTimeout(()=>{r.abort()},G);try{let l=await fetch(n,{method:"GET",headers:{"anthropic-version":"2023-06-01",...t?{"x-api-key":t}:{}},signal:r.signal});if(!l.ok)return 0;let g=await l.json(),{success:h,error:d,data:f}=await E(H,g);if(!h)return console.error(d.message),0;let y=f.max_input_tokens;return Math.max(y,0)}catch(l){return console.error(l),0}finally{clearTimeout(a)}}function Y(){return!0}function q(o){let t=[];for(let e of o)if(e.role==="assistant"){let n=[];if(e.thinkingBlocks)for(let r of e.thinkingBlocks)n.push({type:"thinking",thinking:r.thinking,signature:r.signature});if(e.content&&n.push({type:"text",text:e.content}),e.toolUses)for(let r of e.toolUses)n.push({type:"tool_use",id:r.toolUseId,name:r.toolName,input:r.arguments});n.length===0&&n.push({type:"text",text:""}),t.push({role:"assistant",content:n})}else if(e.toolResults&&e.toolResults.length>0){let n=[];for(let r of e.toolResults)n.push({type:"tool_result",tool_use_id:r.toolUseId,is_error:r.isError,content:r.content});t.push({role:"user",content:n})}else{if(t.length===0){t.push({role:"user",content:[{type:"text",text:e.content}]});continue}let n=!1,r=t[t.length-1],a=r.content;r.role==="user"&&(typeof a=="string"||Array.isArray(a)&&a.length>0&&a[0].type==="text")&&(n=!0),n?(typeof a=="string"&&(a=r.content=a.trim().length>0?[{type:"text",text:a}]:[]),a.push({type:"text",text:e.content})):t.push({role:"user",content:[{type:"text",text:e.content}]})}return t}var W=class{client;model;thinking;systemPrompt;maxOutputTokens;contextWindow;constructor(t,e){let n=R(t);if(!n)throw new M("Anthropic API key not found, set ANTHROPIC_API_KEY in .swifty/config.y(a)ml, or via ANTHROPIC_API_KEY env variable.");this.client=new v({apiKey:n,baseURL:t.base_url}),this.model=t.model,this.thinking=t.thinking??!0,this.systemPrompt=e,this.maxOutputTokens=B(t),this.contextWindow=K(t)}setSystemPrompt(t){this.systemPrompt=t}setMaxOutputTokens(t){this.maxOutputTokens=t}async*stream(t,e,n){let r=q(t.getMessages()),a=e.map(c=>{let p=c.input_schema;return{name:c.name,description:c.description,input_schema:{type:"object",properties:p.properties,required:p.required??[]}}});a.length>0&&(a[a.length-1].cache_control={type:"ephemeral"}),D(r);let l={model:this.model,max_tokens:this.maxOutputTokens,stream:!0,system:[{type:"text",text:this.systemPrompt,cache_control:{type:"ephemeral"}}],messages:r,...a.length>0?{tools:a}:{}};this.thinking?Y()&&(l.thinking={type:"enabled",budget_tokens:this.maxOutputTokens-1}):l.thinking={type:"enabled",budget_tokens:this.maxOutputTokens-1};let g=0,h=0,d=0,f=0,y="end_turn",b="",T="",A=!1,C=0;try{let c=this.client.messages.stream(l,{...n?{signal:n}:{}}),p="",_="",u="";for await(let i of c)switch(i.type){case"content_block_start":{let s=i.content_block;s.type==="thinking"?(A=!0,b="",T=""):s.type==="tool_use"&&(_=s.id,p=s.name,u="",yield{type:"tool_call_start",toolName:p,toolId:_});break}case"content_block_delta":{let s=i.delta;s.type==="thinking_delta"?(b+=s.thinking,yield{type:"thinking_delta",text:s.thinking}):s.type==="signature_delta"?(x.debug({signature:s.signature},"thinking signature received"),T=s.signature):s.type==="text_delta"?yield{type:"text_delta",text:s.text}:s.type==="input_json_delta"&&(u+=s.partial_json,yield{type:"tool_call_delta",text:s.partial_json});break}case"content_block_stop":{if(A&&(yield{type:"thinking_complete",thinking:b,signature:T},A=!1),p){let s={};if(u)try{let m=JSON.parse(u);s=S(m)?I(m):{[P]:u}}catch(m){x.error({err:m},"llm operation failed"),s={[P]:u}}yield{type:"tool_call_complete",toolId:_,toolName:p,arguments:s},p="",_="",u=""}break}case"message_delta":{i.delta.stop_reason&&(y=i.delta.stop_reason),i.usage.output_tokens&&(h=i.usage.output_tokens,i.usage.input_tokens&&(g=i.usage.input_tokens),i.usage.cache_read_input_tokens&&(d=i.usage.cache_read_input_tokens),i.usage.cache_creation_input_tokens&&(f=i.usage.cache_creation_input_tokens));break}case"message_start":{C=performance.now(),g=i.message.usage.input_tokens,h=i.message.usage.output_tokens,d=i.message.usage.cache_read_input_tokens??0,f=i.message.usage.cache_creation_input_tokens??0;break}case"message_stop":{let m=performance.now()-C;x.debug({elapsedMs:m},"message stream complete");break}}yield{type:"stream_end",stopReason:y,usage:{inputTokens:g,outputTokens:h,cacheReadInputTokens:d,cacheCreationInputTokens:f}}}catch(c){throw x.error({err:c},"llm operation failed"),F(c)}}};function D(o){for(let t=o.length-1;t>=0;t--){if(o[t].role!=="user")continue;let e=o[t].content;if(typeof e=="string"&&e.length===0||Array.isArray(e)&&e.length===0)return;typeof e=="string"&&(e=o[t].content=[{type:"text",text:e}]);let n=e[e.length-1];Reflect.set(n,"cache_control",{type:"ephemeral"})}}function F(o){if(o instanceof v.APIError){if(o.status===413||/prompts?\s+too\s+long/i.test(o.message))return new $(`Prompt too long: ${o.message}`);if(o.status===401)return new M(`Invalid API key: ${o.message}`);if(o.status===429){let t=I(o.headers)["retry-after"],e="Rate Limited";if(t){let n=Number.parseInt(k(t));Number.isNaN(n)&&(e+=", please wait."),e+=`, retry after ${k(n)}s.`}else e+=", please wait.";return new j(e,t?k(t):void 0)}return new O(`Anthropic API error (${k(o.status)}): ${o.message}`)}return new U(`Network error: ${N(o)}`)}export{W as AnthropicClient,q as buildAnthropicMessages,tt as fetchModelContextWindow};
|
|
@@ -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{readdir as c,stat as l,unlink as u,access as w}from"fs/promises";import{join as s}from"path";var y=30,g=y*24*60*60*1e3;async function f(t){try{return await w(t),!0}catch{return!1}}async function o(t){if(!await f(t))return 0;let n;try{n=(await c(t)).filter(a=>a.endsWith(".jsonl"))}catch{return 0}let r=Date.now(),e=0;for(let a of n){let i=s(t,a);try{let m=await l(i);r-m.mtimeMs>g&&(await u(i),e++)}catch{}}return e}async function p(t){let n=0;n+=await o(s(t,".swifty","logs"));let r=s(t,".swifty","teams");if(!await f(r))return n;let e;try{e=await c(r)}catch{return n}for(let a of e)n+=await o(s(r,a,"logs"));return n}export{p as a};
|