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.
- package/README.md +114 -36
- package/completions/_shipwright +212 -32
- package/completions/shipwright.bash +97 -25
- package/docs/strategy/01-market-research.md +619 -0
- package/docs/strategy/02-mission-and-brand.md +587 -0
- package/docs/strategy/03-gtm-and-roadmap.md +759 -0
- package/docs/strategy/QUICK-START.txt +289 -0
- package/docs/strategy/README.md +172 -0
- package/package.json +4 -2
- package/scripts/sw +208 -1
- package/scripts/sw-activity.sh +500 -0
- package/scripts/sw-adaptive.sh +925 -0
- package/scripts/sw-adversarial.sh +1 -1
- package/scripts/sw-architecture-enforcer.sh +1 -1
- package/scripts/sw-auth.sh +613 -0
- package/scripts/sw-autonomous.sh +664 -0
- package/scripts/sw-changelog.sh +704 -0
- package/scripts/sw-checkpoint.sh +1 -1
- package/scripts/sw-ci.sh +602 -0
- package/scripts/sw-cleanup.sh +1 -1
- package/scripts/sw-code-review.sh +637 -0
- package/scripts/sw-connect.sh +1 -1
- package/scripts/sw-context.sh +605 -0
- package/scripts/sw-cost.sh +1 -1
- package/scripts/sw-daemon.sh +432 -130
- package/scripts/sw-dashboard.sh +1 -1
- package/scripts/sw-db.sh +540 -0
- package/scripts/sw-decompose.sh +539 -0
- package/scripts/sw-deps.sh +551 -0
- package/scripts/sw-developer-simulation.sh +1 -1
- package/scripts/sw-discovery.sh +412 -0
- package/scripts/sw-docs-agent.sh +539 -0
- package/scripts/sw-docs.sh +1 -1
- package/scripts/sw-doctor.sh +59 -1
- package/scripts/sw-dora.sh +615 -0
- package/scripts/sw-durable.sh +710 -0
- package/scripts/sw-e2e-orchestrator.sh +535 -0
- package/scripts/sw-eventbus.sh +393 -0
- package/scripts/sw-feedback.sh +471 -0
- package/scripts/sw-fix.sh +1 -1
- package/scripts/sw-fleet-discover.sh +567 -0
- package/scripts/sw-fleet-viz.sh +404 -0
- package/scripts/sw-fleet.sh +8 -1
- package/scripts/sw-github-app.sh +596 -0
- package/scripts/sw-github-checks.sh +1 -1
- package/scripts/sw-github-deploy.sh +1 -1
- package/scripts/sw-github-graphql.sh +1 -1
- package/scripts/sw-guild.sh +569 -0
- package/scripts/sw-heartbeat.sh +1 -1
- package/scripts/sw-hygiene.sh +559 -0
- package/scripts/sw-incident.sh +617 -0
- package/scripts/sw-init.sh +88 -1
- package/scripts/sw-instrument.sh +699 -0
- package/scripts/sw-intelligence.sh +1 -1
- package/scripts/sw-jira.sh +1 -1
- package/scripts/sw-launchd.sh +363 -28
- package/scripts/sw-linear.sh +1 -1
- package/scripts/sw-logs.sh +1 -1
- package/scripts/sw-loop.sh +64 -3
- package/scripts/sw-memory.sh +1 -1
- package/scripts/sw-mission-control.sh +487 -0
- package/scripts/sw-model-router.sh +545 -0
- package/scripts/sw-otel.sh +596 -0
- package/scripts/sw-oversight.sh +689 -0
- package/scripts/sw-pipeline-composer.sh +1 -1
- package/scripts/sw-pipeline-vitals.sh +1 -1
- package/scripts/sw-pipeline.sh +687 -24
- package/scripts/sw-pm.sh +693 -0
- package/scripts/sw-pr-lifecycle.sh +522 -0
- package/scripts/sw-predictive.sh +1 -1
- package/scripts/sw-prep.sh +1 -1
- package/scripts/sw-ps.sh +1 -1
- package/scripts/sw-public-dashboard.sh +798 -0
- package/scripts/sw-quality.sh +595 -0
- package/scripts/sw-reaper.sh +1 -1
- package/scripts/sw-recruit.sh +573 -0
- package/scripts/sw-regression.sh +642 -0
- package/scripts/sw-release-manager.sh +736 -0
- package/scripts/sw-release.sh +706 -0
- package/scripts/sw-remote.sh +1 -1
- package/scripts/sw-replay.sh +520 -0
- package/scripts/sw-retro.sh +691 -0
- package/scripts/sw-scale.sh +444 -0
- package/scripts/sw-security-audit.sh +505 -0
- package/scripts/sw-self-optimize.sh +1 -1
- package/scripts/sw-session.sh +1 -1
- package/scripts/sw-setup.sh +1 -1
- package/scripts/sw-standup.sh +712 -0
- package/scripts/sw-status.sh +1 -1
- package/scripts/sw-strategic.sh +658 -0
- package/scripts/sw-stream.sh +450 -0
- package/scripts/sw-swarm.sh +583 -0
- package/scripts/sw-team-stages.sh +511 -0
- package/scripts/sw-templates.sh +1 -1
- package/scripts/sw-testgen.sh +515 -0
- package/scripts/sw-tmux-pipeline.sh +554 -0
- package/scripts/sw-tmux.sh +1 -1
- package/scripts/sw-trace.sh +485 -0
- package/scripts/sw-tracker-github.sh +188 -0
- package/scripts/sw-tracker-jira.sh +172 -0
- package/scripts/sw-tracker-linear.sh +251 -0
- package/scripts/sw-tracker.sh +117 -2
- package/scripts/sw-triage.sh +603 -0
- package/scripts/sw-upgrade.sh +1 -1
- package/scripts/sw-ux.sh +677 -0
- package/scripts/sw-webhook.sh +627 -0
- package/scripts/sw-widgets.sh +530 -0
- 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
|
package/scripts/sw-docs.sh
CHANGED
package/scripts/sw-doctor.sh
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# ║ ║
|
|
5
5
|
# ║ Checks prerequisites, installed files, PATH, and common issues. ║
|
|
6
6
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
7
|
-
VERSION="
|
|
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
|
# ═════════════════════════════════════════════════════════════════════════════
|