@cleocode/core 2026.10.18 → 2026.10.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-provenance.json +2 -2
- package/dist/check/pr-gate.js +1 -1
- package/dist/check/pr-gate.js.map +1 -1
- package/dist/cloud/nexus-vault.d.ts +4 -3
- package/dist/cloud/nexus-vault.d.ts.map +1 -1
- package/dist/cloud/nexus-vault.js +62 -22
- package/dist/cloud/nexus-vault.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +11 -0
- package/dist/config.js.map +1 -1
- package/dist/docs/conduit-history-backfill.d.ts +210 -0
- package/dist/docs/conduit-history-backfill.d.ts.map +1 -0
- package/dist/docs/conduit-history-backfill.js +451 -0
- package/dist/docs/conduit-history-backfill.js.map +1 -0
- package/dist/docs/docs-ops.d.ts +33 -0
- package/dist/docs/docs-ops.d.ts.map +1 -1
- package/dist/docs/docs-ops.js +72 -0
- package/dist/docs/docs-ops.js.map +1 -1
- package/dist/docs/docs-update.d.ts.map +1 -1
- package/dist/docs/docs-update.js +71 -0
- package/dist/docs/docs-update.js.map +1 -1
- package/dist/docs/doctor.d.ts +18 -4
- package/dist/docs/doctor.d.ts.map +1 -1
- package/dist/docs/doctor.js +124 -3
- package/dist/docs/doctor.js.map +1 -1
- package/dist/docs/section-merge.d.ts +70 -9
- package/dist/docs/section-merge.d.ts.map +1 -1
- package/dist/docs/section-merge.js +167 -10
- package/dist/docs/section-merge.js.map +1 -1
- package/dist/docs/section-write-path.d.ts.map +1 -1
- package/dist/docs/section-write-path.js +23 -8
- package/dist/docs/section-write-path.js.map +1 -1
- package/dist/gateway-client/generated/types.gen.d.ts +4 -0
- package/dist/gateway-client/generated/types.gen.d.ts.map +1 -1
- package/dist/go/driver.d.ts +29 -145
- package/dist/go/driver.d.ts.map +1 -1
- package/dist/go/driver.js +75 -168
- package/dist/go/driver.js.map +1 -1
- package/dist/go/index.d.ts +1 -1
- package/dist/go/index.d.ts.map +1 -1
- package/dist/go/index.js +1 -1
- package/dist/go/index.js.map +1 -1
- package/dist/goal/close.d.ts +1 -1
- package/dist/goal/close.js +2 -2
- package/dist/goal/close.js.map +1 -1
- package/dist/goal/draft.d.ts +10 -3
- package/dist/goal/draft.d.ts.map +1 -1
- package/dist/goal/draft.js +14 -9
- package/dist/goal/draft.js.map +1 -1
- package/dist/goal/index.d.ts +9 -12
- package/dist/goal/index.d.ts.map +1 -1
- package/dist/goal/index.js +9 -12
- package/dist/goal/index.js.map +1 -1
- package/dist/goal/runtime.d.ts +126 -0
- package/dist/goal/runtime.d.ts.map +1 -0
- package/dist/goal/runtime.js +289 -0
- package/dist/goal/runtime.js.map +1 -0
- package/dist/goal/status.d.ts +39 -0
- package/dist/goal/status.d.ts.map +1 -0
- package/dist/goal/status.js +101 -0
- package/dist/goal/status.js.map +1 -0
- package/dist/hooks/project-runner.d.ts +8 -3
- package/dist/hooks/project-runner.d.ts.map +1 -1
- package/dist/hooks/project-runner.js +104 -14
- package/dist/hooks/project-runner.js.map +1 -1
- package/dist/hooks/project-state.js +1 -1
- package/dist/hooks/project-state.js.map +1 -1
- package/dist/internal.d.ts +7 -3
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +5 -3
- package/dist/internal.js.map +1 -1
- package/dist/lifecycle/engine-ops.d.ts.map +1 -1
- package/dist/lifecycle/engine-ops.js +9 -6
- package/dist/lifecycle/engine-ops.js.map +1 -1
- package/dist/memory/retrieval/observe.d.ts.map +1 -1
- package/dist/memory/retrieval/observe.js +4 -8
- package/dist/memory/retrieval/observe.js.map +1 -1
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +4 -1
- package/dist/paths.js.map +1 -1
- package/dist/playbooks/cantbook-profile.d.ts +3 -3
- package/dist/playbooks/cantbook-profile.js +3 -3
- package/dist/playbooks/pi-runner-wiring.d.ts +3 -3
- package/dist/playbooks/pi-runner-wiring.js +3 -3
- package/dist/release/ci-evidence.d.ts +28 -0
- package/dist/release/ci-evidence.d.ts.map +1 -1
- package/dist/release/ci-evidence.js +76 -20
- package/dist/release/ci-evidence.js.map +1 -1
- package/dist/release/open.d.ts +11 -0
- package/dist/release/open.d.ts.map +1 -1
- package/dist/release/open.js +49 -10
- package/dist/release/open.js.map +1 -1
- package/dist/release/preflight-skip.d.ts +50 -0
- package/dist/release/preflight-skip.d.ts.map +1 -1
- package/dist/release/preflight-skip.js +151 -34
- package/dist/release/preflight-skip.js.map +1 -1
- package/dist/release/reconcile.d.ts.map +1 -1
- package/dist/release/reconcile.js +6 -3
- package/dist/release/reconcile.js.map +1 -1
- package/dist/scaffold/ensure-config.d.ts +1 -1
- package/dist/scaffold/ensure-config.d.ts.map +1 -1
- package/dist/scaffold/ensure-config.js +2 -0
- package/dist/scaffold/ensure-config.js.map +1 -1
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/index.js.map +1 -1
- package/dist/security/override-cap-setting.d.ts +180 -0
- package/dist/security/override-cap-setting.d.ts.map +1 -0
- package/dist/security/override-cap-setting.js +657 -0
- package/dist/security/override-cap-setting.js.map +1 -0
- package/dist/security/override-cap.d.ts +20 -29
- package/dist/security/override-cap.d.ts.map +1 -1
- package/dist/security/override-cap.js +47 -71
- package/dist/security/override-cap.js.map +1 -1
- package/dist/security/owner-override-auth.d.ts +58 -0
- package/dist/security/owner-override-auth.d.ts.map +1 -1
- package/dist/security/owner-override-auth.js +82 -1
- package/dist/security/owner-override-auth.js.map +1 -1
- package/dist/security/owner-override-authority.d.ts +67 -0
- package/dist/security/owner-override-authority.d.ts.map +1 -0
- package/dist/security/owner-override-authority.js +100 -0
- package/dist/security/owner-override-authority.js.map +1 -0
- package/dist/sessions/handoff.d.ts.map +1 -1
- package/dist/sessions/handoff.js +4 -5
- package/dist/sessions/handoff.js.map +1 -1
- package/dist/sessions/index.d.ts.map +1 -1
- package/dist/sessions/index.js +22 -25
- package/dist/sessions/index.js.map +1 -1
- package/dist/store/conduit-doc-history.d.ts +39 -7
- package/dist/store/conduit-doc-history.d.ts.map +1 -1
- package/dist/store/conduit-doc-history.js +62 -12
- package/dist/store/conduit-doc-history.js.map +1 -1
- package/dist/store/data-safety-central.d.ts.map +1 -1
- package/dist/store/data-safety-central.js +3 -2
- package/dist/store/data-safety-central.js.map +1 -1
- package/dist/store/json.d.ts.map +1 -1
- package/dist/store/json.js +7 -7
- package/dist/store/json.js.map +1 -1
- package/dist/store/portable-bundle-scan.d.ts +23 -1
- package/dist/store/portable-bundle-scan.d.ts.map +1 -1
- package/dist/store/portable-bundle-scan.js +85 -3
- package/dist/store/portable-bundle-scan.js.map +1 -1
- package/dist/store/portable-bundle.d.ts.map +1 -1
- package/dist/store/portable-bundle.js +6 -1
- package/dist/store/portable-bundle.js.map +1 -1
- package/dist/store/row-identity-registry.d.ts +5 -3
- package/dist/store/row-identity-registry.d.ts.map +1 -1
- package/dist/store/row-identity-registry.js +54 -38
- package/dist/store/row-identity-registry.js.map +1 -1
- package/dist/store/schema/cleo-project/conduit.d.ts.map +1 -1
- package/dist/store/schema/cleo-project/conduit.js +4 -3
- package/dist/store/schema/cleo-project/conduit.js.map +1 -1
- package/dist/store/schema/cleo-project/runtime.d.ts +32 -0
- package/dist/store/schema/cleo-project/runtime.d.ts.map +1 -1
- package/dist/store/schema/cleo-project/runtime.js +7 -3
- package/dist/store/schema/cleo-project/runtime.js.map +1 -1
- package/dist/store/session-store.d.ts +45 -0
- package/dist/store/session-store.d.ts.map +1 -1
- package/dist/store/session-store.js +96 -1
- package/dist/store/session-store.js.map +1 -1
- package/dist/store/sqlite.d.ts +2 -0
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.js +12 -0
- package/dist/store/sqlite.js.map +1 -1
- package/dist/store/sync/merge/rules.d.ts.map +1 -1
- package/dist/store/sync/merge/rules.js +0 -16
- package/dist/store/sync/merge/rules.js.map +1 -1
- package/dist/store/sync/sealer.d.ts.map +1 -1
- package/dist/store/sync/sealer.js +27 -1
- package/dist/store/sync/sealer.js.map +1 -1
- package/dist/store/sync/sequencing.d.ts +3 -1
- package/dist/store/sync/sequencing.d.ts.map +1 -1
- package/dist/store/sync/sequencing.js +5 -3
- package/dist/store/sync/sequencing.js.map +1 -1
- package/dist/store/sync/timestamp-canon.d.ts.map +1 -1
- package/dist/store/sync/timestamp-canon.js +4 -0
- package/dist/store/sync/timestamp-canon.js.map +1 -1
- package/dist/store/table-classification.d.ts.map +1 -1
- package/dist/store/table-classification.js +12 -8
- package/dist/store/table-classification.js.map +1 -1
- package/dist/store/twin-collapse.d.ts +21 -0
- package/dist/store/twin-collapse.d.ts.map +1 -1
- package/dist/store/twin-collapse.js +235 -18
- package/dist/store/twin-collapse.js.map +1 -1
- package/dist/system/health.d.ts +17 -0
- package/dist/system/health.d.ts.map +1 -1
- package/dist/system/health.js +96 -0
- package/dist/system/health.js.map +1 -1
- package/dist/tasks/affected-packages.d.ts +67 -2
- package/dist/tasks/affected-packages.d.ts.map +1 -1
- package/dist/tasks/affected-packages.js +149 -21
- package/dist/tasks/affected-packages.js.map +1 -1
- package/dist/tasks/affected-scope.d.ts.map +1 -1
- package/dist/tasks/affected-scope.js +91 -5
- package/dist/tasks/affected-scope.js.map +1 -1
- package/dist/tasks/changeset-plan.d.ts +33 -0
- package/dist/tasks/changeset-plan.d.ts.map +1 -0
- package/dist/tasks/changeset-plan.js +152 -0
- package/dist/tasks/changeset-plan.js.map +1 -0
- package/dist/tasks/complete.d.ts.map +1 -1
- package/dist/tasks/complete.js +9 -1
- package/dist/tasks/complete.js.map +1 -1
- package/dist/tasks/done-plan.d.ts.map +1 -1
- package/dist/tasks/done-plan.js +28 -14
- package/dist/tasks/done-plan.js.map +1 -1
- package/dist/tasks/done-record.d.ts.map +1 -1
- package/dist/tasks/done-record.js +5 -1
- package/dist/tasks/done-record.js.map +1 -1
- package/dist/tasks/evidence.d.ts.map +1 -1
- package/dist/tasks/evidence.js +149 -18
- package/dist/tasks/evidence.js.map +1 -1
- package/dist/tasks/gate-runner.d.ts.map +1 -1
- package/dist/tasks/gate-runner.js +76 -4
- package/dist/tasks/gate-runner.js.map +1 -1
- package/dist/tasks/qa-run-binding.d.ts +18 -0
- package/dist/tasks/qa-run-binding.d.ts.map +1 -1
- package/dist/tasks/qa-run-binding.js +41 -1
- package/dist/tasks/qa-run-binding.js.map +1 -1
- package/dist/tasks/test-run-binding.d.ts +62 -0
- package/dist/tasks/test-run-binding.d.ts.map +1 -1
- package/dist/tasks/test-run-binding.js +126 -21
- package/dist/tasks/test-run-binding.js.map +1 -1
- package/dist/tasks/tool-resolver.d.ts +7 -0
- package/dist/tasks/tool-resolver.d.ts.map +1 -1
- package/dist/tasks/tool-resolver.js +3 -1
- package/dist/tasks/tool-resolver.js.map +1 -1
- package/dist/validation/engine-ops.d.ts.map +1 -1
- package/dist/validation/engine-ops.js +24 -29
- package/dist/validation/engine-ops.js.map +1 -1
- package/migrations/drizzle-cleo-project/20261016120000_t12535-playbook-row-uids/migration.sql +24 -0
- package/package.json +13 -13
- package/schemas/project-context.schema.json +5 -0
- package/templates/CLEO-INJECTION.md +1 -1
- package/templates/CLEO-REFERENCE.md +42 -1
- package/templates/cleo-gitignore +3 -0
- package/templates/git-hooks/legacy-hashes.json +1 -0
- package/templates/git-hooks/pre-push +35 -7
- package/templates/workflows/release-prepare.yml.tmpl +82 -4
- package/dist/goal/advance-with-persist.d.ts +0 -80
- package/dist/goal/advance-with-persist.d.ts.map +0 -1
- package/dist/goal/advance-with-persist.js +0 -105
- package/dist/goal/advance-with-persist.js.map +0 -1
- package/dist/goal/arm.d.ts +0 -61
- package/dist/goal/arm.d.ts.map +0 -1
- package/dist/goal/arm.js +0 -65
- package/dist/goal/arm.js.map +0 -1
- package/dist/goal/continuation.d.ts +0 -66
- package/dist/goal/continuation.d.ts.map +0 -1
- package/dist/goal/continuation.js +0 -111
- package/dist/goal/continuation.js.map +0 -1
- package/dist/goal/judge.d.ts +0 -113
- package/dist/goal/judge.d.ts.map +0 -1
- package/dist/goal/judge.js +0 -193
- package/dist/goal/judge.js.map +0 -1
- package/dist/goal/loop.d.ts +0 -75
- package/dist/goal/loop.d.ts.map +0 -1
- package/dist/goal/loop.js +0 -132
- package/dist/goal/loop.js.map +0 -1
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLEO Goal runtime (`@cleocode/goal`, bin `cleo-goal`, alias `ocg`): health
|
|
3
|
+
* and per-project registration.
|
|
4
|
+
*
|
|
5
|
+
* CLEO Goal is a standalone package CLEO talks to only through its bin
|
|
6
|
+
* (T13791 owner decisions). CLEO works fully without it: every function here
|
|
7
|
+
* reports absence instead of throwing.
|
|
8
|
+
*
|
|
9
|
+
* Bin contracts used here: `cleo-goal --version`, `cleo-goal status --json`
|
|
10
|
+
* and `cleo-goal install --project --json` (idempotent, project-local
|
|
11
|
+
* `.opencode/opencode.json`, never user-global). JSON shapes live in
|
|
12
|
+
* `@cleocode/contracts/goal-runtime.js`; an unknown `schemaVersion` is refused.
|
|
13
|
+
*
|
|
14
|
+
* @task T13819
|
|
15
|
+
* @task T13820
|
|
16
|
+
* @task T13821
|
|
17
|
+
* @epic T13791
|
|
18
|
+
*/
|
|
19
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
20
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
21
|
+
import { dirname, join } from 'node:path';
|
|
22
|
+
import { engineError, engineSuccess } from '@cleocode/contracts/engine-result.js';
|
|
23
|
+
import { ExitCode } from '@cleocode/contracts/exit-codes.js';
|
|
24
|
+
import { GOAL_RUNTIME_SCHEMA_VERSION, } from '@cleocode/contracts/goal-runtime.js';
|
|
25
|
+
import { findOnPath } from '@cleocode/paths';
|
|
26
|
+
import { joinCommandLine } from '../tasks/command-line.js';
|
|
27
|
+
// shortcut: @cleocode/cleo does not list this as an optionalDependency yet, since an unpublished one 404s on every install; add it as ">=0.8.0 <1.0.0" once the package is on npm (T13819 note).
|
|
28
|
+
/** The npm package that ships the CLEO Goal runtime. */
|
|
29
|
+
export const GOAL_PACKAGE = '@cleocode/goal';
|
|
30
|
+
/** Bin names CLEO Goal installs, primary first (`ocg` = "open cleo goal"). */
|
|
31
|
+
export const GOAL_BINS = ['cleo-goal', 'ocg'];
|
|
32
|
+
/** Install command shown in remedies (the fix of every `E_GOAL_RUNTIME_MISSING`). */
|
|
33
|
+
export const GOAL_INSTALL_COMMAND = `npm i -g ${GOAL_PACKAGE}@latest`;
|
|
34
|
+
/** Harnesses `cleo goal install` accepts, in rollout order (owner decision). */
|
|
35
|
+
export const GOAL_HARNESSES = ['opencode', 'claude', 'codex', 'hermes', 'kimi'];
|
|
36
|
+
/** Run a bin; its trimmed stdout, or `null` on any failure. */
|
|
37
|
+
function run(file, args, env, timeout) {
|
|
38
|
+
try {
|
|
39
|
+
// shortcut: no shell, so a Windows `.cmd` shim fails here; add shell resolution when Windows is supported.
|
|
40
|
+
return execFileSync(file, args, {
|
|
41
|
+
encoding: 'utf-8',
|
|
42
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
43
|
+
timeout,
|
|
44
|
+
env: { ...env },
|
|
45
|
+
}).trim();
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** The first semver in `text` (`cleo-goal 0.5.0` → `0.5.0`), or `null`. */
|
|
52
|
+
function semver(text) {
|
|
53
|
+
return text?.match(/\d+\.\d+\.\d+[\w.+-]*/)?.[0] ?? null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Whether `bin` belongs to the `@cleocode/goal` package: the nearest
|
|
57
|
+
* `package.json` above its real path is named {@link GOAL_PACKAGE}. Reads
|
|
58
|
+
* files only, never runs the bin.
|
|
59
|
+
*/
|
|
60
|
+
function isGoalPackageBin(bin) {
|
|
61
|
+
try {
|
|
62
|
+
let dir = dirname(realpathSync(bin));
|
|
63
|
+
for (let i = 0; i < 6; i++) {
|
|
64
|
+
try {
|
|
65
|
+
const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf-8'));
|
|
66
|
+
return pkg.name === GOAL_PACKAGE;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// No readable package.json here; keep walking up.
|
|
70
|
+
}
|
|
71
|
+
const parent = dirname(dir);
|
|
72
|
+
if (parent === dir)
|
|
73
|
+
return false;
|
|
74
|
+
dir = parent;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Dangling link or unreadable path.
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Locate the CLEO Goal bin on PATH without running anything: `cleo-goal`,
|
|
84
|
+
* else `ocg` only when it belongs to {@link GOAL_PACKAGE} (`ocg` is a short
|
|
85
|
+
* name another tool may own).
|
|
86
|
+
*
|
|
87
|
+
* @param env - environment whose `PATH` is searched (default `process.env`)
|
|
88
|
+
* @returns the bin, and a foreign `ocg` that was skipped
|
|
89
|
+
*/
|
|
90
|
+
export function locateGoalBin(env = process.env) {
|
|
91
|
+
const primary = findOnPath('cleo-goal', { env });
|
|
92
|
+
if (primary !== null)
|
|
93
|
+
return { bin: primary, foreign: null };
|
|
94
|
+
const ocg = findOnPath('ocg', { env });
|
|
95
|
+
if (ocg === null)
|
|
96
|
+
return { bin: null, foreign: null };
|
|
97
|
+
return isGoalPackageBin(ocg) ? { bin: ocg, foreign: null } : { bin: null, foreign: ocg };
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Resolve the CLEO Goal bin on PATH: `cleo-goal`, then an `ocg` that belongs
|
|
101
|
+
* to {@link GOAL_PACKAGE}. Synchronous and spawn-free.
|
|
102
|
+
*
|
|
103
|
+
* @param env - environment whose `PATH` is searched (default `process.env`)
|
|
104
|
+
* @returns the absolute bin path, or `null`
|
|
105
|
+
*/
|
|
106
|
+
export function resolveGoalBin(env = process.env) {
|
|
107
|
+
return locateGoalBin(env).bin;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The Stop dispatcher's tick command line for the resolved bin
|
|
111
|
+
* (`<bin> tick`, shell-quoted), or `null` when CLEO Goal is not installed.
|
|
112
|
+
*
|
|
113
|
+
* @param env - environment whose `PATH` is searched (default `process.env`)
|
|
114
|
+
* @returns the command line, or `null`
|
|
115
|
+
*/
|
|
116
|
+
export function goalTickCommand(env = process.env) {
|
|
117
|
+
const bin = resolveGoalBin(env);
|
|
118
|
+
return bin === null ? null : joinCommandLine([bin, 'tick']);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Inspect the installed CLEO Goal runtime. Local only by default: the npm
|
|
122
|
+
* lookup runs only with `checkUpdates` (no network otherwise). Never throws;
|
|
123
|
+
* an unpublished package or no network leaves `latest` null.
|
|
124
|
+
*
|
|
125
|
+
* @param env - environment for the bin lookup and spawns (default `process.env`)
|
|
126
|
+
* @param options - `checkUpdates` asks npm for the latest version
|
|
127
|
+
* @returns installed path, version, npm latest and whether an update exists
|
|
128
|
+
*/
|
|
129
|
+
export function inspectGoalRuntime(env = process.env, options = {}) {
|
|
130
|
+
const { bin, foreign } = locateGoalBin(env);
|
|
131
|
+
const version = semver(bin === null ? null : run(bin, ['--version'], env, 2000));
|
|
132
|
+
const npm = options.checkUpdates === true ? findOnPath('npm', { env }) : null;
|
|
133
|
+
// A 404 (not published yet) or no network both leave `latest` null.
|
|
134
|
+
const latest = semver(npm === null ? null : run(npm, ['view', GOAL_PACKAGE, 'version'], env, 8000));
|
|
135
|
+
return {
|
|
136
|
+
bin,
|
|
137
|
+
foreign,
|
|
138
|
+
version,
|
|
139
|
+
latest,
|
|
140
|
+
updateAvailable: version !== null && latest !== null && version !== latest,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/** `text` parsed as a JSON object carrying a `schemaVersion`, or `null`. */
|
|
144
|
+
function parseVersioned(text) {
|
|
145
|
+
try {
|
|
146
|
+
const parsed = JSON.parse(text);
|
|
147
|
+
return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed) ? parsed : null;
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Run `bin args` in `cwd` and read its JSON reply per the CLEO Goal exit
|
|
155
|
+
* contract: 0 = the `T` shape, 1 = {@link GoalRuntimeError}, 2 = usage error
|
|
156
|
+
* on stderr. Any `schemaVersion` other than {@link GOAL_RUNTIME_SCHEMA_VERSION}
|
|
157
|
+
* is refused.
|
|
158
|
+
*/
|
|
159
|
+
function runGoalJson(bin, args, cwd, env, timeout) {
|
|
160
|
+
// shortcut: no shell, so a Windows `.cmd` shim fails here; add shell resolution when Windows is supported.
|
|
161
|
+
const r = spawnSync(bin, args, { cwd, encoding: 'utf-8', timeout, env: { ...env } });
|
|
162
|
+
const command = `${bin} ${args.join(' ')}`;
|
|
163
|
+
if (r.error !== undefined && 'code' in r.error && r.error.code === 'ETIMEDOUT') {
|
|
164
|
+
// @sync-invariant none:local-only the goal bin did not answer in time; nothing is written
|
|
165
|
+
return engineError('E_GOAL_RUNTIME_TIMEOUT', `${command} did not answer within ${timeout} ms`, {
|
|
166
|
+
exitCode: ExitCode.GENERAL_ERROR,
|
|
167
|
+
fix: `Run ${command} in ${cwd} to see why it is slow`,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
const body = parseVersioned(r.stdout ?? '');
|
|
171
|
+
if ((r.status === 0 || r.status === 1) && body === null) {
|
|
172
|
+
const out = (r.stdout ?? '').trim().slice(0, 200);
|
|
173
|
+
// @sync-invariant none:local-only the goal bin replied with non-JSON; nothing is written
|
|
174
|
+
return engineError('E_GOAL_RUNTIME_FAILED', `${command} replied with non-JSON output: ${out || '(empty)'}`, {
|
|
175
|
+
exitCode: ExitCode.GENERAL_ERROR,
|
|
176
|
+
fix: `${GOAL_INSTALL_COMMAND} (this CLEO reads ${command} as JSON, schemaVersion ${GOAL_RUNTIME_SCHEMA_VERSION})`,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (r.status === 2) {
|
|
180
|
+
// @sync-invariant none:input-shape the goal bin rejected CLEO's arguments; nothing is written
|
|
181
|
+
return engineError('E_GOAL_RUNTIME_USAGE', `${command}: ${(r.stderr ?? '').trim() || 'usage error'}`, {
|
|
182
|
+
exitCode: ExitCode.GENERAL_ERROR,
|
|
183
|
+
fix: `${GOAL_INSTALL_COMMAND} (this CLEO needs a cleo-goal that accepts: ${args.join(' ')})`,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
if ((r.status === 0 || r.status === 1) && body?.schemaVersion !== GOAL_RUNTIME_SCHEMA_VERSION) {
|
|
187
|
+
// @sync-invariant none:local-only the goal bin speaks an unknown schema; nothing is written
|
|
188
|
+
return engineError('E_GOAL_RUNTIME_SCHEMA', `${command} replied with schemaVersion ${String(body?.schemaVersion)}; this CLEO understands ${GOAL_RUNTIME_SCHEMA_VERSION}`, {
|
|
189
|
+
exitCode: ExitCode.DEPENDENCY_ERROR,
|
|
190
|
+
fix: 'Upgrade both: npm i -g @cleocode/cleo@latest @cleocode/goal@latest',
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
if (r.status === 0)
|
|
194
|
+
return engineSuccess(body);
|
|
195
|
+
const reason = r.status === 1
|
|
196
|
+
? body.error
|
|
197
|
+
: (r.error?.message ?? `exit ${String(r.status ?? r.signal)}`);
|
|
198
|
+
// @sync-invariant none:local-only the goal bin failed on this machine; no cleo.db write
|
|
199
|
+
return engineError('E_GOAL_RUNTIME_FAILED', `${command} failed: ${reason}`, {
|
|
200
|
+
exitCode: ExitCode.GENERAL_ERROR,
|
|
201
|
+
fix: `Run ${command} in ${cwd} to see the full error`,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
/** The missing-bin failure, with the install command as the fix. */
|
|
205
|
+
function goalRuntimeMissing() {
|
|
206
|
+
// @sync-invariant none:local-only the goal bin is absent on this machine; nothing is written
|
|
207
|
+
return engineError('E_GOAL_RUNTIME_MISSING', `CLEO Goal is not installed: neither ${GOAL_BINS.join(' nor ')} is on PATH`, { exitCode: ExitCode.DEPENDENCY_ERROR, fix: GOAL_INSTALL_COMMAND });
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Read the live goal through `cleo-goal status --json` in the project. A
|
|
211
|
+
* project with no goal is a success with `status: 'none'`.
|
|
212
|
+
*
|
|
213
|
+
* @param projectRoot - absolute project root (the bin's cwd)
|
|
214
|
+
* @param env - environment for the bin lookup and spawn (default `process.env`)
|
|
215
|
+
* @param timeoutMs - deadline for the bin (default 15 s; `cleo doctor` passes 2 s)
|
|
216
|
+
* @returns the status shape, or a failure (`E_GOAL_RUNTIME_MISSING`,
|
|
217
|
+
* `E_GOAL_RUNTIME_SCHEMA`, `E_GOAL_RUNTIME_USAGE`, `E_GOAL_RUNTIME_TIMEOUT`,
|
|
218
|
+
* `E_GOAL_RUNTIME_FAILED`)
|
|
219
|
+
*/
|
|
220
|
+
export function readGoalStatus(projectRoot, env = process.env, timeoutMs = 15_000) {
|
|
221
|
+
const bin = resolveGoalBin(env);
|
|
222
|
+
if (bin === null)
|
|
223
|
+
return goalRuntimeMissing();
|
|
224
|
+
return runGoalJson(bin, ['status', '--json'], projectRoot, env, timeoutMs);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Register CLEO Goal with one harness for this project. Writes only
|
|
228
|
+
* project-level config, never `~/.claude`, `~/.codex` or `~/.config`.
|
|
229
|
+
*
|
|
230
|
+
* - `opencode`: runs `cleo-goal install --project --json` in the project.
|
|
231
|
+
* - `claude` / `codex`: installs only the project Stop dispatcher (`cleo hook
|
|
232
|
+
* stop`, T13796), which runs the goal tick, whatever the heavy-command mode;
|
|
233
|
+
* no separate Stop hook and no `PreToolUse` hook.
|
|
234
|
+
* - `hermes` / `kimi`: `E_NOT_IMPLEMENTED` until the pilot (T13822).
|
|
235
|
+
*
|
|
236
|
+
* @param projectRoot - absolute project root
|
|
237
|
+
* @param harness - harness to register with
|
|
238
|
+
* @param env - environment for the bin lookup and spawn (default `process.env`)
|
|
239
|
+
* @returns the registration result, or a failure with a remedy
|
|
240
|
+
*/
|
|
241
|
+
export async function installGoalForHarness(projectRoot, harness, env = process.env) {
|
|
242
|
+
if (!GOAL_HARNESSES.includes(harness)) {
|
|
243
|
+
// @sync-invariant none:input-shape unknown --harness value; nothing is written
|
|
244
|
+
return engineError('E_VALIDATION', `Unknown harness '${harness}'`, {
|
|
245
|
+
exitCode: ExitCode.VALIDATION_ERROR,
|
|
246
|
+
fix: `Use --harness ${GOAL_HARNESSES.join('|')}`,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
const h = harness;
|
|
250
|
+
if (h === 'hermes' || h === 'kimi') {
|
|
251
|
+
// @sync-invariant none:input-shape harness not supported yet; nothing is written
|
|
252
|
+
return engineError('E_NOT_IMPLEMENTED', `cleo goal install --harness ${h} is not implemented yet; harness pilots are tracked in T13822`, { exitCode: ExitCode.GENERAL_ERROR, fix: 'cleo show T13822' });
|
|
253
|
+
}
|
|
254
|
+
const bin = resolveGoalBin(env);
|
|
255
|
+
if (bin === null)
|
|
256
|
+
return goalRuntimeMissing();
|
|
257
|
+
if (h === 'opencode') {
|
|
258
|
+
const installed = runGoalJson(bin, ['install', '--project', '--json'], projectRoot, env, 60_000);
|
|
259
|
+
if (installed.success)
|
|
260
|
+
return engineSuccess({ harness: h, bin, goal: installed.data });
|
|
261
|
+
// @sync-invariant none:local-only the goal bin failed writing project config; no cleo.db write
|
|
262
|
+
return engineError('E_GOAL_INSTALL_FAILED', installed.error.message, {
|
|
263
|
+
exitCode: installed.error.exitCode ?? ExitCode.GENERAL_ERROR,
|
|
264
|
+
...(installed.error.fix === undefined ? {} : { fix: installed.error.fix }),
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
const { loadHeavyHookDelivery } = await import('../resources/heavy-command-hook-delivery.js');
|
|
268
|
+
const sync = (await loadHeavyHookDelivery()).syncProjectStopDispatch;
|
|
269
|
+
if (sync === undefined) {
|
|
270
|
+
// @sync-invariant none:local-only the installed adapters package predates Stop-only delivery; nothing is written
|
|
271
|
+
return engineError('E_GOAL_INSTALL_FAILED', '@cleocode/adapters is too old to install the Stop dispatcher alone', {
|
|
272
|
+
exitCode: ExitCode.DEPENDENCY_ERROR,
|
|
273
|
+
fix: 'npm i -g @cleocode/cleo@latest',
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
const delivery = await sync(projectRoot, h === 'claude' ? 'claude-code' : 'codex');
|
|
277
|
+
if (delivery.status === 'failed' ||
|
|
278
|
+
delivery.status === 'needs-consent' ||
|
|
279
|
+
delivery.status === 'skipped') {
|
|
280
|
+
// @sync-invariant none:local-only the project hook file could not be written; no cleo.db write
|
|
281
|
+
return engineError('E_GOAL_INSTALL_FAILED', `Stop dispatcher not installed in ${delivery.target}: ${delivery.reason ?? delivery.status}`, {
|
|
282
|
+
exitCode: ExitCode.GENERAL_ERROR,
|
|
283
|
+
details: delivery,
|
|
284
|
+
...(delivery.remedy === undefined ? {} : { fix: delivery.remedy }),
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
return engineSuccess({ harness: h, bin, delivery });
|
|
288
|
+
}
|
|
289
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/goal/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cleo goal status` — the project's drafted goals, the CLEO tasks they serve,
|
|
3
|
+
* and the live run (T13821).
|
|
4
|
+
*
|
|
5
|
+
* The live run comes only from `cleo-goal status --json` (versioned; an
|
|
6
|
+
* unknown schema version is refused), never from the runtime's internal files.
|
|
7
|
+
* The drafted goals come from their `goal.yaml`, the contract CLEO writes.
|
|
8
|
+
* Read-only.
|
|
9
|
+
*
|
|
10
|
+
* @epic T13791
|
|
11
|
+
* @task T13821
|
|
12
|
+
*/
|
|
13
|
+
import type { EngineResult, GoalDraftEntry, GoalStatusReport } from '@cleocode/contracts';
|
|
14
|
+
/**
|
|
15
|
+
* The drafted goals of a project, from each `.opencode/goals/<slug>/goal.yaml`.
|
|
16
|
+
*
|
|
17
|
+
* @param projectRoot - Project root.
|
|
18
|
+
* @returns One entry per goal directory holding a `goal.yaml`, by slug.
|
|
19
|
+
*/
|
|
20
|
+
export declare function draftedGoals(projectRoot: string): GoalDraftEntry[];
|
|
21
|
+
/**
|
|
22
|
+
* The drafted goals and the live run. A missing CLEO Goal bin is a report
|
|
23
|
+
* (`runtime: 'not-installed'` with the install commands), not a failure; an
|
|
24
|
+
* unknown status schema or a failing bin is a failure.
|
|
25
|
+
*
|
|
26
|
+
* @param opts - Project root, a task to filter by, and the environment.
|
|
27
|
+
* @returns The report, or the runtime's refusal.
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const r = goalStatus({ taskId: 'T123' });
|
|
31
|
+
* if (r.success) r.data.live?.status; // the live run, when there is one
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function goalStatus(opts?: {
|
|
35
|
+
projectRoot?: string;
|
|
36
|
+
taskId?: string;
|
|
37
|
+
env?: Readonly<Record<string, string | undefined>>;
|
|
38
|
+
}): EngineResult<GoalStatusReport>;
|
|
39
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/goal/status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AA4B1F;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,EAAE,CAoBlE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CACxB,IAAI,GAAE;IACJ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;CAC/C,GACL,YAAY,CAAC,gBAAgB,CAAC,CAiBhC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cleo goal status` — the project's drafted goals, the CLEO tasks they serve,
|
|
3
|
+
* and the live run (T13821).
|
|
4
|
+
*
|
|
5
|
+
* The live run comes only from `cleo-goal status --json` (versioned; an
|
|
6
|
+
* unknown schema version is refused), never from the runtime's internal files.
|
|
7
|
+
* The drafted goals come from their `goal.yaml`, the contract CLEO writes.
|
|
8
|
+
* Read-only.
|
|
9
|
+
*
|
|
10
|
+
* @epic T13791
|
|
11
|
+
* @task T13821
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import { engineSuccess } from '@cleocode/contracts/engine-result.js';
|
|
16
|
+
import { parse as parseYaml } from 'yaml';
|
|
17
|
+
import { getProjectRoot } from '../paths.js';
|
|
18
|
+
import { GOAL_INSTALL_COMMAND, readGoalStatus } from './runtime.js';
|
|
19
|
+
const record = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
20
|
+
/** The tasks a contract serves: `cleo.tasks` and every task a criterion `proves`. */
|
|
21
|
+
function contractTasks(contract) {
|
|
22
|
+
const ids = new Set();
|
|
23
|
+
const cleo = contract.cleo;
|
|
24
|
+
if (record(cleo) && Array.isArray(cleo.tasks)) {
|
|
25
|
+
for (const t of cleo.tasks) {
|
|
26
|
+
const id = typeof t === 'string' ? t : record(t) && typeof t.id === 'string' ? t.id : '';
|
|
27
|
+
if (id)
|
|
28
|
+
ids.add(id);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
for (const c of Array.isArray(contract.criteria) ? contract.criteria : []) {
|
|
32
|
+
if (!record(c) || !Array.isArray(c.proves))
|
|
33
|
+
continue;
|
|
34
|
+
for (const ref of c.proves)
|
|
35
|
+
if (typeof ref === 'string')
|
|
36
|
+
ids.add(ref.split('#')[0] ?? ref);
|
|
37
|
+
}
|
|
38
|
+
return [...ids].sort();
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The drafted goals of a project, from each `.opencode/goals/<slug>/goal.yaml`.
|
|
42
|
+
*
|
|
43
|
+
* @param projectRoot - Project root.
|
|
44
|
+
* @returns One entry per goal directory holding a `goal.yaml`, by slug.
|
|
45
|
+
*/
|
|
46
|
+
export function draftedGoals(projectRoot) {
|
|
47
|
+
const dir = join(projectRoot, '.opencode', 'goals');
|
|
48
|
+
if (!existsSync(dir))
|
|
49
|
+
return [];
|
|
50
|
+
const entries = [];
|
|
51
|
+
for (const d of readdirSync(dir, { withFileTypes: true })) {
|
|
52
|
+
const contractPath = join(dir, d.name, 'goal.yaml');
|
|
53
|
+
if (!d.isDirectory() || !existsSync(contractPath))
|
|
54
|
+
continue;
|
|
55
|
+
const entry = { slug: d.name, title: null, tasks: [] };
|
|
56
|
+
try {
|
|
57
|
+
const contract = parseYaml(readFileSync(contractPath, 'utf8'));
|
|
58
|
+
if (record(contract)) {
|
|
59
|
+
entry.title = typeof contract.title === 'string' ? contract.title : null;
|
|
60
|
+
entry.tasks = contractTasks(contract);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
entry.problem = `goal.yaml does not parse: ${err.message}`;
|
|
65
|
+
}
|
|
66
|
+
entries.push(entry);
|
|
67
|
+
}
|
|
68
|
+
return entries.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The drafted goals and the live run. A missing CLEO Goal bin is a report
|
|
72
|
+
* (`runtime: 'not-installed'` with the install commands), not a failure; an
|
|
73
|
+
* unknown status schema or a failing bin is a failure.
|
|
74
|
+
*
|
|
75
|
+
* @param opts - Project root, a task to filter by, and the environment.
|
|
76
|
+
* @returns The report, or the runtime's refusal.
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* const r = goalStatus({ taskId: 'T123' });
|
|
80
|
+
* if (r.success) r.data.live?.status; // the live run, when there is one
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export function goalStatus(opts = {}) {
|
|
84
|
+
const root = opts.projectRoot ?? getProjectRoot();
|
|
85
|
+
const goals = draftedGoals(root).filter((g) => !opts.taskId || g.tasks.includes(opts.taskId));
|
|
86
|
+
const live = readGoalStatus(root, opts.env);
|
|
87
|
+
if (!live.success) {
|
|
88
|
+
if (live.error.code !== 'E_GOAL_RUNTIME_MISSING')
|
|
89
|
+
return live;
|
|
90
|
+
return engineSuccess({
|
|
91
|
+
runtime: 'not-installed',
|
|
92
|
+
install: [GOAL_INSTALL_COMMAND, 'cleo goal install --harness opencode'],
|
|
93
|
+
live: null,
|
|
94
|
+
goals,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// With --task, the live run counts only when it is one of that task's goals.
|
|
98
|
+
const relevant = !opts.taskId || (live.data.goal !== null && goals.some((g) => g.slug === live.data.goal));
|
|
99
|
+
return engineSuccess({ runtime: 'installed', live: relevant ? live.data : null, goals });
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/goal/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { type HookExecutionReceipt, type HookExecutor, type HookInvocation, type HookOutcome } from '@cleocode/contracts/project-hooks.js';
|
|
1
|
+
import { type HookExecutionReceipt, type HookExecutor, type HookInvocation, type HookOutcome, type ProjectHookBinding } from '@cleocode/contracts/project-hooks.js';
|
|
2
2
|
/** Execute activated definitions in source order; internal faults always allow locally. */
|
|
3
3
|
export declare function executeProjectHooks(invocation: HookInvocation, id?: string, activationRoot?: string, signal?: AbortSignal): Promise<HookOutcome[]>;
|
|
4
4
|
/** Contracts port for worktree and other consumers; no internal registry coupling. */
|
|
5
5
|
export declare const projectHookExecutor: HookExecutor;
|
|
6
6
|
/** Bind worktree inheritance to an explicitly activated source checkout. */
|
|
7
7
|
export declare function createProjectHookExecutor(activationRoot: string): HookExecutor;
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Read a bounded redacted execution record, validating its shape and optional binding.
|
|
10
|
+
* @param cwd - Active checkout whose private receipts are inspected.
|
|
11
|
+
* @param binding - Select a retained source/event result; omitted reads the overall latest result.
|
|
12
|
+
* @returns The matching record, or undefined when absent, malformed or mismatched.
|
|
13
|
+
*/
|
|
14
|
+
export declare function readLastProjectHookExecution(cwd: string, binding?: ProjectHookBinding): Promise<HookExecutionReceipt | undefined>;
|
|
10
15
|
//# sourceMappingURL=project-runner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-runner.d.ts","sourceRoot":"","sources":["../../src/hooks/project-runner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"project-runner.d.ts","sourceRoot":"","sources":["../../src/hooks/project-runner.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,cAAc,EAEnB,KAAK,WAAW,EAEhB,KAAK,kBAAkB,EAIxB,MAAM,sCAAsC,CAAC;AAwX9C,2FAA2F;AAC3F,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,cAAc,EAC1B,EAAE,CAAC,EAAE,MAAM,EACX,cAAc,CAAC,EAAE,MAAM,EACvB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,WAAW,EAAE,CAAC,CAwKxB;AAED,sFAAsF;AACtF,eAAO,MAAM,mBAAmB,EAAE,YAA+C,CAAC;AAElF,4EAA4E;AAC5E,wBAAgB,yBAAyB,CAAC,cAAc,EAAE,MAAM,GAAG,YAAY,CAE9E;AAED;;;;;GAKG;AACH,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAa3C"}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/** Ordered project executor, separate from the best-effort internal registry (T13343). */
|
|
2
|
-
import { spawn } from 'node:child_process';
|
|
3
|
-
import {
|
|
2
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
import { lstat } from 'node:fs/promises';
|
|
6
|
+
import { join, relative } from 'node:path';
|
|
7
|
+
import { HookNativeSessionSchema, ProjectHookVerdictSchema, } from '@cleocode/contracts/project-hooks.js';
|
|
4
8
|
import { discoveryEnv } from '../git/work-tree.js';
|
|
5
9
|
import { atomicWrite } from '../store/atomic.js';
|
|
6
10
|
import { computeHookActivation, HookInputUnavailableError, hookBindingsDigest, inspectProjectHooks, matchesProjectHook, readProjectHookRecord, readProjectHooksLocalState, readProjectHooksManifest, resolveProjectHookContext, resolveProjectHookExecutable, resolveProjectHookFile, resolveProjectHookStateFile, } from './project-state.js';
|
|
@@ -9,6 +13,78 @@ const MAX_INPUT_BYTES = 65536;
|
|
|
9
13
|
const MAX_OUTPUT_BYTES = 65536;
|
|
10
14
|
const MAX_INVOCATION_MS = 120000;
|
|
11
15
|
const REACTIVATE = 'Hook inputs changed since activation; review them, then run `cleo hook sync --activate`.';
|
|
16
|
+
/** Fixed-length per-binding slot; event text never becomes a filesystem path. */
|
|
17
|
+
function executionRecordName(binding) {
|
|
18
|
+
const hash = createHash('sha256').update(binding.event).digest('hex');
|
|
19
|
+
return `last-execution-${binding.source}-${hash}.json`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Sandbox receipt fallback (T13775): Codex workspace-write keeps `.git` read-only, so a receipt the
|
|
23
|
+
* Git-private directory refuses goes to this self-ignoring checkout directory instead.
|
|
24
|
+
*/
|
|
25
|
+
const FALLBACK_RECEIPTS = ['.cleo', 'hook-receipts'];
|
|
26
|
+
/** Whether Git tracks a checkout path; an unanswerable probe counts as tracked. */
|
|
27
|
+
function trackedByGit(context, file) {
|
|
28
|
+
return (spawnSync('git', [
|
|
29
|
+
'-C',
|
|
30
|
+
context.projectRoot,
|
|
31
|
+
'ls-files',
|
|
32
|
+
'--error-unmatch',
|
|
33
|
+
'--',
|
|
34
|
+
relative(context.projectRoot, file),
|
|
35
|
+
], { stdio: 'ignore', timeout: 5000, env: discoveryEnv() }).status !== 1);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The lexical fallback path for one slot. Every component is checked with lstat before anything
|
|
39
|
+
* resolves it, so a symlinked slot or parent can never redirect a write, and a tracked file is
|
|
40
|
+
* never a destination or a source.
|
|
41
|
+
*/
|
|
42
|
+
async function fallbackReceiptFile(context, name) {
|
|
43
|
+
if (!/^(?:[a-z0-9-]+\.json|\.gitignore)$/.test(name))
|
|
44
|
+
// @sync-invariant none:local-only Reject unsafe machine-local hook records or executable inputs; no synced rows are written.
|
|
45
|
+
throw new Error('HOOK_STATE_PATH_INVALID');
|
|
46
|
+
let path = context.projectRoot;
|
|
47
|
+
for (const part of [...FALLBACK_RECEIPTS, name]) {
|
|
48
|
+
path = join(path, part);
|
|
49
|
+
const info = await lstat(path).catch(() => undefined);
|
|
50
|
+
if (info && (info.isSymbolicLink() || (part === name ? !info.isFile() : !info.isDirectory())))
|
|
51
|
+
// @sync-invariant none:local-only Reject unsafe machine-local hook records or executable inputs; no synced rows are written.
|
|
52
|
+
throw new Error('HOOK_STATE_SYMLINK');
|
|
53
|
+
}
|
|
54
|
+
if (trackedByGit(context, path))
|
|
55
|
+
// @sync-invariant none:local-only Reject unsafe machine-local hook records or executable inputs; no synced rows are written.
|
|
56
|
+
throw new Error('HOOK_STATE_TRACKED');
|
|
57
|
+
return path;
|
|
58
|
+
}
|
|
59
|
+
/** Write one receipt slot to the Git-private directory, else the sandbox-writable fallback. */
|
|
60
|
+
async function writeReceipt(context, name, serialized) {
|
|
61
|
+
try {
|
|
62
|
+
await atomicWrite(await resolveProjectHookStateFile(context, name), serialized, {
|
|
63
|
+
mode: 0o600,
|
|
64
|
+
});
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Fall through to the checkout directory a workspace-write sandbox leaves writable.
|
|
69
|
+
}
|
|
70
|
+
await atomicWrite(await fallbackReceiptFile(context, name), serialized, { mode: 0o600 });
|
|
71
|
+
const ignore = await fallbackReceiptFile(context, '.gitignore');
|
|
72
|
+
if (!existsSync(ignore))
|
|
73
|
+
await atomicWrite(ignore, '*\n');
|
|
74
|
+
}
|
|
75
|
+
/** Resolve and read one receipt location; any fault leaves only this candidate out. */
|
|
76
|
+
async function readReceipt(locate) {
|
|
77
|
+
try {
|
|
78
|
+
const file = await locate();
|
|
79
|
+
if ((await lstat(file)).isSymbolicLink())
|
|
80
|
+
return undefined;
|
|
81
|
+
const { HookExecutionReceiptSchema } = await import('@cleocode/contracts/project-hooks.js');
|
|
82
|
+
return HookExecutionReceiptSchema.parse(JSON.parse(await readProjectHookRecord(file, MAX_OUTPUT_BYTES)));
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
12
88
|
function fault(invocation, code, id = 'cleo.project-hooks') {
|
|
13
89
|
return {
|
|
14
90
|
id,
|
|
@@ -340,18 +416,27 @@ export async function executeProjectHooks(invocation, id, activationRoot, signal
|
|
|
340
416
|
}
|
|
341
417
|
outcomes.push(await executeOne(hook, invocation, remainingMs, signal));
|
|
342
418
|
}
|
|
343
|
-
//
|
|
419
|
+
// Last-result records are bounded and contain only executor-owned metadata.
|
|
420
|
+
// Preserve the agent binding even when the subsequent Git hook records its result.
|
|
344
421
|
// A failed receipt write is visible as infrastructure fault, never a fake pass.
|
|
345
422
|
try {
|
|
423
|
+
const nativeSession = invocation.nativeSession
|
|
424
|
+
? HookNativeSessionSchema.parse(invocation.nativeSession)
|
|
425
|
+
: undefined;
|
|
346
426
|
const receipt = {
|
|
347
427
|
schemaVersion: 1,
|
|
428
|
+
...nativeSession,
|
|
348
429
|
manifestHash: approved?.manifestHash ?? '',
|
|
349
430
|
source: invocation.source,
|
|
350
431
|
event: invocation.event.slice(0, 128),
|
|
351
432
|
recordedAt: new Date().toISOString(),
|
|
352
433
|
outcomes: outcomes.map(({ message: _message, ...safe }) => safe),
|
|
353
434
|
};
|
|
354
|
-
|
|
435
|
+
const serialized = `${JSON.stringify(receipt)}\n`;
|
|
436
|
+
// Each source/event keeps its own slot, so a Git write never replaces an agent receipt.
|
|
437
|
+
await writeReceipt(inspection.context, executionRecordName(invocation), serialized);
|
|
438
|
+
// Keep the existing overall-last diagnostic API compatible.
|
|
439
|
+
await writeReceipt(inspection.context, 'last-execution.json', serialized);
|
|
355
440
|
}
|
|
356
441
|
catch {
|
|
357
442
|
outcomes.push(fault(invocation, 'HOOK_RECEIPT_WRITE_FAILED'));
|
|
@@ -368,16 +453,21 @@ export const projectHookExecutor = { execute: executeProjectHooks };
|
|
|
368
453
|
export function createProjectHookExecutor(activationRoot) {
|
|
369
454
|
return { execute: (invocation) => executeProjectHooks(invocation, undefined, activationRoot) };
|
|
370
455
|
}
|
|
371
|
-
/**
|
|
372
|
-
|
|
456
|
+
/**
|
|
457
|
+
* Read a bounded redacted execution record, validating its shape and optional binding.
|
|
458
|
+
* @param cwd - Active checkout whose private receipts are inspected.
|
|
459
|
+
* @param binding - Select a retained source/event result; omitted reads the overall latest result.
|
|
460
|
+
* @returns The matching record, or undefined when absent, malformed or mismatched.
|
|
461
|
+
*/
|
|
462
|
+
export async function readLastProjectHookExecution(cwd, binding) {
|
|
373
463
|
const inspection = await inspectProjectHooks(cwd);
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
464
|
+
const name = binding ? executionRecordName(binding) : 'last-execution.json';
|
|
465
|
+
// The newer of the Git-private record and the sandbox fallback (T13775) is the latest result.
|
|
466
|
+
const candidates = [
|
|
467
|
+
await readReceipt(() => resolveProjectHookStateFile(inspection.context, name)),
|
|
468
|
+
await readReceipt(() => fallbackReceiptFile(inspection.context, name)),
|
|
469
|
+
].filter((receipt) => receipt !== undefined &&
|
|
470
|
+
(!binding || (receipt.source === binding.source && receipt.event === binding.event)));
|
|
471
|
+
return candidates.sort((a, b) => b.recordedAt.localeCompare(a.recordedAt))[0];
|
|
382
472
|
}
|
|
383
473
|
//# sourceMappingURL=project-runner.js.map
|