@cairnkeep/cli 2.7.0 → 2.9.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.
Files changed (60) hide show
  1. package/CHANGELOG.md +44 -1
  2. package/README.md +51 -2
  3. package/bin/cairn +19 -1
  4. package/docs/context-packs.md +94 -0
  5. package/docs/design/meta-agent.md +39 -35
  6. package/docs/harness-compatibility.md +7 -1
  7. package/docs/learning/CURRICULUM-MAP.md +14 -4
  8. package/docs/learning/FEATURE-GUIDE.md +11 -4
  9. package/docs/learning/README.md +8 -2
  10. package/docs/learning/lessons/L00-why-cairnkeep.md +1 -1
  11. package/docs/learning/lessons/L01-safe-trial.md +3 -3
  12. package/docs/learning/lessons/L02-installation.md +2 -2
  13. package/docs/learning/lessons/L03-first-project.md +1 -1
  14. package/docs/learning/lessons/L17-agent-boundary.md +12 -8
  15. package/docs/learning/lessons/L18-local-code-graph.md +97 -0
  16. package/docs/learning/lessons/L19-validated-skill-improvement.md +118 -0
  17. package/docs/learning/lessons/L20-mcp-tool-profiles.md +67 -0
  18. package/docs/learning/lessons/L21-context-packs.md +79 -0
  19. package/docs/learning/tracks/evidence-and-evaluation.md +5 -3
  20. package/docs/learning/tracks/operator.md +4 -2
  21. package/docs/learning/tracks/practitioner.md +5 -4
  22. package/docs/mcp-tool-profiles.md +43 -0
  23. package/docs/operating.md +61 -0
  24. package/docs/privacy-and-data-flow.md +59 -1
  25. package/docs/skill-improvement.md +124 -0
  26. package/docs/storage.md +42 -0
  27. package/examples/eval/bundled-fake.json +1 -1
  28. package/mcp-memory-server/dist/context-pack-cli.js +185 -0
  29. package/mcp-memory-server/dist/context-pack.js +830 -0
  30. package/mcp-memory-server/dist/eval-plan.js +22 -5
  31. package/mcp-memory-server/dist/eval-process.js +31 -0
  32. package/mcp-memory-server/dist/eval-report.js +1 -0
  33. package/mcp-memory-server/dist/eval-runner.js +12 -4
  34. package/mcp-memory-server/dist/eval-schema.js +2 -1
  35. package/mcp-memory-server/dist/eval-workspace.js +50 -1
  36. package/mcp-memory-server/dist/index.js +68 -17
  37. package/mcp-memory-server/dist/mcp-tool-catalog.js +47 -0
  38. package/mcp-memory-server/dist/mcp-tool-cli.js +85 -0
  39. package/mcp-memory-server/dist/mcp-tool-profile.js +162 -0
  40. package/mcp-memory-server/dist/skill-cli.js +236 -0
  41. package/mcp-memory-server/dist/skill-evaluation.js +244 -0
  42. package/mcp-memory-server/dist/skill-schema.js +164 -0
  43. package/mcp-memory-server/dist/skill-store.js +583 -0
  44. package/mcp-memory-server/package.json +4 -1
  45. package/package.json +1 -1
  46. package/schemas/context-pack.schema.json +33 -0
  47. package/schemas/eval-report.schema.json +2 -1
  48. package/schemas/skill-adapter.schema.json +39 -0
  49. package/schemas/skill-proposal-protocol.schema.json +97 -0
  50. package/scripts/completion.sh +62 -5
  51. package/scripts/doctor.sh +39 -0
  52. package/scripts/test-cli-dispatch.sh +6 -0
  53. package/scripts/test-completion.sh +16 -0
  54. package/scripts/test-learning-docs.sh +13 -3
  55. package/scripts/test-package-install.sh +35 -0
  56. package/scripts/test-skill-cli.sh +30 -0
  57. package/scripts/test-uninstall.sh +21 -0
  58. package/scripts/uninstall.sh +26 -1
  59. package/templates/env.example.template +11 -0
  60. package/templates/graph-policy.md.template +3 -1
@@ -0,0 +1,39 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://raw.githubusercontent.com/cairnkeep/cairnkeep/main/schemas/skill-adapter.schema.json",
4
+ "title": "Cairnkeep skill proposal adapter configuration v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_version", "id", "command", "limits"],
8
+ "properties": {
9
+ "schema_version": { "const": 1 },
10
+ "id": { "$ref": "#/$defs/identifier" },
11
+ "command": {
12
+ "type": "object",
13
+ "additionalProperties": false,
14
+ "required": ["program", "args"],
15
+ "properties": {
16
+ "program": { "type": "string", "minLength": 1, "maxLength": 4096 },
17
+ "args": { "type": "array", "maxItems": 128, "items": { "type": "string", "maxLength": 16384 } }
18
+ }
19
+ },
20
+ "environment_allowlist": {
21
+ "type": "array",
22
+ "maxItems": 64,
23
+ "default": [],
24
+ "items": { "type": "string", "pattern": "^[A-Z][A-Z0-9_]{0,127}$" }
25
+ },
26
+ "limits": {
27
+ "type": "object",
28
+ "additionalProperties": false,
29
+ "required": ["elapsed_ms", "stdout_bytes"],
30
+ "properties": {
31
+ "elapsed_ms": { "type": "integer", "minimum": 1, "maximum": 3600000 },
32
+ "stdout_bytes": { "type": "integer", "minimum": 1, "maximum": 4194304 }
33
+ }
34
+ }
35
+ },
36
+ "$defs": {
37
+ "identifier": { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[a-z0-9][a-z0-9._-]*$" }
38
+ }
39
+ }
@@ -0,0 +1,97 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://raw.githubusercontent.com/cairnkeep/cairnkeep/main/schemas/skill-proposal-protocol.schema.json",
4
+ "title": "Cairnkeep skill proposal adapter protocol v1",
5
+ "oneOf": [{ "$ref": "#/$defs/request" }, { "$ref": "#/$defs/response" }],
6
+ "$defs": {
7
+ "identifier": { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[a-z0-9][a-z0-9._-]*$" },
8
+ "digest": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
9
+ "timestamp": { "type": "string", "format": "date-time" },
10
+ "relativePath": {
11
+ "type": "string",
12
+ "minLength": 1,
13
+ "maxLength": 1024,
14
+ "pattern": "^(?!/)(?!.*(?:^|/)(?:\\.|\\.\\.)(?:/|$))(?!.*\\\\)(?!.*//)(?!.*[\\u0000-\\u001f\\u007f])(?:.*\\/)?SKILL\\.md$"
15
+ },
16
+ "occurrence": {
17
+ "type": "object",
18
+ "additionalProperties": false,
19
+ "required": ["session_id", "session_digest", "ended_at", "outcome", "evidence"],
20
+ "properties": {
21
+ "session_id": { "type": "string", "minLength": 1, "maxLength": 256 },
22
+ "session_digest": { "$ref": "#/$defs/digest" },
23
+ "ended_at": { "$ref": "#/$defs/timestamp" },
24
+ "outcome": { "enum": ["failure", "resolution", "abandonment"] },
25
+ "tool_name": { "type": "string", "minLength": 1, "maxLength": 256 },
26
+ "evidence": { "type": "string", "maxLength": 4096 }
27
+ }
28
+ },
29
+ "candidate": {
30
+ "type": "object",
31
+ "additionalProperties": false,
32
+ "required": ["schema_version", "id", "project_id", "status", "note_id", "source_digest", "title", "description", "failure_family", "failure_count", "resolution_count", "occurrences", "lessons", "caveats", "created_at", "reviewed_at"],
33
+ "properties": {
34
+ "schema_version": { "const": 1 },
35
+ "id": { "$ref": "#/$defs/identifier" },
36
+ "project_id": { "$ref": "#/$defs/identifier" },
37
+ "status": { "const": "approved" },
38
+ "note_id": { "$ref": "#/$defs/identifier" },
39
+ "source_digest": { "$ref": "#/$defs/digest" },
40
+ "title": { "type": "string", "minLength": 1, "maxLength": 512 },
41
+ "description": { "type": "string", "maxLength": 4096 },
42
+ "failure_family": { "$ref": "#/$defs/identifier" },
43
+ "failure_count": { "type": "integer", "minimum": 2, "maximum": 1024 },
44
+ "resolution_count": { "type": "integer", "minimum": 1, "maximum": 1024 },
45
+ "occurrences": { "type": "array", "minItems": 2, "maxItems": 64, "items": { "$ref": "#/$defs/occurrence" } },
46
+ "lessons": { "type": "array", "maxItems": 16, "items": { "type": "string", "minLength": 1, "maxLength": 1024 } },
47
+ "caveats": { "type": "array", "maxItems": 16, "items": { "type": "string", "minLength": 1, "maxLength": 1024 } },
48
+ "created_at": { "$ref": "#/$defs/timestamp" },
49
+ "reviewed_at": { "$ref": "#/$defs/timestamp" }
50
+ }
51
+ },
52
+ "edit": {
53
+ "type": "object",
54
+ "additionalProperties": false,
55
+ "required": ["operation", "anchor", "content", "rationale"],
56
+ "properties": {
57
+ "operation": { "enum": ["add", "replace", "delete"] },
58
+ "anchor": { "oneOf": [{ "type": "string", "minLength": 1, "maxLength": 16384 }, { "type": "null" }] },
59
+ "content": { "type": "string", "maxLength": 65536 },
60
+ "rationale": { "type": "string", "minLength": 1, "maxLength": 2048 }
61
+ }
62
+ },
63
+ "request": {
64
+ "type": "object",
65
+ "additionalProperties": false,
66
+ "required": ["schema_version", "operation", "candidate", "candidate_digest", "target", "edit_budget"],
67
+ "properties": {
68
+ "schema_version": { "const": 1 },
69
+ "operation": { "const": "propose" },
70
+ "candidate": { "$ref": "#/$defs/candidate" },
71
+ "candidate_digest": { "$ref": "#/$defs/digest" },
72
+ "target": {
73
+ "type": "object",
74
+ "additionalProperties": false,
75
+ "required": ["path", "baseline_digest", "content"],
76
+ "properties": {
77
+ "path": { "$ref": "#/$defs/relativePath" },
78
+ "baseline_digest": { "$ref": "#/$defs/digest" },
79
+ "content": { "type": "string", "maxLength": 262144 }
80
+ }
81
+ },
82
+ "edit_budget": { "type": "integer", "minimum": 1, "maximum": 16 }
83
+ }
84
+ },
85
+ "response": {
86
+ "type": "object",
87
+ "additionalProperties": false,
88
+ "required": ["schema_version", "status"],
89
+ "properties": {
90
+ "schema_version": { "const": 1 },
91
+ "status": { "enum": ["completed", "adapter_error"] },
92
+ "error_code": { "$ref": "#/$defs/identifier" },
93
+ "edits": { "type": "array", "maxItems": 16, "default": [], "items": { "$ref": "#/$defs/edit" } }
94
+ }
95
+ }
96
+ }
97
+ }
@@ -11,7 +11,7 @@ _cairn_complete() {
11
11
  COMPREPLY=()
12
12
  current=${COMP_WORDS[COMP_CWORD]}
13
13
  previous=${COMP_WORDS[COMP_CWORD-1]:-}
14
- commands="bootstrap memory-server sync sync-pi sync-kimi doctor trajectory artifact capabilities notes eval graph memory audit-timer uninstall completion version help"
14
+ commands="bootstrap 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"
15
15
  if (( COMP_CWORD == 1 )); then
16
16
  COMPREPLY=( $(compgen -W "$commands" -- "$current") )
17
17
  return
@@ -51,6 +51,8 @@ _cairn_complete() {
51
51
  *) COMPREPLY=( $(compgen -W "list status enable disable reset logging" -- "$current") ) ;;
52
52
  esac
53
53
  ;;
54
+ mcp-tools) COMPREPLY=( $(compgen -W "list status set reset full read-only custom --tool --project --json" -- "$current") ) ;;
55
+ pack) COMPREPLY=( $(compgen -W "init lock validate install list show remove enable disable update skills approve-skill revoke-skill --id --version --title --description --license --ref --project --project-id --check --apply --confirm --json" -- "$current") ) ;;
54
56
  notes) COMPREPLY=( $(compgen -W "distill search-error promote doctor --project --session --all-projects --para-root --text --component --with --confirm --repair --json" -- "$current") ) ;;
55
57
  eval)
56
58
  case "${COMP_WORDS[2]:-}" in
@@ -63,10 +65,11 @@ _cairn_complete() {
63
65
  *) COMPREPLY=( $(compgen -W "validate run ablate report prune delete" -- "$current") ) ;;
64
66
  esac
65
67
  ;;
68
+ skill) COMPREPLY=( $(compgen -W "harvest list show review propose evaluate apply rollback --project --minimum-occurrences --kind --id --candidate --approve --target --adapter --edit-budget --proposal --exploration-task-set --confirmation-task-set --output --repetitions --seed --minimum-improvement --evaluation --application --confirm --yes --json" -- "$current") ) ;;
66
69
  graph) COMPREPLY=( $(compgen -W "build query status diff explain path --force" -- "$current") ) ;;
67
70
  memory) COMPREPLY=( $(compgen -W "path export import" -- "$current") ) ;;
68
71
  audit-timer) COMPREPLY=( $(compgen -W "--on-calendar --para-root --render-only" -- "$current") ) ;;
69
- uninstall) COMPREPLY=( $(compgen -W "--dry-run --yes --purge-memory --live-root --pi-live-root --kimi-live-root" -- "$current") ) ;;
72
+ uninstall) COMPREPLY=( $(compgen -W "--dry-run --yes --purge-memory --purge-packs --live-root --pi-live-root --kimi-live-root" -- "$current") ) ;;
70
73
  completion) COMPREPLY=( $(compgen -W "bash zsh fish" -- "$current") ) ;;
71
74
  esac
72
75
  }
@@ -88,8 +91,11 @@ _cairn() {
88
91
  'trajectory:inspect and prune local session trajectories'
89
92
  'artifact:inspect, delete, and prune local artifacts'
90
93
  'capabilities:inspect and manage project capability state'
94
+ 'mcp-tools:inspect and restrict MCP tool exposure'
95
+ 'pack:manage immutable context packs and skill approvals'
91
96
  'notes:distill and search local hindsight notes'
92
97
  'eval:run and inspect default-off local evaluations'
98
+ 'skill:review and evaluate evidence-backed skill improvements'
93
99
  'graph:inspect a published Graphify graph'
94
100
  'memory:manage the durable memory store'
95
101
  'audit-timer:install a memory and wiki audit timer'
@@ -118,6 +124,8 @@ _cairn() {
118
124
  *) _values 'artifact command' list show delete prune ;;
119
125
  esac
120
126
  ;;
127
+ mcp-tools) _values 'MCP tool profile command' list status set reset full read-only custom '--tool[allow an exact tool]:tool:' '--project[project root]:directory:_files -/' '--json[emit JSON]' ;;
128
+ pack) _values 'context pack command' init lock validate install list show remove enable disable update skills approve-skill revoke-skill '--ref[pinned Git ref]:ref:' '--project[project root]:directory:_files -/' '--project-id[remote project ID]:project ID:' '--check[inspect update]' '--apply[apply update]' '--confirm[confirm digest]:digest:' '--json[emit JSON]' ;;
121
129
  capabilities)
122
130
  case $words[3] in
123
131
  list|status) _arguments '--json[emit JSON]' ;;
@@ -139,10 +147,23 @@ _cairn() {
139
147
  *) _values 'eval command' validate run ablate report prune delete ;;
140
148
  esac
141
149
  ;;
150
+ skill)
151
+ case $words[3] in
152
+ harvest) _arguments '--project[project root]:directory:_files -/' '--minimum-occurrences[minimum recurring failures]:count:' '--json[emit JSON]' ;;
153
+ list) _arguments '--project[project root]:directory:_files -/' '--kind[artifact kind]:kind:(candidate proposal evaluation application)' '--json[emit JSON]' ;;
154
+ show) _arguments '--project[project root]:directory:_files -/' '--kind[artifact kind]:kind:(candidate proposal evaluation application)' '--id[artifact ID]:artifact ID:' '--json[emit JSON]' ;;
155
+ review) _arguments '--project[project root]:directory:_files -/' '--candidate[candidate ID]:candidate ID:' '--approve[approve evidence]' '--json[emit JSON]' ;;
156
+ propose) _arguments '--project[project root]:directory:_files -/' '--candidate[candidate ID]:candidate ID:' '--target[existing SKILL.md]:file:_files' '--adapter[proposal adapter configuration]:file:_files' '--edit-budget[maximum edits]:count:' '--json[emit JSON]' ;;
157
+ evaluate) _arguments '--project[project root]:directory:_files -/' '--proposal[proposal ID]:proposal ID:' '--exploration-task-set[exploration task set]:file:_files' '--confirmation-task-set[confirmation task set]:file:_files' '--adapter[evaluation adapter configuration]:file:_files' '--output[report root]:directory:_files -/' '--repetitions[repetition count]:count:' '--seed[deterministic seed]:seed:' '--minimum-improvement[distinct improved tasks]:count:' '--yes[confirm execution]' '--json[emit JSON]' ;;
158
+ apply) _arguments '--project[project root]:directory:_files -/' '--proposal[proposal ID]:proposal ID:' '--evaluation[evaluation ID]:evaluation ID:' '--confirm[exact proposal digest]:digest:' '--json[emit JSON]' ;;
159
+ rollback) _arguments '--project[project root]:directory:_files -/' '--application[application ID]:application ID:' '--confirm[confirm rollback]' '--json[emit JSON]' ;;
160
+ *) _values 'skill command' harvest list show review propose evaluate apply rollback ;;
161
+ esac
162
+ ;;
142
163
  graph) _values 'graph command' build query status diff explain path '--force[allow a smaller graph after code deletion]' ;;
143
164
  memory) _values 'memory command' path export import ;;
144
165
  audit-timer) _arguments '--on-calendar[systemd calendar]:calendar:' '--para-root[PARA root]:directory:_files -/' '--render-only[render directory]:directory:_files -/' ;;
145
- uninstall) _arguments '--dry-run[show changes]' '--yes[skip confirmation]' '--purge-memory[delete memory]' '--live-root[project Claude root]:directory:_files -/' '--pi-live-root[Pi agent root]:directory:_files -/' '--kimi-live-root[Kimi Code root]:directory:_files -/' '*:project directory:_files -/' ;;
166
+ uninstall) _arguments '--dry-run[show changes]' '--yes[skip confirmation]' '--purge-memory[delete memory]' '--purge-packs[delete context packs]' '--live-root[project Claude root]:directory:_files -/' '--pi-live-root[Pi agent root]:directory:_files -/' '--kimi-live-root[Kimi Code root]:directory:_files -/' '*:project directory:_files -/' ;;
146
167
  completion) _values 'shell' bash zsh fish ;;
147
168
  *) _arguments '*:argument:_files' ;;
148
169
  esac
@@ -153,8 +174,8 @@ EOF
153
174
  fish)
154
175
  cat <<'EOF'
155
176
  complete -c cairn -f
156
- for command in bootstrap memory-server sync sync-pi sync-kimi doctor trajectory artifact capabilities notes eval graph memory audit-timer uninstall completion version help
157
- complete -c cairn -n "not __fish_seen_subcommand_from bootstrap memory-server sync sync-pi sync-kimi doctor trajectory artifact capabilities notes eval graph memory audit-timer uninstall completion version help" -a $command
177
+ for command in bootstrap 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
178
+ complete -c cairn -n "not __fish_seen_subcommand_from bootstrap 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" -a $command
158
179
  end
159
180
  complete -c cairn -n "__fish_seen_subcommand_from sync" -l apply
160
181
  complete -c cairn -n "__fish_seen_subcommand_from sync" -l live-root -r
@@ -178,6 +199,17 @@ complete -c cairn -n "__fish_seen_subcommand_from capabilities; and __fish_seen_
178
199
  complete -c cairn -n "__fish_seen_subcommand_from capabilities; and __fish_seen_subcommand_from reset" -l all
179
200
  complete -c cairn -n "__fish_seen_subcommand_from capabilities; and __fish_seen_subcommand_from logging; and not __fish_seen_subcommand_from enable disable reset" -a "enable disable reset"
180
201
  complete -c cairn -n "__fish_seen_subcommand_from capabilities; and __fish_seen_subcommand_from list status enable disable reset logging" -l json
202
+ complete -c cairn -n "__fish_seen_subcommand_from mcp-tools" -a "list status set reset"
203
+ complete -c cairn -n "__fish_seen_subcommand_from mcp-tools; and __fish_seen_subcommand_from set" -a "full read-only custom"
204
+ complete -c cairn -n "__fish_seen_subcommand_from mcp-tools" -l project -r
205
+ complete -c cairn -n "__fish_seen_subcommand_from mcp-tools" -l tool -r
206
+ complete -c cairn -n "__fish_seen_subcommand_from mcp-tools" -l json
207
+ complete -c cairn -n "__fish_seen_subcommand_from pack" -a "init lock validate install list show remove enable disable update skills approve-skill revoke-skill"
208
+ complete -c cairn -n "__fish_seen_subcommand_from pack" -l project -r
209
+ complete -c cairn -n "__fish_seen_subcommand_from pack" -l project-id -r
210
+ complete -c cairn -n "__fish_seen_subcommand_from pack" -l ref -r
211
+ complete -c cairn -n "__fish_seen_subcommand_from pack" -l confirm -r
212
+ complete -c cairn -n "__fish_seen_subcommand_from pack" -l json
181
213
  complete -c cairn -n "__fish_seen_subcommand_from notes" -a "distill search-error promote doctor"
182
214
  complete -c cairn -n "__fish_seen_subcommand_from eval; and not __fish_seen_subcommand_from validate run ablate report prune delete" -a "validate run ablate report prune delete"
183
215
  complete -c cairn -n "__fish_seen_subcommand_from eval; and __fish_seen_subcommand_from validate run ablate" -l task-set -r
@@ -191,11 +223,36 @@ complete -c cairn -n "__fish_seen_subcommand_from eval; and __fish_seen_subcomma
191
223
  complete -c cairn -n "__fish_seen_subcommand_from eval; and __fish_seen_subcommand_from prune" -l older-than-days -r
192
224
  complete -c cairn -n "__fish_seen_subcommand_from eval; and __fish_seen_subcommand_from prune delete" -l dry-run
193
225
  complete -c cairn -n "__fish_seen_subcommand_from eval; and __fish_seen_subcommand_from validate run ablate report prune delete" -l json
226
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and not __fish_seen_subcommand_from harvest list show review propose evaluate apply rollback" -a "harvest list show review propose evaluate apply rollback"
227
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from harvest list show review propose evaluate apply rollback" -l project -r
228
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from harvest list show review propose evaluate apply rollback" -l json
229
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from harvest" -l minimum-occurrences -r
230
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from list show" -l kind -r -a "candidate proposal evaluation application"
231
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from show" -l id -r
232
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from review propose" -l candidate -r
233
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from review" -l approve
234
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from propose" -l target -r
235
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from propose evaluate" -l adapter -r
236
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from propose" -l edit-budget -r
237
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from evaluate apply" -l proposal -r
238
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from evaluate" -l exploration-task-set -r
239
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from evaluate" -l confirmation-task-set -r
240
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from evaluate" -l output -r
241
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from evaluate" -l repetitions -r
242
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from evaluate" -l seed -r
243
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from evaluate" -l minimum-improvement -r
244
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from evaluate" -l yes
245
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from apply" -l evaluation -r
246
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from apply" -l confirm -r
247
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from rollback" -l application -r
248
+ complete -c cairn -n "__fish_seen_subcommand_from skill; and __fish_seen_subcommand_from rollback" -l confirm
194
249
  complete -c cairn -n "__fish_seen_subcommand_from graph" -a "build query status diff explain path"
195
250
  complete -c cairn -n "__fish_seen_subcommand_from graph; and __fish_seen_subcommand_from build" -l force
196
251
  complete -c cairn -n "__fish_seen_subcommand_from completion" -a "bash zsh fish"
197
252
  complete -c cairn -n "__fish_seen_subcommand_from uninstall" -l pi-live-root -r
198
253
  complete -c cairn -n "__fish_seen_subcommand_from uninstall" -l kimi-live-root -r
254
+ complete -c cairn -n "__fish_seen_subcommand_from uninstall" -l purge-memory
255
+ complete -c cairn -n "__fish_seen_subcommand_from uninstall" -l purge-packs
199
256
  EOF
200
257
  ;;
201
258
  *)
package/scripts/doctor.sh CHANGED
@@ -400,6 +400,45 @@ case "$eval_state" in
400
400
  unsafe) warn "evaluation report storage is unsafe" ;;
401
401
  esac
402
402
 
403
+ # 12. MCP least-authority profile. Invalid or unknown custom tool names are a
404
+ # startup failure, so doctor treats them as an actionable configuration error.
405
+ mcp_profile_cli="$CAIRN_ROOT/mcp-memory-server/dist/mcp-tool-cli.js"
406
+ if [[ ! -f "$mcp_profile_cli" ]]; then
407
+ fail "MCP tool profile checker is missing from the installed package"
408
+ elif mcp_profile_json=$(node "$mcp_profile_cli" status --project "$PWD" --json 2>/dev/null); then
409
+ if mcp_profile_summary=$(node - "$mcp_profile_json" <<'NODE'
410
+ try {
411
+ const value = JSON.parse(process.argv[2]);
412
+ if (value.schema_version !== 1 || !["full", "read-only", "custom"].includes(value.mode)
413
+ || !/^[a-f0-9]{64}$/.test(value.profile_digest) || !Array.isArray(value.allowed_tools)) process.exit(1);
414
+ process.stdout.write(`${value.mode}\t${value.profile_digest}`);
415
+ } catch { process.exit(1); }
416
+ NODE
417
+ ); then
418
+ pass "MCP tool profile $(printf '%s' "$mcp_profile_summary" | cut -f1) ($(printf '%s' "$mcp_profile_summary" | cut -f2))"
419
+ else
420
+ fail "MCP tool profile status returned invalid data"
421
+ fi
422
+ else
423
+ fail "MCP tool profile is invalid (check .ai/mcp-tools.json and CAIRN_MCP_TOOL_PROFILE)"
424
+ fi
425
+
426
+ # 13. Context-pack objects and project pointers are verified even when MCP pack
427
+ # retrieval is disabled. The feature gate controls reads, not integrity checks.
428
+ pack_cli="$CAIRN_ROOT/mcp-memory-server/dist/context-pack-cli.js"
429
+ pack_base="${CAIRN_PACK_BASE_DIR:-$HOME/.cairnkeep/packs}"
430
+ pack_base="${pack_base/#\~/$HOME}"
431
+ if [[ ! -e "$pack_base" ]]; then
432
+ skip "context packs (not installed)"
433
+ elif [[ ! -f "$pack_cli" ]]; then
434
+ fail "context-pack checker is missing from the installed package"
435
+ elif pack_json=$(node "$pack_cli" doctor --json 2>/dev/null) && node -e 'const v=JSON.parse(process.argv[1]); if(v.ok!==true)process.exit(1)' "$pack_json"; then
436
+ pack_counts=$(node -e 'const v=JSON.parse(process.argv[1]); process.stdout.write(`${v.objects} object(s), ${v.projects} project pointer(s)`)' "$pack_json")
437
+ pass "context packs healthy ($pack_counts)"
438
+ else
439
+ fail "context-pack objects or project pointers failed integrity checks"
440
+ fi
441
+
403
442
  echo
404
443
  if [[ "$fails" -gt 0 ]]; then
405
444
  echo "cairn doctor: $fails configured dependency check(s) failed."
@@ -16,6 +16,8 @@ tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT
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
18
  "$cairn" help | grep -q "cairn capabilities <list|status|enable|disable|reset|logging>" || fail "help missing capabilities"
19
+ "$cairn" help | grep -q "cairn mcp-tools <list|status|set|reset>" || fail "help missing mcp-tools"
20
+ "$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"
19
21
  "$cairn" help | grep -q "cairn graph <build|query|status|diff|explain|path>" || fail "help missing graph"
20
22
  "$cairn" sync --help >/dev/null 2>&1 || fail "cairn sync dispatch"
21
23
  "$cairn" sync-pi --help >/dev/null 2>&1 || fail "cairn sync-pi dispatch"
@@ -23,6 +25,10 @@ tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT
23
25
  env -u CAIRN_NOTE_DISTILLATION "$cairn" notes --help >/dev/null 2>&1 || fail "cairn notes dispatch"
24
26
  "$cairn" artifact --help | grep -q "cairn artifact list" || fail "cairn artifact dispatch"
25
27
  "$cairn" capabilities --help >"$tmp/capabilities-help" 2>/dev/null || fail "cairn capabilities dispatch"
28
+ "$cairn" mcp-tools list --json >"$tmp/mcp-tools.json" || fail "cairn mcp-tools dispatch"
29
+ CAIRN_PACK_BASE_DIR="$tmp/pack-store" "$cairn" pack list --json >"$tmp/packs.json" || fail "cairn pack dispatch"
30
+ node -e 'const v=require(process.argv[1]);if(v.schema_version!==1||!Array.isArray(v.tools))process.exit(1)' "$tmp/mcp-tools.json" || fail "mcp-tools JSON contract"
31
+ node -e 'const v=require(process.argv[1]);if(v.schema_version!==1||!Array.isArray(v.packs))process.exit(1)' "$tmp/packs.json" || fail "pack JSON contract"
26
32
  "$cairn" graph --help | grep -q "cairn graph explain <symbol>" || fail "cairn graph dispatch"
27
33
  grep -qxF 'MCP capability changes require a memory-server restart. Operating capability' "$tmp/capabilities-help" || fail "capability help missing exact restart wording"
28
34
  grep -qxF 'changes apply on the next invocation.' "$tmp/capabilities-help" || fail "capability help missing exact invocation wording"
@@ -14,6 +14,7 @@ done
14
14
  capability_ids='memory.write memory.search notes.distill wiki graph security.audit route.check context.explore'
15
15
  eval_commands='validate run ablate report prune delete'
16
16
  eval_flags='--task-set --adapter --output --repetitions --seed --json --yes --disable --experiment --older-than-days --dry-run'
17
+ skill_commands='harvest list show review propose evaluate apply rollback'
17
18
  for shell in bash zsh fish; do
18
19
  grep -q 'capabilities' "$tmp/$shell"
19
20
  grep -q 'list.*status.*enable.*disable.*reset.*logging\|list status enable disable reset logging' "$tmp/$shell"
@@ -37,6 +38,16 @@ for shell in bash zsh fish; do
37
38
  echo "completion exposed a private eval lifecycle operation for $shell" >&2
38
39
  exit 1
39
40
  fi
41
+ grep -q 'skill' "$tmp/$shell"
42
+ grep -q 'harvest.*list.*show.*review.*propose.*evaluate.*apply.*rollback\|harvest list show review propose evaluate apply rollback' "$tmp/$shell"
43
+ for skill_flag in --candidate --exploration-task-set --confirmation-task-set --evaluation --application --confirm; do
44
+ grep -q -- "$skill_flag\|-l ${skill_flag#--}" "$tmp/$shell"
45
+ done
46
+ grep -q 'mcp-tools' "$tmp/$shell"
47
+ grep -q 'full.*read-only.*custom\|full read-only custom' "$tmp/$shell"
48
+ grep -q 'pack' "$tmp/$shell"
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
+ grep -q -- '--purge-packs\|-l purge-packs' "$tmp/$shell"
40
51
  done
41
52
 
42
53
  "$ROOT/bin/cairn" help >"$tmp/root-help"
@@ -52,6 +63,11 @@ if grep -q 'doctor-diagnosis' "$tmp/eval-help"; then
52
63
  echo 'eval help exposed a private lifecycle operation' >&2
53
64
  exit 1
54
65
  fi
66
+ grep -qF 'cairn skill <harvest|list|show|review|propose|evaluate|apply|rollback>' "$tmp/root-help"
67
+ node "$ROOT/mcp-memory-server/dist/skill-cli.js" --help >"$tmp/skill-help"
68
+ for command in $skill_commands; do
69
+ grep -q "cairn skill $command" "$tmp/skill-help"
70
+ done
55
71
 
56
72
  node - "$ROOT/mcp-memory-server/src/capability-registry.ts" "$tmp/bash" "$tmp/zsh" "$tmp/fish" <<'NODE'
57
73
  const fs = require("fs");
@@ -15,13 +15,14 @@ grep -qF "**Baseline:** Cairnkeep $version" "$coverage"
15
15
  grep -qF "**Baseline:** Cairnkeep $version" "$feature_guide"
16
16
  grep -qF 'https://github.com/cairnkeep/cairnkeep-course-labs' "$landing"
17
17
  for checkpoint in course-00-app course-01-bootstrap course-02-memory course-03-quality \
18
- course-04-operation course-05-evidence course-06-governance course-07-evaluation; do
18
+ course-04-operation course-05-evidence course-06-governance course-07-evaluation \
19
+ course-08-graph course-09-skill course-10-trust-context; do
19
20
  grep -qF "\`$checkpoint\`" "$coverage"
20
21
  done
21
22
 
22
23
  ready=0
23
24
  brief=0
24
- for number in $(seq -w 0 17); do
25
+ for number in $(seq -w 0 21); do
25
26
  matches=(docs/learning/lessons/L"$number"-*.md)
26
27
  [[ ${#matches[@]} -eq 1 && -f ${matches[0]} ]] || {
27
28
  echo "learning path must contain exactly one L$number lesson" >&2
@@ -43,7 +44,16 @@ for number in $(seq -w 0 17); do
43
44
  fi
44
45
  done
45
46
 
46
- [[ $ready -eq 4 && $brief -eq 14 ]] || {
47
+ graph_lesson=docs/learning/lessons/L18-local-code-graph.md
48
+ for mode in build query status diff explain path; do
49
+ grep -qF "cairn graph $mode" "$graph_lesson"
50
+ done
51
+ grep -qF 'Do not run `graphify install`' "$graph_lesson"
52
+ grep -qF '`cairn sync-kimi --apply`' "$graph_lesson"
53
+ grep -qF '`cairn sync-pi --apply`' "$graph_lesson"
54
+ grep -qF '`graphify-out/`' "$graph_lesson"
55
+
56
+ [[ $ready -eq 7 && $brief -eq 15 ]] || {
47
57
  echo "unexpected learning status totals: ready=$ready brief=$brief" >&2
48
58
  exit 1
49
59
  }
@@ -19,6 +19,7 @@ server_dev_dependencies_before=$(manifest_field "$ROOT/mcp-memory-server/package
19
19
  root_lock_before=$(sha256sum "$ROOT/package-lock.json" | cut -d' ' -f1)
20
20
  server_lock_before=$(sha256sum "$ROOT/mcp-memory-server/package-lock.json" | cut -d' ' -f1)
21
21
  node "$ROOT/mcp-memory-server/dist/eval-cli.js" --help >"$tmp/source-eval-help"
22
+ node "$ROOT/mcp-memory-server/dist/skill-cli.js" --help >"$tmp/source-skill-help"
22
23
  env -u CAIRN_EVAL "$ROOT/bin/cairn" eval validate \
23
24
  --task-set "$tmp/source-unread-task-set" \
24
25
  --adapter "$tmp/source-unread-adapter" \
@@ -52,6 +53,11 @@ env -u CAIRN_NOTE_DISTILLATION cairn notes --help >/dev/null \
52
53
  || fail "installed package omitted the notes command"
53
54
 
54
55
  installed_root="$tmp/prefix/lib/node_modules/@cairnkeep/cli"
56
+ cairn skill --help >"$tmp/installed-skill-help"
57
+ cmp -s "$tmp/source-skill-help" "$tmp/installed-skill-help" || \
58
+ fail "source and installed skill help differ"
59
+ cairn skill list --project "$tmp/project" --json >"$tmp/installed-skill-list.json" || \
60
+ fail "installed package skill list failed"
55
61
  cairn eval --help >"$tmp/installed-eval-help"
56
62
  cmp -s "$tmp/source-eval-help" "$tmp/installed-eval-help" || \
57
63
  fail "source and installed eval help differ"
@@ -80,6 +86,35 @@ do
80
86
  [[ -f "$installed_root/$required" ]] || fail "npm tarball omitted $required"
81
87
  done
82
88
 
89
+ for required in \
90
+ schemas/context-pack.schema.json \
91
+ mcp-memory-server/dist/mcp-tool-catalog.js \
92
+ mcp-memory-server/dist/mcp-tool-profile.js \
93
+ mcp-memory-server/dist/mcp-tool-cli.js \
94
+ mcp-memory-server/dist/context-pack.js \
95
+ mcp-memory-server/dist/context-pack-cli.js \
96
+ docs/mcp-tool-profiles.md \
97
+ docs/context-packs.md
98
+ do
99
+ [[ -f "$installed_root/$required" ]] || fail "npm tarball omitted $required"
100
+ done
101
+ CAIRN_PACK_BASE_DIR="$tmp/installed-packs" cairn pack list --json >"$tmp/installed-pack-list.json" || \
102
+ fail "installed package pack CLI failed"
103
+ cairn mcp-tools list --json >"$tmp/installed-mcp-tools.json" || \
104
+ fail "installed package mcp-tools CLI failed"
105
+
106
+ for required in \
107
+ schemas/skill-adapter.schema.json \
108
+ schemas/skill-proposal-protocol.schema.json \
109
+ mcp-memory-server/dist/skill-schema.js \
110
+ mcp-memory-server/dist/skill-store.js \
111
+ mcp-memory-server/dist/skill-evaluation.js \
112
+ mcp-memory-server/dist/skill-cli.js \
113
+ docs/skill-improvement.md
114
+ do
115
+ [[ -f "$installed_root/$required" ]] || fail "npm tarball omitted $required"
116
+ done
117
+
83
118
  for required in \
84
119
  schemas/eval-task-set.schema.json \
85
120
  schemas/eval-adapter.schema.json \
@@ -0,0 +1,30 @@
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
+
8
+ node "$ROOT/mcp-memory-server/dist/skill-cli.js" --help >"$tmp/help"
9
+ for operation in harvest list show review propose evaluate apply rollback; do
10
+ grep -q "$operation" "$tmp/help" || { echo "FAIL: skill help omitted $operation" >&2; exit 1; }
11
+ done
12
+
13
+ mkdir -p "$tmp/project"
14
+ node "$ROOT/mcp-memory-server/dist/skill-cli.js" list --project "$tmp/project" --json >"$tmp/list.json"
15
+ node -e '
16
+ const value = require(process.argv[1]);
17
+ for (const key of ["candidates", "proposals", "evaluations", "applications"]) {
18
+ if (!Array.isArray(value[key]) || value[key].length !== 0) process.exit(1);
19
+ }
20
+ ' "$tmp/list.json"
21
+
22
+ if env -u CAIRN_EVAL node "$ROOT/mcp-memory-server/dist/skill-cli.js" evaluate \
23
+ --project "$tmp/project" --proposal missing --exploration-task-set missing \
24
+ --confirmation-task-set missing --adapter missing --yes >"$tmp/out" 2>"$tmp/err"; then
25
+ echo "FAIL: disabled skill evaluation succeeded" >&2
26
+ exit 1
27
+ fi
28
+ grep -q 'disabled' "$tmp/err" || { echo "FAIL: disabled skill evaluation did not fail closed" >&2; exit 1; }
29
+
30
+ echo "PASS: installed-style skill CLI discovery and default-off gate"
@@ -303,6 +303,27 @@ check "memory store restored" "$(cat "$SB/home/.cairnkeep/db" 2>/dev/null)"
303
303
  check "note store restored exactly" "$(sha256sum "$SB/home/.cairnkeep/notes/projects/example/hindsight/failure.md" | cut -d' ' -f1)" "$purge_note_before"
304
304
  check "typed and journal state restored exactly" "$(tree_hash "$SB/home/.cairnkeep")" "$purge_store_before"
305
305
 
306
+ # --- context-pack data requires its own explicit purge consent ---------------
307
+ PACK_HOME="$SB/pack-home"
308
+ PACK_STORE="$PACK_HOME/.cairnkeep/packs"
309
+ mkdir -p "$PACK_STORE/objects/example" "$PACK_HOME/.cairnkeep/memory"
310
+ printf 'immutable pack\n' >"$PACK_STORE/objects/example/marker"
311
+ printf 'memory\n' >"$PACK_HOME/.cairnkeep/memory/marker"
312
+ pack_output=$(HOME="$PACK_HOME" XDG_CONFIG_HOME="$PACK_HOME/.config" \
313
+ CAIRN_AGENTFS_BASE_DIR="$PACK_HOME/.cairnkeep" CAIRN_PACK_BASE_DIR="$PACK_STORE" \
314
+ "$ROOT_DIR/scripts/uninstall.sh" --yes --live-root "$SB/pack-live" --pi-live-root "$SB/pack-pi" --kimi-live-root "$SB/pack-kimi" 2>&1)
315
+ check "ordinary uninstall reports retained context packs" "$(grep -q 'kept context packs' <<<"$pack_output" && echo yes || echo no)" "yes"
316
+ check "ordinary uninstall retains context packs" "$([[ -f "$PACK_STORE/objects/example/marker" ]] && echo yes || echo no)" "yes"
317
+ HOME="$PACK_HOME" XDG_CONFIG_HOME="$PACK_HOME/.config" \
318
+ CAIRN_AGENTFS_BASE_DIR="$PACK_HOME/.cairnkeep" CAIRN_PACK_BASE_DIR="$PACK_STORE" \
319
+ "$ROOT_DIR/scripts/uninstall.sh" --yes --purge-memory --live-root "$SB/pack-live" --pi-live-root "$SB/pack-pi" --kimi-live-root "$SB/pack-kimi" >/dev/null 2>&1
320
+ check "memory purge retains context packs" "$([[ -f "$PACK_STORE/objects/example/marker" ]] && echo yes || echo no)" "yes"
321
+ check "memory purge removes non-pack memory" "$([[ -e "$PACK_HOME/.cairnkeep/memory" ]] && echo no || echo yes)" "yes"
322
+ HOME="$PACK_HOME" XDG_CONFIG_HOME="$PACK_HOME/.config" \
323
+ CAIRN_AGENTFS_BASE_DIR="$PACK_HOME/.cairnkeep" CAIRN_PACK_BASE_DIR="$PACK_STORE" \
324
+ "$ROOT_DIR/scripts/uninstall.sh" --yes --purge-packs --live-root "$SB/pack-live" --pi-live-root "$SB/pack-pi" --kimi-live-root "$SB/pack-kimi" >/dev/null 2>&1
325
+ check "explicit pack purge removes context packs" "$([[ -e "$PACK_STORE" ]] && echo no || echo yes)" "yes"
326
+
306
327
  echo
307
328
  if [[ "$fails" -gt 0 ]]; then echo "test-uninstall: $fails check(s) failed."; exit 1; fi
308
329
  echo "test-uninstall: OK"
@@ -25,9 +25,12 @@ PI_LIVE_ROOT="${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}"
25
25
  KIMI_LIVE_ROOT="${KIMI_CODE_HOME:-$HOME/.kimi-code}"
26
26
  STORE_DIR="${CAIRN_AGENTFS_BASE_DIR:-$HOME/.cairnkeep}"
27
27
  STORE_DIR="${STORE_DIR/#\~/$HOME}"
28
+ PACK_DIR="${CAIRN_PACK_BASE_DIR:-$HOME/.cairnkeep/packs}"
29
+ PACK_DIR="${PACK_DIR/#\~/$HOME}"
28
30
  DRY_RUN=0
29
31
  ASSUME_YES=0
30
32
  PURGE_MEMORY=0
33
+ PURGE_PACKS=0
31
34
  PROJECTS=()
32
35
 
33
36
  # The hooks cairnkeep registers (basename is the settings.json match token).
@@ -49,7 +52,7 @@ PROJECT_AI_FILES=(
49
52
 
50
53
  usage() {
51
54
  cat <<'EOF'
52
- Usage: uninstall.sh [--dry-run] [--yes] [--purge-memory] [--live-root PATH]
55
+ Usage: uninstall.sh [--dry-run] [--yes] [--purge-memory] [--purge-packs] [--live-root PATH]
53
56
  [--pi-live-root PATH] [--kimi-live-root PATH] [PROJECT ...]
54
57
 
55
58
  Reverse cairnkeep's install. Everything removed/edited is backed up into
@@ -59,6 +62,8 @@ $HOME/.cairnkeep-uninstall-<ts>/ with a revert.sh before any change.
59
62
  --yes Do not prompt for confirmation.
60
63
  --purge-memory Also remove the global memory store and .agentfs/ memory and
61
64
  trajectories in each PROJECT. Backed up first; off by default.
65
+ --purge-packs Also remove immutable context-pack objects and project
66
+ pointers. Backed up first; off by default.
62
67
  --live-root PATH Claude root to clean (default: $CLAUDE_CONFIG_DIR or ~/.claude).
63
68
  --pi-live-root PATH Pi agent root to clean (default: $PI_CODING_AGENT_DIR or ~/.pi/agent).
64
69
  --kimi-live-root PATH Kimi Code root to clean (default: $KIMI_CODE_HOME or ~/.kimi-code).
@@ -73,6 +78,7 @@ while [[ $# -gt 0 ]]; do
73
78
  --dry-run) DRY_RUN=1; shift ;;
74
79
  --yes) ASSUME_YES=1; shift ;;
75
80
  --purge-memory) PURGE_MEMORY=1; shift ;;
81
+ --purge-packs) PURGE_PACKS=1; shift ;;
76
82
  --live-root) LIVE_ROOT="$2"; shift 2 ;;
77
83
  --pi-live-root) PI_LIVE_ROOT="$2"; shift 2 ;;
78
84
  --kimi-live-root) KIMI_LIVE_ROOT="$2"; shift 2 ;;
@@ -142,6 +148,7 @@ echo " live root: $LIVE_ROOT"
142
148
  echo " Pi live root: $PI_LIVE_ROOT"
143
149
  echo " Kimi root: $KIMI_LIVE_ROOT"
144
150
  echo " memory store: $STORE_DIR ($([[ $PURGE_MEMORY -eq 1 ]] && echo 'WILL be purged' || echo 'kept'))"
151
+ echo " context packs: $PACK_DIR ($([[ $PURGE_PACKS -eq 1 ]] && echo 'WILL be purged' || echo 'kept'))"
145
152
  [[ ${#PROJECTS[@]} -gt 0 ]] && echo " projects: ${PROJECTS[*]}"
146
153
 
147
154
  if [[ $DRY_RUN -eq 0 && $ASSUME_YES -eq 0 ]]; then
@@ -231,6 +238,24 @@ fi
231
238
  if [[ $PURGE_MEMORY -eq 1 ]]; then
232
239
  echo "Memory store:"
233
240
  remove_path "$STORE_DIR"
241
+ if [[ $PURGE_PACKS -eq 0 && "$PACK_DIR" == "$STORE_DIR"/* && $DRY_RUN -eq 0 ]]; then
242
+ pack_backup="$BACKUP_DIR/files/${PACK_DIR#/}"
243
+ if [[ -d "$pack_backup" ]]; then
244
+ mkdir -p "$(dirname "$PACK_DIR")"
245
+ cp -a "$pack_backup" "$PACK_DIR"
246
+ echo " retained context packs: $PACK_DIR"
247
+ fi
248
+ fi
249
+ fi
250
+
251
+ if [[ $PURGE_MEMORY -eq 0 && $PURGE_PACKS -eq 0 && -d "$PACK_DIR" ]]; then
252
+ echo "Context packs:"
253
+ echo " kept context packs: $PACK_DIR (use --purge-packs to remove)"
254
+ fi
255
+
256
+ if [[ $PURGE_PACKS -eq 1 && ! ( $PURGE_MEMORY -eq 1 && "$PACK_DIR" == "$STORE_DIR"/* ) ]]; then
257
+ echo "Context packs:"
258
+ remove_path "$PACK_DIR"
234
259
  fi
235
260
 
236
261
  # 6. Per-project bootstrap scaffold.
@@ -17,6 +17,17 @@
17
17
  # scopes default to ~/.cairnkeep; project scope uses <server-cwd>/.agentfs/.
18
18
  # CAIRN_AGENTFS_BASE_DIR=~/.cairnkeep
19
19
  #
20
+ # Least-authority MCP profiles only restrict currently available tools. Project
21
+ # state is managed with `cairn mcp-tools`; these are process overrides.
22
+ # CAIRN_MCP_TOOL_PROFILE=read-only
23
+ # CAIRN_MCP_ALLOWED_TOOLS=memory_read,memory_search
24
+ #
25
+ # Immutable context packs are local and OFF by default. HTTP access requires
26
+ # the separate pack HTTP flag plus the existing authenticated HTTP settings.
27
+ # CAIRN_CONTEXT_PACKS=1
28
+ # CAIRN_CONTEXT_PACK_HTTP=1
29
+ # CAIRN_PACK_BASE_DIR=~/.cairnkeep/packs
30
+ #
20
31
  # Typed metadata, filters, logical note address spaces, and memory_import are
21
32
  # OFF by default. Restart the memory server after changing this construction-
22
33
  # time tool-schema flag. No model, API key, or network endpoint is required.