@swifty.js/swifty 0.0.2-alpha → 0.0.2

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 CHANGED
@@ -181,122 +181,3 @@ Inside the TUI, these commands are available:
181
181
  | Ctrl+O | Toggle full vs. truncated tool output |
182
182
  | Ctrl+T | Toggle Teams dialog overlay |
183
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{q as a}from"./chunk-F6HLYUZ4.js";import"./chunk-FZPTNGTU.js";import"./chunk-4KVSJNS6.js";import"./chunk-MQ5XOYLD.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{g as P}from"./chunk-H7VXSMJR.js";import{f as B,h as W,i as R}from"./chunk-RD3MICOU.js";import{b as O,c as j,d as M,e as U,f as $,g as K}from"./chunk-FZPTNGTU.js";import"./chunk-7MHXMDYC.js";import{H as w,J as v,K as S,L as I,M as k,N,e as L,l as E}from"./chunk-4KVSJNS6.js";import"./chunk-MQ5XOYLD.js";import"./chunk-X3UA5OZL.js";var x=L({module:"llm"});var H=3e3,Y=w.object({max_input_tokens:w.coerce.number()});async function ot(n){if(n.protocol!=="anthropic")return 0;let t=R(n),o=`${n.base_url.replace(/\/+$/,"")}/v1/models/${encodeURIComponent(n.model)}`,r=new AbortController,a=setTimeout(()=>{r.abort()},H);try{let l=await fetch(o,{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:f,data:d}=await E(Y,g);if(!h)return console.error(f.message),0;let y=d.max_input_tokens;return Math.max(y,0)}catch(l){return console.error(l),0}finally{clearTimeout(a)}}function q(){return!0}function D(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});if(e.content&&o.push({type:"text",text:e.content}),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 r of e.toolResults)o.push({type:"tool_result",tool_use_id:r.toolUseId,is_error:r.isError,content:r.content});t.push({role:"user",content:o})}else{if(t.length===0){t.push({role:"user",content:[{type:"text",text:e.content}]});continue}let o=!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")&&(o=!0),o?(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 G=class{client;model;thinking;systemPrompt;maxOutputTokens;contextWindow;constructor(t,e){let o=R(t);if(!o)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 P({apiKey:o,baseURL:t.base_url}),this.model=t.model,this.thinking=t.thinking??!0,this.systemPrompt=e,this.maxOutputTokens=W(t),this.contextWindow=B(t)}setSystemPrompt(t){this.systemPrompt=t}setMaxOutputTokens(t){this.maxOutputTokens=t}async*stream(t,e,o){let r=D(O(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"}),F(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?q()&&(l.thinking={type:"enabled",budget_tokens:this.maxOutputTokens-1}):l.thinking={type:"enabled",budget_tokens:this.maxOutputTokens-1};let g=0,h=0,f=0,d=0,y="end_turn",b="",T="",A=!1,C=0;try{let c=this.client.messages.stream(l,{...o?{signal:o}:{}}),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):{[v]:u}}catch(m){x.error({err:m},"llm operation failed"),s={[v]: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&&(f=i.usage.cache_read_input_tokens),i.usage.cache_creation_input_tokens&&(d=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,f=i.message.usage.cache_read_input_tokens??0,d=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:f,cacheCreationInputTokens:d}}}catch(c){throw x.error({err:c},"llm operation failed"),J(c)}}};function F(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];Reflect.set(o,"cache_control",{type:"ephemeral"})}}function J(n){if(n instanceof P.APIError){if(n.status===413||/prompts?\s+too\s+long/i.test(n.message))return new K(`Prompt too long: ${n.message}`);if(n.status===401)return new M(`Invalid API key: ${n.message}`);if(n.status===429){let t=I(n.headers)["retry-after"],e="Rate Limited";if(t){let o=Number.parseInt(k(t));Number.isNaN(o)&&(e+=", please wait."),e+=`, retry after ${k(o)}s.`}else e+=", please wait.";return new U(e,t?k(t):void 0)}return new j(`Anthropic API error (${k(n.status)}): ${n.message}`)}return new $(`Network error: ${N(n)}`)}export{G as AnthropicClient,D as buildAnthropicMessages,ot 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{a,b,c,d}from"./chunk-6ARDOHBL.js";import"./chunk-7MHXMDYC.js";import"./chunk-4KVSJNS6.js";import"./chunk-MQ5XOYLD.js";import"./chunk-X3UA5OZL.js";export{b as PathSandbox,d as PermissionChecker,c as RuleEngine,a as extractContent};