@dtoolkit/dproxy 0.2.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 +184 -25
  2. package/dist/index.js +97 -173
  3. package/package.json +6 -1
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="../../logo.png" alt="dtoolkit" />
2
+ <img src="https://raw.githubusercontent.com/ivncmp/dtoolkit/main/logo.png" alt="dtoolkit" />
3
3
  </p>
4
4
 
5
5
  <h1 align="center">@dtoolkit/dproxy</h1>
@@ -20,22 +20,162 @@ npm install -g @dtoolkit/dproxy
20
20
 
21
21
  ```bash
22
22
  dproxy init # interactive setup wizard
23
- dproxy "explain this" # single-shot prompt
23
+ dproxy "explain this" # single-shot prompt (default provider)
24
24
  dproxy chat # interactive REPL
25
25
  ```
26
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
+
27
51
  ## Commands
28
52
 
29
- | Command | Description |
30
- | --- | --- |
31
- | `dproxy init` | Interactive setup (required before first use) |
32
- | `dproxy <prompt>` | Single-shot prompt (shorthand for `dproxy ask`) |
33
- | `dproxy ask <prompt>` | Send a prompt with full context injection |
34
- | `dproxy chat` | Interactive REPL with session tracking |
35
- | `dproxy history` | List, show, search, or clear prompt history |
36
- | `dproxy memory` | CRUD for named memory snippets |
37
- | `dproxy template` | CRUD and execution of YAML prompt templates |
38
- | `dproxy config` | Get/set configuration |
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 |
39
179
 
40
180
  ## Context injection
41
181
 
@@ -46,19 +186,38 @@ Every prompt is enriched with context from multiple sources, in priority order:
46
186
  3. **Memory snippets** — named markdown snippets (truncated to 4,000 chars)
47
187
  4. **Life/PARA context** — semantic knowledge base (truncated to 12,000 chars)
48
188
 
49
- ## Key flags
50
-
51
- ```
52
- --no-memory Skip memory injection
53
- --memory <keys> Inject specific memory keys (comma-separated)
54
- --no-life Skip life/PARA context
55
- --no-history Don't save to history
56
- --raw Print raw JSON response
57
- -c, --continue Continue last conversation
58
- -r, --resume <id> Resume a specific session
59
- -m, --model <model> Model to use
60
- --max-turns <n> Max agent turns
61
- --max-budget-usd <n> Max budget in USD
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
+ }
62
221
  ```
63
222
 
64
223
  ## Data storage
package/dist/index.js CHANGED
@@ -8,8 +8,32 @@ import pc7 from "picocolors";
8
8
  import { Command } from "commander";
9
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
@@ -786,70 +709,68 @@ ${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
@@ -876,7 +797,7 @@ 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) {
@@ -887,6 +808,8 @@ function createChatCommand() {
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;
@@ -909,7 +832,7 @@ async function runChat(opts) {
909
832
  },
910
833
  config
911
834
  ) || void 0;
912
- console.log(pc2.bold(pc2.blue("Claude Chat")));
835
+ console.log(pc2.bold(pc2.blue(`${adapter.provider} Chat`)));
913
836
  console.log(pc2.dim('Type "exit" or Ctrl+C to quit.\n'));
914
837
  const rl = createInterface({
915
838
  input: process.stdin,
@@ -931,15 +854,15 @@ async function runChat(opts) {
931
854
  rl.pause();
932
855
  try {
933
856
  process.stdout.write(pc2.dim("thinking...\r"));
934
- const result = await execClaude({
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(pc2.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,13 +873,13 @@ 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
884
  console.error(pc2.red(err.message));
962
885
  }
@@ -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 () => {
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dtoolkit/dproxy",
3
- "version": "0.2.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",
@@ -35,6 +35,11 @@
35
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": {