shipwright-cli 1.7.1 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/code-reviewer.md +90 -0
- package/.claude/agents/devops-engineer.md +142 -0
- package/.claude/agents/pipeline-agent.md +80 -0
- package/.claude/agents/shell-script-specialist.md +150 -0
- package/.claude/agents/test-specialist.md +196 -0
- package/.claude/hooks/post-tool-use.sh +38 -0
- package/.claude/hooks/pre-tool-use.sh +25 -0
- package/.claude/hooks/session-started.sh +37 -0
- package/README.md +212 -814
- package/claude-code/CLAUDE.md.shipwright +54 -0
- package/claude-code/hooks/notify-idle.sh +2 -2
- package/claude-code/hooks/session-start.sh +24 -0
- package/claude-code/hooks/task-completed.sh +6 -2
- package/claude-code/settings.json.template +12 -0
- package/dashboard/public/app.js +4422 -0
- package/dashboard/public/index.html +816 -0
- package/dashboard/public/styles.css +4755 -0
- package/dashboard/server.ts +4315 -0
- package/docs/KNOWN-ISSUES.md +18 -10
- package/docs/TIPS.md +38 -26
- package/docs/patterns/README.md +33 -23
- package/package.json +9 -5
- package/scripts/adapters/iterm2-adapter.sh +1 -1
- package/scripts/adapters/tmux-adapter.sh +52 -23
- package/scripts/adapters/wezterm-adapter.sh +26 -14
- package/scripts/lib/compat.sh +200 -0
- package/scripts/lib/helpers.sh +72 -0
- package/scripts/postinstall.mjs +72 -13
- package/scripts/{cct → sw} +109 -21
- package/scripts/sw-adversarial.sh +274 -0
- package/scripts/sw-architecture-enforcer.sh +330 -0
- package/scripts/sw-checkpoint.sh +390 -0
- package/scripts/{cct-cleanup.sh → sw-cleanup.sh} +3 -1
- package/scripts/sw-connect.sh +619 -0
- package/scripts/{cct-cost.sh → sw-cost.sh} +368 -34
- package/scripts/{cct-daemon.sh → sw-daemon.sh} +2217 -204
- package/scripts/sw-dashboard.sh +477 -0
- package/scripts/sw-developer-simulation.sh +252 -0
- package/scripts/sw-docs.sh +635 -0
- package/scripts/sw-doctor.sh +907 -0
- package/scripts/{cct-fix.sh → sw-fix.sh} +10 -6
- package/scripts/{cct-fleet.sh → sw-fleet.sh} +498 -22
- package/scripts/sw-github-checks.sh +521 -0
- package/scripts/sw-github-deploy.sh +533 -0
- package/scripts/sw-github-graphql.sh +972 -0
- package/scripts/sw-heartbeat.sh +293 -0
- package/scripts/{cct-init.sh → sw-init.sh} +144 -11
- package/scripts/sw-intelligence.sh +1196 -0
- package/scripts/sw-jira.sh +643 -0
- package/scripts/sw-launchd.sh +364 -0
- package/scripts/sw-linear.sh +648 -0
- package/scripts/{cct-logs.sh → sw-logs.sh} +72 -2
- package/scripts/{cct-loop.sh → sw-loop.sh} +534 -44
- package/scripts/{cct-memory.sh → sw-memory.sh} +321 -38
- package/scripts/sw-patrol-meta.sh +417 -0
- package/scripts/sw-pipeline-composer.sh +455 -0
- package/scripts/{cct-pipeline.sh → sw-pipeline.sh} +2319 -178
- package/scripts/sw-predictive.sh +820 -0
- package/scripts/{cct-prep.sh → sw-prep.sh} +339 -49
- package/scripts/{cct-ps.sh → sw-ps.sh} +6 -4
- package/scripts/{cct-reaper.sh → sw-reaper.sh} +6 -4
- package/scripts/sw-remote.sh +687 -0
- package/scripts/sw-self-optimize.sh +947 -0
- package/scripts/sw-session.sh +519 -0
- package/scripts/sw-setup.sh +234 -0
- package/scripts/sw-status.sh +605 -0
- package/scripts/{cct-templates.sh → sw-templates.sh} +9 -4
- package/scripts/sw-tmux.sh +591 -0
- package/scripts/sw-tracker-jira.sh +277 -0
- package/scripts/sw-tracker-linear.sh +292 -0
- package/scripts/sw-tracker.sh +409 -0
- package/scripts/{cct-upgrade.sh → sw-upgrade.sh} +103 -46
- package/scripts/{cct-worktree.sh → sw-worktree.sh} +3 -0
- package/templates/pipelines/autonomous.json +27 -5
- package/templates/pipelines/full.json +12 -0
- package/templates/pipelines/standard.json +12 -0
- package/tmux/{claude-teams-overlay.conf → shipwright-overlay.conf} +27 -9
- package/tmux/templates/accessibility.json +34 -0
- package/tmux/templates/api-design.json +35 -0
- package/tmux/templates/architecture.json +1 -0
- package/tmux/templates/bug-fix.json +9 -0
- package/tmux/templates/code-review.json +1 -0
- package/tmux/templates/compliance.json +36 -0
- package/tmux/templates/data-pipeline.json +36 -0
- package/tmux/templates/debt-paydown.json +34 -0
- package/tmux/templates/devops.json +1 -0
- package/tmux/templates/documentation.json +1 -0
- package/tmux/templates/exploration.json +1 -0
- package/tmux/templates/feature-dev.json +1 -0
- package/tmux/templates/full-stack.json +8 -0
- package/tmux/templates/i18n.json +34 -0
- package/tmux/templates/incident-response.json +36 -0
- package/tmux/templates/migration.json +1 -0
- package/tmux/templates/observability.json +35 -0
- package/tmux/templates/onboarding.json +33 -0
- package/tmux/templates/performance.json +35 -0
- package/tmux/templates/refactor.json +1 -0
- package/tmux/templates/release.json +35 -0
- package/tmux/templates/security-audit.json +8 -0
- package/tmux/templates/spike.json +34 -0
- package/tmux/templates/testing.json +1 -0
- package/tmux/tmux.conf +98 -9
- package/scripts/cct-doctor.sh +0 -414
- package/scripts/cct-session.sh +0 -284
- package/scripts/cct-status.sh +0 -169
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
# ║ shipwright docs — Documentation Keeper ║
|
|
4
|
+
# ║ Auto-sync documentation from source, detect staleness, generate wiki ║
|
|
5
|
+
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
|
+
|
|
9
|
+
VERSION="1.9.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
|
+
# ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
|
|
31
|
+
success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
|
|
32
|
+
warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
|
|
33
|
+
error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
|
|
34
|
+
|
|
35
|
+
emit_event() {
|
|
36
|
+
local event_type="$1"; shift
|
|
37
|
+
local events_file="${HOME}/.shipwright/events.jsonl"
|
|
38
|
+
mkdir -p "$(dirname "$events_file")"
|
|
39
|
+
local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
40
|
+
while [[ $# -gt 0 ]]; do
|
|
41
|
+
local key="${1%%=*}" val="${1#*=}"
|
|
42
|
+
payload="${payload},\"${key}\":\"${val}\""
|
|
43
|
+
shift
|
|
44
|
+
done
|
|
45
|
+
payload="${payload}}"
|
|
46
|
+
echo "$payload" >> "$events_file"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# ─── AUTO Section Processing ────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
# Find all files with AUTO markers
|
|
52
|
+
docs_find_auto_files() {
|
|
53
|
+
grep -rl '<!-- AUTO:' "$REPO_DIR" --include='*.md' 2>/dev/null || true
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Extract section IDs from a file
|
|
57
|
+
docs_get_sections() {
|
|
58
|
+
local file="$1"
|
|
59
|
+
grep -oE '<!-- AUTO:[a-z0-9_-]+ -->' "$file" 2>/dev/null | sed 's/<!-- AUTO://;s/ -->//' || true
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Replace content between AUTO markers (using temp file for multi-line content)
|
|
63
|
+
docs_replace_section() {
|
|
64
|
+
local file="$1" section_id="$2" new_content="$3"
|
|
65
|
+
local tmp_file content_file
|
|
66
|
+
tmp_file=$(mktemp)
|
|
67
|
+
content_file=$(mktemp)
|
|
68
|
+
|
|
69
|
+
printf '%s\n' "$new_content" > "$content_file"
|
|
70
|
+
|
|
71
|
+
awk -v section="$section_id" -v cfile="$content_file" '
|
|
72
|
+
$0 ~ "<!-- AUTO:" section " -->" {
|
|
73
|
+
print
|
|
74
|
+
while ((getline line < cfile) > 0) print line
|
|
75
|
+
close(cfile)
|
|
76
|
+
skip=1
|
|
77
|
+
next
|
|
78
|
+
}
|
|
79
|
+
$0 ~ "<!-- /AUTO:" section " -->" { skip=0 }
|
|
80
|
+
!skip { print }
|
|
81
|
+
' "$file" > "$tmp_file"
|
|
82
|
+
|
|
83
|
+
mv "$tmp_file" "$file"
|
|
84
|
+
rm -f "$content_file"
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# Check if a section is stale (content differs from generated)
|
|
88
|
+
docs_check_section() {
|
|
89
|
+
local file="$1" section_id="$2" expected="$3"
|
|
90
|
+
local current
|
|
91
|
+
current=$(awk -v section="$section_id" '
|
|
92
|
+
$0 ~ "<!-- AUTO:" section " -->" { capture=1; next }
|
|
93
|
+
$0 ~ "<!-- /AUTO:" section " -->" { capture=0 }
|
|
94
|
+
capture { print }
|
|
95
|
+
' "$file" 2>/dev/null || true)
|
|
96
|
+
|
|
97
|
+
if [[ "$current" != "$expected" ]]; then
|
|
98
|
+
return 1 # stale
|
|
99
|
+
fi
|
|
100
|
+
return 0 # fresh
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# ─── Section Generators ─────────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
# Generate architecture table for a given section
|
|
106
|
+
docs_gen_architecture_table() {
|
|
107
|
+
local section="$1"
|
|
108
|
+
|
|
109
|
+
case "$section" in
|
|
110
|
+
core-scripts)
|
|
111
|
+
echo ""
|
|
112
|
+
echo "| File | Lines | Purpose |"
|
|
113
|
+
echo "| --- | ---: | --- |"
|
|
114
|
+
for f in "$REPO_DIR"/scripts/sw-*.sh; do
|
|
115
|
+
[[ -f "$f" ]] || continue
|
|
116
|
+
local basename
|
|
117
|
+
basename=$(basename "$f")
|
|
118
|
+
# Skip test files, tracker providers, github modules
|
|
119
|
+
[[ "$basename" == *-test.sh ]] && continue
|
|
120
|
+
[[ "$basename" == sw-tracker-*.sh ]] && continue
|
|
121
|
+
[[ "$basename" == sw-github-*.sh ]] && continue
|
|
122
|
+
local lines purpose
|
|
123
|
+
lines=$(wc -l < "$f" | xargs)
|
|
124
|
+
# Extract purpose from header: "# ║ shipwright X — Description ║"
|
|
125
|
+
purpose=$(sed -n '3p' "$f" 2>/dev/null | sed 's/^# *║ *//;s/ *║ *$//;s/.*— *//' || echo "")
|
|
126
|
+
if [[ -z "$purpose" || "$purpose" == "#"* ]]; then
|
|
127
|
+
purpose=$(head -5 "$f" | grep -m1 '# .*—' | sed 's/.*— *//' | sed 's/ *║.*//' || echo "")
|
|
128
|
+
fi
|
|
129
|
+
echo "| \`scripts/${basename}\` | ${lines} | ${purpose} |"
|
|
130
|
+
done
|
|
131
|
+
# Also include the CLI router
|
|
132
|
+
if [[ -f "$REPO_DIR/scripts/sw" ]]; then
|
|
133
|
+
local sw_lines
|
|
134
|
+
sw_lines=$(wc -l < "$REPO_DIR/scripts/sw" | xargs)
|
|
135
|
+
echo "| \`scripts/sw\` | ${sw_lines} | CLI router — dispatches subcommands via exec |"
|
|
136
|
+
fi
|
|
137
|
+
;;
|
|
138
|
+
github-modules)
|
|
139
|
+
echo ""
|
|
140
|
+
echo "| File | Lines | Purpose |"
|
|
141
|
+
echo "| --- | ---: | --- |"
|
|
142
|
+
for f in "$REPO_DIR"/scripts/sw-github-*.sh; do
|
|
143
|
+
[[ -f "$f" ]] || continue
|
|
144
|
+
[[ "$f" == *-test.sh ]] && continue
|
|
145
|
+
local basename lines purpose
|
|
146
|
+
basename=$(basename "$f")
|
|
147
|
+
lines=$(wc -l < "$f" | xargs)
|
|
148
|
+
purpose=$(head -5 "$f" | grep -m1 '# .*—' | sed 's/.*— *//;s/ *║.*//' || echo "")
|
|
149
|
+
echo "| \`scripts/${basename}\` | ${lines} | ${purpose} |"
|
|
150
|
+
done
|
|
151
|
+
;;
|
|
152
|
+
tracker-adapters)
|
|
153
|
+
echo ""
|
|
154
|
+
echo "| File | Lines | Purpose |"
|
|
155
|
+
echo "| --- | ---: | --- |"
|
|
156
|
+
for f in "$REPO_DIR"/scripts/sw-linear.sh "$REPO_DIR"/scripts/sw-jira.sh "$REPO_DIR"/scripts/sw-tracker-linear.sh "$REPO_DIR"/scripts/sw-tracker-jira.sh; do
|
|
157
|
+
[[ -f "$f" ]] || continue
|
|
158
|
+
local basename lines purpose
|
|
159
|
+
basename=$(basename "$f")
|
|
160
|
+
lines=$(wc -l < "$f" | xargs)
|
|
161
|
+
purpose=$(head -5 "$f" | grep -m1 '# .*—' | sed 's/.*— *//;s/ *║.*//' || echo "")
|
|
162
|
+
echo "| \`scripts/${basename}\` | ${lines} | ${purpose} |"
|
|
163
|
+
done
|
|
164
|
+
;;
|
|
165
|
+
test-suites)
|
|
166
|
+
echo ""
|
|
167
|
+
echo "| File | Lines | Purpose |"
|
|
168
|
+
echo "| --- | ---: | --- |"
|
|
169
|
+
for f in "$REPO_DIR"/scripts/sw-*-test.sh; do
|
|
170
|
+
[[ -f "$f" ]] || continue
|
|
171
|
+
local basename lines purpose
|
|
172
|
+
basename=$(basename "$f")
|
|
173
|
+
lines=$(wc -l < "$f" | xargs)
|
|
174
|
+
purpose=$(head -5 "$f" | grep -m1 '# .*—' | sed 's/.*— *//;s/ *║.*//' || echo "")
|
|
175
|
+
echo "| \`scripts/${basename}\` | ${lines} | ${purpose} |"
|
|
176
|
+
done
|
|
177
|
+
;;
|
|
178
|
+
esac
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# Generate command table from CLI router
|
|
182
|
+
docs_gen_command_table() {
|
|
183
|
+
local sw_file="$REPO_DIR/scripts/sw"
|
|
184
|
+
[[ -f "$sw_file" ]] || return 0
|
|
185
|
+
echo ""
|
|
186
|
+
echo "| Command | Purpose |"
|
|
187
|
+
echo "| --- | --- |"
|
|
188
|
+
# Parse the show_help() output lines that match the command pattern
|
|
189
|
+
awk '
|
|
190
|
+
/^show_help\(\)/ { in_help=1 }
|
|
191
|
+
in_help && /^}/ { in_help=0 }
|
|
192
|
+
in_help && /echo.*CYAN.*RESET/ {
|
|
193
|
+
line = $0
|
|
194
|
+
# Extract command name between CYAN} and RESET}
|
|
195
|
+
gsub(/.*CYAN\}/, "", line)
|
|
196
|
+
gsub(/\$\{RESET\}.*/, "", line)
|
|
197
|
+
gsub(/^[[:space:]]*/, "", line)
|
|
198
|
+
cmd = line
|
|
199
|
+
# Extract description — everything after the last RESET}
|
|
200
|
+
line = $0
|
|
201
|
+
n = split(line, parts, "\\$\\{RESET\\}")
|
|
202
|
+
desc = parts[n]
|
|
203
|
+
gsub(/^[[:space:]]*/, "", desc)
|
|
204
|
+
gsub(/"[[:space:]]*$/, "", desc)
|
|
205
|
+
if (cmd != "" && desc != "" && cmd !~ /shipwright/) {
|
|
206
|
+
printf "| `shipwright %s` | %s |\n", cmd, desc
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
' "$sw_file" 2>/dev/null || true
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
# Generate test table from package.json
|
|
213
|
+
docs_gen_test_table() {
|
|
214
|
+
local pkg="$REPO_DIR/package.json"
|
|
215
|
+
[[ -f "$pkg" ]] || return 0
|
|
216
|
+
echo ""
|
|
217
|
+
local idx=1
|
|
218
|
+
local test_files
|
|
219
|
+
test_files=$(jq -r '.scripts | to_entries[] | select(.key | startswith("test:")) | .value' "$pkg" 2>/dev/null || true)
|
|
220
|
+
if [[ -z "$test_files" ]]; then
|
|
221
|
+
return 0
|
|
222
|
+
fi
|
|
223
|
+
while IFS= read -r cmd; do
|
|
224
|
+
local test_file
|
|
225
|
+
test_file=$(echo "$cmd" | grep -oE 'sw-[a-z-]+-test\.sh' || true)
|
|
226
|
+
if [[ -n "$test_file" ]] && [[ -f "$REPO_DIR/scripts/$test_file" ]]; then
|
|
227
|
+
local purpose
|
|
228
|
+
purpose=$(head -5 "$REPO_DIR/scripts/$test_file" 2>/dev/null | grep -m1 '# .*—' | sed 's/.*— *//' || echo "")
|
|
229
|
+
echo "${idx}. \`${test_file}\` — ${purpose}"
|
|
230
|
+
idx=$((idx + 1))
|
|
231
|
+
fi
|
|
232
|
+
done <<< "$test_files"
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
# Generate feature flags table from daemon config defaults
|
|
236
|
+
docs_gen_feature_flags() {
|
|
237
|
+
local daemon="$REPO_DIR/scripts/sw-daemon.sh"
|
|
238
|
+
[[ -f "$daemon" ]] || return 0
|
|
239
|
+
echo ""
|
|
240
|
+
echo "| Flag | Default | Purpose |"
|
|
241
|
+
echo "| --- | --- | --- |"
|
|
242
|
+
# Extract intelligence config from the heredoc in sw-daemon.sh
|
|
243
|
+
# Lines look like: "enabled": true,
|
|
244
|
+
local in_intel=false
|
|
245
|
+
while IFS= read -r line; do
|
|
246
|
+
if echo "$line" | grep -q '"intelligence"' 2>/dev/null; then
|
|
247
|
+
in_intel=true
|
|
248
|
+
continue
|
|
249
|
+
fi
|
|
250
|
+
if [[ "$in_intel" == "true" ]] && echo "$line" | grep -q '^\s*}' 2>/dev/null; then
|
|
251
|
+
in_intel=false
|
|
252
|
+
continue
|
|
253
|
+
fi
|
|
254
|
+
if [[ "$in_intel" == "true" ]]; then
|
|
255
|
+
local key val
|
|
256
|
+
key=$(echo "$line" | sed -n 's/.*"\([a-z_]*\)".*/\1/p' 2>/dev/null || true)
|
|
257
|
+
val=$(echo "$line" | sed -n 's/.*: *\([a-z0-9.]*\).*/\1/p' 2>/dev/null || true)
|
|
258
|
+
if [[ -n "$key" && -n "$val" ]]; then
|
|
259
|
+
echo "| \`intelligence.${key}\` | \`${val}\` | |"
|
|
260
|
+
fi
|
|
261
|
+
fi
|
|
262
|
+
done < "$daemon"
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
# Generate runtime state file locations
|
|
266
|
+
docs_gen_file_locations() {
|
|
267
|
+
echo ""
|
|
268
|
+
local locations
|
|
269
|
+
locations="Pipeline state:.claude/pipeline-state.md
|
|
270
|
+
Pipeline artifacts:.claude/pipeline-artifacts/
|
|
271
|
+
Composed pipeline:.claude/pipeline-artifacts/composed-pipeline.json
|
|
272
|
+
Events log:~/.shipwright/events.jsonl
|
|
273
|
+
Daemon config:.claude/daemon-config.json
|
|
274
|
+
Fleet config:.claude/fleet-config.json
|
|
275
|
+
Heartbeats:~/.shipwright/heartbeats/<job-id>.json
|
|
276
|
+
Checkpoints:.claude/pipeline-artifacts/checkpoints/
|
|
277
|
+
Machine registry:~/.shipwright/machines.json
|
|
278
|
+
Cost data:~/.shipwright/costs.json, ~/.shipwright/budget.json
|
|
279
|
+
Intelligence cache:.claude/intelligence-cache.json
|
|
280
|
+
Optimization data:~/.shipwright/optimization/
|
|
281
|
+
Baselines:~/.shipwright/baselines/
|
|
282
|
+
Architecture models:~/.shipwright/memory/<repo-hash>/architecture.json
|
|
283
|
+
Team config:~/.shipwright/team-config.json
|
|
284
|
+
Developer registry:~/.shipwright/developer-registry.json
|
|
285
|
+
Team events:~/.shipwright/team-events.jsonl
|
|
286
|
+
Invite tokens:~/.shipwright/invite-tokens.json
|
|
287
|
+
Connect PID:~/.shipwright/connect.pid
|
|
288
|
+
Connect log:~/.shipwright/connect.log
|
|
289
|
+
GitHub cache:~/.shipwright/github-cache/
|
|
290
|
+
Check run IDs:.claude/pipeline-artifacts/check-run-ids.json
|
|
291
|
+
Deployment tracking:.claude/pipeline-artifacts/deployment.json
|
|
292
|
+
Error log:.claude/pipeline-artifacts/error-log.jsonl"
|
|
293
|
+
|
|
294
|
+
while IFS= read -r loc; do
|
|
295
|
+
[[ -z "$loc" ]] && continue
|
|
296
|
+
local label="${loc%%:*}"
|
|
297
|
+
local path="${loc#*:}"
|
|
298
|
+
echo "- ${label}: \`${path}\`"
|
|
299
|
+
done <<< "$locations"
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
# Generate codebase stats
|
|
303
|
+
docs_gen_stats() {
|
|
304
|
+
local script_count=0 test_count=0 total_lines=0
|
|
305
|
+
|
|
306
|
+
for f in "$REPO_DIR"/scripts/sw-*.sh "$REPO_DIR"/scripts/sw; do
|
|
307
|
+
[[ -f "$f" ]] || continue
|
|
308
|
+
if [[ "$f" == *-test.sh ]]; then
|
|
309
|
+
test_count=$((test_count + 1))
|
|
310
|
+
else
|
|
311
|
+
script_count=$((script_count + 1))
|
|
312
|
+
fi
|
|
313
|
+
local lines
|
|
314
|
+
lines=$(wc -l < "$f" | xargs)
|
|
315
|
+
total_lines=$((total_lines + lines))
|
|
316
|
+
done
|
|
317
|
+
|
|
318
|
+
local template_count=0 team_template_count=0
|
|
319
|
+
if [[ -d "$REPO_DIR/templates/pipelines" ]]; then
|
|
320
|
+
template_count=$(find "$REPO_DIR/templates/pipelines" -name '*.json' 2>/dev/null | wc -l | xargs)
|
|
321
|
+
fi
|
|
322
|
+
if [[ -d "$REPO_DIR/tmux/templates" ]]; then
|
|
323
|
+
team_template_count=$(find "$REPO_DIR/tmux/templates" -name '*.json' 2>/dev/null | wc -l | xargs)
|
|
324
|
+
fi
|
|
325
|
+
|
|
326
|
+
echo "- **${script_count}** core scripts + CLI router"
|
|
327
|
+
echo "- **${test_count}** test suites"
|
|
328
|
+
echo "- **${total_lines}** total lines of shell"
|
|
329
|
+
echo "- **${template_count}** pipeline templates"
|
|
330
|
+
echo "- **${team_template_count}** team composition templates"
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
# ─── Section Router ──────────────────────────────────────────────────────────
|
|
334
|
+
|
|
335
|
+
docs_generate_section() {
|
|
336
|
+
local section_id="$1"
|
|
337
|
+
case "$section_id" in
|
|
338
|
+
core-scripts) docs_gen_architecture_table "core-scripts" ;;
|
|
339
|
+
github-modules) docs_gen_architecture_table "github-modules" ;;
|
|
340
|
+
tracker-adapters) docs_gen_architecture_table "tracker-adapters" ;;
|
|
341
|
+
test-suites) docs_gen_architecture_table "test-suites" ;;
|
|
342
|
+
commands-table) docs_gen_command_table ;;
|
|
343
|
+
test-list) docs_gen_test_table ;;
|
|
344
|
+
feature-flags) docs_gen_feature_flags ;;
|
|
345
|
+
runtime-state) docs_gen_file_locations ;;
|
|
346
|
+
stats) docs_gen_stats ;;
|
|
347
|
+
*) warn "Unknown AUTO section: $section_id"; return 1 ;;
|
|
348
|
+
esac
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
# ─── Subcommands ─────────────────────────────────────────────────────────────
|
|
352
|
+
|
|
353
|
+
# Check which AUTO sections are stale
|
|
354
|
+
docs_check() {
|
|
355
|
+
info "Checking documentation freshness..."
|
|
356
|
+
local stale=0 fresh=0 total=0
|
|
357
|
+
|
|
358
|
+
local files
|
|
359
|
+
files=$(docs_find_auto_files)
|
|
360
|
+
if [[ -z "$files" ]]; then
|
|
361
|
+
warn "No files with AUTO markers found"
|
|
362
|
+
return 0
|
|
363
|
+
fi
|
|
364
|
+
|
|
365
|
+
while IFS= read -r file; do
|
|
366
|
+
[[ -z "$file" ]] && continue
|
|
367
|
+
local sections
|
|
368
|
+
sections=$(docs_get_sections "$file")
|
|
369
|
+
while IFS= read -r section; do
|
|
370
|
+
[[ -z "$section" ]] && continue
|
|
371
|
+
total=$((total + 1))
|
|
372
|
+
local expected
|
|
373
|
+
expected=$(docs_generate_section "$section")
|
|
374
|
+
if ! docs_check_section "$file" "$section" "$expected"; then
|
|
375
|
+
stale=$((stale + 1))
|
|
376
|
+
local rel_file="${file#$REPO_DIR/}"
|
|
377
|
+
warn "Stale: ${rel_file}#${section}"
|
|
378
|
+
else
|
|
379
|
+
fresh=$((fresh + 1))
|
|
380
|
+
fi
|
|
381
|
+
done <<< "$sections"
|
|
382
|
+
done <<< "$files"
|
|
383
|
+
|
|
384
|
+
echo ""
|
|
385
|
+
echo -e "${BOLD}Documentation Status:${RESET} ${fresh} fresh, ${stale} stale, ${total} total"
|
|
386
|
+
|
|
387
|
+
if [[ "$stale" -gt 0 ]]; then
|
|
388
|
+
warn "Run ${CYAN}shipwright docs sync${RESET} to update stale sections"
|
|
389
|
+
return 1
|
|
390
|
+
fi
|
|
391
|
+
success "All documentation sections are fresh"
|
|
392
|
+
return 0
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
# Regenerate all stale AUTO sections
|
|
396
|
+
docs_sync() {
|
|
397
|
+
info "Syncing documentation..."
|
|
398
|
+
local updated=0 total=0
|
|
399
|
+
|
|
400
|
+
local files
|
|
401
|
+
files=$(docs_find_auto_files)
|
|
402
|
+
|
|
403
|
+
while IFS= read -r file; do
|
|
404
|
+
[[ -z "$file" ]] && continue
|
|
405
|
+
local sections
|
|
406
|
+
sections=$(docs_get_sections "$file")
|
|
407
|
+
while IFS= read -r section; do
|
|
408
|
+
[[ -z "$section" ]] && continue
|
|
409
|
+
total=$((total + 1))
|
|
410
|
+
local expected
|
|
411
|
+
expected=$(docs_generate_section "$section")
|
|
412
|
+
if ! docs_check_section "$file" "$section" "$expected"; then
|
|
413
|
+
docs_replace_section "$file" "$section" "$expected"
|
|
414
|
+
updated=$((updated + 1))
|
|
415
|
+
local rel_file="${file#$REPO_DIR/}"
|
|
416
|
+
info "Updated: ${rel_file}#${section}"
|
|
417
|
+
fi
|
|
418
|
+
done <<< "$sections"
|
|
419
|
+
done <<< "$files"
|
|
420
|
+
|
|
421
|
+
if [[ "$updated" -gt 0 ]]; then
|
|
422
|
+
success "Updated ${updated}/${total} sections"
|
|
423
|
+
emit_event "docs.sync" "updated=$updated" "total=$total"
|
|
424
|
+
else
|
|
425
|
+
success "All ${total} sections already fresh"
|
|
426
|
+
fi
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
# Generate GitHub wiki pages
|
|
430
|
+
docs_wiki() {
|
|
431
|
+
local dry_run="${1:-false}"
|
|
432
|
+
local wiki_dir
|
|
433
|
+
wiki_dir=$(mktemp -d)
|
|
434
|
+
|
|
435
|
+
info "Generating wiki pages..."
|
|
436
|
+
|
|
437
|
+
# Home.md — from README intro
|
|
438
|
+
if [[ -f "$REPO_DIR/README.md" ]]; then
|
|
439
|
+
head -50 "$REPO_DIR/README.md" > "$wiki_dir/Home.md"
|
|
440
|
+
echo "" >> "$wiki_dir/Home.md"
|
|
441
|
+
echo "---" >> "$wiki_dir/Home.md"
|
|
442
|
+
echo "*Auto-generated by \`shipwright docs wiki\` on $(date -u +%Y-%m-%d)*" >> "$wiki_dir/Home.md"
|
|
443
|
+
fi
|
|
444
|
+
|
|
445
|
+
# Architecture.md
|
|
446
|
+
{
|
|
447
|
+
echo "# Architecture"
|
|
448
|
+
echo ""
|
|
449
|
+
echo "## Core Scripts"
|
|
450
|
+
docs_gen_architecture_table "core-scripts"
|
|
451
|
+
echo ""
|
|
452
|
+
echo "## GitHub API Modules"
|
|
453
|
+
docs_gen_architecture_table "github-modules"
|
|
454
|
+
echo ""
|
|
455
|
+
echo "## Issue Tracker Adapters"
|
|
456
|
+
docs_gen_architecture_table "tracker-adapters"
|
|
457
|
+
echo ""
|
|
458
|
+
echo "## Test Suites"
|
|
459
|
+
docs_gen_architecture_table "test-suites"
|
|
460
|
+
echo ""
|
|
461
|
+
echo "## Stats"
|
|
462
|
+
docs_gen_stats
|
|
463
|
+
echo ""
|
|
464
|
+
echo "---"
|
|
465
|
+
echo "*Auto-generated by \`shipwright docs wiki\` on $(date -u +%Y-%m-%d)*"
|
|
466
|
+
} > "$wiki_dir/Architecture.md"
|
|
467
|
+
|
|
468
|
+
# Commands.md
|
|
469
|
+
{
|
|
470
|
+
echo "# Commands"
|
|
471
|
+
docs_gen_command_table
|
|
472
|
+
echo ""
|
|
473
|
+
echo "---"
|
|
474
|
+
echo "*Auto-generated by \`shipwright docs wiki\` on $(date -u +%Y-%m-%d)*"
|
|
475
|
+
} > "$wiki_dir/Commands.md"
|
|
476
|
+
|
|
477
|
+
# Intelligence.md
|
|
478
|
+
{
|
|
479
|
+
echo "# Intelligence Layer"
|
|
480
|
+
echo ""
|
|
481
|
+
echo "## Feature Flags"
|
|
482
|
+
docs_gen_feature_flags
|
|
483
|
+
echo ""
|
|
484
|
+
echo "---"
|
|
485
|
+
echo "*Auto-generated by \`shipwright docs wiki\` on $(date -u +%Y-%m-%d)*"
|
|
486
|
+
} > "$wiki_dir/Intelligence.md"
|
|
487
|
+
|
|
488
|
+
# Configuration.md
|
|
489
|
+
{
|
|
490
|
+
echo "# Configuration"
|
|
491
|
+
echo ""
|
|
492
|
+
echo "## Runtime State & Artifacts"
|
|
493
|
+
docs_gen_file_locations
|
|
494
|
+
echo ""
|
|
495
|
+
echo "---"
|
|
496
|
+
echo "*Auto-generated by \`shipwright docs wiki\` on $(date -u +%Y-%m-%d)*"
|
|
497
|
+
} > "$wiki_dir/Configuration.md"
|
|
498
|
+
|
|
499
|
+
if [[ "$dry_run" == "true" ]] || [[ "$dry_run" == "--dry-run" ]]; then
|
|
500
|
+
info "Dry run — wiki pages generated in: $wiki_dir"
|
|
501
|
+
ls -la "$wiki_dir"
|
|
502
|
+
return 0
|
|
503
|
+
fi
|
|
504
|
+
|
|
505
|
+
# Push to GitHub wiki
|
|
506
|
+
if [[ "${NO_GITHUB:-}" == "true" ]] || ! command -v gh &>/dev/null; then
|
|
507
|
+
warn "GitHub not available — wiki pages saved to: $wiki_dir"
|
|
508
|
+
return 0
|
|
509
|
+
fi
|
|
510
|
+
|
|
511
|
+
local repo_url
|
|
512
|
+
repo_url=$(gh repo view --json url -q '.url' 2>/dev/null || true)
|
|
513
|
+
if [[ -z "$repo_url" ]]; then
|
|
514
|
+
warn "Could not determine repo URL — wiki pages saved to: $wiki_dir"
|
|
515
|
+
return 0
|
|
516
|
+
fi
|
|
517
|
+
|
|
518
|
+
local wiki_repo="${repo_url}.wiki.git"
|
|
519
|
+
local wiki_clone
|
|
520
|
+
wiki_clone=$(mktemp -d)
|
|
521
|
+
|
|
522
|
+
if git clone "$wiki_repo" "$wiki_clone" 2>/dev/null; then
|
|
523
|
+
cp "$wiki_dir"/*.md "$wiki_clone/"
|
|
524
|
+
( cd "$wiki_clone" && git add -A && git commit -m "docs: auto-update wiki via shipwright docs wiki" && git push ) 2>/dev/null || true
|
|
525
|
+
success "Wiki updated"
|
|
526
|
+
else
|
|
527
|
+
warn "Could not clone wiki repo — wiki pages saved to: $wiki_dir"
|
|
528
|
+
fi
|
|
529
|
+
|
|
530
|
+
rm -rf "$wiki_clone"
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
# Documentation freshness report
|
|
534
|
+
docs_report() {
|
|
535
|
+
echo ""
|
|
536
|
+
echo -e "${CYAN}${BOLD}shipwright${RESET} ${DIM}v${VERSION}${RESET} — ${BOLD}Documentation Report${RESET}"
|
|
537
|
+
echo -e "${CYAN}═══════════════════════════════════════════════${RESET}"
|
|
538
|
+
echo ""
|
|
539
|
+
|
|
540
|
+
# Stats
|
|
541
|
+
echo -e "${BOLD}Codebase Stats:${RESET}"
|
|
542
|
+
docs_gen_stats
|
|
543
|
+
echo ""
|
|
544
|
+
|
|
545
|
+
# AUTO section status
|
|
546
|
+
echo -e "${BOLD}AUTO Section Status:${RESET}"
|
|
547
|
+
local files
|
|
548
|
+
files=$(docs_find_auto_files)
|
|
549
|
+
if [[ -z "$files" ]]; then
|
|
550
|
+
echo " No AUTO sections found"
|
|
551
|
+
else
|
|
552
|
+
while IFS= read -r file; do
|
|
553
|
+
[[ -z "$file" ]] && continue
|
|
554
|
+
local rel_file="${file#$REPO_DIR/}"
|
|
555
|
+
local sections
|
|
556
|
+
sections=$(docs_get_sections "$file")
|
|
557
|
+
while IFS= read -r section; do
|
|
558
|
+
[[ -z "$section" ]] && continue
|
|
559
|
+
local expected
|
|
560
|
+
expected=$(docs_generate_section "$section")
|
|
561
|
+
if docs_check_section "$file" "$section" "$expected"; then
|
|
562
|
+
echo -e " ${GREEN}✓${RESET} ${rel_file}#${section}"
|
|
563
|
+
else
|
|
564
|
+
echo -e " ${YELLOW}⚠${RESET} ${rel_file}#${section} ${DIM}(stale)${RESET}"
|
|
565
|
+
fi
|
|
566
|
+
done <<< "$sections"
|
|
567
|
+
done <<< "$files"
|
|
568
|
+
fi
|
|
569
|
+
|
|
570
|
+
echo ""
|
|
571
|
+
|
|
572
|
+
# File freshness
|
|
573
|
+
echo -e "${BOLD}Document Freshness:${RESET}"
|
|
574
|
+
for doc in README.md .claude/CLAUDE.md CHANGELOG.md; do
|
|
575
|
+
local full_path="$REPO_DIR/$doc"
|
|
576
|
+
[[ -f "$full_path" ]] || continue
|
|
577
|
+
local last_modified
|
|
578
|
+
last_modified=$(git -C "$REPO_DIR" log -1 --format='%cr' -- "$doc" 2>/dev/null || echo "unknown")
|
|
579
|
+
echo " ${doc}: last modified ${last_modified}"
|
|
580
|
+
done
|
|
581
|
+
echo ""
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
# ─── Help & Main ─────────────────────────────────────────────────────────────
|
|
585
|
+
|
|
586
|
+
show_help() {
|
|
587
|
+
echo -e "${CYAN}${BOLD}shipwright docs${RESET} — Documentation Keeper"
|
|
588
|
+
echo ""
|
|
589
|
+
echo -e "${BOLD}USAGE${RESET}"
|
|
590
|
+
echo -e " ${CYAN}shipwright docs${RESET} <command>"
|
|
591
|
+
echo ""
|
|
592
|
+
echo -e "${BOLD}COMMANDS${RESET}"
|
|
593
|
+
echo -e " ${CYAN}sync${RESET} Regenerate all AUTO sections in markdown files"
|
|
594
|
+
echo -e " ${CYAN}check${RESET} Check which sections are stale (exit 1 if any)"
|
|
595
|
+
echo -e " ${CYAN}wiki${RESET} Generate/update GitHub wiki pages"
|
|
596
|
+
echo -e " ${CYAN}report${RESET} Show documentation freshness report"
|
|
597
|
+
echo -e " ${CYAN}help${RESET} Show this help"
|
|
598
|
+
echo ""
|
|
599
|
+
echo -e "${BOLD}AUTO MARKERS${RESET}"
|
|
600
|
+
echo -e " Place markers in any .md file to auto-generate content:"
|
|
601
|
+
echo -e " ${DIM}<!-- AUTO:core-scripts -->${RESET}"
|
|
602
|
+
echo -e " ${DIM}(auto-generated table)${RESET}"
|
|
603
|
+
echo -e " ${DIM}<!-- /AUTO:core-scripts -->${RESET}"
|
|
604
|
+
echo ""
|
|
605
|
+
echo -e "${BOLD}SECTIONS${RESET}"
|
|
606
|
+
echo -e " ${CYAN}core-scripts${RESET} Architecture table of core scripts"
|
|
607
|
+
echo -e " ${CYAN}github-modules${RESET} Architecture table of GitHub API modules"
|
|
608
|
+
echo -e " ${CYAN}tracker-adapters${RESET} Architecture table of tracker adapters"
|
|
609
|
+
echo -e " ${CYAN}test-suites${RESET} Architecture table of test suites"
|
|
610
|
+
echo -e " ${CYAN}commands-table${RESET} Command reference from CLI router"
|
|
611
|
+
echo -e " ${CYAN}test-list${RESET} Numbered test suite list from package.json"
|
|
612
|
+
echo -e " ${CYAN}feature-flags${RESET} Intelligence feature flags"
|
|
613
|
+
echo -e " ${CYAN}runtime-state${RESET} Runtime state file locations"
|
|
614
|
+
echo -e " ${CYAN}stats${RESET} Codebase statistics"
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
main() {
|
|
618
|
+
local cmd="${1:-help}"
|
|
619
|
+
shift 2>/dev/null || true
|
|
620
|
+
|
|
621
|
+
case "$cmd" in
|
|
622
|
+
sync) docs_sync "$@" ;;
|
|
623
|
+
check) docs_check "$@" ;;
|
|
624
|
+
wiki) docs_wiki "$@" ;;
|
|
625
|
+
report) docs_report "$@" ;;
|
|
626
|
+
help|--help|-h) show_help ;;
|
|
627
|
+
*)
|
|
628
|
+
error "Unknown command: $cmd"
|
|
629
|
+
show_help
|
|
630
|
+
exit 1
|
|
631
|
+
;;
|
|
632
|
+
esac
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
main "$@"
|