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.
- package/.claude-plugin/manifest.json +19 -0
- package/.codex/INSTALL.md +36 -0
- package/.cursor-plugin/manifest.json +21 -0
- package/.opencode/INSTALL.md +52 -0
- package/ARCHITECTURE.md +69 -0
- package/README.md +381 -0
- package/bin/clawpowers.js +390 -0
- package/bin/clawpowers.sh +91 -0
- package/gemini-extension.json +32 -0
- package/hooks/session-start +205 -0
- package/hooks/session-start.cmd +43 -0
- package/hooks/session-start.js +163 -0
- package/package.json +54 -0
- package/runtime/feedback/analyze.js +621 -0
- package/runtime/feedback/analyze.sh +546 -0
- package/runtime/init.js +172 -0
- package/runtime/init.sh +145 -0
- package/runtime/metrics/collector.js +361 -0
- package/runtime/metrics/collector.sh +308 -0
- package/runtime/persistence/store.js +433 -0
- package/runtime/persistence/store.sh +303 -0
- package/skill.json +74 -0
- package/skills/agent-payments/SKILL.md +411 -0
- package/skills/brainstorming/SKILL.md +233 -0
- package/skills/content-pipeline/SKILL.md +282 -0
- package/skills/dispatching-parallel-agents/SKILL.md +305 -0
- package/skills/executing-plans/SKILL.md +255 -0
- package/skills/finishing-a-development-branch/SKILL.md +260 -0
- package/skills/learn-how-to-learn/SKILL.md +235 -0
- package/skills/market-intelligence/SKILL.md +288 -0
- package/skills/prospecting/SKILL.md +313 -0
- package/skills/receiving-code-review/SKILL.md +225 -0
- package/skills/requesting-code-review/SKILL.md +206 -0
- package/skills/security-audit/SKILL.md +308 -0
- package/skills/subagent-driven-development/SKILL.md +244 -0
- package/skills/systematic-debugging/SKILL.md +279 -0
- package/skills/test-driven-development/SKILL.md +299 -0
- package/skills/using-clawpowers/SKILL.md +137 -0
- package/skills/using-git-worktrees/SKILL.md +261 -0
- package/skills/verification-before-completion/SKILL.md +254 -0
- package/skills/writing-plans/SKILL.md +276 -0
- 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
|