@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.
- package/AGENTS.md +45 -0
- package/CONTRIBUTING.md +49 -0
- package/LICENSE +201 -0
- package/README.md +149 -0
- package/SECURITY.md +37 -0
- package/bin/cairn +73 -0
- package/claude/agents/code-reviewer.md +174 -0
- package/claude/agents/security-investigator.md +112 -0
- package/claude/agents/security-target-selector.md +90 -0
- package/claude/agents/security-validator.md +97 -0
- package/claude/agents/wiki-ingester.md +108 -0
- package/claude/agents/wiki-lint-auditor.md +94 -0
- package/claude/agents/wiki-query-analyst.md +84 -0
- package/claude/commands/context-explore.md +40 -0
- package/claude/commands/graphify.md +150 -0
- package/claude/commands/memory-review.md +85 -0
- package/claude/commands/memory-sync.md +71 -0
- package/claude/commands/recall.md +32 -0
- package/claude/commands/remember.md +43 -0
- package/claude/commands/repo-review.md +191 -0
- package/claude/commands/security-audit.md +174 -0
- package/claude/commands/wiki-ingest.md +112 -0
- package/claude/commands/wiki-lint.md +86 -0
- package/claude/commands/wiki-query.md +92 -0
- package/claude/hooks/context-explore-pretask.sh +93 -0
- package/claude/hooks/memory-capture.sh +59 -0
- package/claude/hooks/memory-recall.sh +84 -0
- package/claude/hooks/memory-wakeup.sh +45 -0
- package/docs/building-an-overlay.md +117 -0
- package/docs/domain-knowledge.md +86 -0
- package/docs/git-providers.md +36 -0
- package/docs/operating.md +366 -0
- package/mcp-memory-server/dist/embeddings.js +122 -0
- package/mcp-memory-server/dist/explore-cache.js +97 -0
- package/mcp-memory-server/dist/index.js +1256 -0
- package/mcp-memory-server/package.json +36 -0
- package/opencode/agents/code-reviewer.md +173 -0
- package/opencode/agents/security-investigator.md +111 -0
- package/opencode/agents/security-target-selector.md +89 -0
- package/opencode/agents/security-validator.md +96 -0
- package/opencode/agents/wiki-ingester.md +107 -0
- package/opencode/agents/wiki-lint-auditor.md +93 -0
- package/opencode/agents/wiki-query-analyst.md +83 -0
- package/opencode/command/code-review.md +199 -0
- package/opencode/command/context-explore.md +42 -0
- package/opencode/command/graphify.md +153 -0
- package/opencode/command/memory-review.md +44 -0
- package/opencode/command/memory-sync.md +94 -0
- package/opencode/command/recall.md +38 -0
- package/opencode/command/remember.md +46 -0
- package/opencode/command/security-audit.md +80 -0
- package/opencode/command/wiki-ingest.md +55 -0
- package/opencode/command/wiki-lint.md +51 -0
- package/opencode/command/wiki-query.md +50 -0
- package/opencode/plugins/memory-capture.ts +188 -0
- package/opencode/plugins/memory-recall.ts +132 -0
- package/opencode/plugins/memory-wakeup.ts +102 -0
- package/opencode/templates/security-finding-register.yaml.template +51 -0
- package/opencode/templates/security-issue.md.template +43 -0
- package/opencode/templates/security-policy.md.template +29 -0
- package/opencode/templates/security-report.md.template +43 -0
- package/opencode/templates/wiki-contradictions.md.template +31 -0
- package/opencode/templates/wiki-index.md.template +40 -0
- package/opencode/templates/wiki-lint-report.md.template +38 -0
- package/opencode/templates/wiki-log.md.template +11 -0
- package/opencode/templates/wiki-policy.md.template +47 -0
- package/opencode/templates/wiki-query-answer.md.template +26 -0
- package/opencode/templates/wiki-source-summary.md.template +33 -0
- package/opencode/workflows/memory-review-workflow.md +66 -0
- package/opencode/workflows/security-audit-workflow.md +197 -0
- package/opencode/workflows/wiki-ingest-workflow.md +149 -0
- package/opencode/workflows/wiki-lint-workflow.md +128 -0
- package/opencode/workflows/wiki-query-workflow.md +122 -0
- package/package.json +56 -0
- package/scripts/bootstrap.sh +99 -0
- package/scripts/doctor.sh +91 -0
- package/scripts/install-audit-timer.sh +68 -0
- package/scripts/lib/assert-tool-event.mjs +50 -0
- package/scripts/memory-store.sh +71 -0
- package/scripts/memory-wiki-audit.sh +156 -0
- package/scripts/sync-claude-assets.sh +182 -0
- package/scripts/sync-opencode-explore-assets.sh +187 -0
- package/scripts/sync-opencode-graphify-assets.sh +152 -0
- package/scripts/sync-opencode-memory-assets.sh +160 -0
- package/scripts/sync-opencode-plugin-assets.sh +163 -0
- package/scripts/sync-opencode-security-assets.sh +195 -0
- package/scripts/sync-opencode-wiki-assets.sh +250 -0
- package/scripts/test-audit-timer.sh +29 -0
- package/scripts/test-bootstrap-untracked.sh +41 -0
- package/scripts/test-cli-dispatch.sh +21 -0
- package/scripts/test-doctor.sh +32 -0
- package/scripts/test-launcher-hooks.sh +66 -0
- package/scripts/test-memory-store.sh +44 -0
- package/scripts/test-remember-recall-assertions.sh +70 -0
- package/scripts/transcript-to-text.mjs +53 -0
- package/scripts/verify-docs-parity.sh +112 -0
- package/scripts/verify-explore-maturation.sh +288 -0
- package/scripts/verify-fastcontext-reliability.sh +624 -0
- package/scripts/verify-no-private-references.sh +145 -0
- package/scripts/verify-opencode-live-parity.sh +864 -0
- package/scripts/verify-routing-seam.sh +169 -0
- package/scripts/verify-token-savings-ab.sh +557 -0
- package/templates/PROJECT-INSTRUCTIONS.md.template +42 -0
- package/templates/alignment-gap-register.yaml.template +56 -0
- package/templates/alignment-policy.md.template +28 -0
- package/templates/alignment-pr-template.md.template +18 -0
- package/templates/alignment-report.md.template +56 -0
- package/templates/env.example.template +25 -0
- package/templates/graph-policy.md.template +37 -0
- package/templates/graphs-gitignore.template +3 -0
- package/templates/memory-wiki-audit.service.template +6 -0
- package/templates/memory-wiki-audit.timer.template +9 -0
- package/templates/memory.json.template +4 -0
- package/templates/planning-config.json.template +6 -0
- package/templates/project-brief.md.template +40 -0
- package/templates/security-finding-register.yaml.template +51 -0
- package/templates/security-issue.md.template +43 -0
- package/templates/security-policy.md.template +29 -0
- package/templates/security-report.md.template +43 -0
- package/templates/start-claude.sh.template +46 -0
- package/templates/start-opencode.sh.template +41 -0
- package/templates/structured-change-canvas.md.template +63 -0
- package/templates/wiki-contradictions.md.template +31 -0
- package/templates/wiki-index.md.template +40 -0
- package/templates/wiki-lint-report.md.template +38 -0
- package/templates/wiki-log.md.template +11 -0
- package/templates/wiki-policy.md.template +47 -0
- package/templates/wiki-query-answer.md.template +26 -0
- package/templates/wiki-source-summary.md.template +33 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
shopt -s nullglob
|
|
4
|
+
|
|
5
|
+
usage() {
|
|
6
|
+
cat <<'EOF'
|
|
7
|
+
Usage: sync-claude-assets.sh [--check|--apply] [--live-root PATH]
|
|
8
|
+
|
|
9
|
+
Compare or sync the repo-managed Claude Code assets (commands, agents) and the
|
|
10
|
+
security/wiki scaffold templates against the live Claude Code config tree.
|
|
11
|
+
|
|
12
|
+
Options:
|
|
13
|
+
--check Verify that the live assets match the repo copy (default)
|
|
14
|
+
--apply Copy the repo-managed assets into the live Claude tree, then verify
|
|
15
|
+
--live-root PATH Override the live Claude root (default: $CLAUDE_CONFIG_DIR or $HOME/.claude)
|
|
16
|
+
-h, --help Show this help text
|
|
17
|
+
|
|
18
|
+
Notes:
|
|
19
|
+
- Claude assets source of truth lives under ./claude/ (commands/, agents/).
|
|
20
|
+
- Installs claude/commands/*.md -> <live>/commands/ and claude/agents/*.md -> <live>/agents/.
|
|
21
|
+
- Also installs templates/{security,wiki}-*.template -> <live>/templates/ so the
|
|
22
|
+
/security-audit and /wiki-* commands can scaffold from $HOME/.claude/templates/.
|
|
23
|
+
- Renders claude/hooks/*.sh -> <live>/hooks/ (substituting @@INFRA_ROOT@@) and
|
|
24
|
+
idempotently registers the SessionStart wakeup hook in <live>/settings.json
|
|
25
|
+
(backs up settings.json.bak first; matches by command so re-runs are no-ops).
|
|
26
|
+
- New families are picked up automatically once added under ./claude/.
|
|
27
|
+
EOF
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
31
|
+
CLAUDE_SOURCE="$ROOT_DIR/claude"
|
|
32
|
+
TEMPLATE_SOURCE="$ROOT_DIR/templates"
|
|
33
|
+
LIVE_ROOT="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
|
|
34
|
+
MODE="check"
|
|
35
|
+
|
|
36
|
+
while [[ $# -gt 0 ]]; do
|
|
37
|
+
case "$1" in
|
|
38
|
+
--check) MODE="check"; shift ;;
|
|
39
|
+
--apply) MODE="apply"; shift ;;
|
|
40
|
+
--live-root) LIVE_ROOT="$2"; shift 2 ;;
|
|
41
|
+
-h|--help) usage; exit 0 ;;
|
|
42
|
+
*) echo "Unknown argument: $1" >&2; usage >&2; exit 2 ;;
|
|
43
|
+
esac
|
|
44
|
+
done
|
|
45
|
+
|
|
46
|
+
if [[ ! -d "$CLAUDE_SOURCE" ]]; then
|
|
47
|
+
echo "No repo-managed Claude assets found at $CLAUDE_SOURCE" >&2
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Build the list of (source-abs, dest-abs) pairs.
|
|
52
|
+
SRCS=()
|
|
53
|
+
DSTS=()
|
|
54
|
+
|
|
55
|
+
# 1. claude/ tree -> <live>/...
|
|
56
|
+
while IFS= read -r rel; do
|
|
57
|
+
SRCS+=("$CLAUDE_SOURCE/$rel")
|
|
58
|
+
DSTS+=("$LIVE_ROOT/$rel")
|
|
59
|
+
done < <(cd "$CLAUDE_SOURCE" && find . -type f -name '*.md' | sed 's|^\./||' | sort)
|
|
60
|
+
|
|
61
|
+
# 2. security/wiki scaffold templates -> <live>/templates/
|
|
62
|
+
for tpl in "$TEMPLATE_SOURCE"/security-*.template "$TEMPLATE_SOURCE"/wiki-*.template; do
|
|
63
|
+
[[ -f "$tpl" ]] || continue
|
|
64
|
+
SRCS+=("$tpl")
|
|
65
|
+
DSTS+=("$LIVE_ROOT/templates/$(basename "$tpl")")
|
|
66
|
+
done
|
|
67
|
+
|
|
68
|
+
if ((${#SRCS[@]} == 0)); then
|
|
69
|
+
echo "No managed assets found to sync" >&2
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
status=0
|
|
74
|
+
for i in "${!SRCS[@]}"; do
|
|
75
|
+
src="${SRCS[$i]}"
|
|
76
|
+
dst="${DSTS[$i]}"
|
|
77
|
+
rel="${dst#"$LIVE_ROOT"/}"
|
|
78
|
+
if [[ "$MODE" == "apply" ]]; then
|
|
79
|
+
mkdir -p "$(dirname "$dst")"
|
|
80
|
+
cp "$src" "$dst"
|
|
81
|
+
echo "installed: $rel"
|
|
82
|
+
else
|
|
83
|
+
if [[ -f "$dst" ]] && cmp -s "$src" "$dst"; then
|
|
84
|
+
echo "ok: $rel"
|
|
85
|
+
else
|
|
86
|
+
echo "DRIFT: $rel"
|
|
87
|
+
status=1
|
|
88
|
+
fi
|
|
89
|
+
fi
|
|
90
|
+
done
|
|
91
|
+
|
|
92
|
+
# Claude hooks: render @@INFRA_ROOT@@ and register each hook on its correct
|
|
93
|
+
# event in <live>/settings.json. This is the one place that touches the global
|
|
94
|
+
# user settings; it backs up first and matches by command so re-runs are no-ops.
|
|
95
|
+
HOOK_LIVE_DIR="$LIVE_ROOT/hooks"
|
|
96
|
+
SETTINGS_FILE="$LIVE_ROOT/settings.json"
|
|
97
|
+
|
|
98
|
+
# Map hook filename -> "event[:matcher][@timeout]". matcher only for
|
|
99
|
+
# PreToolUse-style events; an optional trailing "@timeout" (seconds) threads
|
|
100
|
+
# an explicit per-hook timeout into the settings.json registration entry so
|
|
101
|
+
# Claude Code's kill budget is documented, not assumed (Pitfall 1).
|
|
102
|
+
declare -A HOOK_EVENTS=(
|
|
103
|
+
["memory-wakeup.sh"]="SessionStart"
|
|
104
|
+
["memory-capture.sh"]="SessionEnd"
|
|
105
|
+
["memory-recall.sh"]="PreToolUse:Edit|Write|MultiEdit"
|
|
106
|
+
["context-explore-pretask.sh"]="UserPromptSubmit@25"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
for hook_src in "$CLAUDE_SOURCE"/hooks/*.sh; do
|
|
110
|
+
[[ -f "$hook_src" ]] || continue
|
|
111
|
+
hook_name="$(basename "$hook_src")"
|
|
112
|
+
hook_dst="$HOOK_LIVE_DIR/$hook_name"
|
|
113
|
+
hook_cmd="bash \"$hook_dst\""
|
|
114
|
+
event_spec="${HOOK_EVENTS[$hook_name]:-}"
|
|
115
|
+
if [[ -z "$event_spec" ]]; then
|
|
116
|
+
# Unknown hook: still render it, but do not auto-register (avoid stomping settings).
|
|
117
|
+
if [[ "$MODE" == "apply" ]]; then
|
|
118
|
+
mkdir -p "$HOOK_LIVE_DIR"
|
|
119
|
+
sed "s|@@INFRA_ROOT@@|$ROOT_DIR|g" "$hook_src" > "$hook_dst"
|
|
120
|
+
chmod +x "$hook_dst"
|
|
121
|
+
echo "installed (unregistered): hooks/$hook_name"
|
|
122
|
+
fi
|
|
123
|
+
continue
|
|
124
|
+
fi
|
|
125
|
+
hook_timeout=""
|
|
126
|
+
if [[ "$event_spec" == *"@"* ]]; then
|
|
127
|
+
hook_timeout="${event_spec##*@}"
|
|
128
|
+
event_spec="${event_spec%@*}"
|
|
129
|
+
fi
|
|
130
|
+
event="${event_spec%%:*}"
|
|
131
|
+
matcher="${event_spec#*:}"
|
|
132
|
+
[[ "$matcher" == "$event_spec" ]] && matcher=""
|
|
133
|
+
if [[ "$MODE" == "apply" ]]; then
|
|
134
|
+
mkdir -p "$HOOK_LIVE_DIR"
|
|
135
|
+
sed "s|@@INFRA_ROOT@@|$ROOT_DIR|g" "$hook_src" > "$hook_dst"
|
|
136
|
+
chmod +x "$hook_dst"
|
|
137
|
+
echo "installed: hooks/$hook_name"
|
|
138
|
+
node -e '
|
|
139
|
+
const fs = require("fs");
|
|
140
|
+
const [p, cmd, token, event, matcher, timeoutStr] = process.argv.slice(1);
|
|
141
|
+
let s = {};
|
|
142
|
+
if (fs.existsSync(p)) {
|
|
143
|
+
try { s = JSON.parse(fs.readFileSync(p, "utf8")); }
|
|
144
|
+
catch (e) { console.error("settings.json parse failed, leaving untouched"); process.exit(1); }
|
|
145
|
+
}
|
|
146
|
+
s.hooks = s.hooks || {};
|
|
147
|
+
s.hooks[event] = Array.isArray(s.hooks[event]) ? s.hooks[event] : [];
|
|
148
|
+
const already = s.hooks[event].some((entry) =>
|
|
149
|
+
Array.isArray(entry.hooks) && entry.hooks.some((h) => typeof h.command === "string" && h.command.includes(token)));
|
|
150
|
+
if (already) { console.log(`ok: ${event} hook already registered`); process.exit(0); }
|
|
151
|
+
if (fs.existsSync(p)) fs.copyFileSync(p, p + ".bak");
|
|
152
|
+
const hookObj = { type: "command", command: cmd };
|
|
153
|
+
if (timeoutStr) hookObj.timeout = parseInt(timeoutStr, 10);
|
|
154
|
+
const entry = matcher
|
|
155
|
+
? { matcher, hooks: [hookObj] }
|
|
156
|
+
: { hooks: [hookObj] };
|
|
157
|
+
s.hooks[event].push(entry);
|
|
158
|
+
fs.writeFileSync(p, JSON.stringify(s, null, 2) + "\n");
|
|
159
|
+
console.log(`registered: ${event} hook${matcher ? " (" + matcher + ")" : ""}${timeoutStr ? " (timeout=" + timeoutStr + "s)" : ""}`);
|
|
160
|
+
' "$SETTINGS_FILE" "$hook_cmd" "$hook_dst" "$event" "$matcher" "$hook_timeout" || status=1
|
|
161
|
+
else
|
|
162
|
+
if [[ -f "$hook_dst" ]] && grep -qF "$hook_name" "$SETTINGS_FILE" 2>/dev/null; then
|
|
163
|
+
echo "ok: hooks/$hook_name"
|
|
164
|
+
else
|
|
165
|
+
echo "DRIFT: hooks/$hook_name"
|
|
166
|
+
status=1
|
|
167
|
+
fi
|
|
168
|
+
fi
|
|
169
|
+
done
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
if [[ "$MODE" == "apply" ]]; then
|
|
173
|
+
for i in "${!SRCS[@]}"; do
|
|
174
|
+
if ! cmp -s "${SRCS[$i]}" "${DSTS[$i]}"; then
|
|
175
|
+
echo "VERIFY FAILED: ${DSTS[$i]#"$LIVE_ROOT"/}" >&2
|
|
176
|
+
status=1
|
|
177
|
+
fi
|
|
178
|
+
done
|
|
179
|
+
[[ "$status" -eq 0 ]] && echo "All Claude assets installed and verified into $LIVE_ROOT."
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
exit "$status"
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
usage() {
|
|
5
|
+
cat <<'EOF'
|
|
6
|
+
Usage: sync-opencode-explore-assets.sh [--check|--apply] [--live-root PATH]
|
|
7
|
+
|
|
8
|
+
Compare or sync the repo-managed OpenCode explore assets against the live
|
|
9
|
+
OpenCode config tree.
|
|
10
|
+
|
|
11
|
+
Options:
|
|
12
|
+
--check Verify that the managed live assets match the repo copy (default)
|
|
13
|
+
--apply Copy the repo-managed assets into the live OpenCode tree, then verify
|
|
14
|
+
--live-root PATH Override the live OpenCode root (default: $OPENCODE_CONFIG_DIR or $HOME/.config/opencode)
|
|
15
|
+
-h, --help Show this help text
|
|
16
|
+
|
|
17
|
+
Notes:
|
|
18
|
+
- The repo-managed source of truth lives under ./opencode/
|
|
19
|
+
- This script manages only the explore-command OpenCode asset
|
|
20
|
+
- Extra live explore assets are reported as warnings but are not deleted automatically
|
|
21
|
+
EOF
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
25
|
+
SOURCE_ROOT="$ROOT_DIR/opencode"
|
|
26
|
+
LIVE_ROOT="${OPENCODE_CONFIG_DIR:-$HOME/.config/opencode}"
|
|
27
|
+
MODE="check"
|
|
28
|
+
|
|
29
|
+
ASSETS=(
|
|
30
|
+
"command/context-explore.md"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
while [[ $# -gt 0 ]]; do
|
|
34
|
+
case "$1" in
|
|
35
|
+
--check)
|
|
36
|
+
MODE="check"
|
|
37
|
+
shift
|
|
38
|
+
;;
|
|
39
|
+
--apply)
|
|
40
|
+
MODE="apply"
|
|
41
|
+
shift
|
|
42
|
+
;;
|
|
43
|
+
--live-root)
|
|
44
|
+
LIVE_ROOT="$2"
|
|
45
|
+
shift 2
|
|
46
|
+
;;
|
|
47
|
+
-h|--help)
|
|
48
|
+
usage
|
|
49
|
+
exit 0
|
|
50
|
+
;;
|
|
51
|
+
*)
|
|
52
|
+
echo "Unknown argument: $1" >&2
|
|
53
|
+
usage >&2
|
|
54
|
+
exit 2
|
|
55
|
+
;;
|
|
56
|
+
esac
|
|
57
|
+
done
|
|
58
|
+
|
|
59
|
+
collect_extra_live_assets() {
|
|
60
|
+
local root="$1"
|
|
61
|
+
local -A managed=()
|
|
62
|
+
local rel
|
|
63
|
+
|
|
64
|
+
for rel in "${ASSETS[@]}"; do
|
|
65
|
+
managed["$rel"]=1
|
|
66
|
+
done
|
|
67
|
+
|
|
68
|
+
while IFS= read -r rel; do
|
|
69
|
+
[[ -n "$rel" ]] || continue
|
|
70
|
+
if [[ -z "${managed[$rel]:-}" ]]; then
|
|
71
|
+
printf '%s\n' "$rel"
|
|
72
|
+
fi
|
|
73
|
+
done < <(
|
|
74
|
+
find "$root/command" -maxdepth 1 -type f -name 'context-explore*.md' -printf 'command/%f\n' 2>/dev/null | sort -u || true
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
ensure_source_assets_exist() {
|
|
79
|
+
local rel
|
|
80
|
+
local missing=0
|
|
81
|
+
|
|
82
|
+
for rel in "${ASSETS[@]}"; do
|
|
83
|
+
if [[ ! -f "$SOURCE_ROOT/$rel" ]]; then
|
|
84
|
+
echo "Missing repo-managed source asset: $SOURCE_ROOT/$rel" >&2
|
|
85
|
+
missing=1
|
|
86
|
+
fi
|
|
87
|
+
done
|
|
88
|
+
|
|
89
|
+
if [[ $missing -ne 0 ]]; then
|
|
90
|
+
exit 2
|
|
91
|
+
fi
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
report_extra_live_assets() {
|
|
95
|
+
local -a extras=()
|
|
96
|
+
|
|
97
|
+
if [[ ! -d "$LIVE_ROOT" ]]; then
|
|
98
|
+
return
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
mapfile -t extras < <(collect_extra_live_assets "$LIVE_ROOT")
|
|
102
|
+
if ((${#extras[@]} == 0)); then
|
|
103
|
+
return
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
printf 'Warning: found unmanaged live explore assets under %s:\n' "$LIVE_ROOT" >&2
|
|
107
|
+
printf ' - %s\n' "${extras[@]}" >&2
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
run_check() {
|
|
111
|
+
local rel
|
|
112
|
+
local src
|
|
113
|
+
local dst
|
|
114
|
+
local -a missing_live=()
|
|
115
|
+
local -a mismatched=()
|
|
116
|
+
|
|
117
|
+
ensure_source_assets_exist
|
|
118
|
+
|
|
119
|
+
for rel in "${ASSETS[@]}"; do
|
|
120
|
+
src="$SOURCE_ROOT/$rel"
|
|
121
|
+
dst="$LIVE_ROOT/$rel"
|
|
122
|
+
|
|
123
|
+
if [[ ! -f "$dst" ]]; then
|
|
124
|
+
missing_live+=("$rel")
|
|
125
|
+
continue
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
if ! cmp -s "$src" "$dst"; then
|
|
129
|
+
mismatched+=("$rel")
|
|
130
|
+
fi
|
|
131
|
+
done
|
|
132
|
+
|
|
133
|
+
report_extra_live_assets
|
|
134
|
+
|
|
135
|
+
if ((${#missing_live[@]} == 0 && ${#mismatched[@]} == 0)); then
|
|
136
|
+
printf 'Explore assets are in sync: %s managed files match %s\n' "${#ASSETS[@]}" "$LIVE_ROOT"
|
|
137
|
+
return 0
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
if ((${#missing_live[@]} > 0)); then
|
|
141
|
+
printf 'Missing live explore assets under %s:\n' "$LIVE_ROOT" >&2
|
|
142
|
+
printf ' - %s\n' "${missing_live[@]}" >&2
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
if ((${#mismatched[@]} > 0)); then
|
|
146
|
+
printf 'Out-of-sync live explore assets under %s:\n' "$LIVE_ROOT" >&2
|
|
147
|
+
printf ' - %s\n' "${mismatched[@]}" >&2
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
return 1
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
run_apply() {
|
|
154
|
+
local rel
|
|
155
|
+
local src
|
|
156
|
+
local dst
|
|
157
|
+
local updated=0
|
|
158
|
+
local unchanged=0
|
|
159
|
+
|
|
160
|
+
ensure_source_assets_exist
|
|
161
|
+
|
|
162
|
+
for rel in "${ASSETS[@]}"; do
|
|
163
|
+
src="$SOURCE_ROOT/$rel"
|
|
164
|
+
dst="$LIVE_ROOT/$rel"
|
|
165
|
+
|
|
166
|
+
mkdir -p "$(dirname "$dst")"
|
|
167
|
+
if [[ -f "$dst" ]] && cmp -s "$src" "$dst"; then
|
|
168
|
+
unchanged=$((unchanged + 1))
|
|
169
|
+
continue
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
install -m 0644 "$src" "$dst"
|
|
173
|
+
updated=$((updated + 1))
|
|
174
|
+
done
|
|
175
|
+
|
|
176
|
+
printf 'Applied %s explore asset(s); %s already matched.\n' "$updated" "$unchanged"
|
|
177
|
+
run_check
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
case "$MODE" in
|
|
181
|
+
check)
|
|
182
|
+
run_check
|
|
183
|
+
;;
|
|
184
|
+
apply)
|
|
185
|
+
run_apply
|
|
186
|
+
;;
|
|
187
|
+
esac
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
usage() {
|
|
5
|
+
cat <<'EOF'
|
|
6
|
+
Usage: sync-opencode-graphify-assets.sh [--check|--apply] [--live-root PATH]
|
|
7
|
+
|
|
8
|
+
Compare or sync the repo-managed Graphify OpenCode command asset against the
|
|
9
|
+
live OpenCode config tree.
|
|
10
|
+
|
|
11
|
+
Options:
|
|
12
|
+
--check Verify that the managed live asset matches the repo copy (default)
|
|
13
|
+
--apply Copy the repo-managed asset into the live OpenCode tree, then verify
|
|
14
|
+
--live-root PATH Override the live OpenCode root (default: $OPENCODE_CONFIG_DIR or $HOME/.config/opencode)
|
|
15
|
+
-h, --help Show this help text
|
|
16
|
+
|
|
17
|
+
Notes:
|
|
18
|
+
- The repo-managed source of truth lives under ./opencode/
|
|
19
|
+
- This script manages only the /graphify command asset
|
|
20
|
+
EOF
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
24
|
+
SOURCE_ROOT="$ROOT_DIR/opencode"
|
|
25
|
+
LIVE_ROOT="${OPENCODE_CONFIG_DIR:-$HOME/.config/opencode}"
|
|
26
|
+
MODE="check"
|
|
27
|
+
|
|
28
|
+
ASSETS=(
|
|
29
|
+
"command/graphify.md"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
while [[ $# -gt 0 ]]; do
|
|
33
|
+
case "$1" in
|
|
34
|
+
--check)
|
|
35
|
+
MODE="check"
|
|
36
|
+
shift
|
|
37
|
+
;;
|
|
38
|
+
--apply)
|
|
39
|
+
MODE="apply"
|
|
40
|
+
shift
|
|
41
|
+
;;
|
|
42
|
+
--live-root)
|
|
43
|
+
LIVE_ROOT="$2"
|
|
44
|
+
shift 2
|
|
45
|
+
;;
|
|
46
|
+
-h|--help)
|
|
47
|
+
usage
|
|
48
|
+
exit 0
|
|
49
|
+
;;
|
|
50
|
+
*)
|
|
51
|
+
echo "Unknown argument: $1" >&2
|
|
52
|
+
usage >&2
|
|
53
|
+
exit 2
|
|
54
|
+
;;
|
|
55
|
+
esac
|
|
56
|
+
done
|
|
57
|
+
|
|
58
|
+
ensure_source_assets_exist() {
|
|
59
|
+
local rel
|
|
60
|
+
local missing=0
|
|
61
|
+
|
|
62
|
+
for rel in "${ASSETS[@]}"; do
|
|
63
|
+
if [[ ! -f "$SOURCE_ROOT/$rel" ]]; then
|
|
64
|
+
echo "Missing repo-managed source asset: $SOURCE_ROOT/$rel" >&2
|
|
65
|
+
missing=1
|
|
66
|
+
fi
|
|
67
|
+
done
|
|
68
|
+
|
|
69
|
+
if [[ $missing -ne 0 ]]; then
|
|
70
|
+
exit 2
|
|
71
|
+
fi
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
check_asset_sync() {
|
|
75
|
+
local rel
|
|
76
|
+
local src
|
|
77
|
+
local dst
|
|
78
|
+
local -a missing_live=()
|
|
79
|
+
local -a mismatched=()
|
|
80
|
+
|
|
81
|
+
for rel in "${ASSETS[@]}"; do
|
|
82
|
+
src="$SOURCE_ROOT/$rel"
|
|
83
|
+
dst="$LIVE_ROOT/$rel"
|
|
84
|
+
|
|
85
|
+
if [[ ! -f "$dst" ]]; then
|
|
86
|
+
missing_live+=("$rel")
|
|
87
|
+
continue
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
if ! cmp -s "$src" "$dst"; then
|
|
91
|
+
mismatched+=("$rel")
|
|
92
|
+
fi
|
|
93
|
+
done
|
|
94
|
+
|
|
95
|
+
if ((${#missing_live[@]} == 0 && ${#mismatched[@]} == 0)); then
|
|
96
|
+
return 0
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
if ((${#missing_live[@]} > 0)); then
|
|
100
|
+
printf 'Missing live Graphify assets under %s:\n' "$LIVE_ROOT" >&2
|
|
101
|
+
printf ' - %s\n' "${missing_live[@]}" >&2
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
if ((${#mismatched[@]} > 0)); then
|
|
105
|
+
printf 'Out-of-sync live Graphify assets under %s:\n' "$LIVE_ROOT" >&2
|
|
106
|
+
printf ' - %s\n' "${mismatched[@]}" >&2
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
return 1
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
run_check() {
|
|
113
|
+
ensure_source_assets_exist
|
|
114
|
+
check_asset_sync
|
|
115
|
+
printf 'Graphify command asset is in sync under %s\n' "$LIVE_ROOT"
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
run_apply() {
|
|
119
|
+
local rel
|
|
120
|
+
local src
|
|
121
|
+
local dst
|
|
122
|
+
local updated=0
|
|
123
|
+
local unchanged=0
|
|
124
|
+
|
|
125
|
+
ensure_source_assets_exist
|
|
126
|
+
|
|
127
|
+
for rel in "${ASSETS[@]}"; do
|
|
128
|
+
src="$SOURCE_ROOT/$rel"
|
|
129
|
+
dst="$LIVE_ROOT/$rel"
|
|
130
|
+
|
|
131
|
+
mkdir -p "$(dirname "$dst")"
|
|
132
|
+
if [[ -f "$dst" ]] && cmp -s "$src" "$dst"; then
|
|
133
|
+
unchanged=$((unchanged + 1))
|
|
134
|
+
continue
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
install -m 0644 "$src" "$dst"
|
|
138
|
+
updated=$((updated + 1))
|
|
139
|
+
done
|
|
140
|
+
|
|
141
|
+
printf 'Applied %s Graphify asset(s); %s already matched.\n' "$updated" "$unchanged"
|
|
142
|
+
run_check
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
case "$MODE" in
|
|
146
|
+
check)
|
|
147
|
+
run_check
|
|
148
|
+
;;
|
|
149
|
+
apply)
|
|
150
|
+
run_apply
|
|
151
|
+
;;
|
|
152
|
+
esac
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
usage() {
|
|
5
|
+
cat <<'EOF'
|
|
6
|
+
Usage: sync-opencode-memory-assets.sh [--check|--apply] [--live-root PATH]
|
|
7
|
+
|
|
8
|
+
Compare or sync the repo-managed OpenCode memory and review assets against the
|
|
9
|
+
live OpenCode config tree.
|
|
10
|
+
|
|
11
|
+
Options:
|
|
12
|
+
--check Verify that the managed live assets match the repo copy (default)
|
|
13
|
+
--apply Copy the repo-managed assets into the live OpenCode tree, then verify
|
|
14
|
+
--live-root PATH Override the live OpenCode root (default: $OPENCODE_CONFIG_DIR or $HOME/.config/opencode)
|
|
15
|
+
-h, --help Show this help text
|
|
16
|
+
|
|
17
|
+
Notes:
|
|
18
|
+
- The repo-managed source of truth lives under ./opencode/
|
|
19
|
+
- This script manages the memory-sync / memory-review / code-review commands,
|
|
20
|
+
the code-reviewer agent, and the memory-review workflow. The memory-wakeup
|
|
21
|
+
plugin is handled by sync-opencode-plugin-assets.sh.
|
|
22
|
+
EOF
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
26
|
+
SOURCE_ROOT="$ROOT_DIR/opencode"
|
|
27
|
+
LIVE_ROOT="${OPENCODE_CONFIG_DIR:-$HOME/.config/opencode}"
|
|
28
|
+
MODE="check"
|
|
29
|
+
|
|
30
|
+
ASSETS=(
|
|
31
|
+
"command/memory-sync.md"
|
|
32
|
+
"command/memory-review.md"
|
|
33
|
+
"command/code-review.md"
|
|
34
|
+
"command/remember.md"
|
|
35
|
+
"command/recall.md"
|
|
36
|
+
"agents/code-reviewer.md"
|
|
37
|
+
"workflows/memory-review-workflow.md"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
while [[ $# -gt 0 ]]; do
|
|
41
|
+
case "$1" in
|
|
42
|
+
--check)
|
|
43
|
+
MODE="check"
|
|
44
|
+
shift
|
|
45
|
+
;;
|
|
46
|
+
--apply)
|
|
47
|
+
MODE="apply"
|
|
48
|
+
shift
|
|
49
|
+
;;
|
|
50
|
+
--live-root)
|
|
51
|
+
LIVE_ROOT="$2"
|
|
52
|
+
shift 2
|
|
53
|
+
;;
|
|
54
|
+
-h|--help)
|
|
55
|
+
usage
|
|
56
|
+
exit 0
|
|
57
|
+
;;
|
|
58
|
+
*)
|
|
59
|
+
echo "Unknown argument: $1" >&2
|
|
60
|
+
usage >&2
|
|
61
|
+
exit 2
|
|
62
|
+
;;
|
|
63
|
+
esac
|
|
64
|
+
done
|
|
65
|
+
|
|
66
|
+
ensure_source_assets_exist() {
|
|
67
|
+
local rel
|
|
68
|
+
local missing=0
|
|
69
|
+
|
|
70
|
+
for rel in "${ASSETS[@]}"; do
|
|
71
|
+
if [[ ! -f "$SOURCE_ROOT/$rel" ]]; then
|
|
72
|
+
echo "Missing repo-managed source asset: $SOURCE_ROOT/$rel" >&2
|
|
73
|
+
missing=1
|
|
74
|
+
fi
|
|
75
|
+
done
|
|
76
|
+
|
|
77
|
+
if [[ $missing -ne 0 ]]; then
|
|
78
|
+
exit 2
|
|
79
|
+
fi
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
check_asset_sync() {
|
|
83
|
+
local rel
|
|
84
|
+
local src
|
|
85
|
+
local dst
|
|
86
|
+
local -a missing_live=()
|
|
87
|
+
local -a mismatched=()
|
|
88
|
+
|
|
89
|
+
for rel in "${ASSETS[@]}"; do
|
|
90
|
+
src="$SOURCE_ROOT/$rel"
|
|
91
|
+
dst="$LIVE_ROOT/$rel"
|
|
92
|
+
|
|
93
|
+
if [[ ! -f "$dst" ]]; then
|
|
94
|
+
missing_live+=("$rel")
|
|
95
|
+
continue
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
if ! cmp -s "$src" "$dst"; then
|
|
99
|
+
mismatched+=("$rel")
|
|
100
|
+
fi
|
|
101
|
+
done
|
|
102
|
+
|
|
103
|
+
if ((${#missing_live[@]} == 0 && ${#mismatched[@]} == 0)); then
|
|
104
|
+
return 0
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
if ((${#missing_live[@]} > 0)); then
|
|
108
|
+
printf 'Missing live memory/review assets under %s:\n' "$LIVE_ROOT" >&2
|
|
109
|
+
printf ' - %s\n' "${missing_live[@]}" >&2
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
if ((${#mismatched[@]} > 0)); then
|
|
113
|
+
printf 'Out-of-sync live memory/review assets under %s:\n' "$LIVE_ROOT" >&2
|
|
114
|
+
printf ' - %s\n' "${mismatched[@]}" >&2
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
return 1
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
run_check() {
|
|
121
|
+
ensure_source_assets_exist
|
|
122
|
+
check_asset_sync
|
|
123
|
+
printf 'Memory/review assets are in sync: %s managed files match %s\n' "${#ASSETS[@]}" "$LIVE_ROOT"
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
run_apply() {
|
|
127
|
+
local rel
|
|
128
|
+
local src
|
|
129
|
+
local dst
|
|
130
|
+
local updated=0
|
|
131
|
+
local unchanged=0
|
|
132
|
+
|
|
133
|
+
ensure_source_assets_exist
|
|
134
|
+
|
|
135
|
+
for rel in "${ASSETS[@]}"; do
|
|
136
|
+
src="$SOURCE_ROOT/$rel"
|
|
137
|
+
dst="$LIVE_ROOT/$rel"
|
|
138
|
+
|
|
139
|
+
mkdir -p "$(dirname "$dst")"
|
|
140
|
+
if [[ -f "$dst" ]] && cmp -s "$src" "$dst"; then
|
|
141
|
+
unchanged=$((unchanged + 1))
|
|
142
|
+
continue
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
install -m 0644 "$src" "$dst"
|
|
146
|
+
updated=$((updated + 1))
|
|
147
|
+
done
|
|
148
|
+
|
|
149
|
+
printf 'Applied %s memory/review asset(s); %s already matched.\n' "$updated" "$unchanged"
|
|
150
|
+
run_check
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
case "$MODE" in
|
|
154
|
+
check)
|
|
155
|
+
run_check
|
|
156
|
+
;;
|
|
157
|
+
apply)
|
|
158
|
+
run_apply
|
|
159
|
+
;;
|
|
160
|
+
esac
|