@wanghuimvp/axon 0.0.1 → 0.1.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.
Files changed (3) hide show
  1. package/README.md +97 -56
  2. package/dist/cli.js +274 -19
  3. package/package.json +58 -33
package/README.md CHANGED
@@ -1,56 +1,97 @@
1
- # Axon
2
-
3
- An agentic coding CLI. Axon streams from Anthropic and runs a multi-step tool loop over your codebase — it reads, searches, and reasons across your files to answer a prompt.
4
-
5
- > Foundation release (`0.0.x`). Non-interactive `axon -p` mode with read-only tools. Interactive TUI, write/edit/shell tools, and multi-provider support are on the roadmap.
6
-
7
- ## Install
8
-
9
- ```bash
10
- npm install -g @wanghuimvp/axon
11
- ```
12
-
13
- ## Setup
14
-
15
- Axon reads your Anthropic key from the environment:
16
-
17
- ```bash
18
- # macOS / Linux
19
- export ANTHROPIC_API_KEY=sk-ant-...
20
-
21
- # Windows (PowerShell)
22
- $env:ANTHROPIC_API_KEY="sk-ant-..."
23
- ```
24
-
25
- ## Usage
26
-
27
- ```bash
28
- axon -p "list the TypeScript files in src and explain what engine.ts does"
29
- ```
30
-
31
- Axon will stream the model's reasoning, run read-only tools as needed (`read_file`, `list_dir`, `glob`, `grep`), and print the result.
32
-
33
- ### Options
34
-
35
- - `-p, --print <prompt>` — run one prompt non-interactively and stream the result.
36
- - `--version` — print the version.
37
-
38
- ## Configuration
39
-
40
- Optional config file at `~/.axon/config.json`:
41
-
42
- ```json
43
- {
44
- "provider": "anthropic",
45
- "model": "claude-opus-4-8",
46
- "providers": {
47
- "anthropic": { "apiKey": "env:ANTHROPIC_API_KEY" }
48
- }
49
- }
50
- ```
51
-
52
- API keys are read from environment variables and never written to disk.
53
-
54
- ## License
55
-
56
- MIT
1
+ # Axon
2
+
3
+ An agentic coding CLI. Axon streams from Anthropic and runs a multi-step tool loop over your codebase — it reads, searches, and reasons across your files to answer a prompt.
4
+
5
+ > Foundation release (`0.0.x`). Non-interactive `axon -p` mode with read-only tools. Interactive TUI and write/edit/shell tools are on the roadmap.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g @wanghuimvp/axon
11
+ ```
12
+
13
+ ## Providers
14
+
15
+ Axon speaks to three backends. The OpenAI-compatible provider also drives any
16
+ OpenAI-protocol endpoint (DeepSeek, Qwen, Kimi, Groq, OpenRouter, ollama, LM Studio, …)
17
+ by pointing `baseUrl` at it.
18
+
19
+ | Provider | `provider` value | API key env | Notes |
20
+ |------------|------------------|---------------------|-------|
21
+ | Anthropic | `anthropic` | `ANTHROPIC_API_KEY` | Claude models |
22
+ | OpenAI | `openai` | `OPENAI_API_KEY` | GPT + any OpenAI-compatible endpoint via `baseUrl` |
23
+ | Gemini | `gemini` | `GEMINI_API_KEY` | Google Gemini models |
24
+
25
+ Set the relevant API key in your environment before running:
26
+
27
+ ```bash
28
+ # macOS / Linux
29
+ export OPENAI_API_KEY=sk-...
30
+ # Windows (PowerShell)
31
+ $env:OPENAI_API_KEY="sk-..."
32
+ ```
33
+
34
+ ### Select a provider/model
35
+
36
+ ```bash
37
+ # Per run:
38
+ axon --provider openai --model gpt-4.1 -p "explain engine.ts"
39
+
40
+ # Persisted:
41
+ axon config set provider gemini
42
+ axon config set gemini.model gemini-2.5-pro
43
+ axon config get
44
+ ```
45
+
46
+ ### Point at an OpenAI-compatible endpoint (e.g. DeepSeek, ollama)
47
+
48
+ ```bash
49
+ axon config set provider openai
50
+ axon config set openai.baseUrl https://api.deepseek.com/v1
51
+ axon config set openai.model deepseek-chat
52
+ export OPENAI_API_KEY=sk-... # the endpoint's key
53
+ axon -p "summarize src/"
54
+ ```
55
+
56
+ For a local ollama server:
57
+ ```bash
58
+ axon config set openai.baseUrl http://localhost:11434/v1
59
+ axon config set openai.model qwen2.5-coder
60
+ export OPENAI_API_KEY=ollama # ollama ignores the key but the field must be non-empty
61
+ ```
62
+
63
+ ## Usage
64
+
65
+ ```bash
66
+ axon -p "list the TypeScript files in src and explain what engine.ts does"
67
+ ```
68
+
69
+ Axon will stream the model's reasoning, run read-only tools as needed (`read_file`, `list_dir`, `glob`, `grep`), and print the result.
70
+
71
+ ### Options
72
+
73
+ - `-p, --print <prompt>` — run one prompt non-interactively and stream the result.
74
+ - `--provider <name>` — select a provider (anthropic, openai, gemini) for this run.
75
+ - `--model <name>` — select a model for this run.
76
+ - `--version` — print the version.
77
+
78
+ ## Configuration
79
+
80
+ Optional config file at `~/.axon/config.json`:
81
+
82
+ ```json
83
+ {
84
+ "provider": "anthropic",
85
+ "providers": {
86
+ "anthropic": { "model": "claude-opus-4-8" },
87
+ "openai": { "model": "gpt-4.1", "baseUrl": "https://api.openai.com/v1" },
88
+ "gemini": { "model": "gemini-2.5-pro" }
89
+ }
90
+ }
91
+ ```
92
+
93
+ API keys are read from environment variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`) and never written to disk.
94
+
95
+ ## License
96
+
97
+ MIT
package/dist/cli.js CHANGED
@@ -10,13 +10,26 @@ var VERSION = "0.0.1";
10
10
  import { readFileSync } from "node:fs";
11
11
  import { homedir } from "node:os";
12
12
  import { join } from "node:path";
13
+ var DEFAULT_MODELS = {
14
+ anthropic: "claude-opus-4-8",
15
+ openai: "gpt-4.1",
16
+ gemini: "gemini-2.5-pro"
17
+ };
13
18
  var DEFAULTS = {
14
19
  provider: "anthropic",
15
- model: "claude-opus-4-8",
16
20
  providers: {
17
- anthropic: { apiKey: "env:ANTHROPIC_API_KEY" }
21
+ anthropic: { apiKey: "env:ANTHROPIC_API_KEY" },
22
+ openai: { apiKey: "env:OPENAI_API_KEY" },
23
+ gemini: { apiKey: "env:GEMINI_API_KEY" }
18
24
  }
19
25
  };
26
+ function resolveModel(cfg) {
27
+ const model = cfg.model ?? cfg.providers[cfg.provider]?.model ?? DEFAULT_MODELS[cfg.provider];
28
+ if (!model) {
29
+ throw new Error(`No model configured for provider "${cfg.provider}". Set "model" in ~/.axon/config.json or pass --model.`);
30
+ }
31
+ return model;
32
+ }
20
33
  function resolveEnvRefs(cfg) {
21
34
  const providers = {};
22
35
  for (const [name, p] of Object.entries(cfg.providers)) {
@@ -34,14 +47,50 @@ function loadConfig() {
34
47
  }
35
48
  const merged = {
36
49
  provider: fileCfg.provider ?? DEFAULTS.provider,
37
- model: fileCfg.model ?? DEFAULTS.model,
50
+ model: fileCfg.model,
38
51
  providers: { ...DEFAULTS.providers, ...fileCfg.providers ?? {} }
39
52
  };
40
53
  return resolveEnvRefs(merged);
41
54
  }
42
55
 
56
+ // src/config/configFile.ts
57
+ import { readFileSync as readFileSync2, writeFileSync, mkdirSync } from "node:fs";
58
+ import { homedir as homedir2 } from "node:os";
59
+ import { join as join2, dirname } from "node:path";
60
+ function configPath() {
61
+ return join2(homedir2(), ".axon", "config.json");
62
+ }
63
+ function readConfigFile() {
64
+ try {
65
+ return JSON.parse(readFileSync2(configPath(), "utf8"));
66
+ } catch {
67
+ return {};
68
+ }
69
+ }
70
+ function setConfigValue(key, value) {
71
+ const cfg = readConfigFile();
72
+ if (key === "provider" || key === "model") {
73
+ cfg[key] = value;
74
+ } else if (key.includes(".")) {
75
+ const [provider, field] = key.split(".", 2);
76
+ const ALLOWED = /* @__PURE__ */ new Set(["baseUrl", "model"]);
77
+ if (!ALLOWED.has(field)) {
78
+ throw new Error(`Cannot set "${field}" via config. Set API keys via the provider's env var (e.g. ANTHROPIC_API_KEY); only baseUrl and model are settable here.`);
79
+ }
80
+ const providers = cfg.providers ??= {};
81
+ (providers[provider] ??= {})[field] = value;
82
+ } else {
83
+ throw new Error(`Unknown config key "${key}". Use "provider", "model", or "<provider>.<baseUrl|model>".`);
84
+ }
85
+ const path = configPath();
86
+ mkdirSync(dirname(path), { recursive: true });
87
+ writeFileSync(path, JSON.stringify(cfg, null, 2) + "\n");
88
+ }
89
+
43
90
  // src/providers/registry.ts
44
91
  import Anthropic from "@anthropic-ai/sdk";
92
+ import OpenAI from "openai";
93
+ import { GoogleGenAI } from "@google/genai";
45
94
 
46
95
  // src/providers/anthropic.ts
47
96
  function mapStop(reason) {
@@ -131,20 +180,206 @@ var AnthropicProvider = class {
131
180
  }
132
181
  };
133
182
 
183
+ // src/providers/openai.ts
184
+ function textOf(blocks) {
185
+ return blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
186
+ }
187
+ function toOpenAIMessages(system, messages) {
188
+ const out = [{ role: "system", content: system }];
189
+ for (const m of messages) {
190
+ if (m.role === "tool") {
191
+ out.push({ role: "tool", tool_call_id: m.toolCallId, content: m.content });
192
+ continue;
193
+ }
194
+ if (m.role === "user") {
195
+ out.push({ role: "user", content: textOf(m.content) });
196
+ continue;
197
+ }
198
+ const toolCalls = m.content.filter((b) => b.type === "tool_call").map((b) => {
199
+ const t = b;
200
+ return { id: t.id, type: "function", function: { name: t.name, arguments: JSON.stringify(t.args ?? {}) } };
201
+ });
202
+ const msg = { role: "assistant", content: textOf(m.content) };
203
+ if (toolCalls.length) msg.tool_calls = toolCalls;
204
+ out.push(msg);
205
+ }
206
+ return out;
207
+ }
208
+ function toOpenAITools(tools) {
209
+ if (!tools.length) return void 0;
210
+ return tools.map((t) => ({ type: "function", function: { name: t.name, description: t.description, parameters: t.parameters } }));
211
+ }
212
+ var OpenAIProvider = class {
213
+ constructor(deps) {
214
+ this.deps = deps;
215
+ }
216
+ callCounter = 0;
217
+ async *stream(req) {
218
+ const tools = toOpenAITools(req.tools);
219
+ const stream = await this.deps.client.chat.completions.create({
220
+ model: this.deps.model,
221
+ stream: true,
222
+ messages: toOpenAIMessages(req.system, req.messages),
223
+ ...tools ? { tools } : {}
224
+ });
225
+ const calls = /* @__PURE__ */ new Map();
226
+ let finishReason = null;
227
+ for await (const chunk of stream) {
228
+ const choice = chunk.choices?.[0];
229
+ if (!choice) continue;
230
+ const delta = choice.delta ?? {};
231
+ if (typeof delta.content === "string" && delta.content.length) {
232
+ yield { type: "text_delta", text: delta.content };
233
+ }
234
+ for (const tc of delta.tool_calls ?? []) {
235
+ const cur = calls.get(tc.index) ?? { id: "", name: "", args: "" };
236
+ if (tc.id) cur.id = tc.id;
237
+ if (tc.function?.name) cur.name = tc.function.name;
238
+ if (tc.function?.arguments) cur.args += tc.function.arguments;
239
+ calls.set(tc.index, cur);
240
+ }
241
+ if (choice.finish_reason) finishReason = choice.finish_reason;
242
+ }
243
+ for (const c of [...calls.entries()].sort((a, b) => a[0] - b[0]).map((e) => e[1])) {
244
+ const id = c.id || `openai-call-${this.callCounter++}`;
245
+ let args;
246
+ try {
247
+ args = c.args.trim() ? JSON.parse(c.args) : {};
248
+ } catch (err) {
249
+ throw new Error(`Tool "${c.name}" (id=${id}): invalid tool-call JSON: ${JSON.stringify(c.args)}`, { cause: err });
250
+ }
251
+ yield { type: "tool_call", id, name: c.name, args };
252
+ }
253
+ const stopReason = calls.size ? "tool_use" : finishReason === "length" ? "max_tokens" : "end";
254
+ yield { type: "done", stopReason };
255
+ }
256
+ };
257
+
258
+ // src/providers/gemini.ts
259
+ function textOf2(blocks) {
260
+ return blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
261
+ }
262
+ function buildIdToName(messages) {
263
+ const map = /* @__PURE__ */ new Map();
264
+ for (const m of messages) {
265
+ if (m.role === "assistant") {
266
+ for (const b of m.content) {
267
+ if (b.type === "tool_call") map.set(b.id, b.name);
268
+ }
269
+ }
270
+ }
271
+ return map;
272
+ }
273
+ function toGeminiContents(messages) {
274
+ const idToName = buildIdToName(messages);
275
+ const out = [];
276
+ let i = 0;
277
+ while (i < messages.length) {
278
+ const m = messages[i];
279
+ if (m.role === "tool") {
280
+ const parts2 = [];
281
+ while (i < messages.length && messages[i].role === "tool") {
282
+ const t = messages[i];
283
+ parts2.push({ functionResponse: { name: idToName.get(t.toolCallId) ?? "unknown", response: { result: t.content } } });
284
+ i++;
285
+ }
286
+ out.push({ role: "user", parts: parts2 });
287
+ continue;
288
+ }
289
+ if (m.role === "user") {
290
+ out.push({ role: "user", parts: [{ text: textOf2(m.content) }] });
291
+ i++;
292
+ continue;
293
+ }
294
+ const parts = [];
295
+ for (const b of m.content) {
296
+ if (b.type === "text") parts.push({ text: b.text });
297
+ else if (b.type === "tool_call") parts.push({ functionCall: { name: b.name, args: b.args } });
298
+ }
299
+ out.push({ role: "model", parts });
300
+ i++;
301
+ }
302
+ return out;
303
+ }
304
+ function toGeminiTools(tools) {
305
+ if (!tools.length) return void 0;
306
+ return [{ functionDeclarations: tools.map((t) => ({ name: t.name, description: t.description, parameters: t.parameters })) }];
307
+ }
308
+ var GeminiProvider = class {
309
+ constructor(deps) {
310
+ this.deps = deps;
311
+ }
312
+ callCounter = 0;
313
+ async *stream(req) {
314
+ const stream = await this.deps.client.models.generateContentStream({
315
+ model: this.deps.model,
316
+ contents: toGeminiContents(req.messages),
317
+ config: {
318
+ systemInstruction: req.system,
319
+ tools: toGeminiTools(req.tools)
320
+ }
321
+ });
322
+ let sawToolCall = false;
323
+ let finishReason;
324
+ for await (const chunk of stream) {
325
+ const cand = chunk.candidates?.[0];
326
+ const parts = cand?.content?.parts ?? [];
327
+ for (const part of parts) {
328
+ if (typeof part.text === "string" && part.text.length) {
329
+ yield { type: "text_delta", text: part.text };
330
+ } else if (part.functionCall) {
331
+ sawToolCall = true;
332
+ yield {
333
+ type: "tool_call",
334
+ id: `gemini-call-${this.callCounter++}`,
335
+ name: part.functionCall.name,
336
+ args: part.functionCall.args ?? {}
337
+ };
338
+ }
339
+ }
340
+ if (cand?.finishReason) finishReason = cand.finishReason;
341
+ }
342
+ const stopReason = sawToolCall ? "tool_use" : finishReason === "MAX_TOKENS" ? "max_tokens" : "end";
343
+ yield { type: "done", stopReason };
344
+ }
345
+ };
346
+
134
347
  // src/providers/registry.ts
348
+ function requireKey(provider, apiKey) {
349
+ if (!apiKey || !apiKey.trim()) {
350
+ throw new Error(`Missing ${provider} API key. Set it via env or ~/.axon/config.json.`);
351
+ }
352
+ return apiKey;
353
+ }
135
354
  function createProvider(cfg) {
136
- if (cfg.provider === "anthropic") {
137
- const apiKey = cfg.providers.anthropic?.apiKey;
138
- if (!apiKey || !apiKey.trim()) throw new Error("Missing Anthropic API key. Set ANTHROPIC_API_KEY.");
139
- const client = new Anthropic({ apiKey });
140
- return new AnthropicProvider({ client, model: cfg.model });
355
+ const pc = cfg.providers[cfg.provider] ?? {};
356
+ switch (cfg.provider) {
357
+ case "anthropic": {
358
+ const apiKey = requireKey("Anthropic", pc.apiKey);
359
+ const model = resolveModel(cfg);
360
+ const client = new Anthropic({ apiKey });
361
+ return new AnthropicProvider({ client, model });
362
+ }
363
+ case "openai": {
364
+ const apiKey = requireKey("OpenAI", pc.apiKey);
365
+ const model = resolveModel(cfg);
366
+ const client = new OpenAI({ apiKey, baseURL: pc.baseUrl });
367
+ return new OpenAIProvider({ client, model });
368
+ }
369
+ case "gemini": {
370
+ const apiKey = requireKey("Gemini", pc.apiKey);
371
+ const model = resolveModel(cfg);
372
+ const client = new GoogleGenAI({ apiKey });
373
+ return new GeminiProvider({ client, model });
374
+ }
375
+ default:
376
+ throw new Error(`Unsupported provider: ${cfg.provider}`);
141
377
  }
142
- throw new Error(`Unsupported provider: ${cfg.provider}`);
143
378
  }
144
379
 
145
380
  // src/tools/fs.ts
146
381
  import { readFile, readdir } from "node:fs/promises";
147
- import { join as join2, resolve as resolve2 } from "node:path";
382
+ import { join as join3, resolve as resolve2 } from "node:path";
148
383
  import fg from "fast-glob";
149
384
 
150
385
  // src/tools/paths.ts
@@ -249,7 +484,7 @@ var grepTool = {
249
484
  const files = await fg(glob, { cwd: root, onlyFiles: true, dot: false });
250
485
  const hits = [];
251
486
  for (const rel of files) {
252
- const text = await readFile(join2(root, rel), "utf8").catch(() => "");
487
+ const text = await readFile(join3(root, rel), "utf8").catch(() => "");
253
488
  text.split("\n").forEach((line, i) => {
254
489
  if (re.test(line)) hits.push(`${rel}:${i + 1}:${line}`);
255
490
  });
@@ -399,23 +634,43 @@ function truncate(s, max = 500) {
399
634
  // src/cli.ts
400
635
  var SYSTEM = `You are Axon, an agentic coding assistant. Use the provided tools to inspect the project and answer precisely. When done, stop calling tools.`;
401
636
  var program = new Command();
402
- program.name("axon").version(VERSION).option("-p, --print <prompt>", "run one prompt non-interactively and stream the result");
637
+ program.name("axon").version(VERSION).option("-p, --print <prompt>", "run one prompt non-interactively and stream the result").option("--provider <name>", "override the provider for this run (anthropic | openai | gemini)").option("--model <name>", "override the model for this run");
638
+ program.command("config").argument("<action>", "get | set").argument("[key]", "config key (provider | model | <provider>.<baseUrl|model>)").argument("[value]", "value to set").action((action, key, value) => {
639
+ if (action === "get") {
640
+ process.stdout.write(JSON.stringify(readConfigFile(), null, 2) + "\n");
641
+ return;
642
+ }
643
+ if (action === "set") {
644
+ if (!key || value === void 0) throw new Error("Usage: axon config set <key> <value>");
645
+ setConfigValue(key, value);
646
+ process.stdout.write(`set ${key} = ${value}
647
+ `);
648
+ return;
649
+ }
650
+ throw new Error(`Unknown config action "${action}". Use get or set.`);
651
+ });
652
+ program.action(() => {
653
+ const opts = program.opts();
654
+ main(opts).catch((err) => {
655
+ process.stderr.write(`\u{1F4A5} ${err instanceof Error ? err.message : String(err)}
656
+ `);
657
+ process.exit(1);
658
+ });
659
+ });
403
660
  program.parse();
404
- var opts = program.opts();
405
- async function main() {
661
+ async function main(opts) {
406
662
  if (!opts.print) {
407
663
  process.stdout.write('Interactive TUI not built yet \u2014 use: axon -p "your prompt"\n');
408
664
  return;
409
665
  }
410
666
  const cfg = loadConfig();
667
+ if (opts.provider) cfg.provider = opts.provider;
668
+ if (opts.model) cfg.model = opts.model;
411
669
  const provider = createProvider(cfg);
412
670
  const tools = buildReadOnlyTools();
413
671
  const engine = new Engine({ provider, tools, system: SYSTEM, cwd: process.cwd() });
414
672
  printRunner(engine, (s) => process.stdout.write(s));
673
+ process.stderr.write(`[axon: ${cfg.provider} / ${resolveModel(cfg)}]
674
+ `);
415
675
  await engine.submit(opts.print);
416
676
  }
417
- main().catch((err) => {
418
- process.stderr.write(`\u{1F4A5} ${err instanceof Error ? err.message : String(err)}
419
- `);
420
- process.exit(1);
421
- });
package/package.json CHANGED
@@ -1,33 +1,58 @@
1
- {
2
- "name": "@wanghuimvp/axon",
3
- "version": "0.0.1",
4
- "description": "Axon — an agentic coding CLI. Streams from Anthropic, runs a multi-step tool loop over your codebase.",
5
- "type": "module",
6
- "bin": { "axon": "dist/cli.js" },
7
- "files": ["dist"],
8
- "engines": { "node": ">=20" },
9
- "license": "MIT",
10
- "keywords": ["cli", "agent", "agentic", "coding", "anthropic", "claude", "ai", "assistant", "axon"],
11
- "repository": { "type": "git", "url": "git+https://github.com/Wade-DevCode/axon.git" },
12
- "homepage": "https://github.com/Wade-DevCode/axon#readme",
13
- "bugs": { "url": "https://github.com/Wade-DevCode/axon/issues" },
14
- "publishConfig": { "access": "public" },
15
- "scripts": {
16
- "dev": "tsx src/cli.ts",
17
- "test": "vitest run",
18
- "build": "esbuild src/cli.ts --bundle --platform=node --format=esm --outfile=dist/cli.js --packages=external",
19
- "prepublishOnly": "npm test && npm run build"
20
- },
21
- "dependencies": {
22
- "@anthropic-ai/sdk": "^0.40.0",
23
- "commander": "^12.0.0",
24
- "fast-glob": "^3.3.0"
25
- },
26
- "devDependencies": {
27
- "@types/node": "^20.0.0",
28
- "esbuild": "^0.23.0",
29
- "tsx": "^4.0.0",
30
- "typescript": "^5.5.0",
31
- "vitest": "^2.0.0"
32
- }
33
- }
1
+ {
2
+ "name": "@wanghuimvp/axon",
3
+ "version": "0.1.0",
4
+ "description": "Axon — a multi-provider agentic coding CLI (Anthropic, OpenAI + OpenAI-compatible endpoints, Gemini). Runs a multi-step tool loop over your codebase.",
5
+ "type": "module",
6
+ "bin": {
7
+ "axon": "dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "engines": {
13
+ "node": ">=20"
14
+ },
15
+ "license": "MIT",
16
+ "keywords": [
17
+ "cli",
18
+ "agent",
19
+ "agentic",
20
+ "coding",
21
+ "anthropic",
22
+ "claude",
23
+ "ai",
24
+ "assistant",
25
+ "axon"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/Wade-DevCode/axon.git"
30
+ },
31
+ "homepage": "https://github.com/Wade-DevCode/axon#readme",
32
+ "bugs": {
33
+ "url": "https://github.com/Wade-DevCode/axon/issues"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "scripts": {
39
+ "dev": "tsx src/cli.ts",
40
+ "test": "vitest run",
41
+ "build": "esbuild src/cli.ts --bundle --platform=node --format=esm --outfile=dist/cli.js --packages=external",
42
+ "prepublishOnly": "npm test && npm run build"
43
+ },
44
+ "dependencies": {
45
+ "@anthropic-ai/sdk": "^0.40.0",
46
+ "@google/genai": "^1.52.0",
47
+ "commander": "^12.0.0",
48
+ "fast-glob": "^3.3.0",
49
+ "openai": "^4.104.0"
50
+ },
51
+ "devDependencies": {
52
+ "@types/node": "^20.0.0",
53
+ "esbuild": "^0.23.0",
54
+ "tsx": "^4.0.0",
55
+ "typescript": "^5.5.0",
56
+ "vitest": "^2.0.0"
57
+ }
58
+ }