clawpowers 1.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.
Files changed (42) hide show
  1. package/.claude-plugin/manifest.json +19 -0
  2. package/.codex/INSTALL.md +36 -0
  3. package/.cursor-plugin/manifest.json +21 -0
  4. package/.opencode/INSTALL.md +52 -0
  5. package/ARCHITECTURE.md +69 -0
  6. package/README.md +381 -0
  7. package/bin/clawpowers.js +390 -0
  8. package/bin/clawpowers.sh +91 -0
  9. package/gemini-extension.json +32 -0
  10. package/hooks/session-start +205 -0
  11. package/hooks/session-start.cmd +43 -0
  12. package/hooks/session-start.js +163 -0
  13. package/package.json +54 -0
  14. package/runtime/feedback/analyze.js +621 -0
  15. package/runtime/feedback/analyze.sh +546 -0
  16. package/runtime/init.js +172 -0
  17. package/runtime/init.sh +145 -0
  18. package/runtime/metrics/collector.js +361 -0
  19. package/runtime/metrics/collector.sh +308 -0
  20. package/runtime/persistence/store.js +433 -0
  21. package/runtime/persistence/store.sh +303 -0
  22. package/skill.json +74 -0
  23. package/skills/agent-payments/SKILL.md +411 -0
  24. package/skills/brainstorming/SKILL.md +233 -0
  25. package/skills/content-pipeline/SKILL.md +282 -0
  26. package/skills/dispatching-parallel-agents/SKILL.md +305 -0
  27. package/skills/executing-plans/SKILL.md +255 -0
  28. package/skills/finishing-a-development-branch/SKILL.md +260 -0
  29. package/skills/learn-how-to-learn/SKILL.md +235 -0
  30. package/skills/market-intelligence/SKILL.md +288 -0
  31. package/skills/prospecting/SKILL.md +313 -0
  32. package/skills/receiving-code-review/SKILL.md +225 -0
  33. package/skills/requesting-code-review/SKILL.md +206 -0
  34. package/skills/security-audit/SKILL.md +308 -0
  35. package/skills/subagent-driven-development/SKILL.md +244 -0
  36. package/skills/systematic-debugging/SKILL.md +279 -0
  37. package/skills/test-driven-development/SKILL.md +299 -0
  38. package/skills/using-clawpowers/SKILL.md +137 -0
  39. package/skills/using-git-worktrees/SKILL.md +261 -0
  40. package/skills/verification-before-completion/SKILL.md +254 -0
  41. package/skills/writing-plans/SKILL.md +276 -0
  42. package/skills/writing-skills/SKILL.md +260 -0
@@ -0,0 +1,546 @@
1
+ #!/usr/bin/env bash
2
+ # runtime/feedback/analyze.sh — RSI feedback engine
3
+ #
4
+ # Reads metrics, computes per-skill success rates, identifies declining performance,
5
+ # and outputs actionable recommendations for skill improvement.
6
+ #
7
+ # Usage:
8
+ # analyze.sh Full analysis of all skills
9
+ # analyze.sh --skill <name> Analysis for one skill
10
+ # analyze.sh --plan <name> Plan execution analysis
11
+ # analyze.sh --worktrees Worktree lifecycle report
12
+ # analyze.sh --recommendations Show improvement recommendations only
13
+ # analyze.sh --format json Output as JSON instead of human-readable
14
+ #
15
+ # RSI Cycle: measure → analyze → adapt
16
+ # This script implements the "analyze" step of the cycle.
17
+ # The "adapt" output is human-readable recommendations that agents apply.
18
+ set -euo pipefail
19
+
20
+ ## === Configuration ===
21
+
22
+ # All runtime paths derived from CLAWPOWERS_DIR for testability
23
+ CLAWPOWERS_DIR="${CLAWPOWERS_DIR:-$HOME/.clawpowers}"
24
+ METRICS_DIR="$CLAWPOWERS_DIR/metrics"
25
+ STATE_DIR="$CLAWPOWERS_DIR/state"
26
+ FEEDBACK_DIR="$CLAWPOWERS_DIR/feedback"
27
+
28
+ ## === Internal Utilities ===
29
+
30
+ # Creates required runtime directories if they don't exist yet.
31
+ # Allows analysis to run even without a prior `clawpowers init`.
32
+ ensure_dirs() {
33
+ for dir in "$METRICS_DIR" "$STATE_DIR" "$FEEDBACK_DIR"; do
34
+ [[ -d "$dir" ]] || mkdir -p "$dir"
35
+ done
36
+ }
37
+
38
+ ## === Metrics Loading ===
39
+
40
+ # Reads all JSONL metric records from every monthly log file.
41
+ # Files are read in alphabetical (chronological) order.
42
+ # An optional skill filter restricts output to records for a specific skill.
43
+ # Blank lines are skipped; malformed JSON lines are silently passed through
44
+ # (awk handles the actual parsing and can skip bad records).
45
+ #
46
+ # Arguments:
47
+ # $1 (optional) — skill name to filter by (empty = load all records)
48
+ # $2 (optional) — look-back in months (unused placeholder for future filtering)
49
+ load_metrics() {
50
+ local skill_filter="${1:-}"
51
+ local months="${2:-6}" # Reserved for future date-range filtering
52
+
53
+ for f in "$METRICS_DIR"/*.jsonl; do
54
+ [[ -f "$f" ]] || continue # Skip if glob expands to literal string (no files)
55
+ while IFS= read -r line; do
56
+ [[ -z "$line" ]] && continue # Skip blank separator lines
57
+ if [[ -n "$skill_filter" ]]; then
58
+ # Grep-based filter avoids jq dependency — exact JSON field match
59
+ echo "$line" | grep -q "\"skill\":\"${skill_filter}\"" || continue
60
+ fi
61
+ echo "$line"
62
+ done < "$f"
63
+ done
64
+ }
65
+
66
+ ## === Statistics ===
67
+
68
+ # Computes aggregate statistics for a single skill using awk JSON parsing.
69
+ # Output format (one line): "skill=<name> total=<n> success=<n> failure=<n> rate=<n> avg_duration=<n>"
70
+ # The key=value format allows easy extraction with grep -o and cut -d= -f2.
71
+ #
72
+ # avg_duration is -1 when no records include a duration_s field.
73
+ compute_skill_stats() {
74
+ local skill="$1"
75
+ load_metrics "$skill" | awk -v skill="$skill" '
76
+ BEGIN { total=0; success=0; failure=0; partial=0; dur_total=0; dur_count=0 }
77
+ /\"outcome\":\"success\"/ { success++ }
78
+ /\"outcome\":\"failure\"/ { failure++ }
79
+ /\"outcome\":\"partial\"/ { partial++ }
80
+ # Extract duration_s numeric value using string operations
81
+ /\"duration_s\":/ {
82
+ p = index($0, "\"duration_s\":")
83
+ if (p > 0) {
84
+ rest = substr($0, p + 13)
85
+ val = rest + 0
86
+ if (val > 0 || substr(rest, 1, 1) == "0") { dur_total += val; dur_count++ }
87
+ }
88
+ }
89
+ { total++ }
90
+ END {
91
+ if (total > 0) {
92
+ rate = int(success/total*100+0.5)
93
+ avg_dur = (dur_count > 0) ? int(dur_total/dur_count+0.5) : -1
94
+ print "skill=" skill " total=" total " success=" success " failure=" failure " rate=" rate " avg_duration=" avg_dur
95
+ }
96
+ }
97
+ '
98
+ }
99
+
100
+ # Returns a sorted, deduplicated list of all skill names present in the metrics store.
101
+ # Used to iterate over all tracked skills without needing an external registry.
102
+ get_all_skills() {
103
+ load_metrics | awk '
104
+ /\"skill\":/ {
105
+ p = index($0, "\"skill\":\"")
106
+ if (p > 0) {
107
+ rest = substr($0, p + 9)
108
+ q = index(rest, "\"")
109
+ if (q > 0) skills[substr(rest, 1, q - 1)] = 1
110
+ }
111
+ }
112
+ END { for (s in skills) print s }
113
+ ' | sort
114
+ }
115
+
116
+ ## === Trend Detection ===
117
+
118
+ # Detects declining performance by comparing the last N executions to the
119
+ # all-time success rate. A decline is flagged when the gap is >= 20 percentage points.
120
+ #
121
+ # Requires at least 2×window total records for a meaningful comparison;
122
+ # silently returns nothing for skills with insufficient data.
123
+ #
124
+ # Arguments:
125
+ # $1 — skill name
126
+ detect_decline() {
127
+ local skill="$1"
128
+ local window=5 # Compare recent N executions vs. all-time average
129
+
130
+ local all_lines
131
+ all_lines=$(load_metrics "$skill")
132
+
133
+ if [[ -z "$all_lines" ]]; then
134
+ return 0 # No data, no decline to report
135
+ fi
136
+
137
+ echo "$all_lines" | awk -v window="$window" '
138
+ BEGIN { total=0; success_all=0; recent_success=0; recent_total=0 }
139
+ { lines[total] = $0; total++ }
140
+ /\"outcome\":\"success\"/ { success_all++ }
141
+ END {
142
+ # Only compare when we have enough data for both windows
143
+ start = (total > window) ? total - window : 0
144
+ for (i=start; i<total; i++) {
145
+ recent_total++
146
+ if (lines[i] ~ /"outcome":"success"/) recent_success++
147
+ }
148
+ if (total >= window*2 && recent_total > 0) {
149
+ all_rate = success_all/total * 100
150
+ recent_rate = recent_success/recent_total * 100
151
+ # Flag a 20+ percentage point drop as a significant decline
152
+ if (all_rate - recent_rate >= 20) {
153
+ printf "DECLINING: %s (all-time %.0f%% → recent %.0f%%)\n", ARGV[1], all_rate, recent_rate
154
+ }
155
+ }
156
+ }
157
+ ' /dev/null "$skill"
158
+ }
159
+
160
+ ## === Recommendations ===
161
+
162
+ # Generates human-readable improvement recommendations based on success rate tier.
163
+ # Three tiers: <60% (low), 60-79% (moderate), ≥80% (good).
164
+ # Requires at least 3 executions; prints an "insufficient data" message otherwise.
165
+ #
166
+ # Arguments:
167
+ # $1 — skill name (used in output messages)
168
+ # $2 — success rate percentage (integer 0-100)
169
+ # $3 — total execution count
170
+ generate_recommendations() {
171
+ local skill="$1"
172
+ local rate="$2"
173
+ local total="$3"
174
+
175
+ if [[ $total -lt 3 ]]; then
176
+ echo " Not enough data (${total} executions). Need 3+ to analyze."
177
+ return
178
+ fi
179
+
180
+ if [[ $rate -lt 60 ]]; then
181
+ echo " ⚠ LOW SUCCESS RATE (${rate}%): Review skill methodology."
182
+ echo " Consider: Is the 'When to Use' triggering at wrong times?"
183
+ echo " Consider: Are anti-patterns in the skill being followed anyway?"
184
+ elif [[ $rate -lt 80 ]]; then
185
+ echo " ℹ MODERATE RATE (${rate}%): Some improvement opportunity."
186
+ echo " Review recent failure notes for common causes."
187
+ else
188
+ echo " ✓ GOOD RATE (${rate}%): Skill performing well."
189
+ fi
190
+ }
191
+
192
+ ## === Command Implementations ===
193
+
194
+ ## --- Full Analysis ---
195
+
196
+ # Full RSI analysis across all tracked skills.
197
+ # Prints per-skill statistics, recommendations, decline warnings, and an overall summary.
198
+ # Also saves a compact plain-text report to the feedback directory for later reference.
199
+ # This is the default view shown by `clawpowers status`.
200
+ cmd_full_analysis() {
201
+ local output_format="${1:-human}" # Reserved: 'json' format planned for future
202
+
203
+ ensure_dirs
204
+
205
+ echo "ClawPowers RSI Feedback Analysis"
206
+ echo "================================="
207
+ echo "Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
208
+ echo ""
209
+
210
+ local skills
211
+ skills=$(get_all_skills)
212
+
213
+ if [[ -z "$skills" ]]; then
214
+ echo "No metrics found. Run some skills and record outcomes with:"
215
+ echo " bash runtime/metrics/collector.sh record --skill <name> --outcome success"
216
+ return 0
217
+ fi
218
+
219
+ echo "## Per-Skill Analysis"
220
+ echo ""
221
+
222
+ local overall_total=0
223
+ local overall_success=0
224
+ local declining_skills=""
225
+
226
+ ## --- Per-Skill Loop ---
227
+ while IFS= read -r skill; do
228
+ [[ -z "$skill" ]] && continue
229
+
230
+ local stats
231
+ stats=$(compute_skill_stats "$skill")
232
+ [[ -z "$stats" ]] && continue
233
+
234
+ # Extract individual fields from the key=value output of compute_skill_stats
235
+ local total rate avg_dur
236
+ total=$(echo "$stats" | grep -o 'total=[0-9]*' | cut -d= -f2)
237
+ local success
238
+ success=$(echo "$stats" | grep -o 'success=[0-9]*' | cut -d= -f2)
239
+ rate=$(echo "$stats" | grep -o 'rate=[0-9]*' | cut -d= -f2)
240
+ avg_dur=$(echo "$stats" | grep -o 'avg_duration=-\?[0-9]*' | cut -d= -f2)
241
+
242
+ # Accumulate aggregate counters for the overall summary section
243
+ overall_total=$((overall_total + total))
244
+ overall_success=$((overall_success + success))
245
+
246
+ printf "### %s\n" "$skill"
247
+ # Print avg duration only when records include timing data (-1 means no data)
248
+ printf " Executions: %d | Success rate: %d%%" "$total" "$rate"
249
+ if [[ "$avg_dur" -gt 0 ]]; then
250
+ printf " | Avg duration: %ds" "$avg_dur"
251
+ fi
252
+ echo ""
253
+
254
+ # Print improvement recommendations based on the success rate tier
255
+ generate_recommendations "$skill" "$rate" "$total"
256
+
257
+ # Check for a significant performance drop in recent executions
258
+ local decline
259
+ decline=$(detect_decline "$skill" 2>/dev/null || true)
260
+ if [[ -n "$decline" ]]; then
261
+ echo " ⚠ $decline"
262
+ declining_skills+="$skill "
263
+ fi
264
+
265
+ echo ""
266
+ done <<< "$skills"
267
+
268
+ ## --- Overall Summary ---
269
+ echo "## Overall Summary"
270
+ if [[ $overall_total -gt 0 ]]; then
271
+ local overall_rate
272
+ overall_rate=$((overall_success * 100 / overall_total))
273
+ echo " Total executions: $overall_total"
274
+ echo " Overall success rate: ${overall_rate}%"
275
+
276
+ if [[ -n "$declining_skills" ]]; then
277
+ echo ""
278
+ echo " ⚠ Declining skills: $declining_skills"
279
+ echo " These skills show degraded performance in recent executions."
280
+ echo " Recommended: Review skill methodology and recent failure notes."
281
+ fi
282
+ fi
283
+
284
+ ## --- Runtime State Snapshot ---
285
+ # Count state keys and metrics files for the health display section
286
+ local state_keys
287
+ state_keys=$(ls "$STATE_DIR" 2>/dev/null | wc -l | tr -d ' ')
288
+ echo ""
289
+ echo "## Runtime State"
290
+ echo " State keys stored: $state_keys"
291
+ echo " Metrics files: $(ls "$METRICS_DIR"/*.jsonl 2>/dev/null | wc -l | tr -d ' ')"
292
+
293
+ ## --- Persist Report ---
294
+ # Save a compact summary to the feedback directory for trend tracking over time
295
+ local report_file="$FEEDBACK_DIR/analysis-$(date +%Y-%m-%d).txt"
296
+ {
297
+ echo "Analysis generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
298
+ echo "Overall success rate: $((overall_success * 100 / (overall_total > 0 ? overall_total : 1)))%"
299
+ echo "Total executions: $overall_total"
300
+ [[ -n "$declining_skills" ]] && echo "Declining: $declining_skills"
301
+ } > "$report_file"
302
+ chmod 600 "$report_file"
303
+ }
304
+
305
+ ## --- Skill Analysis ---
306
+
307
+ # Detailed analysis for a single named skill.
308
+ # Shows statistics, recommendations, the 5 most recent executions, and
309
+ # any related keys in the state store.
310
+ cmd_skill_analysis() {
311
+ local skill="$1"
312
+ ensure_dirs
313
+
314
+ echo "Skill Analysis: $skill"
315
+ # Dynamic separator matching the header length
316
+ echo "$(printf '=%.0s' {1..40})"
317
+ echo "Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
318
+ echo ""
319
+
320
+ local stats
321
+ stats=$(compute_skill_stats "$skill")
322
+ if [[ -z "$stats" ]]; then
323
+ echo "No metrics found for skill: $skill"
324
+ echo "Record some executions with:"
325
+ echo " bash runtime/metrics/collector.sh record --skill $skill --outcome success"
326
+ return 0
327
+ fi
328
+
329
+ # Extract all stat fields from compute_skill_stats output
330
+ local total success failure rate avg_dur
331
+ total=$(echo "$stats" | grep -o 'total=[0-9]*' | cut -d= -f2)
332
+ success=$(echo "$stats" | grep -o 'success=[0-9]*' | cut -d= -f2)
333
+ failure=$(echo "$stats" | grep -o 'failure=[0-9]*' | cut -d= -f2)
334
+ rate=$(echo "$stats" | grep -o 'rate=[0-9]*' | cut -d= -f2)
335
+ avg_dur=$(echo "$stats" | grep -o 'avg_duration=-\?[0-9]*' | cut -d= -f2)
336
+
337
+ echo "## Statistics"
338
+ echo " Total executions: $total"
339
+ echo " Success: $success (${rate}%)"
340
+ echo " Failure: $failure ($((100 - rate))%)"
341
+ if [[ "$avg_dur" -gt 0 ]]; then
342
+ # Show duration in both seconds and minutes+seconds for readability
343
+ echo " Average duration: ${avg_dur}s ($((avg_dur / 60))m $((avg_dur % 60))s)"
344
+ fi
345
+
346
+ echo ""
347
+ echo "## Recommendations"
348
+ generate_recommendations "$skill" "$rate" "$total"
349
+
350
+ ## --- Recent Executions ---
351
+ # Show last 5 executions as a quick sanity check on recent behavior
352
+ echo ""
353
+ echo "## Recent Executions"
354
+ load_metrics "$skill" | tail -5 | while IFS= read -r line; do
355
+ local ts outcome notes
356
+ ts=$(echo "$line" | grep -o '"ts":"[^"]*"' | cut -d'"' -f4)
357
+ outcome=$(echo "$line" | grep -o '"outcome":"[^"]*"' | cut -d'"' -f4)
358
+ # Notes are optional — default to "(no notes)" when absent
359
+ notes=$(echo "$line" | grep -o '"notes":"[^"]*"' | cut -d'"' -f4 || echo "")
360
+ printf " %s | %-10s | %s\n" "$ts" "$outcome" "${notes:-(no notes)}"
361
+ done
362
+
363
+ ## --- Related State Keys ---
364
+ # List any store keys that belong to this skill's namespace
365
+ echo ""
366
+ echo "## Related State Keys"
367
+ if command -v bash >/dev/null 2>&1; then
368
+ local script_dir
369
+ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
370
+ if [[ -f "$script_dir/persistence/store.sh" ]]; then
371
+ bash "$script_dir/persistence/store.sh" list "${skill}:" 2>/dev/null || echo " (none)"
372
+ fi
373
+ fi
374
+ }
375
+
376
+ ## --- Plan Analysis ---
377
+
378
+ # Analyzes the execution of a named plan.
379
+ # Reads estimated vs. actual duration from the store and computes estimation accuracy.
380
+ # Also lists all task statuses tracked under this plan's namespace.
381
+ #
382
+ # Arguments:
383
+ # $1 — plan name (as used in store keys, e.g. "auth-service")
384
+ cmd_plan_analysis() {
385
+ local plan_name="$1"
386
+ ensure_dirs
387
+
388
+ echo "Plan Execution Analysis: $plan_name"
389
+ echo "$(printf '=%.0s' {1..50})"
390
+ echo ""
391
+
392
+ # Locate store.sh relative to this script (analyze.sh is in feedback/, store.sh in persistence/)
393
+ local script_dir
394
+ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
395
+ local store="$script_dir/persistence/store.sh"
396
+
397
+ if [[ ! -f "$store" ]]; then
398
+ echo "Error: store.sh not found at $store" >&2
399
+ return 1
400
+ fi
401
+
402
+ # Read plan timing metadata — set by the executing-plans skill during plan execution
403
+ local estimated actual
404
+ estimated=$(bash "$store" get "plan:${plan_name}:estimated_duration" "unknown" 2>/dev/null)
405
+ actual=$(bash "$store" get "plan:${plan_name}:actual_duration" "unknown" 2>/dev/null)
406
+
407
+ echo "Estimated duration: ${estimated}min"
408
+ echo "Actual duration: ${actual}min"
409
+
410
+ if [[ "$estimated" != "unknown" && "$actual" != "unknown" ]]; then
411
+ # Compute accuracy ratio via awk (bash can't do floating point arithmetic)
412
+ local error
413
+ error=$(awk "BEGIN { printf \"%.1f\", $actual / $estimated }")
414
+ echo "Estimation accuracy: ${error}x (1.0 = perfect)"
415
+ # Flag significant underestimates (>30% over estimate) with a concrete recommendation
416
+ if (( $(echo "$error > 1.3" | awk '{print ($1 > 0)}') )); then
417
+ echo "Recommendation: Increase task time estimates by ${error}x for similar work"
418
+ fi
419
+ fi
420
+
421
+ echo ""
422
+ echo "Task Status:"
423
+ # Task keys follow the pattern: execution:<planName>:task_<n>:status
424
+ bash "$store" list-values "execution:${plan_name}:task_" 2>/dev/null | while IFS='=' read -r key val; do
425
+ # Pad the key to 40 chars for aligned two-column output
426
+ printf " %-40s %s\n" "$key" "$val"
427
+ done
428
+ }
429
+
430
+ ## --- Worktree Report ---
431
+
432
+ # Reports on all active git worktrees tracked in the state store.
433
+ # Worktrees are registered by the using-git-worktrees skill and should be
434
+ # cleaned up after branch merges to avoid stale worktree accumulation.
435
+ cmd_worktree_report() {
436
+ ensure_dirs
437
+
438
+ echo "Worktree Lifecycle Report"
439
+ echo "========================="
440
+ echo ""
441
+
442
+ local script_dir
443
+ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
444
+ local store="$script_dir/persistence/store.sh"
445
+
446
+ if [[ ! -f "$store" ]]; then
447
+ echo "Error: store.sh not found" >&2
448
+ return 1
449
+ fi
450
+
451
+ echo "Active Worktrees:"
452
+ # Worktree keys are registered under the "worktree:" namespace by the using-git-worktrees skill
453
+ bash "$store" list-values "worktree:" 2>/dev/null | while IFS='=' read -r key val; do
454
+ printf " %s: %s\n" "$key" "$val"
455
+ done || echo " (none registered)"
456
+
457
+ echo ""
458
+ echo "Tip: After merging a branch, clean up its worktree:"
459
+ echo " git worktree remove <path> && git branch -d <branch>"
460
+ }
461
+
462
+ ## --- Recommendations Only ---
463
+
464
+ # Shows only skills that have improvement recommendations (success rate <80%).
465
+ # Useful for quick triage without the full analysis output.
466
+ # Skills with fewer than 3 executions are excluded (insufficient data).
467
+ cmd_recommendations() {
468
+ ensure_dirs
469
+
470
+ echo "ClawPowers Recommendations"
471
+ echo "=========================="
472
+ echo ""
473
+
474
+ local skills
475
+ skills=$(get_all_skills)
476
+
477
+ if [[ -z "$skills" ]]; then
478
+ echo "No metrics yet. Record skill outcomes to get recommendations."
479
+ return 0
480
+ fi
481
+
482
+ local has_recommendations=0
483
+
484
+ while IFS= read -r skill; do
485
+ [[ -z "$skill" ]] && continue
486
+ local stats
487
+ stats=$(compute_skill_stats "$skill")
488
+ [[ -z "$stats" ]] && continue
489
+
490
+ local total rate
491
+ total=$(echo "$stats" | grep -o 'total=[0-9]*' | cut -d= -f2)
492
+ rate=$(echo "$stats" | grep -o 'rate=[0-9]*' | cut -d= -f2)
493
+
494
+ # Only surface skills with enough data that are underperforming
495
+ if [[ $total -ge 3 && $rate -lt 80 ]]; then
496
+ echo "[$skill] Success rate: ${rate}% ($total executions)"
497
+ generate_recommendations "$skill" "$rate" "$total"
498
+ echo ""
499
+ has_recommendations=1
500
+ fi
501
+ done <<< "$skills"
502
+
503
+ if [[ $has_recommendations -eq 0 ]]; then
504
+ echo "All tracked skills performing well (≥80% success rate)."
505
+ echo "Keep recording outcomes to refine this analysis."
506
+ fi
507
+ }
508
+
509
+ ## === Usage ===
510
+
511
+ cmd_usage() {
512
+ cat << 'EOF'
513
+ Usage: analyze.sh [options]
514
+
515
+ Options:
516
+ (no args) Full analysis of all skills
517
+ --skill <name> Analysis for one specific skill
518
+ --plan <name> Plan execution analysis (duration, task status)
519
+ --worktrees Worktree lifecycle report
520
+ --recommendations Show improvement recommendations only
521
+ --format json JSON output (future: human is default)
522
+
523
+ Examples:
524
+ analyze.sh
525
+ analyze.sh --skill systematic-debugging
526
+ analyze.sh --plan auth-service
527
+ analyze.sh --worktrees
528
+ analyze.sh --recommendations
529
+ EOF
530
+ }
531
+
532
+ ## === Main Dispatch ===
533
+
534
+ # Route the first positional argument to the appropriate command function.
535
+ # Each flag corresponds to one analysis mode.
536
+ case "${1:-}" in
537
+ --skill) cmd_skill_analysis "${2:-}" ;;
538
+ --plan) cmd_plan_analysis "${2:-}" ;;
539
+ --worktrees) cmd_worktree_report ;;
540
+ --recommendations) cmd_recommendations ;;
541
+ # --format accepts a format name but human-readable is the only current output
542
+ --format) cmd_full_analysis "${2:-human}" ;;
543
+ help|-h|--help) cmd_usage ;;
544
+ "") cmd_full_analysis ;;
545
+ *) echo "Unknown option: $1"; cmd_usage; exit 1 ;;
546
+ esac