@cairnkeep/cli 2.12.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 +13 -1
- package/README.md +20 -1
- package/docs/harness-compatibility.md +6 -0
- package/docs/learning/CURRICULUM-MAP.md +3 -1
- package/docs/learning/FEATURE-GUIDE.md +6 -2
- package/docs/learning/README.md +1 -1
- 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 +2 -2
- package/docs/learning/lessons/L03-first-project.md +1 -1
- 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 +2 -2
- package/docs/learning/lessons/L23-guided-setup.md +2 -2
- package/docs/learning/tracks/evidence-and-evaluation.md +2 -1
- package/docs/native-windows.md +9 -0
- package/docs/operating.md +43 -0
- package/docs/privacy-and-data-flow.md +36 -0
- package/docs/quickstart.md +5 -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 +1 -1
- package/scripts/cairn-cli.mjs +2 -0
- package/scripts/completion.sh +11 -3
- package/scripts/doctor.sh +30 -1
- package/scripts/test-cli-dispatch.sh +3 -1
- package/scripts/test-completion.sh +4 -0
- package/scripts/test-package-install.sh +4 -0
- package/scripts/test-uninstall.sh +10 -0
- package/scripts/test-windows-native.mjs +3 -0
- package/scripts/test-work-evidence-cli.sh +89 -0
- package/scripts/verify-docs-parity.sh +36 -0
- package/scripts/windows-platform.mjs +15 -2
- package/templates/env.example.template +10 -0
- package/templates/start-claude.sh.template +18 -0
- package/templates/start-codex.sh.template +18 -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
|
@@ -15,6 +15,7 @@ tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT
|
|
|
15
15
|
"$cairn" help | grep -q "cairn memory-server" || fail "help missing memory-server"
|
|
16
16
|
"$cairn" help | grep -q "cairn notes" || fail "help missing notes"
|
|
17
17
|
"$cairn" help | grep -q "cairn artifact <list|show|delete|prune>" || fail "help missing artifact"
|
|
18
|
+
"$cairn" help | grep -q "cairn evidence <list|show|delete|prune|doctor>" || fail "help missing evidence"
|
|
18
19
|
"$cairn" help | grep -q "cairn capabilities <list|status|enable|disable|reset|logging>" || fail "help missing capabilities"
|
|
19
20
|
"$cairn" help | grep -q "cairn mcp-tools <list|status|set|reset>" || fail "help missing mcp-tools"
|
|
20
21
|
"$cairn" help | grep -q "cairn pack <init|lock|validate|install|list|show|remove|enable|disable|update|skills|approve-skill|revoke-skill>" || fail "help missing pack"
|
|
@@ -24,6 +25,7 @@ tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT
|
|
|
24
25
|
"$cairn" sync-kimi --help >/dev/null 2>&1 || fail "cairn sync-kimi dispatch"
|
|
25
26
|
env -u CAIRN_NOTE_DISTILLATION "$cairn" notes --help >/dev/null 2>&1 || fail "cairn notes dispatch"
|
|
26
27
|
"$cairn" artifact --help | grep -q "cairn artifact list" || fail "cairn artifact dispatch"
|
|
28
|
+
"$cairn" evidence --help | grep -q "cairn evidence list" || fail "cairn evidence dispatch"
|
|
27
29
|
"$cairn" capabilities --help >"$tmp/capabilities-help" 2>/dev/null || fail "cairn capabilities dispatch"
|
|
28
30
|
"$cairn" mcp-tools list --json >"$tmp/mcp-tools.json" || fail "cairn mcp-tools dispatch"
|
|
29
31
|
CAIRN_PACK_BASE_DIR="$tmp/pack-store" "$cairn" pack list --json >"$tmp/packs.json" || fail "cairn pack dispatch"
|
|
@@ -70,4 +72,4 @@ CAIRN_AGENTFS_BASE_DIR="$tmp/store" "$cairn" memory path | grep -qx "$tmp/store"
|
|
|
70
72
|
"$cairn" audit-timer --render-only "$tmp/u" >/dev/null || fail "cairn audit-timer dispatch"
|
|
71
73
|
[[ -f "$tmp/u/memory-wiki-audit.timer" ]] || fail "audit-timer render via cairn produced no units"
|
|
72
74
|
|
|
73
|
-
echo "PASS: cairn dispatch (doctor, memory, artifact, audit-timer)"
|
|
75
|
+
echo "PASS: cairn dispatch (doctor, memory, artifact, evidence, audit-timer)"
|
|
@@ -48,6 +48,9 @@ for shell in bash zsh fish; do
|
|
|
48
48
|
grep -q 'pack' "$tmp/$shell"
|
|
49
49
|
grep -q 'init.*lock.*validate.*install.*list.*show.*remove.*enable.*disable.*update.*skills.*approve-skill.*revoke-skill\|init lock validate install list show remove enable disable update skills approve-skill revoke-skill' "$tmp/$shell"
|
|
50
50
|
grep -q -- '--purge-packs\|-l purge-packs' "$tmp/$shell"
|
|
51
|
+
grep -q 'evidence' "$tmp/$shell"
|
|
52
|
+
grep -q 'list.*show.*delete.*prune.*doctor\|list show delete prune doctor' "$tmp/$shell"
|
|
53
|
+
grep -q -- '--status\|-l status' "$tmp/$shell"
|
|
51
54
|
done
|
|
52
55
|
|
|
53
56
|
"$ROOT/bin/cairn" help >"$tmp/root-help"
|
|
@@ -64,6 +67,7 @@ if grep -q 'doctor-diagnosis' "$tmp/eval-help"; then
|
|
|
64
67
|
exit 1
|
|
65
68
|
fi
|
|
66
69
|
grep -qF 'cairn skill <harvest|list|show|review|propose|evaluate|apply|rollback>' "$tmp/root-help"
|
|
70
|
+
grep -qF 'cairn evidence <list|show|delete|prune|doctor>' "$tmp/root-help"
|
|
67
71
|
node "$ROOT/mcp-memory-server/dist/skill-cli.js" --help >"$tmp/skill-help"
|
|
68
72
|
for command in $skill_commands; do
|
|
69
73
|
grep -q "cairn skill $command" "$tmp/skill-help"
|
|
@@ -292,6 +292,10 @@ for module in artifact-schema artifact-store compaction-normalize artifact-cli;
|
|
|
292
292
|
[[ -f "$installed_root/mcp-memory-server/dist/$module.js" ]] || \
|
|
293
293
|
fail "npm tarball omitted compiled $module"
|
|
294
294
|
done
|
|
295
|
+
for module in work-evidence-schema work-evidence-store work-evidence-cli; do
|
|
296
|
+
[[ -f "$installed_root/mcp-memory-server/dist/$module.js" ]] || \
|
|
297
|
+
fail "npm tarball omitted compiled $module"
|
|
298
|
+
done
|
|
295
299
|
[[ -x "$installed_root/claude/hooks/compaction-capture.sh" ]] || \
|
|
296
300
|
fail "npm tarball omitted the executable Claude compaction hook"
|
|
297
301
|
[[ -f "$installed_root/opencode/plugins/memory-capture.ts" ]] || \
|
|
@@ -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"
|
|
@@ -36,6 +36,8 @@ try {
|
|
|
36
36
|
assert.match(launcherModule(), /spawnSync\(command/);
|
|
37
37
|
assert.match(launcherPowerShell(), /pre-launch\.ps1/);
|
|
38
38
|
assert.match(launcherPowerShell(), /post-exit\.ps1/);
|
|
39
|
+
assert.match(launcherPowerShell(), /evidence run --harness/);
|
|
40
|
+
assert.match(launcherPowerShell(), /CAIRN_WORK_EVIDENCE/);
|
|
39
41
|
|
|
40
42
|
const claudeRoot = join(sandbox, "Claude Config");
|
|
41
43
|
await runWindowsCommand({ command: "sync", args: ["--apply", "--live-root", claudeRoot], root });
|
|
@@ -56,6 +58,7 @@ try {
|
|
|
56
58
|
await runWindowsCommand({ command: "memory", args: ["import", archive], root });
|
|
57
59
|
assert.equal(readFileSync(join(memory, "project.db"), "utf8"), "sqlite-a");
|
|
58
60
|
assert.match(powershellCompletion(), /Register-ArgumentCompleter/);
|
|
61
|
+
assert.match(powershellCompletion(), /evidence/);
|
|
59
62
|
|
|
60
63
|
let containerArgs = [];
|
|
61
64
|
runNativeContainer(["stdio", "--image", "example/windows:1", "--volume", "windows-data"], root, (_engine, args) => { containerArgs = args; });
|
|
@@ -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"
|
|
@@ -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
|
|
|
@@ -190,7 +190,19 @@ if ($env:CAIRN_EXTRA_SETTINGS -and $Harness -eq 'claude') {
|
|
|
190
190
|
if ($env:CAIRN_EXTRA_SETTINGS -and $Harness -eq 'opencode') { $env:OPENCODE_CONFIG = $env:CAIRN_EXTRA_SETTINGS }
|
|
191
191
|
Push-Location $ProjectRoot
|
|
192
192
|
try {
|
|
193
|
-
|
|
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
|
+
}
|
|
194
206
|
$Status = if ($null -eq $LASTEXITCODE) { 0 } else { $LASTEXITCODE }
|
|
195
207
|
} finally {
|
|
196
208
|
Pop-Location
|
|
@@ -440,6 +452,7 @@ async function doctorWindows(root, args) {
|
|
|
440
452
|
for (const [label, file, doctorArgs] of [
|
|
441
453
|
["trajectory", "trajectory-cli.js", ["doctor", "--json", ...(repair ? ["--repair"] : [])]],
|
|
442
454
|
["artifact", "artifact-cli.js", ["doctor", "--json", ...(repair ? ["--repair"] : [])]],
|
|
455
|
+
["work evidence", "work-evidence-cli.js", ["doctor", "--json", ...(repair ? ["--repair"] : [])]],
|
|
443
456
|
["typed memory", "node-cli.js", ["doctor", "--project-root", process.cwd(), ...(repair ? ["--repair"] : [])]],
|
|
444
457
|
["capability", "capability-cli.js", ["doctor", "--json"]],
|
|
445
458
|
["context pack", "context-pack-cli.js", ["doctor", "--json"]],
|
|
@@ -695,7 +708,7 @@ export function powershellCompletion() {
|
|
|
695
708
|
const harnesses = HARNESS_IDS.map((id) => `'${id}'`).join(",");
|
|
696
709
|
return `Register-ArgumentCompleter -Native -CommandName cairn -ScriptBlock {
|
|
697
710
|
param($wordToComplete, $commandAst, $cursorPosition)
|
|
698
|
-
$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'
|
|
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'
|
|
699
712
|
$setup = '--git','--harness','--memory','--policy','--yes','--json','init','existing','none',${harnesses},'local'
|
|
700
713
|
$candidates = if ($commandAst.ToString() -match '^\\s*cairn\\s+setup(?:\\s|$)') { $setup } else { $commands }
|
|
701
714
|
$candidates | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
@@ -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
|
|
@@ -17,6 +17,24 @@ 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 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
|
+
|
|
20
38
|
if [[ -f "$ROOT_DIR/.ai/post-exit.sh" ]]; then
|
|
21
39
|
set +e
|
|
22
40
|
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 "$@"
|
|
@@ -19,6 +19,23 @@ fi
|
|
|
19
19
|
|
|
20
20
|
cd "$ROOT_DIR"
|
|
21
21
|
|
|
22
|
+
case "${CAIRN_WORK_EVIDENCE:-}" in
|
|
23
|
+
1|true|TRUE|yes|YES|on|ON)
|
|
24
|
+
if command -v cairn >/dev/null 2>&1 && cairn evidence --help >/dev/null 2>&1; then
|
|
25
|
+
set +e
|
|
26
|
+
cairn evidence run --harness qwen -- qwen "$@"
|
|
27
|
+
CAIRN_EXIT_STATUS=$?
|
|
28
|
+
set -e
|
|
29
|
+
export CAIRN_EXIT_STATUS
|
|
30
|
+
if [[ -f "$ROOT_DIR/.ai/post-exit.sh" ]]; then
|
|
31
|
+
# shellcheck disable=SC1091
|
|
32
|
+
. "$ROOT_DIR/.ai/post-exit.sh" || true
|
|
33
|
+
fi
|
|
34
|
+
exit "$CAIRN_EXIT_STATUS"
|
|
35
|
+
fi
|
|
36
|
+
echo "cairn: compatible evidence command unavailable; launching without capture" >&2
|
|
37
|
+
;;
|
|
38
|
+
esac
|
|
22
39
|
if [[ -f "$ROOT_DIR/.ai/post-exit.sh" ]]; then
|
|
23
40
|
set +e
|
|
24
41
|
qwen "$@"
|