-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinit.sh
More file actions
executable file
·594 lines (555 loc) · 26 KB
/
init.sh
File metadata and controls
executable file
·594 lines (555 loc) · 26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
#!/bin/bash
# cc-discipline init script
# Usage: cd your-project && bash /path/to/init.sh
# cd your-project && bash /path/to/init.sh --auto # non-interactive, defaults
# cd your-project && bash /path/to/init.sh --stack "3 4" --name myapp --global
# Or: curl -sL https://raw.githubusercontent.com/YOU/cc-discipline/main/init.sh | bash
# On Windows (Git Bash), set -e causes silent failures due to path/command differences.
# Use explicit error checking for critical operations instead.
if [[ "$(uname -s)" != MINGW* ]] && [[ "$(uname -s)" != MSYS* ]]; then
set -e
fi
# ─── Version ───
# cli.js sets CC_DISCIPLINE_VERSION on Windows where node paths are tricky
VERSION="${CC_DISCIPLINE_VERSION:-unknown}"
if [ "$VERSION" = "unknown" ]; then
_INIT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if command -v node &>/dev/null && [ -f "$_INIT_DIR/package.json" ]; then
VERSION=$(node -p "require('$_INIT_DIR/package.json').version" 2>/dev/null) || true
fi
fi
VERSION="${VERSION:-unknown}"
# ─── Parse CLI arguments ───
ARG_STACK=""
ARG_NAME=""
ARG_GLOBAL=""
ARG_AUTO=false
while [[ $# -gt 0 ]]; do
case $1 in
--auto)
ARG_AUTO=true
shift
;;
--stack)
ARG_STACK="$2"
shift 2
;;
--name)
ARG_NAME="$2"
shift 2
;;
--global)
ARG_GLOBAL="y"
shift
;;
--no-global)
ARG_GLOBAL="n"
shift
;;
-h|--help)
echo "Usage: bash init.sh [options]"
echo ""
echo "Options:"
echo " --auto Non-interactive with defaults (stack=7, name=dirname, no global)"
echo " --stack <choices> Stack selection: 1-7, space-separated (e.g., --stack \"3 4\")"
echo " --name <name> Project name (default: directory name)"
echo " --global Install global rules to ~/.claude/CLAUDE.md"
echo " --no-global Skip global rules install"
echo " -h, --help Show this help"
echo ""
echo "Stacks: 1=RTL 2=Embedded 3=Python 4=JS/TS 5=Mobile 6=Fullstack 7=General"
exit 0
;;
*)
echo "Unknown option: $1 (use --help for usage)"
exit 1
;;
esac
done
# --auto sets defaults for anything not explicitly provided
if [ "$ARG_AUTO" = true ]; then
[ -z "$ARG_STACK" ] && ARG_STACK="7"
[ -z "$ARG_GLOBAL" ] && ARG_GLOBAL="n"
fi
# ─── Colors ───
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
# Support both direct execution and npm (via CC_DISCIPLINE_PKG_DIR)
if [ -n "$CC_DISCIPLINE_PKG_DIR" ]; then
SCRIPT_DIR="$CC_DISCIPLINE_PKG_DIR"
else
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
fi
PROJECT_DIR="$(pwd)"
echo -e "${CYAN}╔══════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ Claude Code Discipline Framework — Setup ║${NC}"
echo -e "${CYAN}╚══════════════════════════════════════════════╝${NC}"
echo ""
echo -e "Project directory: ${GREEN}${PROJECT_DIR}${NC}"
# ─── Detect install mode ───
INSTALL_MODE="fresh"
INSTALLED_VERSION=""
if [ -f ".claude/.cc-discipline-version" ]; then
INSTALL_MODE="upgrade"
INSTALLED_VERSION=$(cat ".claude/.cc-discipline-version")
elif [ -f ".claude/hooks/streak-breaker.sh" ] || [ -f ".claude/hooks/pre-edit-guard.sh" ] || [ -f ".claude/hooks/session-start.sh" ] || [ -f ".claude/hooks/phase-gate.sh" ]; then
INSTALL_MODE="upgrade"
INSTALLED_VERSION="<2.0.0"
elif [ -d ".claude" ] || [ -f "CLAUDE.md" ]; then
INSTALL_MODE="append"
fi
case "$INSTALL_MODE" in
fresh)
echo -e "Mode: ${GREEN}Fresh install${NC}"
;;
upgrade)
echo -e "Mode: ${YELLOW}Upgrade cc-discipline (${INSTALLED_VERSION} → ${VERSION})${NC}"
;;
append)
echo -e "Mode: ${YELLOW}Adding discipline to existing project${NC}"
echo -e "${YELLOW}Your existing files will be preserved. cc-discipline files will be added alongside them.${NC}"
;;
esac
echo ""
# ─── Backup (upgrade/append) ───
BACKUP_DIR=""
if [ "$INSTALL_MODE" != "fresh" ]; then
BACKUP_DIR=".claude/.backup-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
[ -f "CLAUDE.md" ] && cp "CLAUDE.md" "$BACKUP_DIR/"
[ -f ".claude/settings.json" ] && cp ".claude/settings.json" "$BACKUP_DIR/"
[ -d ".claude/hooks" ] && cp -r ".claude/hooks" "$BACKUP_DIR/"
[ -d ".claude/rules" ] && cp -r ".claude/rules" "$BACKUP_DIR/"
[ -d ".claude/agents" ] && cp -r ".claude/agents" "$BACKUP_DIR/"
[ -d ".claude/skills" ] && cp -r ".claude/skills" "$BACKUP_DIR/"
echo -e "${GREEN}Backup saved to ${BACKUP_DIR}/${NC}"
echo ""
fi
# ─── Detect installed stacks (for upgrade/append) ───
detect_installed_stacks() {
INSTALLED_STACKS=""
[ -f ".claude/rules/stacks/rtl.md" ] && INSTALLED_STACKS="${INSTALLED_STACKS}1 "
[ -f ".claude/rules/stacks/embedded.md" ] && INSTALLED_STACKS="${INSTALLED_STACKS}2 "
[ -f ".claude/rules/stacks/python.md" ] && INSTALLED_STACKS="${INSTALLED_STACKS}3 "
[ -f ".claude/rules/stacks/js-ts.md" ] && INSTALLED_STACKS="${INSTALLED_STACKS}4 "
[ -f ".claude/rules/stacks/mobile.md" ] && INSTALLED_STACKS="${INSTALLED_STACKS}5 "
return 0
}
# ─── Select project type ───
if [ -n "$ARG_STACK" ]; then
STACK_CHOICES="$ARG_STACK"
echo -e "Stack: ${GREEN}${STACK_CHOICES}${NC} (from CLI)"
elif [ "$INSTALL_MODE" = "fresh" ]; then
echo -e "${BLUE}What is your project type? (select multiple with spaces)${NC}"
echo " 1) RTL / IC Design (Verilog, VHDL, SystemVerilog)"
echo " 2) Embedded (C/C++, bare-metal, RTOS)"
echo " 3) Python (backend / scripting / ML)"
echo " 4) JavaScript / TypeScript (Node, Web)"
echo " 5) Mobile (Swift, Kotlin, React Native, Flutter)"
echo " 6) Full-stack Web (frontend + backend)"
echo " 7) Other / General"
echo ""
read -p "Choose (e.g.: 1 2 or 3 4 6): " STACK_CHOICES
else
detect_installed_stacks
echo -e "${BLUE}Stack rules (* = already installed):${NC}"
for i in 1 2 3 4 5; do
MARKER=""
echo "$INSTALLED_STACKS" | grep -q "$i" && MARKER=" ${GREEN}*${NC}"
case $i in
1) echo -e " 1) RTL / IC Design${MARKER}" ;;
2) echo -e " 2) Embedded${MARKER}" ;;
3) echo -e " 3) Python${MARKER}" ;;
4) echo -e " 4) JavaScript / TypeScript${MARKER}" ;;
5) echo -e " 5) Mobile${MARKER}" ;;
esac
done
echo " 6) Full-stack Web (JS/TS + Python)"
echo " 7) Other / General"
echo ""
read -p "Add stacks (e.g.: 2 4), or press Enter to keep current: " STACK_CHOICES
fi
# Parse choices into array
IFS=' ' read -ra STACKS <<< "$STACK_CHOICES"
# ─── Project name ───
if [ -n "$ARG_NAME" ]; then
PROJECT_NAME="$ARG_NAME"
echo -e "Project name: ${GREEN}${PROJECT_NAME}${NC} (from CLI)"
elif [ "$INSTALL_MODE" = "fresh" ]; then
DEFAULT_NAME=$(basename "$PROJECT_DIR")
if [ "$ARG_AUTO" = true ]; then
PROJECT_NAME="$DEFAULT_NAME"
echo -e "Project name: ${GREEN}${PROJECT_NAME}${NC} (auto)"
else
read -p "Project name [$DEFAULT_NAME]: " PROJECT_NAME
PROJECT_NAME=${PROJECT_NAME:-$DEFAULT_NAME}
fi
else
# Try to extract from existing CLAUDE.md: "# MyProject — CLAUDE.md"
PROJECT_NAME=""
if [ -f "CLAUDE.md" ]; then
PROJECT_NAME=$(head -1 CLAUDE.md | sed 's/^# //;s/ — CLAUDE.md$//' 2>/dev/null)
fi
if [ -z "$PROJECT_NAME" ] || [ "$PROJECT_NAME" = "[PROJECT_NAME]" ]; then
DEFAULT_NAME=$(basename "$PROJECT_DIR")
if [ "$ARG_AUTO" = true ]; then
PROJECT_NAME="$DEFAULT_NAME"
echo -e "Project name: ${GREEN}${PROJECT_NAME}${NC} (auto)"
else
read -p "Project name [$DEFAULT_NAME]: " PROJECT_NAME
PROJECT_NAME=${PROJECT_NAME:-$DEFAULT_NAME}
fi
else
echo -e "Project name: ${GREEN}${PROJECT_NAME}${NC} (from CLAUDE.md)"
fi
fi
# ─── Create directory structure ───
echo ""
echo -e "${GREEN}Creating directory structure...${NC}"
mkdir -p .claude/rules/stacks
mkdir -p .claude/hooks
mkdir -p .claude/agents
mkdir -p .claude/skills
mkdir -p docs
# ─── Copy core rules (always applied, always overwrite — these are framework files) ───
echo -e "${GREEN}Installing core rules...${NC}"
cp "$SCRIPT_DIR/templates/.claude/rules/00-core-principles.md" .claude/rules/
cp "$SCRIPT_DIR/templates/.claude/rules/01-debugging.md" .claude/rules/
cp "$SCRIPT_DIR/templates/.claude/rules/02-before-edit.md" .claude/rules/
cp "$SCRIPT_DIR/templates/.claude/rules/03-context-mgmt.md" .claude/rules/
cp "$SCRIPT_DIR/templates/.claude/rules/04-no-mole-whacking.md" .claude/rules/
cp "$SCRIPT_DIR/templates/.claude/rules/05-phase-discipline.md" .claude/rules/
cp "$SCRIPT_DIR/templates/.claude/rules/06-multi-task.md" .claude/rules/
cp "$SCRIPT_DIR/templates/.claude/rules/07-integrity.md" .claude/rules/
# ─── Copy stack-specific rules based on selection ───
if [ ${#STACKS[@]} -gt 0 ] && [ -n "${STACKS[0]}" ]; then
echo -e "${GREEN}Installing stack rules...${NC}"
for choice in "${STACKS[@]}"; do
case $choice in
1)
cp "$SCRIPT_DIR/templates/.claude/rules/stacks/rtl.md" .claude/rules/stacks/
echo " ✓ RTL / IC Design rules"
;;
2)
cp "$SCRIPT_DIR/templates/.claude/rules/stacks/embedded.md" .claude/rules/stacks/
echo " ✓ Embedded development rules"
;;
3)
cp "$SCRIPT_DIR/templates/.claude/rules/stacks/python.md" .claude/rules/stacks/
echo " ✓ Python rules"
;;
4)
cp "$SCRIPT_DIR/templates/.claude/rules/stacks/js-ts.md" .claude/rules/stacks/
echo " ✓ JavaScript / TypeScript rules"
;;
5)
cp "$SCRIPT_DIR/templates/.claude/rules/stacks/mobile.md" .claude/rules/stacks/
echo " ✓ Mobile development rules"
;;
6)
cp "$SCRIPT_DIR/templates/.claude/rules/stacks/js-ts.md" .claude/rules/stacks/
cp "$SCRIPT_DIR/templates/.claude/rules/stacks/python.md" .claude/rules/stacks/
echo " ✓ Full-stack rules (JS/TS + Python)"
;;
7)
echo " ✓ General rules (no additional stack rules)"
;;
esac
done
fi
# ─── Install hooks ───
echo -e "${GREEN}Installing hooks...${NC}"
cp "$SCRIPT_DIR/templates/.claude/hooks/pre-edit-guard.sh" .claude/hooks/
cp "$SCRIPT_DIR/templates/.claude/hooks/post-error-remind.sh" .claude/hooks/
cp "$SCRIPT_DIR/templates/.claude/hooks/streak-breaker.sh" .claude/hooks/
cp "$SCRIPT_DIR/templates/.claude/hooks/session-start.sh" .claude/hooks/
cp "$SCRIPT_DIR/templates/.claude/hooks/phase-gate.sh" .claude/hooks/
cp "$SCRIPT_DIR/templates/.claude/hooks/git-guard.sh" .claude/hooks/
cp "$SCRIPT_DIR/templates/.claude/hooks/action-counter.sh" .claude/hooks/
chmod +x .claude/hooks/*.sh
# ─── Check jq availability ───
HAS_JQ=false
if command -v jq &>/dev/null; then
HAS_JQ=true
else
echo ""
echo -e "${YELLOW}Warning: jq not found — Hooks will use grep/sed fallback, but jq is recommended${NC}"
echo " macOS: brew install jq"
echo " Ubuntu: sudo apt install jq"
echo " Arch: sudo pacman -S jq"
echo ""
fi
# ─── Install/merge settings.json ───
SETTINGS_TEMPLATE="$SCRIPT_DIR/templates/.claude/settings.json"
if [ ! -f ".claude/settings.json" ]; then
# No existing settings.json — just copy template
cp "$SETTINGS_TEMPLATE" .claude/settings.json
echo -e "${GREEN} ✓ settings.json created${NC}"
elif [ "$INSTALL_MODE" = "fresh" ]; then
# Fresh mode shouldn't reach here, but just in case
cp "$SETTINGS_TEMPLATE" .claude/settings.json
else
# Merge: preserve user hooks, add/update cc-discipline hooks
if [ "$HAS_JQ" = true ]; then
TEMP_SETTINGS=$(mktemp)
MERGE_OK=false
if jq -s '
.[0] as $e | .[1] as $t |
def is_cc: .hooks | any(.command | test("pre-edit-guard|streak-breaker|post-error-remind|session-start|phase-gate|action-counter"));
def merge($ev): (($e.hooks[$ev] // []) | map(select(is_cc | not))) + ($t.hooks[$ev] // []);
$e * {
hooks: (($e.hooks // {}) + {
SessionStart: merge("SessionStart"),
PreToolUse: merge("PreToolUse"),
PostToolUse: merge("PostToolUse"),
PostToolUseFailure: merge("PostToolUseFailure")
} | with_entries(select(.value | length > 0)))
}
' .claude/settings.json "$SETTINGS_TEMPLATE" > "$TEMP_SETTINGS" 2>/dev/null; then
# Verify the output is valid JSON
if jq empty "$TEMP_SETTINGS" 2>/dev/null; then
mv "$TEMP_SETTINGS" .claude/settings.json
MERGE_OK=true
echo -e "${GREEN} ✓ settings.json merged (your existing hooks preserved)${NC}"
fi
fi
if [ "$MERGE_OK" = false ]; then
rm -f "$TEMP_SETTINGS"
echo -e "${YELLOW} Warning: settings.json merge failed. Your file was NOT modified.${NC}"
echo -e "${YELLOW} Backed up to: ${BACKUP_DIR}/settings.json${NC}"
cp "$SETTINGS_TEMPLATE" .claude/.cc-discipline-settings-template.json
echo -e "${YELLOW} See .claude/.cc-discipline-settings-template.json for hooks to add manually.${NC}"
fi
else
# No jq — don't touch existing settings.json
echo -e "${YELLOW} settings.json: cannot merge without jq. Your file was NOT modified.${NC}"
echo -e "${YELLOW} Backed up to: ${BACKUP_DIR}/settings.json${NC}"
cp "$SETTINGS_TEMPLATE" .claude/.cc-discipline-settings-template.json
echo -e "${YELLOW} See .claude/.cc-discipline-settings-template.json for hooks to add manually.${NC}"
fi
fi
# ─── Install subagents ───
echo -e "${GREEN}Installing subagents...${NC}"
cp "$SCRIPT_DIR/templates/.claude/agents/reviewer.md" .claude/agents/
cp "$SCRIPT_DIR/templates/.claude/agents/investigator.md" .claude/agents/
# ─── Install skills ───
echo -e "${GREEN}Installing skills...${NC}"
cp -r "$SCRIPT_DIR/templates/.claude/skills/commit" .claude/skills/
cp -r "$SCRIPT_DIR/templates/.claude/skills/self-check" .claude/skills/
cp -r "$SCRIPT_DIR/templates/.claude/skills/evaluate" .claude/skills/
cp -r "$SCRIPT_DIR/templates/.claude/skills/think" .claude/skills/
cp -r "$SCRIPT_DIR/templates/.claude/skills/retro" .claude/skills/
cp -r "$SCRIPT_DIR/templates/.claude/skills/summary" .claude/skills/
cp -r "$SCRIPT_DIR/templates/.claude/skills/investigate" .claude/skills/
echo " ✓ /commit — smart commit (test → update memory → commit)"
echo " ✓ /self-check — periodic discipline check (use with /loop 10m /self-check)"
echo " ✓ /evaluate — evaluate external review/advice against codebase context"
echo " ✓ /think — stop and think before coding (ask → propose → wait)"
echo " ✓ /retro — post-task retrospective (project + framework feedback)"
echo " ✓ /summary — write high-quality compact option before /compact"
echo " ✓ /investigate — multi-agent cross-investigation and proposal review"
# ─── Handle CLAUDE.md ───
if [ ! -f "CLAUDE.md" ]; then
# No CLAUDE.md exists — generate from template
echo -e "${GREEN}Generating CLAUDE.md...${NC}"
sed "s/\[PROJECT_NAME\]/$PROJECT_NAME/g" "$SCRIPT_DIR/templates/CLAUDE.md" > CLAUDE.md
else
if [ "$INSTALL_MODE" = "fresh" ]; then
# Should not happen (fresh mode means no existing files), but handle gracefully
sed "s/\[PROJECT_NAME\]/$PROJECT_NAME/g" "$SCRIPT_DIR/templates/CLAUDE.md" > CLAUDE.md
else
# Existing CLAUDE.md — NEVER overwrite
echo -e "${YELLOW} CLAUDE.md already exists — NOT modified (your content is safe)${NC}"
sed "s/\[PROJECT_NAME\]/$PROJECT_NAME/g" "$SCRIPT_DIR/templates/CLAUDE.md" \
> .claude/.cc-discipline-claude-md-template
echo " Discipline template saved to .claude/.cc-discipline-claude-md-template"
echo " You can reference it to add discipline sections to your CLAUDE.md."
fi
fi
# ─── Copy docs templates (only if not exist) ───
if [ ! -f "docs/progress.md" ]; then
cp "$SCRIPT_DIR/templates/docs/progress.md" docs/
fi
if [ ! -f "docs/debug-log.md" ]; then
cp "$SCRIPT_DIR/templates/docs/debug-log.md" docs/
fi
# ─── Install auto memory (symlink to .claude/memory/) ───
echo -e "${GREEN}Installing auto memory...${NC}"
MEMORY_PROJECT_KEY=$(echo "$PROJECT_DIR" | sed 's|/|-|g')
MEMORY_CLAUDE_DIR="$HOME/.claude/projects/${MEMORY_PROJECT_KEY}"
MEMORY_LOCAL_DIR="$PROJECT_DIR/.claude/memory"
# Ensure local .claude/memory/ exists in repo
mkdir -p "$MEMORY_LOCAL_DIR"
# If MEMORY.md doesn't exist locally, seed from template
if [ ! -f "$MEMORY_LOCAL_DIR/MEMORY.md" ]; then
cp "$SCRIPT_DIR/templates/memory/MEMORY.md" "$MEMORY_LOCAL_DIR/MEMORY.md"
echo " ✓ Memory template installed to .claude/memory/MEMORY.md"
else
echo -e " ${YELLOW}.claude/memory/MEMORY.md already exists (preserved)${NC}"
fi
# Create symlink: ~/.claude/projects/<key>/memory → .claude/memory/
mkdir -p "$MEMORY_CLAUDE_DIR"
if [ -L "$MEMORY_CLAUDE_DIR/memory" ]; then
# Symlink already exists — update it
rm "$MEMORY_CLAUDE_DIR/memory"
ln -s "$MEMORY_LOCAL_DIR" "$MEMORY_CLAUDE_DIR/memory"
echo " ✓ Memory symlink updated"
elif [ -d "$MEMORY_CLAUDE_DIR/memory" ]; then
# Existing real directory — migrate files then replace with symlink
for f in "$MEMORY_CLAUDE_DIR/memory/"*; do
[ -f "$f" ] || continue
fname=$(basename "$f")
if [ ! -f "$MEMORY_LOCAL_DIR/$fname" ]; then
cp "$f" "$MEMORY_LOCAL_DIR/$fname"
fi
done
rm -rf "$MEMORY_CLAUDE_DIR/memory"
ln -s "$MEMORY_LOCAL_DIR" "$MEMORY_CLAUDE_DIR/memory"
echo " ✓ Existing memory migrated to .claude/memory/ and symlinked"
else
ln -s "$MEMORY_LOCAL_DIR" "$MEMORY_CLAUDE_DIR/memory"
echo " ✓ Memory symlinked: ~/.claude/projects/.../ → .claude/memory/"
fi
# ─── Install global rules (optional) ───
if [ -n "$ARG_GLOBAL" ]; then
REPLY="$ARG_GLOBAL"
else
echo ""
echo -e "${BLUE}Install global rules to ~/.claude/CLAUDE.md?${NC}"
echo " (Global rules apply to all projects, only needs to be installed once)"
read -p "Install global rules? (y/N) " -n 1 -r
echo
fi
if [[ $REPLY =~ ^[Yy]$ ]]; then
mkdir -p ~/.claude
if [ -f ~/.claude/CLAUDE.md ]; then
echo -e "${YELLOW} Existing ~/.claude/CLAUDE.md found, backing up to ~/.claude/CLAUDE.md.bak${NC}"
cp ~/.claude/CLAUDE.md ~/.claude/CLAUDE.md.bak
fi
cp "$SCRIPT_DIR/global/CLAUDE.md" ~/.claude/CLAUDE.md
echo -e "${GREEN} ✓ Global rules installed${NC}"
fi
# ─── Install status line (optional) ───
GLOBAL_SETTINGS="$HOME/.claude/settings.json"
HAS_STATUSLINE=false
if [ -f "$GLOBAL_SETTINGS" ] && command -v jq &>/dev/null; then
if jq -e '.statusLine' "$GLOBAL_SETTINGS" &>/dev/null; then
HAS_STATUSLINE=true
fi
elif [ -f "$GLOBAL_SETTINGS" ] && grep -q '"statusLine"' "$GLOBAL_SETTINGS" 2>/dev/null; then
HAS_STATUSLINE=true
fi
if [ "$HAS_STATUSLINE" = false ]; then
if [ "$ARG_AUTO" = true ]; then
# Auto mode: skip statusline (don't install without asking)
true
else
echo ""
echo -e "${BLUE}Install ccstatusline? (shows model, context %, git branch in terminal)${NC}"
echo " This adds a status line to all Claude Code sessions via ~/.claude/settings.json"
read -p "Install status line? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
mkdir -p ~/.claude
if [ -f "$GLOBAL_SETTINGS" ]; then
# Merge statusLine into existing settings
if command -v jq &>/dev/null; then
EXISTING=$(cat "$GLOBAL_SETTINGS")
echo "$EXISTING" | jq '. + {"statusLine": {"type": "command", "command": "npx -y ccstatusline@latest", "padding": 0}}' > "$GLOBAL_SETTINGS"
else
# No jq — warn user to add manually
echo -e "${YELLOW} jq not found — please add statusLine to ~/.claude/settings.json manually:${NC}"
echo ' "statusLine": {"type": "command", "command": "npx -y ccstatusline@latest", "padding": 0}'
fi
else
# No existing settings — create new
cat > "$GLOBAL_SETTINGS" <<'SEOF'
{
"statusLine": {
"type": "command",
"command": "npx -y ccstatusline@latest",
"padding": 0
}
}
SEOF
fi
echo -e "${GREEN} ✓ Status line installed (run 'npx -y ccstatusline@latest' once to configure widgets)${NC}"
fi
fi
fi
# ─── Write version marker ───
echo "$VERSION" > .claude/.cc-discipline-version
# ─── Summary ───
echo ""
echo -e "${CYAN}╔══════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ Setup complete! ║${NC}"
echo -e "${CYAN}╚══════════════════════════════════════════════╝${NC}"
echo ""
if [ "$INSTALL_MODE" = "fresh" ]; then
echo -e "Created files:"
echo -e " ${GREEN}CLAUDE.md${NC} ← Project rules (fill in [TODO] sections)"
echo -e " ${GREEN}.claude/rules/${NC} ← Auto-injected rules"
echo -e " ${GREEN}.claude/hooks/${NC} ← 7 discipline hooks (edit guard, streak breaker, git guard, phase gate, action counter, error remind, session start)"
echo -e " ${GREEN}.claude/agents/${NC} ← Reviewer & investigator subagents"
echo -e " ${GREEN}.claude/skills/commit/${NC} ← /commit smart commit"
echo -e " ${GREEN}.claude/skills/self-check/${NC} ← /self-check periodic discipline check"
echo -e " ${GREEN}.claude/skills/evaluate/${NC} ← /evaluate assess external review advice"
echo -e " ${GREEN}.claude/skills/think/${NC} ← /think stop and think before coding"
echo -e " ${GREEN}.claude/skills/retro/${NC} ← /retro post-task retrospective"
echo -e " ${GREEN}.claude/skills/summary/${NC} ← /summary before compacting"
echo -e " ${GREEN}.claude/skills/investigate/${NC} ← /investigate multi-agent cross-investigation"
echo -e " ${GREEN}.claude/settings.json${NC} ← Hooks configuration"
echo -e " ${GREEN}docs/progress.md${NC} ← Progress log (maintained by Claude)"
echo -e " ${GREEN}docs/debug-log.md${NC} ← Debug log (maintained by Claude)"
echo -e " ${GREEN}.claude/memory/${NC} ← Auto memory (symlinked, lives in repo)"
echo ""
echo -e "${YELLOW}Next steps:${NC}"
echo " 1. Edit CLAUDE.md and fill in the [TODO] sections with project info"
echo " 2. Review rules in .claude/rules/stacks/ and adjust as needed"
echo " 3. For complex tasks, use: /loop 10m /self-check"
echo " 4. Start working with Claude Code!"
else
echo -e "What was done:"
echo -e " ${GREEN}.claude/rules/${NC} ← Discipline rules installed/updated"
echo -e " ${GREEN}.claude/hooks/${NC} ← Hook scripts installed/updated"
echo -e " ${GREEN}.claude/agents/${NC} ← Subagents installed/updated"
echo -e " ${GREEN}.claude/skills/commit/${NC} ← /commit skill installed/updated"
echo -e " ${GREEN}.claude/skills/self-check/${NC} ← /self-check discipline check installed"
echo -e " ${GREEN}.claude/skills/evaluate/${NC} ← /evaluate external review assessment"
echo -e " ${GREEN}.claude/skills/think/${NC} ← /think stop and think before coding"
echo -e " ${GREEN}.claude/skills/retro/${NC} ← /retro post-task retrospective"
echo -e " ${GREEN}.claude/skills/summary/${NC} ← /summary before compacting"
echo -e " ${GREEN}.claude/skills/investigate/${NC} ← /investigate multi-agent cross-investigation"
if [ ! -f "$BACKUP_DIR/settings.json" ] || [ -f ".claude/.cc-discipline-settings-template.json" ]; then
echo -e " ${YELLOW}.claude/settings.json${NC} ← See notes above"
else
echo -e " ${GREEN}.claude/settings.json${NC} ← Hooks merged"
fi
echo ""
echo -e "What was ${GREEN}NOT${NC} touched:"
echo -e " CLAUDE.md ← Your project info is safe"
echo -e " docs/progress.md ← Your progress records are safe"
echo -e " docs/debug-log.md ← Your debug logs are safe"
echo -e " Your custom rules/agents ← Untouched (we only add our files)"
echo ""
echo -e "${YELLOW}Backup:${NC} ${BACKUP_DIR}/"
echo " Consider adding .claude/.backup-* to .gitignore"
echo ""
if [ -f ".claude/.cc-discipline-claude-md-template" ]; then
echo -e "${YELLOW}Next steps:${NC}"
echo " 1. Review .claude/.cc-discipline-claude-md-template"
echo " Consider adding the docs/ structure and discipline sections to your CLAUDE.md"
echo " 2. Review rules in .claude/rules/stacks/ and adjust as needed"
echo " 3. Start working with Claude Code!"
else
echo -e "${YELLOW}Next steps:${NC}"
echo " 1. Review rules in .claude/rules/stacks/ and adjust as needed"
echo " 2. Start working with Claude Code!"
fi
fi
echo ""
echo -e "${YELLOW}Tip:${NC} Commit .claude/ and CLAUDE.md to git to share discipline across the team"