wayfind 0.0.1 → 2.0.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/BOOTSTRAP_PROMPT.md +120 -0
  2. package/bin/connectors/github.js +617 -0
  3. package/bin/connectors/index.js +13 -0
  4. package/bin/connectors/intercom.js +595 -0
  5. package/bin/connectors/llm.js +469 -0
  6. package/bin/connectors/notion.js +747 -0
  7. package/bin/connectors/transport.js +325 -0
  8. package/bin/content-store.js +2006 -0
  9. package/bin/digest.js +813 -0
  10. package/bin/rebuild-status.js +297 -0
  11. package/bin/slack-bot.js +1535 -0
  12. package/bin/slack.js +342 -0
  13. package/bin/storage/index.js +171 -0
  14. package/bin/storage/json-backend.js +348 -0
  15. package/bin/storage/sqlite-backend.js +415 -0
  16. package/bin/team-context.js +4209 -0
  17. package/bin/telemetry.js +159 -0
  18. package/doctor.sh +291 -0
  19. package/install.sh +144 -0
  20. package/journal-summary.sh +577 -0
  21. package/package.json +48 -6
  22. package/setup.sh +641 -0
  23. package/specializations/claude-code/CLAUDE.md-global-fragment.md +53 -0
  24. package/specializations/claude-code/CLAUDE.md-repo-fragment.md +16 -0
  25. package/specializations/claude-code/README.md +99 -0
  26. package/specializations/claude-code/commands/doctor.md +31 -0
  27. package/specializations/claude-code/commands/init-memory.md +154 -0
  28. package/specializations/claude-code/commands/init-team.md +415 -0
  29. package/specializations/claude-code/commands/journal.md +66 -0
  30. package/specializations/claude-code/commands/review-prs.md +119 -0
  31. package/specializations/claude-code/hooks/check-global-state.sh +20 -0
  32. package/specializations/claude-code/hooks/session-end.sh +36 -0
  33. package/specializations/claude-code/settings.json +15 -0
  34. package/specializations/cursor/README.md +120 -0
  35. package/specializations/cursor/global-rule.mdc +53 -0
  36. package/specializations/cursor/repo-rule.mdc +25 -0
  37. package/specializations/generic/README.md +47 -0
  38. package/templates/autopilot/design.md +22 -0
  39. package/templates/autopilot/engineering.md +22 -0
  40. package/templates/autopilot/product.md +22 -0
  41. package/templates/autopilot/strategy.md +22 -0
  42. package/templates/autopilot/unified.md +24 -0
  43. package/templates/deploy/.env.example +110 -0
  44. package/templates/deploy/docker-compose.yml +63 -0
  45. package/templates/deploy/slack-app-manifest.json +45 -0
  46. package/templates/github-actions/meridian-digest.yml +85 -0
  47. package/templates/global.md +79 -0
  48. package/templates/memory-file.md +18 -0
  49. package/templates/personal-state.md +14 -0
  50. package/templates/personas.json +28 -0
  51. package/templates/product-state.md +41 -0
  52. package/templates/prompts-readme.md +19 -0
  53. package/templates/repo-state.md +18 -0
  54. package/templates/session-protocol-fragment.md +46 -0
  55. package/templates/slack-app-manifest.json +27 -0
  56. package/templates/statusline.sh +22 -0
  57. package/templates/strategy-state.md +39 -0
  58. package/templates/team-state.md +55 -0
  59. package/uninstall.sh +105 -0
  60. package/README.md +0 -4
package/setup.sh ADDED
@@ -0,0 +1,641 @@
1
+ #!/usr/bin/env bash
2
+ # Wayfind — Setup Script
3
+ # Installs the memory system for your AI coding assistant.
4
+ # Safe to re-run (idempotent). Backs up existing files before modifying.
5
+ #
6
+ # Usage:
7
+ # bash setup.sh # Interactive — prompts for tool choice
8
+ # bash setup.sh --tool claude-code # Non-interactive
9
+ # bash setup.sh --tool cursor
10
+ # bash setup.sh --tool generic
11
+ # bash setup.sh --dry-run # Preview changes without modifying files
12
+ # bash setup.sh --version # Print installed version and exit
13
+ # bash setup.sh --help # Show this help
14
+
15
+ set -euo pipefail
16
+
17
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
18
+ TOOL=""
19
+ DRY_RUN=false
20
+ REPO_DIR=""
21
+ UPDATE=false
22
+
23
+ # ── Argument parsing ──────────────────────────────────────────────────────────
24
+
25
+ usage() {
26
+ cat <<USAGE
27
+ Usage: bash setup.sh [OPTIONS]
28
+
29
+ Options:
30
+ --tool=TOOL Specify tool without prompt (claude-code, cursor, generic)
31
+ --tool TOOL Same as above (space-separated form)
32
+ --repo=PATH Install per-repo rule into PATH (Cursor only)
33
+ --repo PATH Same as above (space-separated form)
34
+ --update Update mode: overwrite hook scripts and commands (memory files untouched)
35
+ --dry-run Preview what would be installed without modifying files
36
+ --version Print installed version and exit
37
+ --help Show this help message
38
+
39
+ Examples:
40
+ bash setup.sh
41
+ bash setup.sh --tool claude-code
42
+ bash setup.sh --tool=cursor --repo .
43
+ bash setup.sh --tool claude-code --update --dry-run
44
+ USAGE
45
+ }
46
+
47
+ while [[ $# -gt 0 ]]; do
48
+ case "$1" in
49
+ --tool=*) TOOL="${1#--tool=}"; shift ;;
50
+ --tool)
51
+ if [ -z "${2:-}" ]; then
52
+ echo "Error: --tool requires a value (claude-code, cursor, or generic)"
53
+ exit 1
54
+ fi
55
+ TOOL="$2"; shift 2 ;;
56
+ --repo=*) REPO_DIR="$(cd "${1#--repo=}" 2>/dev/null && pwd || echo "")"; shift ;;
57
+ --repo)
58
+ if [ -z "${2:-}" ]; then
59
+ echo "Error: --repo requires a path"
60
+ exit 1
61
+ fi
62
+ REPO_DIR="$(cd "$2" 2>/dev/null && pwd || echo "")"; shift 2 ;;
63
+ --update) UPDATE=true; shift ;;
64
+ --dry-run) DRY_RUN=true; shift ;;
65
+ --version)
66
+ VERSION_FILE="$HOME/.claude/team-context/.wayfind-version"
67
+ if [ -f "$VERSION_FILE" ]; then
68
+ echo "Wayfind v$(cat "$VERSION_FILE")"
69
+ else
70
+ echo "Wayfind version unknown (no .wayfind-version file found)"
71
+ fi
72
+ exit 0
73
+ ;;
74
+ --help) usage; exit 0 ;;
75
+ *) shift ;;
76
+ esac
77
+ done
78
+
79
+ # ── Helpers ───────────────────────────────────────────────────────────────────
80
+
81
+ GREEN='\033[0;32m'
82
+ YELLOW='\033[1;33m'
83
+ RED='\033[0;31m'
84
+ RESET='\033[0m'
85
+
86
+ log() { echo -e "${GREEN}✓${RESET} $1"; }
87
+ warn() { echo -e "${YELLOW}⚠${RESET} $1"; }
88
+ info() { echo " $1"; }
89
+ header() { echo ""; echo -e "${GREEN}── $1 ──${RESET}"; }
90
+
91
+ backup_if_exists() {
92
+ local file="$1"
93
+ if [ -f "$file" ]; then
94
+ local backup="${file}.bak.$(date +%Y%m%d%H%M%S)"
95
+ cp "$file" "$backup"
96
+ warn "Backed up existing $file → $backup"
97
+ fi
98
+ }
99
+
100
+ run() {
101
+ if [ "$DRY_RUN" = true ]; then
102
+ info "[dry-run] $*"
103
+ else
104
+ "$@"
105
+ fi
106
+ }
107
+
108
+ append_if_missing() {
109
+ local needle="$1"
110
+ local content="$2"
111
+ local file="$3"
112
+ if ! grep -qF "$needle" "$file" 2>/dev/null; then
113
+ echo "$content" >> "$file"
114
+ log "Appended to $file"
115
+ else
116
+ info "Already present in $file — skipped"
117
+ fi
118
+ }
119
+
120
+ # ── Tool selection ─────────────────────────────────────────────────────────────
121
+
122
+ if [ -z "$TOOL" ]; then
123
+ echo ""
124
+ echo "Wayfind — Setup"
125
+ echo "─────────────────────"
126
+ echo "Which AI tool are you setting up for?"
127
+ echo " 1) Claude Code"
128
+ echo " 2) Cursor"
129
+ echo " 3) Generic (system prompt / manual)"
130
+ echo ""
131
+ read -rp "Enter choice [1-3]: " choice
132
+ case "$choice" in
133
+ 1) TOOL="claude-code" ;;
134
+ 2) TOOL="cursor" ;;
135
+ 3) TOOL="generic" ;;
136
+ *) echo "Invalid choice. Exiting."; exit 1 ;;
137
+ esac
138
+ fi
139
+
140
+ SPEC_DIR="$SCRIPT_DIR/specializations/$TOOL"
141
+ if [ ! -d "$SPEC_DIR" ]; then
142
+ echo -e "${RED}Error:${RESET} No specialization found for '$TOOL' at $SPEC_DIR"
143
+ echo "Available: $(ls "$SCRIPT_DIR/specializations/")"
144
+ exit 1
145
+ fi
146
+
147
+ echo ""
148
+ echo "Installing Wayfind for: $TOOL"
149
+ [ "$DRY_RUN" = true ] && warn "Dry-run mode — no files will be modified"
150
+ [ "$UPDATE" = true ] && warn "Update mode: overwriting hook scripts and commands (memory files untouched)"
151
+
152
+ # Show upgrade messaging if version info is available (passed from install.sh)
153
+ WAYFIND_OLD_VERSION="${WAYFIND_OLD_VERSION:-}"
154
+ WAYFIND_NEW_VERSION="${WAYFIND_NEW_VERSION:-}"
155
+ if [ "$UPDATE" = true ] && [ -n "$WAYFIND_OLD_VERSION" ] && [ -n "$WAYFIND_NEW_VERSION" ]; then
156
+ if [ "$WAYFIND_OLD_VERSION" != "$WAYFIND_NEW_VERSION" ]; then
157
+ info "Upgrading from v${WAYFIND_OLD_VERSION} to v${WAYFIND_NEW_VERSION}"
158
+ fi
159
+ fi
160
+
161
+ # ── Tool-specific config ───────────────────────────────────────────────────────
162
+
163
+ case "$TOOL" in
164
+ claude-code)
165
+ MEMORY_DIR="$HOME/.claude"
166
+ MEMORY_SUBDIR="$HOME/.claude/memory"
167
+ GLOBAL_STATE="$HOME/.claude/global-state.md"
168
+ ADMIN_STATE="$HOME/.claude/state.md"
169
+ GLOBAL_CLAUDE_MD="$HOME/.claude/CLAUDE.md"
170
+ ;;
171
+ cursor)
172
+ MEMORY_DIR="$HOME/.ai-memory"
173
+ MEMORY_SUBDIR="$HOME/.ai-memory/memory"
174
+ GLOBAL_STATE="$HOME/.ai-memory/global.md"
175
+ ADMIN_STATE="$HOME/.ai-memory/state.md"
176
+ GLOBAL_CLAUDE_MD="" # No global instructions file for Cursor
177
+ ;;
178
+ generic)
179
+ MEMORY_DIR="$HOME/.ai-memory"
180
+ MEMORY_SUBDIR="$HOME/.ai-memory/memory"
181
+ GLOBAL_STATE="$HOME/.ai-memory/global.md"
182
+ ADMIN_STATE="$HOME/.ai-memory/state.md"
183
+ GLOBAL_CLAUDE_MD=""
184
+ ;;
185
+ esac
186
+
187
+ # ── Step 1: Directories ────────────────────────────────────────────────────────
188
+
189
+ header "Creating directories"
190
+
191
+ run mkdir -p "$MEMORY_SUBDIR/journal"
192
+ log "Memory directories: $MEMORY_SUBDIR/journal"
193
+
194
+ if [ "$TOOL" = "claude-code" ]; then
195
+ run mkdir -p "$HOME/.claude/hooks"
196
+ run mkdir -p "$HOME/.claude/commands"
197
+ log "Claude Code hooks and commands directories"
198
+ fi
199
+
200
+ # ── Step 2: Global state file ─────────────────────────────────────────────────
201
+
202
+ header "Global state file"
203
+
204
+ if [ ! -f "$GLOBAL_STATE" ]; then
205
+ if [ "$TOOL" = "claude-code" ]; then
206
+ if [ "$DRY_RUN" = false ]; then
207
+ sed \
208
+ -e 's|~/.ai-memory/|~/.claude/|g' \
209
+ -e 's|\.ai-memory/state\.md|.claude/state.md|g' \
210
+ "$SCRIPT_DIR/templates/global.md" > "$GLOBAL_STATE"
211
+ else
212
+ info "[dry-run] Would sed (path substitution) templates/global.md > $GLOBAL_STATE"
213
+ fi
214
+ else
215
+ run cp "$SCRIPT_DIR/templates/global.md" "$GLOBAL_STATE"
216
+ fi
217
+ log "Created $GLOBAL_STATE"
218
+ warn "Edit $GLOBAL_STATE to add your name, preferences, and projects"
219
+ else
220
+ info "Already exists: $GLOBAL_STATE — skipped"
221
+ fi
222
+
223
+ # ── Step 3: Admin state file (team/personal split) ───────────────────────────
224
+ # For team repos, use templates/team-state.md (committed, shared context).
225
+ # For personal notes, use templates/personal-state.md (gitignored, private).
226
+ # The admin state file at ADMIN_STATE is the non-repo catch-all (email, admin, misc).
227
+
228
+ header "Admin state file"
229
+
230
+ if [ ! -f "$ADMIN_STATE" ]; then
231
+ run cp "$SCRIPT_DIR/templates/repo-state.md" "$ADMIN_STATE"
232
+ log "Created $ADMIN_STATE"
233
+ else
234
+ info "Already exists: $ADMIN_STATE — skipped"
235
+ fi
236
+
237
+ # ── Step 3b: Persona configuration ────────────────────────────────────────────
238
+
239
+ header "Persona configuration"
240
+
241
+ case "$TOOL" in
242
+ claude-code) PERSONAS_DIR="$HOME/.claude/team-context" ;;
243
+ *) PERSONAS_DIR="$HOME/.ai-memory/team-context" ;;
244
+ esac
245
+
246
+ PERSONAS_DEST="$PERSONAS_DIR/personas.json"
247
+ if [ ! -f "$PERSONAS_DEST" ]; then
248
+ run mkdir -p "$PERSONAS_DIR"
249
+ run cp "$SCRIPT_DIR/templates/personas.json" "$PERSONAS_DEST"
250
+ log "Created $PERSONAS_DEST (default personas — edit to customize)"
251
+ else
252
+ info "Already exists: $PERSONAS_DEST — skipped (your customizations are preserved)"
253
+ fi
254
+
255
+ # ── Step 4: Tool-specific files ───────────────────────────────────────────────
256
+
257
+ header "Tool-specific files ($TOOL)"
258
+
259
+ case "$TOOL" in
260
+ claude-code)
261
+ # Global CLAUDE.md fragment
262
+ if [ -n "$GLOBAL_CLAUDE_MD" ]; then
263
+ if [ "$DRY_RUN" = true ]; then
264
+ info "[dry-run] Would append Session State Protocol to $GLOBAL_CLAUDE_MD"
265
+ else
266
+ touch "$GLOBAL_CLAUDE_MD"
267
+ append_if_missing "Session State Protocol" \
268
+ "$(cat "$SPEC_DIR/CLAUDE.md-global-fragment.md")" \
269
+ "$GLOBAL_CLAUDE_MD"
270
+ fi
271
+ fi
272
+
273
+ # Hook scripts
274
+ HOOK_DEST="$HOME/.claude/hooks/check-global-state.sh"
275
+ if [ ! -f "$HOOK_DEST" ] || [ "$UPDATE" = true ]; then
276
+ run cp "$SPEC_DIR/hooks/check-global-state.sh" "$HOOK_DEST"
277
+ run chmod +x "$HOOK_DEST"
278
+ log "Installed hook: $HOOK_DEST"
279
+ else
280
+ info "Hook already exists: $HOOK_DEST — skipped"
281
+ fi
282
+
283
+ SESSION_END_DEST="$HOME/.claude/hooks/session-end.sh"
284
+ if [ ! -f "$SESSION_END_DEST" ] || [ "$UPDATE" = true ]; then
285
+ run cp "$SPEC_DIR/hooks/session-end.sh" "$SESSION_END_DEST"
286
+ run chmod +x "$SESSION_END_DEST"
287
+ log "Installed hook: $SESSION_END_DEST"
288
+ else
289
+ info "Hook already exists: $SESSION_END_DEST — skipped"
290
+ fi
291
+
292
+ # init-memory command
293
+ CMD_DEST="$HOME/.claude/commands/init-memory.md"
294
+ if [ ! -f "$CMD_DEST" ] || [ "$UPDATE" = true ]; then
295
+ run cp "$SPEC_DIR/commands/init-memory.md" "$CMD_DEST"
296
+ log "Installed command: /init-memory"
297
+ else
298
+ info "/init-memory command already exists — skipped"
299
+ fi
300
+
301
+ # doctor command
302
+ DOCTOR_CMD_DEST="$HOME/.claude/commands/doctor.md"
303
+ if [ ! -f "$DOCTOR_CMD_DEST" ] || [ "$UPDATE" = true ]; then
304
+ run cp "$SPEC_DIR/commands/doctor.md" "$DOCTOR_CMD_DEST"
305
+ log "Installed command: /doctor"
306
+ else
307
+ info "/doctor command already exists — skipped"
308
+ fi
309
+
310
+ # doctor.sh script
311
+ run mkdir -p "$HOME/.claude/team-context"
312
+ DOCTOR_DEST="$HOME/.claude/team-context/doctor.sh"
313
+ if [ ! -f "$DOCTOR_DEST" ] || [ "$UPDATE" = true ]; then
314
+ run cp "$SCRIPT_DIR/doctor.sh" "$DOCTOR_DEST"
315
+ run chmod +x "$DOCTOR_DEST"
316
+ log "Installed doctor script: $DOCTOR_DEST"
317
+ else
318
+ info "doctor.sh already exists: $DOCTOR_DEST — skipped"
319
+ fi
320
+
321
+ # journal-summary.sh script
322
+ JOURNAL_DEST="$HOME/.claude/team-context/journal-summary.sh"
323
+ if [ ! -f "$JOURNAL_DEST" ] || [ "$UPDATE" = true ]; then
324
+ run cp "$SCRIPT_DIR/journal-summary.sh" "$JOURNAL_DEST"
325
+ run chmod +x "$JOURNAL_DEST"
326
+ log "Installed journal summary script: $JOURNAL_DEST"
327
+ else
328
+ info "journal-summary.sh already exists: $JOURNAL_DEST — skipped"
329
+ fi
330
+
331
+ # /journal command
332
+ JOURNAL_CMD_DEST="$HOME/.claude/commands/journal.md"
333
+ if [ ! -f "$JOURNAL_CMD_DEST" ] || [ "$UPDATE" = true ]; then
334
+ run cp "$SPEC_DIR/commands/journal.md" "$JOURNAL_CMD_DEST"
335
+ log "Installed command: /journal"
336
+ else
337
+ info "/journal command already exists — skipped"
338
+ fi
339
+
340
+ # /init-team command
341
+ TEAM_CMD_DEST="$HOME/.claude/commands/init-team.md"
342
+ if [ ! -f "$TEAM_CMD_DEST" ] || [ "$UPDATE" = true ]; then
343
+ if [ -f "$SPEC_DIR/commands/init-team.md" ]; then
344
+ run cp "$SPEC_DIR/commands/init-team.md" "$TEAM_CMD_DEST"
345
+ log "Installed command: /init-team"
346
+ fi
347
+ else
348
+ info "/init-team command already exists — skipped"
349
+ fi
350
+
351
+ # /review-prs command
352
+ REVIEW_CMD_DEST="$HOME/.claude/commands/review-prs.md"
353
+ if [ ! -f "$REVIEW_CMD_DEST" ] || [ "$UPDATE" = true ]; then
354
+ if [ -f "$SPEC_DIR/commands/review-prs.md" ]; then
355
+ run cp "$SPEC_DIR/commands/review-prs.md" "$REVIEW_CMD_DEST"
356
+ log "Installed command: /review-prs"
357
+ fi
358
+ else
359
+ info "/review-prs command already exists — skipped"
360
+ fi
361
+
362
+ # settings.json — merge hooks, don't overwrite
363
+ SETTINGS="$HOME/.claude/settings.json"
364
+ START_HOOK_CMD="bash ~/.claude/hooks/check-global-state.sh"
365
+ STOP_HOOK_CMD="bash ~/.claude/hooks/session-end.sh"
366
+
367
+ if [ ! -f "$SETTINGS" ]; then
368
+ run cp "$SPEC_DIR/settings.json" "$SETTINGS"
369
+ log "Created $SETTINGS with hook registration"
370
+ else
371
+ # Merge both hooks into existing settings.json using Python
372
+ NEEDS_MERGE=false
373
+ grep -q "check-global-state" "$SETTINGS" 2>/dev/null || NEEDS_MERGE=true
374
+ grep -q "session-end" "$SETTINGS" 2>/dev/null || NEEDS_MERGE=true
375
+ if [ "$NEEDS_MERGE" = true ]; then
376
+ if [ "$DRY_RUN" = false ]; then
377
+ TMP_SETTINGS="$(mktemp)"
378
+ if python3 - "$SETTINGS" "$START_HOOK_CMD" "$STOP_HOOK_CMD" "$TMP_SETTINGS" <<'PYEOF' 2>/dev/null; then
379
+ import json, sys
380
+ settings_path, start_cmd, stop_cmd, out_path = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]
381
+ try:
382
+ with open(settings_path) as f:
383
+ settings = json.load(f)
384
+ except (json.JSONDecodeError, IOError):
385
+ sys.exit(1)
386
+ hooks = settings.setdefault("hooks", {})
387
+
388
+ # Claude Code hook format: each event is an array of {matcher, hooks} groups.
389
+ # Each group has a "matcher" (string, "" = match all) and "hooks" (array of
390
+ # {type, command} objects).
391
+
392
+ def collect_hook_commands(event_arr):
393
+ """Extract all {type, command, ...} objects from any format variant."""
394
+ cmds = []
395
+ if not isinstance(event_arr, list):
396
+ event_arr = [event_arr] if isinstance(event_arr, dict) else []
397
+ for item in event_arr:
398
+ if not isinstance(item, dict):
399
+ continue
400
+ # Correct format: {matcher, hooks: [{type, command}]}
401
+ if "hooks" in item and isinstance(item["hooks"], list):
402
+ for h in item["hooks"]:
403
+ if isinstance(h, dict) and "type" in h:
404
+ cmds.append(h)
405
+ # Legacy flat format: {type, command} directly in array
406
+ elif "type" in item:
407
+ cmds.append(item)
408
+ return cmds
409
+
410
+ def ensure_hook_cmd(event_name, cmd, extra_fields=None):
411
+ """Add a hook command to an event, normalizing to correct format."""
412
+ existing = collect_hook_commands(hooks.get(event_name, []))
413
+ if any(h.get("command") == cmd for h in existing):
414
+ return # already present
415
+ entry = {"type": "command", "command": cmd}
416
+ if extra_fields:
417
+ entry.update(extra_fields)
418
+ existing.append(entry)
419
+ # Write back in correct {matcher, hooks} format
420
+ hooks[event_name] = [{"matcher": "", "hooks": existing}]
421
+
422
+ ensure_hook_cmd("SessionStart", start_cmd)
423
+ ensure_hook_cmd("Stop", stop_cmd, {"timeout": 30000})
424
+
425
+ with open(out_path, "w") as f:
426
+ json.dump(settings, f, indent=2)
427
+ f.write("\n")
428
+ PYEOF
429
+ mv "$TMP_SETTINGS" "$SETTINGS"
430
+ log "Merged hooks into existing $SETTINGS"
431
+ else
432
+ rm -f "$TMP_SETTINGS"
433
+ warn "Could not auto-merge hooks into $SETTINGS (malformed JSON or python3 unavailable)."
434
+ warn "Manually add hooks from: $SPEC_DIR/settings.json"
435
+ fi
436
+ else
437
+ info "[dry-run] Would merge hooks into $SETTINGS"
438
+ fi
439
+ else
440
+ info "Hooks already registered in settings.json — skipped"
441
+ fi
442
+ fi
443
+
444
+ # Status line script
445
+ STATUSLINE_DEST="$HOME/.claude/team-context/statusline.sh"
446
+ if [ ! -f "$STATUSLINE_DEST" ] || [ "$UPDATE" = true ]; then
447
+ run cp "$SCRIPT_DIR/templates/statusline.sh" "$STATUSLINE_DEST"
448
+ run chmod +x "$STATUSLINE_DEST"
449
+ log "Installed status line: $STATUSLINE_DEST"
450
+ else
451
+ info "Status line already exists: $STATUSLINE_DEST — skipped"
452
+ fi
453
+
454
+ # Merge statusLine config into settings.json
455
+ if [ -f "$SETTINGS" ] && ! grep -q "statusLine" "$SETTINGS" 2>/dev/null; then
456
+ if [ "$DRY_RUN" = false ]; then
457
+ TMP_SL="$(mktemp)"
458
+ if python3 - "$SETTINGS" "$STATUSLINE_DEST" "$TMP_SL" <<'PYEOF' 2>/dev/null; then
459
+ import json, sys
460
+ settings_path, sl_cmd, out_path = sys.argv[1], sys.argv[2], sys.argv[3]
461
+ try:
462
+ with open(settings_path) as f:
463
+ settings = json.load(f)
464
+ except (json.JSONDecodeError, IOError):
465
+ sys.exit(1)
466
+ if "statusLine" not in settings:
467
+ settings["statusLine"] = {"type": "command", "command": sl_cmd, "padding": 2}
468
+ with open(out_path, "w") as f:
469
+ json.dump(settings, f, indent=2)
470
+ f.write("\n")
471
+ PYEOF
472
+ mv "$TMP_SL" "$SETTINGS"
473
+ log "Added statusLine config to $SETTINGS"
474
+ else
475
+ rm -f "$TMP_SL"
476
+ warn "Could not add statusLine to $SETTINGS (malformed JSON or python3 unavailable)"
477
+ fi
478
+ else
479
+ info "[dry-run] Would add statusLine to $SETTINGS"
480
+ fi
481
+ else
482
+ if [ -f "$SETTINGS" ]; then
483
+ info "statusLine already configured in settings.json — skipped"
484
+ fi
485
+ fi
486
+ ;;
487
+
488
+ cursor)
489
+ # Global rules
490
+ run mkdir -p "$HOME/.cursor/rules"
491
+ GLOBAL_RULE="$HOME/.cursor/rules/ai-memory.mdc"
492
+ if [ ! -f "$GLOBAL_RULE" ] || [ "${UPDATE:-false}" = true ]; then
493
+ run cp "$SPEC_DIR/global-rule.mdc" "$GLOBAL_RULE"
494
+ log "Installed global Cursor rule: $GLOBAL_RULE"
495
+ else
496
+ info "Global Cursor rule already exists — skipped"
497
+ fi
498
+
499
+ # Per-repo rule (if --repo was passed)
500
+ if [ -n "${REPO_DIR:-}" ]; then
501
+ if [ -d "$REPO_DIR" ]; then
502
+ RULE_DIR="$REPO_DIR/.cursor/rules"
503
+ run mkdir -p "$RULE_DIR"
504
+ if [ ! -f "$RULE_DIR/memory.mdc" ] || [ "${UPDATE:-false}" = true ]; then
505
+ run cp "$SPEC_DIR/repo-rule.mdc" "$RULE_DIR/memory.mdc"
506
+ log "Installed repo rule: $RULE_DIR/memory.mdc"
507
+ warn "Edit $RULE_DIR/memory.mdc to add repo name and initial status"
508
+ else
509
+ info "Repo rule already exists — skipped"
510
+ fi
511
+ else
512
+ warn "--repo path not found or not a directory: $REPO_DIR"
513
+ fi
514
+ fi
515
+ ;;
516
+
517
+ generic)
518
+ info "Generic setup complete. See $SPEC_DIR/README.md for system prompt instructions."
519
+ ;;
520
+ esac
521
+
522
+ # ── Step 5: Write version file ────────────────────────────────────────────────
523
+
524
+ # If we know the version (passed from install.sh or from package.json), write it
525
+ if [ -n "$WAYFIND_NEW_VERSION" ]; then
526
+ INSTALL_VERSION="$WAYFIND_NEW_VERSION"
527
+ elif [ -f "$SCRIPT_DIR/package.json" ]; then
528
+ INSTALL_VERSION="$(grep '"version"' "$SCRIPT_DIR/package.json" | head -1 | sed 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')"
529
+ else
530
+ INSTALL_VERSION=""
531
+ fi
532
+
533
+ if [ -n "$INSTALL_VERSION" ]; then
534
+ KIT_DEST_DIR="$HOME/.claude/team-context"
535
+ VERSION_DEST="$KIT_DEST_DIR/.wayfind-version"
536
+ if [ "$DRY_RUN" = false ]; then
537
+ mkdir -p "$KIT_DEST_DIR"
538
+ TMP_VER="$(mktemp)"
539
+ echo "$INSTALL_VERSION" > "$TMP_VER"
540
+ mv "$TMP_VER" "$VERSION_DEST"
541
+ log "Version v${INSTALL_VERSION} written to $VERSION_DEST"
542
+ else
543
+ info "[dry-run] Would write v${INSTALL_VERSION} to $KIT_DEST_DIR/.wayfind-version"
544
+ fi
545
+ fi
546
+
547
+ # ── Step 6: Inject elicitation prompts into existing team-state files ────────
548
+
549
+ if [ "$UPDATE" = true ] && [ "$TOOL" = "claude-code" ]; then
550
+ header "Elicitation prompts"
551
+
552
+ ELICITATION_MARKER="## Elicitation Prompts"
553
+ ELICITATION_BLOCK="$ELICITATION_MARKER
554
+
555
+ <!-- These prompts guide the AI to capture richer context at decision moments.
556
+ The AI should ask AT MOST ONE of these when a significant decision is stated
557
+ without reasoning. Do not ask during routine implementation — only at moments
558
+ where a choice was made between alternatives.
559
+
560
+ The answers aren't for you (you already know) — they're for your teammates
561
+ who will read the digest tomorrow. -->
562
+
563
+ When a technical or product decision is made without stated reasoning, ask one of:
564
+ - \"What alternatives did you consider?\"
565
+ - \"What constraint or requirement drove this choice?\"
566
+ - \"What would need to change for you to reverse this decision?\"
567
+ - \"Who else on the team does this affect, and how?\"
568
+ - \"What's the risk if this assumption is wrong?\"
569
+
570
+ Do not ask if the decision already includes reasoning, tradeoffs, or constraints.
571
+ Do not ask more than once per decision. Do not ask during routine implementation."
572
+
573
+ # Scan repos for team-state.md files and inject if missing
574
+ INJECT_COUNT=0
575
+ for state_file in $(find "$HOME/repos" -path '*/.claude/team-state.md' 2>/dev/null); do
576
+ if ! grep -qF "$ELICITATION_MARKER" "$state_file" 2>/dev/null; then
577
+ if [ "$DRY_RUN" = false ]; then
578
+ # Insert before "## Shared Gotchas" if it exists, otherwise append
579
+ if grep -qF "## Shared Gotchas" "$state_file" 2>/dev/null; then
580
+ TMP_STATE="$(mktemp)"
581
+ awk -v block="$ELICITATION_BLOCK" '
582
+ /^## Shared Gotchas/ { print block; print ""; }
583
+ { print }
584
+ ' "$state_file" > "$TMP_STATE"
585
+ mv "$TMP_STATE" "$state_file"
586
+ else
587
+ echo "" >> "$state_file"
588
+ echo "$ELICITATION_BLOCK" >> "$state_file"
589
+ fi
590
+ log "Injected elicitation prompts: $state_file"
591
+ INJECT_COUNT=$((INJECT_COUNT + 1))
592
+ else
593
+ info "[dry-run] Would inject elicitation prompts into $state_file"
594
+ fi
595
+ else
596
+ info "Already has elicitation: $(basename "$(dirname "$(dirname "$state_file")")")/$(basename "$(dirname "$state_file")") — skipped"
597
+ fi
598
+ done
599
+
600
+ if [ "$INJECT_COUNT" -eq 0 ] && [ "$DRY_RUN" = false ]; then
601
+ info "No team-state.md files needed elicitation injection"
602
+ fi
603
+ fi
604
+
605
+ # ── Step 7: Personas (informational) ─────────────────────────────────────────
606
+
607
+ header "Personas"
608
+
609
+ echo ""
610
+ echo " Default personas: Product, Design, Engineering, Strategy"
611
+ echo " You can customize personas later with 'wayfind personas'"
612
+ echo ""
613
+
614
+ # ── Step 8: Summary ───────────────────────────────────────────────────────────
615
+
616
+ header "Done"
617
+
618
+ echo ""
619
+ echo "Wayfind installed for $TOOL."
620
+ echo ""
621
+ echo "Next steps:"
622
+ echo " 1. Edit $GLOBAL_STATE with your preferences and projects"
623
+
624
+ if [ "$TOOL" = "claude-code" ]; then
625
+ echo " 2. Open a repo and run /init-memory to set it up"
626
+ echo " 3. Start a Claude Code session — it will load your state automatically"
627
+ echo " 4. Run /journal for a weekly digest of your AI session logs"
628
+ echo " 5. Set up your profile: wayfind whoami --setup"
629
+ echo " 6. Create or join a team: wayfind team create"
630
+ elif [ "$TOOL" = "cursor" ]; then
631
+ echo " 2. Open a repo and run: bash setup.sh --tool cursor --repo <path>"
632
+ echo " 3. Start a Cursor session — the global rule loads your state automatically"
633
+ echo " 4. See specializations/cursor/README.md for full documentation"
634
+ else
635
+ echo " 2. Add the session protocol to your tool's system prompt"
636
+ echo " See: specializations/generic/README.md"
637
+ fi
638
+
639
+ echo ""
640
+ echo " Full documentation: README.md"
641
+ echo ""