@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.
- package/README.md +238 -0
- package/dist/index.js +161 -237
- 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/
|
|
12
|
-
import {
|
|
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
|
-
|
|
55
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
712
|
+
let sessionId = opts.resume;
|
|
790
713
|
const maxSessionTokens = opts.maxSessionTokens;
|
|
791
|
-
if (
|
|
792
|
-
const currentTokens = await getSessionTokens(
|
|
714
|
+
if (sessionId && maxSessionTokens) {
|
|
715
|
+
const currentTokens = await getSessionTokens(sessionId);
|
|
793
716
|
if (currentTokens > maxSessionTokens) {
|
|
794
|
-
|
|
717
|
+
sessionId = void 0;
|
|
795
718
|
}
|
|
796
719
|
}
|
|
797
|
-
const
|
|
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
|
-
|
|
804
|
-
|
|
805
|
-
|
|
727
|
+
sessionId,
|
|
728
|
+
continueSession: opts.continue,
|
|
729
|
+
options: {
|
|
730
|
+
appendSystemPrompt,
|
|
731
|
+
maxBudgetUsd: opts.maxBudgetUsd
|
|
732
|
+
}
|
|
806
733
|
};
|
|
807
|
-
const result = await
|
|
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.
|
|
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
|
-
|
|
816
|
-
|
|
817
|
-
parts.push(
|
|
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.
|
|
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.
|
|
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.
|
|
761
|
+
console.log(result.text);
|
|
841
762
|
}
|
|
842
763
|
if (opts.history !== false) {
|
|
843
764
|
await addHistoryEntry({
|
|
844
765
|
prompt: fullPrompt,
|
|
845
|
-
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,
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
821
|
+
console.log(pc2.dim(`Continuing session: ${sessionId}`));
|
|
899
822
|
} else {
|
|
900
|
-
console.log(
|
|
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(
|
|
913
|
-
console.log(
|
|
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:
|
|
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(
|
|
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(
|
|
934
|
-
const result = await
|
|
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
|
-
|
|
939
|
-
|
|
861
|
+
sessionId,
|
|
862
|
+
options: { appendSystemPrompt }
|
|
940
863
|
});
|
|
941
864
|
process.stdout.write("\r" + " ".repeat(20) + "\r");
|
|
942
|
-
console.log(
|
|
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.
|
|
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.
|
|
882
|
+
void addChatLog(input, result.text);
|
|
960
883
|
} catch (err) {
|
|
961
|
-
console.error(
|
|
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(
|
|
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 ?
|
|
983
|
-
console.log(`${
|
|
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(
|
|
918
|
+
console.error(pc3.red(`Entry not found: ${id}`));
|
|
996
919
|
process.exit(1);
|
|
997
920
|
}
|
|
998
|
-
console.log(
|
|
921
|
+
console.log(pc3.bold("Prompt:"));
|
|
999
922
|
console.log(entry.prompt);
|
|
1000
923
|
console.log();
|
|
1001
|
-
console.log(
|
|
924
|
+
console.log(pc3.bold("Response:"));
|
|
1002
925
|
console.log(entry.result);
|
|
1003
926
|
console.log();
|
|
1004
927
|
console.log(
|
|
1005
|
-
|
|
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(
|
|
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 ?
|
|
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} ${
|
|
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(
|
|
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(
|
|
1061
|
-
console.log(
|
|
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(
|
|
1081
|
-
console.log(
|
|
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(
|
|
1107
|
-
console.log(
|
|
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
|
-
|
|
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(
|
|
1050
|
+
console.log(pc5.dim("No memory entries."));
|
|
1128
1051
|
return;
|
|
1129
1052
|
}
|
|
1130
1053
|
for (const key of keys) {
|
|
1131
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(`${
|
|
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(
|
|
1090
|
+
console.log(pc5.green(`Memory "${key}" deleted.`));
|
|
1168
1091
|
} else {
|
|
1169
|
-
console.error(
|
|
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(
|
|
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 ?
|
|
1254
|
-
console.log(`${
|
|
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(
|
|
1183
|
+
console.error(pc6.red(`Template "${name}" not found.`));
|
|
1261
1184
|
process.exit(1);
|
|
1262
1185
|
}
|
|
1263
|
-
console.log(
|
|
1264
|
-
if (t.description) console.log(
|
|
1186
|
+
console.log(pc6.bold(t.name));
|
|
1187
|
+
if (t.description) console.log(pc6.dim(t.description));
|
|
1265
1188
|
console.log();
|
|
1266
|
-
console.log(
|
|
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(
|
|
1193
|
+
console.log(pc6.bold("Variables:"));
|
|
1271
1194
|
for (const v of t.variables) {
|
|
1272
|
-
const req = v.required ?
|
|
1273
|
-
const def = v.default ?
|
|
1274
|
-
const src = 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(
|
|
1215
|
+
console.error(pc6.red("Provide --file or --prompt"));
|
|
1293
1216
|
process.exit(1);
|
|
1294
1217
|
}
|
|
1295
1218
|
await saveTemplate(template);
|
|
1296
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
1267
|
+
console.log(pc6.green(`Template "${name}" deleted.`));
|
|
1345
1268
|
} else {
|
|
1346
|
-
console.error(
|
|
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.
|
|
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(
|
|
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(
|
|
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(
|
|
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": "
|
|
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
|
-
"
|
|
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": {
|