@cairnkeep/cli 2.3.1 → 2.5.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 +85 -0
- package/README.md +232 -6
- package/bin/cairn +43 -2
- package/claude/capability-contract/hooks/capability-command-finish.sh +81 -0
- package/claude/capability-contract/hooks/capability-command-start.sh +76 -0
- package/claude/hooks/compaction-capture.sh +34 -0
- package/claude/hooks/memory-capture.sh +47 -5
- package/claude/hooks/memory-wakeup.sh +55 -2
- package/docs/building-an-overlay.md +4 -3
- package/docs/design/meta-agent.md +439 -0
- package/docs/harness-compatibility.md +117 -0
- package/docs/learning/CURRICULUM-MAP.md +91 -0
- package/docs/learning/FEATURE-GUIDE.md +86 -0
- package/docs/learning/PRODUCTION-PLAN.md +45 -14
- package/docs/learning/README.md +20 -1
- package/docs/learning/instructor-guide.md +6 -0
- package/docs/learning/lessons/L00-why-cairnkeep.md +20 -8
- package/docs/learning/lessons/L01-safe-trial.md +3 -3
- package/docs/learning/lessons/L02-installation.md +23 -5
- package/docs/learning/lessons/L03-first-project.md +7 -2
- package/docs/learning/lessons/L04-memory-fundamentals.md +4 -0
- package/docs/learning/lessons/L05-daily-workflow.md +4 -0
- package/docs/learning/lessons/L07-privacy-and-storage.md +3 -0
- package/docs/learning/lessons/L10-context-exploration.md +4 -2
- package/docs/learning/lessons/L12-managed-overlays.md +2 -0
- package/docs/learning/lessons/L13-session-evidence.md +71 -0
- package/docs/learning/lessons/L14-typed-memory.md +51 -0
- package/docs/learning/lessons/L15-capability-governance.md +56 -0
- package/docs/learning/lessons/L16-evaluation.md +67 -0
- package/docs/learning/lessons/L17-agent-boundary.md +55 -0
- package/docs/learning/templates/lesson-template.md +4 -1
- package/docs/learning/tracks/evidence-and-evaluation.md +20 -0
- package/docs/learning/tracks/operator.md +8 -2
- package/docs/learning/tracks/practitioner.md +6 -1
- package/docs/learning/tracks/quickstart.md +3 -0
- package/docs/learning/video-scripts/L00-why-cairnkeep.md +21 -17
- package/docs/learning/video-scripts/L02-installation.md +9 -2
- package/docs/learning/video-scripts/L03-first-project.md +3 -0
- package/docs/operating.md +567 -7
- package/docs/privacy-and-data-flow.md +417 -0
- package/docs/storage.md +252 -0
- package/examples/eval/README.md +42 -0
- package/examples/eval/adapter.json +12 -0
- package/examples/eval/bundled-fake.json +1 -0
- package/examples/eval/task-set.json +1 -0
- package/mcp-memory-server/dist/artifact-cli.js +184 -0
- package/mcp-memory-server/dist/artifact-schema.js +218 -0
- package/mcp-memory-server/dist/artifact-store.js +812 -0
- package/mcp-memory-server/dist/capability-adapter.js +286 -0
- package/mcp-memory-server/dist/capability-cli.js +361 -0
- package/mcp-memory-server/dist/capability-config.js +310 -0
- package/mcp-memory-server/dist/capability-harness.js +496 -0
- package/mcp-memory-server/dist/capability-registry.js +123 -0
- package/mcp-memory-server/dist/capability-schema.js +65 -0
- package/mcp-memory-server/dist/capability-store.js +431 -0
- package/mcp-memory-server/dist/compaction-normalize.js +267 -0
- package/mcp-memory-server/dist/eval-cli.js +523 -0
- package/mcp-memory-server/dist/eval-plan.js +346 -0
- package/mcp-memory-server/dist/eval-process.js +273 -0
- package/mcp-memory-server/dist/eval-report.js +609 -0
- package/mcp-memory-server/dist/eval-runner.js +827 -0
- package/mcp-memory-server/dist/eval-schema.js +368 -0
- package/mcp-memory-server/dist/eval-statistics.js +161 -0
- package/mcp-memory-server/dist/eval-workspace.js +313 -0
- package/mcp-memory-server/dist/failure-signature.js +141 -0
- package/mcp-memory-server/dist/index.js +600 -55
- package/mcp-memory-server/dist/node-cli.js +84 -0
- package/mcp-memory-server/dist/node-schema.js +170 -0
- package/mcp-memory-server/dist/node-store.js +455 -0
- package/mcp-memory-server/dist/note-cli.js +235 -0
- package/mcp-memory-server/dist/note-distiller.js +177 -0
- package/mcp-memory-server/dist/note-enrichment.js +93 -0
- package/mcp-memory-server/dist/note-schema.js +4 -0
- package/mcp-memory-server/dist/note-store.js +985 -0
- package/mcp-memory-server/dist/trajectory-cli.js +141 -0
- package/mcp-memory-server/dist/trajectory-normalize.js +448 -0
- package/mcp-memory-server/dist/trajectory-redaction.js +155 -0
- package/mcp-memory-server/dist/trajectory-schema.js +73 -0
- package/mcp-memory-server/dist/trajectory-store.js +339 -0
- package/mcp-memory-server/package.json +24 -2
- package/mcp-memory-server/scripts/fixtures/compaction-claude-code-2.1.219.json +9 -0
- package/mcp-memory-server/scripts/fixtures/compaction-claude-code-2.1.220.json +9 -0
- package/mcp-memory-server/scripts/fixtures/compaction-opencode-1.17.20-event.json +6 -0
- package/mcp-memory-server/scripts/fixtures/compaction-opencode-1.17.20-messages.json +119 -0
- package/mcp-memory-server/scripts/fixtures/compaction-unknown-version.json +17 -0
- package/opencode/capability-contract/plugins/capability-command.ts +319 -0
- package/opencode/command/graphify.md +25 -14
- package/opencode/plugins/memory-capture.ts +76 -10
- package/opencode/plugins/memory-wakeup.ts +58 -6
- package/package.json +8 -4
- package/pi/extensions/cairnkeep-trajectory.ts +55 -0
- package/schemas/artifact.schema.json +133 -0
- package/schemas/capability-callback.schema.json +68 -0
- package/schemas/capability-contract.schema.json +126 -0
- package/schemas/eval-adapter.schema.json +43 -0
- package/schemas/eval-protocol.schema.json +114 -0
- package/schemas/eval-report.schema.json +423 -0
- package/schemas/eval-task-set.schema.json +114 -0
- package/schemas/memory-node.schema.json +90 -0
- package/schemas/note.schema.json +112 -0
- package/schemas/trajectory-redaction.schema.json +25 -0
- package/schemas/trajectory.schema.json +82 -0
- package/scripts/bootstrap.sh +6 -0
- package/scripts/completion.sh +119 -5
- package/scripts/doctor.sh +284 -0
- package/scripts/fake-eval-adapter.mjs +120 -0
- package/scripts/fixtures/capability-harness-contracts.json +408 -0
- package/scripts/install-audit-timer.sh +3 -1
- package/scripts/lib/capability-opencode-plugin-harness.mjs +291 -0
- package/scripts/lib/trajectory-opencode-plugin-harness.mjs +25 -0
- package/scripts/lib/trajectory-pi-extension-harness.mjs +42 -0
- package/scripts/memory-wiki-audit.sh +49 -0
- package/scripts/smoke-bash32.sh +9 -0
- package/scripts/sync-claude-assets.sh +99 -6
- package/scripts/sync-opencode-graphify-assets.sh +24 -3
- package/scripts/sync-opencode-plugin-assets.sh +31 -5
- package/scripts/sync-opencode-security-assets.sh +24 -3
- package/scripts/sync-opencode-wiki-assets.sh +24 -3
- package/scripts/sync-pi-assets.sh +72 -0
- package/scripts/test-audit-timer.sh +98 -0
- package/scripts/test-bootstrap-untracked.sh +49 -0
- package/scripts/test-cli-dispatch.sh +41 -2
- package/scripts/test-compaction-hooks.sh +495 -0
- package/scripts/test-completion.sh +77 -0
- package/scripts/test-doctor.sh +348 -1
- package/scripts/test-eval-cli.sh +175 -0
- package/scripts/test-launcher-hooks.sh +630 -3
- package/scripts/test-learning-docs.sh +20 -4
- package/scripts/test-notes-cli.sh +114 -0
- package/scripts/test-package-install.sh +254 -0
- package/scripts/test-phase16-node-lifecycle.sh +50 -0
- package/scripts/test-phase17-artifact-lifecycle.sh +370 -0
- package/scripts/test-phase18-capability-lifecycle.sh +504 -0
- package/scripts/test-phase18-harness-boundary.sh +1060 -0
- package/scripts/test-phase19-eval-lifecycle.sh +1015 -0
- package/scripts/test-trajectory-cli.sh +87 -0
- package/scripts/test-trajectory-hooks.sh +142 -0
- package/scripts/test-uninstall.sh +231 -5
- package/scripts/uninstall.sh +38 -5
- package/scripts/verify-dependency-security.mjs +121 -0
- package/scripts/verify-docs-parity.sh +527 -1
- package/scripts/verify-phase18-runtime-evidence.mjs +895 -0
- package/scripts/verify-phase19-runtime-evidence.mjs +947 -0
- package/templates/capabilities.json.template +7 -0
- package/templates/env.example.template +51 -0
- package/templates/memory-wiki-audit.service.template +1 -1
- package/templates/start-claude.sh.template +36 -0
- package/templates/start-kimi.sh.template +39 -0
- package/templates/start-opencode.sh.template +47 -5
- package/templates/start-pi.sh.template +30 -0
- package/templates/trajectory-redaction.json.template +4 -0
|
@@ -3,7 +3,7 @@ set -euo pipefail
|
|
|
3
3
|
|
|
4
4
|
usage() {
|
|
5
5
|
cat <<'EOF'
|
|
6
|
-
Usage: sync-opencode-graphify-assets.sh [--check|--apply] [--live-root PATH]
|
|
6
|
+
Usage: sync-opencode-graphify-assets.sh [--check|--apply] [--capability-overlay] [--live-root PATH]
|
|
7
7
|
|
|
8
8
|
Compare or sync the repo-managed Graphify OpenCode command asset against the
|
|
9
9
|
live OpenCode config tree.
|
|
@@ -11,12 +11,17 @@ live OpenCode config tree.
|
|
|
11
11
|
Options:
|
|
12
12
|
--check Verify that the managed live asset matches the repo copy (default)
|
|
13
13
|
--apply Copy the repo-managed asset into the live OpenCode tree, then verify
|
|
14
|
+
--capability-overlay
|
|
15
|
+
Select the guarded command overlay for a contract-enabled isolated root
|
|
14
16
|
--live-root PATH Override the live OpenCode root (default: $OPENCODE_CONFIG_DIR or $HOME/.config/opencode)
|
|
15
17
|
-h, --help Show this help text
|
|
16
18
|
|
|
17
19
|
Notes:
|
|
18
20
|
- The repo-managed source of truth lives under ./opencode/
|
|
19
21
|
- This script manages only the /graphify command asset
|
|
22
|
+
- Plugin installation is delegated to sync-opencode-plugin-assets.sh;
|
|
23
|
+
--capability-overlay selects its native plugin only when the master switch is on
|
|
24
|
+
- The command asset always retains its legacy owner and bytes
|
|
20
25
|
EOF
|
|
21
26
|
}
|
|
22
27
|
|
|
@@ -24,6 +29,7 @@ ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
|
24
29
|
SOURCE_ROOT="$ROOT_DIR/opencode"
|
|
25
30
|
LIVE_ROOT="${OPENCODE_CONFIG_DIR:-$HOME/.config/opencode}"
|
|
26
31
|
MODE="check"
|
|
32
|
+
CAPABILITY_OVERLAY=0
|
|
27
33
|
|
|
28
34
|
ASSETS=(
|
|
29
35
|
"command/graphify.md"
|
|
@@ -39,6 +45,10 @@ while [[ $# -gt 0 ]]; do
|
|
|
39
45
|
MODE="apply"
|
|
40
46
|
shift
|
|
41
47
|
;;
|
|
48
|
+
--capability-overlay)
|
|
49
|
+
CAPABILITY_OVERLAY=1
|
|
50
|
+
shift
|
|
51
|
+
;;
|
|
42
52
|
--live-root)
|
|
43
53
|
LIVE_ROOT="$2"
|
|
44
54
|
shift 2
|
|
@@ -55,6 +65,11 @@ while [[ $# -gt 0 ]]; do
|
|
|
55
65
|
esac
|
|
56
66
|
done
|
|
57
67
|
|
|
68
|
+
source_for() {
|
|
69
|
+
local rel="$1"
|
|
70
|
+
printf '%s\n' "$SOURCE_ROOT/$rel"
|
|
71
|
+
}
|
|
72
|
+
|
|
58
73
|
ensure_source_assets_exist() {
|
|
59
74
|
local rel
|
|
60
75
|
local missing=0
|
|
@@ -79,7 +94,7 @@ check_asset_sync() {
|
|
|
79
94
|
local -a mismatched=()
|
|
80
95
|
|
|
81
96
|
for rel in "${ASSETS[@]}"; do
|
|
82
|
-
src
|
|
97
|
+
src=$(source_for "$rel")
|
|
83
98
|
dst="$LIVE_ROOT/$rel"
|
|
84
99
|
|
|
85
100
|
if [[ ! -f "$dst" ]]; then
|
|
@@ -125,7 +140,7 @@ run_apply() {
|
|
|
125
140
|
ensure_source_assets_exist
|
|
126
141
|
|
|
127
142
|
for rel in "${ASSETS[@]}"; do
|
|
128
|
-
src
|
|
143
|
+
src=$(source_for "$rel")
|
|
129
144
|
dst="$LIVE_ROOT/$rel"
|
|
130
145
|
|
|
131
146
|
mkdir -p "$(dirname "$dst")"
|
|
@@ -150,3 +165,9 @@ case "$MODE" in
|
|
|
150
165
|
run_apply
|
|
151
166
|
;;
|
|
152
167
|
esac
|
|
168
|
+
|
|
169
|
+
plugin_args=("--$MODE" "--live-root" "$LIVE_ROOT")
|
|
170
|
+
if [[ "$CAPABILITY_OVERLAY" -eq 1 ]]; then
|
|
171
|
+
plugin_args+=("--capability-overlay")
|
|
172
|
+
fi
|
|
173
|
+
"$ROOT_DIR/scripts/sync-opencode-plugin-assets.sh" "${plugin_args[@]}"
|
|
@@ -3,7 +3,7 @@ set -euo pipefail
|
|
|
3
3
|
|
|
4
4
|
usage() {
|
|
5
5
|
cat <<'EOF'
|
|
6
|
-
Usage: sync-opencode-plugin-assets.sh [--check|--apply] [--live-root PATH]
|
|
6
|
+
Usage: sync-opencode-plugin-assets.sh [--check|--apply] [--capability-overlay] [--live-root PATH]
|
|
7
7
|
|
|
8
8
|
Compare or sync the repo-managed OpenCode plugin assets against the live
|
|
9
9
|
OpenCode config tree.
|
|
@@ -11,6 +11,8 @@ OpenCode config tree.
|
|
|
11
11
|
Options:
|
|
12
12
|
--check Verify that the managed live assets match the repo copies (default)
|
|
13
13
|
--apply Copy the repo-managed assets into the live OpenCode tree, then verify
|
|
14
|
+
--capability-overlay
|
|
15
|
+
Include the native capability plugin only when CAIRN_CAPABILITY_CONTRACT is enabled
|
|
14
16
|
--live-root PATH Override the live OpenCode root (default: $OPENCODE_CONFIG_DIR or $HOME/.config/opencode)
|
|
15
17
|
-h, --help Show this help text
|
|
16
18
|
|
|
@@ -24,6 +26,7 @@ ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
|
24
26
|
SOURCE_ROOT="$ROOT_DIR/opencode"
|
|
25
27
|
LIVE_ROOT="${OPENCODE_CONFIG_DIR:-$HOME/.config/opencode}"
|
|
26
28
|
MODE="check"
|
|
29
|
+
CAPABILITY_OVERLAY=0
|
|
27
30
|
|
|
28
31
|
ASSETS=(
|
|
29
32
|
"plugins/memory-wakeup.ts"
|
|
@@ -41,6 +44,10 @@ while [[ $# -gt 0 ]]; do
|
|
|
41
44
|
MODE="apply"
|
|
42
45
|
shift
|
|
43
46
|
;;
|
|
47
|
+
--capability-overlay)
|
|
48
|
+
CAPABILITY_OVERLAY=1
|
|
49
|
+
shift
|
|
50
|
+
;;
|
|
44
51
|
--live-root)
|
|
45
52
|
LIVE_ROOT="$2"
|
|
46
53
|
shift 2
|
|
@@ -57,6 +64,23 @@ while [[ $# -gt 0 ]]; do
|
|
|
57
64
|
esac
|
|
58
65
|
done
|
|
59
66
|
|
|
67
|
+
case "${CAIRN_CAPABILITY_CONTRACT:-}" in
|
|
68
|
+
1|true|TRUE|yes|YES|on|ON)
|
|
69
|
+
if [[ "$CAPABILITY_OVERLAY" -eq 1 ]]; then
|
|
70
|
+
ASSETS+=("plugins/capability-command.ts")
|
|
71
|
+
fi
|
|
72
|
+
;;
|
|
73
|
+
esac
|
|
74
|
+
|
|
75
|
+
source_asset() {
|
|
76
|
+
local rel="$1"
|
|
77
|
+
if [[ "$rel" == "plugins/capability-command.ts" ]]; then
|
|
78
|
+
printf '%s\n' "$SOURCE_ROOT/capability-contract/plugins/capability-command.ts"
|
|
79
|
+
else
|
|
80
|
+
printf '%s\n' "$SOURCE_ROOT/$rel"
|
|
81
|
+
fi
|
|
82
|
+
}
|
|
83
|
+
|
|
60
84
|
render_asset() {
|
|
61
85
|
local src="$1"
|
|
62
86
|
sed "s|@@INFRA_ROOT@@|$ROOT_DIR|g" "$src"
|
|
@@ -67,8 +91,10 @@ ensure_source_assets_exist() {
|
|
|
67
91
|
local missing=0
|
|
68
92
|
|
|
69
93
|
for rel in "${ASSETS[@]}"; do
|
|
70
|
-
|
|
71
|
-
|
|
94
|
+
local src
|
|
95
|
+
src=$(source_asset "$rel")
|
|
96
|
+
if [[ ! -f "$src" ]]; then
|
|
97
|
+
echo "Missing repo-managed source asset: $src" >&2
|
|
72
98
|
missing=1
|
|
73
99
|
fi
|
|
74
100
|
done
|
|
@@ -86,7 +112,7 @@ check_asset_sync() {
|
|
|
86
112
|
local -a mismatched=()
|
|
87
113
|
|
|
88
114
|
for rel in "${ASSETS[@]}"; do
|
|
89
|
-
src
|
|
115
|
+
src=$(source_asset "$rel")
|
|
90
116
|
dst="$LIVE_ROOT/$rel"
|
|
91
117
|
|
|
92
118
|
if [[ ! -f "$dst" ]]; then
|
|
@@ -133,7 +159,7 @@ run_apply() {
|
|
|
133
159
|
ensure_source_assets_exist
|
|
134
160
|
|
|
135
161
|
for rel in "${ASSETS[@]}"; do
|
|
136
|
-
src
|
|
162
|
+
src=$(source_asset "$rel")
|
|
137
163
|
dst="$LIVE_ROOT/$rel"
|
|
138
164
|
|
|
139
165
|
mkdir -p "$(dirname "$dst")"
|
|
@@ -3,7 +3,7 @@ set -euo pipefail
|
|
|
3
3
|
|
|
4
4
|
usage() {
|
|
5
5
|
cat <<'EOF'
|
|
6
|
-
Usage: sync-opencode-security-assets.sh [--check|--apply] [--live-root PATH]
|
|
6
|
+
Usage: sync-opencode-security-assets.sh [--check|--apply] [--capability-overlay] [--live-root PATH]
|
|
7
7
|
|
|
8
8
|
Compare or sync the repo-managed OpenCode security-audit assets against the live
|
|
9
9
|
OpenCode config tree.
|
|
@@ -11,12 +11,17 @@ OpenCode config tree.
|
|
|
11
11
|
Options:
|
|
12
12
|
--check Verify that the managed live assets match the repo copy (default)
|
|
13
13
|
--apply Copy the repo-managed assets into the live OpenCode tree, then verify
|
|
14
|
+
--capability-overlay
|
|
15
|
+
Select guarded command/workflow overlays for a contract-enabled isolated root
|
|
14
16
|
--live-root PATH Override the live OpenCode root (default: $OPENCODE_CONFIG_DIR or $HOME/.config/opencode)
|
|
15
17
|
-h, --help Show this help text
|
|
16
18
|
|
|
17
19
|
Notes:
|
|
18
20
|
- The repo-managed source of truth lives under ./opencode/
|
|
19
21
|
- This script manages only the security-audit OpenCode assets
|
|
22
|
+
- Plugin installation is delegated to sync-opencode-plugin-assets.sh;
|
|
23
|
+
--capability-overlay selects its native plugin only when the master switch is on
|
|
24
|
+
- Command and workflow assets always retain their legacy owners and bytes
|
|
20
25
|
- --apply also removes the legacy gsd-overrides security-audit workflow/template files
|
|
21
26
|
EOF
|
|
22
27
|
}
|
|
@@ -25,6 +30,7 @@ ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
|
25
30
|
SOURCE_ROOT="$ROOT_DIR/opencode"
|
|
26
31
|
LIVE_ROOT="${OPENCODE_CONFIG_DIR:-$HOME/.config/opencode}"
|
|
27
32
|
MODE="check"
|
|
33
|
+
CAPABILITY_OVERLAY=0
|
|
28
34
|
|
|
29
35
|
ASSETS=(
|
|
30
36
|
"command/security-audit.md"
|
|
@@ -56,6 +62,10 @@ while [[ $# -gt 0 ]]; do
|
|
|
56
62
|
MODE="apply"
|
|
57
63
|
shift
|
|
58
64
|
;;
|
|
65
|
+
--capability-overlay)
|
|
66
|
+
CAPABILITY_OVERLAY=1
|
|
67
|
+
shift
|
|
68
|
+
;;
|
|
59
69
|
--live-root)
|
|
60
70
|
LIVE_ROOT="$2"
|
|
61
71
|
shift 2
|
|
@@ -72,6 +82,11 @@ while [[ $# -gt 0 ]]; do
|
|
|
72
82
|
esac
|
|
73
83
|
done
|
|
74
84
|
|
|
85
|
+
source_for() {
|
|
86
|
+
local rel="$1"
|
|
87
|
+
printf '%s\n' "$SOURCE_ROOT/$rel"
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
ensure_source_assets_exist() {
|
|
76
91
|
local rel
|
|
77
92
|
local missing=0
|
|
@@ -116,7 +131,7 @@ run_check() {
|
|
|
116
131
|
ensure_source_assets_exist
|
|
117
132
|
|
|
118
133
|
for rel in "${ASSETS[@]}"; do
|
|
119
|
-
src
|
|
134
|
+
src=$(source_for "$rel")
|
|
120
135
|
dst="$LIVE_ROOT/$rel"
|
|
121
136
|
|
|
122
137
|
if [[ ! -f "$dst" ]]; then
|
|
@@ -160,7 +175,7 @@ run_apply() {
|
|
|
160
175
|
ensure_source_assets_exist
|
|
161
176
|
|
|
162
177
|
for rel in "${ASSETS[@]}"; do
|
|
163
|
-
src
|
|
178
|
+
src=$(source_for "$rel")
|
|
164
179
|
dst="$LIVE_ROOT/$rel"
|
|
165
180
|
|
|
166
181
|
mkdir -p "$(dirname "$dst")"
|
|
@@ -193,3 +208,9 @@ case "$MODE" in
|
|
|
193
208
|
run_apply
|
|
194
209
|
;;
|
|
195
210
|
esac
|
|
211
|
+
|
|
212
|
+
plugin_args=("--$MODE" "--live-root" "$LIVE_ROOT")
|
|
213
|
+
if [[ "$CAPABILITY_OVERLAY" -eq 1 ]]; then
|
|
214
|
+
plugin_args+=("--capability-overlay")
|
|
215
|
+
fi
|
|
216
|
+
"$ROOT_DIR/scripts/sync-opencode-plugin-assets.sh" "${plugin_args[@]}"
|
|
@@ -3,7 +3,7 @@ set -euo pipefail
|
|
|
3
3
|
|
|
4
4
|
usage() {
|
|
5
5
|
cat <<'EOF'
|
|
6
|
-
Usage: sync-opencode-wiki-assets.sh [--check|--apply] [--live-root PATH]
|
|
6
|
+
Usage: sync-opencode-wiki-assets.sh [--check|--apply] [--capability-overlay] [--live-root PATH]
|
|
7
7
|
|
|
8
8
|
Compare or sync the repo-managed OpenCode wiki assets against the live
|
|
9
9
|
OpenCode config tree.
|
|
@@ -11,12 +11,17 @@ OpenCode config tree.
|
|
|
11
11
|
Options:
|
|
12
12
|
--check Verify that the managed live assets match the repo copy (default)
|
|
13
13
|
--apply Copy the repo-managed assets into the live OpenCode tree, then verify
|
|
14
|
+
--capability-overlay
|
|
15
|
+
Select guarded command/workflow overlays for a contract-enabled isolated root
|
|
14
16
|
--live-root PATH Override the live OpenCode root (default: $OPENCODE_CONFIG_DIR or $HOME/.config/opencode)
|
|
15
17
|
-h, --help Show this help text
|
|
16
18
|
|
|
17
19
|
Notes:
|
|
18
20
|
- The repo-managed source of truth lives under ./opencode/
|
|
19
21
|
- This script manages only the wiki-specific OpenCode assets
|
|
22
|
+
- Plugin installation is delegated to sync-opencode-plugin-assets.sh;
|
|
23
|
+
--capability-overlay selects its native plugin only when the master switch is on
|
|
24
|
+
- Command and workflow assets always retain their legacy owners and bytes
|
|
20
25
|
- Extra live wiki assets are reported as warnings but are not deleted automatically
|
|
21
26
|
- --apply also removes the legacy gsd-overrides wiki workflow/template files
|
|
22
27
|
EOF
|
|
@@ -26,6 +31,7 @@ ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
|
26
31
|
SOURCE_ROOT="$ROOT_DIR/opencode"
|
|
27
32
|
LIVE_ROOT="${OPENCODE_CONFIG_DIR:-$HOME/.config/opencode}"
|
|
28
33
|
MODE="check"
|
|
34
|
+
CAPABILITY_OVERLAY=0
|
|
29
35
|
|
|
30
36
|
ASSETS=(
|
|
31
37
|
"command/wiki-ingest.md"
|
|
@@ -68,6 +74,10 @@ while [[ $# -gt 0 ]]; do
|
|
|
68
74
|
MODE="apply"
|
|
69
75
|
shift
|
|
70
76
|
;;
|
|
77
|
+
--capability-overlay)
|
|
78
|
+
CAPABILITY_OVERLAY=1
|
|
79
|
+
shift
|
|
80
|
+
;;
|
|
71
81
|
--live-root)
|
|
72
82
|
LIVE_ROOT="$2"
|
|
73
83
|
shift 2
|
|
@@ -84,6 +94,11 @@ while [[ $# -gt 0 ]]; do
|
|
|
84
94
|
esac
|
|
85
95
|
done
|
|
86
96
|
|
|
97
|
+
source_for() {
|
|
98
|
+
local rel="$1"
|
|
99
|
+
printf '%s\n' "$SOURCE_ROOT/$rel"
|
|
100
|
+
}
|
|
101
|
+
|
|
87
102
|
collect_extra_live_assets() {
|
|
88
103
|
local root="$1"
|
|
89
104
|
local -A managed=()
|
|
@@ -174,7 +189,7 @@ run_check() {
|
|
|
174
189
|
ensure_source_assets_exist
|
|
175
190
|
|
|
176
191
|
for rel in "${ASSETS[@]}"; do
|
|
177
|
-
src
|
|
192
|
+
src=$(source_for "$rel")
|
|
178
193
|
dst="$LIVE_ROOT/$rel"
|
|
179
194
|
|
|
180
195
|
if [[ ! -f "$dst" ]]; then
|
|
@@ -219,7 +234,7 @@ run_apply() {
|
|
|
219
234
|
ensure_source_assets_exist
|
|
220
235
|
|
|
221
236
|
for rel in "${ASSETS[@]}"; do
|
|
222
|
-
src
|
|
237
|
+
src=$(source_for "$rel")
|
|
223
238
|
dst="$LIVE_ROOT/$rel"
|
|
224
239
|
|
|
225
240
|
mkdir -p "$(dirname "$dst")"
|
|
@@ -252,3 +267,9 @@ case "$MODE" in
|
|
|
252
267
|
run_apply
|
|
253
268
|
;;
|
|
254
269
|
esac
|
|
270
|
+
|
|
271
|
+
plugin_args=("--$MODE" "--live-root" "$LIVE_ROOT")
|
|
272
|
+
if [[ "$CAPABILITY_OVERLAY" -eq 1 ]]; then
|
|
273
|
+
plugin_args+=("--capability-overlay")
|
|
274
|
+
fi
|
|
275
|
+
"$ROOT_DIR/scripts/sync-opencode-plugin-assets.sh" "${plugin_args[@]}"
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
usage() {
|
|
5
|
+
cat <<'EOF'
|
|
6
|
+
Usage: sync-pi-assets.sh [--check|--apply] [--live-root PATH]
|
|
7
|
+
|
|
8
|
+
Compare or sync the repo-managed Pi extension assets against the live Pi agent
|
|
9
|
+
configuration tree.
|
|
10
|
+
|
|
11
|
+
Options:
|
|
12
|
+
--check Verify managed live assets match the rendered repo copies (default)
|
|
13
|
+
--apply Install managed assets, then verify
|
|
14
|
+
--live-root PATH Override the Pi agent root (default: $PI_CODING_AGENT_DIR or ~/.pi/agent)
|
|
15
|
+
-h, --help Show this help text
|
|
16
|
+
EOF
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
20
|
+
SOURCE_ROOT="$ROOT_DIR/pi"
|
|
21
|
+
LIVE_ROOT="${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}"
|
|
22
|
+
MODE="check"
|
|
23
|
+
ASSET="extensions/cairnkeep-trajectory.ts"
|
|
24
|
+
|
|
25
|
+
while [[ $# -gt 0 ]]; do
|
|
26
|
+
case "$1" in
|
|
27
|
+
--check) MODE="check"; shift ;;
|
|
28
|
+
--apply) MODE="apply"; shift ;;
|
|
29
|
+
--live-root) LIVE_ROOT="$2"; shift 2 ;;
|
|
30
|
+
-h|--help) usage; exit 0 ;;
|
|
31
|
+
*) echo "Unknown argument: $1" >&2; usage >&2; exit 2 ;;
|
|
32
|
+
esac
|
|
33
|
+
done
|
|
34
|
+
|
|
35
|
+
render_asset() {
|
|
36
|
+
sed "s|@@INFRA_ROOT@@|$ROOT_DIR|g" "$SOURCE_ROOT/$ASSET"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
check_asset() {
|
|
40
|
+
[[ -f "$SOURCE_ROOT/$ASSET" ]] || {
|
|
41
|
+
echo "Missing repo-managed source asset: $SOURCE_ROOT/$ASSET" >&2
|
|
42
|
+
return 2
|
|
43
|
+
}
|
|
44
|
+
[[ -f "$LIVE_ROOT/$ASSET" ]] || {
|
|
45
|
+
echo "Missing live Pi extension under $LIVE_ROOT: $ASSET" >&2
|
|
46
|
+
return 1
|
|
47
|
+
}
|
|
48
|
+
cmp -s <(render_asset) "$LIVE_ROOT/$ASSET" || {
|
|
49
|
+
echo "Out-of-sync live Pi extension under $LIVE_ROOT: $ASSET" >&2
|
|
50
|
+
return 1
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if [[ "$MODE" == "apply" ]]; then
|
|
55
|
+
[[ -f "$SOURCE_ROOT/$ASSET" ]] || {
|
|
56
|
+
echo "Missing repo-managed source asset: $SOURCE_ROOT/$ASSET" >&2
|
|
57
|
+
exit 2
|
|
58
|
+
}
|
|
59
|
+
mkdir -p "$(dirname "$LIVE_ROOT/$ASSET")"
|
|
60
|
+
if [[ -f "$LIVE_ROOT/$ASSET" ]] && cmp -s <(render_asset) "$LIVE_ROOT/$ASSET"; then
|
|
61
|
+
echo "Applied 0 Pi extension asset(s); 1 already matched."
|
|
62
|
+
else
|
|
63
|
+
rendered_tmp=$(mktemp)
|
|
64
|
+
render_asset > "$rendered_tmp"
|
|
65
|
+
install -m 0644 "$rendered_tmp" "$LIVE_ROOT/$ASSET"
|
|
66
|
+
rm -f "$rendered_tmp"
|
|
67
|
+
echo "Applied 1 Pi extension asset(s); 0 already matched."
|
|
68
|
+
fi
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
check_asset
|
|
72
|
+
printf 'Pi extension assets are in sync under %s\n' "$LIVE_ROOT"
|
|
@@ -26,4 +26,102 @@ grep -q "scripts/memory-wiki-audit.sh --para-root $tmp/PARA --report $tmp/audit.
|
|
|
26
26
|
grep -q "OnCalendar=Mon \*-\*-\* 04:00:00" "$tmr" || fail "timer OnCalendar not substituted"
|
|
27
27
|
grep -q "WantedBy=timers.target" "$tmr" || fail "timer missing [Install] WantedBy"
|
|
28
28
|
|
|
29
|
+
# Scheduled note distillation stays in the same separate audit process and is
|
|
30
|
+
# activated only by the effective note capability state.
|
|
31
|
+
npm --prefix "$ROOT/mcp-memory-server" run build >/dev/null
|
|
32
|
+
mkdir -p "$tmp/PARA/Projects/empty-project"
|
|
33
|
+
[[ -x "$ROOT/scripts/memory-wiki-audit.sh" ]] || fail "audit script is not executable for systemd"
|
|
34
|
+
CAIRN_AGENTFS_BASE_DIR="$tmp/store-off" env -u CAIRN_NOTE_DISTILLATION \
|
|
35
|
+
"$ROOT/scripts/memory-wiki-audit.sh" --para-root "$tmp/PARA" >"$tmp/audit-off.md"
|
|
36
|
+
[[ ! -e "$tmp/store-off/notes" ]] || fail "flag-off audit created note storage"
|
|
37
|
+
CAIRN_AGENTFS_BASE_DIR="$tmp/store-on" CAIRN_NOTE_DISTILLATION=1 \
|
|
38
|
+
"$ROOT/scripts/memory-wiki-audit.sh" --para-root "$tmp/PARA" >"$tmp/audit-on.md"
|
|
39
|
+
grep -q "Note distillation" "$tmp/audit-on.md" || fail "flag-on audit omitted note distillation status"
|
|
40
|
+
|
|
41
|
+
managed="$tmp/managed"
|
|
42
|
+
managed_para="$managed/PARA"
|
|
43
|
+
mkdir -p "$managed_para/Projects/empty-project"
|
|
44
|
+
|
|
45
|
+
# Master-off keeps the inherited compatibility path even when staged managed
|
|
46
|
+
# state says otherwise.
|
|
47
|
+
(cd "$managed" && CAIRN_CAPABILITY_CONTRACT=1 "$ROOT/bin/cairn" capabilities enable notes.distill --json >/dev/null)
|
|
48
|
+
(cd "$managed" && env -u CAIRN_CAPABILITY_CONTRACT -u CAIRN_NOTE_DISTILLATION -u CAIRN_CAPABILITY_NOTES_DISTILL \
|
|
49
|
+
CAIRN_AGENTFS_BASE_DIR="$tmp/store-master-off" \
|
|
50
|
+
"$ROOT/scripts/memory-wiki-audit.sh" --para-root "$managed_para" >"$tmp/audit-master-off.md")
|
|
51
|
+
! grep -q "Note distillation" "$tmp/audit-master-off.md" || fail "master-off audit consumed staged managed state"
|
|
52
|
+
|
|
53
|
+
# Project state and the derived environment override follow the central
|
|
54
|
+
# environment > project > compatibility precedence.
|
|
55
|
+
(cd "$managed" && CAIRN_CAPABILITY_CONTRACT=1 "$ROOT/bin/cairn" capabilities disable notes.distill --json >/dev/null)
|
|
56
|
+
(cd "$managed" && CAIRN_CAPABILITY_CONTRACT=1 CAIRN_NOTE_DISTILLATION=1 \
|
|
57
|
+
"$ROOT/scripts/memory-wiki-audit.sh" --para-root "$managed_para" >"$tmp/audit-managed-disabled.md")
|
|
58
|
+
! grep -q "Note distillation" "$tmp/audit-managed-disabled.md" || fail "managed disable did not override compatibility enable"
|
|
59
|
+
|
|
60
|
+
(cd "$managed" && CAIRN_CAPABILITY_CONTRACT=1 CAIRN_CAPABILITY_NOTES_DISTILL=1 \
|
|
61
|
+
"$ROOT/scripts/memory-wiki-audit.sh" --para-root "$managed_para" >"$tmp/audit-env-enabled.md")
|
|
62
|
+
grep -q "Note distillation: completed" "$tmp/audit-env-enabled.md" || fail "environment enable did not override managed disable"
|
|
63
|
+
|
|
64
|
+
(cd "$managed" && CAIRN_CAPABILITY_CONTRACT=1 "$ROOT/bin/cairn" capabilities enable notes.distill --json >/dev/null)
|
|
65
|
+
(cd "$managed" && CAIRN_CAPABILITY_CONTRACT=1 CAIRN_CAPABILITY_NOTES_DISTILL=0 CAIRN_NOTE_DISTILLATION=1 \
|
|
66
|
+
"$ROOT/scripts/memory-wiki-audit.sh" --para-root "$managed_para" >"$tmp/audit-env-disabled.md")
|
|
67
|
+
! grep -q "Note distillation" "$tmp/audit-env-disabled.md" || fail "environment disable did not override managed enable"
|
|
68
|
+
|
|
69
|
+
(cd "$managed" && CAIRN_CAPABILITY_CONTRACT=1 "$ROOT/bin/cairn" capabilities reset notes.distill --json >/dev/null)
|
|
70
|
+
(cd "$managed" && env -u CAIRN_NOTE_DISTILLATION CAIRN_CAPABILITY_CONTRACT=1 \
|
|
71
|
+
"$ROOT/scripts/memory-wiki-audit.sh" --para-root "$managed_para" >"$tmp/audit-reset-off.md")
|
|
72
|
+
! grep -q "Note distillation" "$tmp/audit-reset-off.md" || fail "reset did not restore inherited default-off state"
|
|
73
|
+
(cd "$managed" && CAIRN_CAPABILITY_CONTRACT=1 CAIRN_NOTE_DISTILLATION=1 \
|
|
74
|
+
"$ROOT/scripts/memory-wiki-audit.sh" --para-root "$managed_para" >"$tmp/audit-reset-on.md")
|
|
75
|
+
grep -q "Note distillation: completed" "$tmp/audit-reset-on.md" || fail "reset did not restore compatibility enable"
|
|
76
|
+
|
|
77
|
+
# Disabled state performs no note or callback-store work even when logging and
|
|
78
|
+
# trajectory capture are both consented.
|
|
79
|
+
(cd "$managed" && CAIRN_CAPABILITY_CONTRACT=1 "$ROOT/bin/cairn" capabilities disable notes.distill --json >/dev/null)
|
|
80
|
+
(cd "$managed" && CAIRN_CAPABILITY_CONTRACT=1 CAIRN_CAPABILITY_LOGGING=1 CAIRN_TRAJECTORY_CAPTURE=1 \
|
|
81
|
+
"$ROOT/scripts/memory-wiki-audit.sh" --para-root "$managed_para" >"$tmp/audit-disabled-consented.md")
|
|
82
|
+
[[ ! -e "$managed/.agentfs/trajectory.db" ]] || fail "disabled audit created callback storage"
|
|
83
|
+
|
|
84
|
+
# Enabled jobs create callback metadata only when all three consents are on.
|
|
85
|
+
logging="$tmp/logging"
|
|
86
|
+
logging_para="$logging/PARA"
|
|
87
|
+
mkdir -p "$logging_para/Projects/empty-project"
|
|
88
|
+
(cd "$logging" && CAIRN_CAPABILITY_CONTRACT=1 "$ROOT/bin/cairn" capabilities enable notes.distill --json >/dev/null)
|
|
89
|
+
(cd "$logging" && CAIRN_CAPABILITY_CONTRACT=1 CAIRN_CAPABILITY_LOGGING=1 \
|
|
90
|
+
"$ROOT/scripts/memory-wiki-audit.sh" --para-root "$logging_para" >"$tmp/audit-no-capture.md")
|
|
91
|
+
[[ ! -e "$logging/.agentfs/trajectory.db" ]] || fail "capture-off audit created callback storage"
|
|
92
|
+
(cd "$logging" && CAIRN_CAPABILITY_CONTRACT=1 CAIRN_TRAJECTORY_CAPTURE=1 \
|
|
93
|
+
"$ROOT/scripts/memory-wiki-audit.sh" --para-root "$logging_para" >"$tmp/audit-no-logging.md")
|
|
94
|
+
[[ ! -e "$logging/.agentfs/trajectory.db" ]] || fail "logging-off audit created callback storage"
|
|
95
|
+
(cd "$logging" && CAIRN_CAPABILITY_CONTRACT=1 CAIRN_CAPABILITY_LOGGING=1 CAIRN_TRAJECTORY_CAPTURE=1 \
|
|
96
|
+
"$ROOT/scripts/memory-wiki-audit.sh" --para-root "$logging_para" >"$tmp/audit-logged.md")
|
|
97
|
+
PROJECT="$logging" node --input-type=module -e '
|
|
98
|
+
const { listCapabilityRecords } = await import("./mcp-memory-server/dist/capability-store.js");
|
|
99
|
+
const rows = (await listCapabilityRecords(process.env.PROJECT)).records;
|
|
100
|
+
if (rows.length !== 1 || rows[0].capability_id !== "notes.distill" || rows[0].outcome !== "success"
|
|
101
|
+
|| rows[0].source !== "notes-cli" || rows[0].transport !== "local-process") process.exit(1);
|
|
102
|
+
' || fail "all-consent audit did not create one exact note callback record"
|
|
103
|
+
|
|
104
|
+
# A note child failure remains visible but cannot mask deterministic wiki
|
|
105
|
+
# findings or change their established exit code.
|
|
106
|
+
failure="$tmp/failure"
|
|
107
|
+
failure_para="$failure/PARA"
|
|
108
|
+
wiki="$failure_para/Projects/stale/.planning/wiki"
|
|
109
|
+
mkdir -p "$wiki/sources" "$failure/fake-bin"
|
|
110
|
+
printf '%s\n' '- Last reviewed: 2000-01-01' >"$wiki/sources/stale.md"
|
|
111
|
+
printf '%s\n' '# Index' >"$wiki/index.md"
|
|
112
|
+
(cd "$failure" && CAIRN_CAPABILITY_CONTRACT=1 "$ROOT/bin/cairn" capabilities enable notes.distill --json >/dev/null)
|
|
113
|
+
real_node=$(command -v node)
|
|
114
|
+
printf '%s\n' '#!/usr/bin/env bash' \
|
|
115
|
+
'if [[ "$1" == *"/note-cli.js" ]]; then exit 42; fi' \
|
|
116
|
+
'exec "$REAL_NODE" "$@"' >"$failure/fake-bin/node"
|
|
117
|
+
chmod +x "$failure/fake-bin/node"
|
|
118
|
+
set +e
|
|
119
|
+
(cd "$failure" && REAL_NODE="$real_node" PATH="$failure/fake-bin:$PATH" CAIRN_CAPABILITY_CONTRACT=1 \
|
|
120
|
+
"$ROOT/scripts/memory-wiki-audit.sh" --para-root "$failure_para" >"$tmp/audit-failed-note.md")
|
|
121
|
+
audit_status=$?
|
|
122
|
+
set -e
|
|
123
|
+
[[ "$audit_status" == 3 ]] || fail "note failure changed deterministic wiki audit exit status"
|
|
124
|
+
grep -q "stale source pages" "$tmp/audit-failed-note.md" || fail "note failure masked deterministic wiki findings"
|
|
125
|
+
grep -q "Note distillation: failed" "$tmp/audit-failed-note.md" || fail "note failure status was not reported"
|
|
126
|
+
|
|
29
127
|
echo "PASS: audit-timer render (placeholders substituted, units well-formed)"
|
|
@@ -11,6 +11,36 @@ fail() {
|
|
|
11
11
|
exit 1
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
capability_env_keys='CAIRN_CAPABILITY_CONTRACT CAIRN_CAPABILITY_LOGGING CAIRN_CAPABILITY_MEMORY_WRITE CAIRN_CAPABILITY_MEMORY_SEARCH CAIRN_CAPABILITY_NOTES_DISTILL CAIRN_CAPABILITY_WIKI CAIRN_CAPABILITY_GRAPH CAIRN_CAPABILITY_SECURITY_AUDIT CAIRN_CAPABILITY_ROUTE_CHECK CAIRN_CAPABILITY_CONTEXT_EXPLORE'
|
|
15
|
+
for key in $capability_env_keys; do
|
|
16
|
+
[[ $(grep -cxF "# $key=" "$ROOT/templates/env.example.template") -eq 1 ]] ||
|
|
17
|
+
fail "environment template must document exactly one commented $key entry"
|
|
18
|
+
if grep -q "^[[:space:]]*$key=" "$ROOT/templates/env.example.template"; then
|
|
19
|
+
fail "environment template enabled $key by default"
|
|
20
|
+
fi
|
|
21
|
+
done
|
|
22
|
+
node - "$ROOT/mcp-memory-server/src/capability-registry.ts" "$ROOT/templates/env.example.template" <<'NODE'
|
|
23
|
+
const fs = require("fs");
|
|
24
|
+
const [registryPath, templatePath] = process.argv.slice(2);
|
|
25
|
+
const registry = fs.readFileSync(registryPath, "utf8");
|
|
26
|
+
const template = fs.readFileSync(templatePath, "utf8");
|
|
27
|
+
const ids = [...registry.matchAll(/^\s*id: "([^"]+)",$/gm)].map((match) => match[1]);
|
|
28
|
+
const derived = ids.map((id) => `CAIRN_CAPABILITY_${id.toUpperCase().replaceAll(".", "_")}`);
|
|
29
|
+
const documented = [...template.matchAll(/^# (CAIRN_CAPABILITY_[A-Z_]+)=$/gm)]
|
|
30
|
+
.map((match) => match[1])
|
|
31
|
+
.filter((name) => name !== "CAIRN_CAPABILITY_CONTRACT" && name !== "CAIRN_CAPABILITY_LOGGING");
|
|
32
|
+
if (JSON.stringify(documented) !== JSON.stringify(derived)) process.exit(1);
|
|
33
|
+
NODE
|
|
34
|
+
grep -qF 'Strict booleans: 1 | true | yes | on; 0 | false | no | off.' "$ROOT/templates/env.example.template" ||
|
|
35
|
+
fail "environment template omitted strict capability boolean guidance"
|
|
36
|
+
grep -qF 'CAIRN_NOTE_DISTILLATION remains a compatibility input' "$ROOT/templates/env.example.template" ||
|
|
37
|
+
fail "environment template omitted the existing note compatibility input"
|
|
38
|
+
grep -qF 'CAIRN_TRAJECTORY_CAPTURE is separate required consent' "$ROOT/templates/env.example.template" ||
|
|
39
|
+
fail "environment template omitted independent trajectory consent"
|
|
40
|
+
if grep -Eqi '(prisma|drizzle|typeorm|sequelize|db[[:space:]]+push|schema[[:space:]-]+push|migrat(e|ion))' "$ROOT/scripts/bootstrap.sh"; then
|
|
41
|
+
fail "bootstrap contains an ORM, schema-push, or migration path"
|
|
42
|
+
fi
|
|
43
|
+
|
|
14
44
|
# --untracked outside a git repo fails before creating anything
|
|
15
45
|
mkdir "$tmp/plain"
|
|
16
46
|
if "$ROOT/scripts/bootstrap.sh" --untracked "$tmp/plain" >/dev/null 2>&1; then
|
|
@@ -23,17 +53,35 @@ mkdir "$tmp/repo"
|
|
|
23
53
|
git -C "$tmp/repo" init -q
|
|
24
54
|
"$ROOT/scripts/bootstrap.sh" --untracked "$tmp/repo" >/dev/null
|
|
25
55
|
[[ -f "$tmp/repo/.ai/start-claude.sh" ]] || fail "scaffold missing"
|
|
56
|
+
[[ -x "$tmp/repo/.ai/start-kimi.sh" ]] || fail "Kimi launcher missing"
|
|
26
57
|
[[ -f "$tmp/repo/.planning/config.json" ]] || fail "planning layer missing"
|
|
27
58
|
[[ -f "$tmp/repo/.agentfs/.gitignore" ]] || fail "project-memory ignore guard missing"
|
|
28
59
|
[[ -z "$(git -C "$tmp/repo" status --porcelain)" ]] || fail "scaffold visible to git"
|
|
29
60
|
grep -qxF "/.ai/" "$tmp/repo/.git/info/exclude" || fail "missing /.ai/ exclude entry"
|
|
30
61
|
grep -qxF "/.planning/" "$tmp/repo/.git/info/exclude" || fail "missing /.planning/ exclude entry"
|
|
31
62
|
grep -qxF "/.agentfs/" "$tmp/repo/.git/info/exclude" || fail "missing /.agentfs/ exclude entry"
|
|
63
|
+
[[ -f "$tmp/repo/.ai/capabilities.json" ]] || fail "capability configuration missing"
|
|
64
|
+
[[ ! -e "$tmp/repo/.agentfs/trajectory.db" ]] || fail "fresh bootstrap created a callback database"
|
|
65
|
+
|
|
66
|
+
# Existing scaffold and operator configuration bytes are preserved on rerun.
|
|
67
|
+
printf '%s\n' '{"schema_version":1,"capabilities":{"wiki":false},"logging":{"callbacks":true}}' >"$tmp/repo/.ai/capabilities.json"
|
|
68
|
+
chmod 600 "$tmp/repo/.ai/capabilities.json"
|
|
69
|
+
printf '%s\n' '# operator-owned environment' >"$tmp/repo/.ai/env.example"
|
|
70
|
+
asset_paths='.ai/start-claude.sh .ai/start-opencode.sh .ai/start-pi.sh .ai/start-kimi.sh .ai/env.example .ai/trajectory-redaction.json .ai/capabilities.json .agentfs/.gitignore .planning/config.json .planning/wiki/index.md .planning/wiki/policy.md .planning/wiki/CONTRADICTIONS.md .planning/wiki/LOG.md .planning/alignment/policy.md .planning/alignment/gap-register.yaml .planning/graphs/policy.md .planning/graphs/.gitignore .planning/security/policy.md'
|
|
71
|
+
mkdir -p "$tmp/assets-before"
|
|
72
|
+
for path in $asset_paths; do
|
|
73
|
+
cp "$tmp/repo/$path" "$tmp/assets-before/${path//\//__}"
|
|
74
|
+
done
|
|
32
75
|
|
|
33
76
|
# Re-run is idempotent: no duplicate exclude entries
|
|
34
77
|
"$ROOT/scripts/bootstrap.sh" --untracked "$tmp/repo" >/dev/null
|
|
35
78
|
[[ $(grep -cxF "/.ai/" "$tmp/repo/.git/info/exclude") -eq 1 ]] || fail "duplicate exclude entries"
|
|
36
79
|
[[ $(grep -cxF "/.agentfs/" "$tmp/repo/.git/info/exclude") -eq 1 ]] || fail "duplicate .agentfs exclude entry"
|
|
80
|
+
for path in $asset_paths; do
|
|
81
|
+
[[ -e "$tmp/repo/$path" ]] || fail "bootstrap rerun deleted installed asset: $path"
|
|
82
|
+
cmp -s "$tmp/assets-before/${path//\//__}" "$tmp/repo/$path" || fail "bootstrap rerun rewrote installed asset: $path"
|
|
83
|
+
done
|
|
84
|
+
[[ ! -e "$tmp/repo/.agentfs/trajectory.db" ]] || fail "bootstrap rerun created a callback database"
|
|
37
85
|
|
|
38
86
|
# Default mode is unchanged: scaffold stays visible to git
|
|
39
87
|
mkdir "$tmp/repo2"
|
|
@@ -41,5 +89,6 @@ git -C "$tmp/repo2" init -q
|
|
|
41
89
|
"$ROOT/scripts/bootstrap.sh" "$tmp/repo2" >/dev/null
|
|
42
90
|
git -C "$tmp/repo2" status --porcelain | grep -q "\.ai/" || fail "default mode should leave the scaffold tracked"
|
|
43
91
|
git -C "$tmp/repo2" status --porcelain | grep -q "\.agentfs/" || fail "default mode should track the memory ignore guard"
|
|
92
|
+
[[ ! -e "$tmp/repo2/.agentfs/trajectory.db" ]] || fail "default bootstrap created a callback database"
|
|
44
93
|
|
|
45
94
|
echo "PASS: bootstrap --untracked contributor mode"
|
|
@@ -4,21 +4,60 @@ set -euo pipefail
|
|
|
4
4
|
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
5
5
|
fail() { echo "FAIL: $1" >&2; exit 1; }
|
|
6
6
|
cairn="$ROOT/bin/cairn"
|
|
7
|
+
tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT
|
|
7
8
|
|
|
8
9
|
"$cairn" help | grep -q "cairn doctor" || fail "help missing doctor"
|
|
9
10
|
"$cairn" help | grep -q "cairn memory" || fail "help missing memory"
|
|
10
11
|
"$cairn" help | grep -q "cairn audit-timer" || fail "help missing audit-timer"
|
|
11
12
|
"$cairn" help | grep -q "cairn sync" || fail "help missing sync"
|
|
13
|
+
"$cairn" help | grep -q "cairn sync-pi" || fail "help missing sync-pi"
|
|
12
14
|
"$cairn" help | grep -q "cairn memory-server" || fail "help missing memory-server"
|
|
15
|
+
"$cairn" help | grep -q "cairn notes" || fail "help missing notes"
|
|
16
|
+
"$cairn" help | grep -q "cairn artifact <list|show|delete|prune>" || fail "help missing artifact"
|
|
17
|
+
"$cairn" help | grep -q "cairn capabilities <list|status|enable|disable|reset|logging>" || fail "help missing capabilities"
|
|
13
18
|
"$cairn" sync --help >/dev/null 2>&1 || fail "cairn sync dispatch"
|
|
19
|
+
"$cairn" sync-pi --help >/dev/null 2>&1 || fail "cairn sync-pi dispatch"
|
|
20
|
+
env -u CAIRN_NOTE_DISTILLATION "$cairn" notes --help >/dev/null 2>&1 || fail "cairn notes dispatch"
|
|
21
|
+
"$cairn" artifact --help | grep -q "cairn artifact list" || fail "cairn artifact dispatch"
|
|
22
|
+
"$cairn" capabilities --help >"$tmp/capabilities-help" 2>/dev/null || fail "cairn capabilities dispatch"
|
|
23
|
+
grep -qxF 'MCP capability changes require a memory-server restart. Operating capability' "$tmp/capabilities-help" || fail "capability help missing exact restart wording"
|
|
24
|
+
grep -qxF 'changes apply on the next invocation.' "$tmp/capabilities-help" || fail "capability help missing exact invocation wording"
|
|
25
|
+
if grep -Eq '^ cairn capabilities (guard|start|finish|doctor)' "$tmp/capabilities-help"; then
|
|
26
|
+
fail "capability help exposed a private operation"
|
|
27
|
+
fi
|
|
14
28
|
if "$cairn" not-a-command >/dev/null 2>&1; then
|
|
15
29
|
fail "unknown command should exit non-zero"
|
|
16
30
|
fi
|
|
17
31
|
|
|
18
|
-
|
|
32
|
+
mkdir -p "$tmp/prefix/bin" "$tmp/project"
|
|
33
|
+
ln -s "$ROOT/bin/cairn" "$tmp/prefix/bin/cairn"
|
|
34
|
+
(
|
|
35
|
+
cd "$tmp/project"
|
|
36
|
+
env -u CAIRN_CAPABILITY_CONTRACT "$tmp/prefix/bin/cairn" capabilities status --json >"$tmp/status-one.json"
|
|
37
|
+
env -u CAIRN_CAPABILITY_CONTRACT "$tmp/prefix/bin/cairn" capabilities status --json >"$tmp/status-two.json"
|
|
38
|
+
)
|
|
39
|
+
cmp -s "$tmp/status-one.json" "$tmp/status-two.json" || fail "installed capability status JSON is unstable"
|
|
40
|
+
node - "$tmp/status-one.json" <<'NODE'
|
|
41
|
+
const fs = require("fs");
|
|
42
|
+
const value = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
|
43
|
+
const ids = ["memory.write", "memory.search", "notes.distill", "wiki", "graph", "security.audit", "route.check", "context.explore"];
|
|
44
|
+
if (value.schema_version !== 1 || value.contract_enabled !== false) process.exit(1);
|
|
45
|
+
if (JSON.stringify(value.capabilities.map(({ id }) => id)) !== JSON.stringify(ids)) process.exit(1);
|
|
46
|
+
if (!/^[0-9a-f]{64}$/.test(value.configuration_digest)) process.exit(1);
|
|
47
|
+
NODE
|
|
48
|
+
set +e
|
|
49
|
+
(cd "$tmp/project" && "$tmp/prefix/bin/cairn" capabilities PHASE18_RAW_SENTINEL) >"$tmp/unknown.out" 2>"$tmp/unknown.err"
|
|
50
|
+
unknown_status=$?
|
|
51
|
+
set -e
|
|
52
|
+
[[ "$unknown_status" -eq 2 ]] || fail "unknown capability operation should exit 2"
|
|
53
|
+
[[ ! -s "$tmp/unknown.out" ]] || fail "unknown capability operation wrote stdout"
|
|
54
|
+
grep -qxF 'cairn capabilities: invalid command, arguments, or managed state.' "$tmp/unknown.err" || fail "unknown capability operation was not value-free"
|
|
55
|
+
if grep -qF 'PHASE18_RAW_SENTINEL' "$tmp/unknown.out" "$tmp/unknown.err"; then
|
|
56
|
+
fail "unknown capability operation exposed its raw value"
|
|
57
|
+
fi
|
|
19
58
|
CAIRN_AGENTFS_BASE_DIR="$tmp/store" "$cairn" memory path | grep -qx "$tmp/store" || fail "cairn memory path dispatch"
|
|
20
59
|
( cd "$tmp" && "$cairn" doctor ) >/dev/null || fail "cairn doctor dispatch (clean env should exit 0)"
|
|
21
60
|
"$cairn" audit-timer --render-only "$tmp/u" >/dev/null || fail "cairn audit-timer dispatch"
|
|
22
61
|
[[ -f "$tmp/u/memory-wiki-audit.timer" ]] || fail "audit-timer render via cairn produced no units"
|
|
23
62
|
|
|
24
|
-
echo "PASS: cairn dispatch (doctor, memory, audit-timer)"
|
|
63
|
+
echo "PASS: cairn dispatch (doctor, memory, artifact, audit-timer)"
|