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.
- package/.claude/agents/code-reviewer.md +90 -0
- package/.claude/agents/devops-engineer.md +142 -0
- package/.claude/agents/pipeline-agent.md +80 -0
- package/.claude/agents/shell-script-specialist.md +150 -0
- package/.claude/agents/test-specialist.md +196 -0
- package/.claude/hooks/post-tool-use.sh +38 -0
- package/.claude/hooks/pre-tool-use.sh +25 -0
- package/.claude/hooks/session-started.sh +37 -0
- package/README.md +212 -814
- package/claude-code/CLAUDE.md.shipwright +54 -0
- package/claude-code/hooks/notify-idle.sh +2 -2
- package/claude-code/hooks/session-start.sh +24 -0
- package/claude-code/hooks/task-completed.sh +6 -2
- package/claude-code/settings.json.template +12 -0
- package/dashboard/public/app.js +4422 -0
- package/dashboard/public/index.html +816 -0
- package/dashboard/public/styles.css +4755 -0
- package/dashboard/server.ts +4315 -0
- package/docs/KNOWN-ISSUES.md +18 -10
- package/docs/TIPS.md +38 -26
- package/docs/patterns/README.md +33 -23
- package/package.json +9 -5
- package/scripts/adapters/iterm2-adapter.sh +1 -1
- package/scripts/adapters/tmux-adapter.sh +52 -23
- package/scripts/adapters/wezterm-adapter.sh +26 -14
- package/scripts/lib/compat.sh +200 -0
- package/scripts/lib/helpers.sh +72 -0
- package/scripts/postinstall.mjs +72 -13
- package/scripts/{cct → sw} +109 -21
- package/scripts/sw-adversarial.sh +274 -0
- package/scripts/sw-architecture-enforcer.sh +330 -0
- package/scripts/sw-checkpoint.sh +390 -0
- package/scripts/{cct-cleanup.sh → sw-cleanup.sh} +3 -1
- package/scripts/sw-connect.sh +619 -0
- package/scripts/{cct-cost.sh → sw-cost.sh} +368 -34
- package/scripts/{cct-daemon.sh → sw-daemon.sh} +2217 -204
- package/scripts/sw-dashboard.sh +477 -0
- package/scripts/sw-developer-simulation.sh +252 -0
- package/scripts/sw-docs.sh +635 -0
- package/scripts/sw-doctor.sh +907 -0
- package/scripts/{cct-fix.sh → sw-fix.sh} +10 -6
- package/scripts/{cct-fleet.sh → sw-fleet.sh} +498 -22
- package/scripts/sw-github-checks.sh +521 -0
- package/scripts/sw-github-deploy.sh +533 -0
- package/scripts/sw-github-graphql.sh +972 -0
- package/scripts/sw-heartbeat.sh +293 -0
- package/scripts/sw-init.sh +522 -0
- package/scripts/sw-intelligence.sh +1196 -0
- package/scripts/sw-jira.sh +643 -0
- package/scripts/sw-launchd.sh +364 -0
- package/scripts/sw-linear.sh +648 -0
- package/scripts/{cct-logs.sh → sw-logs.sh} +72 -2
- package/scripts/{cct-loop.sh → sw-loop.sh} +534 -44
- package/scripts/{cct-memory.sh → sw-memory.sh} +321 -38
- package/scripts/sw-patrol-meta.sh +417 -0
- package/scripts/sw-pipeline-composer.sh +455 -0
- package/scripts/{cct-pipeline.sh → sw-pipeline.sh} +2319 -178
- package/scripts/sw-predictive.sh +820 -0
- package/scripts/{cct-prep.sh → sw-prep.sh} +339 -49
- package/scripts/{cct-ps.sh → sw-ps.sh} +6 -4
- package/scripts/{cct-reaper.sh → sw-reaper.sh} +6 -4
- package/scripts/sw-remote.sh +687 -0
- package/scripts/sw-self-optimize.sh +947 -0
- package/scripts/sw-session.sh +519 -0
- package/scripts/sw-setup.sh +234 -0
- package/scripts/sw-status.sh +605 -0
- package/scripts/{cct-templates.sh → sw-templates.sh} +9 -4
- package/scripts/sw-tmux.sh +591 -0
- package/scripts/sw-tracker-jira.sh +277 -0
- package/scripts/sw-tracker-linear.sh +292 -0
- package/scripts/sw-tracker.sh +409 -0
- package/scripts/{cct-upgrade.sh → sw-upgrade.sh} +103 -46
- package/scripts/{cct-worktree.sh → sw-worktree.sh} +3 -0
- package/templates/pipelines/autonomous.json +27 -5
- package/templates/pipelines/full.json +12 -0
- package/templates/pipelines/standard.json +12 -0
- package/tmux/{claude-teams-overlay.conf → shipwright-overlay.conf} +27 -9
- package/tmux/templates/accessibility.json +34 -0
- package/tmux/templates/api-design.json +35 -0
- package/tmux/templates/architecture.json +1 -0
- package/tmux/templates/bug-fix.json +9 -0
- package/tmux/templates/code-review.json +1 -0
- package/tmux/templates/compliance.json +36 -0
- package/tmux/templates/data-pipeline.json +36 -0
- package/tmux/templates/debt-paydown.json +34 -0
- package/tmux/templates/devops.json +1 -0
- package/tmux/templates/documentation.json +1 -0
- package/tmux/templates/exploration.json +1 -0
- package/tmux/templates/feature-dev.json +1 -0
- package/tmux/templates/full-stack.json +8 -0
- package/tmux/templates/i18n.json +34 -0
- package/tmux/templates/incident-response.json +36 -0
- package/tmux/templates/migration.json +1 -0
- package/tmux/templates/observability.json +35 -0
- package/tmux/templates/onboarding.json +33 -0
- package/tmux/templates/performance.json +35 -0
- package/tmux/templates/refactor.json +1 -0
- package/tmux/templates/release.json +35 -0
- package/tmux/templates/security-audit.json +8 -0
- package/tmux/templates/spike.json +34 -0
- package/tmux/templates/testing.json +1 -0
- package/tmux/tmux.conf +98 -9
- package/scripts/cct-doctor.sh +0 -328
- package/scripts/cct-init.sh +0 -282
- package/scripts/cct-session.sh +0 -284
- 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.
|
|
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/
|
|
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 "<!--
|
|
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 "<!--
|
|
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 '/<!--
|
|
182
|
-
after=$(sed '1,/<!--
|
|
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
|
|
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
|
-
<!--
|
|
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
|
|
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
|
-
$
|
|
915
|
-
$(if [[ -z "$ENTRY_POINTS" ]]; then echo "- No standard entry points detected"; fi)
|
|
916
|
-
|
|
1211
|
+
${entry_section}
|
|
917
1212
|
## Module Map
|
|
918
|
-
$
|
|
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
|
-
$
|
|
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
|
|
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
|
|
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
|
|
1257
|
+
- Use the existing error handling patterns
|
|
964
1258
|
- Always handle promise rejections / async errors
|
|
965
1259
|
- Provide meaningful error messages
|
|
966
|
-
-
|
|
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
|
|
1266
|
+
- Use descriptive test names
|
|
973
1267
|
- Keep tests focused — one assertion per test where practical
|
|
974
1268
|
|
|
975
1269
|
## File Organization
|
|
976
|
-
$
|
|
977
|
-
|
|
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=$(
|
|
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 "<!--
|
|
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
|
-
# ║
|
|
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}
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
# ║
|
|
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}/.
|
|
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 ~/.
|
|
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
|
|
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
|
|