@cairnkeep/cli 1.0.2

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 (129) hide show
  1. package/AGENTS.md +45 -0
  2. package/CONTRIBUTING.md +49 -0
  3. package/LICENSE +201 -0
  4. package/README.md +149 -0
  5. package/SECURITY.md +37 -0
  6. package/bin/cairn +73 -0
  7. package/claude/agents/code-reviewer.md +174 -0
  8. package/claude/agents/security-investigator.md +112 -0
  9. package/claude/agents/security-target-selector.md +90 -0
  10. package/claude/agents/security-validator.md +97 -0
  11. package/claude/agents/wiki-ingester.md +108 -0
  12. package/claude/agents/wiki-lint-auditor.md +94 -0
  13. package/claude/agents/wiki-query-analyst.md +84 -0
  14. package/claude/commands/context-explore.md +40 -0
  15. package/claude/commands/graphify.md +150 -0
  16. package/claude/commands/memory-review.md +85 -0
  17. package/claude/commands/memory-sync.md +71 -0
  18. package/claude/commands/recall.md +32 -0
  19. package/claude/commands/remember.md +43 -0
  20. package/claude/commands/repo-review.md +191 -0
  21. package/claude/commands/security-audit.md +174 -0
  22. package/claude/commands/wiki-ingest.md +112 -0
  23. package/claude/commands/wiki-lint.md +86 -0
  24. package/claude/commands/wiki-query.md +92 -0
  25. package/claude/hooks/context-explore-pretask.sh +93 -0
  26. package/claude/hooks/memory-capture.sh +59 -0
  27. package/claude/hooks/memory-recall.sh +84 -0
  28. package/claude/hooks/memory-wakeup.sh +45 -0
  29. package/docs/building-an-overlay.md +117 -0
  30. package/docs/domain-knowledge.md +86 -0
  31. package/docs/git-providers.md +36 -0
  32. package/docs/operating.md +366 -0
  33. package/mcp-memory-server/dist/embeddings.js +122 -0
  34. package/mcp-memory-server/dist/explore-cache.js +97 -0
  35. package/mcp-memory-server/dist/index.js +1256 -0
  36. package/mcp-memory-server/package.json +36 -0
  37. package/opencode/agents/code-reviewer.md +173 -0
  38. package/opencode/agents/security-investigator.md +111 -0
  39. package/opencode/agents/security-target-selector.md +89 -0
  40. package/opencode/agents/security-validator.md +96 -0
  41. package/opencode/agents/wiki-ingester.md +107 -0
  42. package/opencode/agents/wiki-lint-auditor.md +93 -0
  43. package/opencode/agents/wiki-query-analyst.md +83 -0
  44. package/opencode/command/code-review.md +199 -0
  45. package/opencode/command/context-explore.md +42 -0
  46. package/opencode/command/graphify.md +153 -0
  47. package/opencode/command/memory-review.md +44 -0
  48. package/opencode/command/memory-sync.md +94 -0
  49. package/opencode/command/recall.md +38 -0
  50. package/opencode/command/remember.md +46 -0
  51. package/opencode/command/security-audit.md +80 -0
  52. package/opencode/command/wiki-ingest.md +55 -0
  53. package/opencode/command/wiki-lint.md +51 -0
  54. package/opencode/command/wiki-query.md +50 -0
  55. package/opencode/plugins/memory-capture.ts +188 -0
  56. package/opencode/plugins/memory-recall.ts +132 -0
  57. package/opencode/plugins/memory-wakeup.ts +102 -0
  58. package/opencode/templates/security-finding-register.yaml.template +51 -0
  59. package/opencode/templates/security-issue.md.template +43 -0
  60. package/opencode/templates/security-policy.md.template +29 -0
  61. package/opencode/templates/security-report.md.template +43 -0
  62. package/opencode/templates/wiki-contradictions.md.template +31 -0
  63. package/opencode/templates/wiki-index.md.template +40 -0
  64. package/opencode/templates/wiki-lint-report.md.template +38 -0
  65. package/opencode/templates/wiki-log.md.template +11 -0
  66. package/opencode/templates/wiki-policy.md.template +47 -0
  67. package/opencode/templates/wiki-query-answer.md.template +26 -0
  68. package/opencode/templates/wiki-source-summary.md.template +33 -0
  69. package/opencode/workflows/memory-review-workflow.md +66 -0
  70. package/opencode/workflows/security-audit-workflow.md +197 -0
  71. package/opencode/workflows/wiki-ingest-workflow.md +149 -0
  72. package/opencode/workflows/wiki-lint-workflow.md +128 -0
  73. package/opencode/workflows/wiki-query-workflow.md +122 -0
  74. package/package.json +56 -0
  75. package/scripts/bootstrap.sh +99 -0
  76. package/scripts/doctor.sh +91 -0
  77. package/scripts/install-audit-timer.sh +68 -0
  78. package/scripts/lib/assert-tool-event.mjs +50 -0
  79. package/scripts/memory-store.sh +71 -0
  80. package/scripts/memory-wiki-audit.sh +156 -0
  81. package/scripts/sync-claude-assets.sh +182 -0
  82. package/scripts/sync-opencode-explore-assets.sh +187 -0
  83. package/scripts/sync-opencode-graphify-assets.sh +152 -0
  84. package/scripts/sync-opencode-memory-assets.sh +160 -0
  85. package/scripts/sync-opencode-plugin-assets.sh +163 -0
  86. package/scripts/sync-opencode-security-assets.sh +195 -0
  87. package/scripts/sync-opencode-wiki-assets.sh +250 -0
  88. package/scripts/test-audit-timer.sh +29 -0
  89. package/scripts/test-bootstrap-untracked.sh +41 -0
  90. package/scripts/test-cli-dispatch.sh +21 -0
  91. package/scripts/test-doctor.sh +32 -0
  92. package/scripts/test-launcher-hooks.sh +66 -0
  93. package/scripts/test-memory-store.sh +44 -0
  94. package/scripts/test-remember-recall-assertions.sh +70 -0
  95. package/scripts/transcript-to-text.mjs +53 -0
  96. package/scripts/verify-docs-parity.sh +112 -0
  97. package/scripts/verify-explore-maturation.sh +288 -0
  98. package/scripts/verify-fastcontext-reliability.sh +624 -0
  99. package/scripts/verify-no-private-references.sh +145 -0
  100. package/scripts/verify-opencode-live-parity.sh +864 -0
  101. package/scripts/verify-routing-seam.sh +169 -0
  102. package/scripts/verify-token-savings-ab.sh +557 -0
  103. package/templates/PROJECT-INSTRUCTIONS.md.template +42 -0
  104. package/templates/alignment-gap-register.yaml.template +56 -0
  105. package/templates/alignment-policy.md.template +28 -0
  106. package/templates/alignment-pr-template.md.template +18 -0
  107. package/templates/alignment-report.md.template +56 -0
  108. package/templates/env.example.template +25 -0
  109. package/templates/graph-policy.md.template +37 -0
  110. package/templates/graphs-gitignore.template +3 -0
  111. package/templates/memory-wiki-audit.service.template +6 -0
  112. package/templates/memory-wiki-audit.timer.template +9 -0
  113. package/templates/memory.json.template +4 -0
  114. package/templates/planning-config.json.template +6 -0
  115. package/templates/project-brief.md.template +40 -0
  116. package/templates/security-finding-register.yaml.template +51 -0
  117. package/templates/security-issue.md.template +43 -0
  118. package/templates/security-policy.md.template +29 -0
  119. package/templates/security-report.md.template +43 -0
  120. package/templates/start-claude.sh.template +46 -0
  121. package/templates/start-opencode.sh.template +41 -0
  122. package/templates/structured-change-canvas.md.template +63 -0
  123. package/templates/wiki-contradictions.md.template +31 -0
  124. package/templates/wiki-index.md.template +40 -0
  125. package/templates/wiki-lint-report.md.template +38 -0
  126. package/templates/wiki-log.md.template +11 -0
  127. package/templates/wiki-policy.md.template +47 -0
  128. package/templates/wiki-query-answer.md.template +26 -0
  129. package/templates/wiki-source-summary.md.template +33 -0
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env bash
2
+ # context-explore-pretask.sh — UserPromptSubmit hook (fires when the user
3
+ # submits a prompt, before the model responds).
4
+ #
5
+ # Auto-invokes context_explore for the submitted prompt so exploration runs
6
+ # for a task with no manual /context-explore call (CTX-09). Double opt-in
7
+ # (D-07): inert unless CAIRN_EXPLORE_BINARY is set AND
8
+ # CAIRN_EXPLORE_AUTOINVOKE=1. High-signal gated: skips short prompts, slash
9
+ # commands, and bare acknowledgements. Shells out to the shared `explore` CLI
10
+ # subcommand (D-06) with an explicit short timeout (Pitfall 1) so cache
11
+ # (CTX-10) and cross-refs (CTX-08) apply identically to hook and MCP-tool
12
+ # invocations. Fail-open: any error injects nothing, exit 0 always.
13
+ #
14
+ # Protocol: JSON on stdin { prompt, ... }. Output: JSON
15
+ # { hookSpecificOutput: { hookEventName, additionalContext } } only when the
16
+ # explore result is ok:true with non-empty citations; empty output otherwise.
17
+ set -euo pipefail
18
+
19
+ # Double opt-in (D-07): inert unless both are set.
20
+ [ -n "${CAIRN_EXPLORE_BINARY:-}" ] || exit 0
21
+ [ "${CAIRN_EXPLORE_AUTOINVOKE:-}" = "1" ] || exit 0
22
+
23
+ INFRA_ROOT="@@INFRA_ROOT@@"
24
+ SERVER_ENTRY="$INFRA_ROOT/mcp-memory-server/dist/index.js"
25
+ [ -f "$SERVER_ENTRY" ] || exit 0
26
+
27
+ input="$(cat)"
28
+
29
+ # Extract the submitted prompt text.
30
+ prompt="$(printf '%s' "$input" | python3 -c 'import sys,json
31
+ try:
32
+ d=json.load(sys.stdin)
33
+ print(d.get("prompt") or "")
34
+ except Exception: print("")' 2>/dev/null || true)"
35
+ [ -n "$prompt" ] || exit 0
36
+
37
+ # Low-signal skip (Open Question 2 -- ponytail: first-pass heuristic, not a
38
+ # rigorously derived one; revisit thresholds if noise/misses show up live).
39
+ # Skips: too short, a slash command, or a bare acknowledgement.
40
+ [ "${#prompt}" -ge 10 ] || exit 0
41
+ case "$prompt" in
42
+ /*) exit 0 ;;
43
+ esac
44
+ if printf '%s' "$prompt" | grep -Eqi '^[[:space:]]*(ok|yes|no|thanks?)\.?[[:space:]]*$'; then
45
+ exit 0
46
+ fi
47
+
48
+ # Explicit short timeout (Pitfall 1) -- never the tool's own 120s default.
49
+ # `|| true` so a timeout/failure never aborts the hook (fail-open).
50
+ result="$(timeout 20 node "$SERVER_ENTRY" explore "$prompt" 2>/dev/null || true)"
51
+ [ -n "$result" ] || exit 0
52
+
53
+ # Inject only on ok:true + non-empty citations (D-07); compact citations +
54
+ # cross-ref flags only, never expanded_snippets (D-14).
55
+ context="$(printf '%s' "$result" | python3 -c 'import sys,json
56
+ try:
57
+ d=json.load(sys.stdin)
58
+ if d.get("ok") is not True:
59
+ sys.exit(0)
60
+ citations = d.get("citations") or []
61
+ if not citations:
62
+ sys.exit(0)
63
+ lines=[]
64
+ for c in citations:
65
+ line = str(c.get("path")) + ":" + str(c.get("start_line")) + "-" + str(c.get("end_line"))
66
+ parts=[]
67
+ if c.get("memory_refs"):
68
+ parts.append("memory: " + ", ".join(c["memory_refs"]))
69
+ if c.get("wiki_refs"):
70
+ parts.append("wiki: " + ", ".join(c["wiki_refs"]))
71
+ if parts:
72
+ line += " <- " + " - ".join(parts)
73
+ lines.append(line)
74
+ print("\n".join(lines))
75
+ except Exception:
76
+ pass' 2>/dev/null || true)"
77
+ [ -n "$context" ] || exit 0
78
+
79
+ context="$(printf '%s\n' "$context" | head -40)"
80
+
81
+ # Emit the hook output JSON that injects additionalContext for this prompt.
82
+ # The prefix identifies the block as auto-invoked exploration context so the
83
+ # model knows its provenance (D-14).
84
+ python3 -c 'import sys,json
85
+ ctx = sys.stdin.read()
86
+ print(json.dumps({
87
+ "hookSpecificOutput": {
88
+ "hookEventName": "UserPromptSubmit",
89
+ "additionalContext": "Auto-invoked exploration context (context_explore, no manual /context-explore call):\n\n" + ctx
90
+ }
91
+ }))' <<<"$context" 2>/dev/null || true
92
+
93
+ exit 0
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env bash
2
+ # memory-capture.sh — SessionEnd hook (Claude Code + OpenCode share this protocol).
3
+ #
4
+ # At session end, extract durable memory candidates from the session transcript
5
+ # using the configured extraction model, and stage them for review — NOT write
6
+ # them directly. The next session's memory-wakeup.sh surfaces staged candidates;
7
+ # /memory-review is the accept gate that writes accepted ones to AgentFS via the
8
+ # cairn-memory MCP. This keeps writes agent-gated (no premature/auto writes, no
9
+ # AgentFS lock contention) while making extraction fully automatic.
10
+ #
11
+ # Protocol: JSON on stdin with { transcript_path, cwd, hook_event_name, ... }.
12
+ # ponytail: no-op outside a bootstrapped repo or when the extraction config is
13
+ # absent, so it is safe as a global hook.
14
+ set -euo pipefail
15
+
16
+ repo="$(pwd)"
17
+ [ -f "$repo/.agentfs/project.db" ] || exit 0
18
+
19
+ INFRA_ROOT="@@INFRA_ROOT@@"
20
+ SERVER_ENTRY="$INFRA_ROOT/mcp-memory-server/dist/index.js"
21
+ TEXT_HELPER="$INFRA_ROOT/scripts/transcript-to-text.mjs"
22
+ [ -f "$SERVER_ENTRY" ] || exit 0
23
+ [ -z "${CAIRN_LLM_API_KEY:-}" ] && exit 0
24
+ EXTRACT_MODEL="${CAIRN_LLM_EXTRACTION_MODEL:-}"
25
+ [ -z "$EXTRACT_MODEL" ] && exit 0
26
+
27
+ # Read hook JSON from stdin; pull transcript_path (fail-open if absent/malformed).
28
+ input="$(cat)"
29
+ transcript_path="$(printf '%s' "$input" | python3 -c 'import sys,json
30
+ try:
31
+ d=json.load(sys.stdin); print(d.get("transcript_path",""))
32
+ except Exception: print("")' 2>/dev/null || true)"
33
+ [ -n "$transcript_path" ] && [ -f "$transcript_path" ] || exit 0
34
+
35
+ # Convert transcript JSONL → readable text, then extract candidates.
36
+ text="$(node "$TEXT_HELPER" "$transcript_path" 2>/dev/null || true)"
37
+ [ -n "$text" ] || exit 0
38
+
39
+ candidates_json="$(printf '%s' "$text" | node "$SERVER_ENTRY" extract "$EXTRACT_MODEL" 2>/dev/null || true)"
40
+ [ -n "$candidates_json" ] || exit 0
41
+
42
+ count="$(printf '%s' "$candidates_json" | python3 -c 'import sys,json
43
+ try:
44
+ d=json.load(sys.stdin); print(len(d.get("candidates",[])))
45
+ except Exception: print(0)' 2>/dev/null || echo 0)"
46
+ [ "$count" -gt 0 ] || exit 0
47
+
48
+ # Stage for the next session's accept gate. One file per session.
49
+ mkdir -p "$repo/.planning/memory-staging"
50
+ ts="$(date -u +%Y%m%dT%H%M%SZ)"
51
+ stage_file="$repo/.planning/memory-staging/$ts.json"
52
+ printf '%s\n' "$candidates_json" > "$stage_file"
53
+
54
+ # Keep the staging dir bounded — drop the oldest beyond 5 sessions.
55
+ ls -1t "$repo/.planning/memory-staging/"*.json 2>/dev/null | tail -n +6 | while read -r old; do
56
+ rm -f "$old"
57
+ done
58
+
59
+ exit 0
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env bash
2
+ # memory-recall.sh — PreToolUse hook (fires before Edit/Write/MultiEdit).
3
+ #
4
+ # When the model is about to edit a file, surface AgentFS facts and wiki pages
5
+ # that specifically mention that file. This turns "the model might search memory"
6
+ # into "the model always sees relevant pitfalls/decisions for the exact file."
7
+ #
8
+ # High-signal / low-noise design: injects context ONLY when there is a specific
9
+ # match (memory preview or wiki page mentioning the file path or basename). If
10
+ # nothing specific matches, it injects nothing — no noise on routine edits.
11
+ #
12
+ # Protocol: JSON on stdin { tool_name, tool_input: { file_path, ... }, cwd, ... }.
13
+ # Output: JSON { hookSpecificOutput: { hookEventName, additionalContext } } when
14
+ # there is a match; empty output otherwise. Fail-open: any error exits 0.
15
+ set -euo pipefail
16
+
17
+ repo="$(pwd)"
18
+ [ -f "$repo/.agentfs/project.db" ] || [ -d "$repo/.planning/wiki/sources" ] || exit 0
19
+
20
+ INFRA_ROOT="@@INFRA_ROOT@@"
21
+ SERVER_ENTRY="$INFRA_ROOT/mcp-memory-server/dist/index.js"
22
+
23
+ input="$(cat)"
24
+
25
+ # Extract the target file path from the tool input.
26
+ file_path="$(printf '%s' "$input" | python3 -c 'import sys,json
27
+ try:
28
+ d=json.load(sys.stdin); ti=d.get("tool_input",{})
29
+ print(ti.get("file_path") or ti.get("path") or "")
30
+ except Exception: print("")' 2>/dev/null || true)"
31
+ [ -n "$file_path" ] || exit 0
32
+
33
+ # Derive search tokens: basename and the repo-relative stem.
34
+ base="$(basename "$file_path")"
35
+ stem="${base%.*}"
36
+ # Avoid matching on tiny/generic stems that would noise up results.
37
+ [ "${#stem}" -lt 4 ] && exit 0
38
+
39
+ matches=()
40
+
41
+ # 1. AgentFS project memory: filter the compact wakeup index by the stem.
42
+ if [ -f "$repo/.agentfs/project.db" ] && [ -f "$SERVER_ENTRY" ]; then
43
+ idx="$(node "$SERVER_ENTRY" wakeup 2>/dev/null || true)"
44
+ if [ -n "$idx" ]; then
45
+ hit_lines="$(printf '%s' "$idx" | grep -iF "$stem" 2>/dev/null | head -8 || true)"
46
+ if [ -n "$hit_lines" ]; then
47
+ matches+=("## Relevant project memory for $base" "" "$hit_lines" "")
48
+ fi
49
+ fi
50
+ fi
51
+
52
+ # 2. Wiki source pages whose name or content mentions the path/basename.
53
+ if [ -d "$repo/.planning/wiki/sources" ]; then
54
+ wiki_hits=""
55
+ while IFS= read -r page; do
56
+ [[ -n "$page" ]] || continue
57
+ if grep -qiF "$stem" "$page" 2>/dev/null; then
58
+ # Pull the first stable-fact bullet as a teaser.
59
+ teaser="$(grep -m1 -E '^- \*\*' "$page" 2>/dev/null | cut -c1-160 || true)"
60
+ wiki_hits+="- [$(basename "$page")] ${teaser}"$'\n'
61
+ fi
62
+ done < <(find "$repo/.planning/wiki/sources" -maxdepth 1 -name '*.md' -type f 2>/dev/null)
63
+ if [ -n "$wiki_hits" ]; then
64
+ matches+=("## Relevant wiki pages for $base" "" "${wiki_hits}" "")
65
+ fi
66
+ fi
67
+
68
+ # Inject only when there is something specific.
69
+ if [ "${#matches[@]}" -eq 0 ]; then
70
+ exit 0
71
+ fi
72
+
73
+ context="$(printf '%s\n' "${matches[@]}" | head -40)"
74
+ # Emit the hook output JSON that injects additionalContext before the tool runs.
75
+ python3 -c 'import sys,json
76
+ ctx = sys.stdin.read()
77
+ print(json.dumps({
78
+ "hookSpecificOutput": {
79
+ "hookEventName": "PreToolUse",
80
+ "additionalContext": "Memory recall (auto-injected for this file edit):\n\n" + ctx
81
+ }
82
+ }))' <<<"$context" 2>/dev/null || true
83
+
84
+ exit 0
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env bash
2
+ # SessionStart hook: surface the memory layers that are otherwise invisible at
3
+ # session start (AgentFS project scope + wiki index). File-memory (MEMORY.md)
4
+ # already auto-loads, so it is not duplicated here.
5
+ # ponytail: no-op outside a managed repo, so it is safe as a global hook.
6
+ set -euo pipefail
7
+
8
+ repo="$(pwd)"
9
+ [ -f "$repo/.agentfs/project.db" ] || [ -f "$repo/.planning/wiki/index.md" ] || exit 0
10
+
11
+ if [ -f "$repo/.agentfs/project.db" ]; then
12
+ echo "## Project memory (AgentFS)"
13
+ node "@@INFRA_ROOT@@/mcp-memory-server/dist/index.js" wakeup 2>/dev/null || true
14
+ fi
15
+
16
+ if [ -f "$repo/.planning/wiki/index.md" ]; then
17
+ echo
18
+ echo "## Wiki index"
19
+ cat "$repo/.planning/wiki/index.md"
20
+ fi
21
+
22
+ # Surface open HARD wiki contradictions so the agent (and user) see them at
23
+ # session start without anyone having to remember to scan the register. Hard
24
+ # entries cannot both be correct and must be resolved before dependent work.
25
+ contradictions="$repo/.planning/wiki/CONTRADICTIONS.md"
26
+ if [ -f "$contradictions" ]; then
27
+ open_hard="$(awk '/<!-- wiki:contradictions:open:start -->/{f=1;next} /<!-- wiki:contradictions:open:end -->/{f=0} f' "$contradictions" | grep -iE 'severity:[[:space:]]*hard')"
28
+ if [ -n "$open_hard" ]; then
29
+ echo
30
+ echo "## Open HARD contradictions — resolve before dependent work"
31
+ echo "$open_hard"
32
+ fi
33
+ fi
34
+
35
+ # Surface staged memory candidates captured by the SessionEnd hook
36
+ # (memory-capture.sh). These are extracted automatically from the last session
37
+ # but NOT yet written to AgentFS — /memory-review is the accept gate.
38
+ if [ -d "$repo/.planning/memory-staging" ]; then
39
+ staged="$(ls -1 "$repo/.planning/memory-staging/"*.json 2>/dev/null | wc -l)"
40
+ if [ "$staged" -gt 0 ]; then
41
+ echo
42
+ echo "## Staged memory candidates ($staged session(s)) — UNREVIEWED"
43
+ echo "Run /memory-review to accept (→ AgentFS) or discard these before doing other work."
44
+ fi
45
+ fi
@@ -0,0 +1,117 @@
1
+ # Building a private overlay
2
+
3
+ Cairnkeep is deliberately provider-neutral: it hardcodes no endpoints, no
4
+ credentials, and no organization-specific launchers. Everything specific to
5
+ *your* environment — internal LLM endpoints, proxies, credentials, corporate
6
+ launchers, policy — belongs in a small private **overlay** that wraps cairnkeep.
7
+ This guide shows the pattern.
8
+
9
+ ## Principles
10
+
11
+ 1. **Consume, don't fork.** Treat cairnkeep as an upstream dependency: install it
12
+ from npm (`npm i -g @cairnkeep/cli`) or clone it at a **pinned tag**. Never copy or
13
+ patch core files — if core behaviour must change, request it upstream. That way
14
+ you pick up fixes by bumping one version.
15
+ 2. **One-way references.** Your overlay may reference cairnkeep freely. Nothing
16
+ private — internal hosts, IPs, tokens, employer/vendor names — should ever end
17
+ up *in cairnkeep* (in code, docs, or commit messages). Keep those in your
18
+ overlay only, and keep secrets in untracked `.env` files or a secret store,
19
+ never committed.
20
+ 3. **Wrap at the seams, don't reimplement.** Cairnkeep's launchers and tools
21
+ expose extension points (below). Use them instead of replacing core behaviour.
22
+
23
+ ## The launcher seams
24
+
25
+ The generic launchers (`.ai/start-claude.sh`, `.ai/start-opencode.sh`, scaffolded
26
+ by `cairn bootstrap`) run three optional hooks, each a no-op when absent:
27
+
28
+ | Seam | When | Use it for |
29
+ |---|---|---|
30
+ | `.ai/pre-launch.sh` | sourced after `.env`, before launch | export a provider base URL / credentials, refresh a token, run a connectivity check, or **abort** the launch by returning non-zero |
31
+ | `CAIRN_EXTRA_SETTINGS` | read just before launch | path to a settings file layered onto the harness (`--settings` for Claude Code, `--config` for OpenCode); process env still wins, so an inline value beats the file |
32
+ | `.ai/post-exit.sh` | sourced after the harness exits | teardown; `CAIRN_EXIT_STATUS` holds the exit code |
33
+
34
+ **Typical overlay launcher** — a corporate wrapper needs a non-default provider
35
+ plus a credential refresh. Instead of forking the launcher, drop a
36
+ `.ai/pre-launch.sh`:
37
+
38
+ ```bash
39
+ # .ai/pre-launch.sh (sourced by the generic launcher)
40
+ # 1. refresh whatever credential your provider needs
41
+ ./.ai/refresh-credentials.sh || return 1 # non-zero aborts the launch
42
+ # 2. render your provider settings and hand them to the harness
43
+ envsubst < ./.ai/provider-settings.json.template > /tmp/provider.$$.json
44
+ export CAIRN_EXTRA_SETTINGS=/tmp/provider.$$.json
45
+ export ANTHROPIC_BASE_URL="$MY_INTERNAL_GATEWAY"
46
+ ```
47
+
48
+ The user still runs the standard `./.ai/start-claude.sh`; the overlay's behaviour
49
+ is injected by the file's presence.
50
+
51
+ ## An overlay install script
52
+
53
+ Make it one idempotent command from a fresh machine:
54
+
55
+ ```bash
56
+ # 1. Get cairnkeep at a pinned version (npm or a pinned clone)
57
+ npm i -g @cairnkeep/cli@1.0.2 # or: git clone … && git checkout v1.0.2
58
+
59
+ # 2. Register the memory server with your harness
60
+ claude mcp add cairn-memory -s user -- cairn memory-server
61
+
62
+ # 3. Scaffold the target project (contributor mode if you don't own the repo)
63
+ cairn bootstrap --untracked /path/to/project
64
+
65
+ # 4. Install the operating layer — GLOBAL or PROJECT-SCOPED (below)
66
+ sync-claude-assets.sh --apply # global ~/.claude
67
+ # or keep it to one project:
68
+ sync-claude-assets.sh --apply --live-root /path/to/project/.claude
69
+
70
+ # 5. Drop your corporate launcher/helpers/.env template into /path/to/project/.ai/
71
+ ```
72
+
73
+ ### Global vs project-scoped operating layer
74
+
75
+ `sync-claude-assets.sh` installs commands/agents/hooks into `~/.claude` by default
76
+ (global — affects every project). To onboard a single project **without** touching
77
+ global config or other projects, install into that project's `.claude/` with
78
+ `--live-root <project>/.claude`; the project's local assets take precedence there
79
+ while the rest of the machine is untouched.
80
+
81
+ ### Contributor mode
82
+
83
+ If you don't own the target repo, `cairn bootstrap --untracked` writes the
84
+ scaffold paths into the repo's `.git/info/exclude` — nothing to commit, invisible
85
+ to other contributors, no edit to the shared `.gitignore`. To move the durable
86
+ memory itself between machines, use `cairn memory export` / `cairn memory import`.
87
+
88
+ ## Isolating your memory server
89
+
90
+ If your overlay runs its own memory server and you don't want a globally-registered
91
+ one bleeding in, launch the harness with `--strict-mcp-config --mcp-config
92
+ .ai/mcp.json`, listing only the servers you want. A launcher that adds these flags
93
+ when `.ai/mcp.json` is present gives you per-project MCP isolation.
94
+
95
+ ## Wiring the optional integrations
96
+
97
+ All optional, all env-gated — a wrapper turns them on by setting the env:
98
+
99
+ | Integration | Turn on with | See |
100
+ |---|---|---|
101
+ | Memory extraction / semantic search | `CAIRN_LLM_API_KEY` + `CAIRN_LLM_API_URL` (+ embedding vars) | [operating.md](operating.md) |
102
+ | Document RAG (`domain_knowledge_*`) | `ANYTHINGLLM_API_KEY` (+ `ANYTHINGLLM_BASE_URL`, `CAIRN_ANYTHINGLLM_SYNC_SCRIPT`) | [domain-knowledge.md](domain-knowledge.md) |
103
+ | Routing seam (`route_check`) | `CAIRN_ROUTE_ENDPOINT` | [operating.md](operating.md) |
104
+ | Context exploration (`context_explore`) | `CAIRN_EXPLORE_BINARY` (+ `CAIRN_EXPLORE_REPO_ROOT`) | [operating.md](operating.md) |
105
+ | Networked memory (HTTP transport) | `MCP_HTTP_PORT` + `CAIRN_MEMORY_HTTP_TOKEN` | [operating.md](operating.md) |
106
+
107
+ Point the RAG sync script at your own copy (`CAIRN_ANYTHINGLLM_SYNC_SCRIPT`) so the
108
+ integration scripts live in your overlay, not the core.
109
+
110
+ ## Checklist
111
+
112
+ - [ ] cairnkeep pinned to a version; no core files copied or patched
113
+ - [ ] All internal hosts/tokens in untracked `.env` / secret store, never committed
114
+ - [ ] Provider/credential setup in `.ai/pre-launch.sh`, not a forked launcher
115
+ - [ ] Operating layer scoped as intended (global vs `--live-root`)
116
+ - [ ] Optional integrations wired only where wanted
117
+ - [ ] A single documented command takes a fresh machine to a working setup
@@ -0,0 +1,86 @@
1
+ # Domain knowledge (RAG) — optional
2
+
3
+ Cairnkeep's memory server exposes two **optional** document-RAG tools alongside
4
+ the `memory_*` tools:
5
+
6
+ - **`domain_knowledge_query`** — ask a question against a document workspace and
7
+ get a grounded answer with citations.
8
+ - **`domain_knowledge_sync`** — (re)embed a project's documentation into a
9
+ workspace.
10
+
11
+ These are a thin bridge to a running **[AnythingLLM](https://anythingllm.com/)**
12
+ instance (a generic, self-hostable document-RAG product). They are **off unless
13
+ you configure them** — cairnkeep hosts no RAG engine, ships no documents, and
14
+ hardcodes no endpoint. If you never set `ANYTHINGLLM_API_KEY`, the tools simply
15
+ error at call time and nothing else is affected; `memory_*` and the wiki layer
16
+ work exactly the same without them.
17
+
18
+ ## What you need
19
+
20
+ 1. A running AnythingLLM instance with at least one **workspace** that has your
21
+ documents embedded. (Local via podman/docker, or any reachable instance.)
22
+ 2. An AnythingLLM **API key**.
23
+ 3. cairnkeep pointed at it (below).
24
+
25
+ ## Configuration
26
+
27
+ | Variable | Purpose |
28
+ |---|---|
29
+ | `ANYTHINGLLM_API_KEY` | **Required** for the domain-knowledge tools. Unset → the tools error at call time (everything else works). |
30
+ | `ANYTHINGLLM_BASE_URL` | AnythingLLM base URL. Default `http://localhost:3001`. |
31
+ | `CAIRN_ANYTHINGLLM_SYNC_SCRIPT` | Path to the document-sync script used by `domain_knowledge_sync`. The core does not bundle one — point this at your own (see below). Unset → `domain_knowledge_sync` uses the in-repo default path. |
32
+
33
+ The workspaces the tools target come from the project's **memory config** — the
34
+ first of `.agent/memory.json`, `.opencode/memory.json`, `.claude/memory.json`, or
35
+ `memory.json` that exists:
36
+
37
+ ```json
38
+ {
39
+ "scopes": ["identity"],
40
+ "anythingllm_workspaces": ["engineering-patterns", "my-project-docs"]
41
+ }
42
+ ```
43
+
44
+ `domain_knowledge_query` takes an explicit `workspace`, or falls back to the first
45
+ configured workspace that is not `engineering-patterns` (the conventional shared
46
+ workspace). With no workspace given and none configured, it errors.
47
+
48
+ ## Setup (query)
49
+
50
+ ```bash
51
+ # 1. Start AnythingLLM (example: local podman on :3001) and create/populate a
52
+ # workspace with your docs (via the AnythingLLM UI or its API).
53
+ # 2. Point cairnkeep at it, in the project's .ai/.env:
54
+ ANYTHINGLLM_BASE_URL=http://localhost:3001
55
+ ANYTHINGLLM_API_KEY=<your AnythingLLM API key>
56
+ # 3. List the workspace(s) in the project's memory config (see JSON above).
57
+ ```
58
+
59
+ Then `domain_knowledge_query { "workspace": "my-project-docs", "query": "..." }`
60
+ returns a grounded answer.
61
+
62
+ ## Setup (sync / embedding)
63
+
64
+ `domain_knowledge_sync` shells out to a document-upload/embed script. Because the
65
+ scripts that talk to your AnythingLLM (and any include/exclude rules) are
66
+ deployment-specific, they live in **your** wrapper, not the core — point cairnkeep
67
+ at yours:
68
+
69
+ ```bash
70
+ CAIRN_ANYTHINGLLM_SYNC_SCRIPT=/abs/path/to/your/sync_to_anythingllm.py
71
+ ```
72
+
73
+ The script receives the workspace slug and is expected to upload + embed the
74
+ project's documents into that AnythingLLM workspace. See
75
+ [building-an-overlay.md](building-an-overlay.md) for the wrapper pattern.
76
+
77
+ A working, deployment-agnostic starting point lives in
78
+ [`examples/anythingllm/`](../examples/anythingllm/): an incremental
79
+ (sha256-tracked) multi-project sync script plus a config schema. Copy it, adapt
80
+ `anythingllm-projects.json`, and point `CAIRN_ANYTHINGLLM_SYNC_SCRIPT` at your
81
+ copy.
82
+
83
+ ## Not using RAG?
84
+
85
+ Do nothing. The tools stay inert, cost nothing, and the rest of cairnkeep is
86
+ fully functional without an AnythingLLM instance.
@@ -0,0 +1,36 @@
1
+ # Git provider configuration
2
+
3
+ Cairnkeep's collaboration commands — `memory-sync`, `repo-review` / `code-review`,
4
+ and `security-audit` — talk to your git host through whatever git-provider MCP
5
+ server you have registered. The host is selected per project by one setting, so
6
+ the same commands work on GitHub, GitLab, Codeberg/Forgejo, and anything else
7
+ that has an MCP server.
8
+
9
+ ## Configure
10
+
11
+ Set the provider in the project's `.ai/.env`:
12
+
13
+ ```bash
14
+ CAIRN_GIT_PROVIDER=github # github | gitlab | codeberg | forgejo | none
15
+ ```
16
+
17
+ `none` disables the provider steps — the commands run locally only (still write
18
+ memory, still produce local review/audit reports).
19
+
20
+ ## Operations the commands use
21
+
22
+ The commands need a small, stable set of operations. Map each to the tool your
23
+ provider's MCP server exposes:
24
+
25
+ | Operation | Purpose | GitHub (example) | GitLab (example) |
26
+ |---|---|---|---|
27
+ | list open changes | find tracked PRs/MRs | `list_pull_requests` | `list_merge_requests` |
28
+ | get change diff | review a PR/MR diff | `get_pull_request_diff` | `get_merge_request_diffs` |
29
+ | post inline comment | post review findings | `create_review_comment` | `create_merge_request_discussion` |
30
+ | get change state | sync memory with PR/MR status | `get_pull_request` | `get_merge_request` |
31
+ | create issue | file an accepted finding | `create_issue` | `create_issue` |
32
+
33
+ Exact tool names depend on the specific MCP server you install; the table shows
34
+ the common shape. Codeberg/Forgejo and other hosts follow the same operations
35
+ with their own tool names. If no git-provider MCP is registered (or
36
+ `CAIRN_GIT_PROVIDER=none`), the commands skip the provider steps and keep working.