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.
- package/.claude/agents/devops-engineer.md +14 -12
- package/.claude/agents/doc-fleet-agent.md +99 -0
- package/.claude/agents/test-specialist.md +5 -3
- package/README.md +48 -27
- package/claude-code/CLAUDE.md.shipwright +2 -2
- package/config/policy.json +73 -0
- package/config/policy.schema.json +150 -0
- package/docs/AGI-PLATFORM-PLAN.md +126 -0
- package/docs/AGI-WHATS-NEXT.md +72 -0
- package/docs/KNOWN-ISSUES.md +1 -23
- package/docs/PLATFORM-TODO-BACKLOG.md +41 -0
- package/docs/PLATFORM-TODO-TRIAGE.md +56 -0
- package/docs/README.md +83 -0
- package/docs/TIPS.md +39 -2
- package/docs/config-policy.md +40 -0
- package/docs/definition-of-done.example.md +2 -0
- package/docs/patterns/README.md +5 -0
- package/docs/strategy/02-mission-and-brand.md +3 -3
- package/docs/strategy/README.md +4 -3
- package/docs/tmux-research/TMUX-AUDIT.md +2 -0
- package/docs/tmux-research/TMUX-RESEARCH-INDEX.md +17 -0
- package/package.json +3 -2
- package/scripts/lib/daemon-health.sh +32 -0
- package/scripts/lib/helpers.sh +30 -1
- package/scripts/lib/pipeline-detection.sh +278 -0
- package/scripts/lib/pipeline-github.sh +196 -0
- package/scripts/lib/pipeline-intelligence.sh +1712 -0
- package/scripts/lib/pipeline-quality-checks.sh +1052 -0
- package/scripts/lib/pipeline-quality.sh +34 -0
- package/scripts/lib/pipeline-stages.sh +2488 -0
- package/scripts/lib/pipeline-state.sh +529 -0
- package/scripts/lib/policy.sh +32 -0
- package/scripts/sw +5 -1
- package/scripts/sw-activity.sh +35 -46
- package/scripts/sw-adaptive.sh +30 -39
- package/scripts/sw-adversarial.sh +30 -36
- package/scripts/sw-architecture-enforcer.sh +30 -33
- package/scripts/sw-auth.sh +30 -42
- package/scripts/sw-autonomous.sh +60 -40
- package/scripts/sw-changelog.sh +29 -30
- package/scripts/sw-checkpoint.sh +30 -18
- package/scripts/sw-ci.sh +30 -42
- package/scripts/sw-cleanup.sh +32 -15
- package/scripts/sw-code-review.sh +26 -32
- package/scripts/sw-connect.sh +30 -19
- package/scripts/sw-context.sh +30 -19
- package/scripts/sw-cost.sh +30 -40
- package/scripts/sw-daemon.sh +66 -36
- package/scripts/sw-dashboard.sh +31 -40
- package/scripts/sw-db.sh +30 -20
- package/scripts/sw-decompose.sh +30 -38
- package/scripts/sw-deps.sh +30 -41
- package/scripts/sw-developer-simulation.sh +30 -36
- package/scripts/sw-discovery.sh +36 -19
- package/scripts/sw-doc-fleet.sh +822 -0
- package/scripts/sw-docs-agent.sh +30 -36
- package/scripts/sw-docs.sh +29 -31
- package/scripts/sw-doctor.sh +52 -20
- package/scripts/sw-dora.sh +29 -34
- package/scripts/sw-durable.sh +30 -20
- package/scripts/sw-e2e-orchestrator.sh +36 -21
- package/scripts/sw-eventbus.sh +30 -17
- package/scripts/sw-feedback.sh +30 -41
- package/scripts/sw-fix.sh +30 -40
- package/scripts/sw-fleet-discover.sh +30 -41
- package/scripts/sw-fleet-viz.sh +30 -20
- package/scripts/sw-fleet.sh +30 -40
- package/scripts/sw-github-app.sh +30 -41
- package/scripts/sw-github-checks.sh +30 -41
- package/scripts/sw-github-deploy.sh +30 -41
- package/scripts/sw-github-graphql.sh +30 -38
- package/scripts/sw-guild.sh +30 -37
- package/scripts/sw-heartbeat.sh +30 -19
- package/scripts/sw-hygiene.sh +134 -42
- package/scripts/sw-incident.sh +30 -39
- package/scripts/sw-init.sh +31 -14
- package/scripts/sw-instrument.sh +30 -41
- package/scripts/sw-intelligence.sh +39 -44
- package/scripts/sw-jira.sh +31 -41
- package/scripts/sw-launchd.sh +30 -17
- package/scripts/sw-linear.sh +31 -41
- package/scripts/sw-logs.sh +32 -17
- package/scripts/sw-loop.sh +32 -19
- package/scripts/sw-memory.sh +32 -43
- package/scripts/sw-mission-control.sh +31 -40
- package/scripts/sw-model-router.sh +30 -20
- package/scripts/sw-otel.sh +30 -20
- package/scripts/sw-oversight.sh +30 -36
- package/scripts/sw-patrol-meta.sh +31 -0
- package/scripts/sw-pipeline-composer.sh +30 -39
- package/scripts/sw-pipeline-vitals.sh +30 -44
- package/scripts/sw-pipeline.sh +277 -6383
- package/scripts/sw-pm.sh +31 -41
- package/scripts/sw-pr-lifecycle.sh +30 -42
- package/scripts/sw-predictive.sh +32 -34
- package/scripts/sw-prep.sh +30 -19
- package/scripts/sw-ps.sh +32 -17
- package/scripts/sw-public-dashboard.sh +30 -40
- package/scripts/sw-quality.sh +42 -40
- package/scripts/sw-reaper.sh +32 -15
- package/scripts/sw-recruit.sh +428 -48
- package/scripts/sw-regression.sh +30 -38
- package/scripts/sw-release-manager.sh +30 -38
- package/scripts/sw-release.sh +29 -31
- package/scripts/sw-remote.sh +31 -40
- package/scripts/sw-replay.sh +30 -18
- package/scripts/sw-retro.sh +33 -42
- package/scripts/sw-scale.sh +41 -24
- package/scripts/sw-security-audit.sh +30 -20
- package/scripts/sw-self-optimize.sh +33 -37
- package/scripts/sw-session.sh +31 -15
- package/scripts/sw-setup.sh +30 -16
- package/scripts/sw-standup.sh +30 -20
- package/scripts/sw-status.sh +33 -13
- package/scripts/sw-strategic.sh +55 -43
- package/scripts/sw-stream.sh +33 -37
- package/scripts/sw-swarm.sh +30 -21
- package/scripts/sw-team-stages.sh +30 -38
- package/scripts/sw-templates.sh +31 -16
- package/scripts/sw-testgen.sh +30 -31
- package/scripts/sw-tmux-pipeline.sh +29 -31
- package/scripts/sw-tmux-role-color.sh +31 -0
- package/scripts/sw-tmux-status.sh +31 -0
- package/scripts/sw-tmux.sh +31 -15
- package/scripts/sw-trace.sh +30 -19
- package/scripts/sw-tracker-github.sh +31 -0
- package/scripts/sw-tracker-jira.sh +31 -0
- package/scripts/sw-tracker-linear.sh +31 -0
- package/scripts/sw-tracker.sh +30 -40
- package/scripts/sw-triage.sh +68 -61
- package/scripts/sw-upgrade.sh +30 -16
- package/scripts/sw-ux.sh +30 -35
- package/scripts/sw-webhook.sh +30 -25
- package/scripts/sw-widgets.sh +30 -19
- package/scripts/sw-worktree.sh +32 -15
- 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
|