@testchimp/cli 0.1.36 → 0.1.37

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.
@@ -47,9 +47,14 @@ function postJsonFireAndForget(backend, apiKey, path, body) {
47
47
  console.error(`ChimpHands API telemetry failed ${path}: ${detail}`);
48
48
  });
49
49
  }
50
- function resolveOpencodeModel(boot) {
50
+ const TESTCHIMP_PROVIDER_ID = "testchimp";
51
+ function resolveOpencodeModelId(boot) {
51
52
  const raw = (boot.llm_model || "gpt-4o-mini").trim();
52
- return raw.includes("/") ? raw : `openai/${raw}`;
53
+ const modelId = raw.includes("/") ? raw.split("/").pop() || "gpt-4o-mini" : raw;
54
+ return modelId;
55
+ }
56
+ function resolveOpencodeModel(boot) {
57
+ return `${TESTCHIMP_PROVIDER_ID}/${resolveOpencodeModelId(boot)}`;
53
58
  }
54
59
  function extractOpencodeFatalError(raw) {
55
60
  const line = raw.trim();
@@ -69,12 +74,40 @@ function extractOpencodeFatalError(raw) {
69
74
  if (line.includes("Unexpected server error") && line.includes("UnknownError")) {
70
75
  return line;
71
76
  }
77
+ const errorLine = line.match(/^Error:\s*(.+)$/i);
78
+ if (errorLine?.[1]?.trim()) {
79
+ return errorLine[1].trim();
80
+ }
81
+ if (/not found/i.test(line) && line.length < 240) {
82
+ return line.trim();
83
+ }
72
84
  return null;
73
85
  }
86
+ function summarizeOpencodeFailure(stderr, stdout, exitCode) {
87
+ for (const chunk of [stderr, stdout]) {
88
+ for (const line of chunk.split("\n")) {
89
+ const fatal = extractOpencodeFatalError(line);
90
+ if (fatal)
91
+ return fatal;
92
+ }
93
+ }
94
+ const merged = `${stderr}\n${stdout}`.trim();
95
+ if (merged) {
96
+ const errorLines = merged
97
+ .split("\n")
98
+ .map((l) => l.trim())
99
+ .filter((l) => /^error:/i.test(l) || /not found/i.test(l));
100
+ if (errorLines.length)
101
+ return errorLines[errorLines.length - 1].replace(/^error:\s*/i, "").trim();
102
+ return merged.slice(0, 1200);
103
+ }
104
+ return exitCode ? `opencode exited with code ${exitCode}` : "opencode failed";
105
+ }
74
106
  function writeOpencodeConfig(backend, apiKey, boot) {
75
107
  const llmBase = (boot.llm_base_url || `${backend}/v1`).replace(/\/$/, "");
76
108
  const llmKey = apiKey || boot.llm_api_key || "";
77
- const model = resolveOpencodeModel(boot);
109
+ const modelId = resolveOpencodeModelId(boot);
110
+ const model = `${TESTCHIMP_PROVIDER_ID}/${modelId}`;
78
111
  const mcpEnv = {
79
112
  TESTCHIMP_API_KEY: apiKey,
80
113
  TESTCHIMP_BACKEND_URL: backend,
@@ -87,13 +120,19 @@ function writeOpencodeConfig(backend, apiKey, boot) {
87
120
  $schema: "https://opencode.ai/config.json",
88
121
  model,
89
122
  autoupdate: false,
90
- enabled_providers: ["openai"],
91
123
  provider: {
92
- openai: {
124
+ [TESTCHIMP_PROVIDER_ID]: {
125
+ npm: "@ai-sdk/openai-compatible",
126
+ name: "TestChimp",
93
127
  options: {
94
128
  apiKey: llmKey,
95
129
  baseURL: llmBase,
96
130
  },
131
+ models: {
132
+ [modelId]: {
133
+ name: modelId,
134
+ },
135
+ },
97
136
  },
98
137
  },
99
138
  mcp: {
@@ -173,6 +212,10 @@ function runOpencode(prompt, model, childEnv, postEvent) {
173
212
  resolve({ code: 1, err: fatalError });
174
213
  return;
175
214
  }
215
+ if (code != null && code !== 0) {
216
+ resolve({ code, err: summarizeOpencodeFailure(err, buf, code) });
217
+ return;
218
+ }
176
219
  resolve({ code: code == null ? 1 : code, err });
177
220
  });
178
221
  });
@@ -195,11 +238,7 @@ function runOpencode(prompt, model, childEnv, postEvent) {
195
238
  const errObj = e;
196
239
  const stderr = errObj.stderr?.toString() || "";
197
240
  const stdout = errObj.stdout?.toString() || "";
198
- const fatal = extractOpencodeFatalError(stderr) ||
199
- extractOpencodeFatalError(stdout) ||
200
- stderr ||
201
- errObj.message ||
202
- "opencode failed";
241
+ const fatal = summarizeOpencodeFailure(stderr, stdout, errObj.status ?? 1);
203
242
  return Promise.resolve({ code: errObj.status || 1, err: fatal });
204
243
  }
205
244
  }
@@ -347,10 +386,29 @@ export async function runChimphands(opts) {
347
386
  while (prompt) {
348
387
  const result = await runOpencode(ensureTestchimpPrompt(prompt), opencodeModel, childEnv, postEvent);
349
388
  if (result.code !== 0) {
350
- const errMsg = result.err || "opencode failed";
389
+ const errMsg = (result.err || "opencode failed").trim() || "opencode failed";
351
390
  console.error(`ChimpHands OpenCode failed: ${errMsg}`);
352
- postEvent(ROLE_STATUS, errMsg, STATUS_FAILED);
353
- complete(STATUS_FAILED, errMsg);
391
+ try {
392
+ await postJson(backend, apiKey, "/api/chimphands/post_agent_event", {
393
+ sessionId,
394
+ role: ROLE_STATUS,
395
+ content: errMsg,
396
+ status: STATUS_FAILED,
397
+ githubRunId: githubRunId || undefined,
398
+ });
399
+ await postJson(backend, apiKey, "/api/chimphands/complete_session", {
400
+ sessionId,
401
+ status: STATUS_FAILED,
402
+ errorMessage: errMsg,
403
+ githubRunId: githubRunId || undefined,
404
+ });
405
+ }
406
+ catch (reportErr) {
407
+ const detail = reportErr instanceof Error ? reportErr.message : String(reportErr);
408
+ console.error(`ChimpHands failed to report OpenCode error to backend: ${detail}`);
409
+ postEvent(ROLE_STATUS, errMsg, STATUS_FAILED);
410
+ complete(STATUS_FAILED, errMsg);
411
+ }
354
412
  process.exit(result.code || 1);
355
413
  }
356
414
  postEvent(ROLE_STATUS, "Waiting for user input", STATUS_WAITING_USER);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testchimp/cli",
3
- "version": "0.1.36",
3
+ "version": "0.1.37",
4
4
  "description": "TestChimp CLI and MCP server — coverage, plans, EaaS, TrueCoverage, API operations (calls /api/mcp/*)",
5
5
  "type": "module",
6
6
  "main": "dist/bin/testchimp.js",