shipwright-cli 2.1.2 → 2.2.1

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 (136) hide show
  1. package/.claude/agents/devops-engineer.md +14 -12
  2. package/.claude/agents/doc-fleet-agent.md +99 -0
  3. package/.claude/agents/test-specialist.md +5 -3
  4. package/README.md +48 -27
  5. package/claude-code/CLAUDE.md.shipwright +2 -2
  6. package/config/policy.json +73 -0
  7. package/config/policy.schema.json +150 -0
  8. package/docs/AGI-PLATFORM-PLAN.md +126 -0
  9. package/docs/AGI-WHATS-NEXT.md +72 -0
  10. package/docs/KNOWN-ISSUES.md +1 -23
  11. package/docs/PLATFORM-TODO-BACKLOG.md +41 -0
  12. package/docs/PLATFORM-TODO-TRIAGE.md +56 -0
  13. package/docs/README.md +83 -0
  14. package/docs/TIPS.md +39 -2
  15. package/docs/config-policy.md +40 -0
  16. package/docs/definition-of-done.example.md +2 -0
  17. package/docs/patterns/README.md +5 -0
  18. package/docs/strategy/02-mission-and-brand.md +3 -3
  19. package/docs/strategy/README.md +4 -3
  20. package/docs/tmux-research/TMUX-AUDIT.md +2 -0
  21. package/docs/tmux-research/TMUX-RESEARCH-INDEX.md +17 -0
  22. package/package.json +3 -2
  23. package/scripts/lib/daemon-health.sh +32 -0
  24. package/scripts/lib/helpers.sh +30 -1
  25. package/scripts/lib/pipeline-detection.sh +278 -0
  26. package/scripts/lib/pipeline-github.sh +196 -0
  27. package/scripts/lib/pipeline-intelligence.sh +1712 -0
  28. package/scripts/lib/pipeline-quality-checks.sh +1052 -0
  29. package/scripts/lib/pipeline-quality.sh +34 -0
  30. package/scripts/lib/pipeline-stages.sh +2488 -0
  31. package/scripts/lib/pipeline-state.sh +529 -0
  32. package/scripts/lib/policy.sh +32 -0
  33. package/scripts/sw +5 -1
  34. package/scripts/sw-activity.sh +35 -46
  35. package/scripts/sw-adaptive.sh +30 -39
  36. package/scripts/sw-adversarial.sh +30 -36
  37. package/scripts/sw-architecture-enforcer.sh +30 -33
  38. package/scripts/sw-auth.sh +30 -42
  39. package/scripts/sw-autonomous.sh +60 -40
  40. package/scripts/sw-changelog.sh +29 -30
  41. package/scripts/sw-checkpoint.sh +30 -18
  42. package/scripts/sw-ci.sh +30 -42
  43. package/scripts/sw-cleanup.sh +32 -15
  44. package/scripts/sw-code-review.sh +26 -32
  45. package/scripts/sw-connect.sh +30 -19
  46. package/scripts/sw-context.sh +30 -19
  47. package/scripts/sw-cost.sh +30 -40
  48. package/scripts/sw-daemon.sh +66 -36
  49. package/scripts/sw-dashboard.sh +31 -40
  50. package/scripts/sw-db.sh +30 -20
  51. package/scripts/sw-decompose.sh +30 -38
  52. package/scripts/sw-deps.sh +30 -41
  53. package/scripts/sw-developer-simulation.sh +30 -36
  54. package/scripts/sw-discovery.sh +36 -19
  55. package/scripts/sw-doc-fleet.sh +822 -0
  56. package/scripts/sw-docs-agent.sh +30 -36
  57. package/scripts/sw-docs.sh +29 -31
  58. package/scripts/sw-doctor.sh +52 -20
  59. package/scripts/sw-dora.sh +29 -34
  60. package/scripts/sw-durable.sh +30 -20
  61. package/scripts/sw-e2e-orchestrator.sh +36 -21
  62. package/scripts/sw-eventbus.sh +30 -17
  63. package/scripts/sw-feedback.sh +30 -41
  64. package/scripts/sw-fix.sh +30 -40
  65. package/scripts/sw-fleet-discover.sh +30 -41
  66. package/scripts/sw-fleet-viz.sh +30 -20
  67. package/scripts/sw-fleet.sh +30 -40
  68. package/scripts/sw-github-app.sh +30 -41
  69. package/scripts/sw-github-checks.sh +30 -41
  70. package/scripts/sw-github-deploy.sh +30 -41
  71. package/scripts/sw-github-graphql.sh +30 -38
  72. package/scripts/sw-guild.sh +30 -37
  73. package/scripts/sw-heartbeat.sh +30 -19
  74. package/scripts/sw-hygiene.sh +134 -42
  75. package/scripts/sw-incident.sh +30 -39
  76. package/scripts/sw-init.sh +31 -14
  77. package/scripts/sw-instrument.sh +30 -41
  78. package/scripts/sw-intelligence.sh +39 -44
  79. package/scripts/sw-jira.sh +31 -41
  80. package/scripts/sw-launchd.sh +30 -17
  81. package/scripts/sw-linear.sh +31 -41
  82. package/scripts/sw-logs.sh +32 -17
  83. package/scripts/sw-loop.sh +32 -19
  84. package/scripts/sw-memory.sh +32 -43
  85. package/scripts/sw-mission-control.sh +31 -40
  86. package/scripts/sw-model-router.sh +30 -20
  87. package/scripts/sw-otel.sh +30 -20
  88. package/scripts/sw-oversight.sh +30 -36
  89. package/scripts/sw-patrol-meta.sh +31 -0
  90. package/scripts/sw-pipeline-composer.sh +30 -39
  91. package/scripts/sw-pipeline-vitals.sh +30 -44
  92. package/scripts/sw-pipeline.sh +277 -6383
  93. package/scripts/sw-pm.sh +31 -41
  94. package/scripts/sw-pr-lifecycle.sh +30 -42
  95. package/scripts/sw-predictive.sh +32 -34
  96. package/scripts/sw-prep.sh +30 -19
  97. package/scripts/sw-ps.sh +32 -17
  98. package/scripts/sw-public-dashboard.sh +30 -40
  99. package/scripts/sw-quality.sh +42 -40
  100. package/scripts/sw-reaper.sh +32 -15
  101. package/scripts/sw-recruit.sh +428 -48
  102. package/scripts/sw-regression.sh +30 -38
  103. package/scripts/sw-release-manager.sh +30 -38
  104. package/scripts/sw-release.sh +29 -31
  105. package/scripts/sw-remote.sh +31 -40
  106. package/scripts/sw-replay.sh +30 -18
  107. package/scripts/sw-retro.sh +33 -42
  108. package/scripts/sw-scale.sh +41 -24
  109. package/scripts/sw-security-audit.sh +30 -20
  110. package/scripts/sw-self-optimize.sh +33 -37
  111. package/scripts/sw-session.sh +31 -15
  112. package/scripts/sw-setup.sh +30 -16
  113. package/scripts/sw-standup.sh +30 -20
  114. package/scripts/sw-status.sh +33 -13
  115. package/scripts/sw-strategic.sh +55 -43
  116. package/scripts/sw-stream.sh +33 -37
  117. package/scripts/sw-swarm.sh +30 -21
  118. package/scripts/sw-team-stages.sh +30 -38
  119. package/scripts/sw-templates.sh +31 -16
  120. package/scripts/sw-testgen.sh +30 -31
  121. package/scripts/sw-tmux-pipeline.sh +29 -31
  122. package/scripts/sw-tmux-role-color.sh +31 -0
  123. package/scripts/sw-tmux-status.sh +31 -0
  124. package/scripts/sw-tmux.sh +31 -15
  125. package/scripts/sw-trace.sh +30 -19
  126. package/scripts/sw-tracker-github.sh +31 -0
  127. package/scripts/sw-tracker-jira.sh +31 -0
  128. package/scripts/sw-tracker-linear.sh +31 -0
  129. package/scripts/sw-tracker.sh +30 -40
  130. package/scripts/sw-triage.sh +68 -61
  131. package/scripts/sw-upgrade.sh +30 -16
  132. package/scripts/sw-ux.sh +30 -35
  133. package/scripts/sw-webhook.sh +30 -25
  134. package/scripts/sw-widgets.sh +30 -19
  135. package/scripts/sw-worktree.sh +32 -15
  136. package/tmux/templates/doc-fleet.json +43 -0
@@ -0,0 +1,822 @@
1
+ #!/usr/bin/env bash
2
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
3
+ # ║ shipwright doc-fleet — Documentation Fleet Orchestrator ║
4
+ # ║ 5 specialized agents: Architect · Claude MD · Strategy · Patterns · README ║
5
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
6
+ set -euo pipefail
7
+ trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
+
9
+ VERSION="2.2.1"
10
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+ REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
+
13
+ # ─── Cross-platform compatibility ──────────────────────────────────────────
14
+ # shellcheck source=lib/compat.sh
15
+ [[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
16
+ # shellcheck source=lib/helpers.sh
17
+ [[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
18
+ # Fallbacks when helpers not loaded
19
+ [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
20
+ [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
21
+ [[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
22
+ [[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
23
+ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
24
+ now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
25
+ now_epoch() { date +%s; }
26
+ fi
27
+ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
28
+ emit_event() {
29
+ local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
30
+ local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
31
+ while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
32
+ echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
33
+ }
34
+ fi
35
+ CYAN="${CYAN:-\033[38;2;0;212;255m}"
36
+ PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
37
+ BLUE="${BLUE:-\033[38;2;0;102;255m}"
38
+ GREEN="${GREEN:-\033[38;2;74;222;128m}"
39
+ YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
40
+ RED="${RED:-\033[38;2;248;113;113m}"
41
+ DIM="${DIM:-\033[2m}"
42
+ BOLD="${BOLD:-\033[1m}"
43
+ RESET="${RESET:-\033[0m}"
44
+
45
+ # ─── Constants ──────────────────────────────────────────────────────────────
46
+ FLEET_HOME="${HOME}/.shipwright/doc-fleet"
47
+ FLEET_STATE="${FLEET_HOME}/state.json"
48
+ FLEET_LOG="${FLEET_HOME}/runs.jsonl"
49
+ FLEET_REPORT_DIR="${FLEET_HOME}/reports"
50
+ MANIFEST_FILE="${REPO_DIR}/.claude/pipeline-artifacts/docs-manifest.json"
51
+
52
+ # Fleet agent definitions (role → focus areas → description)
53
+ FLEET_ROLES="doc-architect claude-md strategy-curator pattern-writer readme-optimizer"
54
+
55
+ # ─── Ensure directories exist ──────────────────────────────────────────────
56
+ ensure_dirs() {
57
+ mkdir -p "$FLEET_HOME" "$FLEET_REPORT_DIR"
58
+ mkdir -p "${REPO_DIR}/.claude/pipeline-artifacts"
59
+ }
60
+
61
+ # ─── Initialize fleet state ────────────────────────────────────────────────
62
+ init_state() {
63
+ if [[ ! -f "$FLEET_STATE" ]]; then
64
+ local tmp_file
65
+ tmp_file=$(mktemp)
66
+ cat > "$tmp_file" << 'JSON'
67
+ {
68
+ "last_run": null,
69
+ "run_count": 0,
70
+ "agents": {},
71
+ "last_audit": null,
72
+ "docs_health_score": 0
73
+ }
74
+ JSON
75
+ mv "$tmp_file" "$FLEET_STATE"
76
+ fi
77
+ }
78
+
79
+ # ─── Audit: Scan documentation health ──────────────────────────────────────
80
+ cmd_audit() {
81
+ ensure_dirs
82
+ init_state
83
+
84
+ echo ""
85
+ echo -e "${PURPLE}${BOLD}╔═══════════════════════════════════════════════════════════════╗${RESET}"
86
+ echo -e "${PURPLE}${BOLD}║ Documentation Fleet — Health Audit ║${RESET}"
87
+ echo -e "${PURPLE}${BOLD}╚═══════════════════════════════════════════════════════════════╝${RESET}"
88
+ echo ""
89
+
90
+ local total_score=0
91
+ local total_checks=0
92
+ local issues_found=0
93
+
94
+ # --- Check 1: Documentation files exist
95
+ info "Checking documentation inventory..."
96
+ local expected_docs="README.md STRATEGY.md CHANGELOG.md docs/TIPS.md docs/KNOWN-ISSUES.md"
97
+ local missing_docs=""
98
+ for doc in $expected_docs; do
99
+ total_checks=$((total_checks + 1))
100
+ if [[ -f "${REPO_DIR}/${doc}" ]]; then
101
+ total_score=$((total_score + 1))
102
+ else
103
+ missing_docs="${missing_docs} ${doc}"
104
+ issues_found=$((issues_found + 1))
105
+ fi
106
+ done
107
+ if [[ -n "$missing_docs" ]]; then
108
+ warn "Missing docs:${missing_docs}"
109
+ else
110
+ success "All expected documentation files present"
111
+ fi
112
+
113
+ # --- Check 2: CLAUDE.md freshness
114
+ info "Checking CLAUDE.md freshness..."
115
+ total_checks=$((total_checks + 1))
116
+ if [[ -f "${REPO_DIR}/.claude/CLAUDE.md" ]]; then
117
+ local claude_age_days=0
118
+ if command -v stat &>/dev/null; then
119
+ local claude_mtime
120
+ claude_mtime=$(stat -f %m "${REPO_DIR}/.claude/CLAUDE.md" 2>/dev/null || stat -c %Y "${REPO_DIR}/.claude/CLAUDE.md" 2>/dev/null || echo "0")
121
+ local now_epoch_val
122
+ now_epoch_val=$(date +%s)
123
+ claude_age_days=$(( (now_epoch_val - claude_mtime) / 86400 ))
124
+ fi
125
+ if [[ $claude_age_days -gt 14 ]]; then
126
+ warn "CLAUDE.md last modified ${claude_age_days} days ago"
127
+ issues_found=$((issues_found + 1))
128
+ else
129
+ total_score=$((total_score + 1))
130
+ success "CLAUDE.md is fresh (${claude_age_days} days old)"
131
+ fi
132
+ else
133
+ warn "No .claude/CLAUDE.md found"
134
+ issues_found=$((issues_found + 1))
135
+ fi
136
+
137
+ # --- Check 3: Agent role definitions
138
+ info "Checking agent role definitions..."
139
+ total_checks=$((total_checks + 1))
140
+ local agent_count=0
141
+ if [[ -d "${REPO_DIR}/.claude/agents" ]]; then
142
+ agent_count=$(ls -1 "${REPO_DIR}/.claude/agents/"*.md 2>/dev/null | wc -l | tr -d ' ')
143
+ fi
144
+ if [[ $agent_count -ge 5 ]]; then
145
+ total_score=$((total_score + 1))
146
+ success "${agent_count} agent role definitions found"
147
+ else
148
+ warn "Only ${agent_count} agent role definitions (expected 5+)"
149
+ issues_found=$((issues_found + 1))
150
+ fi
151
+
152
+ # --- Check 4: AUTO section staleness
153
+ info "Checking AUTO section sync status..."
154
+ total_checks=$((total_checks + 1))
155
+ local docs_script="${SCRIPT_DIR}/sw-docs.sh"
156
+ if [[ -x "$docs_script" ]]; then
157
+ # Run with a 15-second timeout (macOS-compatible)
158
+ local docs_check_ok=false
159
+ bash "$docs_script" check >/dev/null 2>&1 &
160
+ local docs_pid=$!
161
+ local wait_count=0
162
+ while kill -0 "$docs_pid" 2>/dev/null && [[ $wait_count -lt 15 ]]; do
163
+ sleep 1
164
+ wait_count=$((wait_count + 1))
165
+ done
166
+ if kill -0 "$docs_pid" 2>/dev/null; then
167
+ kill "$docs_pid" 2>/dev/null || true
168
+ wait "$docs_pid" 2>/dev/null || true
169
+ warn "AUTO section check timed out (skipped)"
170
+ total_score=$((total_score + 1))
171
+ elif wait "$docs_pid" 2>/dev/null; then
172
+ docs_check_ok=true
173
+ fi
174
+ if [[ "$docs_check_ok" == "true" ]]; then
175
+ total_score=$((total_score + 1))
176
+ success "All AUTO sections are in sync"
177
+ elif [[ $wait_count -lt 15 ]]; then
178
+ warn "Stale AUTO sections detected"
179
+ issues_found=$((issues_found + 1))
180
+ fi
181
+ else
182
+ warn "sw-docs.sh not found — skipping AUTO section check"
183
+ fi
184
+
185
+ # --- Check 5: Docs directory structure
186
+ info "Checking docs directory structure..."
187
+ total_checks=$((total_checks + 1))
188
+ local expected_dirs="docs docs/strategy docs/patterns docs/tmux-research"
189
+ local missing_dirs=""
190
+ for dir in $expected_dirs; do
191
+ if [[ ! -d "${REPO_DIR}/${dir}" ]]; then
192
+ missing_dirs="${missing_dirs} ${dir}"
193
+ fi
194
+ done
195
+ if [[ -n "$missing_dirs" ]]; then
196
+ warn "Missing directories:${missing_dirs}"
197
+ issues_found=$((issues_found + 1))
198
+ else
199
+ total_score=$((total_score + 1))
200
+ success "Documentation directory structure intact"
201
+ fi
202
+
203
+ # --- Check 6: Orphan detection (md files not linked from any index)
204
+ info "Checking for orphan documentation..."
205
+ total_checks=$((total_checks + 1))
206
+ local orphan_count=0
207
+ while IFS= read -r md_file; do
208
+ local basename_file
209
+ basename_file=$(basename "$md_file")
210
+ # Skip index files, changelogs, and license
211
+ case "$basename_file" in
212
+ README.md|CHANGELOG*|LICENSE*|index.md) continue ;;
213
+ esac
214
+ # Check if referenced from any other md file
215
+ local ref_count
216
+ ref_count=$(grep -rl "$basename_file" "${REPO_DIR}"/*.md "${REPO_DIR}"/docs/*.md 2>/dev/null | wc -l | tr -d ' ') || ref_count=0
217
+ if [[ $ref_count -eq 0 ]]; then
218
+ orphan_count=$((orphan_count + 1))
219
+ fi
220
+ done < <(find "${REPO_DIR}/docs" -name "*.md" -maxdepth 1 2>/dev/null || true)
221
+ if [[ $orphan_count -gt 3 ]]; then
222
+ warn "${orphan_count} potentially orphan docs in docs/ (not linked from index)"
223
+ issues_found=$((issues_found + 1))
224
+ else
225
+ total_score=$((total_score + 1))
226
+ success "Orphan check passed (${orphan_count} unlinked docs)"
227
+ fi
228
+
229
+ # --- Check 7: Strategy alignment
230
+ info "Checking strategy document freshness..."
231
+ total_checks=$((total_checks + 1))
232
+ if [[ -f "${REPO_DIR}/STRATEGY.md" ]]; then
233
+ local strategy_lines
234
+ strategy_lines=$(wc -l < "${REPO_DIR}/STRATEGY.md" | tr -d ' ')
235
+ if [[ $strategy_lines -gt 50 ]]; then
236
+ total_score=$((total_score + 1))
237
+ success "STRATEGY.md has substance (${strategy_lines} lines)"
238
+ else
239
+ warn "STRATEGY.md seems thin (${strategy_lines} lines)"
240
+ issues_found=$((issues_found + 1))
241
+ fi
242
+ else
243
+ warn "No STRATEGY.md found"
244
+ issues_found=$((issues_found + 1))
245
+ fi
246
+
247
+ # --- Check 8: Docs-to-code ratio
248
+ info "Checking documentation coverage..."
249
+ total_checks=$((total_checks + 1))
250
+ local doc_files
251
+ doc_files=$(find "${REPO_DIR}" -name "*.md" -not -path "*/node_modules/*" 2>/dev/null | wc -l | tr -d ' ')
252
+ local script_files
253
+ script_files=$(find "${REPO_DIR}/scripts" -name "*.sh" -not -name "*-test.sh" 2>/dev/null | wc -l | tr -d ' ')
254
+ if [[ $script_files -gt 0 ]]; then
255
+ local ratio=$((doc_files * 100 / script_files))
256
+ if [[ $ratio -ge 30 ]]; then
257
+ total_score=$((total_score + 1))
258
+ success "Docs-to-scripts ratio: ${doc_files}/${script_files} (${ratio}%)"
259
+ else
260
+ warn "Low docs-to-scripts ratio: ${doc_files}/${script_files} (${ratio}%)"
261
+ issues_found=$((issues_found + 1))
262
+ fi
263
+ else
264
+ total_score=$((total_score + 1))
265
+ fi
266
+
267
+ # --- Summary
268
+ echo ""
269
+ local health_pct=0
270
+ if [[ $total_checks -gt 0 ]]; then
271
+ health_pct=$((total_score * 100 / total_checks))
272
+ fi
273
+
274
+ local color="$RED"
275
+ if [[ $health_pct -ge 90 ]]; then
276
+ color="$GREEN"
277
+ elif [[ $health_pct -ge 70 ]]; then
278
+ color="$YELLOW"
279
+ fi
280
+
281
+ echo -e "${BOLD}Documentation Health Score: ${color}${health_pct}%${RESET} (${total_score}/${total_checks} checks passed)"
282
+ echo -e "${DIM}Issues found: ${issues_found}${RESET}"
283
+ echo ""
284
+
285
+ # Update state
286
+ local tmp_file
287
+ tmp_file=$(mktemp)
288
+ jq --arg ts "$(now_iso)" \
289
+ --argjson score "$health_pct" \
290
+ --argjson issues "$issues_found" \
291
+ '.last_audit = $ts | .docs_health_score = $score | .last_issues_found = $issues' \
292
+ "$FLEET_STATE" > "$tmp_file" 2>/dev/null || echo '{}' > "$tmp_file"
293
+ mv "$tmp_file" "$FLEET_STATE"
294
+
295
+ emit_event "doc_fleet.audit" "health_score=${health_pct}" "issues=${issues_found}" "checks=${total_checks}"
296
+ return 0
297
+ }
298
+
299
+ # ─── Launch: Spawn the documentation fleet ──────────────────────────────────
300
+ cmd_launch() {
301
+ local mode="${1:-}"
302
+ shift 2>/dev/null || true
303
+ local specific_role="${1:-}"
304
+ shift 2>/dev/null || true
305
+
306
+ ensure_dirs
307
+ init_state
308
+
309
+ echo ""
310
+ echo -e "${PURPLE}${BOLD}╔═══════════════════════════════════════════════════════════════╗${RESET}"
311
+ echo -e "${PURPLE}${BOLD}║ Documentation Fleet — Launch ║${RESET}"
312
+ echo -e "${PURPLE}${BOLD}╚═══════════════════════════════════════════════════════════════╝${RESET}"
313
+ echo ""
314
+
315
+ local roles_to_launch="$FLEET_ROLES"
316
+ if [[ -n "$specific_role" ]]; then
317
+ # Validate role
318
+ local valid=false
319
+ for r in $FLEET_ROLES; do
320
+ if [[ "$r" == "$specific_role" ]]; then
321
+ valid=true
322
+ break
323
+ fi
324
+ done
325
+ if [[ "$valid" != "true" ]]; then
326
+ error "Unknown role: $specific_role"
327
+ echo -e " Valid roles: ${CYAN}${FLEET_ROLES}${RESET}"
328
+ return 1
329
+ fi
330
+ roles_to_launch="$specific_role"
331
+ fi
332
+
333
+ local run_id
334
+ run_id="docfleet-$(date +%s)-$((RANDOM % 10000))"
335
+ local spawned=0
336
+
337
+ info "Run ID: ${CYAN}${run_id}${RESET}"
338
+ info "Mode: ${CYAN}${mode:-full}${RESET}"
339
+ echo ""
340
+
341
+ for role in $roles_to_launch; do
342
+ local agent_goal=""
343
+ local agent_focus=""
344
+
345
+ case "$role" in
346
+ doc-architect)
347
+ agent_goal="Audit the full documentation tree structure. Find duplicates, orphans, missing cross-links. Create/update index files. Produce a docs manifest. Focus: docs/, .claude/, README.md, STRATEGY.md"
348
+ agent_focus="docs/ .claude/ README.md STRATEGY.md"
349
+ ;;
350
+ claude-md)
351
+ agent_goal="Audit all CLAUDE.md files and agent role definitions. Ensure AUTO sections are current, command tables accurate, development guidelines match reality. Remove stale content. Focus: .claude/CLAUDE.md, .claude/agents/, claude-code/"
352
+ agent_focus=".claude/CLAUDE.md .claude/agents/ claude-code/"
353
+ ;;
354
+ strategy-curator)
355
+ agent_goal="Audit strategic docs — STRATEGY.md, AGI-PLATFORM-PLAN.md, AGI-WHATS-NEXT.md, PLATFORM-TODO-BACKLOG.md, docs/strategy/. Mark completed items done, update metrics, remove aspirational content that is now reality. Focus: STRATEGY.md, docs/AGI-*, docs/PLATFORM-*, docs/strategy/"
356
+ agent_focus="STRATEGY.md docs/AGI-PLATFORM-PLAN.md docs/AGI-WHATS-NEXT.md docs/PLATFORM-TODO-BACKLOG.md docs/strategy/"
357
+ ;;
358
+ pattern-writer)
359
+ agent_goal="Audit developer guides and patterns. Update docs/patterns/ for accuracy, refresh TIPS.md with recent learnings, remove resolved items from KNOWN-ISSUES.md, verify config-policy.md matches actual schema. Focus: docs/patterns/, docs/TIPS.md, docs/KNOWN-ISSUES.md, docs/config-policy.md, docs/tmux-research/"
360
+ agent_focus="docs/patterns/ docs/TIPS.md docs/KNOWN-ISSUES.md docs/config-policy.md docs/tmux-research/"
361
+ ;;
362
+ readme-optimizer)
363
+ agent_goal="Audit the README.md and public-facing documentation. Verify command tables match actual CLI, install instructions work, badges and links are valid. Optimize for scannability. Focus: README.md, install.sh, .github/pull_request_template.md"
364
+ agent_focus="README.md install.sh .github/pull_request_template.md"
365
+ ;;
366
+ esac
367
+
368
+ info "Spawning ${CYAN}${role}${RESET}..."
369
+
370
+ if [[ "$mode" == "--dry-run" ]]; then
371
+ echo -e " ${DIM}[dry-run] Would spawn ${role}${RESET}"
372
+ echo -e " ${DIM}Goal: ${agent_goal}${RESET}"
373
+ echo ""
374
+ spawned=$((spawned + 1))
375
+ continue
376
+ fi
377
+
378
+ # Spawn via tmux if available
379
+ if command -v tmux &>/dev/null; then
380
+ local session_name="docfleet-${role}"
381
+
382
+ # Kill existing session for this role if present
383
+ tmux kill-session -t "$session_name" 2>/dev/null || true
384
+
385
+ # Write agent brief to a file so tmux doesn't mangle the goal text
386
+ local brief_file="${FLEET_HOME}/${role}-brief.md"
387
+ local brief_tmp
388
+ brief_tmp=$(mktemp)
389
+ cat > "$brief_tmp" << BRIEF
390
+ # Doc Fleet Agent: ${role}
391
+
392
+ ## Goal
393
+
394
+ ${agent_goal}
395
+
396
+ ## Focus Files
397
+
398
+ ${agent_focus}
399
+
400
+ ## Instructions
401
+
402
+ Read .claude/agents/doc-fleet-agent.md for your full role definition.
403
+ Run: claude --print .claude/agents/doc-fleet-agent.md to review before starting.
404
+ BRIEF
405
+ mv "$brief_tmp" "$brief_file"
406
+
407
+ if [[ "$mode" == "--autonomous" ]] && [[ -x "${SCRIPT_DIR}/sw-loop.sh" ]]; then
408
+ # Launch via loop harness for autonomous mode
409
+ tmux new-session -d -s "$session_name" -c "$REPO_DIR" \
410
+ "bash \"${SCRIPT_DIR}/sw-loop.sh\" \"${agent_goal}\" --max-iterations 10 --roles docs" 2>/dev/null || true
411
+ success "Autonomous agent: ${CYAN}${session_name}${RESET} (loop mode)"
412
+ else
413
+ # Interactive mode: show the brief and wait for user to attach
414
+ tmux new-session -d -s "$session_name" -c "$REPO_DIR" 2>/dev/null || true
415
+ # Send the brief display commands
416
+ tmux send-keys -t "$session_name" "cat \"${brief_file}\" && echo '' && echo 'Ready — start Claude Code in this session to begin work.'" Enter 2>/dev/null || true
417
+ success "Tmux session: ${CYAN}${session_name}${RESET}"
418
+ fi
419
+ fi
420
+
421
+ spawned=$((spawned + 1))
422
+ echo ""
423
+ done
424
+
425
+ # Record run
426
+ local tmp_file
427
+ tmp_file=$(mktemp)
428
+ jq --arg ts "$(now_iso)" \
429
+ --arg run_id "$run_id" \
430
+ --argjson count "$spawned" \
431
+ --arg mode "${mode:-full}" \
432
+ '.last_run = $ts | .run_count += 1' \
433
+ "$FLEET_STATE" > "$tmp_file" 2>/dev/null || echo '{}' > "$tmp_file"
434
+ mv "$tmp_file" "$FLEET_STATE"
435
+
436
+ local log_entry
437
+ log_entry=$(jq -c -n \
438
+ --arg ts "$(now_iso)" \
439
+ --arg run_id "$run_id" \
440
+ --argjson agents_spawned "$spawned" \
441
+ --arg mode "${mode:-full}" \
442
+ '{ts: $ts, run_id: $run_id, agents_spawned: $agents_spawned, mode: $mode}')
443
+ echo "$log_entry" >> "$FLEET_LOG"
444
+
445
+ emit_event "doc_fleet.launch" "run_id=${run_id}" "agents=${spawned}" "mode=${mode:-full}"
446
+
447
+ echo -e "${GREEN}${BOLD}Fleet launched: ${spawned} agents${RESET}"
448
+ echo ""
449
+ echo -e " ${DIM}Monitor: ${CYAN}shipwright doc-fleet status${RESET}"
450
+ echo -e " ${DIM}Retire: ${CYAN}shipwright doc-fleet retire${RESET}"
451
+ echo ""
452
+ }
453
+
454
+ # ─── Status: Show fleet agent status ────────────────────────────────────────
455
+ cmd_status() {
456
+ ensure_dirs
457
+ init_state
458
+
459
+ echo ""
460
+ echo -e "${PURPLE}${BOLD}╔═══════════════════════════════════════════════════════════════╗${RESET}"
461
+ echo -e "${PURPLE}${BOLD}║ Documentation Fleet — Status ║${RESET}"
462
+ echo -e "${PURPLE}${BOLD}╚═══════════════════════════════════════════════════════════════╝${RESET}"
463
+ echo ""
464
+
465
+ # Show state
466
+ local last_run last_audit health_score run_count
467
+ last_run=$(jq -r '.last_run // "never"' "$FLEET_STATE" 2>/dev/null) || last_run="never"
468
+ last_audit=$(jq -r '.last_audit // "never"' "$FLEET_STATE" 2>/dev/null) || last_audit="never"
469
+ health_score=$(jq -r '.docs_health_score // 0' "$FLEET_STATE" 2>/dev/null) || health_score=0
470
+ run_count=$(jq -r '.run_count // 0' "$FLEET_STATE" 2>/dev/null) || run_count=0
471
+
472
+ echo -e " Last run: ${CYAN}${last_run}${RESET}"
473
+ echo -e " Last audit: ${CYAN}${last_audit}${RESET}"
474
+ echo -e " Health score: ${CYAN}${health_score}%${RESET}"
475
+ echo -e " Total runs: ${CYAN}${run_count}${RESET}"
476
+ echo ""
477
+
478
+ # Show tmux sessions
479
+ info "Active Doc Fleet Sessions:"
480
+ echo ""
481
+
482
+ local active=0
483
+ for role in $FLEET_ROLES; do
484
+ local session_name="docfleet-${role}"
485
+ if command -v tmux &>/dev/null && tmux has-session -t "$session_name" 2>/dev/null; then
486
+ echo -e " ${GREEN}●${RESET} ${CYAN}${role}${RESET} → tmux session: ${DIM}${session_name}${RESET}"
487
+ active=$((active + 1))
488
+ else
489
+ echo -e " ${DIM}○${RESET} ${DIM}${role}${RESET} → ${DIM}not running${RESET}"
490
+ fi
491
+ done
492
+
493
+ echo ""
494
+ echo -e " Active agents: ${CYAN}${active}${RESET} / ${#FLEET_ROLES}"
495
+ echo ""
496
+
497
+ # Show recent runs
498
+ if [[ -f "$FLEET_LOG" ]] && [[ -s "$FLEET_LOG" ]]; then
499
+ info "Recent Runs:"
500
+ echo ""
501
+ tail -5 "$FLEET_LOG" | while IFS= read -r line; do
502
+ local ts run_id agents_spawned mode
503
+ ts=$(echo "$line" | jq -r '.ts // "?"' 2>/dev/null) || ts="?"
504
+ run_id=$(echo "$line" | jq -r '.run_id // "?"' 2>/dev/null) || run_id="?"
505
+ agents_spawned=$(echo "$line" | jq -r '.agents_spawned // 0' 2>/dev/null) || agents_spawned=0
506
+ mode=$(echo "$line" | jq -r '.mode // "?"' 2>/dev/null) || mode="?"
507
+ echo -e " ${DIM}${ts}${RESET} ${CYAN}${run_id}${RESET} agents=${agents_spawned} mode=${mode}"
508
+ done
509
+ echo ""
510
+ fi
511
+ }
512
+
513
+ # ─── Retire: Tear down fleet sessions ──────────────────────────────────────
514
+ cmd_retire() {
515
+ local specific_role="${1:-}"
516
+ shift 2>/dev/null || true
517
+
518
+ ensure_dirs
519
+
520
+ echo ""
521
+ info "Retiring doc fleet agents..."
522
+ echo ""
523
+
524
+ local roles_to_retire="$FLEET_ROLES"
525
+ if [[ -n "$specific_role" ]]; then
526
+ roles_to_retire="$specific_role"
527
+ fi
528
+
529
+ local retired=0
530
+ for role in $roles_to_retire; do
531
+ local session_name="docfleet-${role}"
532
+ if command -v tmux &>/dev/null && tmux has-session -t "$session_name" 2>/dev/null; then
533
+ tmux kill-session -t "$session_name" 2>/dev/null && \
534
+ success "Retired: ${CYAN}${role}${RESET}" || \
535
+ warn "Failed to retire: ${role}"
536
+ retired=$((retired + 1))
537
+ else
538
+ echo -e " ${DIM}${role} — not running${RESET}"
539
+ fi
540
+ done
541
+
542
+ echo ""
543
+ success "Retired ${retired} agents"
544
+ emit_event "doc_fleet.retire" "agents_retired=${retired}"
545
+ echo ""
546
+ }
547
+
548
+ # ─── Manifest: Generate documentation manifest ─────────────────────────────
549
+ cmd_manifest() {
550
+ ensure_dirs
551
+
552
+ echo ""
553
+ info "Generating documentation manifest..."
554
+ echo ""
555
+
556
+ local tmp_file
557
+ tmp_file=$(mktemp)
558
+
559
+ # Build manifest JSON
560
+ local docs_json="[]"
561
+ while IFS= read -r md_file; do
562
+ local rel_path="${md_file#${REPO_DIR}/}"
563
+ local line_count
564
+ line_count=$(wc -l < "$md_file" | tr -d ' ')
565
+ local title=""
566
+ # Extract first heading
567
+ title=$(grep -m1 '^#' "$md_file" 2>/dev/null | sed 's/^#* //' || echo "$rel_path")
568
+ local mtime
569
+ mtime=$(stat -f %m "$md_file" 2>/dev/null || stat -c %Y "$md_file" 2>/dev/null || echo "0")
570
+
571
+ # Determine audience
572
+ local audience="contributor"
573
+ case "$rel_path" in
574
+ README.md|install.sh) audience="user" ;;
575
+ .claude/*) audience="agent" ;;
576
+ docs/strategy/*) audience="stakeholder" ;;
577
+ docs/patterns/*) audience="contributor" ;;
578
+ STRATEGY.md) audience="stakeholder" ;;
579
+ esac
580
+
581
+ docs_json=$(echo "$docs_json" | jq \
582
+ --arg path "$rel_path" \
583
+ --arg title "$title" \
584
+ --argjson lines "$line_count" \
585
+ --arg mtime "$mtime" \
586
+ --arg audience "$audience" \
587
+ '. += [{"path": $path, "title": $title, "lines": $lines, "last_modified": $mtime, "audience": $audience}]')
588
+ done < <(find "${REPO_DIR}" -name "*.md" \
589
+ -not -path "*/node_modules/*" \
590
+ -not -path "*/.git/*" \
591
+ 2>/dev/null | sort)
592
+
593
+ local doc_count
594
+ doc_count=$(echo "$docs_json" | jq 'length')
595
+
596
+ jq -n \
597
+ --arg ts "$(now_iso)" \
598
+ --argjson docs "$docs_json" \
599
+ --argjson count "$doc_count" \
600
+ '{
601
+ generated_at: $ts,
602
+ total_documents: $count,
603
+ documents: $docs,
604
+ audiences: ["user", "contributor", "agent", "stakeholder", "operator"],
605
+ structure: {
606
+ root: ["README.md", "STRATEGY.md", "CHANGELOG.md"],
607
+ claude: [".claude/CLAUDE.md", ".claude/agents/"],
608
+ docs: ["docs/strategy/", "docs/patterns/", "docs/tmux-research/"],
609
+ config: ["docs/config-policy.md", "config/policy.json"]
610
+ }
611
+ }' > "$tmp_file"
612
+
613
+ mv "$tmp_file" "$MANIFEST_FILE"
614
+ success "Manifest written to ${CYAN}.claude/pipeline-artifacts/docs-manifest.json${RESET}"
615
+ echo -e " ${DIM}Total documents: ${doc_count}${RESET}"
616
+ echo ""
617
+
618
+ emit_event "doc_fleet.manifest" "doc_count=${doc_count}"
619
+ }
620
+
621
+ # ─── Report: Generate comprehensive documentation report ────────────────────
622
+ cmd_report() {
623
+ local format="${1:-text}"
624
+ shift 2>/dev/null || true
625
+
626
+ ensure_dirs
627
+ init_state
628
+
629
+ echo ""
630
+ echo -e "${PURPLE}${BOLD}╔═══════════════════════════════════════════════════════════════╗${RESET}"
631
+ echo -e "${PURPLE}${BOLD}║ Documentation Fleet — Report ║${RESET}"
632
+ echo -e "${PURPLE}${BOLD}╚═══════════════════════════════════════════════════════════════╝${RESET}"
633
+ echo ""
634
+
635
+ # Count files by category
636
+ local root_docs=0 claude_docs=0 docs_dir=0 agent_defs=0 pattern_docs=0 strategy_docs=0 tmux_docs=0
637
+
638
+ root_docs=$(find "${REPO_DIR}" -maxdepth 1 -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
639
+ claude_docs=$(find "${REPO_DIR}/.claude" -maxdepth 1 -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
640
+ agent_defs=$(find "${REPO_DIR}/.claude/agents" -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
641
+ docs_dir=$(find "${REPO_DIR}/docs" -maxdepth 1 -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
642
+ pattern_docs=$(find "${REPO_DIR}/docs/patterns" -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
643
+ strategy_docs=$(find "${REPO_DIR}/docs/strategy" -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
644
+ tmux_docs=$(find "${REPO_DIR}/docs/tmux-research" -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
645
+
646
+ info "Documentation Inventory"
647
+ echo ""
648
+ echo -e " Root documents: ${CYAN}${root_docs}${RESET}"
649
+ echo -e " .claude/ documents: ${CYAN}${claude_docs}${RESET}"
650
+ echo -e " Agent definitions: ${CYAN}${agent_defs}${RESET}"
651
+ echo -e " docs/ documents: ${CYAN}${docs_dir}${RESET}"
652
+ echo -e " Pattern guides: ${CYAN}${pattern_docs}${RESET}"
653
+ echo -e " Strategy documents: ${CYAN}${strategy_docs}${RESET}"
654
+ echo -e " tmux documentation: ${CYAN}${tmux_docs}${RESET}"
655
+ echo ""
656
+
657
+ local total=$((root_docs + claude_docs + agent_defs + docs_dir + pattern_docs + strategy_docs + tmux_docs))
658
+ echo -e " ${BOLD}Total documentation files: ${CYAN}${total}${RESET}"
659
+ echo ""
660
+
661
+ # Line count totals
662
+ info "Documentation Volume"
663
+ echo ""
664
+ local total_lines=0
665
+ while IFS= read -r md_file; do
666
+ local lines
667
+ lines=$(wc -l < "$md_file" | tr -d ' ')
668
+ total_lines=$((total_lines + lines))
669
+ done < <(find "${REPO_DIR}" -name "*.md" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null)
670
+ echo -e " Total documentation lines: ${CYAN}${total_lines}${RESET}"
671
+ echo ""
672
+
673
+ # Fleet state
674
+ info "Fleet State"
675
+ echo ""
676
+ local health_score
677
+ health_score=$(jq -r '.docs_health_score // 0' "$FLEET_STATE" 2>/dev/null) || health_score=0
678
+ local last_audit
679
+ last_audit=$(jq -r '.last_audit // "never"' "$FLEET_STATE" 2>/dev/null) || last_audit="never"
680
+ echo -e " Health score: ${CYAN}${health_score}%${RESET}"
681
+ echo -e " Last audit: ${CYAN}${last_audit}${RESET}"
682
+ echo ""
683
+
684
+ # JSON output if requested
685
+ if [[ "$format" == "--json" || "$format" == "json" ]]; then
686
+ local report_file="${FLEET_REPORT_DIR}/report-$(date +%Y%m%d-%H%M%S).json"
687
+ jq -n \
688
+ --arg ts "$(now_iso)" \
689
+ --argjson root "$root_docs" \
690
+ --argjson claude "$claude_docs" \
691
+ --argjson agents "$agent_defs" \
692
+ --argjson docs "$docs_dir" \
693
+ --argjson patterns "$pattern_docs" \
694
+ --argjson strategy "$strategy_docs" \
695
+ --argjson tmux "$tmux_docs" \
696
+ --argjson total "$total" \
697
+ --argjson total_lines "$total_lines" \
698
+ --argjson health "$health_score" \
699
+ '{
700
+ generated_at: $ts,
701
+ inventory: {
702
+ root: $root, claude: $claude, agent_defs: $agents,
703
+ docs: $docs, patterns: $patterns, strategy: $strategy, tmux: $tmux,
704
+ total: $total
705
+ },
706
+ volume: { total_lines: $total_lines },
707
+ health: { score: $health }
708
+ }' > "$report_file"
709
+ success "JSON report: ${CYAN}${report_file}${RESET}"
710
+ fi
711
+
712
+ emit_event "doc_fleet.report" "total_docs=${total}" "total_lines=${total_lines}" "health=${health_score}"
713
+ }
714
+
715
+ # ─── Roles: List available fleet roles ──────────────────────────────────────
716
+ cmd_roles() {
717
+ echo ""
718
+ info "Documentation Fleet Roles"
719
+ echo ""
720
+ echo -e " ${CYAN}doc-architect${RESET} Documentation structure, information architecture, cross-linking"
721
+ echo -e " ${CYAN}claude-md${RESET} CLAUDE.md files, agent role definitions, AUTO sections"
722
+ echo -e " ${CYAN}strategy-curator${RESET} Strategic docs, plans, AGI roadmap, backlog triage"
723
+ echo -e " ${CYAN}pattern-writer${RESET} Developer guides, patterns, tips, known issues"
724
+ echo -e " ${CYAN}readme-optimizer${RESET} README, onboarding, install flow, command tables"
725
+ echo ""
726
+ echo -e " ${DIM}Launch specific: ${CYAN}shipwright doc-fleet launch --role <name>${RESET}"
727
+ echo -e " ${DIM}Launch all: ${CYAN}shipwright doc-fleet launch${RESET}"
728
+ echo ""
729
+ }
730
+
731
+ # ─── Help ───────────────────────────────────────────────────────────────────
732
+ cmd_help() {
733
+ echo ""
734
+ echo -e "${PURPLE}${BOLD}╔═══════════════════════════════════════════════════════════════╗${RESET}"
735
+ echo -e "${PURPLE}${BOLD}║ shipwright doc-fleet — Documentation Fleet Orchestrator ║${RESET}"
736
+ echo -e "${PURPLE}${BOLD}╚═══════════════════════════════════════════════════════════════╝${RESET}"
737
+ echo ""
738
+ echo -e " 5 specialized agents for documentation refactoring, cleanup, and enhancement."
739
+ echo ""
740
+ echo -e " ${BOLD}COMMANDS${RESET}"
741
+ echo ""
742
+ echo -e " ${CYAN}audit${RESET} Run documentation health audit (no agents needed)"
743
+ echo -e " ${CYAN}launch${RESET} Spawn all 5 doc fleet agents in tmux"
744
+ echo -e " ${CYAN}launch --autonomous${RESET} Launch agents in autonomous loop mode"
745
+ echo -e " ${CYAN}launch --dry-run${RESET} Preview what would launch without spawning"
746
+ echo -e " ${CYAN}launch --role <r>${RESET} Launch a specific role only"
747
+ echo -e " ${CYAN}status${RESET} Show fleet agent status and recent runs"
748
+ echo -e " ${CYAN}retire${RESET} Tear down all fleet sessions"
749
+ echo -e " ${CYAN}retire <role>${RESET} Retire a specific agent"
750
+ echo -e " ${CYAN}manifest${RESET} Generate docs-manifest.json"
751
+ echo -e " ${CYAN}report${RESET} Comprehensive documentation report"
752
+ echo -e " ${CYAN}report --json${RESET} Report with JSON output"
753
+ echo -e " ${CYAN}roles${RESET} List available fleet roles"
754
+ echo -e " ${CYAN}help${RESET} Show this help"
755
+ echo ""
756
+ echo -e " ${BOLD}FLEET ROLES${RESET}"
757
+ echo ""
758
+ echo -e " ${CYAN}doc-architect${RESET} Docs structure, info architecture, cross-linking, manifest"
759
+ echo -e " ${CYAN}claude-md${RESET} CLAUDE.md, agent roles, AUTO sections, dev guidelines"
760
+ echo -e " ${CYAN}strategy-curator${RESET} Strategy, AGI plan, backlog, metrics, roadmap"
761
+ echo -e " ${CYAN}pattern-writer${RESET} Patterns, tips, known issues, policy docs, tmux docs"
762
+ echo -e " ${CYAN}readme-optimizer${RESET} README, onboarding, install, commands, public docs"
763
+ echo ""
764
+ echo -e " ${BOLD}EXAMPLES${RESET}"
765
+ echo ""
766
+ echo -e " ${DIM}# Quick health check${RESET}"
767
+ echo -e " shipwright doc-fleet audit"
768
+ echo ""
769
+ echo -e " ${DIM}# Launch full fleet for comprehensive docs overhaul${RESET}"
770
+ echo -e " shipwright doc-fleet launch"
771
+ echo ""
772
+ echo -e " ${DIM}# Launch just the README optimizer${RESET}"
773
+ echo -e " shipwright doc-fleet launch --role readme-optimizer"
774
+ echo ""
775
+ echo -e " ${DIM}# Autonomous mode — agents run via loop harness${RESET}"
776
+ echo -e " shipwright doc-fleet launch --autonomous"
777
+ echo ""
778
+ echo -e " ${DIM}# Check agent status and monitor${RESET}"
779
+ echo -e " shipwright doc-fleet status"
780
+ echo ""
781
+ echo -e " ${DIM}# Generate documentation inventory manifest${RESET}"
782
+ echo -e " shipwright doc-fleet manifest"
783
+ echo ""
784
+ }
785
+
786
+ # ─── Main ───────────────────────────────────────────────────────────────────
787
+ main() {
788
+ local cmd="${1:-help}"
789
+ shift 2>/dev/null || true
790
+
791
+ case "$cmd" in
792
+ audit) cmd_audit "$@" ;;
793
+ launch|start|spawn)
794
+ # Handle --role flag
795
+ local mode="" specific_role=""
796
+ while [[ $# -gt 0 ]]; do
797
+ case "$1" in
798
+ --dry-run) mode="--dry-run"; shift ;;
799
+ --autonomous) mode="--autonomous"; shift ;;
800
+ --role) shift; specific_role="${1:-}"; shift 2>/dev/null || true ;;
801
+ *) shift ;;
802
+ esac
803
+ done
804
+ cmd_launch "$mode" "$specific_role"
805
+ ;;
806
+ status) cmd_status "$@" ;;
807
+ retire|stop) cmd_retire "$@" ;;
808
+ manifest) cmd_manifest "$@" ;;
809
+ report) cmd_report "$@" ;;
810
+ roles) cmd_roles "$@" ;;
811
+ help|--help|-h) cmd_help ;;
812
+ *)
813
+ error "Unknown command: $cmd"
814
+ cmd_help
815
+ return 1
816
+ ;;
817
+ esac
818
+ }
819
+
820
+ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
821
+ main "$@"
822
+ fi