harnessed 4.5.0 โ 4.6.0
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/README.md +12 -4
- package/bin/harnessed-inject-state.mjs +11 -0
- package/dist/cli.mjs +212 -19
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/messages/en.json +2 -2
- package/messages/zh-Hans.json +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
### ๐ The operating loop
|
|
23
23
|
|
|
24
|
-
> **Discuss โ Plan โ Build โ Verify โ Ship
|
|
24
|
+
> **Discuss โ Plan โ Build โ Verify โ Ship**, closed by a **Learn** loop โ machine-executed across the three-layer stack (gstack governance ยท GSD orchestration ยท superpowers TDD ยท checkpoint evidence). Raw agent work drifts; harnessed turns it into a source-of-truth path where progress and evidence persist instead of living in chat. **Learning is automatic**: every completed workflow appends its failure/loop/reject signals to `.planning/LEARNINGS.md`, which are injected into the next cycle โ this is always-on, **not** gated on the optional Retro. Retro (`/retro`) is a separate, optional milestone summary.
|
|
25
25
|
|
|
26
26
|
```mermaid
|
|
27
27
|
flowchart LR
|
|
@@ -30,9 +30,9 @@ flowchart LR
|
|
|
30
30
|
P --> T(["โข Task<br/>TDD build + checkpoint"])
|
|
31
31
|
T --> V(["โฃ Verify<br/>independent review + evidence gate"])
|
|
32
32
|
V --> S(["โค Ship<br/>release-preflight โ tag-ready (publish via CI)"])
|
|
33
|
-
S
|
|
33
|
+
S -. "milestone summary" .-> RT(["Retro<br/>(optional)"]):::opt
|
|
34
34
|
V -. "fail / gap" .-> T
|
|
35
|
-
|
|
35
|
+
S == "๐ Learn โ learnings captured on every workflow completion โ injected next cycle" ==> D
|
|
36
36
|
classDef opt stroke-dasharray:5,opacity:0.8
|
|
37
37
|
```
|
|
38
38
|
|
|
@@ -405,10 +405,18 @@ planning-with-files /plan (cross-cutting tool) โ write artifacts to .planning/
|
|
|
405
405
|
| `harnessed gates <master>` | Evaluate which sub-workflows fire for a master stage (JSON: fire/skip/parallelism). Used by slash commands to orchestrate native spawns. |
|
|
406
406
|
| `harnessed prompt <sub>` | Output a spawn-ready prompt (role + checklist + disciplines + completion/clarification protocols) for a sub-workflow. |
|
|
407
407
|
| `harnessed checkpoint <action> <sub>` | Record sub-workflow start/complete/fail to `~/.claude/harnessed/checkpoints/`. |
|
|
408
|
+
| `harnessed next` | Deterministic next-step contract (`NEXT: auto\|manual\|done`) for the active workflow. |
|
|
409
|
+
| `harnessed reject <sub>` | Mark a sub as user-rejected (terminal, distinct from `failed`). |
|
|
410
|
+
| `harnessed compact [--tokens <n>]` | Summarize+evict resolved ledger entries (G6-safe: `fail_count>0` never evicted); auto-triggers on `checkpoint complete --tokens`. |
|
|
411
|
+
| `harnessed workflows` | List in-flight workflows (one per repo). |
|
|
412
|
+
| `harnessed learn "<lesson>"` | Append a prose learning to this repo's `.planning/LEARNINGS.md`. |
|
|
408
413
|
| `harnessed run <name>` | Run a workflow via in-process SDK spawn (CI/headless mode). Slash commands use CC-native spawn instead. |
|
|
409
414
|
| `harnessed resume` | Resume from the most recent checkpoint after a session interruption |
|
|
410
415
|
| `harnessed status` | Current phase + lock holder |
|
|
411
|
-
| `harnessed doctor` |
|
|
416
|
+
| `harnessed doctor` | 14-check health check (Node / MCP / jq / Win bash / routing / token budget / mattpocock / CodeGraph / update-available, etc.) |
|
|
417
|
+
| `harnessed update [--check\|--upstreams\|--migration-report]` | Self-update (`npm i -g harnessed@latest`); `--check` reports the latest version; `--upstreams` re-runs the base manifests; `--migration-report` is a read-only stale-state inventory |
|
|
418
|
+
| `harnessed release-preflight` | Read-only release-readiness gate (CHANGELOG `[Unreleased]` / version / git-clean / tag-absent); exits 1 if not ready. The Ship-stage gate. |
|
|
419
|
+
| `harnessed retro --done` | Reset the retro-reminder phase counter after running `/retro` (clears the per-turn RETRO-DUE nudge). |
|
|
412
420
|
| `harnessed install <name>` | Install an upstream manifest |
|
|
413
421
|
| `harnessed uninstall [name]` | Reverse uninstall |
|
|
414
422
|
| `harnessed backup` | Snapshot backup management |
|
|
@@ -65,6 +65,17 @@ function workflowStateBlock(wf) {
|
|
|
65
65
|
`BREAK-LOOP: sub '${e.sub}' failed ${e.fail_count}x โ stop retrying, run break-loop skill`,
|
|
66
66
|
)
|
|
67
67
|
}
|
|
68
|
+
// Phase 22 โ smart-reminder lines from envelope flags (parity with injectState.ts).
|
|
69
|
+
if (wf.ship_ready) {
|
|
70
|
+
lines.push(
|
|
71
|
+
`SHIP-READY: ${wf.ship_commits ?? 0} commit(s) since the last release tag โ consider shipping (harnessed release-preflight, then /ship)`,
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
if (wf.retro_due) {
|
|
75
|
+
lines.push(
|
|
76
|
+
'RETRO-DUE: enough phases completed since the last retro โ run /retro, then `harnessed retro --done`',
|
|
77
|
+
)
|
|
78
|
+
}
|
|
68
79
|
lines.push('</workflow-state>')
|
|
69
80
|
return lines.join('\n')
|
|
70
81
|
}
|
package/dist/cli.mjs
CHANGED
|
@@ -38,7 +38,7 @@ var init_package = __esm({
|
|
|
38
38
|
"package.json"() {
|
|
39
39
|
package_default = {
|
|
40
40
|
name: "harnessed",
|
|
41
|
-
version: "4.
|
|
41
|
+
version: "4.6.0",
|
|
42
42
|
description: "AI coding harness package manager + composition orchestrator",
|
|
43
43
|
type: "module",
|
|
44
44
|
license: "Apache-2.0",
|
|
@@ -944,6 +944,10 @@ async function backup(plan, ctx) {
|
|
|
944
944
|
});
|
|
945
945
|
continue;
|
|
946
946
|
}
|
|
947
|
+
if (code === "EISDIR") {
|
|
948
|
+
entries.push({ target: file.target, backup: "", sha1: "", eol: "lf" });
|
|
949
|
+
continue;
|
|
950
|
+
}
|
|
947
951
|
return {
|
|
948
952
|
ok: false,
|
|
949
953
|
error: {
|
|
@@ -1287,7 +1291,19 @@ var init_currentWorkflow_v1 = __esm({
|
|
|
1287
1291
|
},
|
|
1288
1292
|
{ additionalProperties: false }
|
|
1289
1293
|
)
|
|
1290
|
-
)
|
|
1294
|
+
),
|
|
1295
|
+
// Phase 22 โ smart reminders. Additive-optional flags set by `checkpoint
|
|
1296
|
+
// complete` (allResolved) and read by the G4 inject twins (booleans only; the
|
|
1297
|
+
// bin computes neither git nor thresholds). Old files without them Value.Check-pass
|
|
1298
|
+
// โ NO schemaVersion bump.
|
|
1299
|
+
// ship_ready โ there are unshipped commits since the last vX.Y.Z tag (git-derived,
|
|
1300
|
+
// recomputed each completion; self-heals to false after a release).
|
|
1301
|
+
// ship_commits โ commit count since that tag (feeds the SHIP-READY hint text).
|
|
1302
|
+
// retro_due โ phases_since_retro (store sidecar) has crossed the threshold;
|
|
1303
|
+
// cleared by `harnessed retro --done`.
|
|
1304
|
+
ship_ready: Type.Optional(Type.Boolean()),
|
|
1305
|
+
ship_commits: Type.Optional(Type.Integer({ minimum: 0 })),
|
|
1306
|
+
retro_due: Type.Optional(Type.Boolean())
|
|
1291
1307
|
},
|
|
1292
1308
|
{ additionalProperties: false }
|
|
1293
1309
|
);
|
|
@@ -1305,6 +1321,7 @@ var init_schema2 = __esm({
|
|
|
1305
1321
|
// src/checkpoint/workflowStore.ts
|
|
1306
1322
|
var workflowStore_exports = {};
|
|
1307
1323
|
__export(workflowStore_exports, {
|
|
1324
|
+
RetroMetaEntry: () => RetroMetaEntry,
|
|
1308
1325
|
WorkflowStoreV1: () => WorkflowStoreV1,
|
|
1309
1326
|
listWorkflows: () => listWorkflows,
|
|
1310
1327
|
readStoreRaw: () => readStoreRaw,
|
|
@@ -1365,17 +1382,26 @@ async function listWorkflows() {
|
|
|
1365
1382
|
status: wf.status
|
|
1366
1383
|
}));
|
|
1367
1384
|
}
|
|
1368
|
-
var WorkflowStoreV1;
|
|
1385
|
+
var RetroMetaEntry, WorkflowStoreV1;
|
|
1369
1386
|
var init_workflowStore = __esm({
|
|
1370
1387
|
"src/checkpoint/workflowStore.ts"() {
|
|
1371
1388
|
init_harnessedRoot();
|
|
1372
1389
|
init_schemaVersion();
|
|
1373
1390
|
init_atomicWrite();
|
|
1374
1391
|
init_currentWorkflow_v1();
|
|
1392
|
+
RetroMetaEntry = Type.Object(
|
|
1393
|
+
{
|
|
1394
|
+
phases_since_retro: Type.Integer({ minimum: 0 }),
|
|
1395
|
+
last_retro_at: Type.Optional(Type.String({ minLength: 1 }))
|
|
1396
|
+
},
|
|
1397
|
+
{ additionalProperties: false }
|
|
1398
|
+
);
|
|
1375
1399
|
WorkflowStoreV1 = Type.Object(
|
|
1376
1400
|
{
|
|
1377
1401
|
schemaVersion: Type.Literal(SCHEMA_VERSIONS.workflowStore),
|
|
1378
|
-
workflows: Type.Record(Type.String(), CurrentWorkflowV1)
|
|
1402
|
+
workflows: Type.Record(Type.String(), CurrentWorkflowV1),
|
|
1403
|
+
// Additive-optional (no schemaVersion bump โ old stores Value.Check-pass).
|
|
1404
|
+
retro_meta: Type.Optional(Type.Record(Type.String(), RetroMetaEntry))
|
|
1379
1405
|
},
|
|
1380
1406
|
{ additionalProperties: false }
|
|
1381
1407
|
);
|
|
@@ -1389,6 +1415,7 @@ __export(state_exports, {
|
|
|
1389
1415
|
WorkflowStateError: () => WorkflowStateError,
|
|
1390
1416
|
activate: () => activate,
|
|
1391
1417
|
complete: () => complete,
|
|
1418
|
+
mutateStore: () => mutateStore,
|
|
1392
1419
|
mutateSubProgress: () => mutateSubProgress,
|
|
1393
1420
|
pause: () => pause,
|
|
1394
1421
|
readCurrentWorkflow: () => readCurrentWorkflow,
|
|
@@ -1448,6 +1475,12 @@ async function mutateSubProgress(fn) {
|
|
|
1448
1475
|
await writeCurrentWorkflowUnlocked({ ...s, sub_progress: next });
|
|
1449
1476
|
});
|
|
1450
1477
|
}
|
|
1478
|
+
async function mutateStore(fn) {
|
|
1479
|
+
await withLock(async () => {
|
|
1480
|
+
const store = await readStoreRaw();
|
|
1481
|
+
await writeStoreRaw(fn(store));
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1451
1484
|
async function activate(phase, checkpointPath = null) {
|
|
1452
1485
|
await writeCurrentWorkflow({
|
|
1453
1486
|
schemaVersion: SCHEMA_VERSIONS.currentWorkflow,
|
|
@@ -3797,6 +3830,91 @@ var init_compact = __esm({
|
|
|
3797
3830
|
}
|
|
3798
3831
|
});
|
|
3799
3832
|
|
|
3833
|
+
// src/checkpoint/shipReady.ts
|
|
3834
|
+
var shipReady_exports = {};
|
|
3835
|
+
__export(shipReady_exports, {
|
|
3836
|
+
collectShipReady: () => collectShipReady,
|
|
3837
|
+
defaultShipReady: () => defaultShipReady,
|
|
3838
|
+
pickLatestReleaseTag: () => pickLatestReleaseTag
|
|
3839
|
+
});
|
|
3840
|
+
function pickLatestReleaseTag(tags) {
|
|
3841
|
+
let best = null;
|
|
3842
|
+
for (const tag of tags) {
|
|
3843
|
+
const m = RELEASE_TAG.exec(tag.trim());
|
|
3844
|
+
if (!m) continue;
|
|
3845
|
+
const n = [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
3846
|
+
if (!best || n[0] > best.n[0] || n[0] === best.n[0] && n[1] > best.n[1] || n[0] === best.n[0] && n[1] === best.n[1] && n[2] > best.n[2]) {
|
|
3847
|
+
best = { tag: tag.trim(), n };
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
return best?.tag ?? null;
|
|
3851
|
+
}
|
|
3852
|
+
function collectShipReady(tags, countSince) {
|
|
3853
|
+
const baseTag = pickLatestReleaseTag(tags);
|
|
3854
|
+
const commits = countSince(baseTag);
|
|
3855
|
+
return { ready: commits > 0, commits, baseTag };
|
|
3856
|
+
}
|
|
3857
|
+
function defaultShipReady(cwd) {
|
|
3858
|
+
let tags = [];
|
|
3859
|
+
try {
|
|
3860
|
+
tags = execFileSync("git", ["tag", "-l"], { cwd, encoding: "utf8" }).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
3861
|
+
} catch {
|
|
3862
|
+
return { ready: false, commits: 0, baseTag: null };
|
|
3863
|
+
}
|
|
3864
|
+
const countSince = (tag) => {
|
|
3865
|
+
try {
|
|
3866
|
+
const range = tag ? `${tag}..HEAD` : "HEAD";
|
|
3867
|
+
const out = execFileSync("git", ["rev-list", range, "--count"], {
|
|
3868
|
+
cwd,
|
|
3869
|
+
encoding: "utf8"
|
|
3870
|
+
}).trim();
|
|
3871
|
+
const n = Number(out);
|
|
3872
|
+
return Number.isFinite(n) ? n : 0;
|
|
3873
|
+
} catch {
|
|
3874
|
+
return 0;
|
|
3875
|
+
}
|
|
3876
|
+
};
|
|
3877
|
+
return collectShipReady(tags, countSince);
|
|
3878
|
+
}
|
|
3879
|
+
var RELEASE_TAG;
|
|
3880
|
+
var init_shipReady = __esm({
|
|
3881
|
+
"src/checkpoint/shipReady.ts"() {
|
|
3882
|
+
RELEASE_TAG = /^v(\d+)\.(\d+)\.(\d+)$/;
|
|
3883
|
+
}
|
|
3884
|
+
});
|
|
3885
|
+
|
|
3886
|
+
// src/checkpoint/retroMeta.ts
|
|
3887
|
+
var retroMeta_exports = {};
|
|
3888
|
+
__export(retroMeta_exports, {
|
|
3889
|
+
DEFAULT_RETRO_THRESHOLD: () => DEFAULT_RETRO_THRESHOLD,
|
|
3890
|
+
incrementPhases: () => incrementPhases,
|
|
3891
|
+
isRetroDue: () => isRetroDue,
|
|
3892
|
+
resetRetro: () => resetRetro,
|
|
3893
|
+
retroThreshold: () => retroThreshold
|
|
3894
|
+
});
|
|
3895
|
+
function retroThreshold(env) {
|
|
3896
|
+
const raw = env.HARNESSED_RETRO_PHASE_THRESHOLD;
|
|
3897
|
+
if (raw === void 0) return DEFAULT_RETRO_THRESHOLD;
|
|
3898
|
+
const n = Number(raw);
|
|
3899
|
+
return Number.isInteger(n) && n > 0 ? n : DEFAULT_RETRO_THRESHOLD;
|
|
3900
|
+
}
|
|
3901
|
+
function incrementPhases(entry) {
|
|
3902
|
+
if (!entry) return { phases_since_retro: 1 };
|
|
3903
|
+
return entry.last_retro_at ? { phases_since_retro: entry.phases_since_retro + 1, last_retro_at: entry.last_retro_at } : { phases_since_retro: entry.phases_since_retro + 1 };
|
|
3904
|
+
}
|
|
3905
|
+
function isRetroDue(count, threshold) {
|
|
3906
|
+
return count >= threshold;
|
|
3907
|
+
}
|
|
3908
|
+
function resetRetro(now) {
|
|
3909
|
+
return { phases_since_retro: 0, last_retro_at: now };
|
|
3910
|
+
}
|
|
3911
|
+
var DEFAULT_RETRO_THRESHOLD;
|
|
3912
|
+
var init_retroMeta = __esm({
|
|
3913
|
+
"src/checkpoint/retroMeta.ts"() {
|
|
3914
|
+
DEFAULT_RETRO_THRESHOLD = 5;
|
|
3915
|
+
}
|
|
3916
|
+
});
|
|
3917
|
+
|
|
3800
3918
|
// src/checkpoint/breakLoop.ts
|
|
3801
3919
|
var breakLoop_exports = {};
|
|
3802
3920
|
__export(breakLoop_exports, {
|
|
@@ -4816,7 +4934,7 @@ function expandTildeForWindows(cmd) {
|
|
|
4816
4934
|
const home = homedir().replace(/\\/g, "/");
|
|
4817
4935
|
return cmd.replace(/(^|[\s"'`(])~\//g, `$1${home}/`);
|
|
4818
4936
|
}
|
|
4819
|
-
async function spawnCmd(ctx, cmd, args, timeoutMs) {
|
|
4937
|
+
async function spawnCmd(ctx, cmd, args, timeoutMs, opts) {
|
|
4820
4938
|
const violation = checkCmdString(cmd);
|
|
4821
4939
|
if (violation) {
|
|
4822
4940
|
return {
|
|
@@ -4837,14 +4955,21 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
|
|
|
4837
4955
|
const env = { ...process.env, ...installCfg.env ?? {} };
|
|
4838
4956
|
const cwd = installCfg.cwd ?? ctx.cwd;
|
|
4839
4957
|
let child;
|
|
4958
|
+
let triedBash = false;
|
|
4840
4959
|
if (process.platform === "win32") {
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
cwd,
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4960
|
+
if (opts?.posixShell) {
|
|
4961
|
+
const joined = args.length > 0 ? `${cmd} ${args.join(" ")}` : cmd;
|
|
4962
|
+
triedBash = true;
|
|
4963
|
+
child = spawn("bash", ["-c", joined], { cwd, env, windowsHide: true });
|
|
4964
|
+
} else {
|
|
4965
|
+
const expandedCmd = expandTildeForWindows(cmd);
|
|
4966
|
+
const expandedArgs = args.map(expandTildeForWindows);
|
|
4967
|
+
child = spawn("cmd.exe", ["/c", expandedCmd, ...expandedArgs], {
|
|
4968
|
+
cwd,
|
|
4969
|
+
env,
|
|
4970
|
+
windowsHide: true
|
|
4971
|
+
});
|
|
4972
|
+
}
|
|
4848
4973
|
} else {
|
|
4849
4974
|
const joined = args.length > 0 ? `${cmd} ${args.join(" ")}` : cmd;
|
|
4850
4975
|
child = spawn("/bin/sh", ["-c", joined], { cwd, env });
|
|
@@ -4875,16 +5000,17 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
|
|
|
4875
5000
|
}, effectiveTimeoutMs);
|
|
4876
5001
|
child.on("error", (err2) => {
|
|
4877
5002
|
clearTimeout(timer);
|
|
5003
|
+
const bashMissing = triedBash && err2.code === "ENOENT";
|
|
4878
5004
|
resolve20({
|
|
4879
5005
|
ok: false,
|
|
4880
5006
|
phase: "spawn",
|
|
4881
5007
|
error: {
|
|
4882
5008
|
file: ctx.manifest.metadata.name,
|
|
4883
5009
|
path: "/spec/install/cmd",
|
|
4884
|
-
message: `spawn failed: ${err2.message}`,
|
|
5010
|
+
message: bashMissing ? "Git Bash is required for this component on Windows, but `bash` was not found on PATH. Install Git for Windows (https://git-scm.com/download/win) and re-run `harnessed setup`." : `spawn failed: ${err2.message}`,
|
|
4885
5011
|
line: null,
|
|
4886
5012
|
column: null,
|
|
4887
|
-
keyword: "spawn-error"
|
|
5013
|
+
keyword: bashMissing ? "bash-missing" : "spawn-error"
|
|
4888
5014
|
}
|
|
4889
5015
|
});
|
|
4890
5016
|
});
|
|
@@ -4899,7 +5025,7 @@ var init_spawn = __esm({
|
|
|
4899
5025
|
"src/installers/lib/spawn.ts"() {
|
|
4900
5026
|
init_security();
|
|
4901
5027
|
DEFAULT_VERIFY_TIMEOUT_MS = 15e3;
|
|
4902
|
-
DEFAULT_INSTALL_TIMEOUT_MS =
|
|
5028
|
+
DEFAULT_INSTALL_TIMEOUT_MS = 12e4;
|
|
4903
5029
|
}
|
|
4904
5030
|
});
|
|
4905
5031
|
function extractSkillName(cmd, fallback) {
|
|
@@ -5335,7 +5461,7 @@ var init_gitCloneWithSetup2 = __esm({
|
|
|
5335
5461
|
if (ctx.opts.dryRun) return { aborted: true, reason: "user-cancel" };
|
|
5336
5462
|
const bk = await backup(plan, ctx);
|
|
5337
5463
|
if (!bk.ok) return { ok: false, phase: "preflight", error: bk.error };
|
|
5338
|
-
const sp = await spawnCmd(ctx, install.cmd, [], DEFAULT_INSTALL_TIMEOUT_MS);
|
|
5464
|
+
const sp = await spawnCmd(ctx, install.cmd, [], DEFAULT_INSTALL_TIMEOUT_MS, { posixShell: true });
|
|
5339
5465
|
if (!("exitCode" in sp)) return { ...sp, backupId: bk.backupId };
|
|
5340
5466
|
if (sp.exitCode !== 0) {
|
|
5341
5467
|
return {
|
|
@@ -5378,7 +5504,9 @@ var init_gitCloneWithSetup2 = __esm({
|
|
|
5378
5504
|
};
|
|
5379
5505
|
}
|
|
5380
5506
|
const verifyTimeoutMs = ctx.manifest.spec.verify.timeout_ms ?? DEFAULT_VERIFY_TIMEOUT_MS;
|
|
5381
|
-
const vr = await spawnCmd(ctx, ctx.manifest.spec.verify.cmd, [], verifyTimeoutMs
|
|
5507
|
+
const vr = await spawnCmd(ctx, ctx.manifest.spec.verify.cmd, [], verifyTimeoutMs, {
|
|
5508
|
+
posixShell: true
|
|
5509
|
+
});
|
|
5382
5510
|
if (!("exitCode" in vr)) return { ...vr, backupId: bk.backupId };
|
|
5383
5511
|
const expected = ctx.manifest.spec.verify.expected_exit_code ?? 0;
|
|
5384
5512
|
if (vr.exitCode !== expected) {
|
|
@@ -5809,7 +5937,9 @@ var init_npmCli2 = __esm({
|
|
|
5809
5937
|
};
|
|
5810
5938
|
}
|
|
5811
5939
|
const verifyTimeoutMs = ctx.manifest.spec.verify.timeout_ms ?? DEFAULT_VERIFY_TIMEOUT_MS;
|
|
5812
|
-
const vr = await spawnCmd(ctx, ctx.manifest.spec.verify.cmd, [], verifyTimeoutMs
|
|
5940
|
+
const vr = await spawnCmd(ctx, ctx.manifest.spec.verify.cmd, [], verifyTimeoutMs, {
|
|
5941
|
+
posixShell: true
|
|
5942
|
+
});
|
|
5813
5943
|
if (!("exitCode" in vr)) return { ...vr, backupId: bk.backupId };
|
|
5814
5944
|
const expected = ctx.manifest.spec.verify.expected_exit_code ?? 0;
|
|
5815
5945
|
if (vr.exitCode !== expected) {
|
|
@@ -5938,7 +6068,9 @@ var init_npxSkillInstaller2 = __esm({
|
|
|
5938
6068
|
};
|
|
5939
6069
|
}
|
|
5940
6070
|
const verifyTimeoutMs = ctx.manifest.spec.verify.timeout_ms ?? DEFAULT_VERIFY_TIMEOUT_MS;
|
|
5941
|
-
const vr = await spawnCmd(ctx, ctx.manifest.spec.verify.cmd, [], verifyTimeoutMs
|
|
6071
|
+
const vr = await spawnCmd(ctx, ctx.manifest.spec.verify.cmd, [], verifyTimeoutMs, {
|
|
6072
|
+
posixShell: true
|
|
6073
|
+
});
|
|
5942
6074
|
if (!("exitCode" in vr)) return { ...vr, backupId: bk.backupId };
|
|
5943
6075
|
const expected = ctx.manifest.spec.verify.expected_exit_code ?? 0;
|
|
5944
6076
|
if (vr.exitCode !== expected) {
|
|
@@ -6981,6 +7113,35 @@ function registerCheckpoint(program2) {
|
|
|
6981
7113
|
);
|
|
6982
7114
|
}
|
|
6983
7115
|
}
|
|
7116
|
+
if (allResolved) {
|
|
7117
|
+
try {
|
|
7118
|
+
const { defaultShipReady: defaultShipReady2 } = await Promise.resolve().then(() => (init_shipReady(), shipReady_exports));
|
|
7119
|
+
const { incrementPhases: incrementPhases2, isRetroDue: isRetroDue2, retroThreshold: retroThreshold2 } = await Promise.resolve().then(() => (init_retroMeta(), retroMeta_exports));
|
|
7120
|
+
const { mutateStore: mutateStore2, readCurrentWorkflow: readCurrentWorkflow4, writeCurrentWorkflow: writeCurrentWorkflow3 } = await Promise.resolve().then(() => (init_state(), state_exports));
|
|
7121
|
+
const { repoKey: repoKey2 } = await Promise.resolve().then(() => (init_workflowStore(), workflowStore_exports));
|
|
7122
|
+
const ship = defaultShipReady2(process.cwd());
|
|
7123
|
+
const key = repoKey2();
|
|
7124
|
+
const threshold = retroThreshold2(process.env);
|
|
7125
|
+
let retroDue = false;
|
|
7126
|
+
await mutateStore2((store) => {
|
|
7127
|
+
const meta = { ...store.retro_meta ?? {} };
|
|
7128
|
+
const next = incrementPhases2(meta[key]);
|
|
7129
|
+
meta[key] = next;
|
|
7130
|
+
retroDue = isRetroDue2(next.phases_since_retro, threshold);
|
|
7131
|
+
return { ...store, retro_meta: meta };
|
|
7132
|
+
});
|
|
7133
|
+
const latest2 = await readCurrentWorkflow4();
|
|
7134
|
+
if (latest2) {
|
|
7135
|
+
await writeCurrentWorkflow3({
|
|
7136
|
+
...latest2,
|
|
7137
|
+
ship_ready: ship.ready,
|
|
7138
|
+
ship_commits: ship.commits,
|
|
7139
|
+
retro_due: retroDue
|
|
7140
|
+
});
|
|
7141
|
+
}
|
|
7142
|
+
} catch {
|
|
7143
|
+
}
|
|
7144
|
+
}
|
|
6984
7145
|
console.log(`[harnessed] checkpoint complete: ${sub} (evidence: ${evidenceStatus})`);
|
|
6985
7146
|
process.exit(0);
|
|
6986
7147
|
return;
|
|
@@ -7725,6 +7886,37 @@ function registerResume(program2) {
|
|
|
7725
7886
|
process.exit(0);
|
|
7726
7887
|
});
|
|
7727
7888
|
}
|
|
7889
|
+
|
|
7890
|
+
// src/cli/retro.ts
|
|
7891
|
+
init_retroMeta();
|
|
7892
|
+
async function runRetroDone(now) {
|
|
7893
|
+
const { mutateStore: mutateStore2, readCurrentWorkflow: readCurrentWorkflow2, writeCurrentWorkflow: writeCurrentWorkflow2 } = await Promise.resolve().then(() => (init_state(), state_exports));
|
|
7894
|
+
const { repoKey: repoKey2 } = await Promise.resolve().then(() => (init_workflowStore(), workflowStore_exports));
|
|
7895
|
+
const key = repoKey2();
|
|
7896
|
+
await mutateStore2((store) => {
|
|
7897
|
+
const meta = { ...store.retro_meta ?? {} };
|
|
7898
|
+
meta[key] = resetRetro(now);
|
|
7899
|
+
return { ...store, retro_meta: meta };
|
|
7900
|
+
});
|
|
7901
|
+
const wf = await readCurrentWorkflow2();
|
|
7902
|
+
if (wf?.retro_due) await writeCurrentWorkflow2({ ...wf, retro_due: false });
|
|
7903
|
+
return { reset: true };
|
|
7904
|
+
}
|
|
7905
|
+
function registerRetro(program2) {
|
|
7906
|
+
program2.command("retro").description("Retro cadence: --done resets the phase counter after running /retro").option(
|
|
7907
|
+
"--done",
|
|
7908
|
+
"mark a retro as done \u2014 reset the phase counter + clear the RETRO-DUE reminder"
|
|
7909
|
+
).action(async (opts) => {
|
|
7910
|
+
if (!opts.done) {
|
|
7911
|
+
console.error("[harnessed] retro: nothing to do \u2014 pass --done after running /retro");
|
|
7912
|
+
process.exit(1);
|
|
7913
|
+
return;
|
|
7914
|
+
}
|
|
7915
|
+
await runRetroDone((/* @__PURE__ */ new Date()).toISOString());
|
|
7916
|
+
console.log("[harnessed] retro recorded \u2014 phase counter reset, RETRO-DUE reminder cleared");
|
|
7917
|
+
process.exit(0);
|
|
7918
|
+
});
|
|
7919
|
+
}
|
|
7728
7920
|
init_backup();
|
|
7729
7921
|
function normalizeEol(buf, eol) {
|
|
7730
7922
|
const lf = buf.toString("utf8").replace(/\r\n/g, "\n");
|
|
@@ -9220,6 +9412,7 @@ registerWorkflows(program);
|
|
|
9220
9412
|
registerLearn(program);
|
|
9221
9413
|
registerUpdate(program);
|
|
9222
9414
|
registerReleasePreflight(program);
|
|
9415
|
+
registerRetro(program);
|
|
9223
9416
|
program.parse(process.argv);
|
|
9224
9417
|
//# sourceMappingURL=cli.mjs.map
|
|
9225
9418
|
//# sourceMappingURL=cli.mjs.map
|