@cairnkeep/cli 2.11.0 → 2.13.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/CHANGELOG.md +33 -1
- package/README.md +51 -7
- package/docs/building-an-overlay.md +1 -1
- package/docs/harness-compatibility.md +40 -1
- package/docs/learning/CURRICULUM-MAP.md +10 -7
- package/docs/learning/FEATURE-GUIDE.md +14 -5
- package/docs/learning/README.md +4 -3
- package/docs/learning/lessons/L00-why-cairnkeep.md +1 -1
- package/docs/learning/lessons/L01-safe-trial.md +3 -3
- package/docs/learning/lessons/L02-installation.md +16 -9
- package/docs/learning/lessons/L03-first-project.md +34 -15
- package/docs/learning/lessons/L13-session-evidence.md +48 -2
- package/docs/learning/lessons/L19-validated-skill-improvement.md +1 -1
- package/docs/learning/lessons/L20-mcp-tool-profiles.md +1 -1
- package/docs/learning/lessons/L21-context-packs.md +1 -1
- package/docs/learning/lessons/L22-native-windows.md +9 -9
- package/docs/learning/lessons/L23-guided-setup.md +6 -2
- package/docs/learning/tracks/evidence-and-evaluation.md +2 -1
- package/docs/learning/tracks/quickstart.md +5 -4
- package/docs/native-windows.md +23 -0
- package/docs/operating.md +69 -5
- package/docs/privacy-and-data-flow.md +44 -1
- package/docs/quickstart.md +91 -0
- package/docs/storage.md +26 -0
- package/docs/work-evidence.md +80 -0
- package/examples/eval/bundled-fake.json +1 -1
- package/mcp-memory-server/dist/artifact-cli.js +6 -2
- package/mcp-memory-server/dist/index.js +27 -0
- package/mcp-memory-server/dist/mcp-tool-catalog.js +2 -0
- package/mcp-memory-server/dist/trajectory-cli.js +9 -3
- package/mcp-memory-server/dist/work-evidence-cli.js +121 -0
- package/mcp-memory-server/dist/work-evidence-schema.js +121 -0
- package/mcp-memory-server/dist/work-evidence-store.js +516 -0
- package/mcp-memory-server/package.json +2 -1
- package/package.json +2 -1
- package/schemas/cairnkeep-setup-policy.schema.json +3 -3
- package/schemas/cairnkeep-setup.schema.json +2 -2
- package/scripts/bootstrap.sh +7 -15
- package/scripts/cairn-cli.mjs +2 -0
- package/scripts/completion.sh +19 -9
- package/scripts/doctor.sh +35 -5
- package/scripts/harness-registry.mjs +126 -0
- package/scripts/setup-core.mjs +4 -2
- package/scripts/setup-reconcile.mjs +3 -2
- package/scripts/setup.mjs +41 -5
- package/scripts/test-bootstrap-untracked.sh +2 -1
- package/scripts/test-cli-dispatch.sh +3 -1
- package/scripts/test-completion.sh +6 -0
- package/scripts/test-harness-registry.sh +94 -0
- package/scripts/test-package-install.sh +9 -0
- package/scripts/test-phase19-eval-lifecycle.sh +12 -2
- package/scripts/test-setup-compatibility.mjs +5 -4
- package/scripts/test-uninstall.sh +10 -0
- package/scripts/test-windows-native.mjs +27 -1
- package/scripts/test-work-evidence-cli.sh +89 -0
- package/scripts/uninstall.sh +7 -3
- package/scripts/verify-docs-parity.sh +36 -0
- package/scripts/windows-platform.mjs +28 -7
- package/templates/codex-config.toml.template +5 -0
- package/templates/env.example.template +10 -0
- package/templates/start-claude.sh.template +18 -0
- package/templates/start-codex.sh.template +48 -0
- package/templates/start-kimi.sh.template +17 -0
- package/templates/start-opencode.sh.template +17 -0
- package/templates/start-pi.sh.template +17 -0
- package/templates/start-qwen.sh.template +17 -0
|
@@ -336,8 +336,18 @@ const cancelTask={...tasks[0],id:"task-cancel",input:"cancel"};
|
|
|
336
336
|
const cancelPlan=makePlan([cancelTask]);
|
|
337
337
|
const cancelStore=await reports.createEvalReportStore({root:join(fixtureRoot,"reports"),experiment_id:"cancel-fixture"});
|
|
338
338
|
const controller=new AbortController();
|
|
339
|
-
|
|
340
|
-
|
|
339
|
+
const cancellation=runner.runTwoPassExperiment({plan:cancelPlan,report_store:cancelStore,temporary_root:fixtureRoot,signal:controller.signal,distill_command:{program:process.execPath,args:[distillerScript]}});
|
|
340
|
+
let adapterStarted=false;
|
|
341
|
+
for (let attempt=0; attempt<200; attempt+=1) {
|
|
342
|
+
await new Promise((resolve)=>setTimeout(resolve,25));
|
|
343
|
+
if (existsSync(invocationLog) && readFileSync(invocationLog,"utf8").includes("task-cancel:run1")) {
|
|
344
|
+
adapterStarted=true;
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
assert.equal(adapterStarted,true,"cancellation fixture adapter did not start");
|
|
349
|
+
controller.abort();
|
|
350
|
+
const cancelled=await cancellation;
|
|
341
351
|
assert.equal(cancelled.report.status,"partial");
|
|
342
352
|
assert.deepEqual(cancelled.report.observations.map(({terminal_state})=>terminal_state),["cancelled"]);
|
|
343
353
|
assert.equal(cancelled.report.observations[0].process.cleanup!=="pending",true);
|
|
@@ -18,6 +18,7 @@ import { tmpdir } from "node:os";
|
|
|
18
18
|
import { dirname, join, relative, resolve } from "node:path";
|
|
19
19
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
20
20
|
import { bootstrapWindows } from "./windows-platform.mjs";
|
|
21
|
+
import { HARNESS_IDS } from "./harness-registry.mjs";
|
|
21
22
|
|
|
22
23
|
const EXPECTED_RED_EXIT = 86;
|
|
23
24
|
const RED_MARKER = "PHASE26_RED:SETUP_RECONCILE_MISSING";
|
|
@@ -25,8 +26,8 @@ const here = dirname(fileURLToPath(import.meta.url));
|
|
|
25
26
|
const root = resolve(here, "..");
|
|
26
27
|
const setupCorePath = join(here, "setup-core.mjs");
|
|
27
28
|
const setupReconcilePath = join(here, "setup-reconcile.mjs");
|
|
28
|
-
const HARNESSES =
|
|
29
|
-
const MANAGED_ROOTS = [".ai", ".planning", ".agentfs"];
|
|
29
|
+
const HARNESSES = HARNESS_IDS;
|
|
30
|
+
const MANAGED_ROOTS = [".ai", ".codex", ".planning", ".agentfs"];
|
|
30
31
|
|
|
31
32
|
function run(executable, args, options = {}) {
|
|
32
33
|
const result = spawnSync(executable, args, {
|
|
@@ -141,9 +142,9 @@ async function testSetupCompatibility(production, sandbox, legacy) {
|
|
|
141
142
|
choices: Object.freeze({ git: "existing", harnesses: HARNESSES, memory: "local", confirmed: true }),
|
|
142
143
|
});
|
|
143
144
|
const result = await production.reconcile.reconcileSetupPlan(plan);
|
|
144
|
-
assert.deepEqual(result.counts, { created: legacy.posixBefore.size, updated: 0, unchanged: 0, skipped: 0 });
|
|
145
|
+
assert.deepEqual(result.counts, { created: legacy.posixBefore.size + 1, updated: 0, unchanged: 0, skipped: 0 });
|
|
145
146
|
const current = snapshot(target);
|
|
146
|
-
assertSnapshotEqual(current, legacy.posixBefore, "deterministic setup", [".ai/cairnkeep.json"]);
|
|
147
|
+
assertSnapshotEqual(current, legacy.posixBefore, "deterministic setup", [".ai/cairnkeep.json", ".codex/config.toml"]);
|
|
147
148
|
assert.ok(current.has(".ai/cairnkeep.json"), "setup state missing");
|
|
148
149
|
const state = JSON.parse(current.get(".ai/cairnkeep.json").bytes.toString("utf8"));
|
|
149
150
|
assert.deepEqual(state.harnesses, HARNESSES);
|
|
@@ -124,6 +124,7 @@ ADJACENT_PROJECT="$SB/adjacent-project"
|
|
|
124
124
|
mkdir -p "$PROJECT_DATA/.ai" \
|
|
125
125
|
"$PROJECT_DATA/.agentfs/eval/experiments/complete/snapshots/task-one" \
|
|
126
126
|
"$PROJECT_DATA/.agentfs/eval/experiments/partial" \
|
|
127
|
+
"$PROJECT_DATA/.agentfs/work-evidence/v1/records" \
|
|
127
128
|
"$ADJACENT_PROJECT/.agentfs/eval/experiments/adjacent"
|
|
128
129
|
printf '%s\n' '{"schema_version":1,"capabilities":{"wiki":false},"logging":{"callbacks":true}}' >"$PROJECT_DATA/.ai/capabilities.json"
|
|
129
130
|
chmod 600 "$PROJECT_DATA/.ai/capabilities.json"
|
|
@@ -132,17 +133,22 @@ printf 'artifact-v1\000durable\377bytes\n' >"$PROJECT_DATA/.agentfs/artifacts.db
|
|
|
132
133
|
printf 'session-and-callback-v1\000durable\377bytes\n' >"$PROJECT_DATA/.agentfs/trajectory.db"
|
|
133
134
|
printf 'callback-wal-v1\000durable\377bytes\n' >"$PROJECT_DATA/.agentfs/trajectory.db-wal"
|
|
134
135
|
printf 'unrelated-memory-v1\000durable\377bytes\n' >"$PROJECT_DATA/.agentfs/project.db"
|
|
136
|
+
printf 'work-evidence-v1\000durable\377bytes\n' >"$PROJECT_DATA/.agentfs/work-evidence/v1/records/wev-fixture.json"
|
|
135
137
|
printf 'complete-report-v1\000private\377bytes\n' >"$PROJECT_DATA/.agentfs/eval/experiments/complete/report.json"
|
|
136
138
|
printf 'partial-report-v1\000private\377bytes\n' >"$PROJECT_DATA/.agentfs/eval/experiments/partial/report.json"
|
|
137
139
|
printf 'note-snapshot-v1\000private\377bytes\n' >"$PROJECT_DATA/.agentfs/eval/experiments/complete/snapshots/task-one/note.md"
|
|
138
140
|
printf 'adjacent-eval-v1\000must-survive\377bytes\n' >"$ADJACENT_PROJECT/.agentfs/eval/experiments/adjacent/report.json"
|
|
139
141
|
chmod 700 "$PROJECT_DATA/.agentfs" "$PROJECT_DATA/.agentfs/eval" \
|
|
142
|
+
"$PROJECT_DATA/.agentfs/work-evidence" \
|
|
143
|
+
"$PROJECT_DATA/.agentfs/work-evidence/v1" \
|
|
144
|
+
"$PROJECT_DATA/.agentfs/work-evidence/v1/records" \
|
|
140
145
|
"$PROJECT_DATA/.agentfs/eval/experiments" \
|
|
141
146
|
"$PROJECT_DATA/.agentfs/eval/experiments/complete" \
|
|
142
147
|
"$PROJECT_DATA/.agentfs/eval/experiments/complete/snapshots" \
|
|
143
148
|
"$PROJECT_DATA/.agentfs/eval/experiments/complete/snapshots/task-one" \
|
|
144
149
|
"$PROJECT_DATA/.agentfs/eval/experiments/partial"
|
|
145
150
|
chmod 600 "$PROJECT_DATA/.agentfs/project.db" \
|
|
151
|
+
"$PROJECT_DATA/.agentfs/work-evidence/v1/records/wev-fixture.json" \
|
|
146
152
|
"$PROJECT_DATA/.agentfs/eval/experiments/complete/report.json" \
|
|
147
153
|
"$PROJECT_DATA/.agentfs/eval/experiments/partial/report.json"
|
|
148
154
|
chmod 400 "$PROJECT_DATA/.agentfs/eval/experiments/complete/snapshots/task-one/note.md"
|
|
@@ -155,6 +161,7 @@ cp "$PROJECT_DATA/.ai/operator-state.bin" "$SB/operator-state.before.bin"
|
|
|
155
161
|
cp "$PROJECT_DATA/.agentfs/artifacts.db" "$SB/artifacts.before.db"
|
|
156
162
|
cp "$PROJECT_DATA/.agentfs/trajectory.db" "$SB/trajectory.before.db"
|
|
157
163
|
cp "$PROJECT_DATA/.agentfs/trajectory.db-wal" "$SB/trajectory.before.db-wal"
|
|
164
|
+
cp "$PROJECT_DATA/.agentfs/work-evidence/v1/records/wev-fixture.json" "$SB/work-evidence.before.json"
|
|
158
165
|
agentfs_before=$(tree_identity "$PROJECT_DATA/.agentfs")
|
|
159
166
|
eval_before=$(tree_identity "$PROJECT_DATA/.agentfs/eval")
|
|
160
167
|
adjacent_before=$(tree_identity "$ADJACENT_PROJECT/.agentfs")
|
|
@@ -239,6 +246,7 @@ check "default uninstall leaves normal OpenCode installation exact" "$(diff -qr
|
|
|
239
246
|
check "default uninstall keeps unrelated .ai bytes exact" "$(cmp -s "$SB/operator-state.before.bin" "$PROJECT_DATA/.ai/operator-state.bin" && echo yes || echo no)" "yes"
|
|
240
247
|
check "default uninstall keeps callback DB exact" "$(cmp -s "$SB/trajectory.before.db" "$PROJECT_DATA/.agentfs/trajectory.db" && echo yes || echo no)" "yes"
|
|
241
248
|
check "default uninstall keeps callback WAL exact" "$(cmp -s "$SB/trajectory.before.db-wal" "$PROJECT_DATA/.agentfs/trajectory.db-wal" && echo yes || echo no)" "yes"
|
|
249
|
+
check "default uninstall keeps work evidence exact" "$(cmp -s "$SB/work-evidence.before.json" "$PROJECT_DATA/.agentfs/work-evidence/v1/records/wev-fixture.json" && echo yes || echo no)" "yes"
|
|
242
250
|
check "default uninstall keeps complete and partial eval reports, snapshots, modes, and layout exact" "$(tree_identity "$PROJECT_DATA/.agentfs/eval")" "$eval_before"
|
|
243
251
|
check "default uninstall keeps unrelated project memory exact" "$(tree_identity "$PROJECT_DATA/.agentfs")" "$agentfs_before"
|
|
244
252
|
check "default uninstall leaves adjacent project exact" "$(tree_identity "$ADJACENT_PROJECT/.agentfs")" "$adjacent_before"
|
|
@@ -271,6 +279,7 @@ ARTIFACT_BK=$(ls -dt "$ARTIFACT_HOME/.cairnkeep-uninstall-"* 2>/dev/null | head
|
|
|
271
279
|
check "artifact purge backup is exact" "$(cmp -s "$SB/artifacts.before.db" "$ARTIFACT_BK/files/${PROJECT_DATA#/}/.agentfs/artifacts.db" && echo yes || echo no)" "yes"
|
|
272
280
|
check "callback purge backup is exact" "$(cmp -s "$SB/trajectory.before.db" "$ARTIFACT_BK/files/${PROJECT_DATA#/}/.agentfs/trajectory.db" && echo yes || echo no)" "yes"
|
|
273
281
|
check "callback WAL purge backup is exact" "$(cmp -s "$SB/trajectory.before.db-wal" "$ARTIFACT_BK/files/${PROJECT_DATA#/}/.agentfs/trajectory.db-wal" && echo yes || echo no)" "yes"
|
|
282
|
+
check "work-evidence purge backup is exact" "$(cmp -s "$SB/work-evidence.before.json" "$ARTIFACT_BK/files/${PROJECT_DATA#/}/.agentfs/work-evidence/v1/records/wev-fixture.json" && echo yes || echo no)" "yes"
|
|
274
283
|
check "purge backup contains exact eval tree, bytes, and modes" "$(tree_identity "$ARTIFACT_BK/files/${PROJECT_DATA#/}/.agentfs/eval")" "$eval_before"
|
|
275
284
|
check "purge backup contains exact unrelated project memory" "$(tree_identity "$ARTIFACT_BK/files/${PROJECT_DATA#/}/.agentfs")" "$agentfs_before"
|
|
276
285
|
check "selected-project purge leaves adjacent project exact" "$(tree_identity "$ADJACENT_PROJECT/.agentfs")" "$adjacent_before"
|
|
@@ -278,6 +287,7 @@ HOME="$ARTIFACT_HOME" XDG_CONFIG_HOME="$ARTIFACT_HOME/.config" bash "$ARTIFACT_B
|
|
|
278
287
|
check "artifact revert restores exact bytes" "$(cmp -s "$SB/artifacts.before.db" "$PROJECT_DATA/.agentfs/artifacts.db" && echo yes || echo no)" "yes"
|
|
279
288
|
check "callback revert restores exact bytes" "$(cmp -s "$SB/trajectory.before.db" "$PROJECT_DATA/.agentfs/trajectory.db" && echo yes || echo no)" "yes"
|
|
280
289
|
check "callback WAL revert restores exact bytes" "$(cmp -s "$SB/trajectory.before.db-wal" "$PROJECT_DATA/.agentfs/trajectory.db-wal" && echo yes || echo no)" "yes"
|
|
290
|
+
check "work-evidence revert restores exact bytes" "$(cmp -s "$SB/work-evidence.before.json" "$PROJECT_DATA/.agentfs/work-evidence/v1/records/wev-fixture.json" && echo yes || echo no)" "yes"
|
|
281
291
|
check "purge revert restores exact eval tree, bytes, and modes" "$(tree_identity "$PROJECT_DATA/.agentfs/eval")" "$eval_before"
|
|
282
292
|
check "purge revert restores exact unrelated project memory" "$(tree_identity "$PROJECT_DATA/.agentfs")" "$agentfs_before"
|
|
283
293
|
check "purge revert leaves adjacent project exact" "$(tree_identity "$ADJACENT_PROJECT/.agentfs")" "$adjacent_before"
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
runWindowsCommand,
|
|
17
17
|
} from "./windows-platform.mjs";
|
|
18
18
|
import { runNativeContainer } from "./cairn-container-cli.mjs";
|
|
19
|
+
import { HARNESS_IDS } from "./harness-registry.mjs";
|
|
19
20
|
|
|
20
21
|
const root = resolve(fileURLToPath(new URL("..", import.meta.url)));
|
|
21
22
|
const sandbox = mkdtempSync(join(tmpdir(), "cairn-windows-native-"));
|
|
@@ -28,13 +29,15 @@ try {
|
|
|
28
29
|
const project = join(sandbox, "Project with spaces – Unicode");
|
|
29
30
|
mkdirSync(project, { recursive: true });
|
|
30
31
|
bootstrapWindows(root, [project]);
|
|
31
|
-
for (const harness of
|
|
32
|
+
for (const harness of HARNESS_IDS) {
|
|
32
33
|
assert.ok(existsSync(join(project, ".ai", `start-${harness}.cmd`)), `${harness} cmd launcher`);
|
|
33
34
|
}
|
|
34
35
|
assert.match(readFileSync(join(project, ".ai", "start-claude.cmd"), "utf8"), /start-harness\.ps1/);
|
|
35
36
|
assert.match(launcherModule(), /spawnSync\(command/);
|
|
36
37
|
assert.match(launcherPowerShell(), /pre-launch\.ps1/);
|
|
37
38
|
assert.match(launcherPowerShell(), /post-exit\.ps1/);
|
|
39
|
+
assert.match(launcherPowerShell(), /evidence run --harness/);
|
|
40
|
+
assert.match(launcherPowerShell(), /CAIRN_WORK_EVIDENCE/);
|
|
38
41
|
|
|
39
42
|
const claudeRoot = join(sandbox, "Claude Config");
|
|
40
43
|
await runWindowsCommand({ command: "sync", args: ["--apply", "--live-root", claudeRoot], root });
|
|
@@ -55,6 +58,7 @@ try {
|
|
|
55
58
|
await runWindowsCommand({ command: "memory", args: ["import", archive], root });
|
|
56
59
|
assert.equal(readFileSync(join(memory, "project.db"), "utf8"), "sqlite-a");
|
|
57
60
|
assert.match(powershellCompletion(), /Register-ArgumentCompleter/);
|
|
61
|
+
assert.match(powershellCompletion(), /evidence/);
|
|
58
62
|
|
|
59
63
|
let containerArgs = [];
|
|
60
64
|
runNativeContainer(["stdio", "--image", "example/windows:1", "--volume", "windows-data"], root, (_engine, args) => { containerArgs = args; });
|
|
@@ -125,6 +129,28 @@ try {
|
|
|
125
129
|
assert.match(powershellCompletion(), /--policy/);
|
|
126
130
|
assert.match(powershellCompletion(), /--yes/);
|
|
127
131
|
assert.match(powershellCompletion(), /--json/);
|
|
132
|
+
assert.match(powershellCompletion(), /codex/);
|
|
133
|
+
|
|
134
|
+
const codexProject = join(sandbox, "Guided Codex Project");
|
|
135
|
+
let codexOutput = "";
|
|
136
|
+
process.stdout.write = ((chunk) => {
|
|
137
|
+
codexOutput += String(chunk);
|
|
138
|
+
return true;
|
|
139
|
+
});
|
|
140
|
+
try {
|
|
141
|
+
await runWindowsCommand({
|
|
142
|
+
command: "setup",
|
|
143
|
+
args: [codexProject, "--git", "none", "--harness", "codex", "--memory", "local", "--yes", "--json"],
|
|
144
|
+
root,
|
|
145
|
+
});
|
|
146
|
+
} finally {
|
|
147
|
+
process.stdout.write = originalWrite;
|
|
148
|
+
}
|
|
149
|
+
const codexResult = JSON.parse(codexOutput);
|
|
150
|
+
assert.deepEqual(codexResult.harnesses, ["codex"]);
|
|
151
|
+
assert.equal(codexResult.machine_sync.command, null);
|
|
152
|
+
assert.ok(existsSync(join(codexProject, ".ai", "start-codex.cmd")));
|
|
153
|
+
assert.match(readFileSync(join(codexProject, ".codex", "config.toml"), "utf8"), /mcp_servers\.cairn-memory/);
|
|
128
154
|
console.log("PASS: simulated Windows setup parity, private state, recovery, and completion contract");
|
|
129
155
|
}
|
|
130
156
|
} finally {
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
5
|
+
tmp=$(mktemp -d)
|
|
6
|
+
trap 'rm -rf "$tmp"' EXIT
|
|
7
|
+
fail() { echo "FAIL: $1" >&2; exit 1; }
|
|
8
|
+
|
|
9
|
+
npm --prefix "$ROOT/mcp-memory-server" run build >/dev/null
|
|
10
|
+
|
|
11
|
+
fake_bin="$tmp/bin"
|
|
12
|
+
mkdir -p "$fake_bin"
|
|
13
|
+
cat >"$fake_bin/cairn" <<EOF
|
|
14
|
+
#!/usr/bin/env bash
|
|
15
|
+
exec node "$ROOT/bin/cairn" "\$@"
|
|
16
|
+
EOF
|
|
17
|
+
chmod +x "$fake_bin/cairn"
|
|
18
|
+
|
|
19
|
+
for harness in claude opencode pi kimi qwen codex; do
|
|
20
|
+
project="$tmp/$harness"
|
|
21
|
+
mkdir -p "$project/.ai" "$project/.agentfs"
|
|
22
|
+
cp "$ROOT/templates/start-$harness.sh.template" "$project/.ai/start-$harness.sh"
|
|
23
|
+
chmod +x "$project/.ai/start-$harness.sh"
|
|
24
|
+
printf '*\n!.gitignore\n' >"$project/.agentfs/.gitignore"
|
|
25
|
+
printf 'CAIRN_WORK_EVIDENCE=1\n' >"$project/.ai/.env"
|
|
26
|
+
cat >"$fake_bin/$harness" <<EOF
|
|
27
|
+
#!/usr/bin/env bash
|
|
28
|
+
printf '%s\n' "\$*" > "$project/args.txt"
|
|
29
|
+
printf '%s\n' changed > "$project/session.txt"
|
|
30
|
+
exit 17
|
|
31
|
+
EOF
|
|
32
|
+
chmod +x "$fake_bin/$harness"
|
|
33
|
+
cat >"$project/.ai/post-exit.sh" <<EOF
|
|
34
|
+
printf '%s\n' "\$CAIRN_EXIT_STATUS" > "$project/post-status.txt"
|
|
35
|
+
EOF
|
|
36
|
+
(
|
|
37
|
+
cd "$project"
|
|
38
|
+
git init --quiet
|
|
39
|
+
git config user.email fixture@example.invalid
|
|
40
|
+
git config user.name Fixture
|
|
41
|
+
printf '%s\n' start > session.txt
|
|
42
|
+
git add session.txt .agentfs/.gitignore
|
|
43
|
+
git commit --quiet -m start
|
|
44
|
+
)
|
|
45
|
+
status=0
|
|
46
|
+
PATH="$fake_bin:$PATH" "$project/.ai/start-$harness.sh" --fixture "value with spaces" || status=$?
|
|
47
|
+
[[ $status -eq 17 ]] || fail "$harness launcher lost exit status"
|
|
48
|
+
grep -q -- '--fixture value with spaces' "$project/args.txt" || fail "$harness launcher lost arguments"
|
|
49
|
+
[[ $(cat "$project/post-status.txt") == 17 ]] || fail "$harness post-exit seam lost status"
|
|
50
|
+
(cd "$project" && PATH="$fake_bin:$PATH" "$ROOT/bin/cairn" evidence list --json) >"$tmp/$harness.json"
|
|
51
|
+
node -e '
|
|
52
|
+
const value=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));
|
|
53
|
+
if(value.evidence.length!==1||value.evidence[0].status!=="complete"||value.evidence[0].harness!==process.argv[2]||value.evidence[0].exit_status!==17)process.exit(1);
|
|
54
|
+
if(!value.evidence[0].touched_paths.includes("session.txt"))process.exit(1);
|
|
55
|
+
' "$tmp/$harness.json" "$harness" || fail "$harness evidence record is incomplete"
|
|
56
|
+
done
|
|
57
|
+
|
|
58
|
+
disabled="$tmp/disabled"
|
|
59
|
+
mkdir -p "$disabled/.ai"
|
|
60
|
+
cp "$ROOT/templates/start-codex.sh.template" "$disabled/.ai/start-codex.sh"
|
|
61
|
+
chmod +x "$disabled/.ai/start-codex.sh"
|
|
62
|
+
cat >"$fake_bin/cairn" <<'EOF'
|
|
63
|
+
#!/usr/bin/env bash
|
|
64
|
+
exit 88
|
|
65
|
+
EOF
|
|
66
|
+
cat >"$fake_bin/codex" <<'EOF'
|
|
67
|
+
#!/usr/bin/env bash
|
|
68
|
+
exit 0
|
|
69
|
+
EOF
|
|
70
|
+
chmod +x "$fake_bin/cairn" "$fake_bin/codex"
|
|
71
|
+
PATH="$fake_bin:$PATH" "$disabled/.ai/start-codex.sh" || fail "disabled launcher called the evidence wrapper"
|
|
72
|
+
[[ ! -e "$disabled/.agentfs/work-evidence" ]] || fail "disabled launcher created evidence storage"
|
|
73
|
+
|
|
74
|
+
cat >"$fake_bin/codex" <<EOF
|
|
75
|
+
#!/usr/bin/env bash
|
|
76
|
+
printf launched >"$tmp/old-cairn-fallback"
|
|
77
|
+
exit 19
|
|
78
|
+
EOF
|
|
79
|
+
chmod +x "$fake_bin/codex"
|
|
80
|
+
status=0
|
|
81
|
+
CAIRN_WORK_EVIDENCE=1 PATH="$fake_bin:$PATH" "$disabled/.ai/start-codex.sh" 2>"$tmp/old-cairn.err" || status=$?
|
|
82
|
+
[[ $status -eq 19 ]] || fail "old cairn fallback lost the harness exit status"
|
|
83
|
+
[[ -f "$tmp/old-cairn-fallback" ]] || fail "old cairn fallback did not launch the harness"
|
|
84
|
+
grep -q 'compatible evidence command unavailable' "$tmp/old-cairn.err" || fail "old cairn fallback did not explain capture loss"
|
|
85
|
+
|
|
86
|
+
"$ROOT/bin/cairn" help | grep -q 'cairn evidence' || fail "root help omits evidence"
|
|
87
|
+
node "$ROOT/mcp-memory-server/dist/work-evidence-cli.js" --help | grep -q 'cairn evidence list' || fail "evidence help omits list"
|
|
88
|
+
|
|
89
|
+
echo "PASS: work-evidence CLI and all harness launchers preserve lifecycle semantics"
|
package/scripts/uninstall.sh
CHANGED
|
@@ -45,6 +45,7 @@ PROJECT_AI_FILES=(
|
|
|
45
45
|
start-pi.sh
|
|
46
46
|
start-kimi.sh
|
|
47
47
|
start-qwen.sh
|
|
48
|
+
start-codex.sh
|
|
48
49
|
env.example
|
|
49
50
|
trajectory-redaction.json
|
|
50
51
|
capabilities.json
|
|
@@ -129,9 +130,11 @@ remove_path() {
|
|
|
129
130
|
# record. The record identifies candidates, but never grants authority to an
|
|
130
131
|
# arbitrary path. Modified candidates are still backed up before removal.
|
|
131
132
|
setup_owned_paths() {
|
|
132
|
-
node --input-type=module - "$1" <<'NODE'
|
|
133
|
+
node --input-type=module - "$1" "$CAIRN_ROOT/scripts/harness-registry.mjs" <<'NODE'
|
|
133
134
|
import { lstatSync, readFileSync } from "node:fs";
|
|
135
|
+
import { pathToFileURL } from "node:url";
|
|
134
136
|
const path = process.argv[2];
|
|
137
|
+
const registry = await import(pathToFileURL(process.argv[3]).href);
|
|
135
138
|
const info = lstatSync(path);
|
|
136
139
|
if (!info.isFile() || info.isSymbolicLink() || info.size > 1024 * 1024) process.exit(1);
|
|
137
140
|
if (process.platform !== "win32" && (info.mode & 0o777) !== 0o600) process.exit(1);
|
|
@@ -153,7 +156,8 @@ const allowed = new Set([
|
|
|
153
156
|
".planning/wiki/LOG.md", ".planning/alignment/policy.md", ".planning/alignment/gap-register.yaml",
|
|
154
157
|
".planning/graphs/policy.md", ".planning/graphs/.gitignore", ".planning/security/policy.md",
|
|
155
158
|
".ai/start-harness.mjs", ".ai/start-harness.ps1",
|
|
156
|
-
...
|
|
159
|
+
...registry.HARNESS_IDS.flatMap((name) => [`.ai/start-${name}.sh`, `.ai/start-${name}.cmd`]),
|
|
160
|
+
...registry.HARNESS_IDS.flatMap((name) => registry.harnessProjectAssets(name, "local").map(({ path }) => path)),
|
|
157
161
|
]);
|
|
158
162
|
for (const [asset, record] of Object.entries(state.assets)) {
|
|
159
163
|
if (!allowed.has(asset)
|
|
@@ -321,7 +325,7 @@ for proj in "${PROJECTS[@]:-}"; do
|
|
|
321
325
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
322
326
|
for managed_dir in \
|
|
323
327
|
"$proj/.planning/wiki" "$proj/.planning/alignment" "$proj/.planning/graphs" \
|
|
324
|
-
"$proj/.planning/security" "$proj/.planning" "$proj/.ai" "$proj/.agentfs"; do
|
|
328
|
+
"$proj/.planning/security" "$proj/.planning" "$proj/.codex" "$proj/.ai" "$proj/.agentfs"; do
|
|
325
329
|
rmdir "$managed_dir" 2>/dev/null || true
|
|
326
330
|
done
|
|
327
331
|
fi
|
|
@@ -432,6 +432,41 @@ check_artifact_remote_path_contract() {
|
|
|
432
432
|
return "$failed"
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
+
check_work_evidence_contract() {
|
|
436
|
+
local failed=0 key value source_term
|
|
437
|
+
|
|
438
|
+
while IFS='|' read -r key value source_term; do
|
|
439
|
+
[[ -n "$key" ]] || continue
|
|
440
|
+
grep -qF "$key" mcp-memory-server/src/work-evidence-schema.ts || failed=1
|
|
441
|
+
grep -qF "$source_term" mcp-memory-server/src/work-evidence-schema.ts || failed=1
|
|
442
|
+
grep -qxF "# $key=$value" templates/env.example.template || failed=1
|
|
443
|
+
grep -qF "$key" README.md docs/operating.md docs/work-evidence.md || failed=1
|
|
444
|
+
grep -qF "$value" README.md docs/operating.md docs/work-evidence.md || failed=1
|
|
445
|
+
done <<'EOF'
|
|
446
|
+
CAIRN_WORK_EVIDENCE_RETENTION_DAYS|30|WORK_EVIDENCE_DEFAULT_RETENTION_DAYS = 30
|
|
447
|
+
CAIRN_WORK_EVIDENCE_STORE_MAX_BYTES|67108864|WORK_EVIDENCE_DEFAULT_STORE_MAX_BYTES = 64 * 1024 * 1024
|
|
448
|
+
CAIRN_WORK_EVIDENCE_MAX_TOUCHED_PATHS|4096|WORK_EVIDENCE_DEFAULT_MAX_TOUCHED_PATHS = 4096
|
|
449
|
+
CAIRN_WORK_EVIDENCE_PATCH_MAX_BYTES|1048576|WORK_EVIDENCE_DEFAULT_PATCH_MAX_BYTES = 1024 * 1024
|
|
450
|
+
EOF
|
|
451
|
+
|
|
452
|
+
for key in CAIRN_WORK_EVIDENCE CAIRN_WORK_EVIDENCE_PATCH; do
|
|
453
|
+
grep -qF "$key" mcp-memory-server/src/work-evidence-schema.ts templates/env.example.template README.md docs/operating.md docs/privacy-and-data-flow.md || failed=1
|
|
454
|
+
done
|
|
455
|
+
for source_term in work_evidence_list work_evidence_read; do
|
|
456
|
+
grep -qF "\"$source_term\"" mcp-memory-server/src/index.ts || failed=1
|
|
457
|
+
grep -qF "$source_term" README.md docs/operating.md docs/privacy-and-data-flow.md docs/work-evidence.md || failed=1
|
|
458
|
+
done
|
|
459
|
+
for source_term in 'list [--status pending|complete] [--json]' 'show <evidence-id-or-prefix> [--json]' 'delete <evidence-id-or-prefix> [--dry-run] [--json]' 'prune [--dry-run] [--json]' 'doctor [--repair] [--json]'; do
|
|
460
|
+
grep -qF "$source_term" mcp-memory-server/src/work-evidence-cli.ts || failed=1
|
|
461
|
+
done
|
|
462
|
+
grep -qF '.agentfs/work-evidence/v1/' docs/storage.md docs/privacy-and-data-flow.md docs/work-evidence.md || failed=1
|
|
463
|
+
grep -qF 'cairn evidence' docs/learning/CURRICULUM-MAP.md docs/learning/FEATURE-GUIDE.md docs/learning/lessons/L13-session-evidence.md || failed=1
|
|
464
|
+
grep -qF 'never exposed by the HTTP transport' docs/work-evidence.md || failed=1
|
|
465
|
+
|
|
466
|
+
[[ "$failed" -eq 0 ]] && echo "[work-evidence-contract] OK: flags/defaults/tools/storage/privacy/learning match source"
|
|
467
|
+
return "$failed"
|
|
468
|
+
}
|
|
469
|
+
|
|
435
470
|
check_capability_contract() {
|
|
436
471
|
local failed=0 file id env tool term source_file docs_files
|
|
437
472
|
|
|
@@ -624,6 +659,7 @@ main() {
|
|
|
624
659
|
check_typed_contract || failed=1
|
|
625
660
|
check_artifact_contract || failed=1
|
|
626
661
|
check_artifact_remote_path_contract || failed=1
|
|
662
|
+
check_work_evidence_contract || failed=1
|
|
627
663
|
check_capability_contract || failed=1
|
|
628
664
|
check_native_capability_docs || failed=1
|
|
629
665
|
|
|
@@ -20,6 +20,7 @@ import { gzipSync, gunzipSync } from "node:zlib";
|
|
|
20
20
|
import { spawnSync } from "node:child_process";
|
|
21
21
|
import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
|
|
22
22
|
import { homedir, tmpdir } from "node:os";
|
|
23
|
+
import { HARNESS_IDS, harnessProjectAssets } from "./harness-registry.mjs";
|
|
23
24
|
|
|
24
25
|
const BOOTSTRAP_FILES = [
|
|
25
26
|
["env.example.template", ".ai/env.example"],
|
|
@@ -39,7 +40,7 @@ const BOOTSTRAP_FILES = [
|
|
|
39
40
|
["security-policy.md.template", ".planning/security/policy.md"],
|
|
40
41
|
];
|
|
41
42
|
|
|
42
|
-
const HARNESSES =
|
|
43
|
+
const HARNESSES = HARNESS_IDS;
|
|
43
44
|
const HOOK_EVENTS = new Map([
|
|
44
45
|
["memory-wakeup", ["SessionStart", ""]],
|
|
45
46
|
["memory-capture", ["SessionEnd", ""]],
|
|
@@ -189,7 +190,19 @@ if ($env:CAIRN_EXTRA_SETTINGS -and $Harness -eq 'claude') {
|
|
|
189
190
|
if ($env:CAIRN_EXTRA_SETTINGS -and $Harness -eq 'opencode') { $env:OPENCODE_CONFIG = $env:CAIRN_EXTRA_SETTINGS }
|
|
190
191
|
Push-Location $ProjectRoot
|
|
191
192
|
try {
|
|
192
|
-
|
|
193
|
+
$EvidenceEnabled = $env:CAIRN_WORK_EVIDENCE -match '^(1|true|yes|on)$'
|
|
194
|
+
$CairnCommand = if ($EvidenceEnabled) { Get-Command cairn -ErrorAction SilentlyContinue } else { $null }
|
|
195
|
+
$EvidenceSupported = $false
|
|
196
|
+
if ($EvidenceEnabled -and $CairnCommand) {
|
|
197
|
+
& $CairnCommand.Source evidence --help *> $null
|
|
198
|
+
$EvidenceSupported = ($LASTEXITCODE -eq 0)
|
|
199
|
+
}
|
|
200
|
+
if ($EvidenceSupported) {
|
|
201
|
+
& $CairnCommand.Source evidence run --harness $Harness -- $Harness @HarnessArgs
|
|
202
|
+
} else {
|
|
203
|
+
if ($EvidenceEnabled) { Write-Warning 'a compatible cairn evidence command is unavailable; launching without capture' }
|
|
204
|
+
& $Harness @HarnessArgs
|
|
205
|
+
}
|
|
193
206
|
$Status = if ($null -eq $LASTEXITCODE) { 0 } else { $LASTEXITCODE }
|
|
194
207
|
} finally {
|
|
195
208
|
Pop-Location
|
|
@@ -249,7 +262,8 @@ export function bootstrapWindows(root, args) {
|
|
|
249
262
|
installFile(join(templates, source), join(target, ...destination.split("/")), destination.endsWith("capabilities.json") ? 0o600 : 0o644);
|
|
250
263
|
}
|
|
251
264
|
for (const harness of HARNESSES) {
|
|
252
|
-
|
|
265
|
+
const asset = harnessProjectAssets(harness, "local")[0];
|
|
266
|
+
installFile(join(templates, asset.template), join(target, ...asset.path.split("/")), asset.mode);
|
|
253
267
|
const cmdPath = join(target, ".ai", `start-${harness}.cmd`);
|
|
254
268
|
if (!existsSync(cmdPath)) atomicWrite(cmdPath, windowsLauncher(harness), 0o755);
|
|
255
269
|
}
|
|
@@ -267,7 +281,9 @@ export function bootstrapWindows(root, args) {
|
|
|
267
281
|
}
|
|
268
282
|
|
|
269
283
|
console.log(`\nCairnkeep bootstrapped into ${target}`);
|
|
270
|
-
console.log(`
|
|
284
|
+
console.log(`Codex: run 'cairn setup' in the project and select Codex, review trust, then use ${join(target, ".ai", "start-codex.cmd")}`);
|
|
285
|
+
console.log(`Claude: register 'cairn memory-server', run 'cairn sync --apply', then use ${join(target, ".ai", "start-claude.cmd")}`);
|
|
286
|
+
console.log("For new projects, prefer 'cairn setup'; guide: docs/quickstart.md");
|
|
271
287
|
}
|
|
272
288
|
|
|
273
289
|
function walkFiles(root, predicate = () => true, prefix = "") {
|
|
@@ -436,6 +452,7 @@ async function doctorWindows(root, args) {
|
|
|
436
452
|
for (const [label, file, doctorArgs] of [
|
|
437
453
|
["trajectory", "trajectory-cli.js", ["doctor", "--json", ...(repair ? ["--repair"] : [])]],
|
|
438
454
|
["artifact", "artifact-cli.js", ["doctor", "--json", ...(repair ? ["--repair"] : [])]],
|
|
455
|
+
["work evidence", "work-evidence-cli.js", ["doctor", "--json", ...(repair ? ["--repair"] : [])]],
|
|
439
456
|
["typed memory", "node-cli.js", ["doctor", "--project-root", process.cwd(), ...(repair ? ["--repair"] : [])]],
|
|
440
457
|
["capability", "capability-cli.js", ["doctor", "--json"]],
|
|
441
458
|
["context pack", "context-pack-cli.js", ["doctor", "--json"]],
|
|
@@ -616,7 +633,10 @@ function uninstallWindows(root, args) {
|
|
|
616
633
|
for (const project of projects) {
|
|
617
634
|
const base = resolve(project);
|
|
618
635
|
for (const [, rel] of BOOTSTRAP_FILES) targets.push(join(base, ...rel.split("/")));
|
|
619
|
-
for (const harness of HARNESSES)
|
|
636
|
+
for (const harness of HARNESSES) {
|
|
637
|
+
for (const asset of harnessProjectAssets(harness, "local")) targets.push(join(base, ...asset.path.split("/")));
|
|
638
|
+
targets.push(join(base, ".ai", `start-${harness}.cmd`));
|
|
639
|
+
}
|
|
620
640
|
targets.push(join(base, ".ai", "start-harness.mjs"));
|
|
621
641
|
targets.push(join(base, ".ai", "start-harness.ps1"));
|
|
622
642
|
if (options.has("--purge-memory")) targets.push(join(base, ".agentfs"));
|
|
@@ -685,10 +705,11 @@ foreach ($Item in $Manifest.items) {
|
|
|
685
705
|
}
|
|
686
706
|
|
|
687
707
|
export function powershellCompletion() {
|
|
708
|
+
const harnesses = HARNESS_IDS.map((id) => `'${id}'`).join(",");
|
|
688
709
|
return `Register-ArgumentCompleter -Native -CommandName cairn -ScriptBlock {
|
|
689
710
|
param($wordToComplete, $commandAst, $cursorPosition)
|
|
690
|
-
$commands = 'bootstrap','setup','memory-server','sync','sync-pi','sync-kimi','doctor','trajectory','artifact','capabilities','mcp-tools','pack','notes','eval','skill','graph','memory','audit-timer','uninstall','completion','version','help'
|
|
691
|
-
$setup = '--git','--harness','--memory','--policy','--yes','--json','init','existing','none','
|
|
711
|
+
$commands = 'bootstrap','setup','memory-server','sync','sync-pi','sync-kimi','doctor','trajectory','artifact','evidence','capabilities','mcp-tools','pack','notes','eval','skill','graph','memory','audit-timer','uninstall','completion','version','help'
|
|
712
|
+
$setup = '--git','--harness','--memory','--policy','--yes','--json','init','existing','none',${harnesses},'local'
|
|
692
713
|
$candidates = if ($commandAst.ToString() -match '^\\s*cairn\\s+setup(?:\\s|$)') { $setup } else { $commands }
|
|
693
714
|
$candidates | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
694
715
|
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
@@ -65,6 +65,16 @@
|
|
|
65
65
|
# Bootstrap creates .ai/trajectory-redaction.json for optional custom patterns.
|
|
66
66
|
# CAIRN_TRAJECTORY_REDACTION_FILE=.ai/trajectory-redaction.json
|
|
67
67
|
#
|
|
68
|
+
# Git-linked work evidence is local and OFF by default. Generated harness
|
|
69
|
+
# launchers capture bounded Git state before and after a session. Optional patch
|
|
70
|
+
# capture also requires the artifact store; no patch is ever applied by Cairnkeep.
|
|
71
|
+
# CAIRN_WORK_EVIDENCE=1
|
|
72
|
+
# CAIRN_WORK_EVIDENCE_PATCH=1
|
|
73
|
+
# CAIRN_WORK_EVIDENCE_RETENTION_DAYS=30
|
|
74
|
+
# CAIRN_WORK_EVIDENCE_STORE_MAX_BYTES=67108864
|
|
75
|
+
# CAIRN_WORK_EVIDENCE_MAX_TOUCHED_PATHS=4096
|
|
76
|
+
# CAIRN_WORK_EVIDENCE_PATCH_MAX_BYTES=1048576
|
|
77
|
+
#
|
|
68
78
|
# Harness-produced compaction continuity and explicit artifact access are local
|
|
69
79
|
# and OFF by default. Either capability may be enabled independently; neither
|
|
70
80
|
# needs a model, API key, or network endpoint. HTTP artifact tools require the
|
|
@@ -68,6 +68,24 @@ if [[ -n "${CAIRN_EXTRA_SETTINGS:-}" ]]; then
|
|
|
68
68
|
launch_args+=(--settings "$CAIRN_EXTRA_SETTINGS")
|
|
69
69
|
fi
|
|
70
70
|
|
|
71
|
+
case "${CAIRN_WORK_EVIDENCE:-}" in
|
|
72
|
+
1|true|TRUE|yes|YES|on|ON)
|
|
73
|
+
if command -v cairn >/dev/null 2>&1 && cairn evidence --help >/dev/null 2>&1; then
|
|
74
|
+
set +e
|
|
75
|
+
cairn evidence run --harness claude -- claude "${launch_args[@]}" "$@"
|
|
76
|
+
CAIRN_EXIT_STATUS=$?
|
|
77
|
+
set -e
|
|
78
|
+
export CAIRN_EXIT_STATUS
|
|
79
|
+
if [[ -f "$ROOT_DIR/.ai/post-exit.sh" ]]; then
|
|
80
|
+
# shellcheck disable=SC1091
|
|
81
|
+
. "$ROOT_DIR/.ai/post-exit.sh" || true
|
|
82
|
+
fi
|
|
83
|
+
exit "$CAIRN_EXIT_STATUS"
|
|
84
|
+
fi
|
|
85
|
+
echo "cairn: compatible evidence command unavailable; launching without capture" >&2
|
|
86
|
+
;;
|
|
87
|
+
esac
|
|
88
|
+
|
|
71
89
|
if [[ -f "$ROOT_DIR/.ai/post-exit.sh" ]]; then
|
|
72
90
|
# Run (not exec) so the post-exit hook fires after the harness returns.
|
|
73
91
|
set +e
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Cairnkeep launcher for Codex CLI. Loads this project's private .ai/.env and
|
|
3
|
+
# starts Codex in the project root. The project-scoped MCP registration lives in
|
|
4
|
+
# .codex/config.toml and is only loaded after Codex trusts the project.
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
7
|
+
|
|
8
|
+
if [[ -f "$ROOT_DIR/.ai/.env" ]]; then
|
|
9
|
+
set -a
|
|
10
|
+
# shellcheck disable=SC1091
|
|
11
|
+
. "$ROOT_DIR/.ai/.env"
|
|
12
|
+
set +a
|
|
13
|
+
fi
|
|
14
|
+
if [[ -f "$ROOT_DIR/.ai/pre-launch.sh" ]]; then
|
|
15
|
+
# shellcheck disable=SC1091
|
|
16
|
+
. "$ROOT_DIR/.ai/pre-launch.sh"
|
|
17
|
+
fi
|
|
18
|
+
cd "$ROOT_DIR"
|
|
19
|
+
|
|
20
|
+
case "${CAIRN_WORK_EVIDENCE:-}" in
|
|
21
|
+
1|true|TRUE|yes|YES|on|ON)
|
|
22
|
+
if command -v cairn >/dev/null 2>&1 && cairn evidence --help >/dev/null 2>&1; then
|
|
23
|
+
set +e
|
|
24
|
+
cairn evidence run --harness codex -- codex "$@"
|
|
25
|
+
CAIRN_EXIT_STATUS=$?
|
|
26
|
+
set -e
|
|
27
|
+
export CAIRN_EXIT_STATUS
|
|
28
|
+
if [[ -f "$ROOT_DIR/.ai/post-exit.sh" ]]; then
|
|
29
|
+
# shellcheck disable=SC1091
|
|
30
|
+
. "$ROOT_DIR/.ai/post-exit.sh" || true
|
|
31
|
+
fi
|
|
32
|
+
exit "$CAIRN_EXIT_STATUS"
|
|
33
|
+
fi
|
|
34
|
+
echo "cairn: compatible evidence command unavailable; launching without capture" >&2
|
|
35
|
+
;;
|
|
36
|
+
esac
|
|
37
|
+
|
|
38
|
+
if [[ -f "$ROOT_DIR/.ai/post-exit.sh" ]]; then
|
|
39
|
+
set +e
|
|
40
|
+
codex "$@"
|
|
41
|
+
CAIRN_EXIT_STATUS=$?
|
|
42
|
+
set -e
|
|
43
|
+
export CAIRN_EXIT_STATUS
|
|
44
|
+
# shellcheck disable=SC1091
|
|
45
|
+
. "$ROOT_DIR/.ai/post-exit.sh" || true
|
|
46
|
+
exit "$CAIRN_EXIT_STATUS"
|
|
47
|
+
fi
|
|
48
|
+
exec codex "$@"
|
|
@@ -26,6 +26,23 @@ fi
|
|
|
26
26
|
|
|
27
27
|
cd "$ROOT_DIR"
|
|
28
28
|
|
|
29
|
+
case "${CAIRN_WORK_EVIDENCE:-}" in
|
|
30
|
+
1|true|TRUE|yes|YES|on|ON)
|
|
31
|
+
if command -v cairn >/dev/null 2>&1 && cairn evidence --help >/dev/null 2>&1; then
|
|
32
|
+
set +e
|
|
33
|
+
cairn evidence run --harness kimi -- kimi "$@"
|
|
34
|
+
CAIRN_EXIT_STATUS=$?
|
|
35
|
+
set -e
|
|
36
|
+
export CAIRN_EXIT_STATUS
|
|
37
|
+
if [[ -f "$ROOT_DIR/.ai/post-exit.sh" ]]; then
|
|
38
|
+
# shellcheck disable=SC1091
|
|
39
|
+
. "$ROOT_DIR/.ai/post-exit.sh" || true
|
|
40
|
+
fi
|
|
41
|
+
exit "$CAIRN_EXIT_STATUS"
|
|
42
|
+
fi
|
|
43
|
+
echo "cairn: compatible evidence command unavailable; launching without capture" >&2
|
|
44
|
+
;;
|
|
45
|
+
esac
|
|
29
46
|
if [[ -f "$ROOT_DIR/.ai/post-exit.sh" ]]; then
|
|
30
47
|
set +e
|
|
31
48
|
kimi "$@"
|
|
@@ -70,6 +70,23 @@ if [[ -n "${CAIRN_EXTRA_SETTINGS:-}" ]]; then
|
|
|
70
70
|
export OPENCODE_CONFIG="$CAIRN_EXTRA_SETTINGS"
|
|
71
71
|
fi
|
|
72
72
|
|
|
73
|
+
case "${CAIRN_WORK_EVIDENCE:-}" in
|
|
74
|
+
1|true|TRUE|yes|YES|on|ON)
|
|
75
|
+
if command -v cairn >/dev/null 2>&1 && cairn evidence --help >/dev/null 2>&1; then
|
|
76
|
+
set +e
|
|
77
|
+
cairn evidence run --harness opencode -- opencode "$@"
|
|
78
|
+
CAIRN_EXIT_STATUS=$?
|
|
79
|
+
set -e
|
|
80
|
+
export CAIRN_EXIT_STATUS
|
|
81
|
+
if [[ -f "$ROOT_DIR/.ai/post-exit.sh" ]]; then
|
|
82
|
+
# shellcheck disable=SC1091
|
|
83
|
+
. "$ROOT_DIR/.ai/post-exit.sh" || true
|
|
84
|
+
fi
|
|
85
|
+
exit "$CAIRN_EXIT_STATUS"
|
|
86
|
+
fi
|
|
87
|
+
echo "cairn: compatible evidence command unavailable; launching without capture" >&2
|
|
88
|
+
;;
|
|
89
|
+
esac
|
|
73
90
|
if [[ -f "$ROOT_DIR/.ai/post-exit.sh" ]]; then
|
|
74
91
|
set +e
|
|
75
92
|
opencode "$@"
|
|
@@ -17,6 +17,23 @@ if [[ -f "$ROOT_DIR/.ai/pre-launch.sh" ]]; then
|
|
|
17
17
|
fi
|
|
18
18
|
cd "$ROOT_DIR"
|
|
19
19
|
|
|
20
|
+
case "${CAIRN_WORK_EVIDENCE:-}" in
|
|
21
|
+
1|true|TRUE|yes|YES|on|ON)
|
|
22
|
+
if command -v cairn >/dev/null 2>&1 && cairn evidence --help >/dev/null 2>&1; then
|
|
23
|
+
set +e
|
|
24
|
+
cairn evidence run --harness pi -- pi "$@"
|
|
25
|
+
CAIRN_EXIT_STATUS=$?
|
|
26
|
+
set -e
|
|
27
|
+
export CAIRN_EXIT_STATUS
|
|
28
|
+
if [[ -f "$ROOT_DIR/.ai/post-exit.sh" ]]; then
|
|
29
|
+
# shellcheck disable=SC1091
|
|
30
|
+
. "$ROOT_DIR/.ai/post-exit.sh" || true
|
|
31
|
+
fi
|
|
32
|
+
exit "$CAIRN_EXIT_STATUS"
|
|
33
|
+
fi
|
|
34
|
+
echo "cairn: compatible evidence command unavailable; launching without capture" >&2
|
|
35
|
+
;;
|
|
36
|
+
esac
|
|
20
37
|
if [[ -f "$ROOT_DIR/.ai/post-exit.sh" ]]; then
|
|
21
38
|
set +e
|
|
22
39
|
pi "$@"
|