shipwright-cli 1.10.0 → 2.0.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 (108) hide show
  1. package/README.md +114 -36
  2. package/completions/_shipwright +212 -32
  3. package/completions/shipwright.bash +97 -25
  4. package/docs/strategy/01-market-research.md +619 -0
  5. package/docs/strategy/02-mission-and-brand.md +587 -0
  6. package/docs/strategy/03-gtm-and-roadmap.md +759 -0
  7. package/docs/strategy/QUICK-START.txt +289 -0
  8. package/docs/strategy/README.md +172 -0
  9. package/package.json +4 -2
  10. package/scripts/sw +208 -1
  11. package/scripts/sw-activity.sh +500 -0
  12. package/scripts/sw-adaptive.sh +925 -0
  13. package/scripts/sw-adversarial.sh +1 -1
  14. package/scripts/sw-architecture-enforcer.sh +1 -1
  15. package/scripts/sw-auth.sh +613 -0
  16. package/scripts/sw-autonomous.sh +664 -0
  17. package/scripts/sw-changelog.sh +704 -0
  18. package/scripts/sw-checkpoint.sh +1 -1
  19. package/scripts/sw-ci.sh +602 -0
  20. package/scripts/sw-cleanup.sh +1 -1
  21. package/scripts/sw-code-review.sh +637 -0
  22. package/scripts/sw-connect.sh +1 -1
  23. package/scripts/sw-context.sh +605 -0
  24. package/scripts/sw-cost.sh +1 -1
  25. package/scripts/sw-daemon.sh +432 -130
  26. package/scripts/sw-dashboard.sh +1 -1
  27. package/scripts/sw-db.sh +540 -0
  28. package/scripts/sw-decompose.sh +539 -0
  29. package/scripts/sw-deps.sh +551 -0
  30. package/scripts/sw-developer-simulation.sh +1 -1
  31. package/scripts/sw-discovery.sh +412 -0
  32. package/scripts/sw-docs-agent.sh +539 -0
  33. package/scripts/sw-docs.sh +1 -1
  34. package/scripts/sw-doctor.sh +59 -1
  35. package/scripts/sw-dora.sh +615 -0
  36. package/scripts/sw-durable.sh +710 -0
  37. package/scripts/sw-e2e-orchestrator.sh +535 -0
  38. package/scripts/sw-eventbus.sh +393 -0
  39. package/scripts/sw-feedback.sh +471 -0
  40. package/scripts/sw-fix.sh +1 -1
  41. package/scripts/sw-fleet-discover.sh +567 -0
  42. package/scripts/sw-fleet-viz.sh +404 -0
  43. package/scripts/sw-fleet.sh +8 -1
  44. package/scripts/sw-github-app.sh +596 -0
  45. package/scripts/sw-github-checks.sh +1 -1
  46. package/scripts/sw-github-deploy.sh +1 -1
  47. package/scripts/sw-github-graphql.sh +1 -1
  48. package/scripts/sw-guild.sh +569 -0
  49. package/scripts/sw-heartbeat.sh +1 -1
  50. package/scripts/sw-hygiene.sh +559 -0
  51. package/scripts/sw-incident.sh +617 -0
  52. package/scripts/sw-init.sh +88 -1
  53. package/scripts/sw-instrument.sh +699 -0
  54. package/scripts/sw-intelligence.sh +1 -1
  55. package/scripts/sw-jira.sh +1 -1
  56. package/scripts/sw-launchd.sh +363 -28
  57. package/scripts/sw-linear.sh +1 -1
  58. package/scripts/sw-logs.sh +1 -1
  59. package/scripts/sw-loop.sh +64 -3
  60. package/scripts/sw-memory.sh +1 -1
  61. package/scripts/sw-mission-control.sh +487 -0
  62. package/scripts/sw-model-router.sh +545 -0
  63. package/scripts/sw-otel.sh +596 -0
  64. package/scripts/sw-oversight.sh +689 -0
  65. package/scripts/sw-pipeline-composer.sh +1 -1
  66. package/scripts/sw-pipeline-vitals.sh +1 -1
  67. package/scripts/sw-pipeline.sh +687 -24
  68. package/scripts/sw-pm.sh +693 -0
  69. package/scripts/sw-pr-lifecycle.sh +522 -0
  70. package/scripts/sw-predictive.sh +1 -1
  71. package/scripts/sw-prep.sh +1 -1
  72. package/scripts/sw-ps.sh +1 -1
  73. package/scripts/sw-public-dashboard.sh +798 -0
  74. package/scripts/sw-quality.sh +595 -0
  75. package/scripts/sw-reaper.sh +1 -1
  76. package/scripts/sw-recruit.sh +573 -0
  77. package/scripts/sw-regression.sh +642 -0
  78. package/scripts/sw-release-manager.sh +736 -0
  79. package/scripts/sw-release.sh +706 -0
  80. package/scripts/sw-remote.sh +1 -1
  81. package/scripts/sw-replay.sh +520 -0
  82. package/scripts/sw-retro.sh +691 -0
  83. package/scripts/sw-scale.sh +444 -0
  84. package/scripts/sw-security-audit.sh +505 -0
  85. package/scripts/sw-self-optimize.sh +1 -1
  86. package/scripts/sw-session.sh +1 -1
  87. package/scripts/sw-setup.sh +1 -1
  88. package/scripts/sw-standup.sh +712 -0
  89. package/scripts/sw-status.sh +1 -1
  90. package/scripts/sw-strategic.sh +658 -0
  91. package/scripts/sw-stream.sh +450 -0
  92. package/scripts/sw-swarm.sh +583 -0
  93. package/scripts/sw-team-stages.sh +511 -0
  94. package/scripts/sw-templates.sh +1 -1
  95. package/scripts/sw-testgen.sh +515 -0
  96. package/scripts/sw-tmux-pipeline.sh +554 -0
  97. package/scripts/sw-tmux.sh +1 -1
  98. package/scripts/sw-trace.sh +485 -0
  99. package/scripts/sw-tracker-github.sh +188 -0
  100. package/scripts/sw-tracker-jira.sh +172 -0
  101. package/scripts/sw-tracker-linear.sh +251 -0
  102. package/scripts/sw-tracker.sh +117 -2
  103. package/scripts/sw-triage.sh +603 -0
  104. package/scripts/sw-upgrade.sh +1 -1
  105. package/scripts/sw-ux.sh +677 -0
  106. package/scripts/sw-webhook.sh +627 -0
  107. package/scripts/sw-widgets.sh +530 -0
  108. package/scripts/sw-worktree.sh +1 -1
@@ -0,0 +1,539 @@
1
+ #!/usr/bin/env bash
2
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
3
+ # ║ Shipwright Autonomous Docs Agent — Auto-sync README, wiki, API docs ║
4
+ # ║ Change detection · Freshness scoring · Auto-fix stale sections ║
5
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
6
+ set -euo pipefail
7
+ trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
+
9
+ VERSION="2.0.0"
10
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+ REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
+
13
+ # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
14
+ CYAN='\033[38;2;0;212;255m' # #00d4ff — primary accent
15
+ PURPLE='\033[38;2;124;58;237m' # #7c3aed — secondary
16
+ BLUE='\033[38;2;0;102;255m' # #0066ff — tertiary
17
+ GREEN='\033[38;2;74;222;128m' # success
18
+ YELLOW='\033[38;2;250;204;21m' # warning
19
+ RED='\033[38;2;248;113;113m' # error
20
+ DIM='\033[2m'
21
+ BOLD='\033[1m'
22
+ RESET='\033[0m'
23
+
24
+ # ─── Cross-platform compatibility ──────────────────────────────────────────
25
+ # shellcheck source=lib/compat.sh
26
+ [[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
27
+
28
+ # ─── Output Helpers ────────────────────────────────────────────────────────
29
+ info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
30
+ success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
31
+ warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
32
+ error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
33
+
34
+ now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
35
+
36
+ # ─── Event Logging ─────────────────────────────────────────────────────────
37
+ EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
38
+
39
+ emit_event() {
40
+ local event_type="$1"; shift
41
+ mkdir -p "$(dirname "$EVENTS_FILE")"
42
+ local payload="{\"ts\":\"$(now_iso)\",\"type\":\"$event_type\""
43
+ while [[ $# -gt 0 ]]; do
44
+ local key="${1%%=*}" val="${1#*=}"
45
+ val="${val//\"/\\\"}"
46
+ payload="${payload},\"${key}\":\"${val}\""
47
+ shift
48
+ done
49
+ payload="${payload}}"
50
+ echo "$payload" >> "$EVENTS_FILE"
51
+ }
52
+
53
+ # ─── Documentation Agent State ────────────────────────────────────────────
54
+ AGENT_HOME="${HOME}/.shipwright/docs-agent"
55
+ FRESHNESS_DB="${AGENT_HOME}/freshness.json"
56
+
57
+ ensure_agent_dir() {
58
+ mkdir -p "$AGENT_HOME"
59
+ [[ -f "$FRESHNESS_DB" ]] || echo '{}' > "$FRESHNESS_DB"
60
+ }
61
+
62
+ # ─── Change Detection ──────────────────────────────────────────────────────
63
+
64
+ # Detect code changes that affect documentation
65
+ scan_code_changes() {
66
+ local last_commit="${1:-HEAD~1}"
67
+ local current_commit="${2:-HEAD}"
68
+
69
+ if ! git rev-parse "$last_commit" >/dev/null 2>&1; then
70
+ # First run or initial commit
71
+ git diff --name-only HEAD 2>/dev/null | sort || true
72
+ return
73
+ fi
74
+
75
+ git diff --name-only "$last_commit..$current_commit" 2>/dev/null | sort || true
76
+ }
77
+
78
+ # Extract script names, functions, CLI args from code
79
+ extract_script_info() {
80
+ local script="$1"
81
+
82
+ if [[ ! -f "$script" ]]; then
83
+ return 1
84
+ fi
85
+
86
+ local name basename
87
+ basename="$(basename "$script")"
88
+
89
+ # Extract VERSION
90
+ local version
91
+ version=$(grep "^VERSION=" "$script" | head -1 | cut -d'"' -f2)
92
+ [[ -n "$version" ]] || version="unknown"
93
+
94
+ # Extract main functions (function name or case handlers)
95
+ local functions
96
+ functions=$(grep -E "^[a-z_]+\(\)|^\s+[a-z_-]+\)" "$script" | sed 's/().*//' | sort | uniq | tr '\n' ',' | sed 's/,$//')
97
+
98
+ # Line count
99
+ local lines
100
+ lines=$(wc -l < "$script")
101
+
102
+ echo "{\"script\":\"$basename\",\"version\":\"$version\",\"functions\":\"$functions\",\"lines\":$lines}"
103
+ }
104
+
105
+ # ─── Freshness Scoring ────────────────────────────────────────────────────
106
+
107
+ # Score freshness: compare doc update time vs code change time
108
+ score_freshness() {
109
+ local doc_file="$1"
110
+ local code_pattern="$2" # pattern to match related code files
111
+
112
+ if [[ ! -f "$doc_file" ]]; then
113
+ echo "0"
114
+ return
115
+ fi
116
+
117
+ # Find most recent code change matching pattern
118
+ local code_mtime
119
+ code_mtime=$(find "$REPO_DIR" -name "$code_pattern" -type f -exec stat -c %Y {} \; 2>/dev/null | sort -rn | head -1 || echo "0")
120
+
121
+ # Get doc file modification time
122
+ local doc_mtime
123
+ doc_mtime=$(stat -c %Y "$doc_file" 2>/dev/null || echo "0")
124
+
125
+ if [[ "$code_mtime" -eq 0 ]]; then
126
+ echo "100"
127
+ return
128
+ fi
129
+
130
+ if [[ "$doc_mtime" -ge "$code_mtime" ]]; then
131
+ echo "100"
132
+ else
133
+ local diff=$((code_mtime - doc_mtime))
134
+ local days=$((diff / 86400))
135
+
136
+ if [[ $days -eq 0 ]]; then
137
+ echo "95"
138
+ elif [[ $days -le 3 ]]; then
139
+ echo "80"
140
+ elif [[ $days -le 7 ]]; then
141
+ echo "60"
142
+ else
143
+ echo $((100 - (days * 5)))
144
+ fi
145
+ fi
146
+ }
147
+
148
+ # ─── API Reference Generation ─────────────────────────────────────────────
149
+
150
+ # Generate API reference from script help text and argument parsing
151
+ generate_api_reference() {
152
+ local output_file="${1:-${AGENT_HOME}/api-reference.md}"
153
+
154
+ info "Generating API reference from scripts..."
155
+
156
+ local temp_file
157
+ temp_file=$(mktemp)
158
+
159
+ {
160
+ echo "# Shipwright API Reference"
161
+ echo ""
162
+ echo "Generated: $(date -u +%Y-%m-%d\ %H:%M:%SZ)"
163
+ echo ""
164
+
165
+ for script in "$REPO_DIR/scripts"/sw-*.sh; do
166
+ [[ ! -f "$script" ]] && continue
167
+
168
+ local script_name
169
+ script_name=$(basename "$script" .sh)
170
+
171
+ local cmd_name
172
+ cmd_name="${script_name#sw-}"
173
+
174
+ # Extract description (look for comment or first echo)
175
+ local desc
176
+ desc=$(head -5 "$script" | grep -E "^# " | head -1 | sed 's/^# //' || echo "Command: $cmd_name")
177
+
178
+ echo "## \`$cmd_name\`"
179
+ echo ""
180
+ echo "$desc"
181
+ echo ""
182
+
183
+ # Extract usage patterns
184
+ if grep -q "show_help\|usage" "$script"; then
185
+ echo "### Usage"
186
+ echo '```bash'
187
+ grep -A 10 "show_help\|USAGE" "$script" | grep -E "echo|printf" | head -5 | sed 's/.*echo -e *//' | sed "s/'//g" | tr -d '\\' || true
188
+ echo '```'
189
+ echo ""
190
+ fi
191
+ done
192
+ } > "$temp_file"
193
+
194
+ mv "$temp_file" "$output_file"
195
+ success "API reference generated: $output_file"
196
+ emit_event "docs_api_generated" "file=$output_file"
197
+ }
198
+
199
+ # ─── Wiki Generation ──────────────────────────────────────────────────────
200
+
201
+ # Generate/update GitHub wiki pages per module
202
+ generate_wiki_pages() {
203
+ local wiki_dir="${1:-${AGENT_HOME}/wiki}"
204
+
205
+ mkdir -p "$wiki_dir"
206
+ info "Generating wiki pages..."
207
+
208
+ # Create module overview
209
+ local modules_file="${wiki_dir}/Modules.md"
210
+ local temp_file
211
+ temp_file=$(mktemp)
212
+
213
+ {
214
+ echo "# Shipwright Modules"
215
+ echo ""
216
+ echo "## Core Scripts"
217
+ echo ""
218
+
219
+ for script in "$REPO_DIR/scripts"/sw-*.sh; do
220
+ [[ ! -f "$script" ]] && continue
221
+
222
+ local name
223
+ name=$(basename "$script" .sh | sed 's/^sw-//')
224
+ local lines
225
+ lines=$(wc -l < "$script")
226
+
227
+ local desc
228
+ desc=$(sed -n '3p' "$script" | sed 's/^# //' | sed 's/ *$//')
229
+
230
+ echo "### \`$name\`"
231
+ echo ""
232
+ echo "$desc"
233
+ echo ""
234
+ echo "* **File**: \`scripts/$(basename "$script")\`"
235
+ echo "* **Lines**: $lines"
236
+ echo ""
237
+ done
238
+ } > "$temp_file"
239
+
240
+ mv "$temp_file" "$modules_file"
241
+ success "Wiki generated: $wiki_dir"
242
+ emit_event "docs_wiki_generated" "dir=$wiki_dir"
243
+ }
244
+
245
+ # ─── Documentation Impact Analysis ─────────────────────────────────────────
246
+
247
+ # Show documentation impact of recent changes
248
+ analyze_impact() {
249
+ local commit_range="${1:-HEAD~10..HEAD}"
250
+
251
+ info "Analyzing documentation impact for: $commit_range"
252
+ echo ""
253
+
254
+ local changed_scripts
255
+ changed_scripts=$(git diff --name-only "$commit_range" 2>/dev/null | grep "scripts/sw-.*\.sh$" | sort -u)
256
+
257
+ if [[ -z "$changed_scripts" ]]; then
258
+ info "No script changes in range"
259
+ return
260
+ fi
261
+
262
+ {
263
+ echo "## Documentation Impact Summary"
264
+ echo ""
265
+ echo "**Commit Range**: \`$commit_range\`"
266
+ echo "**Analysis Time**: $(now_iso)"
267
+ echo ""
268
+ echo "### Modified Scripts"
269
+ echo ""
270
+
271
+ while IFS= read -r script; do
272
+ [[ -z "$script" ]] && continue
273
+ local basename
274
+ basename="$(basename "$script")"
275
+ echo "* \`$basename\`"
276
+ done <<< "$changed_scripts"
277
+
278
+ echo ""
279
+ echo "### Changes Requiring Documentation Updates"
280
+ echo ""
281
+
282
+ # Check for VERSION changes
283
+ local version_changes
284
+ version_changes=$(git diff "$commit_range" -- "$REPO_DIR/scripts/sw-*.sh" 2>/dev/null | grep "^+VERSION=" | wc -l)
285
+
286
+ if [[ "$version_changes" -gt 0 ]]; then
287
+ echo "* **Version bumps**: $version_changes scripts"
288
+ fi
289
+
290
+ # Check for new functions
291
+ local new_functions
292
+ new_functions=$(git diff "$commit_range" -- "$REPO_DIR/scripts/sw-*.sh" 2>/dev/null | grep "^+[a-z_]*() {" | wc -l)
293
+
294
+ if [[ "$new_functions" -gt 0 ]]; then
295
+ echo "* **New functions**: $new_functions"
296
+ fi
297
+
298
+ # Check for CLI changes
299
+ local cli_changes
300
+ cli_changes=$(git diff "$commit_range" -- "$REPO_DIR/scripts/sw" 2>/dev/null | grep "^[+-].*exec" | wc -l)
301
+
302
+ if [[ "$cli_changes" -gt 0 ]]; then
303
+ echo "* **CLI changes**: $cli_changes routes updated"
304
+ fi
305
+
306
+ echo ""
307
+ }
308
+ }
309
+
310
+ # ─── Coverage Tracking ────────────────────────────────────────────────────
311
+
312
+ # Show documentation coverage metrics
313
+ show_coverage() {
314
+ info "Analyzing documentation coverage..."
315
+ echo ""
316
+
317
+ local total_scripts
318
+ total_scripts=$(find "$REPO_DIR/scripts" -name "sw-*.sh" -type f | wc -l)
319
+
320
+ # Count documented scripts (have AUTO sections or README entries)
321
+ local documented_count=0
322
+ local undocumented_scripts=""
323
+
324
+ for script in "$REPO_DIR/scripts"/sw-*.sh; do
325
+ [[ ! -f "$script" ]] && continue
326
+ local script_name
327
+ script_name=$(basename "$script" .sh | sed 's/^sw-//')
328
+
329
+ if grep -q "$script_name" "$REPO_DIR/.claude/CLAUDE.md" 2>/dev/null; then
330
+ ((documented_count++))
331
+ else
332
+ undocumented_scripts="${undocumented_scripts}${script_name}\\n"
333
+ fi
334
+ done
335
+
336
+ local coverage_pct
337
+ coverage_pct=$((documented_count * 100 / total_scripts))
338
+
339
+ {
340
+ echo "## Documentation Coverage"
341
+ echo ""
342
+ echo "| Metric | Value |"
343
+ echo "|--------|-------|"
344
+ echo "| Total Scripts | $total_scripts |"
345
+ echo "| Documented | $documented_count |"
346
+ echo "| **Coverage** | **${coverage_pct}%** |"
347
+ echo ""
348
+
349
+ if [[ "$undocumented_scripts" != "" ]]; then
350
+ echo "### Undocumented Scripts"
351
+ echo ""
352
+ echo -e "$undocumented_scripts" | while read -r script; do
353
+ [[ -n "$script" ]] && echo "* \`$script\`"
354
+ done
355
+ fi
356
+ }
357
+ }
358
+
359
+ # ─── Scan for Gaps ────────────────────────────────────────────────────────
360
+
361
+ # Scan for documentation gaps and stale sections
362
+ scan_gaps() {
363
+ info "Scanning for documentation gaps..."
364
+ echo ""
365
+ ensure_agent_dir
366
+
367
+ local gaps_found=0
368
+
369
+ # Check README sections
370
+ if [[ -f "$REPO_DIR/README.md" ]]; then
371
+ local readme_sections
372
+ readme_sections=$(grep -o "<!-- AUTO:[a-z0-9_-]* -->" "$REPO_DIR/README.md" | sed 's/<!-- AUTO://;s/ -->//' | sort -u)
373
+
374
+ for section in $readme_sections; do
375
+ local freshness
376
+ freshness=$(score_freshness "$REPO_DIR/README.md" "scripts/sw-*.sh")
377
+
378
+ if [[ "$freshness" -lt 70 ]]; then
379
+ warn "Stale section in README: $section (freshness: ${freshness}%)"
380
+ ((gaps_found++))
381
+ fi
382
+ done
383
+ fi
384
+
385
+ # Check CLAUDE.md
386
+ if [[ -f "$REPO_DIR/.claude/CLAUDE.md" ]]; then
387
+ local claude_sections
388
+ claude_sections=$(grep -o "<!-- AUTO:[a-z0-9_-]* -->" "$REPO_DIR/.claude/CLAUDE.md" 2>/dev/null | sed 's/<!-- AUTO://;s/ -->//' | sort -u)
389
+
390
+ for section in $claude_sections; do
391
+ local freshness
392
+ freshness=$(score_freshness "$REPO_DIR/.claude/CLAUDE.md" "scripts/sw-*.sh")
393
+
394
+ if [[ "$freshness" -lt 70 ]]; then
395
+ warn "Stale section in CLAUDE.md: $section (freshness: ${freshness}%)"
396
+ ((gaps_found++))
397
+ fi
398
+ done
399
+ fi
400
+
401
+ if [[ $gaps_found -eq 0 ]]; then
402
+ success "No stale sections found"
403
+ else
404
+ warn "Found $gaps_found stale sections"
405
+ fi
406
+
407
+ return $((gaps_found > 0 ? 1 : 0))
408
+ }
409
+
410
+ # ─── Auto-Sync ────────────────────────────────────────────────────────────
411
+
412
+ # Auto-update stale documentation sections
413
+ sync_docs() {
414
+ info "Starting documentation sync..."
415
+ ensure_agent_dir
416
+
417
+ local synced_count=0
418
+
419
+ # Regenerate API reference
420
+ generate_api_reference
421
+ ((synced_count++))
422
+
423
+ # Regenerate wiki
424
+ generate_wiki_pages
425
+ ((synced_count++))
426
+
427
+ success "Documentation sync complete ($synced_count updates)"
428
+ emit_event "docs_sync_complete" "updates=$synced_count"
429
+ }
430
+
431
+ # ─── Continuous Watch Mode ────────────────────────────────────────────────
432
+
433
+ # Watch mode: re-scan on file changes
434
+ watch_mode() {
435
+ info "Starting documentation watch mode (Ctrl+C to stop)..."
436
+
437
+ local watch_paths=(
438
+ "$REPO_DIR/scripts/sw-*.sh"
439
+ "$REPO_DIR/README.md"
440
+ "$REPO_DIR/.claude/CLAUDE.md"
441
+ )
442
+
443
+ # Simple polling implementation
444
+ local last_state=""
445
+ local check_interval="${1:-5}" # seconds
446
+
447
+ while true; do
448
+ # Create state string from file modification times
449
+ local current_state=""
450
+ for pattern in "${watch_paths[@]}"; do
451
+ current_state="${current_state}$(find "$REPO_DIR" -path "$pattern" -type f -exec stat -c %Y {} \; 2>/dev/null | sort -n | tail -1 || echo 0),"
452
+ done
453
+
454
+ if [[ "$current_state" != "$last_state" ]]; then
455
+ echo ""
456
+ info "Changes detected, scanning..."
457
+ scan_gaps || true
458
+ last_state="$current_state"
459
+ fi
460
+
461
+ sleep "$check_interval"
462
+ done
463
+ }
464
+
465
+ # ─── Help Text ────────────────────────────────────────────────────────────
466
+
467
+ show_help() {
468
+ cat << 'EOF'
469
+ ╔═══════════════════════════════════════════════════════════════════════════╗
470
+ ║ Autonomous Documentation Agent ║
471
+ ║ Auto-sync README, wiki, API docs, CLAUDE.md — track freshness ║
472
+ ╚═══════════════════════════════════════════════════════════════════════════╝
473
+
474
+ USAGE
475
+ shipwright docs-agent <command> [options]
476
+
477
+ COMMANDS
478
+ scan Scan for documentation gaps and stale sections
479
+ sync Auto-update all stale documentation
480
+ coverage Show documentation coverage metrics
481
+ api Generate API reference from script help text
482
+ wiki Generate/update GitHub wiki pages
483
+ impact Show documentation impact of recent changes
484
+ watch [secs] Continuous mode — re-scan on file changes (default: 5s)
485
+ help Show this help message
486
+
487
+ EXAMPLES
488
+ shipwright docs-agent scan # Check for stale sections
489
+ shipwright docs-agent sync # Auto-update documentation
490
+ shipwright docs-agent coverage # Show coverage metrics
491
+ shipwright docs-agent impact HEAD~5 # Analyze last 5 commits
492
+ shipwright docs-agent watch # Continuous mode (5s polling)
493
+
494
+ EOF
495
+ }
496
+
497
+ # ─── Main Router ──────────────────────────────────────────────────────────
498
+
499
+ main() {
500
+ local cmd="${1:-help}"
501
+ shift 2>/dev/null || true
502
+
503
+ case "$cmd" in
504
+ scan)
505
+ scan_gaps
506
+ ;;
507
+ sync)
508
+ sync_docs
509
+ ;;
510
+ coverage)
511
+ show_coverage
512
+ ;;
513
+ api)
514
+ generate_api_reference "${1:-.}"
515
+ ;;
516
+ wiki)
517
+ generate_wiki_pages "${1:-.}"
518
+ ;;
519
+ impact)
520
+ analyze_impact "${1:-HEAD~10..HEAD}"
521
+ ;;
522
+ watch)
523
+ watch_mode "${1:-5}"
524
+ ;;
525
+ help|--help|-h)
526
+ show_help
527
+ ;;
528
+ *)
529
+ error "Unknown command: $cmd"
530
+ echo ""
531
+ show_help
532
+ exit 1
533
+ ;;
534
+ esac
535
+ }
536
+
537
+ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
538
+ main "$@"
539
+ fi
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="1.10.0"
9
+ VERSION="2.0.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -4,7 +4,7 @@
4
4
  # ║ ║
5
5
  # ║ Checks prerequisites, installed files, PATH, and common issues. ║
6
6
  # ╚═══════════════════════════════════════════════════════════════════════════╝
7
- VERSION="1.10.0"
7
+ VERSION="2.0.0"
8
8
  set -euo pipefail
9
9
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
10
10
 
@@ -885,6 +885,64 @@ else
885
885
  echo -e " ${DIM}Install: brew install gh (macOS) or see https://cli.github.com${RESET}"
886
886
  fi
887
887
 
888
+ # ═════════════════════════════════════════════════════════════════════════════
889
+ # 14. Dashboard & Dependencies
890
+ # ═════════════════════════════════════════════════════════════════════════════
891
+ echo ""
892
+ echo -e "${PURPLE}${BOLD} DASHBOARD & DEPENDENCIES${RESET}"
893
+ echo -e "${DIM} ──────────────────────────────────────────${RESET}"
894
+
895
+ # Bun runtime
896
+ if command -v bun &>/dev/null; then
897
+ bun_ver="$(bun --version 2>/dev/null || echo "unknown")"
898
+ check_pass "bun $bun_ver"
899
+ else
900
+ check_warn "bun not found — required for shipwright dashboard"
901
+ echo -e " ${DIM}Install: curl -fsSL https://bun.sh/install | bash${RESET}"
902
+ fi
903
+
904
+ # Dashboard files — check multiple locations
905
+ _DOCTOR_SCRIPT_DIR="${_DOCTOR_SCRIPT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"
906
+ _DOCTOR_REPO_DIR="$(cd "$_DOCTOR_SCRIPT_DIR/.." 2>/dev/null && pwd 2>/dev/null || echo "")"
907
+ _DASHBOARD_DIR=""
908
+
909
+ if [[ -n "$_DOCTOR_REPO_DIR" && -f "$_DOCTOR_REPO_DIR/dashboard/server.ts" ]]; then
910
+ _DASHBOARD_DIR="$_DOCTOR_REPO_DIR/dashboard"
911
+ elif [[ -f "$HOME/.local/share/shipwright/dashboard/server.ts" ]]; then
912
+ _DASHBOARD_DIR="$HOME/.local/share/shipwright/dashboard"
913
+ fi
914
+
915
+ if [[ -n "$_DASHBOARD_DIR" ]]; then
916
+ check_pass "Dashboard server found: ${DIM}$_DASHBOARD_DIR/server.ts${RESET}"
917
+ if [[ -f "$_DASHBOARD_DIR/public/index.html" ]]; then
918
+ check_pass "Dashboard frontend found"
919
+ else
920
+ check_warn "Dashboard public/index.html not found"
921
+ fi
922
+ else
923
+ check_warn "Dashboard files not found"
924
+ echo -e " ${DIM}Expected: dashboard/server.ts in repo or ~/.local/share/shipwright/dashboard/${RESET}"
925
+ fi
926
+
927
+ # Port 3000 availability
928
+ if command -v lsof &>/dev/null; then
929
+ if lsof -i :3000 -sTCP:LISTEN &>/dev/null 2>&1; then
930
+ dr_port_proc="$(lsof -i :3000 -sTCP:LISTEN -t 2>/dev/null | head -1 || echo "unknown")"
931
+ check_warn "Port 3000 in use (PID: $dr_port_proc) — dashboard may need a different port"
932
+ echo -e " ${DIM}Use: shipwright dashboard start --port 3001${RESET}"
933
+ else
934
+ check_pass "Port 3000 available"
935
+ fi
936
+ elif command -v ss &>/dev/null; then
937
+ if ss -tlnp 2>/dev/null | grep -q ':3000 '; then
938
+ check_warn "Port 3000 in use — dashboard may need a different port"
939
+ else
940
+ check_pass "Port 3000 available"
941
+ fi
942
+ else
943
+ info " Port check skipped ${DIM}(lsof/ss not found)${RESET}"
944
+ fi
945
+
888
946
  # ═════════════════════════════════════════════════════════════════════════════
889
947
  # Summary
890
948
  # ═════════════════════════════════════════════════════════════════════════════