codeam-cli 2.75.8 → 2.75.9
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/CHANGELOG.md +6 -0
- package/dist/index.js +656 -148
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to `codeam-cli` are documented here.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [2.75.8] — 2026-09-23
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** Heartbeat failures visible + retried, exit when the backend says the session is gone
|
|
12
|
+
|
|
7
13
|
## [2.75.7] — 2026-09-19
|
|
8
14
|
|
|
9
15
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -2865,6 +2865,11 @@ function normalizeGuardrailPolicy(raw) {
|
|
|
2865
2865
|
return out2;
|
|
2866
2866
|
}
|
|
2867
2867
|
|
|
2868
|
+
// ../../packages/shared/src/packs/types.ts
|
|
2869
|
+
var PACK_FINDINGS_FILE = "REVIEW-FINDINGS.pack.json";
|
|
2870
|
+
var MAX_PACK_FINDINGS = 50;
|
|
2871
|
+
var PACK_HANDOFF_HEADING = "## Handoff";
|
|
2872
|
+
|
|
2868
2873
|
// ../../packages/shared/src/packs/roles.ts
|
|
2869
2874
|
var SPECIFIER_PROMPT = `# Role: Specifier
|
|
2870
2875
|
|
|
@@ -2880,6 +2885,8 @@ Method:
|
|
|
2880
2885
|
- **Verification plan** \u2014 for each criterion, the level that proves it (unit / integration / manual) and why.
|
|
2881
2886
|
3. Right-size: if the task is clearly too large for one pipeline run, narrow the criteria to a coherent first slice and record the rest under "Out of scope / next".
|
|
2882
2887
|
|
|
2888
|
+
4. Close with a \`## Handoff\` section: the criteria count, what you scoped out, and any assumption the Coder must know.
|
|
2889
|
+
|
|
2883
2890
|
Handoff bar: the spec file is committed; every acceptance criterion is testable as written; a competent implementer could start without asking you anything.`;
|
|
2884
2891
|
var CODER_PROMPT = `# Role: Coder
|
|
2885
2892
|
|
|
@@ -2890,6 +2897,7 @@ Method:
|
|
|
2890
2897
|
2. Test-first where it fits: write the test that proves a criterion, watch it fail, implement until it passes. Where strict test-first doesn't fit, still land tests alongside the change.
|
|
2891
2898
|
3. Match the project's existing style, structure, and conventions. Simplest design that fully solves the problem \u2014 no speculative abstractions, no "while I'm here" changes.
|
|
2892
2899
|
4. Run the project's tests / linters / build and make them pass.
|
|
2900
|
+
5. Close with a \`## Handoff\` section: which criteria are implemented (by number) and where their tests live, anything you consciously did NOT do, and what the Reviewer should look at first.
|
|
2893
2901
|
|
|
2894
2902
|
Handoff bar: every acceptance criterion is implemented and covered by a test; the project's checks pass; the work is committed in focused commits.`;
|
|
2895
2903
|
var REVIEWER_PROMPT = `# Role: Reviewer
|
|
@@ -2905,21 +2913,42 @@ Method:
|
|
|
2905
2913
|
- **Conventions & naming** \u2014 matches the surrounding code; names say what things are.
|
|
2906
2914
|
- **Safety** \u2014 no secrets, credentials, or debugging remnants in code, tests, or fixtures.
|
|
2907
2915
|
3. Fix what is justified \u2014 smallest change that resolves the finding, keeping behavior. Re-run the checks after material fixes.
|
|
2908
|
-
4.
|
|
2909
|
-
|
|
2910
|
-
|
|
2916
|
+
4. Write your findings as DATA to \`REVIEW-FINDINGS.pack.json\` at the repo root \u2014 the next stage verifies them one by one, so what is not in this file does not get verified. Exact shape:
|
|
2917
|
+
\`\`\`json
|
|
2918
|
+
{
|
|
2919
|
+
"findings": [
|
|
2920
|
+
{
|
|
2921
|
+
"id": "R1",
|
|
2922
|
+
"severity": "blocker | major | minor | nit",
|
|
2923
|
+
"title": "one line: what is wrong",
|
|
2924
|
+
"detail": "why it matters and what you did about it",
|
|
2925
|
+
"file": "src/path/to/file.ts",
|
|
2926
|
+
"line": 42,
|
|
2927
|
+
"resolution": "fixed | deferred | wont_fix | needs_verification",
|
|
2928
|
+
"commit": "abcdef1234"
|
|
2929
|
+
}
|
|
2930
|
+
],
|
|
2931
|
+
"checked": ["what you audited when the list is empty: e.g. error paths of X, test coverage of Y"]
|
|
2932
|
+
}
|
|
2933
|
+
\`\`\`
|
|
2934
|
+
Every finding you fixed says \`fixed\` with its commit; anything you deliberately left says \`deferred\` or \`wont_fix\` with the reason in \`detail\`; anything you could not verify says \`needs_verification\`. An empty \`findings\` array is a valid, honest result \u2014 then \`checked\` must say what you looked at. Commit this file with your stage.
|
|
2935
|
+
5. Close with a \`## Handoff\` section: what you found (counts by severity), what you fixed, what you deliberately left, and what the next stage must verify.
|
|
2936
|
+
|
|
2937
|
+
Handoff bar: checks pass on YOUR final commit; every fix is committed; \`REVIEW-FINDINGS.pack.json\` is committed and lists every finding with its resolution (an empty list says what you checked).`;
|
|
2911
2938
|
var QA_PROMPT = `# Role: QA
|
|
2912
2939
|
|
|
2913
2940
|
You are the final gate. You verify the delivered work against the acceptance criteria as a whole \u2014 end to end, the way a demanding user would \u2014 and produce the run's closing report.
|
|
2914
2941
|
|
|
2915
2942
|
Method:
|
|
2916
2943
|
1. Read the task and \`SPEC.pack.md\` (when present). Your contract is the acceptance criteria; without a spec, derive them from the task.
|
|
2917
|
-
2.
|
|
2918
|
-
3.
|
|
2919
|
-
4.
|
|
2920
|
-
5.
|
|
2944
|
+
2. Read \`REVIEW-FINDINGS.pack.json\` (when present \u2014 the Reviewer's structured findings, also summarized in your handoff input). Every finding there is an item on YOUR checklist: a \`fixed\` one must be verified fixed without regression; a \`deferred\`/\`wont_fix\` one must be judged acceptable or escalated; a \`needs_verification\` one is yours to settle.
|
|
2945
|
+
3. For EACH criterion, verify it against the real project: run the relevant tests, execute the code paths where feasible, inspect actual behavior/output. Do not take earlier stages' word for anything.
|
|
2946
|
+
4. Run the project's full checks (tests, lint, types, build) one final time.
|
|
2947
|
+
5. Write \`QA-REPORT.pack.md\` at the repo root with two sections: **Acceptance criteria** \u2014 per-criterion verdict (\u2705 verified / \u26A0\uFE0F partially / \u274C failed \u2014 with evidence for each); **Review findings** \u2014 per finding id, verdict (\u2705 verified fixed / \u26A0\uFE0F still open / \u274C regressed \u2014 with evidence). Then the checks' results, anything not verifiable in this environment (stated plainly), and a short "ready to ship?" conclusion.
|
|
2948
|
+
6. If a criterion FAILS: fix it only when the fix is small and unambiguous; otherwise mark it failed with exact evidence \u2014 the user decides. Never paper over a failure.
|
|
2949
|
+
7. Close with a \`## Handoff\` section: the ship/no-ship conclusion, the failed or open items by id, and what you could not verify.
|
|
2921
2950
|
|
|
2922
|
-
Handoff bar: the report is committed; every verdict carries evidence; the conclusion is honest about anything unverified.`;
|
|
2951
|
+
Handoff bar: the report is committed; every verdict carries evidence; every finding id from the Reviewer appears in the report; the conclusion is honest about anything unverified.`;
|
|
2923
2952
|
|
|
2924
2953
|
// ../../packages/shared/src/packs/registry.ts
|
|
2925
2954
|
var PACK_REGISTRY = {
|
|
@@ -2943,7 +2972,9 @@ var PACK_REGISTRY = {
|
|
|
2943
2972
|
skillIds: ["code-review", "code-naming"],
|
|
2944
2973
|
prompt: REVIEWER_PROMPT,
|
|
2945
2974
|
// Review-only: approving clean with no change is success, not a stall.
|
|
2946
|
-
requiresCommit: false
|
|
2975
|
+
requiresCommit: false,
|
|
2976
|
+
// Leaves REVIEW-FINDINGS.pack.json — the next stage gets a list, not prose.
|
|
2977
|
+
producesFindings: true
|
|
2947
2978
|
}
|
|
2948
2979
|
]
|
|
2949
2980
|
},
|
|
@@ -2974,7 +3005,9 @@ var PACK_REGISTRY = {
|
|
|
2974
3005
|
skillIds: ["code-review", "code-naming"],
|
|
2975
3006
|
prompt: REVIEWER_PROMPT,
|
|
2976
3007
|
// Review-only: approving clean with no change is success, not a stall.
|
|
2977
|
-
requiresCommit: false
|
|
3008
|
+
requiresCommit: false,
|
|
3009
|
+
// Leaves REVIEW-FINDINGS.pack.json — the next stage gets a list, not prose.
|
|
3010
|
+
producesFindings: true
|
|
2978
3011
|
},
|
|
2979
3012
|
{
|
|
2980
3013
|
role: "qa",
|
|
@@ -3004,8 +3037,140 @@ You are ONE specialist role in a multi-role pipeline running on this repository.
|
|
|
3004
3037
|
- **Never leave the tree broken.** Run the project's checks before finishing when the project has them; your stage ends with a working tree the next role can build on.
|
|
3005
3038
|
- **Do not push, force-push, or touch remotes** \u2014 the pipeline works locally; publishing is the user's call at the end.
|
|
3006
3039
|
- **Never read, edit, or commit anything under \`.codeam/\`** \u2014 that is the pipeline's own ledger, not project code.
|
|
3007
|
-
- **Finish
|
|
3008
|
-
- **If you
|
|
3040
|
+
- **Finish with a \`## Handoff\` section.** When your stage's job is done and committed, end your reply with a heading \`## Handoff\` followed by 2-6 lines: what you did, what you verified, anything the next stage must know. That section \u2014 not the rest of your reply \u2014 is what the next role receives. Don't ask "should I continue?" \u2014 the pipeline advances automatically.
|
|
3041
|
+
- **If you need the user's decision** (contradictory requirements, a real product choice), ask ONE clear question and end your reply with 2-4 numbered options on their own lines (\`1. \u2026\`), then stop. The pipeline pauses until they answer in this conversation; keep working in it once they do, and commit as usual.
|
|
3042
|
+
- **If you are genuinely blocked** (missing access, a broken environment you cannot fix), say exactly what is blocking you and stop \u2014 the user is supervising and will decide.`;
|
|
3043
|
+
|
|
3044
|
+
// ../../packages/shared/src/packs/findings.ts
|
|
3045
|
+
var SEVERITIES = /* @__PURE__ */ new Set([
|
|
3046
|
+
"blocker",
|
|
3047
|
+
"major",
|
|
3048
|
+
"minor",
|
|
3049
|
+
"nit"
|
|
3050
|
+
]);
|
|
3051
|
+
var RESOLUTIONS = /* @__PURE__ */ new Set([
|
|
3052
|
+
"fixed",
|
|
3053
|
+
"deferred",
|
|
3054
|
+
"wont_fix",
|
|
3055
|
+
"needs_verification"
|
|
3056
|
+
]);
|
|
3057
|
+
var TITLE_MAX = 200;
|
|
3058
|
+
var DETAIL_MAX = 1200;
|
|
3059
|
+
function parsePackFindings(raw) {
|
|
3060
|
+
const text = stripFence(raw).trim();
|
|
3061
|
+
if (text.length === 0) return { ok: false, error: "findings file is empty" };
|
|
3062
|
+
let data;
|
|
3063
|
+
try {
|
|
3064
|
+
data = JSON.parse(text);
|
|
3065
|
+
} catch {
|
|
3066
|
+
const salvaged = salvageJson(text);
|
|
3067
|
+
if (salvaged === null) return { ok: false, error: "findings file is not valid JSON" };
|
|
3068
|
+
data = salvaged;
|
|
3069
|
+
}
|
|
3070
|
+
const container = Array.isArray(data) ? { findings: data } : data;
|
|
3071
|
+
if (!isRecord(container) || !Array.isArray(container.findings)) {
|
|
3072
|
+
return { ok: false, error: 'findings file has no "findings" array' };
|
|
3073
|
+
}
|
|
3074
|
+
const findings = [];
|
|
3075
|
+
let dropped = 0;
|
|
3076
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3077
|
+
for (const entry of container.findings) {
|
|
3078
|
+
const f = coerceFinding(entry, findings.length + 1);
|
|
3079
|
+
if (!f) {
|
|
3080
|
+
dropped += 1;
|
|
3081
|
+
continue;
|
|
3082
|
+
}
|
|
3083
|
+
if (seen.has(f.id)) f.id = `${f.id}-${findings.length + 1}`;
|
|
3084
|
+
seen.add(f.id);
|
|
3085
|
+
findings.push(f);
|
|
3086
|
+
if (findings.length >= MAX_PACK_FINDINGS) break;
|
|
3087
|
+
}
|
|
3088
|
+
const truncated = container.findings.length - dropped - findings.length;
|
|
3089
|
+
const checked = Array.isArray(container.checked) ? container.checked.filter((c2) => typeof c2 === "string" && c2.trim().length > 0).slice(0, 20) : void 0;
|
|
3090
|
+
const notes = [];
|
|
3091
|
+
if (dropped > 0) notes.push(`${dropped} malformed entr${dropped === 1 ? "y" : "ies"} dropped`);
|
|
3092
|
+
if (truncated > 0)
|
|
3093
|
+
notes.push(`list truncated to ${MAX_PACK_FINDINGS} (${truncated} more in the file)`);
|
|
3094
|
+
return {
|
|
3095
|
+
ok: true,
|
|
3096
|
+
value: {
|
|
3097
|
+
findings,
|
|
3098
|
+
...checked && checked.length > 0 ? { checked } : {},
|
|
3099
|
+
...notes.length > 0 ? { note: notes.join("; ") } : {}
|
|
3100
|
+
}
|
|
3101
|
+
};
|
|
3102
|
+
}
|
|
3103
|
+
function coerceFinding(entry, ordinal) {
|
|
3104
|
+
if (!isRecord(entry)) return null;
|
|
3105
|
+
const title = str(entry.title) ?? str(entry.summary);
|
|
3106
|
+
if (!title) return null;
|
|
3107
|
+
const id = str(entry.id) ?? `R${ordinal}`;
|
|
3108
|
+
const severityRaw = (str(entry.severity) ?? "").toLowerCase();
|
|
3109
|
+
const resolutionRaw = (str(entry.resolution) ?? str(entry.status) ?? "").toLowerCase().replace(/[\s-]+/g, "_");
|
|
3110
|
+
const line = typeof entry.line === "number" && Number.isFinite(entry.line) && entry.line > 0 ? Math.floor(entry.line) : void 0;
|
|
3111
|
+
const detail = str(entry.detail) ?? str(entry.description);
|
|
3112
|
+
const file = str(entry.file) ?? str(entry.path);
|
|
3113
|
+
const commit = str(entry.commit);
|
|
3114
|
+
return {
|
|
3115
|
+
id: id.slice(0, 32),
|
|
3116
|
+
severity: SEVERITIES.has(severityRaw) ? severityRaw : "major",
|
|
3117
|
+
title: title.slice(0, TITLE_MAX),
|
|
3118
|
+
...detail ? { detail: detail.slice(0, DETAIL_MAX) } : {},
|
|
3119
|
+
...file ? { file: file.slice(0, 300) } : {},
|
|
3120
|
+
...line !== void 0 ? { line } : {},
|
|
3121
|
+
resolution: RESOLUTIONS.has(resolutionRaw) ? resolutionRaw : "needs_verification",
|
|
3122
|
+
...commit && /^[0-9a-f]{7,40}$/i.test(commit) ? { commit: commit.slice(0, 10) } : {}
|
|
3123
|
+
};
|
|
3124
|
+
}
|
|
3125
|
+
function extractHandoffSection(reply) {
|
|
3126
|
+
const lines = reply.split("\n");
|
|
3127
|
+
const want = PACK_HANDOFF_HEADING.replace(/^#+\s*/, "").toLowerCase();
|
|
3128
|
+
let start2 = -1;
|
|
3129
|
+
let level = 0;
|
|
3130
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
3131
|
+
const m = lines[i].match(/^\s{0,3}(#{1,4})\s+(.+?)\s*#*\s*$/);
|
|
3132
|
+
if (m && m[2].trim().toLowerCase().replace(/[:.]+$/, "") === want) {
|
|
3133
|
+
start2 = i;
|
|
3134
|
+
level = m[1].length;
|
|
3135
|
+
break;
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3138
|
+
if (start2 < 0) return null;
|
|
3139
|
+
const body = [];
|
|
3140
|
+
for (let i = start2 + 1; i < lines.length; i++) {
|
|
3141
|
+
const m = lines[i].match(/^\s{0,3}(#{1,6})\s+/);
|
|
3142
|
+
if (m && m[1].length <= level) break;
|
|
3143
|
+
body.push(lines[i]);
|
|
3144
|
+
}
|
|
3145
|
+
const text = body.join("\n").trim();
|
|
3146
|
+
return text.length > 0 ? text : null;
|
|
3147
|
+
}
|
|
3148
|
+
function isRecord(v) {
|
|
3149
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3150
|
+
}
|
|
3151
|
+
function str(v) {
|
|
3152
|
+
return typeof v === "string" && v.trim().length > 0 ? v.trim() : void 0;
|
|
3153
|
+
}
|
|
3154
|
+
function stripFence(raw) {
|
|
3155
|
+
const m = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
3156
|
+
return m ? m[1] : raw;
|
|
3157
|
+
}
|
|
3158
|
+
function salvageJson(text) {
|
|
3159
|
+
for (const [open, close] of [
|
|
3160
|
+
["{", "}"],
|
|
3161
|
+
["[", "]"]
|
|
3162
|
+
]) {
|
|
3163
|
+
const a = text.indexOf(open);
|
|
3164
|
+
const b = text.lastIndexOf(close);
|
|
3165
|
+
if (a >= 0 && b > a) {
|
|
3166
|
+
try {
|
|
3167
|
+
return JSON.parse(text.slice(a, b + 1));
|
|
3168
|
+
} catch {
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
return null;
|
|
3173
|
+
}
|
|
3009
3174
|
|
|
3010
3175
|
// ../../packages/shared/src/api-url.ts
|
|
3011
3176
|
var DEFAULT_API_BASE_URL = "https://api.codeagent-mobile.com";
|
|
@@ -8689,7 +8854,7 @@ var TOPLEVEL_SENTINELS = [
|
|
|
8689
8854
|
field: "window_id"
|
|
8690
8855
|
}
|
|
8691
8856
|
];
|
|
8692
|
-
function
|
|
8857
|
+
function isRecord2(value) {
|
|
8693
8858
|
return "object" == typeof value && null !== value && !Array.isArray(value);
|
|
8694
8859
|
}
|
|
8695
8860
|
function toRfc3339(timestamp) {
|
|
@@ -8708,7 +8873,7 @@ function relocateInto(properties, key, value) {
|
|
|
8708
8873
|
if (void 0 !== value && !(key in properties)) properties[key] = value;
|
|
8709
8874
|
}
|
|
8710
8875
|
function buildV1Event(message) {
|
|
8711
|
-
const sourceProperties =
|
|
8876
|
+
const sourceProperties = isRecord2(message.properties) ? message.properties : {};
|
|
8712
8877
|
const properties = {
|
|
8713
8878
|
...sourceProperties
|
|
8714
8879
|
};
|
|
@@ -10344,7 +10509,7 @@ function readAnonId() {
|
|
|
10344
10509
|
}
|
|
10345
10510
|
function superProperties() {
|
|
10346
10511
|
return {
|
|
10347
|
-
cliVersion: true ? "2.75.
|
|
10512
|
+
cliVersion: true ? "2.75.9" : "0.0.0-dev",
|
|
10348
10513
|
nodeVersion: process.version,
|
|
10349
10514
|
platform: process.platform,
|
|
10350
10515
|
arch: process.arch,
|
|
@@ -10586,7 +10751,7 @@ var http = __toESM(require("http"));
|
|
|
10586
10751
|
// package.json
|
|
10587
10752
|
var package_default = {
|
|
10588
10753
|
name: "codeam-cli",
|
|
10589
|
-
version: "2.75.
|
|
10754
|
+
version: "2.75.9",
|
|
10590
10755
|
description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
|
|
10591
10756
|
type: "commonjs",
|
|
10592
10757
|
main: "dist/index.js",
|
|
@@ -11384,7 +11549,7 @@ var streamHost = new StreamHostSelector(
|
|
|
11384
11549
|
);
|
|
11385
11550
|
|
|
11386
11551
|
// src/services/command-relay.service.ts
|
|
11387
|
-
function
|
|
11552
|
+
function isRecord3(v) {
|
|
11388
11553
|
return typeof v === "object" && v !== null;
|
|
11389
11554
|
}
|
|
11390
11555
|
function httpStatusOf(err) {
|
|
@@ -11916,14 +12081,14 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
11916
12081
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
11917
12082
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
11918
12083
|
// pair/reconnect). Older backends ignore the extra field.
|
|
11919
|
-
..."2.75.
|
|
12084
|
+
..."2.75.9" ? { ideVersion: "2.75.9" } : {}
|
|
11920
12085
|
};
|
|
11921
12086
|
try {
|
|
11922
12087
|
const res = await _postJson(`${API_BASE2}/api/plugin/heartbeat`, body);
|
|
11923
12088
|
this.heartbeatFailStreak = 0;
|
|
11924
12089
|
log.trace("relay", `heartbeat ok online=${online}`);
|
|
11925
12090
|
const data = res?.data;
|
|
11926
|
-
if (online &&
|
|
12091
|
+
if (online && isRecord3(data) && data.paired === false) this.handleSessionGone();
|
|
11927
12092
|
} catch (err) {
|
|
11928
12093
|
if (!online) {
|
|
11929
12094
|
log.trace("relay", "goodbye heartbeat failed", err);
|
|
@@ -31297,14 +31462,14 @@ var zGuardCreateElicitationResponseCancel = z7.object({
|
|
|
31297
31462
|
|
|
31298
31463
|
// ../../node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js
|
|
31299
31464
|
var CANCEL_REQUEST_METHOD = "$/cancel_request";
|
|
31300
|
-
function
|
|
31465
|
+
function isRecord4(value) {
|
|
31301
31466
|
return typeof value === "object" && value !== null;
|
|
31302
31467
|
}
|
|
31303
31468
|
function isJsonRpcId(value) {
|
|
31304
31469
|
return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
|
|
31305
31470
|
}
|
|
31306
31471
|
function cancelRequestId(params) {
|
|
31307
|
-
if (!
|
|
31472
|
+
if (!isRecord4(params) || !isJsonRpcId(params["requestId"])) {
|
|
31308
31473
|
return void 0;
|
|
31309
31474
|
}
|
|
31310
31475
|
return params["requestId"];
|
|
@@ -31736,7 +31901,7 @@ var Connection = class {
|
|
|
31736
31901
|
if (this.abortController.signal.aborted) {
|
|
31737
31902
|
return;
|
|
31738
31903
|
}
|
|
31739
|
-
if (!
|
|
31904
|
+
if (!isRecord4(message)) {
|
|
31740
31905
|
console.error("Invalid message", { message });
|
|
31741
31906
|
return;
|
|
31742
31907
|
}
|
|
@@ -31829,7 +31994,7 @@ var Connection = class {
|
|
|
31829
31994
|
pendingResponse.cleanup?.();
|
|
31830
31995
|
if ("result" in response) {
|
|
31831
31996
|
pendingResponse.resolve(response.result);
|
|
31832
|
-
} else if ("error" in response &&
|
|
31997
|
+
} else if ("error" in response && isRecord4(response.error)) {
|
|
31833
31998
|
const { code, message, data } = response.error;
|
|
31834
31999
|
pendingResponse.reject(new RequestError(code, message, data));
|
|
31835
32000
|
} else {
|
|
@@ -32102,7 +32267,7 @@ function ndJsonStream(output, input) {
|
|
|
32102
32267
|
if (trimmedLine) {
|
|
32103
32268
|
try {
|
|
32104
32269
|
const message = JSON.parse(trimmedLine);
|
|
32105
|
-
if (
|
|
32270
|
+
if (isRecord4(message)) {
|
|
32106
32271
|
controller.enqueue(message);
|
|
32107
32272
|
} else {
|
|
32108
32273
|
console.warn("Skipping JSON line that is not an object:", trimmedLine);
|
|
@@ -37356,11 +37521,16 @@ async function postCredentialSync(opts, fetchImpl = fetch) {
|
|
|
37356
37521
|
var import_node_child_process22 = require("child_process");
|
|
37357
37522
|
var fs60 = __toESM(require("fs"));
|
|
37358
37523
|
var path70 = __toESM(require("path"));
|
|
37359
|
-
var defaultCommandRunner = (file, args2, cwd, timeoutMs) => new Promise((resolve11) => {
|
|
37524
|
+
var defaultCommandRunner = (file, args2, cwd, timeoutMs, env) => new Promise((resolve11) => {
|
|
37360
37525
|
(0, import_node_child_process22.execFile)(
|
|
37361
37526
|
file,
|
|
37362
37527
|
args2,
|
|
37363
|
-
{
|
|
37528
|
+
{
|
|
37529
|
+
cwd,
|
|
37530
|
+
timeout: timeoutMs,
|
|
37531
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
37532
|
+
env: env ? { ...process.env, ...env } : process.env
|
|
37533
|
+
},
|
|
37364
37534
|
(err, stdout, stderr) => {
|
|
37365
37535
|
let code = 0;
|
|
37366
37536
|
if (err) {
|
|
@@ -37377,7 +37547,12 @@ async function gitHead(run, cwd) {
|
|
|
37377
37547
|
return res.code === 0 ? res.stdout.trim() : null;
|
|
37378
37548
|
}
|
|
37379
37549
|
async function canonicalCommit(run, cwd, sha) {
|
|
37380
|
-
const verify = await run(
|
|
37550
|
+
const verify = await run(
|
|
37551
|
+
"git",
|
|
37552
|
+
["rev-parse", "--verify", `${sha}^{commit}`],
|
|
37553
|
+
cwd,
|
|
37554
|
+
GIT_TIMEOUT_MS
|
|
37555
|
+
);
|
|
37381
37556
|
if (verify.code !== 0) return null;
|
|
37382
37557
|
const short = await run("git", ["rev-parse", "--short=10", sha], cwd, GIT_TIMEOUT_MS);
|
|
37383
37558
|
return short.code === 0 ? short.stdout.trim() : null;
|
|
@@ -37388,6 +37563,18 @@ async function diffStat(run, cwd, from, to) {
|
|
|
37388
37563
|
const lines = res.stdout.trim().split("\n").filter(Boolean);
|
|
37389
37564
|
return lines.length > 0 ? lines[lines.length - 1].trim() : "";
|
|
37390
37565
|
}
|
|
37566
|
+
async function changedFiles(run, cwd, from, to) {
|
|
37567
|
+
const res = await run("git", ["diff", "--name-only", `${from}..${to}`], cwd, GIT_TIMEOUT_MS);
|
|
37568
|
+
if (res.code !== 0) return [];
|
|
37569
|
+
return res.stdout.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
37570
|
+
}
|
|
37571
|
+
function readWorkspaceFile(cwd, relPath) {
|
|
37572
|
+
try {
|
|
37573
|
+
return fs60.readFileSync(path70.join(cwd, relPath), "utf8");
|
|
37574
|
+
} catch {
|
|
37575
|
+
return null;
|
|
37576
|
+
}
|
|
37577
|
+
}
|
|
37391
37578
|
var NO_TEST_PLACEHOLDER = 'echo "Error: no test specified"';
|
|
37392
37579
|
var CHECKS_TIMEOUT_MS = 5 * 6e4;
|
|
37393
37580
|
function detectChecksCommand(cwd) {
|
|
@@ -37409,7 +37596,7 @@ function detectChecksCommand(cwd) {
|
|
|
37409
37596
|
return null;
|
|
37410
37597
|
}
|
|
37411
37598
|
async function runChecks(run, cwd, command2) {
|
|
37412
|
-
const res = await run("sh", ["-c", command2], cwd, CHECKS_TIMEOUT_MS);
|
|
37599
|
+
const res = await run("sh", ["-c", command2], cwd, CHECKS_TIMEOUT_MS, { CI: "1" });
|
|
37413
37600
|
const combined = `${res.stdout}
|
|
37414
37601
|
${res.stderr}`.trim();
|
|
37415
37602
|
const tail = combined.split("\n").slice(-12).join("\n").slice(-1500);
|
|
@@ -37506,33 +37693,109 @@ async function postPackState(opts, state, fetchImpl = fetch) {
|
|
|
37506
37693
|
}
|
|
37507
37694
|
|
|
37508
37695
|
// src/packs/runner.ts
|
|
37509
|
-
var
|
|
37510
|
-
|
|
37511
|
-
|
|
37696
|
+
var TERMINAL_PACK_STATUSES = /* @__PURE__ */ new Set([
|
|
37697
|
+
"completed",
|
|
37698
|
+
"aborted",
|
|
37699
|
+
"failed"
|
|
37700
|
+
]);
|
|
37701
|
+
var NUDGE_PROMPT = "Your stage is not committed yet. Commit your completed work now (focused commits, ending with your role byline `By <role>.` on its own line), then close with your `## Handoff` section and stop. If you are blocked, say exactly what is blocking you instead.";
|
|
37702
|
+
var SUMMARY_TAIL_MAX_CHARS = 600;
|
|
37703
|
+
var SUMMARY_SECTION_MAX_CHARS = 1500;
|
|
37704
|
+
var EARLIER_HANDOFF_MAX_CHARS = 400;
|
|
37705
|
+
function composeStagePrompt(pack, stageIndex, task, ctx) {
|
|
37512
37706
|
const stage = pack.stages[stageIndex];
|
|
37513
37707
|
const pipeline2 = pack.stages.map((s, i) => i === stageIndex ? `[${s.name}]` : s.name).join(" \u2192 ");
|
|
37708
|
+
const position = [
|
|
37709
|
+
`${pack.name}: ${pipeline2} \u2014 you are stage ${stageIndex + 1} of ${pack.stages.length}. Your commit byline: \`By ${stage.role}.\``
|
|
37710
|
+
];
|
|
37711
|
+
if (ctx.baseCommit) {
|
|
37712
|
+
position.push(
|
|
37713
|
+
`Pipeline base commit: \`${ctx.baseCommit}\` \u2014 everything the pipeline has done so far is \`git log ${ctx.baseCommit}..HEAD\` / \`git diff ${ctx.baseCommit}..HEAD\`. Use that range; do not guess it.`
|
|
37714
|
+
);
|
|
37715
|
+
}
|
|
37514
37716
|
const parts = [
|
|
37515
37717
|
stage.prompt,
|
|
37516
37718
|
PACK_WORKFLOW_ARTICLE,
|
|
37517
37719
|
`## Your pipeline position
|
|
37518
|
-
${
|
|
37720
|
+
${position.join("\n")}`,
|
|
37519
37721
|
`## Task
|
|
37520
37722
|
${task}`
|
|
37521
37723
|
];
|
|
37522
|
-
|
|
37724
|
+
const done = ctx.prior.filter((p2) => p2.status === "done" && p2.handoff);
|
|
37725
|
+
const skipped = ctx.prior.filter((p2) => p2.status === "skipped");
|
|
37726
|
+
if (done.length > 0) {
|
|
37727
|
+
const last = done[done.length - 1];
|
|
37728
|
+
const earlier = done.slice(0, -1);
|
|
37729
|
+
if (earlier.length > 0) {
|
|
37730
|
+
parts.push(
|
|
37731
|
+
`## Earlier handoffs
|
|
37732
|
+
${earlier.map((p2) => renderHandoff(p2, EARLIER_HANDOFF_MAX_CHARS)).join("\n\n")}`
|
|
37733
|
+
);
|
|
37734
|
+
}
|
|
37523
37735
|
parts.push(
|
|
37524
|
-
`## Previous stage handoff (${
|
|
37525
|
-
|
|
37526
|
-
|
|
37527
|
-
|
|
37528
|
-
|
|
37736
|
+
`## Previous stage handoff (${last.role})
|
|
37737
|
+
${renderHandoff(last, SUMMARY_SECTION_MAX_CHARS)}`
|
|
37738
|
+
);
|
|
37739
|
+
}
|
|
37740
|
+
if (skipped.length > 0) {
|
|
37741
|
+
parts.push(
|
|
37742
|
+
`## Skipped stages
|
|
37743
|
+
${skipped.map((p2) => `- ${p2.name} (${p2.role}) was skipped by the user \u2014 its artifacts do not exist; do not assume them.`).join("\n")}`
|
|
37744
|
+
);
|
|
37745
|
+
}
|
|
37746
|
+
if (ctx.attempt > 1) {
|
|
37747
|
+
parts.push(
|
|
37748
|
+
`## Previous attempt of this stage
|
|
37749
|
+
Attempt ${ctx.attempt - 1} did not hand off${ctx.previousAttemptError ? `: ${ctx.previousAttemptError}` : "."}
|
|
37750
|
+
The working tree may contain that attempt's partial work. Inspect it first, keep what is right, and finish the stage properly.`
|
|
37529
37751
|
);
|
|
37530
37752
|
}
|
|
37531
37753
|
return parts.join("\n\n");
|
|
37532
37754
|
}
|
|
37755
|
+
function renderHandoff(p2, summaryMax) {
|
|
37756
|
+
const h = p2.handoff;
|
|
37757
|
+
const head = [`### ${p2.name} (${p2.role})`, `commit: ${h.commit}`];
|
|
37758
|
+
if (h.diffStat) head.push(h.diffStat);
|
|
37759
|
+
if (h.checks)
|
|
37760
|
+
head.push(`checks (\`${h.checks.command}\`): ${h.checks.passed ? "passed" : "FAILED"}`);
|
|
37761
|
+
const lines = [head.join("\n")];
|
|
37762
|
+
const summary = h.summary.trim();
|
|
37763
|
+
if (summary) lines.push(summary.length > summaryMax ? `\u2026${summary.slice(-summaryMax)}` : summary);
|
|
37764
|
+
if (h.checks && !h.checks.passed && h.checks.tail) {
|
|
37765
|
+
lines.push(`Checks output (tail):
|
|
37766
|
+
${h.checks.tail}`);
|
|
37767
|
+
}
|
|
37768
|
+
if (h.findings) {
|
|
37769
|
+
lines.push(renderFindings(h.findings, h.findingsNote));
|
|
37770
|
+
} else if (h.findingsNote) {
|
|
37771
|
+
lines.push(`Structured findings: ${h.findingsNote}`);
|
|
37772
|
+
}
|
|
37773
|
+
return lines.join("\n\n");
|
|
37774
|
+
}
|
|
37775
|
+
function renderFindings(findings, note) {
|
|
37776
|
+
const list = findings ?? [];
|
|
37777
|
+
const header = `Structured findings (${PACK_FINDINGS_FILE}): ${list.length}${note ? ` \u2014 ${note}` : ""}`;
|
|
37778
|
+
if (list.length === 0) return header;
|
|
37779
|
+
const rows = list.map((f) => {
|
|
37780
|
+
const where = f.file ? ` @ ${f.file}${f.line ? `:${f.line}` : ""}` : "";
|
|
37781
|
+
return `- [${f.id}] ${f.severity.toUpperCase()} \u2014 ${f.title}${where} \u2192 ${f.resolution}${f.commit ? ` (${f.commit})` : ""}`;
|
|
37782
|
+
});
|
|
37783
|
+
return `${header}
|
|
37784
|
+
${rows.join("\n")}`;
|
|
37785
|
+
}
|
|
37786
|
+
function summarizeReply(text) {
|
|
37787
|
+
const section = extractHandoffSection(text);
|
|
37788
|
+
if (section)
|
|
37789
|
+
return section.length > SUMMARY_SECTION_MAX_CHARS ? section.slice(0, SUMMARY_SECTION_MAX_CHARS) : section;
|
|
37790
|
+
return text.trim().slice(-SUMMARY_TAIL_MAX_CHARS);
|
|
37791
|
+
}
|
|
37533
37792
|
function nowIso() {
|
|
37534
37793
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
37535
37794
|
}
|
|
37795
|
+
function sameCommit(a, b) {
|
|
37796
|
+
if (!a || !b) return a === b;
|
|
37797
|
+
return a.startsWith(b) || b.startsWith(a);
|
|
37798
|
+
}
|
|
37536
37799
|
var PackRunner = class _PackRunner {
|
|
37537
37800
|
constructor(deps, pack, initial) {
|
|
37538
37801
|
this.deps = deps;
|
|
@@ -37544,6 +37807,10 @@ var PackRunner = class _PackRunner {
|
|
|
37544
37807
|
state;
|
|
37545
37808
|
control = "run";
|
|
37546
37809
|
looping = false;
|
|
37810
|
+
inTurn = false;
|
|
37811
|
+
/** Next `runStage` continues the current stage in place (no new prompt). */
|
|
37812
|
+
resumeInPlace = false;
|
|
37813
|
+
mountedSkills = [];
|
|
37547
37814
|
static create(deps, packId, task, runId) {
|
|
37548
37815
|
const pack = getPackDefinition(packId);
|
|
37549
37816
|
if (!pack) throw new Error(`unknown pack: ${packId}`);
|
|
@@ -37564,9 +37831,51 @@ var PackRunner = class _PackRunner {
|
|
|
37564
37831
|
};
|
|
37565
37832
|
return new _PackRunner(deps, pack, state);
|
|
37566
37833
|
}
|
|
37834
|
+
/**
|
|
37835
|
+
* Rebuild a runner from the workspace ledger after a CLI restart. The
|
|
37836
|
+
* in-memory run died with the process; its conversation is gone. A stage
|
|
37837
|
+
* that was mid-turn is marked failed with the real reason (never left
|
|
37838
|
+
* "active" forever), and the run lands paused/stalled so Resume / Retry /
|
|
37839
|
+
* Skip / Abort all work again. Null when the pack id is unknown.
|
|
37840
|
+
*/
|
|
37841
|
+
static rehydrate(deps, stored) {
|
|
37842
|
+
const pack = getPackDefinition(stored.packId);
|
|
37843
|
+
if (!pack) return null;
|
|
37844
|
+
const interrupted = stored.stages.some((s) => s.status === "active");
|
|
37845
|
+
const stages = stored.stages.map(
|
|
37846
|
+
(s) => s.status === "active" ? {
|
|
37847
|
+
...s,
|
|
37848
|
+
status: "failed",
|
|
37849
|
+
error: "the CLI restarted while this stage was running",
|
|
37850
|
+
conversationId: void 0,
|
|
37851
|
+
awaitingUser: void 0
|
|
37852
|
+
} : s
|
|
37853
|
+
);
|
|
37854
|
+
let status2 = stored.status;
|
|
37855
|
+
let stalledReason = stored.stalledReason;
|
|
37856
|
+
if (interrupted) {
|
|
37857
|
+
status2 = "stalled";
|
|
37858
|
+
stalledReason = "The CLI restarted mid-stage. Resume re-checks whether the stage committed its work; Retry stage runs it again in a fresh conversation.";
|
|
37859
|
+
} else if (status2 === "running") {
|
|
37860
|
+
status2 = "paused";
|
|
37861
|
+
stalledReason = "The CLI restarted between stages \u2014 tap Resume to continue.";
|
|
37862
|
+
}
|
|
37863
|
+
const state = {
|
|
37864
|
+
...stored,
|
|
37865
|
+
stages,
|
|
37866
|
+
status: status2,
|
|
37867
|
+
stalledReason,
|
|
37868
|
+
pendingControl: void 0
|
|
37869
|
+
};
|
|
37870
|
+
return new _PackRunner(deps, pack, state);
|
|
37871
|
+
}
|
|
37567
37872
|
getState() {
|
|
37568
37873
|
return this.state;
|
|
37569
37874
|
}
|
|
37875
|
+
/** Persist + post the current state without changing it (rehydration announce). */
|
|
37876
|
+
async announce() {
|
|
37877
|
+
await this.publish();
|
|
37878
|
+
}
|
|
37570
37879
|
/** Persist + post the current state (ledger first — it's the truth). */
|
|
37571
37880
|
async publish() {
|
|
37572
37881
|
this.state = { ...this.state, updatedAt: nowIso() };
|
|
@@ -37578,64 +37887,101 @@ var PackRunner = class _PackRunner {
|
|
|
37578
37887
|
await this.deps.postState(this.state);
|
|
37579
37888
|
}
|
|
37580
37889
|
async settle(status2, stalledReason) {
|
|
37581
|
-
this.state = { ...this.state, status: status2, stalledReason };
|
|
37890
|
+
this.state = { ...this.state, status: status2, stalledReason, pendingControl: void 0 };
|
|
37582
37891
|
await this.publish();
|
|
37583
37892
|
}
|
|
37893
|
+
patchStage(index, patch) {
|
|
37894
|
+
const stages = this.state.stages.slice();
|
|
37895
|
+
stages[index] = { ...stages[index], ...patch };
|
|
37896
|
+
this.state = { ...this.state, stages };
|
|
37897
|
+
}
|
|
37584
37898
|
// ── Control surface (relay `pack_action`) ────────────────────────────────
|
|
37585
37899
|
async applyAction(action) {
|
|
37900
|
+
const status2 = this.state.status;
|
|
37901
|
+
const idx = this.state.currentStage;
|
|
37902
|
+
const reject = (why) => ({ state: this.state, rejected: why });
|
|
37903
|
+
if (TERMINAL_PACK_STATUSES.has(status2)) return reject(`the run already finished (${status2})`);
|
|
37586
37904
|
switch (action) {
|
|
37587
|
-
case "pause":
|
|
37905
|
+
case "pause": {
|
|
37906
|
+
if (status2 === "paused" || status2 === "stalled") return { state: this.state };
|
|
37588
37907
|
this.control = "pause";
|
|
37589
|
-
|
|
37590
|
-
|
|
37591
|
-
await this.publish();
|
|
37592
|
-
}
|
|
37908
|
+
this.state = { ...this.state, pendingControl: "pause" };
|
|
37909
|
+
await this.publish();
|
|
37593
37910
|
break;
|
|
37594
|
-
|
|
37911
|
+
}
|
|
37912
|
+
case "resume": {
|
|
37913
|
+
if (status2 !== "paused" && status2 !== "stalled")
|
|
37914
|
+
return reject(`nothing to resume \u2014 the run is ${status2}`);
|
|
37595
37915
|
this.control = "run";
|
|
37596
|
-
|
|
37597
|
-
|
|
37598
|
-
|
|
37599
|
-
|
|
37600
|
-
|
|
37916
|
+
const stage = this.state.stages[idx];
|
|
37917
|
+
this.resumeInPlace = !!stage && (stage.awaitingUser === true || stage.status === "failed");
|
|
37918
|
+
this.state = {
|
|
37919
|
+
...this.state,
|
|
37920
|
+
status: "running",
|
|
37921
|
+
stalledReason: void 0,
|
|
37922
|
+
pendingControl: void 0
|
|
37923
|
+
};
|
|
37924
|
+
await this.publish();
|
|
37925
|
+
void this.run();
|
|
37601
37926
|
break;
|
|
37927
|
+
}
|
|
37602
37928
|
case "retry_stage": {
|
|
37603
|
-
|
|
37604
|
-
|
|
37605
|
-
|
|
37606
|
-
|
|
37607
|
-
|
|
37608
|
-
|
|
37609
|
-
|
|
37610
|
-
void
|
|
37611
|
-
|
|
37929
|
+
if (status2 === "running" || this.inTurn)
|
|
37930
|
+
return reject("the stage is still running \u2014 pause or abort first");
|
|
37931
|
+
if (idx >= this.state.stages.length) return reject("no stage left to retry");
|
|
37932
|
+
const prev = this.state.stages[idx];
|
|
37933
|
+
this.patchStage(idx, {
|
|
37934
|
+
status: "pending",
|
|
37935
|
+
conversationId: void 0,
|
|
37936
|
+
handoff: void 0,
|
|
37937
|
+
awaitingUser: void 0,
|
|
37938
|
+
startCommit: void 0,
|
|
37939
|
+
attempts: prev.attempts,
|
|
37940
|
+
error: prev.error
|
|
37941
|
+
});
|
|
37942
|
+
this.resumeInPlace = false;
|
|
37943
|
+
this.control = "run";
|
|
37944
|
+
this.state = {
|
|
37945
|
+
...this.state,
|
|
37946
|
+
status: "running",
|
|
37947
|
+
stalledReason: void 0,
|
|
37948
|
+
pendingControl: void 0
|
|
37949
|
+
};
|
|
37950
|
+
await this.publish();
|
|
37951
|
+
void this.run();
|
|
37612
37952
|
break;
|
|
37613
37953
|
}
|
|
37614
37954
|
case "skip_stage": {
|
|
37615
|
-
|
|
37616
|
-
|
|
37617
|
-
|
|
37618
|
-
|
|
37619
|
-
|
|
37620
|
-
|
|
37621
|
-
|
|
37622
|
-
|
|
37623
|
-
|
|
37624
|
-
|
|
37625
|
-
|
|
37626
|
-
|
|
37627
|
-
|
|
37628
|
-
|
|
37629
|
-
|
|
37955
|
+
if (status2 === "running" || this.inTurn)
|
|
37956
|
+
return reject("the stage is still running \u2014 pause or abort first");
|
|
37957
|
+
if (idx >= this.state.stages.length) return reject("no stage left to skip");
|
|
37958
|
+
this.patchStage(idx, { status: "skipped", awaitingUser: void 0, error: void 0 });
|
|
37959
|
+
this.resumeInPlace = false;
|
|
37960
|
+
this.control = "run";
|
|
37961
|
+
this.state = {
|
|
37962
|
+
...this.state,
|
|
37963
|
+
currentStage: idx + 1,
|
|
37964
|
+
status: "running",
|
|
37965
|
+
stalledReason: void 0,
|
|
37966
|
+
pendingControl: void 0
|
|
37967
|
+
};
|
|
37968
|
+
await this.publish();
|
|
37969
|
+
void this.run();
|
|
37630
37970
|
break;
|
|
37631
37971
|
}
|
|
37632
|
-
case "abort":
|
|
37972
|
+
case "abort": {
|
|
37633
37973
|
this.control = "abort";
|
|
37634
|
-
|
|
37635
|
-
|
|
37974
|
+
if (this.looping) {
|
|
37975
|
+
this.state = { ...this.state, pendingControl: "abort" };
|
|
37976
|
+
await this.publish();
|
|
37977
|
+
await this.deps.driver.cancel().catch(() => void 0);
|
|
37978
|
+
} else {
|
|
37979
|
+
await this.settle("aborted");
|
|
37980
|
+
}
|
|
37636
37981
|
break;
|
|
37982
|
+
}
|
|
37637
37983
|
}
|
|
37638
|
-
return this.state;
|
|
37984
|
+
return { state: this.state };
|
|
37639
37985
|
}
|
|
37640
37986
|
// ── The loop ──────────────────────────────────────────────────────────────
|
|
37641
37987
|
async run() {
|
|
@@ -37660,104 +38006,225 @@ var PackRunner = class _PackRunner {
|
|
|
37660
38006
|
await this.settle("failed", err.message).catch(() => void 0);
|
|
37661
38007
|
} finally {
|
|
37662
38008
|
this.looping = false;
|
|
38009
|
+
this.inTurn = false;
|
|
37663
38010
|
}
|
|
37664
38011
|
}
|
|
37665
38012
|
/** Run one stage to its handoff. True = advanced; false = run settled. */
|
|
37666
38013
|
async runStage(index) {
|
|
37667
38014
|
const stageDef = this.pack.stages[index];
|
|
38015
|
+
const inPlace = this.resumeInPlace;
|
|
38016
|
+
this.resumeInPlace = false;
|
|
37668
38017
|
const startedMs = Date.now();
|
|
37669
|
-
const startSha = await this.deps.gates.head();
|
|
37670
|
-
let stages = this.state.stages.slice();
|
|
37671
|
-
stages[index] = { ...stages[index], status: "active" };
|
|
37672
|
-
this.state = { ...this.state, stages, status: "running" };
|
|
37673
38018
|
try {
|
|
37674
|
-
|
|
37675
|
-
|
|
37676
|
-
|
|
37677
|
-
|
|
37678
|
-
|
|
37679
|
-
|
|
37680
|
-
|
|
37681
|
-
|
|
37682
|
-
|
|
37683
|
-
|
|
37684
|
-
|
|
38019
|
+
let startSha;
|
|
38020
|
+
let turn;
|
|
38021
|
+
if (inPlace) {
|
|
38022
|
+
startSha = this.state.stages[index].startCommit ?? await this.deps.gates.head();
|
|
38023
|
+
this.patchStage(index, { status: "active", awaitingUser: void 0, error: void 0 });
|
|
38024
|
+
this.state = { ...this.state, status: "running" };
|
|
38025
|
+
await this.publish();
|
|
38026
|
+
turn = { text: "", awaitingUser: false };
|
|
38027
|
+
} else {
|
|
38028
|
+
startSha = await this.deps.gates.head();
|
|
38029
|
+
const prevStage = this.state.stages[index];
|
|
38030
|
+
const attempt = (prevStage.attempts ?? 0) + 1;
|
|
38031
|
+
const previousAttemptError = attempt > 1 ? prevStage.error : void 0;
|
|
38032
|
+
const startCommit = startSha ? await this.deps.gates.canonicalCommit(startSha) : null;
|
|
38033
|
+
this.patchStage(index, {
|
|
38034
|
+
status: "active",
|
|
38035
|
+
attempts: attempt,
|
|
38036
|
+
startCommit: startCommit ?? void 0,
|
|
38037
|
+
conversationId: void 0,
|
|
38038
|
+
handoff: void 0,
|
|
38039
|
+
awaitingUser: void 0,
|
|
38040
|
+
error: void 0
|
|
38041
|
+
});
|
|
38042
|
+
if (index === 0 && !this.state.baseCommit && startCommit) {
|
|
38043
|
+
this.state = { ...this.state, baseCommit: startCommit };
|
|
38044
|
+
}
|
|
38045
|
+
this.state = { ...this.state, status: "running" };
|
|
38046
|
+
const conversationId = await this.deps.driver.newConversation();
|
|
38047
|
+
this.patchStage(index, { conversationId });
|
|
38048
|
+
await this.publish();
|
|
38049
|
+
this.swapSkills(stageDef.skillIds);
|
|
38050
|
+
const prompt = composeStagePrompt(this.pack, index, this.state.task, {
|
|
38051
|
+
baseCommit: this.state.baseCommit,
|
|
38052
|
+
prior: this.priorOutcomes(index),
|
|
38053
|
+
attempt,
|
|
38054
|
+
previousAttemptError
|
|
38055
|
+
});
|
|
38056
|
+
const displayLine = `\u25B6 ${this.pack.name} \u2014 stage ${index + 1}/${this.pack.stages.length}: ${stageDef.name}`;
|
|
38057
|
+
turn = await this.drive(prompt, displayLine);
|
|
38058
|
+
}
|
|
38059
|
+
if (this.aborted()) {
|
|
37685
38060
|
await this.settle("aborted");
|
|
37686
38061
|
return false;
|
|
37687
38062
|
}
|
|
38063
|
+
if (turn.awaitingUser) return this.awaitUser(index);
|
|
37688
38064
|
const commitOptional = stageDef.requiresCommit === false;
|
|
38065
|
+
let text = turn.text;
|
|
37689
38066
|
let endSha = await this.deps.gates.head();
|
|
37690
|
-
if (!endSha || endSha
|
|
37691
|
-
if (commitOptional &&
|
|
37692
|
-
|
|
37693
|
-
|
|
37694
|
-
|
|
37695
|
-
|
|
37696
|
-
|
|
37697
|
-
durationMs: Date.now() - startedMs
|
|
37698
|
-
};
|
|
37699
|
-
return this.recordHandoff(index, stageDef.role, handoff2);
|
|
38067
|
+
if (!endSha || sameCommit(endSha, startSha)) {
|
|
38068
|
+
if (commitOptional && text.trim().length > 0) {
|
|
38069
|
+
return this.recordHandoff(
|
|
38070
|
+
index,
|
|
38071
|
+
stageDef,
|
|
38072
|
+
await this.noChangeHandoff(startSha, text, startedMs)
|
|
38073
|
+
);
|
|
37700
38074
|
}
|
|
37701
|
-
|
|
38075
|
+
if (!this.state.stages[index].conversationId) {
|
|
38076
|
+
return this.stall(
|
|
38077
|
+
index,
|
|
38078
|
+
"stage produced no commit \u2014 Retry stage to run it again in a fresh conversation"
|
|
38079
|
+
);
|
|
38080
|
+
}
|
|
38081
|
+
const nudged = await this.drive(NUDGE_PROMPT, "\u25B6 Waiting for the stage commit\u2026");
|
|
38082
|
+
if (this.aborted()) {
|
|
38083
|
+
await this.settle("aborted");
|
|
38084
|
+
return false;
|
|
38085
|
+
}
|
|
38086
|
+
if (nudged.awaitingUser) return this.awaitUser(index);
|
|
38087
|
+
text = nudged.text.trim().length > 0 ? nudged.text : text;
|
|
37702
38088
|
endSha = await this.deps.gates.head();
|
|
37703
|
-
if (!endSha || endSha
|
|
37704
|
-
if (commitOptional &&
|
|
37705
|
-
|
|
37706
|
-
|
|
37707
|
-
|
|
37708
|
-
|
|
37709
|
-
|
|
37710
|
-
durationMs: Date.now() - startedMs
|
|
37711
|
-
});
|
|
38089
|
+
if (!endSha || sameCommit(endSha, startSha)) {
|
|
38090
|
+
if (commitOptional && text.trim().length > 0) {
|
|
38091
|
+
return this.recordHandoff(
|
|
38092
|
+
index,
|
|
38093
|
+
stageDef,
|
|
38094
|
+
await this.noChangeHandoff(startSha, text, startedMs)
|
|
38095
|
+
);
|
|
37712
38096
|
}
|
|
37713
|
-
return this.stall(index, "stage produced no commit",
|
|
38097
|
+
return this.stall(index, "stage produced no commit", text);
|
|
37714
38098
|
}
|
|
37715
38099
|
}
|
|
37716
38100
|
const commit = await this.deps.gates.canonicalCommit(endSha);
|
|
37717
|
-
if (!commit) return this.stall(index, "stage HEAD did not resolve to a commit",
|
|
38101
|
+
if (!commit) return this.stall(index, "stage HEAD did not resolve to a commit", text);
|
|
37718
38102
|
const handoff = {
|
|
37719
38103
|
commit,
|
|
37720
|
-
summary:
|
|
38104
|
+
summary: summarizeReply(text),
|
|
37721
38105
|
diffStat: startSha ? await this.deps.gates.diffStat(startSha, endSha) : "",
|
|
37722
38106
|
checks: await this.deps.gates.runChecks() ?? void 0,
|
|
37723
38107
|
durationMs: Date.now() - startedMs
|
|
37724
38108
|
};
|
|
37725
|
-
|
|
38109
|
+
if (stageDef.producesFindings) {
|
|
38110
|
+
Object.assign(handoff, await this.captureFindings(startSha, endSha));
|
|
38111
|
+
}
|
|
38112
|
+
return this.recordHandoff(index, stageDef, handoff);
|
|
37726
38113
|
} catch (err) {
|
|
37727
|
-
if (this.
|
|
38114
|
+
if (this.aborted()) {
|
|
37728
38115
|
await this.settle("aborted");
|
|
37729
38116
|
return false;
|
|
37730
38117
|
}
|
|
37731
38118
|
return this.stall(index, err.message);
|
|
37732
38119
|
}
|
|
37733
38120
|
}
|
|
38121
|
+
/** Read through a method: `control` flips from another call while a turn is
|
|
38122
|
+
* awaited, which TS's property narrowing would otherwise hide. */
|
|
38123
|
+
aborted() {
|
|
38124
|
+
return this.control === "abort";
|
|
38125
|
+
}
|
|
38126
|
+
async drive(prompt, displayLine) {
|
|
38127
|
+
this.inTurn = true;
|
|
38128
|
+
try {
|
|
38129
|
+
return await this.deps.driver.runTurn(prompt, displayLine);
|
|
38130
|
+
} finally {
|
|
38131
|
+
this.inTurn = false;
|
|
38132
|
+
}
|
|
38133
|
+
}
|
|
38134
|
+
swapSkills(next) {
|
|
38135
|
+
const stale = this.mountedSkills.filter((id) => !next.includes(id));
|
|
38136
|
+
const fresh = next.filter((id) => !this.mountedSkills.includes(id));
|
|
38137
|
+
if (stale.length > 0) this.deps.driver.unmountSkills(stale);
|
|
38138
|
+
if (fresh.length > 0) this.deps.driver.mountSkills(fresh);
|
|
38139
|
+
this.mountedSkills = next.slice();
|
|
38140
|
+
}
|
|
38141
|
+
async noChangeHandoff(startSha, text, startedMs) {
|
|
38142
|
+
const reviewedCommit = startSha ? await this.deps.gates.canonicalCommit(startSha) : null;
|
|
38143
|
+
return {
|
|
38144
|
+
commit: reviewedCommit ?? "(no changes)",
|
|
38145
|
+
summary: summarizeReply(text),
|
|
38146
|
+
diffStat: "reviewed \u2014 no changes needed",
|
|
38147
|
+
durationMs: Date.now() - startedMs
|
|
38148
|
+
};
|
|
38149
|
+
}
|
|
38150
|
+
/**
|
|
38151
|
+
* Parse the stage's `PACK_FINDINGS_FILE` into the handoff. Only a file the
|
|
38152
|
+
* stage itself touched counts — a stale one left by an earlier run must not
|
|
38153
|
+
* masquerade as this review's verdict. Every failure path leaves an honest
|
|
38154
|
+
* `findingsNote`; none of them stalls the run.
|
|
38155
|
+
*/
|
|
38156
|
+
async captureFindings(startSha, endSha) {
|
|
38157
|
+
try {
|
|
38158
|
+
if (startSha) {
|
|
38159
|
+
const changed = await this.deps.gates.changedFiles(startSha, endSha);
|
|
38160
|
+
if (!changed.includes(PACK_FINDINGS_FILE)) {
|
|
38161
|
+
return {
|
|
38162
|
+
findingsNote: `${PACK_FINDINGS_FILE} was not written by this stage \u2014 no structured findings`
|
|
38163
|
+
};
|
|
38164
|
+
}
|
|
38165
|
+
}
|
|
38166
|
+
const raw = await this.deps.gates.readFile(PACK_FINDINGS_FILE);
|
|
38167
|
+
if (raw === null)
|
|
38168
|
+
return { findingsNote: `no ${PACK_FINDINGS_FILE} \u2014 the stage left no structured findings` };
|
|
38169
|
+
const parsed = parsePackFindings(raw);
|
|
38170
|
+
if (!parsed.ok) return { findingsNote: `${PACK_FINDINGS_FILE}: ${parsed.error}` };
|
|
38171
|
+
const notes = [];
|
|
38172
|
+
if (parsed.value.findings.length === 0 && parsed.value.checked) {
|
|
38173
|
+
notes.push(`no findings; checked: ${parsed.value.checked.join(", ")}`);
|
|
38174
|
+
}
|
|
38175
|
+
if (parsed.value.note) notes.push(parsed.value.note);
|
|
38176
|
+
return {
|
|
38177
|
+
findings: parsed.value.findings,
|
|
38178
|
+
...notes.length > 0 ? { findingsNote: notes.join(" \u2014 ") } : {}
|
|
38179
|
+
};
|
|
38180
|
+
} catch (err) {
|
|
38181
|
+
return { findingsNote: `could not read ${PACK_FINDINGS_FILE}: ${err.message}` };
|
|
38182
|
+
}
|
|
38183
|
+
}
|
|
37734
38184
|
/** Mark a stage `done` with its handoff, persist, and advance. */
|
|
37735
|
-
async recordHandoff(index,
|
|
37736
|
-
|
|
37737
|
-
|
|
37738
|
-
this.state = { ...this.state, stages, currentStage: index + 1 };
|
|
38185
|
+
async recordHandoff(index, stageDef, handoff) {
|
|
38186
|
+
this.patchStage(index, { status: "done", handoff, awaitingUser: void 0, error: void 0 });
|
|
38187
|
+
this.state = { ...this.state, currentStage: index + 1 };
|
|
37739
38188
|
try {
|
|
37740
|
-
this.deps.ledger.saveStageHandoff(this.state.runId, index, role, handoff);
|
|
38189
|
+
this.deps.ledger.saveStageHandoff(this.state.runId, index, stageDef.role, handoff);
|
|
37741
38190
|
} catch (err) {
|
|
37742
38191
|
this.deps.log(`pack handoff save failed: ${err.message}`);
|
|
37743
38192
|
}
|
|
37744
38193
|
await this.publish();
|
|
37745
38194
|
return true;
|
|
37746
38195
|
}
|
|
38196
|
+
/** The stage asked the user something: park the run, keep the conversation. */
|
|
38197
|
+
async awaitUser(index) {
|
|
38198
|
+
const name = this.state.stages[index].name;
|
|
38199
|
+
this.patchStage(index, { awaitingUser: true });
|
|
38200
|
+
this.control = "pause";
|
|
38201
|
+
await this.settle(
|
|
38202
|
+
"paused",
|
|
38203
|
+
`${name} asked you a question \u2014 answer it in the stage chat, then tap Resume.`
|
|
38204
|
+
);
|
|
38205
|
+
this.deps.log(
|
|
38206
|
+
`pack run ${this.state.runId} paused at stage ${index + 1}: awaiting the user's answer`
|
|
38207
|
+
);
|
|
38208
|
+
return false;
|
|
38209
|
+
}
|
|
37747
38210
|
async stall(index, reason, lastReply) {
|
|
37748
|
-
|
|
37749
|
-
|
|
37750
|
-
|
|
37751
|
-
|
|
38211
|
+
this.patchStage(index, { status: "failed", error: reason, awaitingUser: void 0 });
|
|
38212
|
+
await this.settle(
|
|
38213
|
+
"stalled",
|
|
38214
|
+
lastReply ? `${reason} \u2014 last reply: ${lastReply.slice(-300)}` : reason
|
|
38215
|
+
);
|
|
37752
38216
|
this.deps.log(`pack run ${this.state.runId} stalled at stage ${index + 1}: ${reason}`);
|
|
37753
38217
|
return false;
|
|
37754
38218
|
}
|
|
37755
|
-
|
|
37756
|
-
|
|
38219
|
+
priorOutcomes(index) {
|
|
38220
|
+
const out2 = [];
|
|
38221
|
+
for (let i = 0; i < index; i++) {
|
|
37757
38222
|
const s = this.state.stages[i];
|
|
37758
|
-
if (s.status === "done" && s.handoff)
|
|
38223
|
+
if (s.status === "done" && s.handoff)
|
|
38224
|
+
out2.push({ role: s.role, name: s.name, status: "done", handoff: s.handoff });
|
|
38225
|
+
else if (s.status === "skipped") out2.push({ role: s.role, name: s.name, status: "skipped" });
|
|
37759
38226
|
}
|
|
37760
|
-
return
|
|
38227
|
+
return out2;
|
|
37761
38228
|
}
|
|
37762
38229
|
};
|
|
37763
38230
|
|
|
@@ -37771,7 +38238,7 @@ function getActivePackRunner() {
|
|
|
37771
38238
|
}
|
|
37772
38239
|
|
|
37773
38240
|
// src/packs/handlers.ts
|
|
37774
|
-
var TERMINAL_STATUSES =
|
|
38241
|
+
var TERMINAL_STATUSES = TERMINAL_PACK_STATUSES;
|
|
37775
38242
|
var PACK_ACTIONS = /* @__PURE__ */ new Set(["pause", "resume", "retry_stage", "skip_stage", "abort"]);
|
|
37776
38243
|
function buildPackRunnerDeps(ctx) {
|
|
37777
38244
|
const cwd = ctx.opts.cwd;
|
|
@@ -37791,10 +38258,10 @@ function buildPackRunnerDeps(ctx) {
|
|
|
37791
38258
|
ctx.history.appendUserPrompt(displayLine);
|
|
37792
38259
|
await ctx.client.prompt(prompt);
|
|
37793
38260
|
const text = ctx.streaming.getCurrentText();
|
|
37794
|
-
await ctx.streaming.closeTurnWithInteractiveDetection();
|
|
38261
|
+
const awaitingUser = await ctx.streaming.closeTurnWithInteractiveDetection();
|
|
37795
38262
|
ctx.history.appendAgentReply(text);
|
|
37796
38263
|
await ctx.history.flush();
|
|
37797
|
-
return text;
|
|
38264
|
+
return { text, awaitingUser };
|
|
37798
38265
|
},
|
|
37799
38266
|
cancel: () => ctx.client.cancel(),
|
|
37800
38267
|
mountSkills: (skillIds) => {
|
|
@@ -37805,16 +38272,27 @@ function buildPackRunnerDeps(ctx) {
|
|
|
37805
38272
|
log.warn("packs", `skill mount failed for ${id}: ${err.message}`);
|
|
37806
38273
|
}
|
|
37807
38274
|
}
|
|
38275
|
+
},
|
|
38276
|
+
unmountSkills: (skillIds) => {
|
|
38277
|
+
for (const id of skillIds) {
|
|
38278
|
+
try {
|
|
38279
|
+
configureSkill("remove", id);
|
|
38280
|
+
} catch (err) {
|
|
38281
|
+
log.warn("packs", `skill unmount failed for ${id}: ${err.message}`);
|
|
38282
|
+
}
|
|
38283
|
+
}
|
|
37808
38284
|
}
|
|
37809
38285
|
},
|
|
37810
38286
|
gates: {
|
|
37811
38287
|
head: () => gitHead(run, cwd),
|
|
37812
38288
|
canonicalCommit: (sha) => canonicalCommit(run, cwd, sha),
|
|
37813
38289
|
diffStat: (from, to) => diffStat(run, cwd, from, to),
|
|
38290
|
+
changedFiles: (from, to) => changedFiles(run, cwd, from, to),
|
|
37814
38291
|
runChecks: async () => {
|
|
37815
38292
|
const command2 = detectChecksCommand(cwd);
|
|
37816
38293
|
return command2 ? runChecks(run, cwd, command2) : null;
|
|
37817
|
-
}
|
|
38294
|
+
},
|
|
38295
|
+
readFile: async (relPath) => readWorkspaceFile(cwd, relPath)
|
|
37818
38296
|
},
|
|
37819
38297
|
ledger: {
|
|
37820
38298
|
saveRun: (state) => saveRun(cwd, state),
|
|
@@ -37832,19 +38310,38 @@ function buildPackRunnerDeps(ctx) {
|
|
|
37832
38310
|
log: (message) => log.info("packs", message)
|
|
37833
38311
|
};
|
|
37834
38312
|
}
|
|
38313
|
+
async function ensureRunner(ctx) {
|
|
38314
|
+
const active2 = getActivePackRunner();
|
|
38315
|
+
if (active2) return active2;
|
|
38316
|
+
const stored = loadLatestRun(ctx.opts.cwd);
|
|
38317
|
+
if (!stored || TERMINAL_STATUSES.has(stored.status)) return null;
|
|
38318
|
+
const runner = PackRunner.rehydrate(buildPackRunnerDeps(ctx), stored);
|
|
38319
|
+
if (!runner) return null;
|
|
38320
|
+
setActivePackRunner(runner);
|
|
38321
|
+
log.info(
|
|
38322
|
+
"packs",
|
|
38323
|
+
`rehydrated run ${stored.runId} from the ledger (was ${stored.status}) \u2192 ${runner.getState().status}`
|
|
38324
|
+
);
|
|
38325
|
+
await runner.announce();
|
|
38326
|
+
return runner;
|
|
38327
|
+
}
|
|
37835
38328
|
var packStartH = async (ctx) => {
|
|
37836
38329
|
const payload = ctx.cmd.payload;
|
|
37837
38330
|
const packId = typeof payload?.packId === "string" ? payload.packId : "";
|
|
37838
38331
|
const task = typeof payload?.task === "string" ? payload.task.trim() : "";
|
|
37839
38332
|
if (!isPackId(packId)) {
|
|
37840
|
-
await ctx.relay.sendResult(ctx.cmd.id, "failed", {
|
|
38333
|
+
await ctx.relay.sendResult(ctx.cmd.id, "failed", {
|
|
38334
|
+
error: `unknown pack: ${packId || "(none)"}`
|
|
38335
|
+
});
|
|
37841
38336
|
return;
|
|
37842
38337
|
}
|
|
37843
38338
|
if (task.length === 0) {
|
|
37844
|
-
await ctx.relay.sendResult(ctx.cmd.id, "failed", {
|
|
38339
|
+
await ctx.relay.sendResult(ctx.cmd.id, "failed", {
|
|
38340
|
+
error: "pack_start requires a non-empty task"
|
|
38341
|
+
});
|
|
37845
38342
|
return;
|
|
37846
38343
|
}
|
|
37847
|
-
const existing =
|
|
38344
|
+
const existing = await ensureRunner(ctx);
|
|
37848
38345
|
if (existing && !TERMINAL_STATUSES.has(existing.getState().status)) {
|
|
37849
38346
|
await ctx.relay.sendResult(ctx.cmd.id, "failed", {
|
|
37850
38347
|
error: "a pack run is already active on this session \u2014 pause/abort it first",
|
|
@@ -37867,10 +38364,12 @@ var packActionH = async (ctx) => {
|
|
|
37867
38364
|
const payload = ctx.cmd.payload;
|
|
37868
38365
|
const action = typeof payload?.action === "string" ? payload.action : "";
|
|
37869
38366
|
if (!PACK_ACTIONS.has(action)) {
|
|
37870
|
-
await ctx.relay.sendResult(ctx.cmd.id, "failed", {
|
|
38367
|
+
await ctx.relay.sendResult(ctx.cmd.id, "failed", {
|
|
38368
|
+
error: `unknown pack action: ${action || "(none)"}`
|
|
38369
|
+
});
|
|
37871
38370
|
return;
|
|
37872
38371
|
}
|
|
37873
|
-
const runner =
|
|
38372
|
+
const runner = await ensureRunner(ctx);
|
|
37874
38373
|
if (!runner) {
|
|
37875
38374
|
await ctx.relay.sendResult(ctx.cmd.id, "failed", {
|
|
37876
38375
|
error: "no active pack run in this session",
|
|
@@ -37878,11 +38377,20 @@ var packActionH = async (ctx) => {
|
|
|
37878
38377
|
});
|
|
37879
38378
|
return;
|
|
37880
38379
|
}
|
|
37881
|
-
const
|
|
37882
|
-
|
|
38380
|
+
const outcome = await runner.applyAction(action);
|
|
38381
|
+
if (outcome.rejected) {
|
|
38382
|
+
log.info("packs", `pack_action ${action} rejected: ${outcome.rejected}`);
|
|
38383
|
+
await ctx.relay.sendResult(ctx.cmd.id, "failed", {
|
|
38384
|
+
error: outcome.rejected,
|
|
38385
|
+
state: outcome.state
|
|
38386
|
+
});
|
|
38387
|
+
return;
|
|
38388
|
+
}
|
|
38389
|
+
await ctx.relay.sendResult(ctx.cmd.id, "completed", { state: outcome.state });
|
|
37883
38390
|
};
|
|
37884
38391
|
var packStatusH = async (ctx) => {
|
|
37885
|
-
const
|
|
38392
|
+
const runner = await ensureRunner(ctx);
|
|
38393
|
+
const state = runner?.getState() ?? loadLatestRun(ctx.opts.cwd);
|
|
37886
38394
|
await ctx.relay.sendResult(ctx.cmd.id, "completed", { state });
|
|
37887
38395
|
};
|
|
37888
38396
|
|
|
@@ -46506,7 +47014,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
46506
47014
|
if (process.env.NODE_ENV === "test") return;
|
|
46507
47015
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
46508
47016
|
if (process.env.CI) return;
|
|
46509
|
-
const current2 = true ? "2.75.
|
|
47017
|
+
const current2 = true ? "2.75.9" : null;
|
|
46510
47018
|
if (!current2) return;
|
|
46511
47019
|
const cache = readCache();
|
|
46512
47020
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -46523,7 +47031,7 @@ function checkForUpdates() {
|
|
|
46523
47031
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
46524
47032
|
if (process.env.CI) return;
|
|
46525
47033
|
if (!process.stdout.isTTY) return;
|
|
46526
|
-
const current2 = true ? "2.75.
|
|
47034
|
+
const current2 = true ? "2.75.9" : null;
|
|
46527
47035
|
if (!current2) return;
|
|
46528
47036
|
const cache = readCache();
|
|
46529
47037
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -46546,7 +47054,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
|
46546
47054
|
var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
|
|
46547
47055
|
var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
|
|
46548
47056
|
function currentCliVersion() {
|
|
46549
|
-
return true ? "2.75.
|
|
47057
|
+
return true ? "2.75.9" : null;
|
|
46550
47058
|
}
|
|
46551
47059
|
async function installedVersion(deps) {
|
|
46552
47060
|
const ls = await deps.run(
|
|
@@ -48286,7 +48794,7 @@ function checkChokidar() {
|
|
|
48286
48794
|
}
|
|
48287
48795
|
async function doctor(args2 = []) {
|
|
48288
48796
|
const json = args2.includes("--json");
|
|
48289
|
-
const cliVersion = true ? "2.75.
|
|
48797
|
+
const cliVersion = true ? "2.75.9" : "0.0.0-dev";
|
|
48290
48798
|
const apiBase2 = resolveApiBaseUrl();
|
|
48291
48799
|
const diagnosticId = (0, import_node_crypto12.randomUUID)();
|
|
48292
48800
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -48810,7 +49318,7 @@ async function mcpWarm(argv = [], opts = {}) {
|
|
|
48810
49318
|
// src/commands/version.ts
|
|
48811
49319
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
48812
49320
|
function version2() {
|
|
48813
|
-
const v = true ? "2.75.
|
|
49321
|
+
const v = true ? "2.75.9" : "unknown";
|
|
48814
49322
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
48815
49323
|
}
|
|
48816
49324
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.75.
|
|
3
|
+
"version": "2.75.9",
|
|
4
4
|
"description": "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device — async. The terminal companion for CodeAgent Mobile.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "dist/index.js",
|