@testchimp/cli 0.1.76 → 0.1.78
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/dist/chimphands/run.js +72 -70
- package/dist/chimphands/worktreeCommitPaths.d.ts +17 -0
- package/dist/chimphands/worktreeCommitPaths.js +72 -0
- package/dist/chimphands/worktreeCommitPaths.test.d.ts +1 -0
- package/dist/chimphands/worktreeCommitPaths.test.js +28 -0
- package/dist/core/tools.js +12 -3
- package/package.json +2 -2
package/dist/chimphands/run.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { execSync, spawn } from "node:child_process";
|
|
7
7
|
import { mkdirSync, openSync, writeFileSync } from "node:fs";
|
|
8
8
|
import fs from "node:fs/promises";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
9
10
|
import path from "node:path";
|
|
10
11
|
import http from "node:http";
|
|
11
12
|
import https from "node:https";
|
|
@@ -15,6 +16,9 @@ import { isChimphandsLlmRetryEnabled, startLlmRetryProxy } from "./llmRetryProxy
|
|
|
15
16
|
import { midTurnSteeringKey, releaseUnansweredMidTurnMessages, } from "./midTurnSteering.js";
|
|
16
17
|
import { runSteeringSidecarReply } from "./steeringSidecar.js";
|
|
17
18
|
import { ensureAgentSessionBranch, ensurePullRequest, isAgentBranchName, isPrOnlyMode, resolveSessionBaseBranch, runGit, } from "./agentBranch.js";
|
|
19
|
+
import { isChimpHandsInternalCommitPath, resolveCommitPaths, } from "./worktreeCommitPaths.js";
|
|
20
|
+
/** Runner logs live outside the git worktree so they never enter commit status. */
|
|
21
|
+
const OPENCODE_SERVER_LOG_PATH = path.join(tmpdir(), "chimphands-opencode-server.log");
|
|
18
22
|
const ROLE_ASSISTANT = "CHIMPHANDS_MESSAGE_ROLE_ASSISTANT";
|
|
19
23
|
const ROLE_TOOL = "CHIMPHANDS_MESSAGE_ROLE_TOOL";
|
|
20
24
|
const ROLE_REASONING = "CHIMPHANDS_MESSAGE_ROLE_REASONING";
|
|
@@ -98,7 +102,7 @@ const CHIMPHANDS_AGENT_PROMPT = `You are ChimpHands, TestChimp's coding agent. Y
|
|
|
98
102
|
|
|
99
103
|
## CI runner hygiene (GitHub Actions)
|
|
100
104
|
- On hosted runners: load skill \`references/chimphands-ci-runner.md\` before compile, Docker builds, or large installs.
|
|
101
|
-
- Never \`git fetch origin\` (full fetch) — shallow single-branch fetch only; branch names are in
|
|
105
|
+
- Never \`git fetch origin\` (full fetch) — shallow single-branch fetch only; branch names are in \`/tmp/chimphands/remote-branch-names.txt\` (or \`$CHIMPHANDS_REMOTE_BRANCH_NAMES\`).
|
|
102
106
|
- Reclaim disk between heavy phases (\`docker builder prune -af\` after image builds, stop compile daemons, delete temp/test artifacts). One reclaim+retry on ENOSPC — no blind loops.
|
|
103
107
|
|
|
104
108
|
## Honesty
|
|
@@ -1101,6 +1105,10 @@ function writeOpencodeConfig(backend, apiKey, boot, llmBaseOverride) {
|
|
|
1101
1105
|
TESTCHIMP_EXECUTION_SOURCE: "CLOUD_AGENT",
|
|
1102
1106
|
CHIMPHANDS_UI_ATTACHED: uiAttached ? "true" : "false",
|
|
1103
1107
|
};
|
|
1108
|
+
if (sessionId) {
|
|
1109
|
+
mcpEnv.SESSION_ID = sessionId;
|
|
1110
|
+
mcpEnv.CHIMPHANDS_SESSION_ID = sessionId;
|
|
1111
|
+
}
|
|
1104
1112
|
const serviceUserId = bootStr(boot, "chimphands_service_account_user_id", "chimphandsServiceAccountUserId");
|
|
1105
1113
|
if (serviceUserId) {
|
|
1106
1114
|
mcpEnv.TESTCHIMP_USER_ID = serviceUserId;
|
|
@@ -1312,7 +1320,7 @@ async function restartLocalOpencodeServer(attachUrl) {
|
|
|
1312
1320
|
const port = u.port || (u.protocol === "https:" ? "443" : "80");
|
|
1313
1321
|
killListenersOnPort(port);
|
|
1314
1322
|
await sleep(300);
|
|
1315
|
-
const logFd = openSync(
|
|
1323
|
+
const logFd = openSync(OPENCODE_SERVER_LOG_PATH, "a");
|
|
1316
1324
|
const child = spawn("opencode", ["serve", "--port", port, "--hostname", hostname], {
|
|
1317
1325
|
detached: true,
|
|
1318
1326
|
stdio: ["ignore", logFd, logFd],
|
|
@@ -1327,6 +1335,7 @@ async function restartLocalOpencodeServer(attachUrl) {
|
|
|
1327
1335
|
/* best effort */
|
|
1328
1336
|
}
|
|
1329
1337
|
}
|
|
1338
|
+
console.error(`ChimpHands opencode serve log: ${OPENCODE_SERVER_LOG_PATH}`);
|
|
1330
1339
|
console.error(`ChimpHands restarted OpenCode serve on ${hostname}:${port} (pid ${child.pid ?? "?"})`);
|
|
1331
1340
|
await waitForOpencodeHttp(attachUrl, 60_000);
|
|
1332
1341
|
}
|
|
@@ -1627,8 +1636,13 @@ async function ackWorktreeFileWrite(backend, apiKey, sessionId, requestId, ok, e
|
|
|
1627
1636
|
requestId,
|
|
1628
1637
|
ok,
|
|
1629
1638
|
};
|
|
1630
|
-
if (errorMessage)
|
|
1631
|
-
body.errorMessage = errorMessage
|
|
1639
|
+
if (errorMessage) {
|
|
1640
|
+
body.errorMessage = errorMessage
|
|
1641
|
+
.replace(/[\r\n\t]+/g, " ")
|
|
1642
|
+
.replace(/ +/g, " ")
|
|
1643
|
+
.trim()
|
|
1644
|
+
.slice(0, 2000);
|
|
1645
|
+
}
|
|
1632
1646
|
await postJson(backend, apiKey, "/api/chimphands/ack_worktree_file_write", body).catch((err) => {
|
|
1633
1647
|
console.error(`ChimpHands ack_worktree_file_write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1634
1648
|
});
|
|
@@ -1651,7 +1665,9 @@ async function applyUserFileEdit(backend, apiKey, sessionId, edit, turnActive) {
|
|
|
1651
1665
|
await ackWorktreeFileWrite(backend, apiKey, sessionId, edit.requestId, true);
|
|
1652
1666
|
return;
|
|
1653
1667
|
}
|
|
1654
|
-
const pushed = await commitAndPushDirtyWorktree("User Update"
|
|
1668
|
+
const pushed = await commitAndPushDirtyWorktree("User Update", {
|
|
1669
|
+
paths: [relative],
|
|
1670
|
+
});
|
|
1655
1671
|
if (!pushed.ok) {
|
|
1656
1672
|
await ackWorktreeFileWrite(backend, apiKey, sessionId, edit.requestId, false, pushed.error || "commit/push failed");
|
|
1657
1673
|
return;
|
|
@@ -2180,6 +2196,8 @@ export async function runChimphands(opts) {
|
|
|
2180
2196
|
TESTCHIMP_API_KEY: apiKey,
|
|
2181
2197
|
TESTCHIMP_BACKEND_URL: backend,
|
|
2182
2198
|
TESTCHIMP_EXECUTION_SOURCE: "CLOUD_AGENT",
|
|
2199
|
+
SESSION_ID: sessionId,
|
|
2200
|
+
CHIMPHANDS_SESSION_ID: sessionId,
|
|
2183
2201
|
};
|
|
2184
2202
|
if (userId)
|
|
2185
2203
|
childEnv.TESTCHIMP_USER_ID = userId;
|
|
@@ -2509,41 +2527,15 @@ function startRuntimeHeartbeat(backend, apiKey, runtimeId, onUiAttached) {
|
|
|
2509
2527
|
stopped = true;
|
|
2510
2528
|
};
|
|
2511
2529
|
}
|
|
2512
|
-
/**
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
":(exclude).chimphands",
|
|
2522
|
-
":(exclude).chimphands/**",
|
|
2523
|
-
":(exclude).agents",
|
|
2524
|
-
":(exclude).agents/**",
|
|
2525
|
-
];
|
|
2526
|
-
function isChimpHandsInternalCommitPath(filePath) {
|
|
2527
|
-
let normalized = filePath.replace(/\\/g, "/").toLowerCase();
|
|
2528
|
-
while (normalized.startsWith("./"))
|
|
2529
|
-
normalized = normalized.slice(2);
|
|
2530
|
-
const slash = normalized.lastIndexOf("/");
|
|
2531
|
-
const name = slash >= 0 ? normalized.slice(slash + 1) : normalized;
|
|
2532
|
-
if (name === "opencode.json" ||
|
|
2533
|
-
name === "opencode-server.log" ||
|
|
2534
|
-
name === "chimphands-run.log" ||
|
|
2535
|
-
name === "remote-branches.txt" ||
|
|
2536
|
-
name === "remote-branch-names.txt" ||
|
|
2537
|
-
name.startsWith("gha-creds")) {
|
|
2538
|
-
return true;
|
|
2539
|
-
}
|
|
2540
|
-
return (normalized === ".chimphands" ||
|
|
2541
|
-
normalized.startsWith(".chimphands/") ||
|
|
2542
|
-
normalized === ".agents" ||
|
|
2543
|
-
normalized.startsWith(".agents/"));
|
|
2544
|
-
}
|
|
2545
|
-
/** Commit+push dirty worktree on the session agent branch before idle/teardown. */
|
|
2546
|
-
async function commitAndPushDirtyWorktree(message) {
|
|
2530
|
+
/**
|
|
2531
|
+
* Commit+push product changes on the session agent branch.
|
|
2532
|
+
*
|
|
2533
|
+
* Policy: allowlist staging from `git status` (never `git add -A` / exclude pathspecs).
|
|
2534
|
+
* Runner scaffolding is filtered out; anything already staged by the agent that is
|
|
2535
|
+
* scaffolding is unstaged before commit. Session logs are written under os.tmpdir(),
|
|
2536
|
+
* not the worktree.
|
|
2537
|
+
*/
|
|
2538
|
+
async function commitAndPushDirtyWorktree(message, opts) {
|
|
2547
2539
|
const run = (args, env) => runGit(args, env);
|
|
2548
2540
|
try {
|
|
2549
2541
|
const branch = await run(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
@@ -2567,35 +2559,16 @@ async function commitAndPushDirtyWorktree(message) {
|
|
|
2567
2559
|
console.error(`ChimpHands git status failed: ${error}`);
|
|
2568
2560
|
return { ok: false, error };
|
|
2569
2561
|
}
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
...CHIMPHANDS_COMMIT_EXCLUDE_PATHSPECS,
|
|
2579
|
-
]);
|
|
2580
|
-
if (add.code !== 0) {
|
|
2581
|
-
const error = add.err || add.out || "git add failed";
|
|
2582
|
-
console.error(`ChimpHands git add failed: ${error}`);
|
|
2583
|
-
return { ok: false, error };
|
|
2562
|
+
const toStage = resolveCommitPaths(status.out, opts?.paths);
|
|
2563
|
+
if (toStage.length) {
|
|
2564
|
+
const add = await run(["add", "--", ...toStage]);
|
|
2565
|
+
if (add.code !== 0) {
|
|
2566
|
+
const error = add.err || add.out || "git add failed";
|
|
2567
|
+
console.error(`ChimpHands git add failed: ${error}`);
|
|
2568
|
+
return { ok: false, error };
|
|
2569
|
+
}
|
|
2584
2570
|
}
|
|
2585
|
-
// Drop
|
|
2586
|
-
await run([
|
|
2587
|
-
"reset",
|
|
2588
|
-
"-q",
|
|
2589
|
-
"HEAD",
|
|
2590
|
-
"--",
|
|
2591
|
-
"opencode.json",
|
|
2592
|
-
"opencode-server.log",
|
|
2593
|
-
"chimphands-run.log",
|
|
2594
|
-
"remote-branches.txt",
|
|
2595
|
-
"remote-branch-names.txt",
|
|
2596
|
-
".chimphands",
|
|
2597
|
-
".agents",
|
|
2598
|
-
]);
|
|
2571
|
+
// Drop scaffolding the agent may have staged (git add -A, etc.).
|
|
2599
2572
|
const staged = await run(["diff", "--cached", "--name-only"]);
|
|
2600
2573
|
if (staged.code !== 0) {
|
|
2601
2574
|
const error = staged.err || staged.out || "git diff --cached failed";
|
|
@@ -2611,7 +2584,13 @@ async function commitAndPushDirtyWorktree(message) {
|
|
|
2611
2584
|
await run(["reset", "-q", "HEAD", "--", ...internalStaged]);
|
|
2612
2585
|
}
|
|
2613
2586
|
const keepStaged = stagedPaths.filter((p) => !isChimpHandsInternalCommitPath(p));
|
|
2614
|
-
|
|
2587
|
+
const pathScoped = !!opts?.paths?.length;
|
|
2588
|
+
if (pathScoped) {
|
|
2589
|
+
if (!toStage.length) {
|
|
2590
|
+
return { ok: true };
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
else if (!keepStaged.length) {
|
|
2615
2594
|
return { ok: true };
|
|
2616
2595
|
}
|
|
2617
2596
|
const commitEnv = {
|
|
@@ -2620,7 +2599,30 @@ async function commitAndPushDirtyWorktree(message) {
|
|
|
2620
2599
|
GIT_COMMITTER_NAME: process.env.GIT_COMMITTER_NAME || "ChimpHands",
|
|
2621
2600
|
GIT_COMMITTER_EMAIL: process.env.GIT_COMMITTER_EMAIL || "chimphands@testchimp.io",
|
|
2622
2601
|
};
|
|
2623
|
-
|
|
2602
|
+
// Pathspec form is `git commit --only` — commits just those paths even if
|
|
2603
|
+
// the index has other staged product files.
|
|
2604
|
+
const commitArgs = pathScoped
|
|
2605
|
+
? [
|
|
2606
|
+
"-c",
|
|
2607
|
+
"user.name=ChimpHands",
|
|
2608
|
+
"-c",
|
|
2609
|
+
"user.email=chimphands@testchimp.io",
|
|
2610
|
+
"commit",
|
|
2611
|
+
"-m",
|
|
2612
|
+
message,
|
|
2613
|
+
"--",
|
|
2614
|
+
...toStage,
|
|
2615
|
+
]
|
|
2616
|
+
: [
|
|
2617
|
+
"-c",
|
|
2618
|
+
"user.name=ChimpHands",
|
|
2619
|
+
"-c",
|
|
2620
|
+
"user.email=chimphands@testchimp.io",
|
|
2621
|
+
"commit",
|
|
2622
|
+
"-m",
|
|
2623
|
+
message,
|
|
2624
|
+
];
|
|
2625
|
+
const commit = await run(commitArgs, commitEnv);
|
|
2624
2626
|
if (commit.code !== 0) {
|
|
2625
2627
|
const error = commit.err || commit.out || "git commit failed";
|
|
2626
2628
|
console.error(`ChimpHands git commit: ${error}`);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paths that must not be committed on the agent branch.
|
|
3
|
+
*
|
|
4
|
+
* Worktree-resident (needed at runtime, never product):
|
|
5
|
+
* - opencode.json, .agents/**
|
|
6
|
+
*
|
|
7
|
+
* Legacy / accidental worktree pollution (now written under /tmp when possible):
|
|
8
|
+
* - *.log runner logs, .chimphands/**, remote-branch*.txt, gha-creds*
|
|
9
|
+
*/
|
|
10
|
+
export declare function isChimpHandsInternalCommitPath(filePath: string): boolean;
|
|
11
|
+
/** Parse paths from `git status --porcelain` (newline form, not -z). */
|
|
12
|
+
export declare function pathsFromGitPorcelain(porcelain: string): string[];
|
|
13
|
+
/** Dirty worktree paths that are eligible to commit on the agent branch. */
|
|
14
|
+
export declare function commitCandidatePathsFromPorcelain(porcelain: string): string[];
|
|
15
|
+
export declare function normalizeWorktreePath(filePath: string): string;
|
|
16
|
+
/** Intersect status candidates with an optional path allowlist (e.g. a single UI save). */
|
|
17
|
+
export declare function resolveCommitPaths(porcelain: string, onlyPaths?: string[]): string[];
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paths that must not be committed on the agent branch.
|
|
3
|
+
*
|
|
4
|
+
* Worktree-resident (needed at runtime, never product):
|
|
5
|
+
* - opencode.json, .agents/**
|
|
6
|
+
*
|
|
7
|
+
* Legacy / accidental worktree pollution (now written under /tmp when possible):
|
|
8
|
+
* - *.log runner logs, .chimphands/**, remote-branch*.txt, gha-creds*
|
|
9
|
+
*/
|
|
10
|
+
export function isChimpHandsInternalCommitPath(filePath) {
|
|
11
|
+
let normalized = filePath.replace(/\\/g, "/").toLowerCase();
|
|
12
|
+
while (normalized.startsWith("./"))
|
|
13
|
+
normalized = normalized.slice(2);
|
|
14
|
+
const slash = normalized.lastIndexOf("/");
|
|
15
|
+
const name = slash >= 0 ? normalized.slice(slash + 1) : normalized;
|
|
16
|
+
if (name === "opencode.json" ||
|
|
17
|
+
name === "opencode-server.log" ||
|
|
18
|
+
name === "chimphands-run.log" ||
|
|
19
|
+
name === "remote-branches.txt" ||
|
|
20
|
+
name === "remote-branch-names.txt" ||
|
|
21
|
+
name.startsWith("gha-creds")) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
return (normalized === ".chimphands" ||
|
|
25
|
+
normalized.startsWith(".chimphands/") ||
|
|
26
|
+
normalized === ".agents" ||
|
|
27
|
+
normalized.startsWith(".agents/"));
|
|
28
|
+
}
|
|
29
|
+
/** Parse paths from `git status --porcelain` (newline form, not -z). */
|
|
30
|
+
export function pathsFromGitPorcelain(porcelain) {
|
|
31
|
+
const paths = [];
|
|
32
|
+
for (const raw of porcelain.split("\n")) {
|
|
33
|
+
if (!raw || raw.length < 4)
|
|
34
|
+
continue;
|
|
35
|
+
let body = raw.slice(3);
|
|
36
|
+
if (body.includes(" -> ")) {
|
|
37
|
+
body = body.slice(body.lastIndexOf(" -> ") + 4);
|
|
38
|
+
}
|
|
39
|
+
if (body.startsWith('"') && body.endsWith('"')) {
|
|
40
|
+
body = body
|
|
41
|
+
.slice(1, -1)
|
|
42
|
+
.replace(/\\([\\"nt])/g, (_m, c) => {
|
|
43
|
+
if (c === "n")
|
|
44
|
+
return "\n";
|
|
45
|
+
if (c === "t")
|
|
46
|
+
return "\t";
|
|
47
|
+
return c;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (body)
|
|
51
|
+
paths.push(body);
|
|
52
|
+
}
|
|
53
|
+
return paths;
|
|
54
|
+
}
|
|
55
|
+
/** Dirty worktree paths that are eligible to commit on the agent branch. */
|
|
56
|
+
export function commitCandidatePathsFromPorcelain(porcelain) {
|
|
57
|
+
return pathsFromGitPorcelain(porcelain).filter((p) => !isChimpHandsInternalCommitPath(p));
|
|
58
|
+
}
|
|
59
|
+
export function normalizeWorktreePath(filePath) {
|
|
60
|
+
let normalized = filePath.replace(/\\/g, "/");
|
|
61
|
+
while (normalized.startsWith("./"))
|
|
62
|
+
normalized = normalized.slice(2);
|
|
63
|
+
return normalized;
|
|
64
|
+
}
|
|
65
|
+
/** Intersect status candidates with an optional path allowlist (e.g. a single UI save). */
|
|
66
|
+
export function resolveCommitPaths(porcelain, onlyPaths) {
|
|
67
|
+
const candidates = commitCandidatePathsFromPorcelain(porcelain);
|
|
68
|
+
if (!onlyPaths?.length)
|
|
69
|
+
return candidates;
|
|
70
|
+
const want = new Set(onlyPaths.map(normalizeWorktreePath));
|
|
71
|
+
return candidates.filter((p) => want.has(normalizeWorktreePath(p)));
|
|
72
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
import { commitCandidatePathsFromPorcelain, isChimpHandsInternalCommitPath, pathsFromGitPorcelain, resolveCommitPaths, } from "./worktreeCommitPaths.js";
|
|
4
|
+
describe("worktreeCommitPaths", () => {
|
|
5
|
+
it("parses porcelain paths and rename destinations", () => {
|
|
6
|
+
assert.deepEqual(pathsFromGitPorcelain([" M ui/plans/foo.md", "?? chimphands-run.log", "R old.md -> new.md"].join("\n")), ["ui/plans/foo.md", "chimphands-run.log", "new.md"]);
|
|
7
|
+
});
|
|
8
|
+
it("allowlists only product paths for commit", () => {
|
|
9
|
+
assert.deepEqual(commitCandidatePathsFromPorcelain([
|
|
10
|
+
" M ui/plans/stories/x.md",
|
|
11
|
+
"?? chimphands-run.log",
|
|
12
|
+
"?? opencode-server.log",
|
|
13
|
+
"?? opencode.json",
|
|
14
|
+
" M src/app.ts",
|
|
15
|
+
"?? .agents/skills/testchimp/SKILL.md",
|
|
16
|
+
].join("\n")), ["ui/plans/stories/x.md", "src/app.ts"]);
|
|
17
|
+
});
|
|
18
|
+
it("can narrow commit to an explicit path set", () => {
|
|
19
|
+
const porcelain = [" M ui/plans/a.md", " M src/app.ts"].join("\n");
|
|
20
|
+
assert.deepEqual(resolveCommitPaths(porcelain, ["ui/plans/a.md"]), ["ui/plans/a.md"]);
|
|
21
|
+
assert.deepEqual(resolveCommitPaths(porcelain), ["ui/plans/a.md", "src/app.ts"]);
|
|
22
|
+
});
|
|
23
|
+
it("recognizes internal commit paths", () => {
|
|
24
|
+
assert.equal(isChimpHandsInternalCommitPath("chimphands-run.log"), true);
|
|
25
|
+
assert.equal(isChimpHandsInternalCommitPath(".chimphands/tmp"), true);
|
|
26
|
+
assert.equal(isChimpHandsInternalCommitPath("ui/plans/x.md"), false);
|
|
27
|
+
});
|
|
28
|
+
});
|
package/dist/core/tools.js
CHANGED
|
@@ -1045,8 +1045,13 @@ export const TOOL_DEFINITIONS = [
|
|
|
1045
1045
|
inputSchema: S.reportAgentActionInput,
|
|
1046
1046
|
execute: async (args, { postMcp }) => {
|
|
1047
1047
|
const a = args;
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1048
|
+
const sessionId = (process.env.SESSION_ID ||
|
|
1049
|
+
process.env.CHIMPHANDS_SESSION_ID ||
|
|
1050
|
+
"").trim();
|
|
1051
|
+
const actorRaw = (a.actorType ?? (sessionId ? "cloud-agent" : "local-agent")).toString();
|
|
1052
|
+
const actorType = actorRaw.toUpperCase().replace(/-/g, "_") === "CLOUD_AGENT" || sessionId
|
|
1053
|
+
? "CLOUD_AGENT"
|
|
1054
|
+
: "LOCAL_AGENT";
|
|
1050
1055
|
const actionNorm = a.actionType.toString().toUpperCase().replace(/-/g, "_");
|
|
1051
1056
|
let actionType = actionNorm;
|
|
1052
1057
|
if (actionNorm === "COMPLETED" || actionNorm === "ACTION_COMPLETED") {
|
|
@@ -1062,6 +1067,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
1062
1067
|
actorType,
|
|
1063
1068
|
entityType: a.entityType,
|
|
1064
1069
|
};
|
|
1070
|
+
if (sessionId)
|
|
1071
|
+
body.chimphandsSessionId = sessionId;
|
|
1065
1072
|
if (a.policyFile)
|
|
1066
1073
|
body.policyFile = a.policyFile;
|
|
1067
1074
|
if (a.policyVersion)
|
|
@@ -1071,8 +1078,10 @@ export const TOOL_DEFINITIONS = [
|
|
|
1071
1078
|
body.gitSha = gitSha;
|
|
1072
1079
|
if (a.userId)
|
|
1073
1080
|
body.userId = a.userId;
|
|
1074
|
-
else if (process.env.TESTCHIMP_USER_ID)
|
|
1081
|
+
else if (!sessionId && process.env.TESTCHIMP_USER_ID) {
|
|
1082
|
+
// ChimpHands: server resolves responsible user from SESSION_ID; do not stamp service account.
|
|
1075
1083
|
body.userId = process.env.TESTCHIMP_USER_ID;
|
|
1084
|
+
}
|
|
1076
1085
|
if (a.branchName)
|
|
1077
1086
|
body.branchName = a.branchName;
|
|
1078
1087
|
// Nested traceability wins for agentModel / skillVersion / cliVersion;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testchimp/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.78",
|
|
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",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
15
|
"build": "tsc",
|
|
16
|
-
"test": "npm run build && node --test dist/chimphands/llmRetryProxy.test.js dist/chimphands/midTurnSteering.test.js dist/chimphands/steeringSidecar.test.js dist/chimphands/agentBranch.test.js",
|
|
16
|
+
"test": "npm run build && node --test dist/chimphands/llmRetryProxy.test.js dist/chimphands/midTurnSteering.test.js dist/chimphands/steeringSidecar.test.js dist/chimphands/agentBranch.test.js dist/chimphands/worktreeCommitPaths.test.js",
|
|
17
17
|
"watch": "tsc --watch",
|
|
18
18
|
"start": "node dist/bin/testchimp.js",
|
|
19
19
|
"prepublishOnly": "npm run build"
|