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,569 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
# ║ shipwright guild — Knowledge Guilds & Cross-Team Learning ║
|
|
4
|
+
# ║ Patterns · Best Practices · Cross-Pollination · Guild Intelligence ║
|
|
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
|
+
|
|
12
|
+
# ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
|
|
13
|
+
CYAN='\033[38;2;0;212;255m' # #00d4ff — primary accent
|
|
14
|
+
PURPLE='\033[38;2;124;58;237m' # #7c3aed — secondary
|
|
15
|
+
BLUE='\033[38;2;0;102;255m' # #0066ff — tertiary
|
|
16
|
+
GREEN='\033[38;2;74;222;128m' # success
|
|
17
|
+
YELLOW='\033[38;2;250;204;21m' # warning
|
|
18
|
+
RED='\033[38;2;248;113;113m' # error
|
|
19
|
+
DIM='\033[2m'
|
|
20
|
+
BOLD='\033[1m'
|
|
21
|
+
RESET='\033[0m'
|
|
22
|
+
|
|
23
|
+
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
|
24
|
+
# shellcheck source=lib/compat.sh
|
|
25
|
+
[[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
|
|
26
|
+
|
|
27
|
+
# ─── Output Helpers ─────────────────────────────────────────────────────────
|
|
28
|
+
info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
|
|
29
|
+
success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
|
|
30
|
+
warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*" >&2; }
|
|
31
|
+
error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
|
|
32
|
+
|
|
33
|
+
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
34
|
+
|
|
35
|
+
# ─── Guild Storage Paths ───────────────────────────────────────────────────
|
|
36
|
+
GUILD_ROOT="${HOME}/.shipwright/guilds"
|
|
37
|
+
GUILD_CONFIG="${GUILD_ROOT}/config.json"
|
|
38
|
+
GUILD_DATA="${GUILD_ROOT}/guilds.json"
|
|
39
|
+
|
|
40
|
+
# ─── Event Logging ────────────────────────────────────────────────────────
|
|
41
|
+
EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
|
|
42
|
+
|
|
43
|
+
emit_event() {
|
|
44
|
+
local event_type="$1"
|
|
45
|
+
shift
|
|
46
|
+
local json_fields=""
|
|
47
|
+
for kv in "$@"; do
|
|
48
|
+
local key="${kv%%=*}"
|
|
49
|
+
local val="${kv#*=}"
|
|
50
|
+
if [[ "$val" =~ ^-?[0-9]+\.?[0-9]*$ ]]; then
|
|
51
|
+
json_fields="${json_fields},\"${key}\":${val}"
|
|
52
|
+
else
|
|
53
|
+
val="${val//\"/\\\"}"
|
|
54
|
+
json_fields="${json_fields},\"${key}\":\"${val}\""
|
|
55
|
+
fi
|
|
56
|
+
done
|
|
57
|
+
mkdir -p "${HOME}/.shipwright"
|
|
58
|
+
echo "{\"ts\":\"$(now_iso)\",\"type\":\"${event_type}\"${json_fields}}" >> "$EVENTS_FILE"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# ─── Initialization ────────────────────────────────────────────────────────
|
|
62
|
+
ensure_guild_dir() {
|
|
63
|
+
mkdir -p "$GUILD_ROOT"
|
|
64
|
+
|
|
65
|
+
if [[ ! -f "$GUILD_CONFIG" ]]; then
|
|
66
|
+
cat > "$GUILD_CONFIG" << 'EOF'
|
|
67
|
+
{
|
|
68
|
+
"version": "1.0",
|
|
69
|
+
"created_at": "NOW",
|
|
70
|
+
"guild_definitions": {
|
|
71
|
+
"security": {
|
|
72
|
+
"description": "Security patterns, vulnerability fixes, threat modeling",
|
|
73
|
+
"members": [],
|
|
74
|
+
"pattern_count": 0,
|
|
75
|
+
"practice_count": 0
|
|
76
|
+
},
|
|
77
|
+
"performance": {
|
|
78
|
+
"description": "Optimization patterns, caching, bottleneck analysis",
|
|
79
|
+
"members": [],
|
|
80
|
+
"pattern_count": 0,
|
|
81
|
+
"practice_count": 0
|
|
82
|
+
},
|
|
83
|
+
"testing": {
|
|
84
|
+
"description": "Test strategies, coverage patterns, edge cases",
|
|
85
|
+
"members": [],
|
|
86
|
+
"pattern_count": 0,
|
|
87
|
+
"practice_count": 0
|
|
88
|
+
},
|
|
89
|
+
"architecture": {
|
|
90
|
+
"description": "Design patterns, layering, dependency management",
|
|
91
|
+
"members": [],
|
|
92
|
+
"pattern_count": 0,
|
|
93
|
+
"practice_count": 0
|
|
94
|
+
},
|
|
95
|
+
"documentation": {
|
|
96
|
+
"description": "Doc patterns, clarity, examples, API docs",
|
|
97
|
+
"members": [],
|
|
98
|
+
"pattern_count": 0,
|
|
99
|
+
"practice_count": 0
|
|
100
|
+
},
|
|
101
|
+
"reliability": {
|
|
102
|
+
"description": "Error handling, observability, resilience patterns",
|
|
103
|
+
"members": [],
|
|
104
|
+
"pattern_count": 0,
|
|
105
|
+
"practice_count": 0
|
|
106
|
+
},
|
|
107
|
+
"cost-optimization": {
|
|
108
|
+
"description": "Resource efficiency, token optimization, cost patterns",
|
|
109
|
+
"members": [],
|
|
110
|
+
"pattern_count": 0,
|
|
111
|
+
"practice_count": 0
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
EOF
|
|
116
|
+
sed -i "" "s/NOW/$(date -u +"%Y-%m-%dT%H:%M:%SZ")/g" "$GUILD_CONFIG" 2>/dev/null || true
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
if [[ ! -f "$GUILD_DATA" ]]; then
|
|
120
|
+
echo '{"patterns":{},"practices":{},"cross_pollination":[]}' > "$GUILD_DATA"
|
|
121
|
+
fi
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# ─── List Guilds ──────────────────────────────────────────────────────────
|
|
125
|
+
cmd_list() {
|
|
126
|
+
ensure_guild_dir
|
|
127
|
+
|
|
128
|
+
info "Available Guilds"
|
|
129
|
+
echo ""
|
|
130
|
+
|
|
131
|
+
jq -r '.guild_definitions | to_entries[] |
|
|
132
|
+
"\(.key | ascii_upcase): \(.value.description)\n Patterns: \(.value.pattern_count) | Practices: \(.value.practice_count)"' \
|
|
133
|
+
"$GUILD_CONFIG" 2>/dev/null | while read -r line; do
|
|
134
|
+
if [[ "$line" =~ ^[A-Z] ]]; then
|
|
135
|
+
echo -e "${CYAN}${BOLD}${line}${RESET}"
|
|
136
|
+
else
|
|
137
|
+
echo -e " ${DIM}${line}${RESET}"
|
|
138
|
+
fi
|
|
139
|
+
done
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# ─── Show Guild Details ───────────────────────────────────────────────────
|
|
143
|
+
cmd_show() {
|
|
144
|
+
local guild="${1:-}"
|
|
145
|
+
|
|
146
|
+
if [[ -z "$guild" ]]; then
|
|
147
|
+
error "Guild name required. Usage: shipwright guild show <guild>"
|
|
148
|
+
return 1
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
ensure_guild_dir
|
|
152
|
+
|
|
153
|
+
# Validate guild exists
|
|
154
|
+
if ! jq -e ".guild_definitions[\"$guild\"]" "$GUILD_CONFIG" >/dev/null 2>&1; then
|
|
155
|
+
error "Guild not found: $guild"
|
|
156
|
+
return 1
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
info "Guild: ${CYAN}${BOLD}${guild}${RESET}"
|
|
160
|
+
|
|
161
|
+
jq -r ".guild_definitions[\"$guild\"] |
|
|
162
|
+
\"Description: \(.description)\n\" +
|
|
163
|
+
\"Members: \(.members | length)\n\" +
|
|
164
|
+
\"Patterns: \(.pattern_count)\n\" +
|
|
165
|
+
\"Practices: \(.practice_count)\"" \
|
|
166
|
+
"$GUILD_CONFIG"
|
|
167
|
+
|
|
168
|
+
echo ""
|
|
169
|
+
info "Patterns:"
|
|
170
|
+
jq -r ".patterns[\"$guild\"] // [] | .[] |
|
|
171
|
+
\" • \(.title) (confidence: \(.confidence | tostring)%, used \(.usage_count) times)\"" \
|
|
172
|
+
"$GUILD_DATA" | head -10 || echo " ${DIM}(none)${RESET}"
|
|
173
|
+
|
|
174
|
+
echo ""
|
|
175
|
+
info "Best Practices:"
|
|
176
|
+
jq -r ".practices[\"$guild\"] // [] | .[] |
|
|
177
|
+
\" • \(.title) (confidence: \(.confidence | tostring)%, adopted \(.adoption_count) times)\"" \
|
|
178
|
+
"$GUILD_DATA" | head -10 || echo " ${DIM}(none)${RESET}"
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# ─── Search Knowledge Base ────────────────────────────────────────────────
|
|
182
|
+
cmd_search() {
|
|
183
|
+
local query="${1:-}"
|
|
184
|
+
local domain="${2:-}"
|
|
185
|
+
|
|
186
|
+
if [[ -z "$query" ]]; then
|
|
187
|
+
error "Search query required. Usage: shipwright guild search <query> [domain]"
|
|
188
|
+
return 1
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
ensure_guild_dir
|
|
192
|
+
|
|
193
|
+
info "Searching knowledge base for: ${CYAN}${query}${RESET}"
|
|
194
|
+
[[ -n "$domain" ]] && info "Domain filter: ${CYAN}${domain}${RESET}"
|
|
195
|
+
|
|
196
|
+
echo ""
|
|
197
|
+
|
|
198
|
+
# Search patterns
|
|
199
|
+
local pattern_count=0
|
|
200
|
+
jq -r ".patterns | to_entries[] |
|
|
201
|
+
select(.value | length > 0) |
|
|
202
|
+
select((.key | contains(\"$domain\")) or (\"$domain\" == \"\")) |
|
|
203
|
+
.value[] |
|
|
204
|
+
select((.title | test(\"$query\"; \"i\")) or (.description | test(\"$query\"; \"i\"))) |
|
|
205
|
+
\"PATTERN: \(.title) [\(.source.pipeline // \"unknown\")]\\n \(.description)\"" \
|
|
206
|
+
"$GUILD_DATA" 2>/dev/null | while read -r line; do
|
|
207
|
+
echo -e "${GREEN}${line}${RESET}"
|
|
208
|
+
done
|
|
209
|
+
pattern_count=$(jq -r ".patterns | to_entries[] |
|
|
210
|
+
select(.value | length > 0) |
|
|
211
|
+
select((.key | contains(\"$domain\")) or (\"$domain\" == \"\")) |
|
|
212
|
+
.value[] |
|
|
213
|
+
select((.title | test(\"$query\"; \"i\")) or (.description | test(\"$query\"; \"i\"))) | \"1\"" \
|
|
214
|
+
"$GUILD_DATA" 2>/dev/null | wc -l)
|
|
215
|
+
|
|
216
|
+
# Search practices
|
|
217
|
+
jq -r ".practices | to_entries[] |
|
|
218
|
+
select(.value | length > 0) |
|
|
219
|
+
select((.key | contains(\"$domain\")) or (\"$domain\" == \"\")) |
|
|
220
|
+
.value[] |
|
|
221
|
+
select((.title | test(\"$query\"; \"i\")) or (.description | test(\"$query\"; \"i\"))) |
|
|
222
|
+
\"PRACTICE: \(.title) [confidence: \(.confidence)%]\\n \(.description)\"" \
|
|
223
|
+
"$GUILD_DATA" 2>/dev/null | while read -r line; do
|
|
224
|
+
echo -e "${BLUE}${line}${RESET}"
|
|
225
|
+
done
|
|
226
|
+
|
|
227
|
+
local practice_count=0
|
|
228
|
+
practice_count=$(jq -r ".practices | to_entries[] |
|
|
229
|
+
select(.value | length > 0) |
|
|
230
|
+
select((.key | contains(\"$domain\")) or (\"$domain\" == \"\")) |
|
|
231
|
+
.value[] |
|
|
232
|
+
select((.title | test(\"$query\"; \"i\")) or (.description | test(\"$query\"; \"i\"))) | \"1\"" \
|
|
233
|
+
"$GUILD_DATA" 2>/dev/null | wc -l)
|
|
234
|
+
|
|
235
|
+
if [[ $pattern_count -eq 0 && $practice_count -eq 0 ]]; then
|
|
236
|
+
warn "No matches found"
|
|
237
|
+
return 1
|
|
238
|
+
fi
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
# ─── Add Pattern or Practice ──────────────────────────────────────────────
|
|
242
|
+
cmd_add() {
|
|
243
|
+
local type="${1:-}"
|
|
244
|
+
local guild="${2:-}"
|
|
245
|
+
local title="${3:-}"
|
|
246
|
+
|
|
247
|
+
if [[ -z "$type" || -z "$guild" || -z "$title" ]]; then
|
|
248
|
+
error "Usage: shipwright guild add <pattern|practice> <guild> <title>"
|
|
249
|
+
return 1
|
|
250
|
+
fi
|
|
251
|
+
|
|
252
|
+
[[ "$type" != "pattern" && "$type" != "practice" ]] && { error "Type must be pattern or practice"; return 1; }
|
|
253
|
+
|
|
254
|
+
ensure_guild_dir
|
|
255
|
+
|
|
256
|
+
if ! jq -e ".guild_definitions[\"$guild\"]" "$GUILD_CONFIG" >/dev/null 2>&1; then
|
|
257
|
+
error "Guild not found: $guild"
|
|
258
|
+
return 1
|
|
259
|
+
fi
|
|
260
|
+
|
|
261
|
+
# Read description from stdin if piped
|
|
262
|
+
local description="${4:-}"
|
|
263
|
+
if [[ -z "$description" && -t 0 ]]; then
|
|
264
|
+
echo -n "Description: "
|
|
265
|
+
read -r description
|
|
266
|
+
fi
|
|
267
|
+
|
|
268
|
+
local tmp_file
|
|
269
|
+
tmp_file=$(mktemp "$GUILD_DATA.tmp.XXXXXX")
|
|
270
|
+
|
|
271
|
+
if [[ "$type" == "pattern" ]]; then
|
|
272
|
+
jq --arg guild "$guild" \
|
|
273
|
+
--arg title "$title" \
|
|
274
|
+
--arg desc "$description" \
|
|
275
|
+
--arg ts "$(now_iso)" \
|
|
276
|
+
".patterns[\$guild] //= [] |
|
|
277
|
+
.patterns[\$guild] += [{
|
|
278
|
+
title: \$title,
|
|
279
|
+
description: \$desc,
|
|
280
|
+
confidence: 75,
|
|
281
|
+
usage_count: 1,
|
|
282
|
+
source: {pipeline: \"manual\", created_at: \$ts},
|
|
283
|
+
tags: []
|
|
284
|
+
}]" \
|
|
285
|
+
"$GUILD_DATA" > "$tmp_file" && mv "$tmp_file" "$GUILD_DATA"
|
|
286
|
+
success "Pattern added to ${CYAN}${guild}${RESET}"
|
|
287
|
+
else
|
|
288
|
+
jq --arg guild "$guild" \
|
|
289
|
+
--arg title "$title" \
|
|
290
|
+
--arg desc "$description" \
|
|
291
|
+
--arg ts "$(now_iso)" \
|
|
292
|
+
".practices[\$guild] //= [] |
|
|
293
|
+
.practices[\$guild] += [{
|
|
294
|
+
title: \$title,
|
|
295
|
+
description: \$desc,
|
|
296
|
+
confidence: 80,
|
|
297
|
+
adoption_count: 0,
|
|
298
|
+
source: {pipeline: \"manual\", created_at: \$ts},
|
|
299
|
+
tags: []
|
|
300
|
+
}]" \
|
|
301
|
+
"$GUILD_DATA" > "$tmp_file" && mv "$tmp_file" "$GUILD_DATA"
|
|
302
|
+
success "Practice added to ${CYAN}${guild}${RESET}"
|
|
303
|
+
fi
|
|
304
|
+
|
|
305
|
+
emit_event "guild.add" "type=${type}" "guild=${guild}" "title=${title}"
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
# ─── Learn from Pipeline ──────────────────────────────────────────────────
|
|
309
|
+
cmd_learn() {
|
|
310
|
+
local pipeline_dir="${1:-}"
|
|
311
|
+
|
|
312
|
+
if [[ -z "$pipeline_dir" || ! -d "$pipeline_dir" ]]; then
|
|
313
|
+
error "Pipeline artifacts directory required. Usage: shipwright guild learn <artifacts_dir>"
|
|
314
|
+
return 1
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
ensure_guild_dir
|
|
318
|
+
|
|
319
|
+
info "Extracting learnings from pipeline: ${CYAN}${pipeline_dir}${RESET}"
|
|
320
|
+
|
|
321
|
+
# This would be called by the pipeline with artifacts directory
|
|
322
|
+
# For now, emit a learning event that signals a Claude agent to analyze
|
|
323
|
+
emit_event "guild.learn_requested" "pipeline_dir=${pipeline_dir}"
|
|
324
|
+
|
|
325
|
+
success "Learning extraction queued (requires Claude analysis)"
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
# ─── Inject Knowledge into Prompt ────────────────────────────────────────
|
|
329
|
+
cmd_inject() {
|
|
330
|
+
local task_type="${1:-}"
|
|
331
|
+
local context="${2:-}"
|
|
332
|
+
|
|
333
|
+
if [[ -z "$task_type" ]]; then
|
|
334
|
+
error "Task type required. Usage: shipwright guild inject <task_type> [context]"
|
|
335
|
+
return 1
|
|
336
|
+
fi
|
|
337
|
+
|
|
338
|
+
ensure_guild_dir
|
|
339
|
+
|
|
340
|
+
info "Relevant guild knowledge for ${CYAN}${task_type}${RESET}:"
|
|
341
|
+
echo ""
|
|
342
|
+
|
|
343
|
+
# Map task types to relevant guilds
|
|
344
|
+
case "$task_type" in
|
|
345
|
+
security|auth|vulnerability)
|
|
346
|
+
echo "# Security Guild Knowledge"
|
|
347
|
+
jq -r ".practices.security // [] | .[0:3] | .[] |
|
|
348
|
+
\"- \(.title): \(.description)\"" "$GUILD_DATA"
|
|
349
|
+
;;
|
|
350
|
+
performance|optimization)
|
|
351
|
+
echo "# Performance Guild Knowledge"
|
|
352
|
+
jq -r ".practices.performance // [] | .[0:3] | .[] |
|
|
353
|
+
\"- \(.title): \(.description)\"" "$GUILD_DATA"
|
|
354
|
+
;;
|
|
355
|
+
testing|test|coverage)
|
|
356
|
+
echo "# Testing Guild Knowledge"
|
|
357
|
+
jq -r ".practices.testing // [] | .[0:3] | .[] |
|
|
358
|
+
\"- \(.title): \(.description)\"" "$GUILD_DATA"
|
|
359
|
+
;;
|
|
360
|
+
architecture|design|refactor)
|
|
361
|
+
echo "# Architecture Guild Knowledge"
|
|
362
|
+
jq -r ".practices.architecture // [] | .[0:3] | .[] |
|
|
363
|
+
\"- \(.title): \(.description)\"" "$GUILD_DATA"
|
|
364
|
+
;;
|
|
365
|
+
docs|documentation)
|
|
366
|
+
echo "# Documentation Guild Knowledge"
|
|
367
|
+
jq -r ".practices.documentation // [] | .[0:3] | .[] |
|
|
368
|
+
\"- \(.title): \(.description)\"" "$GUILD_DATA"
|
|
369
|
+
;;
|
|
370
|
+
reliability|error|observability)
|
|
371
|
+
echo "# Reliability Guild Knowledge"
|
|
372
|
+
jq -r ".practices.reliability // [] | .[0:3] | .[] |
|
|
373
|
+
\"- \(.title): \(.description)\"" "$GUILD_DATA"
|
|
374
|
+
;;
|
|
375
|
+
cost|budget|tokens)
|
|
376
|
+
echo "# Cost Optimization Guild Knowledge"
|
|
377
|
+
jq -r ".practices.cost-optimization // [] | .[0:3] | .[] |
|
|
378
|
+
\"- \(.title): \(.description)\"" "$GUILD_DATA"
|
|
379
|
+
;;
|
|
380
|
+
*)
|
|
381
|
+
info "Top practices across all guilds:"
|
|
382
|
+
jq -r ".practices | to_entries[] | .value[] |
|
|
383
|
+
select(.confidence >= 80) |
|
|
384
|
+
\"- \(.title) [\(.source.pipeline // \"unknown\")]\"" \
|
|
385
|
+
"$GUILD_DATA" | head -5 || true
|
|
386
|
+
;;
|
|
387
|
+
esac
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
# ─── Guild Reports ───────────────────────────────────────────────────────
|
|
391
|
+
cmd_report() {
|
|
392
|
+
local guild="${1:-all}"
|
|
393
|
+
|
|
394
|
+
ensure_guild_dir
|
|
395
|
+
|
|
396
|
+
if [[ "$guild" == "all" ]]; then
|
|
397
|
+
info "Guild Knowledge Growth Report"
|
|
398
|
+
echo ""
|
|
399
|
+
|
|
400
|
+
jq -r '.guild_definitions | to_entries[] |
|
|
401
|
+
"\(.key | ascii_upcase):\n Patterns: \(.value.pattern_count)\n Practices: \(.value.practice_count)"' \
|
|
402
|
+
"$GUILD_CONFIG" | while read -r line; do
|
|
403
|
+
if [[ "$line" =~ ^[A-Z] ]]; then
|
|
404
|
+
echo -e "${CYAN}${BOLD}${line}${RESET}"
|
|
405
|
+
else
|
|
406
|
+
echo -e " ${DIM}${line}${RESET}"
|
|
407
|
+
fi
|
|
408
|
+
done
|
|
409
|
+
else
|
|
410
|
+
if ! jq -e ".guild_definitions[\"$guild\"]" "$GUILD_CONFIG" >/dev/null 2>&1; then
|
|
411
|
+
error "Guild not found: $guild"
|
|
412
|
+
return 1
|
|
413
|
+
fi
|
|
414
|
+
|
|
415
|
+
info "Guild Report: ${CYAN}${BOLD}${guild}${RESET}"
|
|
416
|
+
echo ""
|
|
417
|
+
|
|
418
|
+
local pattern_count
|
|
419
|
+
pattern_count=$(jq -r ".patterns[\"$guild\"] // [] | length" "$GUILD_DATA")
|
|
420
|
+
local practice_count
|
|
421
|
+
practice_count=$(jq -r ".practices[\"$guild\"] // [] | length" "$GUILD_DATA")
|
|
422
|
+
|
|
423
|
+
echo -e " Patterns: ${CYAN}${pattern_count}${RESET}"
|
|
424
|
+
echo -e " Practices: ${CYAN}${practice_count}${RESET}"
|
|
425
|
+
|
|
426
|
+
local avg_conf
|
|
427
|
+
avg_conf=$(jq -r ".practices[\"$guild\"] // [] |
|
|
428
|
+
if length > 0 then map(.confidence) | add / length | floor else 0 end" \
|
|
429
|
+
"$GUILD_DATA")
|
|
430
|
+
echo -e " Avg Confidence: ${GREEN}${avg_conf}%${RESET}"
|
|
431
|
+
fi
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
# ─── Export Knowledge ────────────────────────────────────────────────────
|
|
435
|
+
cmd_export() {
|
|
436
|
+
local format="${1:-json}"
|
|
437
|
+
local output_file="${2:-}"
|
|
438
|
+
|
|
439
|
+
[[ "$format" != "json" && "$format" != "markdown" ]] && { error "Format must be json or markdown"; return 1; }
|
|
440
|
+
|
|
441
|
+
ensure_guild_dir
|
|
442
|
+
|
|
443
|
+
if [[ -z "$output_file" ]]; then
|
|
444
|
+
output_file="${GUILD_ROOT}/export.${format}"
|
|
445
|
+
fi
|
|
446
|
+
|
|
447
|
+
if [[ "$format" == "json" ]]; then
|
|
448
|
+
cp "$GUILD_DATA" "$output_file"
|
|
449
|
+
success "Exported to ${CYAN}${output_file}${RESET}"
|
|
450
|
+
else
|
|
451
|
+
{
|
|
452
|
+
echo "# Shipwright Guild Knowledge Base"
|
|
453
|
+
echo ""
|
|
454
|
+
echo "Generated: $(date)"
|
|
455
|
+
echo ""
|
|
456
|
+
|
|
457
|
+
jq -r '.guild_definitions | keys[]' "$GUILD_CONFIG" | while read -r guild; do
|
|
458
|
+
local guild_title
|
|
459
|
+
guild_title=$(echo "$guild" | sed 's/^./\U&/')
|
|
460
|
+
echo "## $guild_title"
|
|
461
|
+
jq -r ".guild_definitions[\"$guild\"].description" "$GUILD_CONFIG"
|
|
462
|
+
echo ""
|
|
463
|
+
|
|
464
|
+
echo "### Patterns"
|
|
465
|
+
jq -r ".patterns[\"$guild\"] // [] | .[] |
|
|
466
|
+
\"- **\(.title)**: \(.description) (confidence: \(.confidence)%, used \(.usage_count) times)\"" \
|
|
467
|
+
"$GUILD_DATA" || echo "No patterns yet."
|
|
468
|
+
echo ""
|
|
469
|
+
|
|
470
|
+
echo "### Best Practices"
|
|
471
|
+
jq -r ".practices[\"$guild\"] // [] | .[] |
|
|
472
|
+
\"- **\(.title)**: \(.description) (confidence: \(.confidence)%, adopted \(.adoption_count) times)\"" \
|
|
473
|
+
"$GUILD_DATA" || echo "No practices yet."
|
|
474
|
+
echo ""
|
|
475
|
+
done
|
|
476
|
+
} > "$output_file"
|
|
477
|
+
success "Exported to ${CYAN}${output_file}${RESET}"
|
|
478
|
+
fi
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
# ─── Help ──────────────────────────────────────────────────────────────────
|
|
482
|
+
show_help() {
|
|
483
|
+
cat << EOF
|
|
484
|
+
${CYAN}${BOLD}shipwright guild${RESET} — Knowledge Guilds & Cross-Team Learning
|
|
485
|
+
|
|
486
|
+
${BOLD}USAGE${RESET}
|
|
487
|
+
${CYAN}shipwright guild${RESET} <command> [options]
|
|
488
|
+
|
|
489
|
+
${BOLD}COMMANDS${RESET}
|
|
490
|
+
${CYAN}list${RESET} List all guilds and their knowledge stats
|
|
491
|
+
${CYAN}show${RESET} <guild> Show guild details, patterns, and practices
|
|
492
|
+
${CYAN}search${RESET} <query> Search knowledge base by keyword
|
|
493
|
+
${CYAN}add${RESET} <type> <guild> <title>
|
|
494
|
+
Manually add a pattern or best practice
|
|
495
|
+
${CYAN}learn${RESET} <dir> Extract patterns from pipeline artifacts
|
|
496
|
+
${CYAN}inject${RESET} <task> Show knowledge to inject for a task type
|
|
497
|
+
${CYAN}report${RESET} [guild] Guild knowledge growth report
|
|
498
|
+
${CYAN}export${RESET} [format] Export knowledge as JSON or Markdown
|
|
499
|
+
${CYAN}help${RESET} Show this help message
|
|
500
|
+
|
|
501
|
+
${BOLD}GUILDS${RESET}
|
|
502
|
+
• ${CYAN}security${RESET} Security patterns, vulnerability fixes
|
|
503
|
+
• ${CYAN}performance${RESET} Optimization patterns, caching strategies
|
|
504
|
+
• ${CYAN}testing${RESET} Test strategies, coverage patterns
|
|
505
|
+
• ${CYAN}architecture${RESET} Design patterns, layering rules
|
|
506
|
+
• ${CYAN}documentation${RESET} Doc patterns, clarity guidelines
|
|
507
|
+
• ${CYAN}reliability${RESET} Error handling, observability patterns
|
|
508
|
+
• ${CYAN}cost-optimization${RESET} Resource efficiency patterns
|
|
509
|
+
|
|
510
|
+
${BOLD}EXAMPLES${RESET}
|
|
511
|
+
${DIM}shipwright guild list${RESET}
|
|
512
|
+
${DIM}shipwright guild show security${RESET}
|
|
513
|
+
${DIM}shipwright guild search "error handling" reliability${RESET}
|
|
514
|
+
${DIM}shipwright guild add pattern testing "Unit test template" < description.txt${RESET}
|
|
515
|
+
${DIM}shipwright guild inject security${RESET}
|
|
516
|
+
${DIM}shipwright guild report performance${RESET}
|
|
517
|
+
${DIM}shipwright guild export markdown knowledge-base.md${RESET}
|
|
518
|
+
|
|
519
|
+
EOF
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
# ─── Main Router ───────────────────────────────────────────────────────────
|
|
523
|
+
main() {
|
|
524
|
+
local cmd="${1:-}"
|
|
525
|
+
|
|
526
|
+
case "$cmd" in
|
|
527
|
+
list)
|
|
528
|
+
cmd_list
|
|
529
|
+
;;
|
|
530
|
+
show)
|
|
531
|
+
cmd_show "${2:-}"
|
|
532
|
+
;;
|
|
533
|
+
search)
|
|
534
|
+
cmd_search "${2:-}" "${3:-}"
|
|
535
|
+
;;
|
|
536
|
+
add)
|
|
537
|
+
cmd_add "${2:-}" "${3:-}" "${4:-}" "${5:-}"
|
|
538
|
+
;;
|
|
539
|
+
learn)
|
|
540
|
+
cmd_learn "${2:-}"
|
|
541
|
+
;;
|
|
542
|
+
inject)
|
|
543
|
+
cmd_inject "${2:-}" "${3:-}"
|
|
544
|
+
;;
|
|
545
|
+
report)
|
|
546
|
+
cmd_report "${2:-all}"
|
|
547
|
+
;;
|
|
548
|
+
export)
|
|
549
|
+
cmd_export "${2:-json}" "${3:-}"
|
|
550
|
+
;;
|
|
551
|
+
help|--help|-h)
|
|
552
|
+
show_help
|
|
553
|
+
;;
|
|
554
|
+
*)
|
|
555
|
+
if [[ -z "$cmd" ]]; then
|
|
556
|
+
show_help
|
|
557
|
+
else
|
|
558
|
+
error "Unknown command: ${cmd}"
|
|
559
|
+
echo ""
|
|
560
|
+
show_help
|
|
561
|
+
exit 1
|
|
562
|
+
fi
|
|
563
|
+
;;
|
|
564
|
+
esac
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
|
568
|
+
main "$@"
|
|
569
|
+
fi
|
package/scripts/sw-heartbeat.sh
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="
|
|
9
|
+
VERSION="2.0.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
|
|
12
12
|
# ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
|