@dtoolkit/dproxy 0.1.0 → 1.0.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 +238 -0
  2. package/dist/index.js +161 -237
  3. package/package.json +7 -2
package/README.md ADDED
@@ -0,0 +1,238 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/ivncmp/dtoolkit/main/logo.png" alt="dtoolkit" />
3
+ </p>
4
+
5
+ <h1 align="center">@dtoolkit/dproxy</h1>
6
+ <p align="center">Universal CLI adapter for invoking models via local CLIs</p>
7
+
8
+ <p align="center">
9
+ <a href="https://www.npmjs.com/package/@dtoolkit/dproxy"><img src="https://img.shields.io/npm/v/@dtoolkit/dproxy.svg" alt="npm"></a>
10
+ <a href="../../LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License"></a>
11
+ </p>
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install -g @dtoolkit/dproxy
17
+ ```
18
+
19
+ ## Quick start
20
+
21
+ ```bash
22
+ dproxy init # interactive setup wizard
23
+ dproxy "explain this" # single-shot prompt (default provider)
24
+ dproxy chat # interactive REPL
25
+ ```
26
+
27
+ ## Providers
28
+
29
+ dproxy supports 5 providers out of the box. Each provider shells out to its respective CLI:
30
+
31
+ | Provider | CLI | Features |
32
+ | --- | --- | --- |
33
+ | `claude` (default) | [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | Sessions, cost, usage, tools, system prompt |
34
+ | `codex` | [Codex](https://github.com/openai/codex) | Usage, approval modes |
35
+ | `gemini` | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | Sessions, usage, yolo mode |
36
+ | `ollama` | [Ollama](https://ollama.com/) | Local/offline, any model |
37
+ | `opencode` | [OpenCode](https://github.com/nicholasgriffintn/opencode) | Sessions, cost, usage |
38
+
39
+ ### Switching providers
40
+
41
+ ```bash
42
+ # Per-command
43
+ dproxy -p ollama "explain this code"
44
+ dproxy ask -p gemini "write a test for this"
45
+ dproxy chat -p codex
46
+
47
+ # Set default in config
48
+ dproxy config set provider.default gemini
49
+ ```
50
+
51
+ ## Commands
52
+
53
+ ### `dproxy <prompt>` / `dproxy ask <prompt>`
54
+
55
+ Single-shot prompt with context injection. Reads from stdin if piped.
56
+
57
+ ```bash
58
+ # Basic usage
59
+ dproxy "explain what this function does"
60
+
61
+ # With a specific provider and model
62
+ dproxy -p ollama -m codellama "refactor this"
63
+
64
+ # Pipe content
65
+ cat src/index.ts | dproxy "review this code"
66
+ git diff | dproxy ask "summarize these changes"
67
+
68
+ # Raw JSON output
69
+ dproxy --raw "hello"
70
+
71
+ # JSON output format
72
+ dproxy -o json "hello"
73
+
74
+ # Token usage footer
75
+ dproxy --token-footer "explain monads"
76
+
77
+ # Skip memory/life context
78
+ dproxy --no-memory --no-life "just answer directly"
79
+
80
+ # Inject specific memory keys only
81
+ dproxy --memory "project-rules,style-guide" "review this"
82
+
83
+ # Limit agent turns and budget
84
+ dproxy --max-turns 5 --max-budget-usd 0.50 "refactor the auth module"
85
+
86
+ # System prompt override
87
+ dproxy ask --system-prompt "You are a security auditor" "review this code"
88
+ ```
89
+
90
+ ### `dproxy chat`
91
+
92
+ Interactive REPL with session tracking across turns.
93
+
94
+ ```bash
95
+ # Start a new chat
96
+ dproxy chat
97
+
98
+ # Chat with a specific provider
99
+ dproxy chat -p gemini
100
+
101
+ # Continue the last conversation
102
+ dproxy chat -c
103
+
104
+ # Resume a specific session
105
+ dproxy chat -r sess_abc123
106
+ ```
107
+
108
+ ### `dproxy history`
109
+
110
+ Manage prompt history.
111
+
112
+ ```bash
113
+ dproxy history list # show recent entries
114
+ dproxy history show <id> # show a specific entry
115
+ dproxy history search <q> # search history
116
+ dproxy history clear # clear all history
117
+ ```
118
+
119
+ ### `dproxy memory`
120
+
121
+ Named memory snippets injected into every prompt.
122
+
123
+ ```bash
124
+ dproxy memory list # list all snippets
125
+ dproxy memory get <key> # show a snippet
126
+ dproxy memory set <key> # set (reads from stdin or editor)
127
+ dproxy memory rm <key> # delete a snippet
128
+ ```
129
+
130
+ ### `dproxy template`
131
+
132
+ YAML prompt templates with `{{variable}}` interpolation.
133
+
134
+ ```bash
135
+ dproxy template list # list templates
136
+ dproxy template show <name> # show a template
137
+ dproxy template run <name> # execute a template
138
+ dproxy template create # create a new template
139
+ dproxy template rm <name> # delete a template
140
+ ```
141
+
142
+ ### `dproxy config`
143
+
144
+ Get/set configuration values.
145
+
146
+ ```bash
147
+ dproxy config # show full config
148
+ dproxy config get provider.default # get a value
149
+ dproxy config set provider.default ollama # set a value
150
+ ```
151
+
152
+ ### `dproxy init`
153
+
154
+ Interactive setup wizard. Required before first use.
155
+
156
+ ```bash
157
+ dproxy init
158
+ ```
159
+
160
+ ## Flags reference
161
+
162
+ | Flag | Scope | Description |
163
+ | --- | --- | --- |
164
+ | `-p, --provider <name>` | ask, chat | Provider: `claude`, `codex`, `gemini`, `ollama`, `opencode` |
165
+ | `-m, --model <model>` | ask, chat | Model to use |
166
+ | `--max-turns <n>` | ask, chat | Max agent turns per message |
167
+ | `--max-budget-usd <n>` | ask | Max budget in USD |
168
+ | `-o, --output-format <fmt>` | ask | Output format: `text`, `json`, `stream-json` |
169
+ | `--system-prompt <text>` | ask | System prompt override |
170
+ | `--no-memory` | ask, chat | Skip memory injection |
171
+ | `--memory <keys>` | ask | Inject specific memory keys (comma-separated) |
172
+ | `--no-life` | ask, chat | Skip life/PARA context |
173
+ | `--no-history` | ask | Don't save to history |
174
+ | `--raw` | ask | Print raw JSON response |
175
+ | `--token-footer` | ask | Append token usage footer |
176
+ | `--max-session-tokens <n>` | ask | Reset session if context exceeds this |
177
+ | `-c, --continue` | ask, chat | Continue last conversation |
178
+ | `-r, --resume <id>` | ask, chat | Resume a specific session |
179
+
180
+ ## Context injection
181
+
182
+ Every prompt is enriched with context from multiple sources, in priority order:
183
+
184
+ 1. **Day chat log** — today's conversation history
185
+ 2. **Workspace bootstrap** — identity/personality files
186
+ 3. **Memory snippets** — named markdown snippets (truncated to 4,000 chars)
187
+ 4. **Life/PARA context** — semantic knowledge base (truncated to 12,000 chars)
188
+
189
+ Disable with `--no-memory` and `--no-life`.
190
+
191
+ ## Provider configuration
192
+
193
+ Configure provider-specific options in `~/.dproxy/config.json`:
194
+
195
+ ```json
196
+ {
197
+ "provider": {
198
+ "default": "claude",
199
+ "claude": {
200
+ "bin": "claude",
201
+ "skipPermissions": false
202
+ },
203
+ "codex": {
204
+ "bin": "codex",
205
+ "approval": "suggest"
206
+ },
207
+ "gemini": {
208
+ "bin": "gemini",
209
+ "yolo": false
210
+ },
211
+ "ollama": {
212
+ "bin": "ollama",
213
+ "defaultModel": "llama3"
214
+ },
215
+ "opencode": {
216
+ "bin": "opencode",
217
+ "skipPermissions": false
218
+ }
219
+ }
220
+ }
221
+ ```
222
+
223
+ ## Data storage
224
+
225
+ ```
226
+ ~/.dproxy/
227
+ ├── config.json # App configuration
228
+ ├── history.jsonl # Prompt history
229
+ ├── current-session.json # Active session state
230
+ ├── memory/ # Named memory snippets (.md)
231
+ └── templates/ # Prompt templates (.yaml)
232
+ ```
233
+
234
+ Override with `DPROXY_DATA_DIR` env var.
235
+
236
+ ## License
237
+
238
+ [MIT](../../LICENSE)
package/dist/index.js CHANGED
@@ -1,15 +1,39 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import chalk7 from "chalk";
5
4
  import { Command as Command7 } from "commander";
5
+ import pc7 from "picocolors";
6
6
 
7
7
  // src/commands/ask.ts
8
- import chalk from "chalk";
9
8
  import { Command } from "commander";
9
+ import pc from "picocolors";
10
10
 
11
- // src/claude.ts
12
- import { spawn } from "child_process";
11
+ // src/lib/adapter.ts
12
+ import { createClaudeAdapter } from "@dtoolkit/adapter-claude";
13
+ import { createCodexAdapter } from "@dtoolkit/adapter-codex";
14
+ import { createGeminiAdapter } from "@dtoolkit/adapter-gemini";
15
+ import { createOllamaAdapter } from "@dtoolkit/adapter-ollama";
16
+ import { createOpenCodeAdapter } from "@dtoolkit/adapter-opencode";
17
+ function resolveAdapter(provider, config) {
18
+ switch (provider) {
19
+ case "claude":
20
+ return createClaudeAdapter(config.provider.claude);
21
+ case "codex":
22
+ return createCodexAdapter(config.provider.codex);
23
+ case "gemini":
24
+ return createGeminiAdapter(config.provider.gemini);
25
+ case "ollama":
26
+ return createOllamaAdapter(config.provider.ollama);
27
+ case "opencode":
28
+ return createOpenCodeAdapter(config.provider.opencode);
29
+ default:
30
+ throw new Error(`Unknown provider: ${provider}`);
31
+ }
32
+ }
33
+
34
+ // src/lib/chat-log-store.ts
35
+ import { appendFile, mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
36
+ import { join as join2 } from "path";
13
37
 
14
38
  // src/lib/config.ts
15
39
  import { readFile, writeFile, rename, mkdir } from "fs/promises";
@@ -51,9 +75,13 @@ var DEFAULT_CONFIG = {
51
75
  assistantPrefix: "Assistant:",
52
76
  sectionHeader: "## Today's conversation"
53
77
  },
54
- claude: {
55
- bin: "claude",
56
- skipPermissions: false
78
+ provider: {
79
+ default: "claude",
80
+ claude: { bin: "claude", skipPermissions: false },
81
+ codex: { bin: "codex", approval: "suggest" },
82
+ gemini: { bin: "gemini", yolo: false },
83
+ ollama: { bin: "ollama", defaultModel: "llama3" },
84
+ opencode: { bin: "opencode", skipPermissions: false }
57
85
  },
58
86
  defaults: {},
59
87
  debug: false
@@ -84,6 +112,10 @@ async function loadConfig() {
84
112
  try {
85
113
  const raw = await readFile(join(DATA_DIR, "config.json"), "utf-8");
86
114
  const parsed = JSON.parse(raw);
115
+ if (parsed.claude && !parsed.provider) {
116
+ parsed.provider = { default: "claude", claude: parsed.claude };
117
+ delete parsed.claude;
118
+ }
87
119
  return deepMerge(
88
120
  DEFAULT_CONFIG,
89
121
  parsed
@@ -132,135 +164,7 @@ async function setConfigValue(key, value) {
132
164
  await saveConfig(config);
133
165
  }
134
166
 
135
- // src/claude.ts
136
- async function execClaude(options) {
137
- const config = await loadConfig();
138
- const args = buildArgs(options, config.claude?.skipPermissions ?? false);
139
- const bin = config.claude?.bin || "claude";
140
- const startTime = Date.now();
141
- return new Promise((resolve, reject) => {
142
- const proc = spawn(bin, args, {
143
- stdio: ["pipe", "pipe", "pipe"],
144
- env: { ...process.env }
145
- });
146
- let stdout = "";
147
- let stderr = "";
148
- proc.stdout.on("data", (data) => {
149
- stdout += data.toString();
150
- });
151
- proc.stderr.on("data", (data) => {
152
- stderr += data.toString();
153
- });
154
- if (options.stdinContent) {
155
- proc.stdin.write(options.stdinContent);
156
- proc.stdin.end();
157
- } else {
158
- proc.stdin.end();
159
- }
160
- proc.on("error", (err) => {
161
- if (err.code === "ENOENT") {
162
- reject(
163
- new Error("claude CLI not found. Make sure Claude Code is installed and on your PATH.")
164
- );
165
- } else {
166
- reject(err);
167
- }
168
- });
169
- proc.on("close", (code) => {
170
- const durationMs = Date.now() - startTime;
171
- if (code !== 0 && !stdout.trim()) {
172
- reject(new Error(stderr || `claude exited with code ${code}`));
173
- return;
174
- }
175
- try {
176
- const parsed = JSON.parse(stdout);
177
- const u = parsed.usage ?? {};
178
- const usage = {
179
- input: u.input_tokens ?? 0,
180
- output: u.output_tokens ?? 0,
181
- cacheWrite: u.cache_creation_input_tokens ?? 0,
182
- cacheRead: u.cache_read_input_tokens ?? 0,
183
- total: (u.input_tokens ?? 0) + (u.output_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0) + (u.cache_read_input_tokens ?? 0)
184
- };
185
- resolve({
186
- result: parsed.result ?? parsed.content ?? stdout,
187
- sessionId: parsed.session_id ?? "",
188
- costUsd: parsed.cost_usd ?? parsed.total_cost_usd ?? 0,
189
- durationMs,
190
- isError: parsed.is_error ?? false,
191
- usage,
192
- raw: parsed
193
- });
194
- } catch {
195
- resolve({
196
- result: stdout.trim(),
197
- sessionId: "",
198
- costUsd: 0,
199
- durationMs,
200
- isError: code !== 0
201
- });
202
- }
203
- });
204
- });
205
- }
206
- function buildArgs(options, skipPermissions) {
207
- const args = ["--print", "--output-format", "json"];
208
- if (skipPermissions) {
209
- args.push("--dangerously-skip-permissions");
210
- }
211
- if (options.model) {
212
- args.push("--model", options.model);
213
- }
214
- if (options.maxTurns !== void 0) {
215
- args.push("--max-turns", String(options.maxTurns));
216
- }
217
- if (options.maxBudgetUsd !== void 0) {
218
- args.push("--max-budget-usd", String(options.maxBudgetUsd));
219
- }
220
- if (options.systemPrompt) {
221
- args.push("--system-prompt", options.systemPrompt);
222
- }
223
- if (options.appendSystemPrompt) {
224
- args.push("--append-system-prompt", options.appendSystemPrompt);
225
- }
226
- if (options.resumeSessionId) {
227
- args.push("--resume", options.resumeSessionId);
228
- }
229
- if (options.continueSession) {
230
- args.push("--continue");
231
- }
232
- if (options.allowedTools) {
233
- for (const tool of options.allowedTools) {
234
- args.push("--allowedTools", tool);
235
- }
236
- }
237
- if (options.additionalArgs) {
238
- args.push(...options.additionalArgs);
239
- }
240
- args.push(options.prompt);
241
- return args;
242
- }
243
- async function readStdin() {
244
- if (process.stdin.isTTY) {
245
- return "";
246
- }
247
- return new Promise((resolve) => {
248
- let data = "";
249
- const timeout = setTimeout(() => resolve(data), 5e3);
250
- process.stdin.setEncoding("utf-8");
251
- process.stdin.on("data", (chunk) => {
252
- data += chunk;
253
- });
254
- process.stdin.on("end", () => {
255
- clearTimeout(timeout);
256
- resolve(data);
257
- });
258
- });
259
- }
260
-
261
167
  // src/lib/chat-log-store.ts
262
- import { appendFile, mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
263
- import { join as join2 } from "path";
264
168
  function getTodayFile(dir) {
265
169
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
266
170
  return join2(dir, `${today}.md`);
@@ -744,9 +648,28 @@ async function updateSessionTokens(sessionId, totalTokens) {
744
648
  await saveState(pruned);
745
649
  }
746
650
 
651
+ // src/lib/stdin.ts
652
+ async function readStdin() {
653
+ if (process.stdin.isTTY) {
654
+ return "";
655
+ }
656
+ return new Promise((resolve) => {
657
+ let data = "";
658
+ const timeout = setTimeout(() => resolve(data), 5e3);
659
+ process.stdin.setEncoding("utf-8");
660
+ process.stdin.on("data", (chunk) => {
661
+ data += chunk;
662
+ });
663
+ process.stdin.on("end", () => {
664
+ clearTimeout(timeout);
665
+ resolve(data);
666
+ });
667
+ });
668
+ }
669
+
747
670
  // src/commands/ask.ts
748
671
  function createAskCommand() {
749
- return new Command("ask").description("Send a prompt to Claude").argument("[prompt...]", "The prompt to send").option("-m, --model <model>", "Model to use").option("--max-turns <n>", "Max agent turns", parseInt).option("--max-budget-usd <n>", "Max budget in USD", parseFloat).option("-o, --output-format <format>", "Output format: text, json, stream-json").option("--system-prompt <text>", "System prompt override").option("--no-memory", "Skip memory injection").option("--memory <keys>", "Inject only specific memory keys (comma-separated)").option("--no-life", "Skip life/PARA context injection").option("--no-history", "Don't save to history").option("--raw", "Print raw JSON response").option("--token-footer", "Append token usage footer to response text").option(
672
+ return new Command("ask").description("Send a prompt to an AI model").argument("[prompt...]", "The prompt to send").option("-p, --provider <provider>", "Provider to use (claude, codex, gemini, ollama, opencode)").option("-m, --model <model>", "Model to use").option("--max-turns <n>", "Max agent turns", parseInt).option("--max-budget-usd <n>", "Max budget in USD", parseFloat).option("-o, --output-format <format>", "Output format: text, json, stream-json").option("--system-prompt <text>", "System prompt override").option("--no-memory", "Skip memory injection").option("--memory <keys>", "Inject only specific memory keys (comma-separated)").option("--no-life", "Skip life/PARA context injection").option("--no-history", "Don't save to history").option("--raw", "Print raw JSON response").option("--token-footer", "Append token usage footer to response text").option(
750
673
  "--max-session-tokens <n>",
751
674
  "Reset session if context exceeds this token count",
752
675
  parseInt
@@ -754,7 +677,7 @@ function createAskCommand() {
754
677
  try {
755
678
  await runAsk(promptParts, opts);
756
679
  } catch (err) {
757
- console.error(chalk.red(err.message));
680
+ console.error(pc.red(err.message));
758
681
  process.exit(1);
759
682
  }
760
683
  });
@@ -764,7 +687,7 @@ async function runAsk(promptParts, opts) {
764
687
  const stdinContent = await readStdin();
765
688
  const promptText = promptParts.join(" ");
766
689
  if (!promptText && !stdinContent) {
767
- console.error(chalk.red('No prompt provided. Usage: dproxy ask "your question"'));
690
+ console.error(pc.red('No prompt provided. Usage: dproxy ask "your question"'));
768
691
  process.exit(1);
769
692
  }
770
693
  let fullPrompt = promptText;
@@ -786,78 +709,76 @@ ${stdinContent}` : stdinContent;
786
709
  },
787
710
  config
788
711
  ) || void 0;
789
- let resumeSessionId = opts.resume;
712
+ let sessionId = opts.resume;
790
713
  const maxSessionTokens = opts.maxSessionTokens;
791
- if (resumeSessionId && maxSessionTokens) {
792
- const currentTokens = await getSessionTokens(resumeSessionId);
714
+ if (sessionId && maxSessionTokens) {
715
+ const currentTokens = await getSessionTokens(sessionId);
793
716
  if (currentTokens > maxSessionTokens) {
794
- resumeSessionId = void 0;
717
+ sessionId = void 0;
795
718
  }
796
719
  }
797
- const claudeOpts = {
720
+ const providerName = opts.provider ?? config.provider.default;
721
+ const adapter = resolveAdapter(providerName, config);
722
+ const request = {
798
723
  prompt: fullPrompt,
799
724
  model: opts.model ?? config.defaults.model,
800
725
  maxTurns: opts.maxTurns ?? config.defaults.maxTurns,
801
- maxBudgetUsd: opts.maxBudgetUsd,
802
726
  systemPrompt: opts.systemPrompt,
803
- appendSystemPrompt,
804
- resumeSessionId,
805
- continueSession: opts.continue
727
+ sessionId,
728
+ continueSession: opts.continue,
729
+ options: {
730
+ appendSystemPrompt,
731
+ maxBudgetUsd: opts.maxBudgetUsd
732
+ }
806
733
  };
807
- const result = await execClaude(claudeOpts);
808
- const resultText = result.result;
734
+ const result = await adapter.execute(request);
809
735
  if (result.sessionId && result.usage) {
810
- await updateSessionTokens(result.sessionId, result.usage.total);
736
+ await updateSessionTokens(result.sessionId, result.usage.totalTokens);
811
737
  }
812
738
  if (opts.tokenFooter && result.usage) {
813
739
  const u = result.usage;
814
740
  const parts = [];
815
- if (u.cacheWrite > 0) parts.push(`cW:${u.cacheWrite.toLocaleString()}`);
816
- if (u.cacheRead > 0) parts.push(`cR:${u.cacheRead.toLocaleString()}`);
817
- parts.push(`in:${u.input.toLocaleString()}`);
818
- parts.push(`out:${u.output.toLocaleString()}`);
819
- parts.push(`~${u.total.toLocaleString()}`);
741
+ parts.push(`in:${u.inputTokens.toLocaleString()}`);
742
+ parts.push(`out:${u.outputTokens.toLocaleString()}`);
743
+ parts.push(`~${u.totalTokens.toLocaleString()}`);
820
744
  const footer = `
821
745
 
822
746
  \u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014
823
747
  \`${parts.join(" \xB7 ")}\``;
824
- result.result += footer;
825
- if (result.raw && typeof result.raw === "object") {
826
- result.raw.result = result.result;
827
- }
748
+ result.text += footer;
828
749
  }
829
750
  if (opts.raw) {
830
751
  console.log(JSON.stringify(result.raw ?? result, null, 2));
831
752
  } else if (opts.outputFormat === "json") {
832
753
  console.log(
833
754
  JSON.stringify(
834
- { result: result.result, sessionId: result.sessionId, costUsd: result.costUsd },
755
+ { result: result.text, sessionId: result.sessionId, costUsd: result.costUsd },
835
756
  null,
836
757
  2
837
758
  )
838
759
  );
839
760
  } else {
840
- console.log(result.result);
761
+ console.log(result.text);
841
762
  }
842
763
  if (opts.history !== false) {
843
764
  await addHistoryEntry({
844
765
  prompt: fullPrompt,
845
- result: result.result,
846
- sessionId: result.sessionId,
847
- costUsd: result.costUsd,
766
+ result: result.text,
767
+ sessionId: result.sessionId ?? "",
768
+ costUsd: result.costUsd ?? 0,
848
769
  durationMs: result.durationMs,
849
770
  model: opts.model ?? config.defaults.model
850
771
  });
851
772
  }
852
- void addChatLog(promptText, resultText);
773
+ void addChatLog(promptText, result.text);
853
774
  }
854
775
 
855
776
  // src/commands/chat.ts
856
777
  import { readFile as readFile8, writeFile as writeFile3 } from "fs/promises";
857
778
  import { join as join8 } from "path";
858
779
  import { createInterface } from "readline";
859
- import chalk2 from "chalk";
860
780
  import { Command as Command2 } from "commander";
781
+ import pc2 from "picocolors";
861
782
  var SESSION_FILE = "current-session.json";
862
783
  async function loadSession() {
863
784
  try {
@@ -876,28 +797,30 @@ async function saveSession(session) {
876
797
  );
877
798
  }
878
799
  function createChatCommand() {
879
- return new Command2("chat").description("Start an interactive conversation with Claude").option("-c, --continue", "Continue last conversation").option("-r, --resume <id>", "Resume a specific session").option("-m, --model <model>", "Model to use").option("--max-turns <n>", "Max agent turns per message", parseInt).option("--no-memory", "Skip memory injection").option("--no-life", "Skip life/PARA context injection").action(async (opts) => {
800
+ return new Command2("chat").description("Start an interactive conversation").option("-p, --provider <provider>", "Provider to use (claude, codex, gemini, ollama, opencode)").option("-c, --continue", "Continue last conversation").option("-r, --resume <id>", "Resume a specific session").option("-m, --model <model>", "Model to use").option("--max-turns <n>", "Max agent turns per message", parseInt).option("--no-memory", "Skip memory injection").option("--no-life", "Skip life/PARA context injection").action(async (opts) => {
880
801
  try {
881
802
  await runChat(opts);
882
803
  } catch (err) {
883
- console.error(chalk2.red(err.message));
804
+ console.error(pc2.red(err.message));
884
805
  process.exit(1);
885
806
  }
886
807
  });
887
808
  }
888
809
  async function runChat(opts) {
889
810
  const config = await loadConfig();
811
+ const providerName = opts.provider ?? config.provider.default;
812
+ const adapter = resolveAdapter(providerName, config);
890
813
  let sessionId;
891
814
  if (opts.resume) {
892
815
  sessionId = opts.resume;
893
- console.log(chalk2.dim(`Resuming session: ${sessionId}`));
816
+ console.log(pc2.dim(`Resuming session: ${sessionId}`));
894
817
  } else if (opts.continue) {
895
818
  const prev = await loadSession();
896
819
  if (prev) {
897
820
  sessionId = prev.sessionId;
898
- console.log(chalk2.dim(`Continuing session: ${sessionId}`));
821
+ console.log(pc2.dim(`Continuing session: ${sessionId}`));
899
822
  } else {
900
- console.log(chalk2.dim("No previous session found. Starting new chat."));
823
+ console.log(pc2.dim("No previous session found. Starting new chat."));
901
824
  }
902
825
  }
903
826
  const appendSystemPrompt = await buildSystemPromptContext(
@@ -909,12 +832,12 @@ async function runChat(opts) {
909
832
  },
910
833
  config
911
834
  ) || void 0;
912
- console.log(chalk2.bold.blue("Claude Chat"));
913
- console.log(chalk2.dim('Type "exit" or Ctrl+C to quit.\n'));
835
+ console.log(pc2.bold(pc2.blue(`${adapter.provider} Chat`)));
836
+ console.log(pc2.dim('Type "exit" or Ctrl+C to quit.\n'));
914
837
  const rl = createInterface({
915
838
  input: process.stdin,
916
839
  output: process.stdout,
917
- prompt: chalk2.green("you > ")
840
+ prompt: pc2.green("you > ")
918
841
  });
919
842
  rl.prompt();
920
843
  rl.on("line", async (line) => {
@@ -924,22 +847,22 @@ async function runChat(opts) {
924
847
  return;
925
848
  }
926
849
  if (input === "exit" || input === "quit") {
927
- console.log(chalk2.dim("Bye!"));
850
+ console.log(pc2.dim("Bye!"));
928
851
  rl.close();
929
852
  return;
930
853
  }
931
854
  rl.pause();
932
855
  try {
933
- process.stdout.write(chalk2.dim("thinking...\r"));
934
- const result = await execClaude({
856
+ process.stdout.write(pc2.dim("thinking...\r"));
857
+ const result = await adapter.execute({
935
858
  prompt: input,
936
859
  model: opts.model ?? config.defaults.model,
937
860
  maxTurns: opts.maxTurns ?? config.defaults.maxTurns,
938
- appendSystemPrompt,
939
- resumeSessionId: sessionId
861
+ sessionId,
862
+ options: { appendSystemPrompt }
940
863
  });
941
864
  process.stdout.write("\r" + " ".repeat(20) + "\r");
942
- console.log(chalk2.cyan("claude > ") + result.result);
865
+ console.log(pc2.cyan(`${adapter.provider} > `) + result.text);
943
866
  console.log();
944
867
  if (!sessionId && result.sessionId) {
945
868
  sessionId = result.sessionId;
@@ -950,15 +873,15 @@ async function runChat(opts) {
950
873
  }
951
874
  await addHistoryEntry({
952
875
  prompt: input,
953
- result: result.result,
954
- sessionId: result.sessionId,
955
- costUsd: result.costUsd,
876
+ result: result.text,
877
+ sessionId: result.sessionId ?? "",
878
+ costUsd: result.costUsd ?? 0,
956
879
  durationMs: result.durationMs,
957
880
  model: opts.model ?? config.defaults.model
958
881
  });
959
- void addChatLog(input, result.result);
882
+ void addChatLog(input, result.text);
960
883
  } catch (err) {
961
- console.error(chalk2.red(err.message));
884
+ console.error(pc2.red(err.message));
962
885
  }
963
886
  rl.resume();
964
887
  rl.prompt();
@@ -969,18 +892,18 @@ async function runChat(opts) {
969
892
  }
970
893
 
971
894
  // src/commands/history.ts
972
- import chalk3 from "chalk";
973
895
  import { Command as Command3 } from "commander";
896
+ import pc3 from "picocolors";
974
897
  function printEntryList(entries, showCost = true) {
975
898
  if (entries.length === 0) {
976
- console.log(chalk3.dim("No history entries."));
899
+ console.log(pc3.dim("No history entries."));
977
900
  return;
978
901
  }
979
902
  for (const e of entries) {
980
903
  const date = new Date(e.timestamp).toLocaleString();
981
904
  const prompt = e.prompt.length > 60 ? e.prompt.slice(0, 60) + "\u2026" : e.prompt;
982
- const cost = showCost && e.costUsd ? chalk3.dim(`$${e.costUsd.toFixed(4)}`) : "";
983
- console.log(`${chalk3.dim(e.id.slice(0, 8))} ${chalk3.blue(date)} ${prompt} ${cost}`);
905
+ const cost = showCost && e.costUsd ? pc3.dim(`$${e.costUsd.toFixed(4)}`) : "";
906
+ console.log(`${pc3.dim(e.id.slice(0, 8))} ${pc3.blue(date)} ${prompt} ${cost}`);
984
907
  }
985
908
  }
986
909
  function createHistoryCommand() {
@@ -992,17 +915,17 @@ function createHistoryCommand() {
992
915
  cmd.command("show <id>").description("Show a specific history entry").action(async (id) => {
993
916
  const entry = await getHistoryEntry(id);
994
917
  if (!entry) {
995
- console.error(chalk3.red(`Entry not found: ${id}`));
918
+ console.error(pc3.red(`Entry not found: ${id}`));
996
919
  process.exit(1);
997
920
  }
998
- console.log(chalk3.bold("Prompt:"));
921
+ console.log(pc3.bold("Prompt:"));
999
922
  console.log(entry.prompt);
1000
923
  console.log();
1001
- console.log(chalk3.bold("Response:"));
924
+ console.log(pc3.bold("Response:"));
1002
925
  console.log(entry.result);
1003
926
  console.log();
1004
927
  console.log(
1005
- chalk3.dim(
928
+ pc3.dim(
1006
929
  `Session: ${entry.sessionId || "n/a"} | Cost: $${entry.costUsd.toFixed(4)} | Duration: ${entry.durationMs}ms | ${entry.timestamp}`
1007
930
  )
1008
931
  );
@@ -1013,7 +936,7 @@ function createHistoryCommand() {
1013
936
  });
1014
937
  cmd.command("clear").description("Clear history").option("--before <date>", "Clear entries before this date").action(async (opts) => {
1015
938
  const removed = await clearHistory(opts.before);
1016
- console.log(chalk3.green(`Cleared ${removed} entries.`));
939
+ console.log(pc3.green(`Cleared ${removed} entries.`));
1017
940
  });
1018
941
  cmd.action(async () => {
1019
942
  const entries = await listHistory(20);
@@ -1024,10 +947,10 @@ function createHistoryCommand() {
1024
947
 
1025
948
  // src/commands/init.ts
1026
949
  import { createInterface as createInterface2 } from "readline";
1027
- import chalk4 from "chalk";
1028
950
  import { Command as Command4 } from "commander";
951
+ import pc4 from "picocolors";
1029
952
  function ask(rl, question, def = "") {
1030
- const suffix = def ? chalk4.dim(` (${def})`) : "";
953
+ const suffix = def ? pc4.dim(` (${def})`) : "";
1031
954
  return new Promise((resolve) => {
1032
955
  rl.question(`${question}${suffix}: `, (answer) => {
1033
956
  resolve(answer.trim() || def);
@@ -1037,7 +960,7 @@ function ask(rl, question, def = "") {
1037
960
  function askBool(rl, question, def = false) {
1038
961
  const hint = def ? "Y/n" : "y/N";
1039
962
  return new Promise((resolve) => {
1040
- rl.question(`${question} ${chalk4.dim(`[${hint}]`)}: `, (answer) => {
963
+ rl.question(`${question} ${pc4.dim(`[${hint}]`)}: `, (answer) => {
1041
964
  const a = answer.trim().toLowerCase();
1042
965
  if (!a) return resolve(def);
1043
966
  resolve(a === "y" || a === "yes");
@@ -1049,7 +972,7 @@ function createInitCommand() {
1049
972
  try {
1050
973
  await runInit();
1051
974
  } catch (err) {
1052
- console.error(chalk4.red(err.message));
975
+ console.error(pc4.red(err.message));
1053
976
  process.exit(1);
1054
977
  }
1055
978
  });
@@ -1057,8 +980,8 @@ function createInitCommand() {
1057
980
  async function runInit() {
1058
981
  const config = await loadConfig();
1059
982
  console.log();
1060
- console.log(chalk4.bold.blue("dproxy \u2014 Setup"));
1061
- console.log(chalk4.dim("Configure your environment. Press Enter to accept defaults.\n"));
983
+ console.log(pc4.bold(pc4.blue("dproxy \u2014 Setup")));
984
+ console.log(pc4.dim("Configure your environment. Press Enter to accept defaults.\n"));
1062
985
  const rl = createInterface2({
1063
986
  input: process.stdin,
1064
987
  output: process.stdout
@@ -1077,8 +1000,8 @@ async function runInit() {
1077
1000
  config.claude.skipPermissions
1078
1001
  );
1079
1002
  console.log();
1080
- console.log(chalk4.bold("Optional integrations"));
1081
- console.log(chalk4.dim("These inject extra context into every prompt.\n"));
1003
+ console.log(pc4.bold("Optional integrations"));
1004
+ console.log(pc4.dim("These inject extra context into every prompt.\n"));
1082
1005
  const enableWorkspace = await askBool(
1083
1006
  rl,
1084
1007
  "Enable workspace context (IDENTITY.md, SOUL.md, USER.md, MEMORY.md)?",
@@ -1103,8 +1026,8 @@ async function runInit() {
1103
1026
  config.initialized = true;
1104
1027
  await saveConfig(config);
1105
1028
  console.log();
1106
- console.log(chalk4.green("Configuration saved to ") + chalk4.dim(getDataDir() + "/config.json"));
1107
- console.log(chalk4.green("You're ready to go! Try: ") + chalk4.bold('dproxy "hello"'));
1029
+ console.log(pc4.green("Configuration saved to ") + pc4.dim(getDataDir() + "/config.json"));
1030
+ console.log(pc4.green("You're ready to go! Try: ") + pc4.bold('dproxy "hello"'));
1108
1031
  console.log();
1109
1032
  } finally {
1110
1033
  rl.close();
@@ -1114,21 +1037,21 @@ async function requireInit() {
1114
1037
  const config = await loadConfig();
1115
1038
  if (config.initialized) return;
1116
1039
  console.error(
1117
- chalk4.yellow("dproxy is not configured yet. Run ") + chalk4.bold("dproxy init") + chalk4.yellow(" to set up your environment.")
1040
+ pc4.yellow("dproxy is not configured yet. Run ") + pc4.bold("dproxy init") + pc4.yellow(" to set up your environment.")
1118
1041
  );
1119
1042
  process.exit(1);
1120
1043
  }
1121
1044
 
1122
1045
  // src/commands/memory.ts
1123
- import chalk5 from "chalk";
1124
1046
  import { Command as Command5 } from "commander";
1047
+ import pc5 from "picocolors";
1125
1048
  function printKeyList(keys) {
1126
1049
  if (keys.length === 0) {
1127
- console.log(chalk5.dim("No memory entries."));
1050
+ console.log(pc5.dim("No memory entries."));
1128
1051
  return;
1129
1052
  }
1130
1053
  for (const key of keys) {
1131
- console.log(chalk5.blue(key));
1054
+ console.log(pc5.blue(key));
1132
1055
  }
1133
1056
  }
1134
1057
  function createMemoryCommand() {
@@ -1136,12 +1059,12 @@ function createMemoryCommand() {
1136
1059
  cmd.command("set <key> <value...>").description("Set a memory entry").action(async (key, valueParts) => {
1137
1060
  const value = valueParts.join(" ");
1138
1061
  await setMemory(key, value);
1139
- console.log(chalk5.green(`Memory "${key}" saved.`));
1062
+ console.log(pc5.green(`Memory "${key}" saved.`));
1140
1063
  });
1141
1064
  cmd.command("get <key>").description("Get a memory entry").action(async (key) => {
1142
1065
  const value = await getMemory(key);
1143
1066
  if (value === null) {
1144
- console.error(chalk5.red(`Memory "${key}" not found.`));
1067
+ console.error(pc5.red(`Memory "${key}" not found.`));
1145
1068
  process.exit(1);
1146
1069
  }
1147
1070
  console.log(value);
@@ -1153,20 +1076,20 @@ function createMemoryCommand() {
1153
1076
  cmd.command("search <query>").description("Search memory entries").action(async (query) => {
1154
1077
  const results = await searchMemory(query);
1155
1078
  if (results.length === 0) {
1156
- console.log(chalk5.dim("No matches."));
1079
+ console.log(pc5.dim("No matches."));
1157
1080
  return;
1158
1081
  }
1159
1082
  for (const { key, content } of results) {
1160
1083
  const preview = content.length > 80 ? content.slice(0, 80) + "\u2026" : content;
1161
- console.log(`${chalk5.blue(key)}: ${preview}`);
1084
+ console.log(`${pc5.blue(key)}: ${preview}`);
1162
1085
  }
1163
1086
  });
1164
1087
  cmd.command("delete <key>").description("Delete a memory entry").action(async (key) => {
1165
1088
  const deleted = await deleteMemory(key);
1166
1089
  if (deleted) {
1167
- console.log(chalk5.green(`Memory "${key}" deleted.`));
1090
+ console.log(pc5.green(`Memory "${key}" deleted.`));
1168
1091
  } else {
1169
- console.error(chalk5.red(`Memory "${key}" not found.`));
1092
+ console.error(pc5.red(`Memory "${key}" not found.`));
1170
1093
  }
1171
1094
  });
1172
1095
  cmd.action(async () => {
@@ -1178,8 +1101,8 @@ function createMemoryCommand() {
1178
1101
 
1179
1102
  // src/commands/template.ts
1180
1103
  import { readFile as readFile10 } from "fs/promises";
1181
- import chalk6 from "chalk";
1182
1104
  import { Command as Command6 } from "commander";
1105
+ import pc6 from "picocolors";
1183
1106
  import { parse as parse2 } from "yaml";
1184
1107
 
1185
1108
  // src/lib/template-store.ts
@@ -1246,32 +1169,32 @@ function createTemplateCommand() {
1246
1169
  cmd.command("list").description("List all templates").action(async () => {
1247
1170
  const templates = await listTemplates();
1248
1171
  if (templates.length === 0) {
1249
- console.log(chalk6.dim("No templates. Use 'dproxy template add <name>' to create one."));
1172
+ console.log(pc6.dim("No templates. Use 'dproxy template add <name>' to create one."));
1250
1173
  return;
1251
1174
  }
1252
1175
  for (const t of templates) {
1253
- const desc = t.description ? chalk6.dim(` \u2014 ${t.description}`) : "";
1254
- console.log(`${chalk6.blue(t.name)}${desc}`);
1176
+ const desc = t.description ? pc6.dim(` \u2014 ${t.description}`) : "";
1177
+ console.log(`${pc6.blue(t.name)}${desc}`);
1255
1178
  }
1256
1179
  });
1257
1180
  cmd.command("show <name>").description("Show template details").action(async (name) => {
1258
1181
  const t = await getTemplate(name);
1259
1182
  if (!t) {
1260
- console.error(chalk6.red(`Template "${name}" not found.`));
1183
+ console.error(pc6.red(`Template "${name}" not found.`));
1261
1184
  process.exit(1);
1262
1185
  }
1263
- console.log(chalk6.bold(t.name));
1264
- if (t.description) console.log(chalk6.dim(t.description));
1186
+ console.log(pc6.bold(t.name));
1187
+ if (t.description) console.log(pc6.dim(t.description));
1265
1188
  console.log();
1266
- console.log(chalk6.bold("Prompt:"));
1189
+ console.log(pc6.bold("Prompt:"));
1267
1190
  console.log(t.prompt);
1268
1191
  if (t.variables?.length) {
1269
1192
  console.log();
1270
- console.log(chalk6.bold("Variables:"));
1193
+ console.log(pc6.bold("Variables:"));
1271
1194
  for (const v of t.variables) {
1272
- const req = v.required ? chalk6.red("*") : "";
1273
- const def = v.default ? chalk6.dim(` (default: ${v.default})`) : "";
1274
- const src = v.source ? chalk6.dim(` [${v.source}]`) : "";
1195
+ const req = v.required ? pc6.red("*") : "";
1196
+ const def = v.default ? pc6.dim(` (default: ${v.default})`) : "";
1197
+ const src = v.source ? pc6.dim(` [${v.source}]`) : "";
1275
1198
  console.log(` {{${v.name}}}${req}${def}${src}`);
1276
1199
  }
1277
1200
  }
@@ -1289,16 +1212,16 @@ function createTemplateCommand() {
1289
1212
  prompt: opts.prompt
1290
1213
  };
1291
1214
  } else {
1292
- console.error(chalk6.red("Provide --file or --prompt"));
1215
+ console.error(pc6.red("Provide --file or --prompt"));
1293
1216
  process.exit(1);
1294
1217
  }
1295
1218
  await saveTemplate(template);
1296
- console.log(chalk6.green(`Template "${name}" saved.`));
1219
+ console.log(pc6.green(`Template "${name}" saved.`));
1297
1220
  });
1298
1221
  cmd.command("run <name>").description("Run a template").option("--var <pairs...>", "Variables as key=value pairs").option("-m, --model <model>", "Model override").option("--raw", "Print raw JSON response").action(async (name, opts) => {
1299
1222
  const t = await getTemplate(name);
1300
1223
  if (!t) {
1301
- console.error(chalk6.red(`Template "${name}" not found.`));
1224
+ console.error(pc6.red(`Template "${name}" not found.`));
1302
1225
  process.exit(1);
1303
1226
  }
1304
1227
  const vars = {};
@@ -1322,7 +1245,7 @@ function createTemplateCommand() {
1322
1245
  vars[v.name] = v.default;
1323
1246
  }
1324
1247
  if (v.required && !(v.name in vars)) {
1325
- console.error(chalk6.red(`Missing required variable: {{${v.name}}}`));
1248
+ console.error(pc6.red(`Missing required variable: {{${v.name}}}`));
1326
1249
  process.exit(1);
1327
1250
  }
1328
1251
  }
@@ -1341,9 +1264,9 @@ function createTemplateCommand() {
1341
1264
  cmd.command("delete <name>").description("Delete a template").action(async (name) => {
1342
1265
  const deleted = await deleteTemplate(name);
1343
1266
  if (deleted) {
1344
- console.log(chalk6.green(`Template "${name}" deleted.`));
1267
+ console.log(pc6.green(`Template "${name}" deleted.`));
1345
1268
  } else {
1346
- console.error(chalk6.red(`Template "${name}" not found.`));
1269
+ console.error(pc6.red(`Template "${name}" not found.`));
1347
1270
  }
1348
1271
  });
1349
1272
  return cmd;
@@ -1351,7 +1274,8 @@ function createTemplateCommand() {
1351
1274
 
1352
1275
  // src/index.ts
1353
1276
  var program = new Command7();
1354
- program.name("dproxy").description("Universal adapter for invoking models via local CLIs").version("0.1.0");
1277
+ program.enablePositionalOptions();
1278
+ program.name("dproxy").description("Universal adapter for invoking models via local CLIs").version("1.0.0");
1355
1279
  program.addCommand(createInitCommand());
1356
1280
  var guarded = (cmd) => {
1357
1281
  cmd.hook("preAction", async () => {
@@ -1367,12 +1291,12 @@ program.addCommand(guarded(createTemplateCommand()));
1367
1291
  var configCmd = new Command7("config").description("Manage configuration");
1368
1292
  configCmd.command("set <key> <value>").description("Set a config value (e.g., memory.autoInject true)").action(async (key, value) => {
1369
1293
  await setConfigValue(key, value);
1370
- console.log(chalk7.green(`${key} = ${value}`));
1294
+ console.log(pc7.green(`${key} = ${value}`));
1371
1295
  });
1372
1296
  configCmd.command("get <key>").description("Get a config value").action(async (key) => {
1373
1297
  const value = await getConfigValue(key);
1374
1298
  if (value === void 0) {
1375
- console.error(chalk7.red(`Config key "${key}" not found.`));
1299
+ console.error(pc7.red(`Config key "${key}" not found.`));
1376
1300
  process.exit(1);
1377
1301
  }
1378
1302
  console.log(JSON.stringify(value, null, 2));
@@ -1382,7 +1306,7 @@ configCmd.action(async () => {
1382
1306
  console.log(JSON.stringify(config, null, 2));
1383
1307
  });
1384
1308
  program.addCommand(guarded(configCmd));
1385
- program.argument("[prompt...]", "Send a quick prompt (shorthand for 'dproxy ask')").option("-m, --model <model>", "Model to use").option("--max-turns <n>", "Max agent turns", parseInt).option("--max-budget-usd <n>", "Max budget in USD", parseFloat).option("-o, --output-format <format>", "Output format").option("--no-memory", "Skip memory injection").option("--memory <keys>", "Inject specific memory keys").option("--no-life", "Skip life/PARA context injection").option("--no-history", "Don't save to history").option("--raw", "Print raw JSON response").option("--token-footer", "Append token usage footer to response text").option("--max-session-tokens <n>", "Reset session if context exceeds this token count", parseInt).option("-c, --continue", "Continue last conversation").option("-r, --resume <id>", "Resume a specific session").action(async (promptParts, opts) => {
1309
+ program.argument("[prompt...]", "Send a quick prompt (shorthand for 'dproxy ask')").option("-p, --provider <provider>", "Provider to use (claude, codex, gemini, ollama, opencode)").option("-m, --model <model>", "Model to use").option("--max-turns <n>", "Max agent turns", parseInt).option("--max-budget-usd <n>", "Max budget in USD", parseFloat).option("-o, --output-format <format>", "Output format").option("--no-memory", "Skip memory injection").option("--memory <keys>", "Inject specific memory keys").option("--no-life", "Skip life/PARA context injection").option("--no-history", "Don't save to history").option("--raw", "Print raw JSON response").option("--token-footer", "Append token usage footer to response text").option("--max-session-tokens <n>", "Reset session if context exceeds this token count", parseInt).option("-c, --continue", "Continue last conversation").option("-r, --resume <id>", "Resume a specific session").action(async (promptParts, opts) => {
1386
1310
  if (promptParts.length === 0) {
1387
1311
  program.help();
1388
1312
  return;
@@ -1391,7 +1315,7 @@ program.argument("[prompt...]", "Send a quick prompt (shorthand for 'dproxy ask'
1391
1315
  try {
1392
1316
  await runAsk(promptParts, opts);
1393
1317
  } catch (err) {
1394
- console.error(chalk7.red(err.message));
1318
+ console.error(pc7.red(err.message));
1395
1319
  process.exit(1);
1396
1320
  }
1397
1321
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dtoolkit/dproxy",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "Universal adapter for invoking models via local CLIs",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -32,9 +32,14 @@
32
32
  "adapter"
33
33
  ],
34
34
  "dependencies": {
35
- "chalk": "^5.4.0",
35
+ "picocolors": "^1.1.1",
36
36
  "commander": "^13.0.0",
37
37
  "yaml": "^2.7.0",
38
+ "@dtoolkit/adapter-claude": "1.0.0",
39
+ "@dtoolkit/adapter-codex": "1.0.0",
40
+ "@dtoolkit/adapter-gemini": "1.0.0",
41
+ "@dtoolkit/adapter-ollama": "1.0.0",
42
+ "@dtoolkit/adapter-opencode": "1.0.0",
38
43
  "@dtoolkit/core": "0.1.0"
39
44
  },
40
45
  "devDependencies": {