shipwright-cli 1.7.0 → 1.9.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 (106) hide show
  1. package/.claude/agents/code-reviewer.md +90 -0
  2. package/.claude/agents/devops-engineer.md +142 -0
  3. package/.claude/agents/pipeline-agent.md +80 -0
  4. package/.claude/agents/shell-script-specialist.md +150 -0
  5. package/.claude/agents/test-specialist.md +196 -0
  6. package/.claude/hooks/post-tool-use.sh +38 -0
  7. package/.claude/hooks/pre-tool-use.sh +25 -0
  8. package/.claude/hooks/session-started.sh +37 -0
  9. package/README.md +212 -814
  10. package/claude-code/CLAUDE.md.shipwright +54 -0
  11. package/claude-code/hooks/notify-idle.sh +2 -2
  12. package/claude-code/hooks/session-start.sh +24 -0
  13. package/claude-code/hooks/task-completed.sh +6 -2
  14. package/claude-code/settings.json.template +12 -0
  15. package/dashboard/public/app.js +4422 -0
  16. package/dashboard/public/index.html +816 -0
  17. package/dashboard/public/styles.css +4755 -0
  18. package/dashboard/server.ts +4315 -0
  19. package/docs/KNOWN-ISSUES.md +18 -10
  20. package/docs/TIPS.md +38 -26
  21. package/docs/patterns/README.md +33 -23
  22. package/package.json +9 -5
  23. package/scripts/adapters/iterm2-adapter.sh +1 -1
  24. package/scripts/adapters/tmux-adapter.sh +52 -23
  25. package/scripts/adapters/wezterm-adapter.sh +26 -14
  26. package/scripts/lib/compat.sh +200 -0
  27. package/scripts/lib/helpers.sh +72 -0
  28. package/scripts/postinstall.mjs +72 -13
  29. package/scripts/{cct → sw} +109 -21
  30. package/scripts/sw-adversarial.sh +274 -0
  31. package/scripts/sw-architecture-enforcer.sh +330 -0
  32. package/scripts/sw-checkpoint.sh +390 -0
  33. package/scripts/{cct-cleanup.sh → sw-cleanup.sh} +3 -1
  34. package/scripts/sw-connect.sh +619 -0
  35. package/scripts/{cct-cost.sh → sw-cost.sh} +368 -34
  36. package/scripts/{cct-daemon.sh → sw-daemon.sh} +2217 -204
  37. package/scripts/sw-dashboard.sh +477 -0
  38. package/scripts/sw-developer-simulation.sh +252 -0
  39. package/scripts/sw-docs.sh +635 -0
  40. package/scripts/sw-doctor.sh +907 -0
  41. package/scripts/{cct-fix.sh → sw-fix.sh} +10 -6
  42. package/scripts/{cct-fleet.sh → sw-fleet.sh} +498 -22
  43. package/scripts/sw-github-checks.sh +521 -0
  44. package/scripts/sw-github-deploy.sh +533 -0
  45. package/scripts/sw-github-graphql.sh +972 -0
  46. package/scripts/sw-heartbeat.sh +293 -0
  47. package/scripts/sw-init.sh +522 -0
  48. package/scripts/sw-intelligence.sh +1196 -0
  49. package/scripts/sw-jira.sh +643 -0
  50. package/scripts/sw-launchd.sh +364 -0
  51. package/scripts/sw-linear.sh +648 -0
  52. package/scripts/{cct-logs.sh → sw-logs.sh} +72 -2
  53. package/scripts/{cct-loop.sh → sw-loop.sh} +534 -44
  54. package/scripts/{cct-memory.sh → sw-memory.sh} +321 -38
  55. package/scripts/sw-patrol-meta.sh +417 -0
  56. package/scripts/sw-pipeline-composer.sh +455 -0
  57. package/scripts/{cct-pipeline.sh → sw-pipeline.sh} +2319 -178
  58. package/scripts/sw-predictive.sh +820 -0
  59. package/scripts/{cct-prep.sh → sw-prep.sh} +339 -49
  60. package/scripts/{cct-ps.sh → sw-ps.sh} +6 -4
  61. package/scripts/{cct-reaper.sh → sw-reaper.sh} +6 -4
  62. package/scripts/sw-remote.sh +687 -0
  63. package/scripts/sw-self-optimize.sh +947 -0
  64. package/scripts/sw-session.sh +519 -0
  65. package/scripts/sw-setup.sh +234 -0
  66. package/scripts/sw-status.sh +605 -0
  67. package/scripts/{cct-templates.sh → sw-templates.sh} +9 -4
  68. package/scripts/sw-tmux.sh +591 -0
  69. package/scripts/sw-tracker-jira.sh +277 -0
  70. package/scripts/sw-tracker-linear.sh +292 -0
  71. package/scripts/sw-tracker.sh +409 -0
  72. package/scripts/{cct-upgrade.sh → sw-upgrade.sh} +103 -46
  73. package/scripts/{cct-worktree.sh → sw-worktree.sh} +3 -0
  74. package/templates/pipelines/autonomous.json +27 -5
  75. package/templates/pipelines/full.json +12 -0
  76. package/templates/pipelines/standard.json +12 -0
  77. package/tmux/{claude-teams-overlay.conf → shipwright-overlay.conf} +27 -9
  78. package/tmux/templates/accessibility.json +34 -0
  79. package/tmux/templates/api-design.json +35 -0
  80. package/tmux/templates/architecture.json +1 -0
  81. package/tmux/templates/bug-fix.json +9 -0
  82. package/tmux/templates/code-review.json +1 -0
  83. package/tmux/templates/compliance.json +36 -0
  84. package/tmux/templates/data-pipeline.json +36 -0
  85. package/tmux/templates/debt-paydown.json +34 -0
  86. package/tmux/templates/devops.json +1 -0
  87. package/tmux/templates/documentation.json +1 -0
  88. package/tmux/templates/exploration.json +1 -0
  89. package/tmux/templates/feature-dev.json +1 -0
  90. package/tmux/templates/full-stack.json +8 -0
  91. package/tmux/templates/i18n.json +34 -0
  92. package/tmux/templates/incident-response.json +36 -0
  93. package/tmux/templates/migration.json +1 -0
  94. package/tmux/templates/observability.json +35 -0
  95. package/tmux/templates/onboarding.json +33 -0
  96. package/tmux/templates/performance.json +35 -0
  97. package/tmux/templates/refactor.json +1 -0
  98. package/tmux/templates/release.json +35 -0
  99. package/tmux/templates/security-audit.json +8 -0
  100. package/tmux/templates/spike.json +34 -0
  101. package/tmux/templates/testing.json +1 -0
  102. package/tmux/tmux.conf +98 -9
  103. package/scripts/cct-doctor.sh +0 -328
  104. package/scripts/cct-init.sh +0 -282
  105. package/scripts/cct-session.sh +0 -284
  106. package/scripts/cct-status.sh +0 -169
@@ -4,14 +4,15 @@
4
4
  # ║ Analyze repos · Generate configs · Equip autonomous agents ║
5
5
  # ╚═══════════════════════════════════════════════════════════════════════════╝
6
6
  set -euo pipefail
7
+ trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
7
8
 
8
- VERSION="1.7.0"
9
+ VERSION="1.9.0"
9
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
11
 
11
12
  # ─── Handle subcommands ───────────────────────────────────────────────────────
12
13
  if [[ "${1:-}" == "test" ]]; then
13
14
  shift
14
- exec "$SCRIPT_DIR/cct-prep-test.sh" "$@"
15
+ exec "$SCRIPT_DIR/sw-prep-test.sh" "$@"
15
16
  fi
16
17
 
17
18
  # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
@@ -25,6 +26,9 @@ DIM='\033[2m'
25
26
  BOLD='\033[1m'
26
27
  RESET='\033[0m'
27
28
 
29
+ # ─── Cross-platform compatibility ──────────────────────────────────────────
30
+ # shellcheck source=lib/compat.sh
31
+ [[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
28
32
  # ─── Output Helpers ─────────────────────────────────────────────────────────
29
33
  info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
30
34
  success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
@@ -74,6 +78,10 @@ HAS_DB=false
74
78
  HAS_MIDDLEWARE=false
75
79
  ROUTE_PATTERNS=""
76
80
  DB_PATTERNS=""
81
+ ARCHITECTURE_PATTERN=""
82
+ SEMICOLONS=""
83
+ QUOTE_STYLE=""
84
+ INDENT_STYLE=""
77
85
 
78
86
  # Tracking generated files
79
87
  GENERATED_FILES=()
@@ -159,7 +167,7 @@ should_write() {
159
167
  fi
160
168
  if $UPDATE_MODE; then
161
169
  # In update mode, only write if file has auto markers
162
- if grep -q "<!-- cct:auto-start -->" "$filepath" 2>/dev/null; then
170
+ if grep -q "<!-- sw:auto-start -->" "$filepath" 2>/dev/null; then
163
171
  return 0
164
172
  fi
165
173
  info "Skipping ${filepath##"$PROJECT_ROOT"/} (no auto markers, user-customized)"
@@ -175,11 +183,11 @@ update_auto_section() {
175
183
  local filepath="$1"
176
184
  local new_content="$2"
177
185
 
178
- if $UPDATE_MODE && [[ -f "$filepath" ]] && grep -q "<!-- cct:auto-start -->" "$filepath"; then
186
+ if $UPDATE_MODE && [[ -f "$filepath" ]] && grep -q "<!-- sw:auto-start -->" "$filepath"; then
179
187
  # Replace content between markers, preserve everything else
180
188
  local before after
181
- before=$(sed '/<!-- cct:auto-start -->/,$d' "$filepath")
182
- after=$(sed '1,/<!-- cct:auto-end -->/d' "$filepath")
189
+ before=$(sed '/<!-- sw:auto-start -->/,$d' "$filepath")
190
+ after=$(sed '1,/<!-- sw:auto-end -->/d' "$filepath")
183
191
  {
184
192
  echo "$before"
185
193
  echo "$new_content"
@@ -507,8 +515,8 @@ prep_extract_patterns() {
507
515
  # ── Import style ──
508
516
  if [[ "$LANG_DETECTED" == "nodejs" || "$LANG_DETECTED" == "typescript" ]]; then
509
517
  local es_count cjs_count
510
- es_count=$(grep -rl "^import " "$root/src" "$root/app" "$root/lib" 2>/dev/null | wc -l | tr -d ' ')
511
- cjs_count=$(grep -rl "require(" "$root/src" "$root/app" "$root/lib" 2>/dev/null | wc -l | tr -d ' ')
518
+ es_count=$( { grep -rl "^import " "$root/src" "$root/app" "$root/lib" 2>/dev/null || true; } | wc -l | tr -d ' ')
519
+ cjs_count=$( { grep -rl "require(" "$root/src" "$root/app" "$root/lib" 2>/dev/null || true; } | wc -l | tr -d ' ')
512
520
  if [[ "$es_count" -gt "$cjs_count" ]]; then
513
521
  IMPORT_STYLE="ES modules (import/export)"
514
522
  elif [[ "$cjs_count" -gt 0 ]]; then
@@ -520,9 +528,9 @@ prep_extract_patterns() {
520
528
 
521
529
  # ── Naming convention ──
522
530
  local camel_count snake_count
523
- camel_count=$(grep -roh '[a-z][a-zA-Z]*(' "$root/src" "$root/app" "$root/lib" 2>/dev/null | grep -c '[a-z][A-Z]' 2>/dev/null || true)
531
+ camel_count=$( { grep -roh '[a-z][a-zA-Z]*(' "$root/src" "$root/app" "$root/lib" 2>/dev/null || true; } | grep -c '[a-z][A-Z]' 2>/dev/null || true)
524
532
  camel_count="${camel_count:-0}"
525
- snake_count=$(grep -roh '[a-z_]*_[a-z]*(' "$root/src" "$root/app" "$root/lib" 2>/dev/null | wc -l 2>/dev/null | tr -d ' ')
533
+ snake_count=$( { grep -roh '[a-z_]*_[a-z]*(' "$root/src" "$root/app" "$root/lib" 2>/dev/null || true; } | wc -l 2>/dev/null | tr -d ' ')
526
534
  snake_count="${snake_count:-0}"
527
535
  if [[ "$camel_count" -gt "$snake_count" ]]; then
528
536
  NAMING_CONVENTION="camelCase"
@@ -569,6 +577,271 @@ prep_extract_patterns() {
569
577
  success "Patterns: ${NAMING_CONVENTION} naming${IMPORT_STYLE:+, ${IMPORT_STYLE}}${ROUTE_PATTERNS:+, ${ROUTE_PATTERNS}}"
570
578
  }
571
579
 
580
+ # ─── Intelligence Check ──────────────────────────────────────────────────
581
+
582
+ intelligence_available() {
583
+ command -v claude &>/dev/null || return 1
584
+ # Honor --with-claude flag
585
+ $WITH_CLAUDE && return 0
586
+ # Check daemon config for intelligence.enabled
587
+ local config="${PROJECT_ROOT}/.claude/daemon-config.json"
588
+ if [[ -f "$config" ]]; then
589
+ local enabled
590
+ enabled=$(jq -r '.intelligence.enabled // false' "$config" 2>/dev/null || echo "false")
591
+ [[ "$enabled" == "true" ]] && return 0
592
+ fi
593
+ return 1
594
+ }
595
+
596
+ # ─── prep_smart_detect — Claude-enhanced detection ────────────────────────
597
+
598
+ prep_smart_detect() {
599
+ intelligence_available || return 0
600
+
601
+ info "Running intelligent stack analysis..."
602
+
603
+ # Collect dependency manifests (truncated)
604
+ local dep_info=""
605
+ local manifest
606
+ for manifest in package.json requirements.txt Cargo.toml go.mod pyproject.toml Gemfile pom.xml build.gradle; do
607
+ if [[ -f "$PROJECT_ROOT/$manifest" ]]; then
608
+ dep_info+="=== ${manifest} ===
609
+ $(head -100 "$PROJECT_ROOT/$manifest" 2>/dev/null)
610
+
611
+ "
612
+ fi
613
+ done
614
+
615
+ # Collect grep detection results summary
616
+ local grep_results="Language: ${LANG_DETECTED:-unknown}
617
+ Framework: ${FRAMEWORK:-unknown}
618
+ Package Manager: ${PACKAGE_MANAGER:-unknown}
619
+ Test Framework: ${TEST_FRAMEWORK:-unknown}
620
+ Import Style: ${IMPORT_STYLE:-unknown}
621
+ Naming: ${NAMING_CONVENTION:-unknown}
622
+ Routes: ${ROUTE_PATTERNS:-none}
623
+ Database: ${DB_PATTERNS:-none}"
624
+
625
+ # Sample code from entry points (first 50 lines of up to 3 files)
626
+ local code_samples=""
627
+ local sample_count=0
628
+ local entry
629
+ for entry in $ENTRY_POINTS; do
630
+ [[ $sample_count -ge 3 ]] && break
631
+ if [[ -f "$PROJECT_ROOT/$entry" ]]; then
632
+ code_samples+="=== ${entry} (first 50 lines) ===
633
+ $(head -50 "$PROJECT_ROOT/$entry" 2>/dev/null)
634
+
635
+ "
636
+ sample_count=$((sample_count + 1))
637
+ fi
638
+ done
639
+
640
+ # If no entry points found, sample some source files
641
+ if [[ $sample_count -eq 0 ]]; then
642
+ local f
643
+ while IFS= read -r f; do
644
+ [[ $sample_count -ge 3 ]] && break
645
+ [[ -z "$f" ]] && continue
646
+ local relpath="${f#"$PROJECT_ROOT"/}"
647
+ code_samples+="=== ${relpath} (first 50 lines) ===
648
+ $(head -50 "$f" 2>/dev/null)
649
+
650
+ "
651
+ sample_count=$((sample_count + 1))
652
+ done < <(find "$PROJECT_ROOT/src" "$PROJECT_ROOT/app" "$PROJECT_ROOT/lib" \
653
+ \( -name '*.ts' -o -name '*.js' -o -name '*.py' -o -name '*.go' -o -name '*.rs' \) \
654
+ -not -path "*/node_modules/*" 2>/dev/null | head -3)
655
+ fi
656
+
657
+ # Sample route files if routes detected
658
+ local route_samples=""
659
+ if $HAS_ROUTES; then
660
+ local route_count=0
661
+ local f
662
+ while IFS= read -r f; do
663
+ [[ $route_count -ge 2 ]] && break
664
+ [[ -z "$f" ]] && continue
665
+ local relpath="${f#"$PROJECT_ROOT"/}"
666
+ route_samples+="=== ${relpath} (first 40 lines) ===
667
+ $(head -40 "$f" 2>/dev/null)
668
+
669
+ "
670
+ route_count=$((route_count + 1))
671
+ done < <(find "$PROJECT_ROOT/src" "$PROJECT_ROOT/app" "$PROJECT_ROOT/routes" "$PROJECT_ROOT/lib" \
672
+ \( -name '*route*' -o -name '*controller*' -o -name '*handler*' -o -name '*endpoint*' \) \
673
+ -not -path "*/node_modules/*" 2>/dev/null | head -2)
674
+ fi
675
+
676
+ # Sample style from up to 10 source files (first 20 lines each)
677
+ local style_samples=""
678
+ local style_count=0
679
+ local f
680
+ while IFS= read -r f; do
681
+ [[ $style_count -ge 10 ]] && break
682
+ [[ -z "$f" ]] && continue
683
+ local relpath="${f#"$PROJECT_ROOT"/}"
684
+ style_samples+="=== ${relpath} (first 20 lines) ===
685
+ $(head -20 "$f" 2>/dev/null)
686
+
687
+ "
688
+ style_count=$((style_count + 1))
689
+ done < <(find "$PROJECT_ROOT/src" "$PROJECT_ROOT/app" "$PROJECT_ROOT/lib" \
690
+ \( -name '*.ts' -o -name '*.js' -o -name '*.py' -o -name '*.go' -o -name '*.rs' -o -name '*.rb' -o -name '*.java' \) \
691
+ -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null | head -10)
692
+
693
+ [[ -z "$dep_info" && -z "$code_samples" && -z "$style_samples" ]] && return 0
694
+
695
+ local prompt
696
+ prompt="Analyze this project and respond in EXACTLY this format (one value per line, no extra text):
697
+ PRIMARY_LANGUAGE: <language>
698
+ FRAMEWORK: <framework or none>
699
+ ARCHITECTURE: <monolith|microservice|serverless|CLI|library>
700
+ TEST_FRAMEWORK: <test framework or unknown>
701
+ CI_SYSTEM: <CI system or unknown>
702
+ IMPORT_STYLE: <ESM|CJS|mixed|N/A>
703
+ NAMING: <camelCase|snake_case|PascalCase|kebab-case|mixed>
704
+ SEMICOLONS: <yes|no|N/A>
705
+ QUOTE_STYLE: <single|double|mixed|N/A>
706
+ INDENT: <spaces-2|spaces-4|tabs|mixed>
707
+ ROUTE_STYLE: <description of routing pattern or none>
708
+ DB_PATTERN: <description of database access pattern or none>
709
+
710
+ Detected so far by grep:
711
+ ${grep_results}
712
+
713
+ Dependencies:
714
+ ${dep_info}
715
+ Code samples:
716
+ ${code_samples}
717
+ ${route_samples:+Route files:
718
+ ${route_samples}}
719
+ Style samples (analyze imports, naming, code style):
720
+ ${style_samples}"
721
+
722
+ local analysis
723
+ analysis=$(claude --print "$prompt" 2>/dev/null || true)
724
+
725
+ [[ -z "$analysis" ]] && { warn "Smart detection returned empty — using grep results"; return 0; }
726
+
727
+ # Parse and enrich (only override gaps — grep results take priority)
728
+ local smart_val
729
+
730
+ smart_val=$(echo "$analysis" | grep "^FRAMEWORK:" | sed 's/^FRAMEWORK:[[:space:]]*//' | head -1)
731
+ if [[ -n "$smart_val" && "$smart_val" != "none" && "$smart_val" != "unknown" && -z "$FRAMEWORK" ]]; then
732
+ FRAMEWORK="$smart_val"
733
+ fi
734
+
735
+ smart_val=$(echo "$analysis" | grep "^TEST_FRAMEWORK:" | sed 's/^TEST_FRAMEWORK:[[:space:]]*//' | head -1)
736
+ if [[ -n "$smart_val" && "$smart_val" != "unknown" && -z "$TEST_FRAMEWORK" ]]; then
737
+ TEST_FRAMEWORK="$smart_val"
738
+ fi
739
+
740
+ smart_val=$(echo "$analysis" | grep "^IMPORT_STYLE:" | sed 's/^IMPORT_STYLE:[[:space:]]*//' | head -1)
741
+ if [[ -n "$smart_val" && "$smart_val" != "N/A" ]]; then
742
+ case "$smart_val" in
743
+ ESM) IMPORT_STYLE="ES modules (import/export)" ;;
744
+ CJS) IMPORT_STYLE="CommonJS (require/module.exports)" ;;
745
+ mixed) IMPORT_STYLE="Mixed (ESM + CJS)" ;;
746
+ esac
747
+ fi
748
+
749
+ smart_val=$(echo "$analysis" | grep "^NAMING:" | sed 's/^NAMING:[[:space:]]*//' | head -1)
750
+ if [[ -n "$smart_val" && "$smart_val" != "mixed" && "$smart_val" != "unknown" ]]; then
751
+ NAMING_CONVENTION="$smart_val"
752
+ fi
753
+
754
+ smart_val=$(echo "$analysis" | grep "^ARCHITECTURE:" | sed 's/^ARCHITECTURE:[[:space:]]*//' | head -1)
755
+ ARCHITECTURE_PATTERN="${smart_val:-}"
756
+
757
+ smart_val=$(echo "$analysis" | grep "^ROUTE_STYLE:" | sed 's/^ROUTE_STYLE:[[:space:]]*//' | head -1)
758
+ if [[ -n "$smart_val" && "$smart_val" != "none" && -z "$ROUTE_PATTERNS" ]]; then
759
+ HAS_ROUTES=true
760
+ ROUTE_PATTERNS="$smart_val"
761
+ fi
762
+
763
+ smart_val=$(echo "$analysis" | grep "^DB_PATTERN:" | sed 's/^DB_PATTERN:[[:space:]]*//' | head -1)
764
+ if [[ -n "$smart_val" && "$smart_val" != "none" && -z "$DB_PATTERNS" ]]; then
765
+ HAS_DB=true
766
+ DB_PATTERNS="$smart_val"
767
+ fi
768
+
769
+ SEMICOLONS=$(echo "$analysis" | grep "^SEMICOLONS:" | sed 's/^SEMICOLONS:[[:space:]]*//' | head -1)
770
+ QUOTE_STYLE=$(echo "$analysis" | grep "^QUOTE_STYLE:" | sed 's/^QUOTE_STYLE:[[:space:]]*//' | head -1)
771
+ INDENT_STYLE=$(echo "$analysis" | grep "^INDENT:" | sed 's/^INDENT:[[:space:]]*//' | head -1)
772
+
773
+ success "Smart detection: ${ARCHITECTURE_PATTERN:-unknown} architecture${FRAMEWORK:+, ${FRAMEWORK}}"
774
+ }
775
+
776
+ # ─── prep_learn_patterns — Record detected patterns per repo ──────────────
777
+
778
+ prep_learn_patterns() {
779
+ intelligence_available || return 0
780
+
781
+ local repo_hash
782
+ repo_hash=$(compute_md5 --string "$PROJECT_ROOT" || echo "default")
783
+
784
+ local baselines_dir="${HOME}/.shipwright/baselines/${repo_hash}"
785
+ mkdir -p "$baselines_dir"
786
+
787
+ local patterns_file="${baselines_dir}/file-patterns.json"
788
+
789
+ # Detect test file patterns present in this repo
790
+ local test_patterns="[]"
791
+ if find "$PROJECT_ROOT" -name "*.test.*" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null | head -1 | grep -q .; then
792
+ test_patterns=$(echo "$test_patterns" | jq '. + ["*.test.*"]')
793
+ fi
794
+ if find "$PROJECT_ROOT" -name "*.spec.*" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null | head -1 | grep -q .; then
795
+ test_patterns=$(echo "$test_patterns" | jq '. + ["*.spec.*"]')
796
+ fi
797
+ if find "$PROJECT_ROOT" -name "*_test.*" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null | head -1 | grep -q .; then
798
+ test_patterns=$(echo "$test_patterns" | jq '. + ["*_test.*"]')
799
+ fi
800
+ if find "$PROJECT_ROOT" -name "test_*" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null | head -1 | grep -q .; then
801
+ test_patterns=$(echo "$test_patterns" | jq '. + ["test_*"]')
802
+ fi
803
+
804
+ # Build config file list
805
+ local config_json="[]"
806
+ local f
807
+ for f in $CONFIG_FILES; do
808
+ config_json=$(echo "$config_json" | jq --arg f "$f" '. + [$f]')
809
+ done
810
+
811
+ # Build entry points list
812
+ local entry_json="[]"
813
+ for f in $ENTRY_POINTS; do
814
+ entry_json=$(echo "$entry_json" | jq --arg f "$f" '. + [$f]')
815
+ done
816
+
817
+ # Write patterns file atomically
818
+ local tmp_patterns
819
+ tmp_patterns=$(mktemp)
820
+ jq -n \
821
+ --arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
822
+ --arg lang "${LANG_DETECTED:-}" \
823
+ --arg framework "${FRAMEWORK:-}" \
824
+ --arg naming "${NAMING_CONVENTION:-}" \
825
+ --arg imports "${IMPORT_STYLE:-}" \
826
+ --arg arch "${ARCHITECTURE_PATTERN:-}" \
827
+ --argjson test_patterns "$test_patterns" \
828
+ --argjson config_files "$config_json" \
829
+ --argjson entry_points "$entry_json" \
830
+ '{
831
+ updated_at: $ts,
832
+ language: $lang,
833
+ framework: $framework,
834
+ architecture: $arch,
835
+ naming_convention: $naming,
836
+ import_style: $imports,
837
+ test_file_patterns: $test_patterns,
838
+ config_files: $config_files,
839
+ entry_points: $entry_points
840
+ }' > "$tmp_patterns" 2>/dev/null && mv "$tmp_patterns" "$patterns_file" || rm -f "$tmp_patterns"
841
+
842
+ success "Learned file patterns → ${patterns_file##"$HOME"/}"
843
+ }
844
+
572
845
  # ─── prep_generate_claude_md ────────────────────────────────────────────────
573
846
 
574
847
  prep_generate_claude_md() {
@@ -613,8 +886,7 @@ prep_generate_claude_md() {
613
886
  fi
614
887
 
615
888
  local content
616
- content=$(cat <<HEREDOC
617
- <!-- cct:auto-start -->
889
+ content="<!-- sw:auto-start -->
618
890
  # Project: ${PROJECT_NAME}
619
891
 
620
892
  ## Stack
@@ -638,9 +910,7 @@ $(echo -e "$conventions")
638
910
 
639
911
  ## Important Files
640
912
  $(echo -e "$important")
641
- <!-- cct:auto-end -->
642
- HEREDOC
643
- )
913
+ <!-- sw:auto-end -->"
644
914
 
645
915
  update_auto_section "$filepath" "$content"
646
916
  track_file "$filepath"
@@ -883,7 +1153,7 @@ prep_generate_architecture() {
883
1153
  fi
884
1154
  elif [[ -f "$PROJECT_ROOT/go.mod" ]]; then
885
1155
  local go_deps
886
- go_deps=$(grep "^\t" "$PROJECT_ROOT/go.mod" 2>/dev/null | awk '{print $1}' | head -15)
1156
+ go_deps=$( { grep "^\t" "$PROJECT_ROOT/go.mod" 2>/dev/null || true; } | awk '{print $1}' | head -15)
887
1157
  if [[ -n "$go_deps" ]]; then
888
1158
  deps_section="## Dependencies\n"
889
1159
  while IFS= read -r dep; do
@@ -902,35 +1172,52 @@ prep_generate_architecture() {
902
1172
  data_flow+="Request → ${ROUTE_PATTERNS:-Router} → Handler → Response\n"
903
1173
  fi
904
1174
 
1175
+ # Pre-compute entry points section
1176
+ local entry_section=""
1177
+ if [[ -n "$ENTRY_POINTS" ]]; then
1178
+ for f in $ENTRY_POINTS; do
1179
+ entry_section+="- \`${f}\`"$'\n'
1180
+ done
1181
+ else
1182
+ entry_section="- No standard entry points detected"$'\n'
1183
+ fi
1184
+
1185
+ # Pre-compute module map section
1186
+ local module_section=""
1187
+ if [[ -n "$module_map" ]]; then
1188
+ module_section=$(echo -e "$module_map")
1189
+ else
1190
+ module_section="No standard module directories detected."
1191
+ fi
1192
+
1193
+ # Pre-compute infrastructure section
1194
+ local infra_section=""
1195
+ $HAS_DOCKER && infra_section+="- Docker: Dockerfile present"$'\n'
1196
+ $HAS_COMPOSE && infra_section+="- Docker Compose: multi-service setup"$'\n'
1197
+ $HAS_CI && infra_section+="- CI/CD: GitHub Actions workflows"$'\n'
1198
+ $HAS_MAKEFILE && infra_section+="- Makefile: build automation"$'\n'
1199
+ if [[ -z "$infra_section" ]]; then
1200
+ infra_section="- No infrastructure files detected"$'\n'
1201
+ fi
1202
+
905
1203
  local content
906
- content=$(cat <<HEREDOC
907
- <!-- cct:auto-start -->
1204
+ content="<!-- sw:auto-start -->
908
1205
  # Architecture
909
1206
 
910
1207
  ## Overview
911
1208
  **${PROJECT_NAME}** is a ${LANG_DETECTED:-unknown}${FRAMEWORK:+ / ${FRAMEWORK}} project with ${SRC_FILE_COUNT} source files and ~${TOTAL_LINES} lines of code.
912
1209
 
913
1210
  ## Entry Points
914
- $(for f in $ENTRY_POINTS; do echo "- \`${f}\`"; done)
915
- $(if [[ -z "$ENTRY_POINTS" ]]; then echo "- No standard entry points detected"; fi)
916
-
1211
+ ${entry_section}
917
1212
  ## Module Map
918
- $(echo -e "$module_map")
919
- $(if [[ -z "$module_map" ]]; then echo "No standard module directories detected."; fi)
1213
+ ${module_section}
920
1214
 
921
1215
  $(echo -e "${deps_section}")
922
1216
 
923
1217
  $(echo -e "${data_flow}")
924
1218
 
925
1219
  ## Infrastructure
926
- $(if $HAS_DOCKER; then echo "- Docker: \`Dockerfile\` present"; fi)
927
- $(if $HAS_COMPOSE; then echo "- Docker Compose: multi-service setup"; fi)
928
- $(if $HAS_CI; then echo "- CI/CD: GitHub Actions workflows"; fi)
929
- $(if $HAS_MAKEFILE; then echo "- Makefile: build automation"; fi)
930
- $(if ! $HAS_DOCKER && ! $HAS_COMPOSE && ! $HAS_CI && ! $HAS_MAKEFILE; then echo "- No infrastructure files detected"; fi)
931
- <!-- cct:auto-end -->
932
- HEREDOC
933
- )
1220
+ ${infra_section}<!-- sw:auto-end -->"
934
1221
 
935
1222
  update_auto_section "$filepath" "$content"
936
1223
  track_file "$filepath"
@@ -945,44 +1232,43 @@ prep_generate_standards() {
945
1232
 
946
1233
  info "Generating .claude/CODING-STANDARDS.md..."
947
1234
 
1235
+ local file_org=""
1236
+ if [[ -n "${SRC_DIRS:-}" ]]; then
1237
+ file_org+="- Source code: \`${SRC_DIRS}\`"$'\n'
1238
+ fi
1239
+ if [[ -n "${TEST_DIRS:-}" ]]; then
1240
+ file_org+="- Tests: \`${TEST_DIRS}\`"$'\n'
1241
+ fi
1242
+
948
1243
  local content
949
- content=$(cat <<HEREDOC
950
- <!-- cct:auto-start -->
1244
+ content="<!-- sw:auto-start -->
951
1245
  # Coding Standards
952
1246
 
953
1247
  ## Naming
954
1248
  - Convention: **${NAMING_CONVENTION:-not detected}**
955
1249
  - Files: follow existing file naming patterns in the project
956
- - Variables/functions: use ${NAMING_CONVENTION:-the project's established} style consistently
1250
+ - Variables/functions: use **${NAMING_CONVENTION:-mixed}** style consistently
957
1251
 
958
1252
  ## Imports
959
1253
  - Style: **${IMPORT_STYLE:-follow existing patterns}**
960
1254
  - Keep imports organized: stdlib → external deps → internal modules
961
1255
 
962
1256
  ## Error Handling
963
- - Use the project's existing error handling patterns
1257
+ - Use the existing error handling patterns
964
1258
  - Always handle promise rejections / async errors
965
1259
  - Provide meaningful error messages
966
- - Don't swallow errors silently
1260
+ - Do not swallow errors silently
967
1261
 
968
1262
  ## Testing
969
1263
  - Framework: **${TEST_FRAMEWORK:-unknown}**
970
1264
  - Write tests for all new functionality
971
1265
  - Test both success and error paths
972
- - Use descriptive test names: \`describe("module") → it("should do X when Y")\`
1266
+ - Use descriptive test names
973
1267
  - Keep tests focused — one assertion per test where practical
974
1268
 
975
1269
  ## File Organization
976
- $(if [[ -n "$SRC_DIRS" ]]; then
977
- echo "- Source code: \`${SRC_DIRS}\`"
978
- fi)
979
- $(if [[ -n "$TEST_DIRS" ]]; then
980
- echo "- Tests: \`${TEST_DIRS}\`"
981
- fi)
982
- - Follow the existing directory structure — don't create new top-level dirs without discussion
983
- <!-- cct:auto-end -->
984
- HEREDOC
985
- )
1270
+ ${file_org}- Follow the existing directory structure — do not create new top-level dirs without discussion
1271
+ <!-- sw:auto-end -->"
986
1272
 
987
1273
  update_auto_section "$filepath" "$content"
988
1274
  track_file "$filepath"
@@ -1080,7 +1366,7 @@ prep_generate_manifest() {
1080
1366
  fname="${entry%%|*}"
1081
1367
  flines="${entry##*|}"
1082
1368
  local checksum
1083
- checksum=$(md5 -q "$PROJECT_ROOT/$fname" 2>/dev/null || md5sum "$PROJECT_ROOT/$fname" 2>/dev/null | awk '{print $1}' || echo "unknown")
1369
+ checksum=$(compute_md5 "$PROJECT_ROOT/$fname" || echo "unknown")
1084
1370
  if $first; then
1085
1371
  first=false
1086
1372
  else
@@ -1245,7 +1531,7 @@ prep_check() {
1245
1531
 
1246
1532
  # Check for auto markers in markdown files
1247
1533
  if [[ "$f" == *.md && "$f" != *"ISSUE_TEMPLATE"* && "$f" != *"DEFINITION-OF-DONE"* ]]; then
1248
- if grep -q "<!-- cct:auto-start -->" "$PROJECT_ROOT/$f" 2>/dev/null; then
1534
+ if grep -q "<!-- sw:auto-start -->" "$PROJECT_ROOT/$f" 2>/dev/null; then
1249
1535
  echo -e " ${DIM}↳ Has auto-update markers${RESET}"
1250
1536
  else
1251
1537
  echo -e " ${YELLOW}↳ No auto-update markers (user-customized)${RESET}"
@@ -1328,6 +1614,10 @@ main() {
1328
1614
  prep_scan_structure
1329
1615
  prep_extract_patterns
1330
1616
 
1617
+ # Smart detection (intelligence-gated)
1618
+ prep_smart_detect
1619
+ prep_learn_patterns
1620
+
1331
1621
  # Generation
1332
1622
  prep_generate_claude_md
1333
1623
  prep_generate_settings
@@ -1,11 +1,13 @@
1
1
  #!/usr/bin/env bash
2
2
  # ╔═══════════════════════════════════════════════════════════════════════════╗
3
- # ║ cct-ps.sh — Show running agent process status ║
3
+ # ║ sw-ps.sh — Show running agent process status ║
4
4
  # ║ ║
5
5
  # ║ Displays a table of agents running in claude-* tmux windows with ║
6
6
  # ║ PID, status, idle time, and pane references. ║
7
7
  # ╚═══════════════════════════════════════════════════════════════════════════╝
8
+ VERSION="1.9.0"
8
9
  set -euo pipefail
10
+ trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
11
 
10
12
  # ─── Colors ──────────────────────────────────────────────────────────────────
11
13
  CYAN='\033[38;2;0;212;255m'
@@ -82,7 +84,7 @@ status_display() {
82
84
 
83
85
  # ─── Header ─────────────────────────────────────────────────────────────────
84
86
  echo ""
85
- echo -e "${CYAN}${BOLD} Claude Code Teams — Process Status${RESET}"
87
+ echo -e "${CYAN}${BOLD} Shipwright — Process Status${RESET}"
86
88
  echo -e "${DIM} $(date '+%Y-%m-%d %H:%M:%S')${RESET}"
87
89
  echo -e "${DIM} ══════════════════════════════════════════${RESET}"
88
90
  echo ""
@@ -118,13 +120,13 @@ while IFS='|' read -r window_name pane_title pane_pid cmd pane_active pane_idle
118
120
  local_idle_fmt="$(format_idle "$pane_idle")"
119
121
 
120
122
  # Active pane indicator
121
- local active_marker=""
123
+ active_marker=""
122
124
  if [[ "$pane_active" == "1" ]]; then
123
125
  active_marker=" ${CYAN}●${RESET}"
124
126
  fi
125
127
 
126
128
  # Agent display name
127
- local agent_name="${pane_title:-${cmd}}"
129
+ agent_name="${pane_title:-${cmd}}"
128
130
 
129
131
  printf " %-20s %-8s " "$agent_name" "$pane_pid"
130
132
  status_display "$local_status"
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  # ╔═══════════════════════════════════════════════════════════════════════════╗
3
- # ║ cct-reaper.sh — Automatic tmux pane cleanup when agents exit ║
3
+ # ║ sw-reaper.sh — Automatic tmux pane cleanup when agents exit ║
4
4
  # ║ ║
5
5
  # ║ Detects agent panes where the Claude process has exited (shell is ║
6
6
  # ║ idle), kills them after a grace period, and cleans up associated ║
@@ -11,7 +11,9 @@
11
11
  # ║ shipwright reaper --watch Continuous loop (default: 5s) ║
12
12
  # ║ shipwright reaper --dry-run Preview what would be reaped ║
13
13
  # ╚═══════════════════════════════════════════════════════════════════════════╝
14
+ VERSION="1.9.0"
14
15
  set -euo pipefail
16
+ trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
15
17
 
16
18
  # ─── Colors (matches Seth's tmux theme) ────────────────────────────────────
17
19
  CYAN='\033[38;2;0;212;255m'
@@ -35,7 +37,7 @@ VERBOSE=false
35
37
  INTERVAL=5
36
38
  GRACE_PERIOD=15
37
39
  LOG_FILE=""
38
- PID_FILE="${HOME}/.cct-reaper.pid"
40
+ PID_FILE="${HOME}/.sw-reaper.pid"
39
41
 
40
42
  # ─── Parse Args ────────────────────────────────────────────────────────────
41
43
  show_help() {
@@ -65,7 +67,7 @@ show_help() {
65
67
  echo ""
66
68
  echo -e "${BOLD}EXAMPLES${RESET}"
67
69
  echo -e " ${DIM}shipwright reaper --watch --interval 10 --grace-period 30${RESET}"
68
- echo -e " ${DIM}shipwright reaper --watch --log-file ~/.cct-reaper.log &${RESET}"
70
+ echo -e " ${DIM}shipwright reaper --watch --log-file ~/.sw-reaper.log &${RESET}"
69
71
  }
70
72
 
71
73
  while [[ $# -gt 0 ]]; do
@@ -108,7 +110,7 @@ release_pid_lock() {
108
110
  rm -f "$PID_FILE"
109
111
  }
110
112
 
111
- # ─── tmux format string (reused from cct-ps.sh) ──────────────────────────
113
+ # ─── tmux format string (reused from sw-ps.sh) ──────────────────────────
112
114
  # Fields: window_name | pane_title | pane_pid | pane_current_command | pane_active | pane_idle | pane_dead | session:window.pane
113
115
  FORMAT='#{window_name}|#{pane_title}|#{pane_pid}|#{pane_current_command}|#{pane_active}|#{pane_idle}|#{pane_dead}|#{session_name}:#{window_index}.#{pane_index}'
114
116