@cleocode/cleo-os 2026.5.130 → 2026.5.132

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.
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Claude Code Stop-hook — goal-continuation loop adapter (E4-GOAL-LOOP).
3
+ *
4
+ * ## What this does
5
+ *
6
+ * Claude Code fires the `Stop` event every time Claude finishes a response
7
+ * turn. When a CLEO goal is active, this hook advances the goal one turn
8
+ * (via `cleo goal advance <goalId>`) and — if the goal is not yet terminal —
9
+ * emits a `{ decision: "block", reason: "<continuation>" }` JSON response that
10
+ * Claude Code interprets as "do NOT stop; inject this message instead". This
11
+ * re-nudges Claude to keep working toward the goal, closing the
12
+ * self-renudging loop (AC2).
13
+ *
14
+ * ## Decision protocol (Claude Code Stop-hook contract)
15
+ *
16
+ * - Output `{ "decision": "block", "reason": "<user message>" }` → Claude Code
17
+ * does not stop; it injects `reason` as a fresh user message and continues.
18
+ * - Output nothing (or anything not parseable as `{ decision: "block" }`) →
19
+ * Claude Code stops normally.
20
+ *
21
+ * ## Safety
22
+ *
23
+ * - Best-effort: any error exits 0 (Claude Code stops normally — no crash loop).
24
+ * - Terminal goals (satisfied/abandoned/impossible) emit nothing → stop.
25
+ * - Missing/no active goal → stop normally.
26
+ *
27
+ * ## Installation
28
+ *
29
+ * This script is registered as a Claude Code Stop hook via
30
+ * `ClaudeCodeHookProvider.registerGoalLoopHook()` or by running
31
+ * `cleo goal arm` (AC3). The hook entry in `~/.claude/settings.json`:
32
+ *
33
+ * ```json
34
+ * {
35
+ * "Stop": [{
36
+ * "matcher": "",
37
+ * "hooks": [{
38
+ * "type": "command",
39
+ * "command": "node /path/to/goal-stop-hook.js # cleo-goal-loop"
40
+ * }]
41
+ * }]
42
+ * }
43
+ * ```
44
+ *
45
+ * @module @cleocode/cleo-os/harnesses/goal-stop-hook
46
+ *
47
+ * @task T11496 E4-GOAL-LOOP
48
+ * @epic T11492 SG-AUTOPILOT
49
+ * @saga T11283 SG-COGNITIVE-SUBSTRATE
50
+ * @adr ADR-051
51
+ */
52
+ export {};
53
+ //# sourceMappingURL=goal-stop-hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-stop-hook.d.ts","sourceRoot":"","sources":["../../src/harnesses/goal-stop-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Claude Code Stop-hook — goal-continuation loop adapter (E4-GOAL-LOOP).
3
+ *
4
+ * ## What this does
5
+ *
6
+ * Claude Code fires the `Stop` event every time Claude finishes a response
7
+ * turn. When a CLEO goal is active, this hook advances the goal one turn
8
+ * (via `cleo goal advance <goalId>`) and — if the goal is not yet terminal —
9
+ * emits a `{ decision: "block", reason: "<continuation>" }` JSON response that
10
+ * Claude Code interprets as "do NOT stop; inject this message instead". This
11
+ * re-nudges Claude to keep working toward the goal, closing the
12
+ * self-renudging loop (AC2).
13
+ *
14
+ * ## Decision protocol (Claude Code Stop-hook contract)
15
+ *
16
+ * - Output `{ "decision": "block", "reason": "<user message>" }` → Claude Code
17
+ * does not stop; it injects `reason` as a fresh user message and continues.
18
+ * - Output nothing (or anything not parseable as `{ decision: "block" }`) →
19
+ * Claude Code stops normally.
20
+ *
21
+ * ## Safety
22
+ *
23
+ * - Best-effort: any error exits 0 (Claude Code stops normally — no crash loop).
24
+ * - Terminal goals (satisfied/abandoned/impossible) emit nothing → stop.
25
+ * - Missing/no active goal → stop normally.
26
+ *
27
+ * ## Installation
28
+ *
29
+ * This script is registered as a Claude Code Stop hook via
30
+ * `ClaudeCodeHookProvider.registerGoalLoopHook()` or by running
31
+ * `cleo goal arm` (AC3). The hook entry in `~/.claude/settings.json`:
32
+ *
33
+ * ```json
34
+ * {
35
+ * "Stop": [{
36
+ * "matcher": "",
37
+ * "hooks": [{
38
+ * "type": "command",
39
+ * "command": "node /path/to/goal-stop-hook.js # cleo-goal-loop"
40
+ * }]
41
+ * }]
42
+ * }
43
+ * ```
44
+ *
45
+ * @module @cleocode/cleo-os/harnesses/goal-stop-hook
46
+ *
47
+ * @task T11496 E4-GOAL-LOOP
48
+ * @epic T11492 SG-AUTOPILOT
49
+ * @saga T11283 SG-COGNITIVE-SUBSTRATE
50
+ * @adr ADR-051
51
+ */
52
+ import { execFile } from 'node:child_process';
53
+ import { promisify } from 'node:util';
54
+ const execFileAsync = promisify(execFile);
55
+ // ---------------------------------------------------------------------------
56
+ // Main
57
+ // ---------------------------------------------------------------------------
58
+ /**
59
+ * Run the goal-loop Stop-hook logic.
60
+ *
61
+ * 1. Query `cleo goal status` to find the active goal.
62
+ * 2. If no active goal → stop normally (no output).
63
+ * 3. Call `cleo goal advance <goalId>` to advance one turn.
64
+ * 4. If the advance result carries a continuation (goal still active) →
65
+ * emit a `{ decision: "block", reason: content }` JSON block to stdout.
66
+ * 5. If terminal (satisfied/abandoned/impossible) → stop normally.
67
+ */
68
+ async function main() {
69
+ // 1. Resolve the active goal (per-agent scoped).
70
+ let statusEnvelope;
71
+ try {
72
+ const { stdout } = await execFileAsync('cleo', ['goal', 'status'], {
73
+ timeout: 10_000,
74
+ env: process.env,
75
+ });
76
+ statusEnvelope = JSON.parse(stdout.trim());
77
+ }
78
+ catch {
79
+ // cleo unavailable or no project — stop normally.
80
+ return;
81
+ }
82
+ const goalData = statusEnvelope?.data;
83
+ // `active: null` means no active goal; an absent or null `id` similarly.
84
+ if (!goalData || 'active' in goalData || !goalData.id) {
85
+ // No active goal — stop normally.
86
+ return;
87
+ }
88
+ const goalId = goalData.id;
89
+ // 2. Advance one turn.
90
+ let advanceEnvelope;
91
+ try {
92
+ const { stdout } = await execFileAsync('cleo', ['goal', 'advance', goalId], {
93
+ timeout: 30_000,
94
+ env: process.env,
95
+ });
96
+ advanceEnvelope = JSON.parse(stdout.trim());
97
+ }
98
+ catch {
99
+ // Advance failed — stop normally rather than crashing.
100
+ return;
101
+ }
102
+ if (!advanceEnvelope?.success) {
103
+ // Advance returned an error envelope — stop normally.
104
+ return;
105
+ }
106
+ const continuation = advanceEnvelope?.data?.continuation;
107
+ // 3. If there is a continuation nudge, emit the block decision.
108
+ if (continuation && typeof continuation.content === 'string') {
109
+ const decision = {
110
+ decision: 'block',
111
+ reason: continuation.content,
112
+ };
113
+ process.stdout.write(JSON.stringify(decision) + '\n'); // stdout-discipline-allowed: Stop-hook contract — Claude Code reads decision JSON from stdout to determine whether to continue // stdout-write-allowed: Stop-hook protocol output (not render layer — Claude Code reads raw stdout)
114
+ }
115
+ // Otherwise (terminal goal) — no output → Claude Code stops normally.
116
+ }
117
+ // Run and exit cleanly on any error (never crash the hook).
118
+ main().catch(() => {
119
+ process.exit(0);
120
+ });
121
+ //# sourceMappingURL=goal-stop-hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-stop-hook.js","sourceRoot":"","sources":["../../src/harnesses/goal-stop-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAoC1C,8EAA8E;AAC9E,OAAO;AACP,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,KAAK,UAAU,IAAI;IACjB,iDAAiD;IACjD,IAAI,cAAkC,CAAC;IACvC,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE;YACjE,OAAO,EAAE,MAAM;YACf,GAAG,EAAE,OAAO,CAAC,GAAG;SACjB,CAAC,CAAC;QACH,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAuB,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,kDAAkD;QAClD,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,cAAc,EAAE,IAAI,CAAC;IACtC,yEAAyE;IACzE,IAAI,CAAC,QAAQ,IAAI,QAAQ,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACtD,kCAAkC;QAClC,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,EAAE,CAAC;IAE3B,uBAAuB;IACvB,IAAI,eAAoC,CAAC;IACzC,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE;YAC1E,OAAO,EAAE,MAAM;YACf,GAAG,EAAE,OAAO,CAAC,GAAG;SACjB,CAAC,CAAC;QACH,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAwB,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,uDAAuD;QACvD,OAAO;IACT,CAAC;IAED,IAAI,CAAC,eAAe,EAAE,OAAO,EAAE,CAAC;QAC9B,sDAAsD;QACtD,OAAO;IACT,CAAC;IAED,MAAM,YAAY,GAAG,eAAe,EAAE,IAAI,EAAE,YAAY,CAAC;IAEzD,gEAAgE;IAChE,IAAI,YAAY,IAAI,OAAO,YAAY,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC7D,MAAM,QAAQ,GAAiB;YAC7B,QAAQ,EAAE,OAAO;YACjB,MAAM,EAAE,YAAY,CAAC,OAAO;SAC7B,CAAC;QACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,oOAAoO;IAC7R,CAAC;IACD,sEAAsE;AACxE,CAAC;AAED,4DAA4D;AAC5D,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;IAChB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleocode/cleo-os",
3
- "version": "2026.5.130",
3
+ "version": "2026.5.132",
4
4
  "description": "CleoOS — the batteries-included agentic development environment wrapping Pi",
5
5
  "type": "module",
6
6
  "main": "./dist/cli.js",
@@ -11,11 +11,11 @@
11
11
  "@mariozechner/pi-coding-agent": ">=0.60.0",
12
12
  "@sinclair/typebox": "^0.34.49",
13
13
  "env-paths": "^4.0.0",
14
- "@cleocode/agents": "2026.5.130",
15
- "@cleocode/cant": "2026.5.130",
16
- "@cleocode/paths": "2026.5.130",
17
- "@cleocode/core": "2026.5.130",
18
- "@cleocode/contracts": "2026.5.130"
14
+ "@cleocode/agents": "2026.5.132",
15
+ "@cleocode/cant": "2026.5.132",
16
+ "@cleocode/contracts": "2026.5.132",
17
+ "@cleocode/paths": "2026.5.132",
18
+ "@cleocode/core": "2026.5.132"
19
19
  },
20
20
  "devDependencies": {
21
21
  "typescript": "^6.0.2",