claude-dev-kit 2.1.3 → 2.1.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-kit",
3
- "version": "2.1.3",
3
+ "version": "2.1.5",
4
4
  "description": "Transform Claude Code into a fully autonomous development team — orchestrated sub-agents for planning, implementation, testing, and review.",
5
5
  "bin": {
6
6
  "claude-dev-kit": "./bin/claude-dev-kit.js"
@@ -15,7 +15,9 @@
15
15
  ".claude/skills/",
16
16
  ".claude/templates/",
17
17
  "CLAUDE.local.md.example",
18
- "scripts/install.sh"
18
+ "scripts/install.sh",
19
+ "scripts/migrate.sh",
20
+ "scripts/lib/"
19
21
  ],
20
22
  "keywords": [
21
23
  "claude",
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+ // scripts/lib/merge-settings.js
3
+ // Merges CDK's settings.json into a user's existing settings.json.
4
+ //
5
+ // Usage: node merge-settings.js <user-settings-path> <cdk-settings-path>
6
+ // Output: merged JSON on stdout
7
+ //
8
+ // Merge rules:
9
+ // permissions.allow — union (add CDK entries not already present)
10
+ // permissions.deny — union (add CDK entries not already present)
11
+ // hooks — per-event: add CDK hook commands not already present
12
+ // (dedup key: command string, trimmed)
13
+ // All other user settings are preserved unchanged.
14
+
15
+ 'use strict';
16
+
17
+ const fs = require('fs');
18
+
19
+ const [, , userPath, cdkPath] = process.argv;
20
+
21
+ if (!userPath || !cdkPath) {
22
+ process.stderr.write('Usage: node merge-settings.js <user-settings-path> <cdk-settings-path>\n');
23
+ process.exit(1);
24
+ }
25
+
26
+ let user, cdk;
27
+ try {
28
+ user = JSON.parse(fs.readFileSync(userPath, 'utf8'));
29
+ } catch (e) {
30
+ process.stderr.write(`Failed to parse user settings at ${userPath}: ${e.message}\n`);
31
+ process.exit(1);
32
+ }
33
+ try {
34
+ cdk = JSON.parse(fs.readFileSync(cdkPath, 'utf8'));
35
+ } catch (e) {
36
+ process.stderr.write(`Failed to parse CDK settings at ${cdkPath}: ${e.message}\n`);
37
+ process.exit(1);
38
+ }
39
+
40
+ // Deep clone user settings as the base
41
+ const result = JSON.parse(JSON.stringify(user));
42
+
43
+ // Strip CDK internal-only comment keys if they leaked in
44
+ delete result._comment;
45
+ delete result._hooks_note;
46
+
47
+ // ── Merge permissions.allow ──────────────────────────────────────────────────
48
+ result.permissions = result.permissions ?? {};
49
+ const userAllow = new Set(result.permissions.allow ?? []);
50
+ for (const entry of (cdk.permissions?.allow ?? [])) {
51
+ userAllow.add(entry);
52
+ }
53
+ result.permissions.allow = [...userAllow];
54
+
55
+ // ── Merge permissions.deny ───────────────────────────────────────────────────
56
+ const userDeny = new Set(result.permissions.deny ?? []);
57
+ for (const entry of (cdk.permissions?.deny ?? [])) {
58
+ userDeny.add(entry);
59
+ }
60
+ result.permissions.deny = [...userDeny];
61
+
62
+ // ── Merge hooks ──────────────────────────────────────────────────────────────
63
+ result.hooks = result.hooks ?? {};
64
+
65
+ for (const [event, cdkGroups] of Object.entries(cdk.hooks ?? {})) {
66
+ result.hooks[event] = result.hooks[event] ?? [];
67
+
68
+ // Collect all command strings already present for this event
69
+ const existingCommands = new Set();
70
+ for (const group of result.hooks[event]) {
71
+ for (const h of (group.hooks ?? [])) {
72
+ if (h.command) existingCommands.add(h.command.trim());
73
+ }
74
+ }
75
+
76
+ // For each CDK hook group, keep only commands not already present
77
+ for (const cdkGroup of cdkGroups) {
78
+ const newHooks = (cdkGroup.hooks ?? []).filter(
79
+ (h) => h.command && !existingCommands.has(h.command.trim())
80
+ );
81
+ if (newHooks.length > 0) {
82
+ result.hooks[event].push({ ...cdkGroup, hooks: newHooks });
83
+ }
84
+ }
85
+ }
86
+
87
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
@@ -0,0 +1,419 @@
1
+ #!/usr/bin/env bash
2
+ # scripts/migrate.sh — Claude Dev Kit Migration Tool
3
+ #
4
+ # Intelligently merges CDK into an existing .claude/ setup without data loss.
5
+ # Detects NEW / UNCHANGED / MODIFIED files, asks about conflicts, and
6
+ # performs smart merges of settings.json and CLAUDE.md.
7
+ #
8
+ # Usage (called by install.sh):
9
+ # bash scripts/migrate.sh <kit-root> <target-dir>
10
+ #
11
+ # Safe to run standalone. Exit codes: 0 = success, 1 = aborted/error.
12
+
13
+ set -euo pipefail
14
+
15
+ # ─── Args ─────────────────────────────────────────────────────────────────────
16
+ KIT_ROOT="${1:-}"
17
+ TARGET="${2:-}"
18
+
19
+ if [[ -z "$KIT_ROOT" || -z "$TARGET" ]]; then
20
+ echo "Usage: bash scripts/migrate.sh <kit-root> <target-dir>" >&2
21
+ exit 1
22
+ fi
23
+
24
+ CDK_CLAUDE="$KIT_ROOT/.claude"
25
+ TGT_CLAUDE="$TARGET/.claude"
26
+ MANIFEST="$TGT_CLAUDE/.cdk-manifest"
27
+
28
+ # ─── Colors ───────────────────────────────────────────────────────────────────
29
+ BOLD='\033[1m'
30
+ DIM='\033[2m'
31
+ GREEN='\033[0;32m'
32
+ YELLOW='\033[1;33m'
33
+ CYAN='\033[0;36m'
34
+ RED='\033[0;31m'
35
+ BLUE='\033[0;34m'
36
+ NC='\033[0m'
37
+
38
+ info() { echo -e "${CYAN} →${NC} $*"; }
39
+ success() { echo -e "${GREEN} ✓${NC} $*"; }
40
+ warn() { echo -e "${YELLOW} ⚠${NC} $*"; }
41
+ error() { echo -e "${RED} ✗${NC} $*" >&2; }
42
+ header() { echo -e "\n${BOLD}$*${NC}"; }
43
+ dim() { echo -e "${DIM}$*${NC}"; }
44
+
45
+ CI_MODE="${CI:-false}"
46
+
47
+ # ─── Section 1: Load existing manifest ────────────────────────────────────────
48
+ declare -A manifest_set
49
+ IS_FIRST_INSTALL=false
50
+
51
+ mkdir -p "$TGT_CLAUDE"
52
+
53
+ if [[ -f "$MANIFEST" ]]; then
54
+ while IFS= read -r line; do
55
+ [[ "$line" =~ ^# ]] && continue
56
+ [[ -z "$line" ]] && continue
57
+ manifest_set["$line"]=1
58
+ done < "$MANIFEST"
59
+ else
60
+ IS_FIRST_INSTALL=true
61
+ fi
62
+
63
+ # ─── Section 2: Enumerate CDK files ───────────────────────────────────────────
64
+ declare -a cdk_files=()
65
+
66
+ while IFS= read -r -d '' f; do
67
+ rel="${f#"$CDK_CLAUDE"/}"
68
+ # Skip files that have their own special merge logic or should never be copied
69
+ [[ "$rel" == "settings.json" ]] && continue
70
+ [[ "$rel" == "settings.json.example" ]] && continue
71
+ [[ "$rel" == ".cdk-manifest" ]] && continue
72
+ [[ "$rel" == *"node_modules"* ]] && continue
73
+ [[ "$rel" == *.jsonl ]] && continue
74
+ [[ "$rel" == "learning/"* ]] && continue
75
+ cdk_files+=("$rel")
76
+ done < <(find "$CDK_CLAUDE" -type f -print0 2>/dev/null | sort -z)
77
+
78
+ # ─── Section 3: Categorize files ──────────────────────────────────────────────
79
+ declare -a cat_new=()
80
+ declare -a cat_unchanged=()
81
+ declare -a cat_modified=()
82
+
83
+ for rel in "${cdk_files[@]}"; do
84
+ src="$CDK_CLAUDE/$rel"
85
+ dst="$TGT_CLAUDE/$rel"
86
+
87
+ if [[ ! -f "$dst" ]]; then
88
+ cat_new+=("$rel")
89
+ elif cmp -s "$src" "$dst"; then
90
+ cat_unchanged+=("$rel")
91
+ else
92
+ # File exists in both and content differs — it was modified
93
+ cat_modified+=("$rel")
94
+ fi
95
+ done
96
+
97
+ # Count user-only files (in target .claude but not in CDK source)
98
+ user_only_count=0
99
+ if [[ -d "$TGT_CLAUDE" ]]; then
100
+ while IFS= read -r -d '' f; do
101
+ rel="${f#"$TGT_CLAUDE"/}"
102
+ [[ "$rel" == ".cdk-manifest" ]] && continue
103
+ # Check if this file is in the CDK source
104
+ if [[ ! -f "$CDK_CLAUDE/$rel" ]]; then
105
+ (( user_only_count++ )) || true
106
+ fi
107
+ done < <(find "$TGT_CLAUDE" -type f -print0 2>/dev/null)
108
+ fi
109
+
110
+ # ─── Section 4: Print migration report ────────────────────────────────────────
111
+ echo ""
112
+ echo -e "${BOLD}╔═══════════════════════════════════════════╗${NC}"
113
+ echo -e "${BOLD}║ Claude Dev Kit — Migration Tool ║${NC}"
114
+ echo -e "${BOLD}╚═══════════════════════════════════════════╝${NC}"
115
+ echo ""
116
+ echo -e " ${DIM}Source: $KIT_ROOT${NC}"
117
+ echo -e " ${DIM}Target: $TARGET${NC}"
118
+ echo ""
119
+
120
+ if [[ "$IS_FIRST_INSTALL" == "true" ]] && [[ ! -d "$TARGET/.claude" || -z "$(ls -A "$TGT_CLAUDE" 2>/dev/null)" ]]; then
121
+ info "No existing .claude/ found — performing fresh install"
122
+ else
123
+ header "Migration Report"
124
+ echo ""
125
+
126
+ echo -e " ${GREEN}${BOLD}NEW${NC} ${#cat_new[@]} file(s) — will be added"
127
+ if [[ ${#cat_new[@]} -gt 0 && ${#cat_new[@]} -le 10 ]]; then
128
+ for f in "${cat_new[@]}"; do echo -e " ${DIM}$f${NC}"; done
129
+ elif [[ ${#cat_new[@]} -gt 10 ]]; then
130
+ for f in "${cat_new[@]:0:5}"; do echo -e " ${DIM}$f${NC}"; done
131
+ echo -e " ${DIM}... and $((${#cat_new[@]} - 5)) more${NC}"
132
+ fi
133
+ echo ""
134
+
135
+ echo -e " ${CYAN}${BOLD}UNCHANGED${NC} ${#cat_unchanged[@]} file(s) — CDK version matches yours, will refresh"
136
+ echo ""
137
+
138
+ if [[ ${#cat_modified[@]} -gt 0 ]]; then
139
+ echo -e " ${YELLOW}${BOLD}MODIFIED${NC} ${#cat_modified[@]} file(s) — differ from CDK, ${BOLD}requires your input${NC}"
140
+ for f in "${cat_modified[@]}"; do echo -e " ${YELLOW}$f${NC}"; done
141
+ echo ""
142
+ fi
143
+
144
+ if [[ $user_only_count -gt 0 ]]; then
145
+ echo -e " ${BLUE}${BOLD}YOURS${NC} $user_only_count file(s) — only in your .claude/, CDK will never touch"
146
+ echo ""
147
+ fi
148
+
149
+ echo -e " ${BOLD}SPECIAL MERGES${NC}"
150
+ if [[ -f "$TGT_CLAUDE/settings.json" ]]; then
151
+ echo -e " ${DIM}settings.json${NC} — will merge hooks + permissions (yours preserved)"
152
+ else
153
+ echo -e " ${DIM}settings.json${NC} — will be created from CDK template"
154
+ fi
155
+ if [[ -f "$TARGET/CLAUDE.md" ]]; then
156
+ if grep -qF "CDK:GENERATED:START" "$TARGET/CLAUDE.md" 2>/dev/null; then
157
+ echo -e " ${DIM}CLAUDE.md${NC} — will update CDK block (your custom content preserved)"
158
+ else
159
+ echo -e " ${DIM}CLAUDE.md${NC} — will append CDK block (your existing content preserved)"
160
+ fi
161
+ else
162
+ echo -e " ${DIM}CLAUDE.md${NC} — will be created from template"
163
+ fi
164
+ echo ""
165
+ fi
166
+
167
+ # ─── Section 5: Confirm before proceeding ─────────────────────────────────────
168
+ if [[ "$CI_MODE" != "true" && ${#cat_modified[@]} -eq 0 ]]; then
169
+ echo -ne " Proceed with migration? ${DIM}[Y/n]${NC}: "
170
+ read -r _confirm </dev/tty
171
+ if [[ "$_confirm" =~ ^[Nn]$ ]]; then
172
+ echo " Aborted."
173
+ exit 1
174
+ fi
175
+ fi
176
+
177
+ # ─── Section 6: Resolve MODIFIED files interactively ──────────────────────────
178
+ declare -A resolutions
179
+
180
+ if [[ ${#cat_modified[@]} -gt 0 ]]; then
181
+ if [[ "$CI_MODE" == "true" ]]; then
182
+ warn "CI mode — all ${#cat_modified[@]} modified file(s) will be preserved (keeping your versions)"
183
+ warn "To update them: run scripts/migrate.sh manually and choose [u] for each"
184
+ for rel in "${cat_modified[@]}"; do
185
+ resolutions["$rel"]="keep"
186
+ done
187
+ else
188
+ header "Resolving modified files"
189
+ echo -e " ${DIM}For each file that differs from CDK, choose what to do.${NC}"
190
+ echo ""
191
+
192
+ for rel in "${cat_modified[@]}"; do
193
+ src="$CDK_CLAUDE/$rel"
194
+ dst="$TGT_CLAUDE/$rel"
195
+
196
+ echo -e " ${YELLOW}${BOLD}MODIFIED:${NC} $rel"
197
+
198
+ while true; do
199
+ echo -ne " [k]eep mine [u]se CDK version [d]iff [s]kip: "
200
+ read -r -n1 choice </dev/tty
201
+ echo ""
202
+
203
+ case "$choice" in
204
+ k|K)
205
+ resolutions["$rel"]="keep"
206
+ info "Keeping your version"
207
+ break
208
+ ;;
209
+ u|U)
210
+ resolutions["$rel"]="cdk"
211
+ info "Will use CDK version"
212
+ break
213
+ ;;
214
+ d|D)
215
+ echo ""
216
+ if command -v diff &>/dev/null; then
217
+ diff --color=always -u "$dst" "$src" 2>/dev/null | head -80 || true
218
+ else
219
+ warn "diff not available — cannot show comparison"
220
+ fi
221
+ echo ""
222
+ ;;
223
+ s|S)
224
+ resolutions["$rel"]="keep"
225
+ info "Skipped (keeping yours)"
226
+ break
227
+ ;;
228
+ *)
229
+ echo -ne " Invalid choice. "
230
+ ;;
231
+ esac
232
+ done
233
+ echo ""
234
+ done
235
+ fi
236
+ fi
237
+
238
+ # ─── Section 7: Apply regular file changes ────────────────────────────────────
239
+ header "Applying changes"
240
+ echo ""
241
+
242
+ new_count=0
243
+ unchanged_count=0
244
+ modified_applied=0
245
+ modified_kept=0
246
+
247
+ # Apply NEW files
248
+ for rel in "${cat_new[@]}"; do
249
+ src="$CDK_CLAUDE/$rel"
250
+ dst="$TGT_CLAUDE/$rel"
251
+ mkdir -p "$(dirname "$dst")"
252
+ cp "$src" "$dst"
253
+ (( new_count++ )) || true
254
+ done
255
+
256
+ # Apply UNCHANGED files (refresh to latest CDK version — they're identical so this is safe)
257
+ for rel in "${cat_unchanged[@]}"; do
258
+ src="$CDK_CLAUDE/$rel"
259
+ dst="$TGT_CLAUDE/$rel"
260
+ mkdir -p "$(dirname "$dst")"
261
+ cp "$src" "$dst"
262
+ (( unchanged_count++ )) || true
263
+ done
264
+
265
+ # Apply MODIFIED based on user decisions
266
+ for rel in "${cat_modified[@]}"; do
267
+ case "${resolutions[$rel]:-keep}" in
268
+ cdk)
269
+ mkdir -p "$(dirname "$TGT_CLAUDE/$rel")"
270
+ cp "$CDK_CLAUDE/$rel" "$TGT_CLAUDE/$rel"
271
+ (( modified_applied++ )) || true
272
+ ;;
273
+ keep)
274
+ (( modified_kept++ )) || true
275
+ ;;
276
+ esac
277
+ done
278
+
279
+ [[ $new_count -gt 0 ]] && success "$new_count new file(s) added"
280
+ [[ $unchanged_count -gt 0 ]] && success "$unchanged_count unchanged file(s) refreshed"
281
+ [[ $modified_applied -gt 0 ]] && success "$modified_applied modified file(s) updated to CDK version"
282
+ [[ $modified_kept -gt 0 ]] && info "$modified_kept modified file(s) kept as your version"
283
+
284
+ # ─── Section 8: Merge settings.json ───────────────────────────────────────────
285
+ merge_settings() {
286
+ local tgt_settings="$TGT_CLAUDE/settings.json"
287
+ local cdk_settings="$CDK_CLAUDE/settings.json"
288
+ local merge_script="$KIT_ROOT/scripts/lib/merge-settings.js"
289
+
290
+ if [[ ! -f "$cdk_settings" ]]; then
291
+ warn "settings.json: CDK source not found — skipping"
292
+ return
293
+ fi
294
+
295
+ if [[ ! -f "$tgt_settings" ]]; then
296
+ # No existing settings — install CDK's (strip internal comment keys)
297
+ node -e "
298
+ const s = JSON.parse(require('fs').readFileSync('$cdk_settings', 'utf8'));
299
+ delete s._comment; delete s._hooks_note;
300
+ process.stdout.write(JSON.stringify(s, null, 2) + '\n');
301
+ " > "$tgt_settings"
302
+ success "settings.json: created from CDK template"
303
+ return
304
+ fi
305
+
306
+ local tmp
307
+ tmp=$(mktemp /tmp/cdk-settings-XXXXXX.json)
308
+ # shellcheck disable=SC2064
309
+ trap "rm -f '$tmp'" RETURN
310
+
311
+ if node "$merge_script" "$tgt_settings" "$cdk_settings" > "$tmp" 2>&1; then
312
+ # Validate output is parseable JSON before overwriting
313
+ if node -e "JSON.parse(require('fs').readFileSync('$tmp', 'utf8'))" 2>/dev/null; then
314
+ cp "$tmp" "$tgt_settings"
315
+ success "settings.json: merged (permissions + hooks updated)"
316
+ else
317
+ warn "settings.json: merge produced invalid JSON — your original preserved"
318
+ warn "Manual merge reference: $CDK_CLAUDE/settings.json.example"
319
+ fi
320
+ else
321
+ warn "settings.json: merge failed — your original preserved"
322
+ warn "Manual merge reference: $CDK_CLAUDE/settings.json.example"
323
+ fi
324
+ }
325
+
326
+ merge_settings
327
+
328
+ # ─── Section 9: Merge CLAUDE.md ───────────────────────────────────────────────
329
+ merge_claude_md() {
330
+ local tgt_md="$TARGET/CLAUDE.md"
331
+ local cdk_template="$CDK_CLAUDE/templates/claude-md-template.md"
332
+ local START_MARKER="CDK:GENERATED:START"
333
+ local END_MARKER="CDK:GENERATED:END"
334
+
335
+ if [[ ! -f "$cdk_template" ]]; then
336
+ warn "CLAUDE.md: template not found at $cdk_template — skipping"
337
+ return
338
+ fi
339
+
340
+ if [[ ! -f "$tgt_md" ]]; then
341
+ cp "$cdk_template" "$tgt_md"
342
+ success "CLAUDE.md: created from template"
343
+ return
344
+ fi
345
+
346
+ if grep -qF "$START_MARKER" "$tgt_md" 2>/dev/null; then
347
+ # Has CDK markers — replace only the block between markers (inclusive)
348
+ local tmp
349
+ tmp=$(mktemp /tmp/cdk-claude-md-XXXXXX.md)
350
+ # shellcheck disable=SC2064
351
+ trap "rm -f '$tmp'" RETURN
352
+
353
+ # Extract CDK block from template
354
+ local cdk_block
355
+ cdk_block=$(awk "/<!-- ${START_MARKER}/,/<!-- ${END_MARKER} -->/" "$cdk_template")
356
+
357
+ # Build merged file: content before START + cdk block + content after END
358
+ awk -v block="$cdk_block" \
359
+ -v start="$START_MARKER" \
360
+ -v end="$END_MARKER" \
361
+ 'BEGIN { in_block=0; printed=0 }
362
+ index($0, start) > 0 {
363
+ if (!printed) { print block; printed=1 }
364
+ in_block=1; next
365
+ }
366
+ in_block && index($0, end) > 0 { in_block=0; next }
367
+ !in_block { print }
368
+ ' "$tgt_md" > "$tmp"
369
+
370
+ cp "$tmp" "$tgt_md"
371
+ success "CLAUDE.md: CDK block updated (your custom content preserved)"
372
+ else
373
+ # No CDK markers — append the CDK block at the end
374
+ local cdk_block
375
+ cdk_block=$(awk "/<!-- ${START_MARKER}/,/<!-- ${END_MARKER} -->/" "$cdk_template")
376
+ {
377
+ echo ""
378
+ echo "---"
379
+ echo ""
380
+ printf '%s\n' "$cdk_block"
381
+ } >> "$tgt_md"
382
+ success "CLAUDE.md: CDK block appended (your existing content preserved)"
383
+ info "Tip: you can move the appended block anywhere in the file — the markers let future migrations update it in place"
384
+ fi
385
+ }
386
+
387
+ merge_claude_md
388
+
389
+ # ─── Section 10: Write manifest ───────────────────────────────────────────────
390
+ write_manifest() {
391
+ {
392
+ echo "# CDK Manifest — generated by scripts/migrate.sh"
393
+ echo "# Lists all files installed by claude-dev-kit."
394
+ echo "# Do not edit manually. Used to distinguish CDK-owned from user-created files."
395
+ echo "#"
396
+ for rel in "${cdk_files[@]}"; do
397
+ echo "$rel"
398
+ done
399
+ } > "$MANIFEST"
400
+ success "Manifest written (.claude/.cdk-manifest — tracks CDK-owned files for future migrations)"
401
+ }
402
+
403
+ write_manifest
404
+
405
+ # ─── Done ─────────────────────────────────────────────────────────────────────
406
+ echo ""
407
+ success "Migration complete"
408
+ echo ""
409
+ echo -e " ${DIM}Next steps:${NC}"
410
+ if [[ ! -f "$TARGET/CLAUDE.md" ]] || ! grep -qF "CDK:GENERATED" "$TARGET/CLAUDE.md" 2>/dev/null; then
411
+ echo -e " ${DIM} 1. Run \`/init\` inside Claude Code to configure CLAUDE.md for your stack${NC}"
412
+ echo -e " ${DIM} 2. Copy CLAUDE.local.md.example → CLAUDE.local.md for personal preferences${NC}"
413
+ echo -e " ${DIM} 3. Run \`/primer\` to verify Claude understands the project${NC}"
414
+ else
415
+ echo -e " ${DIM} 1. Copy CLAUDE.local.md.example → CLAUDE.local.md for personal preferences${NC}"
416
+ echo -e " ${DIM} 2. Review any modified files you chose to keep — ensure they're still compatible${NC}"
417
+ echo -e " ${DIM} 3. Run \`/primer\` to verify Claude understands the project${NC}"
418
+ fi
419
+ echo ""