@fifthrevision/axle-cli 0.19.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Chong Han Chua
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { };
package/dist/cli.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import{t as e}from"./memory-BmDsikXS.js";import{t}from"./store-BFxiiKeG.js";import{a as n,i as r,n as i,r as a,t as o}from"./tools-Cp5kiYmz.js";import{Command as s}from"@commander-js/extra-typings";import{Agent as c,Anthropic as l,Gemini as u,Instruct as d,MCP as f,OpenAI as p,SimpleWriter as m,Tracer as h,addStats as g,anthropic as _,chatCompletions as v,createAgentConfig as y,createStats as ee,gemini as te,loadFileContent as b,openai as x}from"@fifthrevision/axle";import{createHash as ne}from"node:crypto";import{z as S}from"zod";import{appendFile as re,mkdir as ie,readFile as C}from"node:fs/promises";import{basename as ae,dirname as oe,extname as w}from"node:path";import{config as se}from"dotenv";import T from"yaml";import{glob as ce}from"glob";import{createInterface as le}from"node:readline";var ue=`0.19.0`;async function de(e,t){let n=[];for(let r of e){let e=new f(r);await e.connect({tracer:t}),n.push(e)}return n}async function fe(e,t){for(let n of e)try{await n.close({tracer:t})}catch{}}function pe(e,t){switch(e){case`calculator`:return n;case`exec`:{let e=t?.exec;return e&&r.configure(e),r}case`patch-file`:return a;case`read-file`:return i;case`write-file`:return o;default:throw Error(`Unknown tool: ${e}`)}}function E(e,t){return e.map(e=>pe(e,t))}function D(e,t){let n=e.config??{};switch(e.type){case`openai`:{let e={...t.openai,...n},r=O(e);if(!r)throw Error(`The provider openai is not configured. Please check your configuration.`);return{provider:x(r),model:e.model||p.DefaultModel}}case`anthropic`:{let e={...t.anthropic,...n},r=O(e);if(!r)throw Error(`The provider anthropic is not configured. Please check your configuration.`);return{provider:_(r),model:e.model||l.DefaultModel}}case`gemini`:{let e={...t.gemini,...n},r=O(e);if(!r)throw Error(`The provider gemini is not configured. Please check your configuration.`);return{provider:te(r),model:e.model||u.DefaultModel}}case`chatcompletions`:{let e={...t.chatcompletions,...n},r=e[`base-url`],i=e.model;if(!r||!i)throw Error(`The provider chatcompletions is not configured. Please check your configuration.`);return{provider:v(r,O(e)),model:i}}default:throw Error(`Unknown provider type: ${e.type}`)}}function O(e){let t=e.apiKeyEnv??e[`api-key-env`];return typeof t==`string`&&t.length>0?process.env[t]:e[`api-key`]}function k(e){let{type:t,...n}=e.provider,r=Object.keys(n).length>0?{type:t,config:n}:{type:t};return{version:1,name:e.name,provider:r,tools:e.tools?.map(e=>({name:e})),providerTools:e.provider_tools?.map(e=>({name:e})),mcps:e.mcps}}async function A(n,r,i){let a=k(n),o=a.mcps?.length?await de(a.mcps,i):[],s=await y(a,e=>{let t=D(e.provider,r);return{provider:t.provider,model:t.model,tools:e.tools?.length?E(e.tools.map(e=>e.name)):void 0,mcps:o.length>0?o:void 0}});return{agentConfig:{...s,memory:new e({provider:s.provider,model:s.model,store:new t(`.axle`)})},mcps:o}}S.object({value:S.string()}),S.object({timeout:S.number().optional(),maxBuffer:S.number().optional(),cwd:S.string().optional()});const j={"api-key":S.string().optional(),apiKeyEnv:S.string().optional(),"api-key-env":S.string().optional()},M=S.strictObject({type:S.literal(`chatcompletions`),"base-url":S.string().optional(),model:S.string().optional(),...j}),N=S.strictObject({type:S.literal(`anthropic`),model:S.string().optional(),...j}),P=S.strictObject({type:S.literal(`openai`),model:S.string().optional(),...j}),F=S.strictObject({type:S.literal(`gemini`),model:S.string().optional(),...j}),I=S.discriminatedUnion(`type`,[M,N,P,F]),L=S.object({transport:S.literal(`stdio`),name:S.string().optional(),command:S.string(),args:S.array(S.string()).optional(),env:S.record(S.string(),S.string()).optional()}),R=S.object({transport:S.literal(`http`),name:S.string().optional(),url:S.string(),headers:S.record(S.string(),S.string()).optional()}),z=S.discriminatedUnion(`transport`,[L,R]),B=S.object({files:S.string(),resume:S.boolean().default(!1),concurrency:S.number().int().positive().default(3)}),me=S.object({name:S.string().optional(),provider:I,task:S.string(),tools:S.array(S.string()).optional(),provider_tools:S.array(S.string()).optional(),files:S.array(S.string()).optional(),mcps:S.array(z).optional(),batch:B.optional()});async function he(e,t){let{tracer:n}=t,r=w(e).slice(1);if(r!==`yaml`&&r!==`yml`)throw Error(`Invalid job file format. Expected .yaml or .yml`);let i;try{i=await C(e,{encoding:`utf-8`})}catch{throw Error(`Job File not found, see --help for details`)}let a=T.parse(i);n?.debug(`Job config: `+JSON.stringify(a,null,2));let o=me.safeParse(a);if(!o.success)throw Error(`The job file is not valid:\n${be(o.error)}`);return o.data.name||(o.data.name=ae(e,w(e))),o.data}async function ge(e){let{tracer:t}=e;se({quiet:!0});let n=_e();return t?.debug(`Service config: `+JSON.stringify(V(n),null,2)),n}function _e(){return ve({openai:process.env.OPENAI_API_KEY?{"api-key":process.env.OPENAI_API_KEY,model:process.env.OPENAI_MODEL}:void 0,anthropic:process.env.ANTHROPIC_API_KEY?{"api-key":process.env.ANTHROPIC_API_KEY,model:process.env.ANTHROPIC_MODEL}:void 0,gemini:process.env.GEMINI_API_KEY?{"api-key":process.env.GEMINI_API_KEY,model:process.env.GEMINI_MODEL}:void 0,chatcompletions:process.env.CHATCOMPLETIONS_BASE_URL&&process.env.CHATCOMPLETIONS_MODEL?{"base-url":process.env.CHATCOMPLETIONS_BASE_URL,model:process.env.CHATCOMPLETIONS_MODEL,"api-key":process.env.CHATCOMPLETIONS_API_KEY}:void 0})}function ve(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>e&&Object.keys(e).length>0))}function V(e){return Array.isArray(e)?e.map(e=>V(e)):!e||typeof e!=`object`?e:Object.fromEntries(Object.entries(e).map(([e,t])=>[e,ye(e)&&t?`[redacted]`:V(t)]))}function ye(e){return e===`api-key`||e.toLowerCase().includes(`secret`)}function be(e){return e.issues.map(e=>` - ${e.path.join(`.`)||`root`}: ${e.message}`).join(`
3
+ `)}const H=`.axle/batch.jsonl`;function xe(e,t){let n=ne(`sha256`);return n.update(e),n.update(`\0`),n.update(t),n.digest(`hex`)}async function Se(e=H){let t=new Map,n;try{n=await C(e,`utf-8`)}catch{return t}for(let e of n.split(`
4
+ `)){let n=e.trim();if(n)try{let e=JSON.parse(n);e.file&&e.hash&&t.set(e.file,e)}catch{}}return t}async function Ce(e,t=H){await ie(oe(t),{recursive:!0}),await re(t,JSON.stringify(e)+`
5
+ `,`utf-8`)}async function we(e,t,n,r,i,a){let o=new d({prompt:e.task});if(e.files)for(let t of e.files)o.addFile(await b(t));let s=a.startSpan(`job`,{type:`workflow`}),l=new c({...t,tracer:s});try{let e=await l.send(o.withInputs(n)).final;if(g(i,e.usage),e.response){let t=e.response;a.info(t,{markdown:!0})}r.interactive&&await Te(l,i,a),s.end()}catch(e){let t=e instanceof Error?e.message:String(e);throw s.error(t),s.end(`error`),e}}async function Te(e,t,n){let r=le({input:process.stdin,output:process.stdout});r.on(`SIGINT`,()=>{r.close()});let i=e=>new Promise(t=>{r.question(e,t),r.once(`close`,()=>t(null))});try{for(;;){let r=await i(`
6
+ > `);if(r===null||r.trim()===``)break;try{let i=await e.send(r.trim()).final;if(g(t,i.usage),i.response){let e=i.response;n.info(e,{markdown:!0})}}catch(e){let t=e instanceof Error?e.message:String(e);n.error(t)}}}finally{r.close()}}async function Ee(e,t,n,r,i,a,o){let s=await ce(t.files);if(s.length===0){o.warn(`No files matched pattern: ${t.files}`);return}o.info(`Batch: ${s.length} file(s) matched "${t.files}"`);let l=t.resume?await Se():new Map,u=e.files?await Promise.all(e.files.map(e=>b(e))):[],f=0,p=0,m=0;await De(t.concurrency??3,s,async i=>{let s=o.startSpan(`batch:${i}`,{type:`workflow`});try{let o=await C(i),m=xe(e.task,o),h=l.get(i);if(t.resume&&h&&h.hash===m){s.info(`Skipped (already completed)`),s.end(),p++;return}let _=new d({prompt:e.task});for(let e of u)_.addFile(e);_.addFile(await b(i));let v={...r,file:i};g(a,(await new c({...n,tracer:s}).send(_.withInputs(v)).final).usage),await Ce({file:i,hash:m,timestamp:Date.now()}),s.end(),f++}catch(e){let t=e instanceof Error?e.message:String(e);s.error(`Failed: ${t}`),s.end(`error`),m++}}),o.info(`Batch complete: ${f} completed, ${p} skipped, ${m} failed`)}async function De(e,t,n){let r=0;async function i(){for(;r<t.length;)await n(t[r++])}let a=Array.from({length:Math.min(e,t.length)},()=>i());await Promise.all(a)}const U=new s().name(`axle`).description(`Axle is a CLI tool for running AI workflows`).version(ue).requiredOption(`-j, --job <path>`,`Path to the YAML job file`).option(`--no-log`,`Do not write the output to a log file`).option(`-d, --debug`,`Print additional debug information`).option(`-i, --interactive`,`Continue the conversation interactively after the initial task`).option(`--args <args...>`,`Additional arguments in the form key=value`);U.parse(process.argv);const W=U.opts(),G={date:new Date().toISOString().split(`T`)[0],datetime:new Date().toISOString(),cwd:process.cwd()};W.args&&W.args.forEach(e=>{let[t,n]=e.split(`=`);t&&n&&(G[t.trim()]=n.trim())});const K=new h;W.debug&&(K.minLevel=`debug`);const Oe=new m({minLevel:W.debug?`debug`:`info`,showInternal:W.debug,showTimestamp:!0,markdown:!0});if(K.addWriter(Oe),W.log){let e=new m({minLevel:`debug`,showInternal:!0,showTimestamp:!0,output:e=>{}});K.addWriter(e)}const q=K.startSpan(`cli`,{type:`root`});process.on(`uncaughtException`,async e=>{console.error(`Uncaught exception:`),console.error(e),q.error(`Uncaught exception:`),q.error(e.message),q.error(e.stack||``),q.end(`error`),await K.flush(),process.exit(1)}),W.debug&&(q.debug(`Options: `+JSON.stringify(W,null,2)),q.debug(`Additional Arguments: `+JSON.stringify(G,null,2)));let J,Y;try{J=await ge({tracer:q}),Y=await he(W.job,{tracer:q})}catch(e){let t=e instanceof Error?e:Error(String(e));q.error(t.message),q.debug(t.stack??``),q.end(`error`),await K.flush(),U.outputHelp(),process.exit(1)}let X=[],Z;try{let e=await A(Y,J,q);Z=e.agentConfig,X=e.mcps}catch(e){let t=e instanceof Error?e:Error(String(e));q.error(t.message),q.error(t.stack??``),q.end(`error`),await K.flush(),U.outputHelp(),process.exit(1)}if(!Z)throw Error(`Failed to create agent config.`);q.info(`All systems operational. Running job...`);const Q=ee(),ke=performance.now(),$={task:Y.task,files:Y.files};try{Y.batch?await Ee($,Y.batch,Z,G,W,Q,q):await we($,Z,G,W,Q,q)}catch(e){let t=e instanceof Error?e:Error(String(e));q.error(t.message),q.debug(t.stack??``)}finally{X.length>0&&await fe(X,q)}const Ae=performance.now()-ke;q.info(`Total run time: ${Math.round(Ae)}ms`),q.info(`Input tokens: ${Q.in}`),q.info(`Output tokens: ${Q.out}`),Q.cachedIn!==void 0&&q.info(`Cached input tokens: ${Q.cachedIn}`),Q.cacheWriteIn!==void 0&&q.info(`Cache write input tokens: ${Q.cacheWriteIn}`),Q.reasoningOut!==void 0&&q.info(`Reasoning output tokens: ${Q.reasoningOut}`),q.info(`Complete. Goodbye`),q.end(),await K.flush();export{};
@@ -0,0 +1,30 @@
1
+ import { AIProvider, AgentMemory, ExecutableTool, FileStore, MemoryContext, RecallResult } from "@fifthrevision/axle";
2
+
3
+ //#region src/memory/ProceduralMemory.d.ts
4
+ interface ProceduralMemoryConfig {
5
+ provider: AIProvider;
6
+ model: string;
7
+ enableTools?: boolean;
8
+ store: FileStore;
9
+ /** Memory-owned partition metadata, such as user or workspace ids. */
10
+ scope?: Record<string, string>;
11
+ }
12
+ declare class ProceduralMemory implements AgentMemory {
13
+ private provider;
14
+ private model;
15
+ private enableTools;
16
+ private store;
17
+ private scope?;
18
+ private lastAgentName?;
19
+ constructor(config: ProceduralMemoryConfig);
20
+ recall(context: MemoryContext): Promise<RecallResult>;
21
+ record(context: MemoryContext): Promise<void>;
22
+ tools(): ExecutableTool[];
23
+ private formatMessages;
24
+ private parseInstructions;
25
+ private getStorePath;
26
+ private loadStore;
27
+ private saveStore;
28
+ }
29
+ //#endregion
30
+ export { ProceduralMemory, type ProceduralMemoryConfig };
@@ -0,0 +1 @@
1
+ import{t as e}from"../memory-BmDsikXS.js";export{e as ProceduralMemory};
@@ -0,0 +1,20 @@
1
+ import{generate as e}from"@fifthrevision/axle";import t from"node:crypto";import{z as n}from"zod";var r=class{provider;model;enableTools;store;scope;lastAgentName;constructor(e){this.provider=e.provider,this.model=e.model,this.enableTools=e.enableTools??!1,this.store=e.store,this.scope=e.scope}async recall(e){let t=e.tracer?.startSpan(`memory.recall`,{type:`internal`});this.lastAgentName=e.agentName;let n=await this.loadStore(e.agentName);if(n.instructions.length===0)return t?.info(`no stored instructions`),t?.end(),{};t?.info(`loaded instructions`,{count:n.instructions.length});let r=n.instructions.map((e,t)=>`${t+1}. ${e}`).join(`
2
+ `);return t?.end(),{systemSuffix:`## Learned Instructions\n\n${r}`}}async record(t){if(!t.newMessages||t.newMessages.length===0)return;let n=t.tracer?.startSpan(`memory.record`,{type:`internal`}),r=this.formatMessages(t.newMessages);if(!r.trim()){n?.info(`no text content to extract from`),n?.end();return}let a=n?.startSpan(`memory.extract`,{type:`llm`}),o=await e({provider:this.provider,model:this.model,messages:[{role:`user`,content:r}],system:`You are a memory extraction system. Your job is to extract learnings from a conversation that should be remembered for future runs.
3
+
4
+ Only extract:
5
+ - Explicit user corrections (e.g., "No, always use bullet points")
6
+ - Stated preferences (e.g., "I prefer concise summaries")
7
+ - Patterns that clearly emerged from user feedback
8
+
9
+ Do NOT extract:
10
+ - General knowledge or facts from the conversation content
11
+ - Inferences or speculation about what the user might want
12
+ - Task-specific details that won't apply to future runs
13
+
14
+ Respond with a JSON array of instruction strings. Each instruction should be a clear, actionable directive.
15
+ If there are no learnings to extract, respond with an empty array: []
16
+
17
+ Example response:
18
+ ["Always use bullet points for lists", "Keep summaries under 3 sentences"]`,tracer:a});if(!o.ok){n?.warn(`extraction failed`,{error:o.error}),n?.end();return}let s=i(o.final.content);if(!s){n?.end();return}let c=this.parseInstructions(s);if(c.length===0){n?.info(`no instructions extracted`),n?.end();return}let l=await this.loadStore(t.agentName);l.instructions.push(...c),await this.saveStore(t.agentName,l),n?.info(`saved instructions`,{count:c.length}),n?.end()}tools(){if(!this.enableTools)return[];let e=this;return[{name:`add_instruction`,description:`Save a learned instruction for future runs. Use this when the user explicitly asks you to remember something.`,schema:n.object({instruction:n.string().describe(`The instruction to remember`)}),async execute(t){if(!e.lastAgentName)return`Error: memory not initialized (no recall has been called yet)`;let n=await e.loadStore(e.lastAgentName);return n.instructions.push(t.instruction),await e.saveStore(e.lastAgentName,n),`Instruction saved: "${t.instruction}"`}}]}formatMessages(e){let t=[];for(let n of e)if(n.role===`user`){let e=typeof n.content==`string`?n.content:i(n.content);e&&t.push(`User: ${e}`)}else if(n.role===`assistant`){let e=i(n.content);e&&t.push(`Assistant: ${e}`)}return t.join(`
19
+
20
+ `)}parseInstructions(e){let t=e.trim(),n=t.match(/^```(?:json)?\s*\n?([\s\S]*?)\n?\s*```$/);n&&(t=n[1].trim());try{let e=JSON.parse(t);if(Array.isArray(e)&&e.every(e=>typeof e==`string`))return e}catch{}return[]}getStorePath(e,n){let r=e??`default`,i=r;if(n&&Object.keys(n).length>0){let e=Object.entries(n).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>`${e}=${t}`).join(`&`);i=`${r}-${t.createHash(`sha256`).update(e).digest(`hex`).slice(0,8)}`}return`memory/procedural/${i}.json`}async loadStore(e){let t=this.getStorePath(e,this.scope),n=await this.store.read(t);if(n)try{let e=JSON.parse(n);if(e&&Array.isArray(e.instructions))return{instructions:e.instructions}}catch{}return{instructions:[]}}async saveStore(e,t){if(!t)return;let n=this.getStorePath(e,this.scope);await this.store.write(n,JSON.stringify(t,null,2))}};function i(e){return typeof e==`string`?e:e.filter(e=>e.type===`text`||e.type===`thinking`).map(e=>e.text).join(``)}export{r as t};
@@ -0,0 +1,11 @@
1
+ import { FileStore } from "@fifthrevision/axle";
2
+
3
+ //#region src/store/LocalFileStore.d.ts
4
+ declare class LocalFileStore implements FileStore {
5
+ readonly rootPath: string;
6
+ constructor(rootPath: string);
7
+ read(path: string): Promise<string | null>;
8
+ write(path: string, content: string): Promise<void>;
9
+ }
10
+ //#endregion
11
+ export { LocalFileStore };
@@ -0,0 +1 @@
1
+ import{t as e}from"../store-BFxiiKeG.js";export{e as LocalFileStore};
@@ -0,0 +1 @@
1
+ import e from"node:fs/promises";import t from"node:path";var n=class{rootPath;constructor(e){this.rootPath=e}async read(n){let r=t.join(this.rootPath,n);try{return await e.readFile(r,`utf-8`)}catch{return null}}async write(n,r){let i=t.join(this.rootPath,n);await e.mkdir(t.dirname(i),{recursive:!0}),await e.writeFile(i,r,`utf-8`)}};export{n as t};
@@ -0,0 +1,71 @@
1
+ import { ExecutableTool, ProviderTool, ToolContext, ToolDefinition } from "@fifthrevision/axle";
2
+ import * as z$1 from "zod";
3
+ import { z } from "zod";
4
+
5
+ //#region src/tools/types.d.ts
6
+ interface ExecProviderConfig {
7
+ timeout?: number;
8
+ maxBuffer?: number;
9
+ cwd?: string;
10
+ }
11
+ interface ToolProviderConfig {
12
+ exec?: ExecProviderConfig;
13
+ }
14
+ //#endregion
15
+ //#region src/tools/calculator.d.ts
16
+ declare const calculatorSchema: z.ZodObject<{
17
+ operation: z.ZodEnum<{
18
+ add: "add";
19
+ subtract: "subtract";
20
+ multiply: "multiply";
21
+ divide: "divide";
22
+ }>;
23
+ a: z.ZodNumber;
24
+ b: z.ZodNumber;
25
+ }, z.core.$strip>;
26
+ declare const calculatorTool: ExecutableTool<typeof calculatorSchema>;
27
+ //#endregion
28
+ //#region src/tools/exec/index.d.ts
29
+ declare const execSchema: z$1.ZodObject<{
30
+ command: z$1.ZodString;
31
+ }, z$1.core.$strip>;
32
+ declare class ExecTool implements ExecutableTool<typeof execSchema> {
33
+ name: string;
34
+ description: string;
35
+ schema: z$1.ZodObject<{
36
+ command: z$1.ZodString;
37
+ }, z$1.core.$strip>;
38
+ private timeout;
39
+ private maxBuffer;
40
+ private cwd?;
41
+ constructor(config?: ExecProviderConfig);
42
+ configure(config: ExecProviderConfig): void;
43
+ summarize(params: z$1.infer<typeof execSchema>): string;
44
+ execute(params: z$1.infer<typeof execSchema>, ctx: ToolContext): Promise<string>;
45
+ }
46
+ declare const execTool: ExecTool;
47
+ //#endregion
48
+ //#region src/tools/patch-file.d.ts
49
+ declare const patchFileSchema: z.ZodObject<{
50
+ path: z.ZodString;
51
+ old_string: z.ZodString;
52
+ new_string: z.ZodString;
53
+ start_line: z.ZodNumber;
54
+ end_line: z.ZodNumber;
55
+ }, z.core.$strip>;
56
+ declare const patchFileTool: ExecutableTool<typeof patchFileSchema>;
57
+ //#endregion
58
+ //#region src/tools/read-file.d.ts
59
+ declare const readFileSchema: z.ZodObject<{
60
+ path: z.ZodString;
61
+ }, z.core.$strip>;
62
+ declare const readFileTool: ExecutableTool<typeof readFileSchema>;
63
+ //#endregion
64
+ //#region src/tools/write-file.d.ts
65
+ declare const writeFileSchema: z.ZodObject<{
66
+ path: z.ZodString;
67
+ content: z.ZodString;
68
+ }, z.core.$strip>;
69
+ declare const writeFileTool: ExecutableTool<typeof writeFileSchema>;
70
+ //#endregion
71
+ export { type ExecProviderConfig, type ExecutableTool, type ProviderTool, type ToolContext, type ToolDefinition, type ToolProviderConfig, calculatorTool, execTool, patchFileTool, readFileTool, writeFileTool };
@@ -0,0 +1 @@
1
+ import{a as e,i as t,n,r,t as i}from"../tools-Cp5kiYmz.js";export{e as calculatorTool,t as execTool,r as patchFileTool,n as readFileTool,i as writeFileTool};
@@ -0,0 +1,5 @@
1
+ import*as e from"zod";import{z as t}from"zod";import{mkdir as n,readFile as r,writeFile as i}from"node:fs/promises";import{dirname as a}from"node:path";import{spawn as o}from"node:child_process";const s={name:`calculator`,description:`Performs basic arithmetic operations`,schema:t.object({operation:t.enum([`add`,`subtract`,`multiply`,`divide`]).describe(`The operation to perform (add, subtract, multiply, divide)`),a:t.number().describe(`First operand`),b:t.number().describe(`Second operand`)}),execute:async({operation:e,a:t,b:n})=>{switch(e){case`add`:return`${t} + ${n} = ${t+n}`;case`subtract`:return`${t} - ${n} = ${t-n}`;case`multiply`:return`${t} * ${n} = ${t*n}`;case`divide`:if(n===0)throw Error(`Cannot divide by zero`);return`${t} / ${n} = ${t/n}`;default:throw Error(`Unknown operation: ${e}`)}}};async function c(e,t={}){let n=t.timeout??3e4,r=t.maxBuffer??1048576;return new Promise((i,a)=>{let s=o(e,[],{shell:!0,cwd:t.cwd}),c=``,l=``,u=!1,d=!1,f=!1,p=setTimeout(()=>{u=!0,s.kill(`SIGTERM`)},n),m=()=>{d=!0,s.kill(`SIGTERM`)};t.signal?.addEventListener(`abort`,m);let h=()=>{clearTimeout(p),t.signal?.removeEventListener(`abort`,m)};s.stdout?.setEncoding(`utf-8`),s.stderr?.setEncoding(`utf-8`),s.stdout?.on(`data`,e=>{if(c+=e,c.length+l.length>r){f=!0,s.kill(`SIGTERM`);return}t.onChunk?.(e)}),s.stderr?.on(`data`,e=>{if(l+=e,c.length+l.length>r){f=!0,s.kill(`SIGTERM`);return}t.onChunk?.(e)}),s.on(`error`,e=>{h(),a(e)}),s.on(`close`,e=>{if(h(),u){let e=Error(`Command timed out after ${n}ms`);e.stdout=c,e.stderr=l,a(e);return}if(d){let e=Error(`Command aborted`);e.stdout=c,e.stderr=l,a(e);return}if(f){let e=Error(`Command output exceeded maxBuffer (${r} bytes)`);e.stdout=c,e.stderr=l,a(e);return}if(e!==0){let t=Error(`Command failed with exit code ${e}`);t.stdout=c,t.stderr=l,t.code=e??-1,a(t);return}i({stdout:c,stderr:l})})})}function l(e){if(e instanceof Error){let t=e,n=`Error executing command: ${e.message}`;return t.stdout&&(n+=`\n[stdout]: ${t.stdout}`),t.stderr&&(n+=`\n[stderr]: ${t.stderr}`),n}return`Error executing command: ${String(e)}`}function u(e,t){return t&&t.trim()?`${e}\n[stderr]: ${t}`:e}const d=e.object({command:e.string().describe(`The shell command to execute`)}),f=new class{name=`exec`;description=`Execute a shell command and return the output.`;schema=d;timeout=3e4;maxBuffer=1024*1024;cwd;constructor(e){e&&this.configure(e)}configure(e){this.timeout=e.timeout??3e4,this.maxBuffer=e.maxBuffer??1024*1024,this.cwd=e.cwd}summarize(e){return e.command}async execute(e,t){let{command:n}=e;try{let e=await c(n,{timeout:this.timeout,maxBuffer:this.maxBuffer,cwd:this.cwd,signal:t.signal,onChunk:e=>t.emit(e)});return u(e.stdout,e.stderr)}catch(e){return l(e)}}},p={name:`patch-file`,description:`Patch a file by replacing an exact string match within a specified line range`,schema:t.object({path:t.string().describe(`The file path to patch`),old_string:t.string().describe(`The exact text to find and replace`),new_string:t.string().describe(`The replacement text`),start_line:t.number().int().positive().describe(`1-indexed start line of the region to match within`),end_line:t.number().int().positive().describe(`1-indexed end line (inclusive) of the region to match within`)}),summarize:({path:e,start_line:t,end_line:n})=>`${e}:${t}:${n}`,execute:async({path:e,old_string:t,new_string:n,start_line:a,end_line:o})=>{if(o<a)throw Error(`end_line (${o}) must be >= start_line (${a})`);let s;try{s=await r(e,`utf-8`)}catch(t){throw t instanceof Error?Error(`Failed to read file "${e}": ${t.message}`):t}let c=s.split(`
2
+ `);if(a>c.length)throw Error(`start_line (${a}) exceeds file length (${c.length} lines)`);if(o>c.length)throw Error(`end_line (${o}) exceeds file length (${c.length} lines)`);let l=c.slice(a-1,o).join(`
3
+ `),u=l.indexOf(t);if(u===-1)throw Error(`old_string not found within lines ${a}-${o} of "${e}"`);if(l.indexOf(t,u+1)!==-1)throw Error(`old_string matches multiple times within lines ${a}-${o} of "${e}"`);let d=l.replace(t,n),f=[...c.slice(0,a-1),...d.split(`
4
+ `),...c.slice(o)].join(`
5
+ `);try{await i(e,f,`utf-8`)}catch(t){throw t instanceof Error?Error(`Failed to write file "${e}": ${t.message}`):t}return`Successfully patched "${e}" (lines ${a}-${o})`}},m={name:`read-file`,description:`Read the contents of a file from disk`,schema:t.object({path:t.string().describe(`The file path to read from`)}),summarize:({path:e})=>e,execute:async({path:e})=>{try{return await r(e,`utf-8`)}catch(t){throw t instanceof Error?Error(`Failed to read file "${e}": ${t.message}`):t}}},h={name:`write-file`,description:`Write content to a file on disk, creating directories if needed`,schema:t.object({path:t.string().describe(`The file path to write to`),content:t.string().describe(`The content to write to the file`)}),summarize:({path:e})=>e,execute:async({path:e,content:t})=>{try{return await n(a(e),{recursive:!0}),await i(e,t,`utf-8`),`Successfully wrote ${t.length} characters to "${e}"`}catch(t){throw t instanceof Error?Error(`Failed to write file "${e}": ${t.message}`):t}}};export{s as a,f as i,m as n,p as r,h as t};
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@fifthrevision/axle-cli",
3
+ "version": "0.19.0",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/johncch/axle.git"
7
+ },
8
+ "description": "Axle command line workflow runner.",
9
+ "type": "module",
10
+ "bin": {
11
+ "axle": "dist/cli.js"
12
+ },
13
+ "exports": {
14
+ "./tools": {
15
+ "types": "./dist/tools/index.d.ts",
16
+ "import": "./dist/tools/index.js"
17
+ },
18
+ "./memory": {
19
+ "types": "./dist/memory/index.d.ts",
20
+ "import": "./dist/memory/index.js"
21
+ },
22
+ "./store": {
23
+ "types": "./dist/store/index.d.ts",
24
+ "import": "./dist/store/index.js"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "author": "Chong Han Chua",
31
+ "license": "ISC",
32
+ "dependencies": {
33
+ "@commander-js/extra-typings": "^14.0.0",
34
+ "commander": "^14.0.3",
35
+ "dotenv": "^17.4.2",
36
+ "glob": "^13.0.6",
37
+ "yaml": "^2.9.0",
38
+ "zod": "^4.4.3",
39
+ "@fifthrevision/axle": "0.19.0"
40
+ },
41
+ "devDependencies": {
42
+ "@arethetypeswrong/core": "^0.18.2",
43
+ "publint": "^0.3.21",
44
+ "tsdown": "^0.22.0",
45
+ "tsx": "^4.22.3",
46
+ "typescript": "^6.0.3"
47
+ },
48
+ "scripts": {
49
+ "start": "tsx ./src/cli.ts",
50
+ "build": "tsdown --clean --minify",
51
+ "build:watch": "tsdown --watch",
52
+ "build-dev": "tsdown"
53
+ }
54
+ }