meridian-dev 1.1.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 (33) hide show
  1. package/BOOTSTRAP_PROMPT.md +110 -0
  2. package/README.md +344 -0
  3. package/backup/hooks/session-end.sh +44 -0
  4. package/backup/hooks/session-start.sh +37 -0
  5. package/backup/setup.sh +156 -0
  6. package/bin/meridian.js +100 -0
  7. package/doctor.sh +173 -0
  8. package/install.sh +62 -0
  9. package/journal-summary.sh +577 -0
  10. package/package.json +42 -0
  11. package/setup.sh +407 -0
  12. package/specializations/claude-code/CLAUDE.md-global-fragment.md +52 -0
  13. package/specializations/claude-code/CLAUDE.md-repo-fragment.md +16 -0
  14. package/specializations/claude-code/README.md +96 -0
  15. package/specializations/claude-code/commands/doctor.md +31 -0
  16. package/specializations/claude-code/commands/init-memory.md +127 -0
  17. package/specializations/claude-code/commands/init-team.md +335 -0
  18. package/specializations/claude-code/commands/journal.md +66 -0
  19. package/specializations/claude-code/hooks/check-global-state.sh +68 -0
  20. package/specializations/claude-code/settings.json +10 -0
  21. package/specializations/cursor/README.md +112 -0
  22. package/specializations/cursor/global-rule.mdc +53 -0
  23. package/specializations/cursor/repo-rule.mdc +25 -0
  24. package/specializations/generic/README.md +47 -0
  25. package/templates/global.md +73 -0
  26. package/templates/memory-file.md +18 -0
  27. package/templates/personal-state.md +14 -0
  28. package/templates/product-state.md +39 -0
  29. package/templates/repo-state.md +18 -0
  30. package/templates/session-protocol-fragment.md +46 -0
  31. package/templates/strategy-state.md +37 -0
  32. package/templates/team-state.md +29 -0
  33. package/uninstall.sh +85 -0
@@ -0,0 +1,110 @@
1
+ # Bootstrap Prompt
2
+
3
+ Copy and paste the block below into a new Claude Code session. That's it.
4
+
5
+ ---
6
+
7
+ ```
8
+ Please set up Meridian for my Claude Code environment.
9
+
10
+ Run this command:
11
+ npx meridian-dev init
12
+
13
+ Once it completes:
14
+ 1. Tell me what was installed and what still needs to be configured
15
+ 2. Run /init-memory to initialize memory for the current repo
16
+ 3. Ask me what my preferences are (communication style, tool preferences, commit
17
+ conventions, anything I want Claude to always know) so we can fill in
18
+ global-state.md together
19
+ 4. Ask me if I want to set up backup (see below) — I will need to provide a
20
+ private GitHub repo URL before you can proceed with that step
21
+ 5. Ask me if I want to set up team context (/init-team) for shared journals,
22
+ digests, and product state
23
+ ```
24
+
25
+ ---
26
+
27
+ ## What happens
28
+
29
+ The install command will:
30
+ - Create `~/.claude/memory/` and `~/.claude/memory/journal/`
31
+ - Install `~/.claude/global-state.md` (your persistent index)
32
+ - Install `~/.claude/hooks/check-global-state.sh` (warns when state is stale)
33
+ - Install slash commands: `/init-memory`, `/init-team`, `/journal`, `/doctor`
34
+ - Register the hook in `~/.claude/settings.json`
35
+
36
+ After the paste, Claude will walk you through filling in your preferences. From
37
+ then on, every session in every repo will start with full context of where you
38
+ left off. You don't need to do anything special — just open Claude Code and
39
+ start working. Claude reads the state files automatically.
40
+
41
+ ---
42
+
43
+ ## Setting up backup (recommended)
44
+
45
+ Your memory files live locally at `~/.claude/`. If you want them backed up and
46
+ synced across machines, you need to provide a **private GitHub repo**.
47
+
48
+ **You must create this repo yourself** — Claude cannot do it for you.
49
+
50
+ Steps:
51
+ 1. Go to github.com/new
52
+ 2. Create a **private** repo (name it anything — e.g. `claude-memory`)
53
+ 3. Copy the repo URL (SSH preferred: `git@github.com:you/claude-memory.git`)
54
+ 4. Tell Claude: *"Set up backup using <your-repo-url>"*
55
+
56
+ Claude will then run:
57
+ ```
58
+ bash ~/.claude/skip-tissue/backup/setup.sh <your-repo-url>
59
+ ```
60
+
61
+ This will:
62
+ - Clone your backup repo to `~/.claude-backup/`
63
+ - Do an initial sync of your memory files
64
+ - Install hooks that automatically restore at session start and push at session end
65
+
66
+ After that, your memory is backed up silently on every session — no manual steps.
67
+
68
+ ---
69
+
70
+ ## Setting up team context (optional)
71
+
72
+ Once you have the basics working, run `/init-team` in a Claude Code session to set up:
73
+ - A shared team context repo for journals and digests
74
+ - Slack integration for weekly digest posts
75
+ - Notion integration for browseable product state and digest archives
76
+ - Product state for your pilot repo (PM intent, success criteria, constraints)
77
+
78
+ This is the team-level layer — it turns individual session context into shared
79
+ visibility across product, engineering, and strategy.
80
+
81
+ ---
82
+
83
+ ## Per-repo initialization
84
+
85
+ In any repo you work in, run:
86
+ ```
87
+ /init-memory
88
+ ```
89
+
90
+ Claude will create `.claude/team-state.md` (shared) and `.claude/personal-state.md`
91
+ (gitignored), fix your `.gitignore`, and register the repo in your global index.
92
+
93
+ ---
94
+
95
+ ## For Cursor users
96
+
97
+ ```
98
+ npx meridian-dev init-cursor
99
+ ```
100
+
101
+ ---
102
+
103
+ ## Source
104
+
105
+ https://github.com/leizerowicz/skip-tissue
106
+
107
+ To install a specific version:
108
+ ```
109
+ npx meridian-dev@1.1.0 init
110
+ ```
package/README.md ADDED
@@ -0,0 +1,344 @@
1
+ # Skip Tissue
2
+ ## Persistent Cross-Session Context for AI Coding Assistants
3
+
4
+ > **Tool-agnostic.** Works with Claude Code, Cursor, Copilot, or any AI assistant that reads files. See `specializations/` for tool-specific setup.
5
+
6
+ ---
7
+
8
+ ## Give Your AI a Working Model of Your World
9
+
10
+ The most valuable things your AI can know aren't in your codebase. They're the context that takes minutes to re-explain every session:
11
+
12
+ **Team dynamics:**
13
+ > "Jordan (co-founder) is a peer, not a report. Keep Jordan informed; Jordan manages their own workload. Don't suggest assigning tasks to Jordan."
14
+
15
+ **Project status:**
16
+ > "The checkout flow rewrite landed in staging but conversion dropped 3%. We're debugging before GA — don't touch the payment module until this is resolved."
17
+
18
+ **Decision frameworks:**
19
+ > "Build vs. buy: default to buy for non-core infrastructure unless there's significant vendor lock-in risk. We're a 4-person team — operational simplicity beats optimal."
20
+
21
+ These aren't README comments. They're the working model your AI needs to handle your real work — saved once, loaded every session.
22
+
23
+ ---
24
+
25
+ ## Why Not Just Write a Good README?
26
+
27
+ A README is for contributors. Memory files are for your AI — and they contain things you would never put in a README:
28
+
29
+ - Team dynamics and working style notes
30
+ - Personal decision frameworks and context that shifts over time
31
+ - Current status of every active project, updated after each session
32
+ - Solutions to recurring problems and cross-repo architectural decisions
33
+
34
+ A README is static documentation. Memory files are a live operating context.
35
+
36
+ ---
37
+
38
+ ## The Problem
39
+
40
+ AI coding assistants have no memory between sessions. Every conversation starts from zero. You re-explain the project, re-establish conventions, re-describe where you left off. The AI re-discovers things it already learned last time.
41
+
42
+ This kit solves that with **plain markdown files** that your AI reads at the start of every session. No databases, no proprietary formats, no vendor lock-in — just text files you own and can read yourself.
43
+
44
+ ---
45
+
46
+ ## How It Works
47
+
48
+ Your AI reads a small set of markdown files at session start to reconstruct context. At session end, it updates those files with what changed. The next session picks up exactly where you left off.
49
+
50
+ > **Note on paths:** The memory directory depends on your tool. Claude Code uses `~/.claude/`. Cursor and generic setups use `~/.ai-memory/`. The setup script handles this automatically — all examples below show the generic path.
51
+
52
+ ### The Three-File Pattern
53
+
54
+ ```
55
+ ~/.ai-memory/
56
+ global.md # Thin index — always loaded. Preferences, project table, file manifest.
57
+ state.md # Admin/non-repo notes (decisions, emails, misc tasks)
58
+ memory/ # Topic files — loaded on demand when the session topic matches
59
+ <topic>.md
60
+ journal/
61
+ YYYY-MM-DD.md # Daily log, append-only
62
+
63
+ <repo>/.ai-memory/
64
+ state.md # Per-repo: branch, status, next steps, gotchas
65
+ ```
66
+
67
+ **The index stays small** (under 80 lines). Detail lives in `memory/` files and is loaded only when relevant. This keeps every session fast and focused.
68
+
69
+ ---
70
+
71
+ ## Quick Start
72
+
73
+ ```bash
74
+ curl -fsSL https://raw.githubusercontent.com/leizerowicz/skip-tissue/main/install.sh | bash
75
+ ```
76
+
77
+ Or if you've cloned the repo:
78
+
79
+ ```bash
80
+ bash setup.sh
81
+ ```
82
+
83
+ The setup script will:
84
+ - Create the memory directory structure
85
+ - Install template files
86
+ - Install your chosen tool's specialization
87
+ - Register any hooks or system prompt fragments
88
+
89
+ Then pick up your tool-specific README in `specializations/`.
90
+
91
+ ---
92
+
93
+ ## Manual Setup
94
+
95
+ If you prefer to set up by hand:
96
+
97
+ ### 1. Create the directory structure
98
+
99
+ ```bash
100
+ mkdir -p ~/.ai-memory/memory/journal
101
+ ```
102
+
103
+ ### 2. Copy and fill in the global index
104
+
105
+ ```bash
106
+ cp templates/global.md ~/.ai-memory/global.md
107
+ # Edit it with your name, preferences, and initial projects
108
+ ```
109
+
110
+ ### 3. Add the session protocol to your AI tool
111
+
112
+ Each tool has a different mechanism. See `specializations/` for your tool:
113
+
114
+ | Tool | Specialization |
115
+ |------|---------------|
116
+ | Claude Code | `specializations/claude-code/` |
117
+ | Cursor | `specializations/cursor/` |
118
+ | Any (manual system prompt) | `specializations/generic/` |
119
+
120
+ ### 4. Initialize each repo
121
+
122
+ Copy `templates/repo-state.md` to `<repo>/.ai-memory/state.md` and fill it in. Or use the `/init-memory` command if your tool supports custom commands (see specialization).
123
+
124
+ ---
125
+
126
+ ## What Your Memory Files Look Like
127
+
128
+ Here are examples of the kinds of content that belong in memory files. These are the entries that make the difference between an AI that re-asks the same questions every session and one that picks up exactly where you left off.
129
+
130
+ **Working style and technical preferences** (`~/.ai-memory/memory/technical-patterns.md`):
131
+
132
+ ```markdown
133
+ ## Working Style
134
+ - Commit messages: imperative mood, explain why not what
135
+ - Never touch production before local repro
136
+ - Default to TypeScript strict mode
137
+ - Build vs. buy: default buy for non-core infrastructure unless vendor lock-in risk
138
+ - When in doubt, prefer the boring solution over the clever one
139
+ ```
140
+
141
+ **Team context** (`~/.ai-memory/memory/team-context.md`):
142
+
143
+ ```markdown
144
+ ## Team Context
145
+ - Design Lead: prefers async feedback via Figma, not Slack. Needs 24h heads-up for scope changes.
146
+ - Co-founder: peer, not a report. Keep informed; they manage their own workload.
147
+ - Support team: PST-based. Async decisions logged in Notion, not Slack.
148
+ ```
149
+
150
+ **Active projects** (in `~/.ai-memory/global.md`):
151
+
152
+ ```markdown
153
+ ## Active Projects
154
+ | Project | Status | Next |
155
+ |---------|--------|------|
156
+ | Checkout flow rewrite | Staging — conversion regression under investigation | Fix promo code edge case, re-run A/B |
157
+ | Notification service | In prod, but email deliverability dropped to 89% | Investigate SPF/DKIM alignment with new domain |
158
+ | Mobile app v2 | Design approved, API contracts drafted | Scaffold React Native nav + auth screens |
159
+ | Billing migration | **ON HOLD** — waiting on Stripe Connect approval | Unblock after Stripe responds (ETA ~1 week) |
160
+ ```
161
+
162
+ **Per-repo state** (`<repo>/.ai-memory/state.md`):
163
+
164
+ ```markdown
165
+ ## Branch
166
+ feat/webhooks-retry
167
+
168
+ ## Status
169
+ Retry logic works for 5xx errors. Stuck on idempotency — duplicate events when
170
+ the upstream acks late and we retry. Need a dedup key in the event payload.
171
+
172
+ ## Next
173
+ Add `idempotency_key` to WebhookEvent model, enforce uniqueness in Postgres,
174
+ then re-run the chaos test suite.
175
+
176
+ ## Gotchas
177
+ - ngrok tunnel expires every 2 hours — restart before long test runs.
178
+ - The `webhook_events` table in staging has 800K rows of junk. Truncate before
179
+ benchmarking or the index scan will mislead you.
180
+ ```
181
+
182
+ These files are plain markdown. You own them, you can read them, and they travel with you across tools.
183
+
184
+ ---
185
+
186
+ ## What to Store — and What Not To
187
+
188
+ Not everything belongs in AI-readable memory files. These files are transmitted to cloud AI provider APIs on every session. Here's a practical guide:
189
+
190
+ ### Tier 1 — Safe to include in any AI session
191
+ - Project status and next steps
192
+ - Technical patterns, gotchas, and architectural decisions
193
+ - Workflow preferences and tool conventions
194
+ - Team communication preferences (using roles, not names where possible)
195
+
196
+ ### Tier 2 — Handle with care
197
+ - Named individuals with working style notes — keep factual, avoid psychological characterizations. Use roles ("Design Lead") rather than names when possible.
198
+ - Vendor or client context — include enough to be useful, but not dispute positions or settlement targets
199
+ - Anything you'd be comfortable sharing in a work setting
200
+
201
+ ### Tier 3 — Do not store in AI-readable files
202
+ - Active legal dispute positions and settlement targets
203
+ - Salary, compensation, or financial negotiation strategy
204
+ - Named individual characterizations you'd be uncomfortable seeing quoted externally
205
+ - Medical or personal information about anyone
206
+
207
+ > **Guiding principle:** If you would be uncomfortable having this content quoted back in an employment discussion, a legal proceeding, or a vendor negotiation, it should not be in a file transmitted to a cloud AI API.
208
+
209
+ **A practical middle ground:** Pseudonymize sensitive context. "Design Lead prefers async feedback" is useful and carries no personal data risk. "Sarah thinks the PM is incompetent and threatened to quit" should stay in your head.
210
+
211
+ ### A note on GDPR
212
+
213
+ European users storing personal data about named third parties (employees, contractors, clients) in files transmitted to a cloud API likely constitute data processing under GDPR Article 4. If this applies to you, treat AI memory files as you would any other data processor relationship — store only what's necessary, and consider pseudonymization.
214
+
215
+ ---
216
+
217
+ ## What to Store — Examples
218
+
219
+ **Good memory file candidates:**
220
+ - Decisions that span multiple repos or sessions
221
+ - Architectural patterns your team has settled on
222
+ - Solutions to recurring problems
223
+ - Cross-repo conventions
224
+ - Team communication preferences and working style notes
225
+
226
+ **Don't store:**
227
+ - Things already in the codebase (code is the source of truth)
228
+ - Single-session temporary state
229
+ - Anything that duplicates what's in a project README or CLAUDE.md
230
+ - Sensitive negotiation positions or personal information (see tiers above)
231
+
232
+ ---
233
+
234
+ ## Tool-Specific Setup
235
+
236
+ ### Claude Code
237
+
238
+ See `specializations/claude-code/README.md` for:
239
+ - Adding the session protocol to `CLAUDE.md`
240
+ - Registering the `/init-memory` command
241
+ - Session start/end hooks
242
+
243
+ ### Cursor
244
+
245
+ See `specializations/cursor/README.md` for:
246
+ - Adding the protocol to `.cursorrules`
247
+ - Cursor-compatible memory file paths
248
+ - Session management without hooks
249
+
250
+ ---
251
+
252
+ ## Backup
253
+
254
+ Memory files are plain text — back them up like any other important file:
255
+
256
+ ```bash
257
+ # Add to an existing private repo, or create one
258
+ cd ~/.ai-memory
259
+ git init
260
+ git remote add origin git@github.com:<you>/ai-memory-private.git
261
+ git add -A && git commit -m "backup"
262
+ git push -u origin main
263
+ ```
264
+
265
+ Run this periodically or hook it into your session-end workflow.
266
+
267
+ ---
268
+
269
+ ## The Protocol
270
+
271
+ ### Session Start
272
+
273
+ Your AI will:
274
+ 1. Read `~/.ai-memory/global.md` (always)
275
+ 2. Read the current repo's `.ai-memory/state.md` (always)
276
+ 3. Check the Memory Files table in `global.md` — load any topic files that match the session
277
+ 4. Say: **"Resuming [project]: [summary of current state]"**
278
+ 5. Ask: **"What's the goal for this session? What does success look like?"**
279
+
280
+ ### Mid-Session
281
+
282
+ If the session drifts from the stated goal, the AI flags it:
283
+
284
+ > *"Quick check — we set out to [goal]. This feels like [tangent]. Stay the course or pivot?"*
285
+
286
+ ### Session End
287
+
288
+ When you say "stop", "done", "pause", "wrap up", or "tomorrow":
289
+ 1. AI updates `.ai-memory/state.md` in the current repo
290
+ 2. AI updates the Active Projects row in `~/.ai-memory/global.md`
291
+ 3. AI creates/updates any relevant topic memory files
292
+ 4. AI appends to `~/.ai-memory/memory/journal/YYYY-MM-DD.md`
293
+ 5. AI confirms: **"State saved. Say 'let's continue' next time."**
294
+
295
+ ---
296
+
297
+ ## Journal Format
298
+
299
+ The journal is a daily append-only log. Each entry follows this structure:
300
+
301
+ ```markdown
302
+ ## [Project or context] — [Brief title]
303
+ **Why:** [The stated goal for this session]
304
+ **What:** [Bullet list of what was actually done]
305
+ **Outcome:** [Did we hit the goal? Key deliverables]
306
+ **On track?:** [Focused or did we drift? What caused drift?]
307
+ **Lessons:** [Worth remembering cross-session]
308
+ ```
309
+
310
+ Multiple sessions per day just append more blocks to the same file.
311
+
312
+ ---
313
+
314
+ ## File Templates
315
+
316
+ All templates are in `templates/`. See them for copy-paste-ready starting points:
317
+
318
+ | Template | Purpose |
319
+ |----------|---------|
320
+ | `templates/global.md` | Starting `~/.ai-memory/global.md` |
321
+ | `templates/repo-state.md` | Starting `.ai-memory/state.md` for a repo |
322
+ | `templates/memory-file.md` | Starting point for a topic memory file |
323
+ | `templates/session-protocol-fragment.md` | The protocol block to inject into your AI tool |
324
+
325
+ ---
326
+
327
+ ## Contributing
328
+
329
+ ### Adding a Specialization
330
+
331
+ To add support for a new tool:
332
+ 1. Create `specializations/<tool-name>/README.md` following the pattern in `specializations/claude-code/README.md`
333
+ 2. Include any config files, hook scripts, or command files needed
334
+ 3. Document the install steps clearly — assume no prior context
335
+
336
+ ---
337
+
338
+ ## Critical Rules
339
+
340
+ 1. **Always read state files at session start.** They're plain files — they cannot fail.
341
+ 2. **Always update state files at session end.** Edit in place; don't append new sections.
342
+ 3. **Keep the global index small** (under 80 lines). Detail goes in `memory/` files.
343
+ 4. **Per-repo state stays focused** on that repo. Cross-repo context goes in topic files.
344
+ 5. **Don't gitignore your AI tool's config directory as a whole.** Only ignore local-only files (state.md, secrets). Commands, skills, and settings should be tracked.
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env bash
2
+ # Skip Tissue — Backup Push Hook
3
+ # Runs at session end. Syncs ~/.claude/ memory files to backup repo and pushes.
4
+ # Silently no-ops if backup isn't configured or nothing changed.
5
+
6
+ set -euo pipefail
7
+
8
+ MEMORY_DIR="$HOME/.claude"
9
+ BACKUP_DIR_FILE="$MEMORY_DIR/.backup-dir"
10
+
11
+ [ -f "$BACKUP_DIR_FILE" ] || exit 0
12
+ BACKUP_DIR="$(cat "$BACKUP_DIR_FILE")"
13
+ [ -d "$BACKUP_DIR/.git" ] || exit 0
14
+ command -v git &>/dev/null || exit 0
15
+
16
+ # Sync: ~/.claude/ -> backup repo
17
+ rsync -a \
18
+ "$MEMORY_DIR/global-state.md" \
19
+ "$MEMORY_DIR/state.md" \
20
+ "$BACKUP_DIR/" 2>/dev/null || true
21
+
22
+ if [ -d "$MEMORY_DIR/memory" ]; then
23
+ rsync -a "$MEMORY_DIR/memory/" "$BACKUP_DIR/memory/" 2>/dev/null || true
24
+ fi
25
+
26
+ # Commit and push if anything changed
27
+ cd "$BACKUP_DIR" || { echo "ERROR: cannot cd to backup dir $BACKUP_DIR"; exit 1; }
28
+ git add -A
29
+ if git diff --cached --quiet; then
30
+ exit 0 # Nothing changed — skip silently
31
+ fi
32
+
33
+ git commit -m "auto: $(date '+%Y-%m-%d %H:%M')" --quiet
34
+
35
+ # Push
36
+ GIT_ERROR=$(git push origin "${BRANCH:-main}" --quiet 2>&1) && {
37
+ echo "$(date '+%Y-%m-%d %H:%M')" > "$HOME/.claude/.backup-last-push"
38
+ rm -f "$HOME/.claude/.backup-last-error"
39
+ } || {
40
+ ERROR_MSG="$(date '+%Y-%m-%d %H:%M') — git push failed: $GIT_ERROR"
41
+ echo "$ERROR_MSG" > "$HOME/.claude/.backup-last-error"
42
+ echo "Warning: Backup push failed. Details: $HOME/.claude/.backup-last-error"
43
+ exit 1
44
+ }
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env bash
2
+ # Skip Tissue — Backup Restore Hook
3
+ # Runs at session start. Pulls latest from backup repo, then syncs to ~/.claude/
4
+ # Silently no-ops if backup isn't configured or git isn't available.
5
+
6
+ set -euo pipefail
7
+
8
+ MEMORY_DIR="$HOME/.claude"
9
+ BACKUP_DIR_FILE="$MEMORY_DIR/.backup-dir"
10
+
11
+ [ -f "$BACKUP_DIR_FILE" ] || exit 0
12
+ BACKUP_DIR="$(cat "$BACKUP_DIR_FILE")"
13
+ [ -d "$BACKUP_DIR/.git" ] || exit 0
14
+ command -v git &>/dev/null || exit 0
15
+
16
+ # Warn if last push failed
17
+ LAST_ERROR="$HOME/.claude/.backup-last-error"
18
+ if [ -f "$LAST_ERROR" ]; then
19
+ echo "Warning: Backup: last push FAILED at $(cat "$LAST_ERROR")"
20
+ echo " Fix and re-run: cd ~/.claude-backup && git push"
21
+ fi
22
+
23
+ # Pull latest (silently — don't block session start)
24
+ git -C "$BACKUP_DIR" pull --quiet --ff-only 2>/dev/null || true
25
+
26
+ # Restore: sync backup -> ~/.claude/
27
+ rsync -a --update \
28
+ "$BACKUP_DIR/global-state.md" \
29
+ "$MEMORY_DIR/" 2>/dev/null || true
30
+
31
+ rsync -a --update \
32
+ "$BACKUP_DIR/state.md" \
33
+ "$MEMORY_DIR/" 2>/dev/null || true
34
+
35
+ if [ -d "$BACKUP_DIR/memory" ]; then
36
+ rsync -a --update "$BACKUP_DIR/memory/" "$MEMORY_DIR/memory/" 2>/dev/null || true
37
+ fi
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env bash
2
+ # Skip Tissue — Backup Setup
3
+ # Wires a private GitHub repo as your memory backup.
4
+ #
5
+ # Usage: bash backup/setup.sh <repo-url>
6
+ # Example: bash backup/setup.sh git@github.com:you/claude-memory.git
7
+ # bash backup/setup.sh https://github.com/you/claude-memory.git
8
+ #
9
+ # What this does:
10
+ # 1. Clones your backup repo to ~/.claude-backup/
11
+ # 2. Does an initial sync of your memory files into it
12
+ # 3. Installs session-start (restore) and session-end (push) hooks
13
+ # 4. Registers both hooks in ~/.claude/settings.json
14
+
15
+ set -euo pipefail
16
+
17
+ REPO_URL="${1:-}"
18
+ BACKUP_DIR="$HOME/.claude-backup"
19
+ MEMORY_DIR="$HOME/.claude"
20
+ HOOKS_DIR="$HOME/.claude/hooks"
21
+ SETTINGS="$HOME/.claude/settings.json"
22
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
23
+
24
+ GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; RESET='\033[0m'
25
+ log() { echo -e "${GREEN}✓${RESET} $1"; }
26
+ warn() { echo -e "${YELLOW}⚠${RESET} $1"; }
27
+ err() { echo -e "${RED}✗${RESET} $1"; exit 1; }
28
+
29
+ # ── Validate ──────────────────────────────────────────────────────────────────
30
+
31
+ if [ -z "$REPO_URL" ]; then
32
+ err "No repo URL provided.\nUsage: bash backup/setup.sh <repo-url>"
33
+ fi
34
+
35
+ if ! command -v git &>/dev/null; then
36
+ err "git is required but not installed."
37
+ fi
38
+
39
+ if ! command -v rsync &>/dev/null; then
40
+ err "rsync is required but not installed. On macOS: brew install rsync"
41
+ fi
42
+
43
+ # ── Clone or init backup repo ─────────────────────────────────────────────────
44
+
45
+ echo ""
46
+ echo "Skip Tissue — Backup Setup"
47
+ echo ""
48
+
49
+ if [ -d "$BACKUP_DIR/.git" ]; then
50
+ warn "Backup repo already exists at $BACKUP_DIR — pulling latest"
51
+ git -C "$BACKUP_DIR" pull --quiet
52
+ else
53
+ echo "Cloning backup repo..."
54
+ if git clone "$REPO_URL" "$BACKUP_DIR" 2>/dev/null; then
55
+ log "Cloned $REPO_URL → $BACKUP_DIR"
56
+ else
57
+ # Repo exists on GitHub but is empty — clone fails. Init locally instead.
58
+ mkdir -p "$BACKUP_DIR"
59
+ git -C "$BACKUP_DIR" init
60
+ git -C "$BACKUP_DIR" remote add origin "$REPO_URL"
61
+ log "Initialized empty backup repo at $BACKUP_DIR"
62
+ fi
63
+ fi
64
+
65
+ # ── Initial sync ──────────────────────────────────────────────────────────────
66
+
67
+ echo "Syncing memory files to backup repo..."
68
+
69
+ # Files to back up: global index, admin state, and the entire memory/ directory
70
+ mkdir -p "$BACKUP_DIR"
71
+ rsync -a \
72
+ "$MEMORY_DIR/global-state.md" \
73
+ "$MEMORY_DIR/state.md" \
74
+ "$BACKUP_DIR/" 2>/dev/null || true
75
+
76
+ if [ -d "$MEMORY_DIR/memory" ]; then
77
+ mkdir -p "$BACKUP_DIR/memory"
78
+ rsync -a "$MEMORY_DIR/memory/" "$BACKUP_DIR/memory/" 2>/dev/null || true
79
+ fi
80
+
81
+ # Initial commit
82
+ cd "$BACKUP_DIR"
83
+ git add -A
84
+ if ! git diff --cached --quiet; then
85
+ git commit -m "initial backup — $(date '+%Y-%m-%d')"
86
+ git push -u origin main 2>/dev/null || git push -u origin master 2>/dev/null || \
87
+ warn "Push failed — check your repo URL and SSH/token auth, then run: git -C $BACKUP_DIR push"
88
+ log "Initial backup pushed"
89
+ else
90
+ log "Nothing to commit in initial sync"
91
+ fi
92
+
93
+ # ── Install hook scripts ──────────────────────────────────────────────────────
94
+
95
+ mkdir -p "$HOOKS_DIR"
96
+
97
+ cp "$SCRIPT_DIR/hooks/session-start.sh" "$HOOKS_DIR/backup-restore.sh"
98
+ chmod +x "$HOOKS_DIR/backup-restore.sh"
99
+ log "Installed: $HOOKS_DIR/backup-restore.sh"
100
+
101
+ cp "$SCRIPT_DIR/hooks/session-end.sh" "$HOOKS_DIR/backup-push.sh"
102
+ chmod +x "$HOOKS_DIR/backup-push.sh"
103
+ log "Installed: $HOOKS_DIR/backup-push.sh"
104
+
105
+ # Store the backup dir path so hooks know where to find it
106
+ echo "$BACKUP_DIR" > "$MEMORY_DIR/.backup-dir"
107
+ log "Stored backup dir config: $MEMORY_DIR/.backup-dir"
108
+
109
+ # ── Register hooks in settings.json ──────────────────────────────────────────
110
+
111
+ if [ ! -f "$SETTINGS" ]; then
112
+ cp "$SCRIPT_DIR/../specializations/claude-code/settings.json" "$SETTINGS"
113
+ fi
114
+
115
+ python3 - <<PYEOF
116
+ import json, os
117
+
118
+ settings_path = os.path.expanduser("$SETTINGS")
119
+ with open(settings_path) as f:
120
+ s = json.load(f)
121
+
122
+ hooks = s.setdefault("hooks", {})
123
+
124
+ # SessionStart: restore (pull) — prepend so it runs before check-global-state
125
+ start_hooks = hooks.setdefault("SessionStart", [])
126
+ restore_cmd = {"type": "command", "command": "bash ~/.claude/hooks/backup-restore.sh"}
127
+ if not any(h.get("command", "").endswith("backup-restore.sh") for h in start_hooks):
128
+ start_hooks.insert(0, restore_cmd)
129
+ print(" + Registered backup-restore.sh in SessionStart")
130
+ else:
131
+ print(" - backup-restore.sh already in SessionStart")
132
+
133
+ # Stop: push — runs when Claude session ends
134
+ stop_hooks = hooks.setdefault("Stop", [])
135
+ push_cmd = {"type": "command", "command": "bash ~/.claude/hooks/backup-push.sh"}
136
+ if not any(h.get("command", "").endswith("backup-push.sh") for h in stop_hooks):
137
+ stop_hooks.append(push_cmd)
138
+ print(" + Registered backup-push.sh in Stop")
139
+ else:
140
+ print(" - backup-push.sh already in Stop")
141
+
142
+ with open(settings_path, "w") as f:
143
+ json.dump(s, f, indent=2)
144
+ f.write("\n")
145
+ PYEOF
146
+
147
+ log "Updated $SETTINGS"
148
+
149
+ # ── Done ──────────────────────────────────────────────────────────────────────
150
+
151
+ echo ""
152
+ echo "Backup setup complete."
153
+ echo " Backup repo: $BACKUP_DIR → $REPO_URL"
154
+ echo " Restore hook: runs at session start (pulls latest)"
155
+ echo " Push hook: runs at session end (commits + pushes changes)"
156
+ echo ""