claude-code-starter 0.14.0 → 0.15.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 (2) hide show
  1. package/dist/cli.js +482 -245
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { execSync, spawn } from "child_process";
5
- import fs3 from "fs";
6
- import path3 from "path";
5
+ import fs5 from "fs";
6
+ import path5 from "path";
7
7
  import { fileURLToPath } from "url";
8
8
  import ora from "ora";
9
9
  import pc from "picocolors";
@@ -511,38 +511,12 @@ function countSourceFiles(rootDir, _languages) {
511
511
  countFiles(rootDir);
512
512
  return count;
513
513
  }
514
- function summarizeTechStack(stack) {
515
- const parts = [];
516
- if (stack.primaryLanguage) {
517
- parts.push(`Language: ${stack.primaryLanguage}`);
518
- }
519
- if (stack.primaryFramework) {
520
- parts.push(`Framework: ${stack.primaryFramework}`);
521
- }
522
- if (stack.packageManager) {
523
- parts.push(`Package Manager: ${stack.packageManager}`);
524
- }
525
- if (stack.testingFramework) {
526
- parts.push(`Testing: ${stack.testingFramework}`);
527
- }
528
- if (stack.isMonorepo) {
529
- parts.push("Monorepo: yes");
530
- }
531
- return parts.join(" | ");
532
- }
533
514
 
534
515
  // src/generator.ts
535
516
  import fs2 from "fs";
536
517
  import path2 from "path";
537
518
  function ensureDirectories(rootDir) {
538
- const dirs = [
539
- ".claude",
540
- ".claude/skills",
541
- ".claude/agents",
542
- ".claude/rules",
543
- ".claude/commands",
544
- ".claude/state"
545
- ];
519
+ const dirs = [".claude", ".claude/skills", ".claude/agents", ".claude/rules", ".claude/commands"];
546
520
  for (const dir of dirs) {
547
521
  fs2.mkdirSync(path2.join(rootDir, dir), { recursive: true });
548
522
  }
@@ -625,6 +599,239 @@ function writeSettings(rootDir, stack) {
625
599
  fs2.writeFileSync(fullPath, content);
626
600
  }
627
601
 
602
+ // src/hooks.ts
603
+ import fs3 from "fs";
604
+ import path3 from "path";
605
+ var HOOK_SCRIPT = String.raw`#!/usr/bin/env node
606
+ /**
607
+ * Block Dangerous Commands - PreToolUse Hook for Bash
608
+ * Blocks dangerous patterns before execution.
609
+ *
610
+ * SAFETY_LEVEL: 'critical' | 'high' | 'strict'
611
+ * critical - Only catastrophic: rm -rf ~, dd to disk, fork bombs
612
+ * high - + risky: force push main, secrets exposure, git reset --hard
613
+ * strict - + cautionary: any force push, sudo rm, docker prune
614
+ */
615
+
616
+ const fs = require('fs');
617
+ const path = require('path');
618
+
619
+ const SAFETY_LEVEL = 'high';
620
+
621
+ const PATTERNS = [
622
+ // CRITICAL — Catastrophic, unrecoverable
623
+
624
+ // Filesystem destruction
625
+ { level: 'critical', id: 'rm-home', regex: /\brm\s+(-.+\s+)*["']?~\/?["']?(\s|$|[;&|])/, reason: 'rm targeting home directory' },
626
+ { level: 'critical', id: 'rm-home-var', regex: /\brm\s+(-.+\s+)*["']?\$HOME["']?(\s|$|[;&|])/, reason: 'rm targeting $HOME' },
627
+ { level: 'critical', id: 'rm-home-trailing', regex: /\brm\s+.+\s+["']?(~\/?|\$HOME)["']?(\s*$|[;&|])/, reason: 'rm with trailing ~/ or $HOME' },
628
+ { level: 'critical', id: 'rm-root', regex: /\brm\s+(-.+\s+)*\/(\*|\s|$|[;&|])/, reason: 'rm targeting root filesystem' },
629
+ { level: 'critical', id: 'rm-system', regex: /\brm\s+(-.+\s+)*\/(etc|usr|var|bin|sbin|lib|boot|dev|proc|sys)(\/|\s|$)/, reason: 'rm targeting system directory' },
630
+ { level: 'critical', id: 'rm-cwd', regex: /\brm\s+(-.+\s+)*(\.\/?|\*|\.\/\*)(\s|$|[;&|])/, reason: 'rm deleting current directory contents' },
631
+
632
+ // Disk operations
633
+ { level: 'critical', id: 'dd-disk', regex: /\bdd\b.+of=\/dev\/(sd[a-z]|nvme|hd[a-z]|vd[a-z]|xvd[a-z])/, reason: 'dd writing to disk device' },
634
+ { level: 'critical', id: 'mkfs', regex: /\bmkfs(\.\w+)?\s+\/dev\/(sd[a-z]|nvme|hd[a-z]|vd[a-z])/, reason: 'mkfs formatting disk' },
635
+ { level: 'critical', id: 'fdisk', regex: /\b(fdisk|wipefs|parted)\s+\/dev\//, reason: 'disk partitioning/wiping operation' },
636
+
637
+ // Shell exploits
638
+ { level: 'critical', id: 'fork-bomb', regex: /:\(\)\s*\{.*:\s*\|\s*:.*&/, reason: 'fork bomb detected' },
639
+
640
+ // Git — history destruction
641
+ { level: 'critical', id: 'git-filter', regex: /\bgit\s+(filter-branch|filter-repo)\b/, reason: 'git history rewriting blocked' },
642
+ { level: 'critical', id: 'git-reflog-exp', regex: /\bgit\s+(reflog\s+expire|gc\s+--prune|prune)\b/, reason: 'removes git recovery safety net' },
643
+
644
+ // HIGH — Significant risk, data loss, security exposure
645
+
646
+ // Remote code execution
647
+ { level: 'high', id: 'curl-pipe-sh', regex: /\b(curl|wget)\b.+\|\s*(ba)?sh\b/, reason: 'piping URL to shell (RCE risk)' },
648
+
649
+ // Git — destructive operations
650
+ { level: 'high', id: 'git-force-main', regex: /\bgit\s+push\b(?!.+--force-with-lease).+(--force|-f)\b.+\b(main|master)\b/, reason: 'force push to main/master' },
651
+ { level: 'high', id: 'git-reset-hard', regex: /\bgit\s+reset\s+--hard/, reason: 'git reset --hard loses uncommitted work' },
652
+ { level: 'high', id: 'git-clean-f', regex: /\bgit\s+clean\s+(-\w*f|-f)/, reason: 'git clean -f deletes untracked files' },
653
+ { level: 'high', id: 'git-no-verify', regex: /\bgit\b.+--no-verify/, reason: '--no-verify skips safety hooks' },
654
+ { level: 'high', id: 'git-stash-destruct', regex: /\bgit\s+stash\s+(drop|clear|pop)\b/, reason: 'destructive git stash operation' },
655
+ { level: 'high', id: 'git-branch-D', regex: /\bgit\s+branch\s+(-D|--delete\s+--force)\b/, reason: 'git branch -D force-deletes branch' },
656
+ { level: 'high', id: 'git-checkout-force', regex: /\bgit\s+checkout\s+(-f|--\s+\.)/, reason: 'git checkout -f/-- . discards changes' },
657
+ { level: 'high', id: 'git-restore-destruct', regex: /\bgit\s+restore\s+(--staged\s+--worktree|\.)/, reason: 'git restore discards changes' },
658
+ { level: 'high', id: 'git-update-ref', regex: /\bgit\s+(update-ref|symbolic-ref|replace)\b/, reason: 'git ref manipulation blocked' },
659
+ { level: 'high', id: 'git-config-global', regex: /\bgit\s+config\s+--(global|system)\b/, reason: 'git global/system config blocked' },
660
+ { level: 'high', id: 'git-tag-delete', regex: /\bgit\s+tag\s+(-d|--delete)\b/, reason: 'git tag deletion blocked' },
661
+
662
+ // Git — write operations (user handles manually)
663
+ { level: 'high', id: 'git-push', regex: /\bgit\s+push\b/, reason: 'git push blocked — user handles manually' },
664
+ { level: 'high', id: 'git-pull', regex: /\bgit\s+pull\b/, reason: 'git pull blocked — user handles manually' },
665
+ { level: 'high', id: 'git-fetch', regex: /\bgit\s+fetch\b/, reason: 'git fetch blocked — user handles manually' },
666
+ { level: 'high', id: 'git-clone', regex: /\bgit\s+clone\b/, reason: 'git clone blocked — user handles manually' },
667
+ { level: 'high', id: 'git-add', regex: /\bgit\s+(add|stage)\b/, reason: 'git add/stage blocked — user handles manually' },
668
+ { level: 'high', id: 'git-commit', regex: /\bgit\s+commit\b/, reason: 'git commit blocked — user handles manually' },
669
+ { level: 'high', id: 'git-merge', regex: /\bgit\s+merge\b/, reason: 'git merge blocked — user handles manually' },
670
+ { level: 'high', id: 'git-rebase', regex: /\bgit\s+rebase\b/, reason: 'git rebase blocked — user handles manually' },
671
+ { level: 'high', id: 'git-reset', regex: /\bgit\s+reset\b/, reason: 'git reset blocked — user handles manually' },
672
+ { level: 'high', id: 'git-remote-mod', regex: /\bgit\s+remote\s+(add|set-url|remove)\b/, reason: 'git remote modification blocked' },
673
+ { level: 'high', id: 'git-submodule', regex: /\bgit\s+submodule\s+(add|update)\b/, reason: 'git submodule operation blocked' },
674
+
675
+ // Credentials & secrets
676
+ { level: 'high', id: 'chmod-777', regex: /\bchmod\b.+\b777\b/, reason: 'chmod 777 is a security risk' },
677
+ { level: 'high', id: 'cat-env', regex: /\b(cat|less|head|tail|more)\s+\.env\b/, reason: 'reading .env file exposes secrets' },
678
+ { level: 'high', id: 'cat-secrets', regex: /\b(cat|less|head|tail|more)\b.+(credentials|secrets?|\.pem|\.key|id_rsa|id_ed25519)/i, reason: 'reading secrets file' },
679
+ { level: 'high', id: 'env-dump', regex: /\b(printenv|^env)\s*([;&|]|$)/, reason: 'env dump may expose secrets' },
680
+ { level: 'high', id: 'echo-secret', regex: /\becho\b.+\$\w*(SECRET|KEY|TOKEN|PASSWORD|API_|PRIVATE)/i, reason: 'echoing secret variable' },
681
+ { level: 'high', id: 'rm-ssh', regex: /\brm\b.+\.ssh\/(id_|authorized_keys|known_hosts)/, reason: 'deleting SSH keys' },
682
+ { level: 'high', id: 'security-keychain', regex: /\bsecurity\s+find-generic-password\b/, reason: 'keychain access blocked' },
683
+ { level: 'high', id: 'gpg-export-secret', regex: /\bgpg\s+--export-secret-keys\b/, reason: 'GPG secret key export blocked' },
684
+ { level: 'high', id: 'history-cmd', regex: /\bhistory\b/, reason: 'history may expose secrets' },
685
+
686
+ // Destructive system commands
687
+ { level: 'high', id: 'elevated-priv', regex: /\b(sudo|doas|pkexec)\b/, reason: 'elevated privilege command blocked' },
688
+ { level: 'high', id: 'su-cmd', regex: /\bsu\b/, reason: 'su (switch user) blocked' },
689
+ { level: 'high', id: 'chmod-R', regex: /\bchmod\s+(-\w*R|-R)/, reason: 'recursive chmod blocked' },
690
+ { level: 'high', id: 'chown-R', regex: /\bchown\s+(-\w*R|-R)/, reason: 'recursive chown blocked' },
691
+ { level: 'high', id: 'kill-all', regex: /\bkill\s+-9\s+-1\b/, reason: 'kill all processes blocked' },
692
+ { level: 'high', id: 'killall', regex: /\b(killall|pkill\s+-9)\b/, reason: 'mass process killing blocked' },
693
+ { level: 'high', id: 'truncate-zero', regex: /\btruncate\s+-s\s*0\b/, reason: 'truncating file to zero blocked' },
694
+ { level: 'high', id: 'empty-file', regex: /\bcat\s+\/dev\/null\s*>/, reason: 'emptying file via /dev/null blocked' },
695
+ { level: 'high', id: 'crontab-r', regex: /\bcrontab\s+-r/, reason: 'removes all cron jobs' },
696
+
697
+ // Docker
698
+ { level: 'high', id: 'docker-vol-rm', regex: /\bdocker\s+volume\s+(rm|prune)/, reason: 'docker volume deletion loses data' },
699
+ { level: 'high', id: 'docker-push', regex: /\bdocker\s+push\b/, reason: 'docker push blocked' },
700
+ { level: 'high', id: 'docker-rm-all', regex: /\bdocker\s+rm\s+-f\b.+\$\(docker\s+ps/, reason: 'docker rm all containers blocked' },
701
+ { level: 'high', id: 'docker-sys-prune-a', regex: /\bdocker\s+system\s+prune\s+-a/, reason: 'docker system prune -a blocked' },
702
+ { level: 'high', id: 'docker-compose-destr', regex: /\bdocker[\s-]compose\s+down\s+(-v|--rmi)/, reason: 'docker-compose destructive down blocked' },
703
+
704
+ // Publishing & deployment
705
+ { level: 'high', id: 'npm-publish', regex: /\bnpm\s+(publish|unpublish|deprecate)\b/, reason: 'npm publishing blocked' },
706
+ { level: 'high', id: 'npm-audit-force', regex: /\bnpm\s+audit\s+fix\s+--force\b/, reason: 'npm audit fix --force can break deps' },
707
+ { level: 'high', id: 'cargo-publish', regex: /\bcargo\s+publish\b/, reason: 'cargo publish blocked' },
708
+ { level: 'high', id: 'pip-twine-upload', regex: /\b(pip|twine)\s+upload\b/, reason: 'Python package upload blocked' },
709
+ { level: 'high', id: 'gem-push', regex: /\bgem\s+push\b/, reason: 'gem push blocked' },
710
+ { level: 'high', id: 'pod-push', regex: /\bpod\s+trunk\s+push\b/, reason: 'pod trunk push blocked' },
711
+ { level: 'high', id: 'vercel-prod', regex: /\bvercel\b.+--prod/, reason: 'vercel production deploy blocked' },
712
+ { level: 'high', id: 'netlify-prod', regex: /\bnetlify\s+deploy\b.+--prod/, reason: 'netlify production deploy blocked' },
713
+ { level: 'high', id: 'fly-deploy', regex: /\bfly\s+deploy\b/, reason: 'fly deploy blocked' },
714
+ { level: 'high', id: 'firebase-deploy', regex: /\bfirebase\s+deploy\b/, reason: 'firebase deploy blocked' },
715
+ { level: 'high', id: 'terraform', regex: /\bterraform\s+(apply|destroy)\b/, reason: 'terraform apply/destroy blocked' },
716
+ { level: 'high', id: 'pulumi-cdktf', regex: /\b(pulumi|cdktf)\s+destroy\b/, reason: 'infrastructure destroy blocked' },
717
+ { level: 'high', id: 'kubectl-mutate', regex: /\bkubectl\s+(apply|delete|drain)\b/, reason: 'kubectl mutating operation blocked' },
718
+ { level: 'high', id: 'kubectl-scale-zero', regex: /\bkubectl\s+scale\b.+--replicas=0/, reason: 'kubectl scale to zero blocked' },
719
+ { level: 'high', id: 'helm-ops', regex: /\bhelm\s+(install|uninstall|upgrade)\b/, reason: 'helm operation blocked' },
720
+ { level: 'high', id: 'heroku', regex: /\bheroku\b/, reason: 'heroku command blocked' },
721
+ { level: 'high', id: 'eb-terminate', regex: /\beb\s+terminate\b/, reason: 'eb terminate blocked' },
722
+ { level: 'high', id: 'serverless-remove', regex: /\bserverless\s+remove\b/, reason: 'serverless remove blocked' },
723
+ { level: 'high', id: 'cap-prod-deploy', regex: /\bcap\s+production\s+deploy\b/, reason: 'production deploy blocked' },
724
+ { level: 'high', id: 'cloud-delete', regex: /\b(aws\s+cloudformation\s+delete-stack|gcloud\s+projects\s+delete|az\s+group\s+delete)\b/, reason: 'cloud resource deletion blocked' },
725
+
726
+ // Network & infrastructure
727
+ { level: 'high', id: 'curl-mutating', regex: /\bcurl\b.+-X\s*(POST|PUT|DELETE|PATCH)\b/, reason: 'mutating HTTP request blocked' },
728
+ { level: 'high', id: 'ssh-remote', regex: /\bssh\s/, reason: 'SSH remote connection blocked' },
729
+ { level: 'high', id: 'scp-remote', regex: /\bscp\s/, reason: 'SCP remote copy blocked' },
730
+ { level: 'high', id: 'rsync-delete', regex: /\brsync\b.+--delete/, reason: 'rsync --delete blocked' },
731
+ { level: 'high', id: 'firewall', regex: /\b(iptables\s+-F|ufw\s+disable)\b/, reason: 'firewall manipulation blocked' },
732
+ { level: 'high', id: 'network-kill', regex: /\bifconfig\s+\w+\s+down\b/, reason: 'network interface down blocked' },
733
+ { level: 'high', id: 'route-delete', regex: /\broute\s+del\s+default\b/, reason: 'default route deletion blocked' },
734
+
735
+ // Database
736
+ { level: 'high', id: 'sql-drop', regex: /\b(DROP\s+(DATABASE|TABLE)|TRUNCATE\s+TABLE)\b/i, reason: 'SQL drop/truncate blocked' },
737
+ { level: 'high', id: 'sql-mass-delete', regex: /\bDELETE\s+FROM\b.+\bWHERE\s+1\s*=\s*1/i, reason: 'SQL mass delete blocked' },
738
+ { level: 'high', id: 'redis-flush', regex: /\bredis-cli\s+(FLUSHALL|FLUSHDB)\b/, reason: 'redis flush blocked' },
739
+ { level: 'high', id: 'orm-reset', regex: /\b(prisma\s+migrate\s+reset|rails\s+db:(drop|reset)|django\s+flush)\b/, reason: 'ORM database reset blocked' },
740
+ { level: 'high', id: 'alembic-downgrade', regex: /\balembic\s+downgrade\s+base\b/, reason: 'alembic downgrade base blocked' },
741
+ { level: 'high', id: 'mongo-drop', regex: /\bmongosh\b.+dropDatabase/, reason: 'MongoDB drop database blocked' },
742
+
743
+ // STRICT — Cautionary, context-dependent
744
+ { level: 'strict', id: 'git-checkout-dot', regex: /\bgit\s+checkout\s+\./, reason: 'git checkout . discards changes' },
745
+ { level: 'strict', id: 'docker-prune', regex: /\bdocker\s+(system|image)\s+prune/, reason: 'docker prune removes images' },
746
+ ];
747
+
748
+ const LEVELS = { critical: 1, high: 2, strict: 3 };
749
+ const EMOJIS = { critical: '\u{1F6A8}', high: '\u26D4', strict: '\u26A0\uFE0F' };
750
+ const LOG_DIR = path.join(process.env.HOME || '/tmp', '.claude', 'hooks-logs');
751
+
752
+ function log(data) {
753
+ try {
754
+ if (!fs.existsSync(LOG_DIR)) fs.mkdirSync(LOG_DIR, { recursive: true });
755
+ const file = path.join(LOG_DIR, new Date().toISOString().slice(0, 10) + '.jsonl');
756
+ fs.appendFileSync(file, JSON.stringify({ ts: new Date().toISOString(), ...data }) + '\n');
757
+ } catch {}
758
+ }
759
+
760
+ function checkCommand(cmd, safetyLevel) {
761
+ safetyLevel = safetyLevel || SAFETY_LEVEL;
762
+ const threshold = LEVELS[safetyLevel] || 2;
763
+ for (const p of PATTERNS) {
764
+ if (LEVELS[p.level] <= threshold && p.regex.test(cmd)) {
765
+ return { blocked: true, pattern: p };
766
+ }
767
+ }
768
+ return { blocked: false, pattern: null };
769
+ }
770
+
771
+ async function main() {
772
+ let input = '';
773
+ for await (const chunk of process.stdin) input += chunk;
774
+
775
+ try {
776
+ const data = JSON.parse(input);
777
+ const { tool_name, tool_input, session_id, cwd, permission_mode } = data;
778
+ if (tool_name !== 'Bash') return console.log('{}');
779
+
780
+ const cmd = tool_input?.command || '';
781
+ const result = checkCommand(cmd);
782
+
783
+ if (result.blocked) {
784
+ const p = result.pattern;
785
+ log({ level: 'BLOCKED', id: p.id, priority: p.level, cmd, session_id, cwd, permission_mode });
786
+ return console.log(JSON.stringify({
787
+ hookSpecificOutput: {
788
+ hookEventName: 'PreToolUse',
789
+ permissionDecision: 'deny',
790
+ permissionDecisionReason: EMOJIS[p.level] + ' [' + p.id + '] ' + p.reason
791
+ }
792
+ }));
793
+ }
794
+ console.log('{}');
795
+ } catch (e) {
796
+ log({ level: 'ERROR', error: e.message });
797
+ console.log('{}');
798
+ }
799
+ }
800
+
801
+ if (require.main === module) {
802
+ main();
803
+ } else {
804
+ module.exports = { PATTERNS, LEVELS, SAFETY_LEVEL, checkCommand };
805
+ }
806
+ `;
807
+ function installHook(rootDir) {
808
+ const hooksDir = path3.join(rootDir, ".claude", "hooks");
809
+ const hookPath = path3.join(hooksDir, "block-dangerous-commands.js");
810
+ const settingsPath = path3.join(rootDir, ".claude", "settings.json");
811
+ fs3.mkdirSync(hooksDir, { recursive: true });
812
+ fs3.writeFileSync(hookPath, HOOK_SCRIPT);
813
+ fs3.chmodSync(hookPath, 493);
814
+ try {
815
+ const existing = fs3.existsSync(settingsPath) ? JSON.parse(fs3.readFileSync(settingsPath, "utf-8")) : {};
816
+ existing.hooks = {
817
+ ...existing.hooks,
818
+ PreToolUse: [
819
+ {
820
+ matcher: "Bash",
821
+ hooks: [
822
+ {
823
+ type: "command",
824
+ command: "node .claude/hooks/block-dangerous-commands.js"
825
+ }
826
+ ]
827
+ }
828
+ ]
829
+ };
830
+ fs3.writeFileSync(settingsPath, JSON.stringify(existing, null, 2));
831
+ } catch {
832
+ }
833
+ }
834
+
628
835
  // src/prompt.ts
629
836
  function getAnalysisPrompt(projectInfo) {
630
837
  const context = buildContextSection(projectInfo);
@@ -658,14 +865,14 @@ ${templateVars}
658
865
 
659
866
  1. Read this entire prompt to understand all phases
660
867
  2. Execute Phase 1 completely - read files, analyze code, gather all data
661
- 3. Execute Phase 2 - generate the CLAUDE.md using only discovered information
868
+ 3. Execute Phase 2 - generate the CLAUDE.md (max 120 lines) using only discovered information
662
869
  4. Execute Phase 3 - verify quality before writing
663
870
  5. Use the Write tool to create \`.claude/CLAUDE.md\` with the final content
664
- 6. Execute Phase 4 - generate ALL skill files
871
+ 6. Execute Phase 4 - generate ALL skill files (4 core + framework-specific if detected)
665
872
  7. Execute Phase 5 - generate agent files
666
873
  8. Execute Phase 6 - generate rule files
667
- 9. Execute Phase 7 - generate command files
668
- 10. Run the Anti-Redundancy Checklist one final time across ALL generated files \u2014 if any convention is restated or any rule lacks a \`paths:\` filter, fix it before proceeding
874
+ 9. Execute Phase 7 - generate command files (2 commands: analyze, code-review)
875
+ 10. Run the Anti-Redundancy Enforcement checks one final time across ALL generated files \u2014 if any convention is restated, any command is duplicated, or any rule lacks a \`paths:\` filter, fix it before proceeding
669
876
  11. Output a brief summary of what was generated and any gaps found
670
877
 
671
878
  Do NOT output file contents to stdout. Write all files to disk using the Write tool.
@@ -830,14 +1037,26 @@ frequencies, so place information where it costs the least while remaining acces
830
1037
  5. **Agents have zero main-context cost.** Put detailed checklists and review criteria in agent files \u2014 they run in subprocesses and don't consume the user's context window.
831
1038
  6. **Each piece of information must live in exactly ONE place.** If it's in CLAUDE.md, don't repeat it in rules, skills, or commands.
832
1039
 
833
- ### Anti-Redundancy Checklist
1040
+ ### Anti-Redundancy Enforcement
834
1041
 
835
- Before writing EACH artifact, verify:
836
- - [ ] No convention from CLAUDE.md is restated (commit format, naming, import order, etc.)
837
- - [ ] No content from another artifact is duplicated
838
- - [ ] Cross-references are used instead of copies (e.g., "Follow conventions in CLAUDE.md")
839
- - [ ] All rules have a \`paths:\` filter
840
- - [ ] Information is placed at the lowest-cost tier that still makes it accessible
1042
+ Before writing EACH artifact, apply these hard constraints:
1043
+
1044
+ - **REJECT** any artifact that restates a convention from CLAUDE.md. If a convention appears in CLAUDE.md, it MUST NOT appear in any other file. Not paraphrased, not summarized, not restated in different words.
1045
+ - **Test commands, lint commands, and build commands** MUST appear in exactly ONE place: CLAUDE.md's Common Commands section. Skills and agents MUST write "See Common Commands in CLAUDE.md" instead.
1046
+ - **All rules MUST have a \`paths:\` filter** \u2014 no unfiltered rules.
1047
+ - **Cross-references replace copies** \u2014 write "Follow conventions in CLAUDE.md" instead of restating any convention.
1048
+
1049
+ #### Forbidden Duplication List
1050
+
1051
+ The following MUST NOT appear in skills, agents, rules, or commands \u2014 they belong exclusively in CLAUDE.md:
1052
+ - Test commands (the literal test runner invocation)
1053
+ - Lint commands (the literal linter invocation)
1054
+ - Build commands (the literal build invocation)
1055
+ - Import convention descriptions (absolute vs relative, ordering, type imports)
1056
+ - Naming convention descriptions (camelCase, PascalCase, file naming)
1057
+ - Commit format descriptions (conventional commits, message format)
1058
+ - Anti-patterns list (things to avoid)
1059
+ - Testing framework syntax examples (describe/it/expect \u2014 belongs in test-writer agent only)
841
1060
 
842
1061
  ---
843
1062
 
@@ -936,9 +1155,13 @@ Read at least 3-5 source files and document the ACTUAL patterns used:
936
1155
  Using ONLY information discovered in Phase 1, generate the \`.claude/CLAUDE.md\` file.
937
1156
  Every section must contain PROJECT-SPECIFIC content. Skip sections that don't apply.
938
1157
 
1158
+ **The CLAUDE.md MUST NOT exceed 120 lines. Prioritize density over completeness.**
1159
+
1160
+ Do NOT include sections that duplicate information available in package.json, tsconfig.json, or other config files the agent can read directly.
1161
+
939
1162
  ### Output Structure
940
1163
 
941
- The CLAUDE.md MUST follow this structure:
1164
+ The CLAUDE.md MUST follow this compact structure:
942
1165
 
943
1166
  \`\`\`markdown
944
1167
  # {Project Name}
@@ -952,17 +1175,9 @@ Written for an AI assistant that needs to understand PURPOSE to make good decisi
952
1175
 
953
1176
  ## Architecture
954
1177
 
955
- {Describe the actual architecture pattern found}
956
-
957
- ### Directory Structure
958
-
959
- \\\`\\\`\\\`
960
- {Actual directory tree, depth 3, with annotations}
961
- \\\`\\\`\\\`
962
-
963
- ### Data Flow
964
-
965
- {How a typical request flows through the system}
1178
+ {1-2 sentences describing the actual architecture pattern found, then the Key Files table.
1179
+ Do NOT include a Directory Structure ASCII tree or Data Flow subsection \u2014 the agent can
1180
+ read the filesystem directly.}
966
1181
 
967
1182
  ### Key Files
968
1183
 
@@ -970,42 +1185,18 @@ Written for an AI assistant that needs to understand PURPOSE to make good decisi
970
1185
  |------|---------|
971
1186
  | \`path/to/file\` | What it does |
972
1187
 
973
- ## Tech Stack
974
-
975
- | Category | Technology | Notes |
976
- |----------|-----------|-------|
977
- | Language | X | Config details |
978
- | Framework | Y | How it's used |
979
-
980
- ## Development Setup
981
-
982
- ### Prerequisites
983
-
984
- {Exact versions and tools needed}
985
-
986
- ### Getting Started
987
-
988
- \\\`\\\`\\\`bash
989
- {Actual commands to get running}
990
- \\\`\\\`\\\`
991
-
992
- ### Environment Variables
993
-
994
- | Variable | Description | Example |
995
- |----------|-------------|---------|
996
- | \`VAR_NAME\` | What it's for | \`example_value\` |
997
-
998
1188
  ## Common Commands
999
1189
 
1000
1190
  \\\`\\\`\\\`bash
1001
- {Actual commands from package.json scripts or equivalent}
1191
+ {5 critical commands max, from package.json scripts or equivalent.
1192
+ Only the commands developers use daily \u2014 not every script.}
1002
1193
  \\\`\\\`\\\`
1003
1194
 
1004
1195
  ## Code Conventions
1005
1196
 
1006
1197
  ### Naming
1007
1198
 
1008
- {ACTUAL naming patterns found}
1199
+ {ACTUAL naming patterns found \u2014 be brief}
1009
1200
 
1010
1201
  ### Patterns to Follow
1011
1202
 
@@ -1018,45 +1209,16 @@ Written for an AI assistant that needs to understand PURPOSE to make good decisi
1018
1209
  > **This Code Conventions section is the single source of truth.**
1019
1210
  > Rules and skills cross-reference this section \u2014 they do not repeat it.
1020
1211
 
1021
- ## Skills
1022
-
1023
- {List each generated skill with a one-line description}
1024
-
1025
- | Skill | Purpose |
1026
- |-------|---------|
1027
- | \`pattern-discovery\` | Discover and document codebase patterns |
1028
- | ... | ... |
1029
-
1030
- ## Agents
1031
-
1032
- {List each generated agent with a one-line description}
1033
-
1034
- | Agent | Purpose |
1035
- |-------|---------|
1036
- | \`code-reviewer\` | Reviews code for quality and security |
1037
- | \`test-writer\` | Generates tests for code |
1038
-
1039
1212
  ## Testing
1040
1213
 
1041
- ### Running Tests
1042
-
1043
- \\\`\\\`\\\`bash
1044
- {actual test commands}
1045
- \\\`\\\`\\\`
1046
-
1047
- ### Writing Tests
1048
-
1049
- {Testing patterns, utilities, fixtures available}
1214
+ {2-3 lines: test command, test file location, key testing pattern.
1215
+ NOT a full guide \u2014 the test-writer agent handles detailed test methodology.}
1050
1216
 
1051
1217
  ## Domain Knowledge
1052
1218
 
1053
1219
  ### Core Entities
1054
1220
 
1055
- {Main domain objects and relationships}
1056
-
1057
- ### Key Workflows
1058
-
1059
- {3-5 most important workflows}
1221
+ {Brief list of main domain objects and relationships}
1060
1222
 
1061
1223
  ## Gotchas & Important Notes
1062
1224
 
@@ -1070,12 +1232,22 @@ Written for an AI assistant that needs to understand PURPOSE to make good decisi
1070
1232
  4. {project-specific rules discovered during analysis}
1071
1233
  \`\`\`
1072
1234
 
1235
+ **Sections NOT to include** (the agent can read these from config files directly):
1236
+ - Directory Structure ASCII tree (agent uses Glob/Read)
1237
+ - Tech Stack table (available in package.json, tsconfig.json, etc.)
1238
+ - Development Setup / Getting Started / Prerequisites
1239
+ - Environment Variables table (available in .env.example)
1240
+ - Skills index table
1241
+ - Agents index table
1242
+ - Key Workflows (duplicates Data Flow / Architecture)
1243
+
1073
1244
  ---
1074
1245
 
1075
1246
  ## Phase 3: Quality Checklist
1076
1247
 
1077
1248
  Before writing the CLAUDE.md, verify:
1078
1249
 
1250
+ - [ ] The file does NOT exceed 120 lines
1079
1251
  - [ ] Every section contains PROJECT-SPECIFIC information (not generic boilerplate)
1080
1252
  - [ ] File paths referenced actually exist in the project
1081
1253
  - [ ] File references use \`path/to/file.ts (functionName)\` format, not line numbers
@@ -1084,12 +1256,13 @@ Before writing the CLAUDE.md, verify:
1084
1256
  - [ ] The "Gotchas" section contains genuinely useful, non-obvious information
1085
1257
  - [ ] An AI reading this CLAUDE.md could add a new feature following existing patterns
1086
1258
  - [ ] Sections without real content have been omitted entirely
1259
+ - [ ] No section duplicates information available in config files the agent can read
1087
1260
 
1088
1261
  ### Cross-Artifact Deduplication Check
1089
1262
 
1090
1263
  Before writing ANY artifact (rule, skill, agent, command), verify:
1091
1264
  - [ ] No conventions from CLAUDE.md are restated (naming, commit format, import order, style)
1092
- - [ ] No commit format description is restated outside CLAUDE.md
1265
+ - [ ] No item from the Forbidden Duplication List appears outside CLAUDE.md
1093
1266
  - [ ] No content from one artifact is duplicated in another
1094
1267
  - [ ] Cross-references are used instead of copies (e.g., "Follow conventions in CLAUDE.md")
1095
1268
  - [ ] Every rule file has a \`paths:\` filter \u2014 no unfiltered rules
@@ -1117,57 +1290,38 @@ var SKILLS_PROMPT = `---
1117
1290
  Write each skill file to \`.claude/skills/\` using the Write tool. Every skill must have
1118
1291
  YAML frontmatter with \`name\`, \`description\`, and optionally \`globs\` for file matching.
1119
1292
 
1120
- **Tailor ALL skills to this specific project** \u2014 use the actual test command, lint command,
1121
- file patterns, and conventions discovered during Phase 1.
1293
+ **Tailor ALL skills to this specific project** \u2014 use the actual file patterns and
1294
+ conventions discovered during Phase 1.
1122
1295
 
1123
1296
  ### Skill Content Rules
1124
1297
 
1125
1298
  1. **Cross-reference, don't copy** \u2014 write "Follow conventions in CLAUDE.md" instead of restating naming, style, or commit conventions. Skills focus on methodology (HOW to do something), not conventions (WHAT the conventions are).
1126
1299
  2. **Use stable references** \u2014 reference code as \`path/to/file.ts (functionName)\`, not line numbers which become stale.
1127
1300
  3. **No convention duplication** \u2014 if CLAUDE.md already documents commit format, import order, or naming rules, the skill must not repeat them.
1301
+ 4. **No command duplication** \u2014 for test, lint, and build commands, write "See Common Commands in CLAUDE.md" instead of repeating the literal command.
1128
1302
 
1129
- ### 4.1 Core Skills (ALWAYS generate all 8)
1130
-
1131
- **\`.claude/skills/pattern-discovery.md\`**
1132
- - Name: pattern-discovery
1133
- - Description: Analyze codebase to discover and document patterns
1134
- - Content: How to search for patterns in THIS project's structure. Include the actual source directories, key file patterns, and import conventions found.
1135
-
1136
- **\`.claude/skills/systematic-debugging.md\`**
1137
- - Name: systematic-debugging
1138
- - Description: 4-phase debugging methodology \u2014 Reproduce, Locate, Diagnose, Fix
1139
- - Content: Tailor reproduction steps to the project's actual test runner and dev server commands. Include how to use the project's logging/debugging setup.
1140
-
1141
- **\`.claude/skills/testing-methodology.md\`**
1142
- - Name: testing-methodology
1143
- - Description: AAA testing pattern with project-specific framework syntax
1144
- - Content: Use the project's actual testing framework syntax. Include real examples of test patterns found in the codebase (describe/it blocks, pytest fixtures, etc.). Reference the actual test command. Include mocking/stubbing patterns specific to the stack.
1303
+ ### 4.1 Core Skills (ALWAYS generate all 4)
1145
1304
 
1146
1305
  **\`.claude/skills/iterative-development.md\`**
1147
1306
  - Name: iterative-development
1148
- - Description: TDD workflow with project-specific test and lint commands
1149
- - Content: The TDD loop using the actual test command and lint command. Include the project's verification steps (typecheck, build, etc.).
1150
-
1151
- **\`.claude/skills/commit-hygiene.md\`**
1152
- - Name: commit-hygiene
1153
- - Description: Atomic commits, conventional format, size thresholds
1154
- - Content: Size thresholds (\xB1300 lines per commit), when-to-commit triggers. For commit format, write "Follow the commit conventions in CLAUDE.md" \u2014 do NOT restate the format here. If the project uses commitlint or similar, reference its config.
1307
+ - Description: TDD workflow with debugging methodology and verification chain
1308
+ - Content: The TDD loop referencing "See Common Commands in CLAUDE.md" for actual commands. Include the project's verification steps (typecheck, build, etc.). Add a Debugging section with: 4-phase methodology (Reproduce, Locate, Diagnose, Fix), project-specific file-to-module mapping for tracing bugs, how to use the project's logging/debugging setup. Add commit guidance: size thresholds (\xB1300 lines per commit), when-to-commit triggers, "Follow commit conventions in CLAUDE.md" for format.
1155
1309
 
1156
1310
  **\`.claude/skills/code-deduplication.md\`**
1157
1311
  - Name: code-deduplication
1158
- - Description: Check-before-write principle and search checklist
1159
- - Content: Search existing code before writing new code. Include project-specific glob patterns for source files. Reference the actual directory structure for where to look.
1160
-
1161
- **\`.claude/skills/simplicity-rules.md\`**
1162
- - Name: simplicity-rules
1163
- - Description: Function and file size limits, decomposition patterns
1164
- - Content: Function length limits (40 lines), file limits (300 lines), cyclomatic complexity. Decomposition patterns appropriate for the project's architecture style.
1312
+ - Description: Check-before-write principle, search checklist, and size limits
1313
+ - Content: Search existing code before writing new code. Include project-specific glob patterns for source files. Reference the actual directory structure for where to look. Include a "Where to Look" checklist for discovering patterns in THIS project's structure (source directories, key file patterns). Add size limits: function length (40 lines), file length (300 lines), decomposition patterns appropriate for the project's architecture style.
1165
1314
 
1166
1315
  **\`.claude/skills/security.md\`**
1167
1316
  - Name: security
1168
1317
  - Description: Security patterns and secrets management for this stack
1169
1318
  - Content: .gitignore entries appropriate for the detected stack. Environment variable handling patterns. OWASP checklist items relevant to the detected framework. Include actual secrets patterns to watch for (API keys, database URLs, etc.).
1170
1319
 
1320
+ **\`.claude/skills/testing-methodology.md\`**
1321
+ - Name: testing-methodology
1322
+ - Description: Test design methodology \u2014 what to test, edge cases, test organization
1323
+ - Content: Focus on test DESIGN: what to test, how to identify edge cases, test organization strategy, when to use unit vs integration tests. Include project-specific test file naming and location conventions. Reference "See Common Commands in CLAUDE.md" for the test command. Do NOT include testing framework syntax examples (describe/it/expect, pytest fixtures, etc.) \u2014 those belong in the test-writer agent, not here. The \`testing-methodology\` skill focuses on test DESIGN (what to test, edge cases, test organization). The \`test-writer\` agent focuses on test EXECUTION (writing code, running tests). They must not overlap.
1324
+
1171
1325
  ### 4.2 Framework-Specific Skills (ONLY if detected)
1172
1326
 
1173
1327
  Generate the matching skill ONLY if the framework was detected in the tech stack:
@@ -1255,7 +1409,8 @@ Body content \u2014 instructions for the test writer agent:
1255
1409
  - Follow existing test file naming conventions
1256
1410
  - Include edge cases: empty inputs, nulls, errors, boundaries
1257
1411
  - Mock external dependencies following project patterns
1258
- - Run tests after writing to verify they pass`;
1412
+ - Run tests after writing to verify they pass
1413
+ - Do NOT duplicate the testing-methodology skill content. The skill covers test design (what to test, edge cases, organization); this agent covers writing and running tests (framework syntax, assertions, execution).`;
1259
1414
  var RULES_PROMPT = `---
1260
1415
 
1261
1416
  ## Phase 6: Generate Rules
@@ -1320,39 +1475,11 @@ var COMMANDS_PROMPT = `---
1320
1475
 
1321
1476
  ## Phase 7: Generate Commands
1322
1477
 
1323
- Write 5 command files to \`.claude/commands/\`. Each needs YAML frontmatter with
1478
+ Write 2 command files to \`.claude/commands/\`. Each needs YAML frontmatter with
1324
1479
  \`allowed-tools\`, \`description\`, and optionally \`argument-hint\`.
1325
1480
 
1326
- ### \`.claude/commands/task.md\`
1327
- \`\`\`yaml
1328
- ---
1329
- allowed-tools: ["Read", "Write", "Edit", "Glob"]
1330
- description: "Start or switch to a new task"
1331
- argument-hint: "<task description>"
1332
- ---
1333
- \`\`\`
1334
- Body: Instructions to read current \`.claude/state/task.md\`, update status to "In Progress",
1335
- record the task description and timestamp. If starting a new task, archive the previous one.
1336
-
1337
- ### \`.claude/commands/status.md\`
1338
- \`\`\`yaml
1339
- ---
1340
- allowed-tools: ["Read", "Glob", "Grep", "Bash(git status)", "Bash(git diff --stat)"]
1341
- description: "Show current task and session state"
1342
- ---
1343
- \`\`\`
1344
- Body: Read \`.claude/state/task.md\`, show git status, list recently modified files,
1345
- summarize current state in a concise format.
1346
-
1347
- ### \`.claude/commands/done.md\`
1348
- \`\`\`yaml
1349
- ---
1350
- allowed-tools: ["Read", "Write", "Edit", "Glob", "Grep", "Bash(git:*)", "Bash({test_command})", "Bash({lint_command})"]
1351
- description: "Mark current task complete"
1352
- ---
1353
- \`\`\`
1354
- Body: Run tests and lint checks. If they pass, update \`.claude/state/task.md\`
1355
- status to "Done". Show a summary of what was accomplished. Suggest next steps.
1481
+ Do NOT generate task management commands (\`task.md\`, \`status.md\`, \`done.md\`) \u2014
1482
+ Claude Code has built-in TaskCreate/TaskUpdate/TaskList tools for task management.
1356
1483
 
1357
1484
  ### \`.claude/commands/analyze.md\`
1358
1485
  \`\`\`yaml
@@ -1378,10 +1505,154 @@ Body: This command delegates to the code-reviewer agent for thorough review.
1378
1505
  3. If the agent is unavailable, perform a lightweight review: run the linter and check for obvious issues
1379
1506
  Do NOT duplicate the code-reviewer agent's checklist here \u2014 the agent has the full review criteria.`;
1380
1507
 
1508
+ // src/validator.ts
1509
+ import fs4 from "fs";
1510
+ import path4 from "path";
1511
+ function extractCommands(claudeMd) {
1512
+ const commands = [];
1513
+ const match = claudeMd.match(/## Common Commands[\s\S]*?```(?:bash)?\n([\s\S]*?)```/);
1514
+ if (!match) return commands;
1515
+ for (const line of match[1].split("\n")) {
1516
+ const trimmed = line.trim();
1517
+ if (!trimmed || trimmed.startsWith("#")) continue;
1518
+ const cmd = trimmed.split(/\s+#/)[0].trim();
1519
+ if (cmd.length > 3) commands.push(cmd);
1520
+ }
1521
+ return commands;
1522
+ }
1523
+ function extractConventionFingerprints(claudeMd) {
1524
+ const fingerprints = [];
1525
+ const startIdx = claudeMd.indexOf("## Code Conventions");
1526
+ if (startIdx === -1) return fingerprints;
1527
+ const rest = claudeMd.slice(startIdx + "## Code Conventions".length);
1528
+ const nextHeading = rest.match(/\n## [A-Z]/);
1529
+ const section = nextHeading ? claudeMd.slice(startIdx, startIdx + "## Code Conventions".length + nextHeading.index) : claudeMd.slice(startIdx);
1530
+ for (const kw of ["camelCase", "PascalCase", "kebab-case", "snake_case"]) {
1531
+ if (section.includes(kw)) fingerprints.push(kw);
1532
+ }
1533
+ if (/\bnamed exports?\b/i.test(section)) fingerprints.push("named export");
1534
+ if (/\bdefault exports?\b/i.test(section)) fingerprints.push("default export");
1535
+ if (section.includes("import type")) fingerprints.push("import type");
1536
+ for (const kw of [".skip()", ".only()", "console.log"]) {
1537
+ if (section.includes(kw)) fingerprints.push(kw);
1538
+ }
1539
+ return fingerprints;
1540
+ }
1541
+ var RULE_WORDS = /\b(verify|check|ensure|always|never|must|should|avoid)\b/i;
1542
+ function isConventionDuplication(line, fingerprints) {
1543
+ const trimmed = line.trim();
1544
+ if (!trimmed || trimmed.startsWith("#") || trimmed.includes("CLAUDE.md")) return false;
1545
+ if (!/^[-*]\s/.test(trimmed)) return false;
1546
+ const matchCount = fingerprints.filter((fp) => trimmed.includes(fp)).length;
1547
+ if (matchCount >= 2) return true;
1548
+ if (matchCount === 1 && RULE_WORDS.test(trimmed)) return true;
1549
+ return false;
1550
+ }
1551
+ function findLiteralCommand(line, commands) {
1552
+ const trimmed = line.trim();
1553
+ if (!trimmed || trimmed.startsWith("#") || trimmed.includes("CLAUDE.md")) return null;
1554
+ for (const cmd of commands) {
1555
+ if (trimmed.includes(cmd)) return cmd;
1556
+ }
1557
+ return null;
1558
+ }
1559
+ function separateFrontmatter(content) {
1560
+ const match = content.match(/^---\n[\s\S]*?\n---(?:\n|$)/);
1561
+ if (!match) {
1562
+ return { frontmatter: "", body: content };
1563
+ }
1564
+ return {
1565
+ frontmatter: match[0],
1566
+ body: content.slice(match[0].length)
1567
+ };
1568
+ }
1569
+ function processFile(filePath, commands, fingerprints) {
1570
+ const content = fs4.readFileSync(filePath, "utf-8");
1571
+ const { frontmatter, body } = separateFrontmatter(content);
1572
+ const lines = body.split("\n");
1573
+ const changes = [];
1574
+ const newLines = [];
1575
+ let inCodeBlock = false;
1576
+ for (const line of lines) {
1577
+ if (line.trim().startsWith("```")) {
1578
+ inCodeBlock = !inCodeBlock;
1579
+ newLines.push(line);
1580
+ continue;
1581
+ }
1582
+ if (inCodeBlock) {
1583
+ newLines.push(line);
1584
+ continue;
1585
+ }
1586
+ if (isConventionDuplication(line, fingerprints)) {
1587
+ changes.push({ file: filePath, original: line.trim(), replacement: null });
1588
+ continue;
1589
+ }
1590
+ const cmd = findLiteralCommand(line, commands);
1591
+ if (cmd) {
1592
+ const newLine = line.replace(cmd, "see Common Commands in CLAUDE.md");
1593
+ changes.push({ file: filePath, original: line.trim(), replacement: newLine.trim() });
1594
+ newLines.push(newLine);
1595
+ continue;
1596
+ }
1597
+ newLines.push(line);
1598
+ }
1599
+ if (changes.length > 0) {
1600
+ fs4.writeFileSync(filePath, frontmatter + newLines.join("\n"));
1601
+ }
1602
+ return changes;
1603
+ }
1604
+ function walkMdFiles(dir) {
1605
+ const files = [];
1606
+ if (!fs4.existsSync(dir)) return files;
1607
+ const entries = fs4.readdirSync(dir, { withFileTypes: true });
1608
+ for (const entry of entries) {
1609
+ const fullPath = path4.join(dir, entry.name);
1610
+ if (entry.isDirectory()) {
1611
+ files.push(...walkMdFiles(fullPath));
1612
+ } else if (entry.name.endsWith(".md")) {
1613
+ files.push(fullPath);
1614
+ }
1615
+ }
1616
+ return files;
1617
+ }
1618
+ function validateArtifacts(rootDir) {
1619
+ const result = {
1620
+ filesChecked: 0,
1621
+ filesModified: 0,
1622
+ duplicationsRemoved: 0,
1623
+ changes: []
1624
+ };
1625
+ const claudeMdPath = path4.join(rootDir, ".claude", "CLAUDE.md");
1626
+ if (!fs4.existsSync(claudeMdPath)) return result;
1627
+ try {
1628
+ const claudeMd = fs4.readFileSync(claudeMdPath, "utf-8");
1629
+ const commands = extractCommands(claudeMd);
1630
+ const fingerprints = extractConventionFingerprints(claudeMd);
1631
+ if (commands.length === 0 && fingerprints.length === 0) return result;
1632
+ const claudeDir = path4.join(rootDir, ".claude");
1633
+ const files = walkMdFiles(claudeDir).filter((f) => !f.endsWith("CLAUDE.md"));
1634
+ for (const filePath of files) {
1635
+ result.filesChecked++;
1636
+ const changes = processFile(filePath, commands, fingerprints);
1637
+ if (changes.length > 0) {
1638
+ result.filesModified++;
1639
+ result.duplicationsRemoved += changes.length;
1640
+ for (const change of changes) {
1641
+ change.file = path4.relative(rootDir, filePath);
1642
+ }
1643
+ result.changes.push(...changes);
1644
+ }
1645
+ }
1646
+ } catch {
1647
+ return result;
1648
+ }
1649
+ return result;
1650
+ }
1651
+
1381
1652
  // src/cli.ts
1382
- var __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
1653
+ var __dirname2 = path5.dirname(fileURLToPath(import.meta.url));
1383
1654
  var VERSION = JSON.parse(
1384
- fs3.readFileSync(path3.join(__dirname2, "..", "package.json"), "utf-8")
1655
+ fs5.readFileSync(path5.join(__dirname2, "..", "package.json"), "utf-8")
1385
1656
  ).version;
1386
1657
  function parseArgs(args) {
1387
1658
  return {
@@ -1419,7 +1690,7 @@ ${pc.bold("WHAT IT DOES")}
1419
1690
  - Skills for your frameworks and workflows
1420
1691
  - Agents for code review and testing
1421
1692
  - Rules matching your code style
1422
- - Commands for task management
1693
+ - Commands for analysis and code review
1423
1694
 
1424
1695
  ${pc.bold("REQUIREMENTS")}
1425
1696
  Claude CLI must be installed: https://claude.ai/download
@@ -1756,61 +2027,6 @@ function mapFormatter(linter) {
1756
2027
  };
1757
2028
  return mapping[linter] ?? null;
1758
2029
  }
1759
- function createTaskFile(projectInfo, preferences) {
1760
- const taskPath = path3.join(projectInfo.rootDir, ".claude", "state", "task.md");
1761
- fs3.mkdirSync(path3.dirname(taskPath), { recursive: true });
1762
- if (fs3.existsSync(taskPath)) {
1763
- return;
1764
- }
1765
- let content;
1766
- if (projectInfo.isExisting) {
1767
- content = `# Current Task
1768
-
1769
- ## Status: Ready
1770
-
1771
- No active task. Start one with \`/task <description>\`.
1772
-
1773
- ## Project Summary
1774
-
1775
- ${projectInfo.name}${projectInfo.description ? ` - ${projectInfo.description}` : ""}
1776
-
1777
- **Tech Stack:** ${summarizeTechStack(projectInfo.techStack)}
1778
-
1779
- ## Quick Commands
1780
-
1781
- - \`/task\` - Start working on something
1782
- - \`/status\` - See current state
1783
- - \`/analyze\` - Deep dive into code
1784
- - \`/done\` - Mark task complete
1785
- `;
1786
- } else {
1787
- const description = preferences?.description || "Explore and set up project";
1788
- content = `# Current Task
1789
-
1790
- ## Status: In Progress
1791
-
1792
- **Task:** ${description}
1793
-
1794
- ## Context
1795
-
1796
- New project - setting up from scratch.
1797
-
1798
- ${preferences?.framework ? `**Framework:** ${formatFramework(preferences.framework)}` : ""}
1799
- ${preferences?.primaryLanguage ? `**Language:** ${formatLanguage(preferences.primaryLanguage)}` : ""}
1800
-
1801
- ## Next Steps
1802
-
1803
- 1. Define project structure
1804
- 2. Set up development environment
1805
- 3. Start implementation
1806
-
1807
- ## Decisions
1808
-
1809
- (None yet - starting fresh)
1810
- `;
1811
- }
1812
- fs3.writeFileSync(taskPath, content);
1813
- }
1814
2030
  function formatLanguage(lang) {
1815
2031
  const names = {
1816
2032
  typescript: "TypeScript",
@@ -1950,17 +2166,17 @@ function runClaudeAnalysis(projectDir, projectInfo) {
1950
2166
  });
1951
2167
  }
1952
2168
  function getGeneratedFiles(projectDir) {
1953
- const claudeDir = path3.join(projectDir, ".claude");
2169
+ const claudeDir = path5.join(projectDir, ".claude");
1954
2170
  const files = [];
1955
2171
  function walk(dir) {
1956
- if (!fs3.existsSync(dir)) return;
1957
- const entries = fs3.readdirSync(dir, { withFileTypes: true });
2172
+ if (!fs5.existsSync(dir)) return;
2173
+ const entries = fs5.readdirSync(dir, { withFileTypes: true });
1958
2174
  for (const entry of entries) {
1959
- const fullPath = path3.join(dir, entry.name);
2175
+ const fullPath = path5.join(dir, entry.name);
1960
2176
  if (entry.isDirectory()) {
1961
2177
  walk(fullPath);
1962
2178
  } else {
1963
- files.push(path3.relative(projectDir, fullPath));
2179
+ files.push(path5.relative(projectDir, fullPath));
1964
2180
  }
1965
2181
  }
1966
2182
  }
@@ -2017,7 +2233,7 @@ async function main() {
2017
2233
  const { proceed } = await prompts({
2018
2234
  type: "confirm",
2019
2235
  name: "proceed",
2020
- message: "Update existing configuration? (preserves task state)",
2236
+ message: "Update existing configuration?",
2021
2237
  initial: true
2022
2238
  });
2023
2239
  if (!proceed) {
@@ -2039,12 +2255,19 @@ async function main() {
2039
2255
  console.log(pc.green("Created:"));
2040
2256
  console.log(pc.green(" + .claude/settings.json"));
2041
2257
  console.log();
2042
- createTaskFile(projectInfo, preferences);
2043
2258
  const success = await runClaudeAnalysis(projectDir, projectInfo);
2044
2259
  if (!success) {
2045
2260
  console.error(pc.red("Claude analysis failed. Please try again."));
2046
2261
  process.exit(1);
2047
2262
  }
2263
+ const validation = validateArtifacts(projectDir);
2264
+ if (validation.duplicationsRemoved > 0) {
2265
+ console.log(
2266
+ pc.gray(
2267
+ ` Deduplication: removed ${validation.duplicationsRemoved} redundancies from ${validation.filesModified} files`
2268
+ )
2269
+ );
2270
+ }
2048
2271
  const generatedFiles = getGeneratedFiles(projectDir);
2049
2272
  console.log();
2050
2273
  console.log(pc.green(`Done! (${generatedFiles.length} files)`));
@@ -2059,12 +2282,12 @@ async function main() {
2059
2282
  }
2060
2283
  if (skills.length > 0) {
2061
2284
  console.log(
2062
- ` ${skills.length} skills (${skills.map((s) => path3.basename(s, ".md")).join(", ")})`
2285
+ ` ${skills.length} skills (${skills.map((s) => path5.basename(s, ".md")).join(", ")})`
2063
2286
  );
2064
2287
  }
2065
2288
  if (agents.length > 0) {
2066
2289
  console.log(
2067
- ` ${agents.length} agents (${agents.map((a) => path3.basename(a, ".md")).join(", ")})`
2290
+ ` ${agents.length} agents (${agents.map((a) => path5.basename(a, ".md")).join(", ")})`
2068
2291
  );
2069
2292
  }
2070
2293
  if (rules.length > 0) {
@@ -2074,6 +2297,21 @@ async function main() {
2074
2297
  console.log(` ${commands.length} commands`);
2075
2298
  }
2076
2299
  console.log();
2300
+ if (args.interactive) {
2301
+ console.log();
2302
+ const { installSafetyHook } = await prompts({
2303
+ type: "confirm",
2304
+ name: "installSafetyHook",
2305
+ message: "Add a safety hook to block dangerous commands? (git push, rm -rf, etc.)",
2306
+ initial: true
2307
+ });
2308
+ if (installSafetyHook) {
2309
+ installHook(projectDir);
2310
+ console.log(pc.green(" + .claude/hooks/block-dangerous-commands.js"));
2311
+ console.log(pc.gray(" Blocks destructive Bash commands before execution"));
2312
+ }
2313
+ }
2314
+ console.log();
2077
2315
  console.log(`${pc.cyan("Next step:")} Run ${pc.bold("claude")} to start working!`);
2078
2316
  console.log();
2079
2317
  console.log(
@@ -2083,7 +2321,7 @@ async function main() {
2083
2321
  );
2084
2322
  }
2085
2323
  try {
2086
- const isMain = process.argv[1] && fs3.realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
2324
+ const isMain = process.argv[1] && fs5.realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
2087
2325
  if (isMain) {
2088
2326
  main().catch((err) => {
2089
2327
  console.error(pc.red("Error:"), err.message);
@@ -2097,7 +2335,6 @@ try {
2097
2335
  }
2098
2336
  export {
2099
2337
  checkClaudeCli,
2100
- createTaskFile,
2101
2338
  formatFramework,
2102
2339
  formatLanguage,
2103
2340
  getVersion,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-starter",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "A lightweight starter kit for AI-assisted development with Claude Code",
5
5
  "keywords": [
6
6
  "claude",