@testchimp/cli 0.1.62 → 0.1.64

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.
@@ -19,6 +19,8 @@ type RunOptions = {
19
19
  runtimeId?: string;
20
20
  };
21
21
  export declare function runChimphands(opts: RunOptions): Promise<void>;
22
+ /** OpenCode workspace-scoped APIs require directory routing (header + query). */
23
+ export declare function appendOpencodeDirectoryRouting(path: string, query: string | undefined, headers: Record<string, string>, directory: string): string;
22
24
  /** Runtime-aware entry: register + attach to local OpenCode server (Phase 1+). */
23
25
  export declare function serveChimphands(opts: RunOptions & {
24
26
  attachUrl: string;
@@ -67,7 +67,7 @@ const CHIMPHANDS_AGENT_PROMPT = `You are ChimpHands, TestChimp's coding agent. Y
67
67
  - This conversation uses ONE working branch and ONE pull request. Reuse them for all follow-up work in this chat.
68
68
  - If bootstrap lists a working branch, checkout that branch and push additional commits there — update the same PR.
69
69
  - Only create a NEW branch/PR when (a) no working branch exists yet for this conversation, or (b) the prior PR was merged/closed (verify with \`gh pr view\`).
70
- - Commit and push on the session working branch after meaningful edit batches. The host also commits any dirty worktree before idle teardown — keep the branch pushed so the UI can show diffs from GitHub.
70
+ - Commit and push on the session working branch after meaningful edit batches. When \`CHIMPHANDS_UI_ATTACHED\` is \`false\` (no browser watching live), **commit and push before ending every turn** so the user can review async via PR / Files changed. The host also commits any remaining dirty worktree after each turn and before idle teardown.
71
71
  - Branch names MUST start with \`testchimp-\` or \`chimphands-\`.
72
72
  - When creating a NEW working branch: create it, then IMMEDIATELY publish it with
73
73
  \`git push -u origin <branch>\` BEFORE calling report-branch. Users open the branch URL in the UI —
@@ -939,10 +939,12 @@ function writeOpencodeConfig(backend, apiKey, boot) {
939
939
  const modelId = resolveOpencodeModelId(boot);
940
940
  const model = `${TESTCHIMP_PROVIDER_ID}/${modelId}`;
941
941
  const sessionId = bootStr(boot, "session_id", "sessionId");
942
+ const uiAttached = !!(boot.uiAttached ?? boot.ui_attached);
942
943
  const mcpEnv = {
943
944
  TESTCHIMP_API_KEY: apiKey,
944
945
  TESTCHIMP_BACKEND_URL: backend,
945
946
  TESTCHIMP_EXECUTION_SOURCE: "CLOUD_AGENT",
947
+ CHIMPHANDS_UI_ATTACHED: uiAttached ? "true" : "false",
946
948
  };
947
949
  const serviceUserId = bootStr(boot, "chimphands_service_account_user_id", "chimphandsServiceAccountUserId");
948
950
  if (serviceUserId) {
@@ -1569,6 +1571,7 @@ export async function runChimphands(opts) {
1569
1571
  const githubRunId = (process.env.GITHUB_RUN_ID || "").trim();
1570
1572
  const poster = new AgentEventPoster(backend, apiKey, sessionId);
1571
1573
  poster.uiAttached = !!(boot.uiAttached ?? boot.ui_attached);
1574
+ process.env.CHIMPHANDS_UI_ATTACHED = poster.uiAttached ? "true" : "false";
1572
1575
  let stopLiveSse = null;
1573
1576
  /** Do not open localhost OpenCode /event until serve has been (re)started with config. */
1574
1577
  let opencodeHttpReady = !attachUrl;
@@ -1597,6 +1600,7 @@ export async function runChimphands(opts) {
1597
1600
  };
1598
1601
  const syncLiveSse = (attached) => {
1599
1602
  pendingUiAttached = attached;
1603
+ process.env.CHIMPHANDS_UI_ATTACHED = attached ? "true" : "false";
1600
1604
  if (!attachUrl) {
1601
1605
  poster.uiAttached = attached;
1602
1606
  return;
@@ -1977,6 +1981,11 @@ export async function runChimphands(opts) {
1977
1981
  console.error(`ChimpHands turn-end reconcile failed: ${err instanceof Error ? err.message : String(err)}`);
1978
1982
  }
1979
1983
  }
1984
+ // Async / no UI: checkpoint dirty worktree after each turn so PR + Files changed
1985
+ // are reviewable when the user opens the session later (live VCS tunnel may be idle).
1986
+ if (!poster.uiAttached) {
1987
+ await commitAndPushDirtyWorktree("chimphands: commit after turn (async / no UI attached)");
1988
+ }
1980
1989
  if (result.code !== 0 && !result.cancelled) {
1981
1990
  const errMsg = (result.err || "opencode failed").trim() || "opencode failed";
1982
1991
  console.error(`ChimpHands OpenCode failed: ${errMsg}`);
@@ -2122,9 +2131,35 @@ async function commitAndPushDirtyWorktree(message) {
2122
2131
  console.error(`ChimpHands commit-before-idle failed: ${err instanceof Error ? err.message : String(err)}`);
2123
2132
  }
2124
2133
  }
2134
+ function opencodeProxyNeedsDirectory(path) {
2135
+ const p = path.startsWith("/") ? path : `/${path}`;
2136
+ return (p.startsWith("/vcs") ||
2137
+ p.startsWith("/api/vcs") ||
2138
+ p.startsWith("/file") ||
2139
+ p.startsWith("/api/fs") ||
2140
+ p.startsWith("/api/session") ||
2141
+ p.startsWith("/path") ||
2142
+ p.startsWith("/session") ||
2143
+ p.startsWith("/instance") ||
2144
+ p.startsWith("/event") ||
2145
+ p.startsWith("/global/event"));
2146
+ }
2147
+ /** OpenCode workspace-scoped APIs require directory routing (header + query). */
2148
+ export function appendOpencodeDirectoryRouting(path, query, headers, directory) {
2149
+ if (!opencodeProxyNeedsDirectory(path)) {
2150
+ return query || "";
2151
+ }
2152
+ headers["x-opencode-directory"] = directory;
2153
+ if (query?.includes("directory=")) {
2154
+ return query;
2155
+ }
2156
+ const dirParam = `directory=${encodeURIComponent(directory)}`;
2157
+ return query ? `${query}&${dirParam}` : dirParam;
2158
+ }
2125
2159
  function startTunnelWorker(backend, apiKey, runtimeId, attachUrl, sessionId, handlers) {
2126
2160
  let stopped = false;
2127
2161
  const base = attachUrl.replace(/\/$/, "");
2162
+ const workDirectory = process.cwd();
2128
2163
  let ws = null;
2129
2164
  let reconnectTimer = null;
2130
2165
  let backoffMs = 1000;
@@ -2140,8 +2175,10 @@ function startTunnelWorker(backend, apiKey, runtimeId, attachUrl, sessionId, han
2140
2175
  if (!req.requestId || socket.readyState !== 1)
2141
2176
  return;
2142
2177
  const requestId = req.requestId;
2143
- const target = base + (req.path || "/") + (req.query ? `?${req.query}` : "");
2178
+ const pathPart = req.path || "/";
2144
2179
  const headers = { ...(req.headers || {}) };
2180
+ const query = appendOpencodeDirectoryRouting(pathPart, req.query, headers, workDirectory);
2181
+ const target = base + pathPart + (query ? `?${query}` : "");
2145
2182
  const init = { method: req.method || "GET", headers };
2146
2183
  if (req.bodyBase64) {
2147
2184
  init.body = Buffer.from(req.bodyBase64, "base64");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testchimp/cli",
3
- "version": "0.1.62",
3
+ "version": "0.1.64",
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",