wraptc 1.0.2 → 1.0.4
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/bin/wraptc +4 -4
- package/package.json +2 -2
- package/src/cli/__tests__/cli.test.ts +337 -0
- package/src/cli/index.ts +149 -0
- package/src/core/__tests__/fixtures/configs/project-config.json +14 -0
- package/src/core/__tests__/fixtures/configs/system-config.json +14 -0
- package/src/core/__tests__/fixtures/configs/user-config.json +15 -0
- package/src/core/__tests__/integration/integration.test.ts +241 -0
- package/src/core/__tests__/integration/mock-coder-adapter.test.ts +243 -0
- package/src/core/__tests__/test-utils.ts +136 -0
- package/src/core/__tests__/unit/adapters/runner.test.ts +302 -0
- package/src/core/__tests__/unit/basic-test.test.ts +44 -0
- package/src/core/__tests__/unit/basic.test.ts +12 -0
- package/src/core/__tests__/unit/config.test.ts +244 -0
- package/src/core/__tests__/unit/error-patterns.test.ts +181 -0
- package/src/core/__tests__/unit/memory-monitor.test.ts +354 -0
- package/src/core/__tests__/unit/plugin/registry.test.ts +356 -0
- package/src/core/__tests__/unit/providers/codex.test.ts +173 -0
- package/src/core/__tests__/unit/providers/configurable.test.ts +429 -0
- package/src/core/__tests__/unit/providers/gemini.test.ts +251 -0
- package/src/core/__tests__/unit/providers/opencode.test.ts +258 -0
- package/src/core/__tests__/unit/providers/qwen-code.test.ts +195 -0
- package/src/core/__tests__/unit/providers/simple-codex.test.ts +18 -0
- package/src/core/__tests__/unit/router.test.ts +967 -0
- package/src/core/__tests__/unit/state.test.ts +1079 -0
- package/src/core/__tests__/unit/unified/capabilities.test.ts +186 -0
- package/src/core/__tests__/unit/wrap-terminalcoder.test.ts +32 -0
- package/src/core/adapters/builtin/codex.ts +35 -0
- package/src/core/adapters/builtin/gemini.ts +34 -0
- package/src/core/adapters/builtin/index.ts +31 -0
- package/src/core/adapters/builtin/mock-coder.ts +148 -0
- package/src/core/adapters/builtin/qwen.ts +34 -0
- package/src/core/adapters/define.ts +48 -0
- package/src/core/adapters/index.ts +43 -0
- package/src/core/adapters/loader.ts +143 -0
- package/src/core/adapters/provider-bridge.ts +190 -0
- package/src/core/adapters/runner.ts +437 -0
- package/src/core/adapters/types.ts +172 -0
- package/src/core/config.ts +290 -0
- package/src/core/define-provider.ts +212 -0
- package/src/core/error-patterns.ts +147 -0
- package/src/core/index.ts +130 -0
- package/src/core/memory-monitor.ts +171 -0
- package/src/core/plugin/builtin.ts +87 -0
- package/src/core/plugin/index.ts +34 -0
- package/src/core/plugin/registry.ts +350 -0
- package/src/core/plugin/types.ts +209 -0
- package/src/core/provider-factory.ts +397 -0
- package/src/core/provider-loader.ts +171 -0
- package/src/core/providers/codex.ts +56 -0
- package/src/core/providers/configurable.ts +637 -0
- package/src/core/providers/custom.ts +261 -0
- package/src/core/providers/gemini.ts +41 -0
- package/src/core/providers/index.ts +383 -0
- package/src/core/providers/opencode.ts +168 -0
- package/src/core/providers/qwen-code.ts +41 -0
- package/src/core/router.ts +370 -0
- package/src/core/state.ts +258 -0
- package/src/core/types.ts +206 -0
- package/src/core/unified/capabilities.ts +184 -0
- package/src/core/unified/errors.ts +141 -0
- package/src/core/unified/index.ts +29 -0
- package/src/core/unified/output.ts +189 -0
- package/src/core/wrap-terminalcoder.ts +245 -0
- package/src/mcp/__tests__/server.test.ts +295 -0
- package/src/mcp/server.ts +284 -0
- package/src/test-fixtures/mock-coder.sh +194 -0
- package/dist/cli/index.js +0 -16501
- package/dist/core/index.js +0 -7531
- package/dist/mcp/server.js +0 -14568
- package/dist/wraptc-1.0.2.tgz +0 -0
package/bin/wraptc
CHANGED
|
@@ -4,9 +4,9 @@ const args = process.argv.slice(2);
|
|
|
4
4
|
|
|
5
5
|
// Check if --mcp flag is present or first command is 'mcp'
|
|
6
6
|
if (args.includes('--mcp') || args[0] === 'mcp') {
|
|
7
|
-
// Run MCP server
|
|
8
|
-
|
|
7
|
+
// Run MCP server from source
|
|
8
|
+
import('../src/mcp/server.ts');
|
|
9
9
|
} else {
|
|
10
|
-
// Run CLI
|
|
11
|
-
|
|
10
|
+
// Run CLI from source
|
|
11
|
+
import('../src/cli/index.ts');
|
|
12
12
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wraptc",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Unified CLI wrapper for multiple coding AI agents with intelligent routing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/core/index.js",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"typescript": "^5.0.0"
|
|
64
64
|
},
|
|
65
65
|
"files": [
|
|
66
|
-
"
|
|
66
|
+
"src",
|
|
67
67
|
"bin",
|
|
68
68
|
"README.md",
|
|
69
69
|
"LICENSE",
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
import { WrapTerminalCoder } from "../../core/index.js";
|
|
3
|
+
import { ConfigLoader } from "../../core/index.js";
|
|
4
|
+
import { StateManager } from "../../core/index.js";
|
|
5
|
+
import { Router } from "../../core/index.js";
|
|
6
|
+
|
|
7
|
+
// Mock the core modules
|
|
8
|
+
mock.module("../../core/index.js", () => {
|
|
9
|
+
return {
|
|
10
|
+
WrapTerminalCoder: {
|
|
11
|
+
create: mock(async () => ({
|
|
12
|
+
providers: new Map(),
|
|
13
|
+
config: {},
|
|
14
|
+
stateManager: new StateManager(),
|
|
15
|
+
router: new Router(new Map(), { config: {}, stateManager: new StateManager() }),
|
|
16
|
+
route: mock(async (req: any) => ({
|
|
17
|
+
provider: "gemini",
|
|
18
|
+
text: "Mock response",
|
|
19
|
+
usage: { inputTokens: 10, outputTokens: 20 },
|
|
20
|
+
meta: { elapsedMs: 100 },
|
|
21
|
+
})),
|
|
22
|
+
routeStream: async function* (req: any) {
|
|
23
|
+
yield {
|
|
24
|
+
provider: "gemini",
|
|
25
|
+
text: "Mock stream response",
|
|
26
|
+
usage: { inputTokens: 10, outputTokens: 20 },
|
|
27
|
+
meta: { elapsedMs: 100 },
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
getProviderInfo: mock(async () => [
|
|
31
|
+
{
|
|
32
|
+
id: "gemini",
|
|
33
|
+
displayName: "Gemini CLI",
|
|
34
|
+
requestsToday: 5,
|
|
35
|
+
outOfCreditsUntil: undefined,
|
|
36
|
+
},
|
|
37
|
+
]),
|
|
38
|
+
})),
|
|
39
|
+
},
|
|
40
|
+
ConfigLoader: mock(() => ({
|
|
41
|
+
loadConfig: mock(async () => ({
|
|
42
|
+
routing: { defaultOrder: ["gemini", "qwen-code", "codex"] },
|
|
43
|
+
providers: {},
|
|
44
|
+
credits: { providers: {} },
|
|
45
|
+
})),
|
|
46
|
+
})),
|
|
47
|
+
StateManager: mock(() => ({
|
|
48
|
+
initialize: mock(async () => {}),
|
|
49
|
+
getProviderState: mock(async () => ({
|
|
50
|
+
requestsToday: 0,
|
|
51
|
+
outOfCreditsUntil: undefined,
|
|
52
|
+
lastErrors: [],
|
|
53
|
+
})),
|
|
54
|
+
recordSuccess: mock(async () => {}),
|
|
55
|
+
recordError: mock(async () => {}),
|
|
56
|
+
})),
|
|
57
|
+
Router: mock(() => ({
|
|
58
|
+
route: mock(async (req: any) => ({
|
|
59
|
+
provider: "gemini",
|
|
60
|
+
text: "Mock response",
|
|
61
|
+
})),
|
|
62
|
+
})),
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe("CLI Integration", () => {
|
|
67
|
+
describe("Providers Command", () => {
|
|
68
|
+
test("should list providers", async () => {
|
|
69
|
+
const wtc = await WrapTerminalCoder.create();
|
|
70
|
+
const providers = await wtc.getProviderInfo();
|
|
71
|
+
|
|
72
|
+
expect(providers).toBeInstanceOf(Array);
|
|
73
|
+
expect(providers.length).toBeGreaterThan(0);
|
|
74
|
+
expect(providers[0]).toHaveProperty("id");
|
|
75
|
+
expect(providers[0]).toHaveProperty("displayName");
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("Ask Command", () => {
|
|
80
|
+
test("should process basic request", async () => {
|
|
81
|
+
const wtc = await WrapTerminalCoder.create();
|
|
82
|
+
const req = {
|
|
83
|
+
prompt: "Hello world",
|
|
84
|
+
mode: "generate",
|
|
85
|
+
stream: false,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const response = await wtc.route(req);
|
|
89
|
+
|
|
90
|
+
expect(response).toHaveProperty("provider");
|
|
91
|
+
expect(response).toHaveProperty("text");
|
|
92
|
+
expect(response.text).toBeDefined();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("should handle explicit provider", async () => {
|
|
96
|
+
const wtc = await WrapTerminalCoder.create();
|
|
97
|
+
const req = {
|
|
98
|
+
prompt: "Test",
|
|
99
|
+
mode: "generate",
|
|
100
|
+
stream: false,
|
|
101
|
+
provider: "gemini",
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const response = await wtc.route(req);
|
|
105
|
+
|
|
106
|
+
expect(response.provider).toBe("gemini");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("should handle different modes", async () => {
|
|
110
|
+
const wtc = await WrapTerminalCoder.create();
|
|
111
|
+
const modes = ["generate", "edit", "explain", "test"];
|
|
112
|
+
|
|
113
|
+
for (const mode of modes) {
|
|
114
|
+
const req = {
|
|
115
|
+
prompt: "Test",
|
|
116
|
+
mode,
|
|
117
|
+
stream: false,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const response = await wtc.route(req);
|
|
121
|
+
expect(response).toBeDefined();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("should handle file context", async () => {
|
|
126
|
+
const wtc = await WrapTerminalCoder.create();
|
|
127
|
+
const req = {
|
|
128
|
+
prompt: "Explain this code",
|
|
129
|
+
mode: "explain",
|
|
130
|
+
fileContext: ["src/file1.ts", "src/file2.ts"],
|
|
131
|
+
stream: false,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const response = await wtc.route(req);
|
|
135
|
+
expect(response).toBeDefined();
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe("Configuration", () => {
|
|
140
|
+
test("should load configuration", async () => {
|
|
141
|
+
const configLoader = new ConfigLoader();
|
|
142
|
+
const config = await configLoader.loadConfig();
|
|
143
|
+
|
|
144
|
+
expect(config).toHaveProperty("routing");
|
|
145
|
+
expect(config).toHaveProperty("providers");
|
|
146
|
+
expect(config).toHaveProperty("credits");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("should handle config file path", async () => {
|
|
150
|
+
const configLoader = new ConfigLoader({
|
|
151
|
+
projectConfigPath: "/test/path/config.json",
|
|
152
|
+
});
|
|
153
|
+
const config = await configLoader.loadConfig();
|
|
154
|
+
|
|
155
|
+
expect(config).toBeDefined();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe("State Management", () => {
|
|
160
|
+
test("should track provider state", async () => {
|
|
161
|
+
const stateManager = new StateManager();
|
|
162
|
+
await stateManager.initialize();
|
|
163
|
+
|
|
164
|
+
const state = await stateManager.getProviderState("gemini");
|
|
165
|
+
expect(state).toHaveProperty("requestsToday");
|
|
166
|
+
expect(state).toHaveProperty("outOfCreditsUntil");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("should record successful requests", async () => {
|
|
170
|
+
// Override mock for this test to track state changes
|
|
171
|
+
const mockGetState = mock(async () => ({
|
|
172
|
+
requestsToday: 1,
|
|
173
|
+
outOfCreditsUntil: undefined,
|
|
174
|
+
lastErrors: [],
|
|
175
|
+
}));
|
|
176
|
+
const stateManager = new StateManager();
|
|
177
|
+
stateManager.getProviderState = mockGetState;
|
|
178
|
+
|
|
179
|
+
await stateManager.recordSuccess("gemini");
|
|
180
|
+
|
|
181
|
+
const state = await stateManager.getProviderState("gemini");
|
|
182
|
+
expect(state.requestsToday).toBeGreaterThan(0);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe("CLI Commands", () => {
|
|
188
|
+
describe("Providers Command", () => {
|
|
189
|
+
test("should return JSON array of providers", async () => {
|
|
190
|
+
const wtc = await WrapTerminalCoder.create();
|
|
191
|
+
const providers = await wtc.getProviderInfo();
|
|
192
|
+
|
|
193
|
+
expect(Array.isArray(providers)).toBe(true);
|
|
194
|
+
providers.forEach((provider: any) => {
|
|
195
|
+
expect(provider).toHaveProperty("id");
|
|
196
|
+
expect(provider).toHaveProperty("displayName");
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("should show correct provider status", async () => {
|
|
201
|
+
const wtc = await WrapTerminalCoder.create();
|
|
202
|
+
const providers = await wtc.getProviderInfo();
|
|
203
|
+
|
|
204
|
+
providers.forEach((provider: any) => {
|
|
205
|
+
expect(typeof provider.requestsToday).toBe("number");
|
|
206
|
+
expect(provider.requestsToday).toBeGreaterThanOrEqual(0);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
describe("Ask Command", () => {
|
|
212
|
+
test("should handle basic ask command", async () => {
|
|
213
|
+
const wtc = await WrapTerminalCoder.create();
|
|
214
|
+
const req = {
|
|
215
|
+
prompt: "Write hello world",
|
|
216
|
+
mode: "generate",
|
|
217
|
+
stream: false,
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const response = await wtc.route(req);
|
|
221
|
+
|
|
222
|
+
expect(response).toHaveProperty("text");
|
|
223
|
+
expect(response.text).toBeDefined();
|
|
224
|
+
expect(response.text.length).toBeGreaterThan(0);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("should handle ask with provider flag", async () => {
|
|
228
|
+
const wtc = await WrapTerminalCoder.create();
|
|
229
|
+
const req = {
|
|
230
|
+
prompt: "Test",
|
|
231
|
+
mode: "generate",
|
|
232
|
+
stream: false,
|
|
233
|
+
provider: "gemini",
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const response = await wtc.route(req);
|
|
237
|
+
|
|
238
|
+
expect(response.provider).toBe("gemini");
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("should handle ask with file context", async () => {
|
|
242
|
+
const wtc = await WrapTerminalCoder.create();
|
|
243
|
+
const req = {
|
|
244
|
+
prompt: "Refactor this",
|
|
245
|
+
mode: "edit",
|
|
246
|
+
fileContext: ["src/utils.ts"],
|
|
247
|
+
stream: false,
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const response = await wtc.route(req);
|
|
251
|
+
expect(response).toBeDefined();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test("should handle streaming mode", async () => {
|
|
255
|
+
const wtc = await WrapTerminalCoder.create();
|
|
256
|
+
const req = {
|
|
257
|
+
prompt: "Write a long document",
|
|
258
|
+
mode: "generate",
|
|
259
|
+
stream: true,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const responses = [];
|
|
263
|
+
for await (const response of wtc.routeStream(req)) {
|
|
264
|
+
responses.push(response);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
expect(responses.length).toBeGreaterThan(0);
|
|
268
|
+
expect(responses[responses.length - 1]).toHaveProperty("meta");
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe("Output Formats", () => {
|
|
273
|
+
test("should support JSON output format", async () => {
|
|
274
|
+
const wtc = await WrapTerminalCoder.create();
|
|
275
|
+
const req = {
|
|
276
|
+
prompt: "Test",
|
|
277
|
+
mode: "generate",
|
|
278
|
+
stream: false,
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const response = await wtc.route(req);
|
|
282
|
+
|
|
283
|
+
expect(response).toHaveProperty("provider");
|
|
284
|
+
expect(response).toHaveProperty("text");
|
|
285
|
+
expect(response).toHaveProperty("usage");
|
|
286
|
+
expect(response).toHaveProperty("meta");
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
describe("Error Handling", () => {
|
|
291
|
+
test("should handle provider not found", async () => {
|
|
292
|
+
const wtc = await WrapTerminalCoder.create();
|
|
293
|
+
|
|
294
|
+
// Override route to throw error for this test
|
|
295
|
+
wtc.route = mock(async (req: any) => {
|
|
296
|
+
if (req.provider === "nonexistent") {
|
|
297
|
+
throw new Error("Provider not found: nonexistent");
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
provider: "gemini",
|
|
301
|
+
text: "Mock response",
|
|
302
|
+
usage: { inputTokens: 10, outputTokens: 20 },
|
|
303
|
+
meta: { elapsedMs: 100 },
|
|
304
|
+
};
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const req = {
|
|
308
|
+
prompt: "Test",
|
|
309
|
+
provider: "nonexistent",
|
|
310
|
+
mode: "generate",
|
|
311
|
+
stream: false,
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
await expect(wtc.route(req)).rejects.toThrow("Provider not found");
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
describe("Configuration Validation", () => {
|
|
320
|
+
test("should validate configuration with Zod", async () => {
|
|
321
|
+
const configLoader = new ConfigLoader();
|
|
322
|
+
const config = await configLoader.loadConfig();
|
|
323
|
+
|
|
324
|
+
expect(config).toBeDefined();
|
|
325
|
+
expect(config).toHaveProperty("routing");
|
|
326
|
+
expect(config).toHaveProperty("providers");
|
|
327
|
+
expect(config).toHaveProperty("credits");
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test("should handle invalid configuration", async () => {
|
|
331
|
+
const configLoader = new ConfigLoader({
|
|
332
|
+
projectConfigPath: "/invalid/path/config.json",
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
await expect(configLoader.loadConfig()).resolves.toBeDefined();
|
|
336
|
+
});
|
|
337
|
+
});
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import { WrapTerminalCoder } from "../core/index.js";
|
|
5
|
+
import { CodingRequestSchema } from "../core/index.js";
|
|
6
|
+
import type { CodingRequest } from "../core/index.js";
|
|
7
|
+
import { runMCPServer } from "../mcp/server.js";
|
|
8
|
+
|
|
9
|
+
const program = new Command();
|
|
10
|
+
|
|
11
|
+
program
|
|
12
|
+
.name("wraptc")
|
|
13
|
+
.description("wraptc - unified CLI for multiple coding agents")
|
|
14
|
+
.version("0.1.0")
|
|
15
|
+
.option("--mcp", "Start MCP server mode");
|
|
16
|
+
|
|
17
|
+
// Handle MCP mode flag
|
|
18
|
+
program.hook("preAction", async (thisCommand) => {
|
|
19
|
+
const options = thisCommand.opts();
|
|
20
|
+
if (options.mcp) {
|
|
21
|
+
await runMCPServer();
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
program
|
|
27
|
+
.command("ask")
|
|
28
|
+
.alias("a")
|
|
29
|
+
.description("Send a coding request to the best available provider")
|
|
30
|
+
.argument("[prompt]", "The coding prompt (can also use -p or pipe from stdin)")
|
|
31
|
+
.option("-p, --prompt <prompt>", "The prompt to send to the coding agent")
|
|
32
|
+
.option("--use <provider>", "Specific provider to use (qwen, gemini, codex)")
|
|
33
|
+
.option("--mode <mode>", "Task type: generate, edit, explain, test", "generate")
|
|
34
|
+
.option("--lang <language>", "Language hint (js, ts, py, etc.)")
|
|
35
|
+
.option("-f, --file <files...>", "Include file(s) for context")
|
|
36
|
+
.option("-t, --temperature <temp>", "Creativity level (0-2)", Number.parseFloat)
|
|
37
|
+
.option("-s, --stream", "Stream responses in real-time")
|
|
38
|
+
.option("-o, --output <format>", "Output format: text, json", "text")
|
|
39
|
+
.option("-d, --dry-run", "Show which provider will be used")
|
|
40
|
+
.option("-c, --config <path>", "Custom config file path")
|
|
41
|
+
.option("--timeout <seconds>", "Request timeout in seconds", Number.parseInt)
|
|
42
|
+
.option("--retry <count>", "Retry failed requests", Number.parseInt)
|
|
43
|
+
.action(async (promptArg, options) => {
|
|
44
|
+
try {
|
|
45
|
+
// Handle positional argument or option
|
|
46
|
+
let prompt = promptArg || options.prompt;
|
|
47
|
+
|
|
48
|
+
// Validate prompt
|
|
49
|
+
if (!prompt && process.stdin.isTTY) {
|
|
50
|
+
console.error("Error: Prompt is required (use positional arg, -p, or pipe from stdin)");
|
|
51
|
+
console.error("Examples:");
|
|
52
|
+
console.error(" wraptc 'Write a function'");
|
|
53
|
+
console.error(" wraptc -p 'Write a function'");
|
|
54
|
+
console.error(" echo 'Write a function' | wraptc");
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Read from stdin if prompt not provided
|
|
59
|
+
if (!prompt && !process.stdin.isTTY) {
|
|
60
|
+
prompt = await Bun.stdin.text();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Build request
|
|
64
|
+
const request: CodingRequest = {
|
|
65
|
+
prompt: prompt.trim(),
|
|
66
|
+
mode: options.mode,
|
|
67
|
+
language: options.lang,
|
|
68
|
+
fileContext: options.file,
|
|
69
|
+
temperature: options.temperature,
|
|
70
|
+
stream: options.stream,
|
|
71
|
+
provider: options.use, // Changed from options.provider
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Validate request
|
|
75
|
+
const parsed = CodingRequestSchema.safeParse(request);
|
|
76
|
+
if (!parsed.success) {
|
|
77
|
+
console.error("Error: Invalid request:", parsed.error.errors);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Create WrapTerminalCoder instance
|
|
82
|
+
const wtc = await WrapTerminalCoder.create({
|
|
83
|
+
configPath: options.config,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Dry run mode
|
|
87
|
+
if (options.dryRun) {
|
|
88
|
+
console.log("Would use provider:", request.provider || "auto-selected");
|
|
89
|
+
process.exit(0);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Execute request
|
|
93
|
+
if (request.stream) {
|
|
94
|
+
for await (const event of wtc.routeStream(request)) {
|
|
95
|
+
if (options.output === "json") {
|
|
96
|
+
console.log(JSON.stringify(event));
|
|
97
|
+
} else {
|
|
98
|
+
console.log(event.text);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
const response = await wtc.route(request);
|
|
103
|
+
|
|
104
|
+
if (options.output === "json") {
|
|
105
|
+
console.log(JSON.stringify(response));
|
|
106
|
+
} else {
|
|
107
|
+
console.log(response.text);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error("Error:", error instanceof Error ? error.message : String(error));
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
program
|
|
117
|
+
.command("providers")
|
|
118
|
+
.description("List available providers and their status")
|
|
119
|
+
.option("-c, --config <path>", "Custom config file path")
|
|
120
|
+
.action(async (options) => {
|
|
121
|
+
try {
|
|
122
|
+
const wtc = await WrapTerminalCoder.create({
|
|
123
|
+
configPath: options.config,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const providers = await wtc.getProviderInfo();
|
|
127
|
+
|
|
128
|
+
console.log("Available Providers:");
|
|
129
|
+
for (const provider of providers) {
|
|
130
|
+
const status = provider.outOfCreditsUntil
|
|
131
|
+
? `⚠️ Out of credits until ${new Date(provider.outOfCreditsUntil).toLocaleString()}`
|
|
132
|
+
: `✅ ${provider.requestsToday} requests today`;
|
|
133
|
+
|
|
134
|
+
console.log(` ${provider.displayName} (${provider.id}): ${status}`);
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error("Error:", error instanceof Error ? error.message : String(error));
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
program
|
|
143
|
+
.command("mcp")
|
|
144
|
+
.description("Start MCP server")
|
|
145
|
+
.action(async () => {
|
|
146
|
+
await runMCPServer();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
program.parse();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"routing": {
|
|
3
|
+
"defaultOrder": ["project-provider", "user-provider", "system-provider"]
|
|
4
|
+
},
|
|
5
|
+
"providers": {
|
|
6
|
+
"project-provider": {
|
|
7
|
+
"binary": "./project-provider",
|
|
8
|
+
"args": ["--project-flag"],
|
|
9
|
+
"jsonMode": "none",
|
|
10
|
+
"streamingMode": "line",
|
|
11
|
+
"capabilities": ["generate", "edit", "test"]
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"routing": {
|
|
3
|
+
"defaultOrder": ["user-provider", "system-provider"]
|
|
4
|
+
},
|
|
5
|
+
"providers": {
|
|
6
|
+
"user-provider": {
|
|
7
|
+
"binary": "/usr/local/bin/user-provider",
|
|
8
|
+
"args": ["--user-flag"],
|
|
9
|
+
"jsonMode": "flag",
|
|
10
|
+
"jsonFlag": "--json",
|
|
11
|
+
"streamingMode": "jsonl",
|
|
12
|
+
"capabilities": ["generate", "edit"]
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|