specweave 1.0.263 → 1.0.265
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.md +25 -25
- package/dist/src/cli/commands/docs.d.ts.map +1 -1
- package/dist/src/cli/commands/docs.js +13 -5
- package/dist/src/cli/commands/docs.js.map +1 -1
- package/dist/src/core/living-docs/content-distributor.js +1 -1
- package/dist/src/core/living-docs/content-distributor.js.map +1 -1
- package/dist/src/core/living-docs/scaffolding/template-engine.js +1 -1
- package/dist/src/core/living-docs/scaffolding/template-engine.js.map +1 -1
- package/dist/src/core/living-docs/sync-helpers/generators.js +1 -1
- package/dist/src/core/living-docs/sync-helpers/generators.js.map +1 -1
- package/dist/src/core/llm/consent.d.ts +33 -0
- package/dist/src/core/llm/consent.d.ts.map +1 -0
- package/dist/src/core/llm/consent.js +94 -0
- package/dist/src/core/llm/consent.js.map +1 -0
- package/dist/src/core/llm/provider-factory.d.ts +2 -0
- package/dist/src/core/llm/provider-factory.d.ts.map +1 -1
- package/dist/src/core/llm/provider-factory.js +12 -0
- package/dist/src/core/llm/provider-factory.js.map +1 -1
- package/dist/src/core/llm/types.d.ts +17 -0
- package/dist/src/core/llm/types.d.ts.map +1 -1
- package/dist/src/core/llm/types.js.map +1 -1
- package/dist/src/core/skills/skill-judge.d.ts.map +1 -1
- package/dist/src/core/skills/skill-judge.js +14 -0
- package/dist/src/core/skills/skill-judge.js.map +1 -1
- package/dist/src/utils/docs-preview/config-generator.d.ts +14 -6
- package/dist/src/utils/docs-preview/config-generator.d.ts.map +1 -1
- package/dist/src/utils/docs-preview/config-generator.js +526 -137
- package/dist/src/utils/docs-preview/config-generator.js.map +1 -1
- package/dist/src/utils/docs-preview/docusaurus-setup.d.ts +1 -1
- package/dist/src/utils/docs-preview/docusaurus-setup.d.ts.map +1 -1
- package/dist/src/utils/docs-preview/docusaurus-setup.js +121 -48
- package/dist/src/utils/docs-preview/docusaurus-setup.js.map +1 -1
- package/dist/src/utils/docs-preview/index.d.ts +3 -2
- package/dist/src/utils/docs-preview/index.d.ts.map +1 -1
- package/dist/src/utils/docs-preview/index.js +3 -2
- package/dist/src/utils/docs-preview/index.js.map +1 -1
- package/dist/src/utils/docs-preview/project-detector.d.ts +16 -0
- package/dist/src/utils/docs-preview/project-detector.d.ts.map +1 -0
- package/dist/src/utils/docs-preview/project-detector.js +234 -0
- package/dist/src/utils/docs-preview/project-detector.js.map +1 -0
- package/dist/src/utils/docs-preview/server-manager.d.ts +4 -0
- package/dist/src/utils/docs-preview/server-manager.d.ts.map +1 -1
- package/dist/src/utils/docs-preview/server-manager.js +102 -7
- package/dist/src/utils/docs-preview/server-manager.js.map +1 -1
- package/dist/src/utils/docs-preview/types.d.ts +28 -0
- package/dist/src/utils/docs-preview/types.d.ts.map +1 -1
- package/package.json +1 -6
- package/plugins/specweave/scripts/progress.js +148 -50
- package/plugins/specweave/scripts/read-progress.sh +128 -52
- package/plugins/specweave/scripts/rebuild-dashboard-cache.sh +23 -17
- package/plugins/specweave/skills/done/SKILL.md +5 -4
- package/plugins/specweave/skills/judge-llm/SKILL.md +18 -0
- package/plugins/specweave/skills/progress/SKILL.md +7 -21
|
@@ -38,7 +38,8 @@ if [[ ! -d "$PROJECT_ROOT/.specweave" ]]; then
|
|
|
38
38
|
fi
|
|
39
39
|
|
|
40
40
|
CACHE_FILE="$PROJECT_ROOT/.specweave/state/dashboard.json"
|
|
41
|
-
|
|
41
|
+
# Resolve scripts dir relative to this script (works in both dev and plugin cache)
|
|
42
|
+
SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
42
43
|
|
|
43
44
|
# Check if jq is available, fall back to Node if not
|
|
44
45
|
if ! command -v jq >/dev/null 2>&1; then
|
|
@@ -60,6 +61,36 @@ elif ! jq -e '.' "$CACHE_FILE" >/dev/null 2>&1; then
|
|
|
60
61
|
NEED_REBUILD=true
|
|
61
62
|
fi
|
|
62
63
|
|
|
64
|
+
# Staleness check: if any increment file is newer than cache, rebuild
|
|
65
|
+
if [[ "$NEED_REBUILD" == "false" ]] && [[ -f "$CACHE_FILE" ]]; then
|
|
66
|
+
CACHE_MTIME=0
|
|
67
|
+
if [[ "$(uname)" == "Darwin" ]]; then
|
|
68
|
+
CACHE_MTIME=$(stat -f "%m" "$CACHE_FILE" 2>/dev/null || echo "0")
|
|
69
|
+
else
|
|
70
|
+
CACHE_MTIME=$(stat -c "%Y" "$CACHE_FILE" 2>/dev/null || echo "0")
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
INC_DIR="$PROJECT_ROOT/.specweave/increments"
|
|
74
|
+
if [[ -d "$INC_DIR" ]]; then
|
|
75
|
+
for d in "$INC_DIR"/[0-9]*/; do
|
|
76
|
+
[[ -d "$d" ]] || continue
|
|
77
|
+
for f in "$d"metadata.json "$d"tasks.md "$d"spec.md; do
|
|
78
|
+
[[ -f "$f" ]] || continue
|
|
79
|
+
FILE_MTIME=0
|
|
80
|
+
if [[ "$(uname)" == "Darwin" ]]; then
|
|
81
|
+
FILE_MTIME=$(stat -f "%m" "$f" 2>/dev/null || echo "0")
|
|
82
|
+
else
|
|
83
|
+
FILE_MTIME=$(stat -c "%Y" "$f" 2>/dev/null || echo "0")
|
|
84
|
+
fi
|
|
85
|
+
if [[ "$FILE_MTIME" -gt "$CACHE_MTIME" ]]; then
|
|
86
|
+
NEED_REBUILD=true
|
|
87
|
+
break 2
|
|
88
|
+
fi
|
|
89
|
+
done
|
|
90
|
+
done
|
|
91
|
+
fi
|
|
92
|
+
fi
|
|
93
|
+
|
|
63
94
|
if [[ "$NEED_REBUILD" == "true" ]]; then
|
|
64
95
|
bash "$SCRIPTS_DIR/rebuild-dashboard-cache.sh" --quiet 2>/dev/null || true
|
|
65
96
|
fi
|
|
@@ -170,7 +201,9 @@ if [[ -n "$INCREMENT_ID" ]]; then
|
|
|
170
201
|
echo "Priority: $PRIORITY_FMT"
|
|
171
202
|
echo ""
|
|
172
203
|
echo "Tasks: $COMPLETED/$TOTAL ($TASK_PCT%) $BAR"
|
|
173
|
-
|
|
204
|
+
if [[ "$AC_TOTAL" -gt 0 ]]; then
|
|
205
|
+
echo "ACs: $AC_COMPLETED/$AC_TOTAL ($AC_PCT%)"
|
|
206
|
+
fi
|
|
174
207
|
exit 0
|
|
175
208
|
fi
|
|
176
209
|
|
|
@@ -179,17 +212,39 @@ echo ""
|
|
|
179
212
|
echo "📊 Increment Progress"
|
|
180
213
|
echo ""
|
|
181
214
|
|
|
182
|
-
#
|
|
215
|
+
# ── Section 1: Needs Closure (100% done but still active/planning) ──
|
|
216
|
+
NEEDS_CLOSURE=$(jq -r '
|
|
217
|
+
[.increments | to_entries[] |
|
|
218
|
+
select((.value.status == "active" or .value.status == "planning" or .value.status == "planned") and
|
|
219
|
+
.value.tasks.total > 0 and
|
|
220
|
+
.value.tasks.completed == .value.tasks.total)] |
|
|
221
|
+
sort_by(.value.lastActivityEpoch // 0) | reverse | .[] |
|
|
222
|
+
.key
|
|
223
|
+
' "$CACHE_FILE" 2>/dev/null)
|
|
224
|
+
|
|
225
|
+
CLOSURE_COUNT=0
|
|
226
|
+
if [[ -n "$NEEDS_CLOSURE" ]]; then
|
|
227
|
+
echo "⚠️ Needs Closure (100% tasks done):"
|
|
228
|
+
while IFS= read -r id; do
|
|
229
|
+
[[ -z "$id" ]] && continue
|
|
230
|
+
CLOSURE_COUNT=$((CLOSURE_COUNT + 1))
|
|
231
|
+
echo " $id → /sw:done $id"
|
|
232
|
+
done <<< "$NEEDS_CLOSURE"
|
|
233
|
+
echo ""
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
# ── Section 2: Ready for Review ──
|
|
183
237
|
READY_FOR_REVIEW=$(jq -r '
|
|
184
|
-
.increments | to_entries[] |
|
|
185
|
-
|
|
186
|
-
|
|
238
|
+
[.increments | to_entries[] |
|
|
239
|
+
select(.value.status == "ready_for_review")] |
|
|
240
|
+
sort_by(.value.lastActivityEpoch // 0) | reverse | .[] |
|
|
241
|
+
"\(.key)|\(.value.tasks.completed)|\(.value.tasks.total)|\(.value.acs.completed)|\(.value.acs.total)|\(.value.priority)"
|
|
187
242
|
' "$CACHE_FILE" 2>/dev/null)
|
|
188
243
|
|
|
189
244
|
REVIEW_COUNT=0
|
|
190
245
|
if [[ -n "$READY_FOR_REVIEW" ]]; then
|
|
191
246
|
echo "👀 Ready for Review:"
|
|
192
|
-
while IFS='|' read -r id completed total ac_completed ac_total priority
|
|
247
|
+
while IFS='|' read -r id completed total ac_completed ac_total priority; do
|
|
193
248
|
[[ -z "$id" ]] && continue
|
|
194
249
|
REVIEW_COUNT=$((REVIEW_COUNT + 1))
|
|
195
250
|
if [[ "$total" -gt 0 ]]; then
|
|
@@ -197,34 +252,36 @@ if [[ -n "$READY_FOR_REVIEW" ]]; then
|
|
|
197
252
|
else
|
|
198
253
|
pct=0
|
|
199
254
|
fi
|
|
200
|
-
if [[ "$ac_total" -gt 0 ]]; then
|
|
201
|
-
ac_pct=$((ac_completed * 100 / ac_total))
|
|
202
|
-
else
|
|
203
|
-
ac_pct=0
|
|
204
|
-
fi
|
|
205
255
|
bar=$(progress_bar "$pct" 12)
|
|
206
|
-
# Format priority badge
|
|
207
256
|
pri_badge=""
|
|
208
257
|
case "$priority" in P0|critical) pri_badge="🔴" ;; P1|high) pri_badge="🟠" ;; esac
|
|
258
|
+
ac_info=""
|
|
259
|
+
if [[ "$ac_total" -gt 0 ]]; then
|
|
260
|
+
ac_pct=$((ac_completed * 100 / ac_total))
|
|
261
|
+
ac_info=" | $ac_completed/$ac_total ACs ($ac_pct%)"
|
|
262
|
+
fi
|
|
209
263
|
echo " $pri_badge $id"
|
|
210
|
-
echo " $bar $completed/$total tasks
|
|
264
|
+
echo " $bar $completed/$total tasks$ac_info"
|
|
211
265
|
echo " → /sw:done $id"
|
|
212
266
|
done <<< "$READY_FOR_REVIEW"
|
|
213
267
|
echo ""
|
|
214
268
|
fi
|
|
215
269
|
|
|
216
|
-
#
|
|
217
|
-
#
|
|
270
|
+
# ── Section 3: Active (with real work, not 100% done, not empty) ──
|
|
271
|
+
# Excludes: 100%-done (shown in Needs Closure), 0-tasks-0-ACs (shown in Planning)
|
|
218
272
|
ACTIVE=$(jq -r '
|
|
219
|
-
.increments | to_entries[] |
|
|
220
|
-
|
|
221
|
-
|
|
273
|
+
[.increments | to_entries[] |
|
|
274
|
+
select((.value.status == "active" or .value.status == "planning" or .value.status == "planned") and
|
|
275
|
+
(.value.tasks.total > 0 or .value.acs.total > 0) and
|
|
276
|
+
((.value.tasks.total == 0) or (.value.tasks.completed < .value.tasks.total)))] |
|
|
277
|
+
sort_by(.value.lastActivityEpoch // 0) | reverse | .[] |
|
|
278
|
+
"\(.key)|\(.value.tasks.completed)|\(.value.tasks.total)|\(.value.acs.completed)|\(.value.acs.total)|\(.value.status)|\(.value.priority)"
|
|
222
279
|
' "$CACHE_FILE" 2>/dev/null)
|
|
223
280
|
|
|
224
281
|
ACTIVE_COUNT=0
|
|
225
282
|
if [[ -n "$ACTIVE" ]]; then
|
|
226
283
|
echo "🔄 Active:"
|
|
227
|
-
while IFS='|' read -r id completed total ac_completed ac_total inc_status priority
|
|
284
|
+
while IFS='|' read -r id completed total ac_completed ac_total inc_status priority; do
|
|
228
285
|
[[ -z "$id" ]] && continue
|
|
229
286
|
ACTIVE_COUNT=$((ACTIVE_COUNT + 1))
|
|
230
287
|
if [[ "$total" -gt 0 ]]; then
|
|
@@ -232,37 +289,57 @@ if [[ -n "$ACTIVE" ]]; then
|
|
|
232
289
|
else
|
|
233
290
|
pct=0
|
|
234
291
|
fi
|
|
235
|
-
if [[ "$ac_total" -gt 0 ]]; then
|
|
236
|
-
ac_pct=$((ac_completed * 100 / ac_total))
|
|
237
|
-
else
|
|
238
|
-
ac_pct=0
|
|
239
|
-
fi
|
|
240
292
|
bar=$(progress_bar "$pct" 12)
|
|
241
|
-
# Format priority badge
|
|
242
293
|
pri_badge=""
|
|
243
294
|
case "$priority" in P0|critical) pri_badge="🔴" ;; P1|high) pri_badge="🟠" ;; esac
|
|
244
|
-
# Show status indicator if planning
|
|
245
295
|
status_indicator=""
|
|
246
296
|
case "$inc_status" in
|
|
247
297
|
planning|planned) status_indicator=" (📝 planning)" ;;
|
|
248
298
|
esac
|
|
299
|
+
ac_info=""
|
|
300
|
+
if [[ "$ac_total" -gt 0 ]]; then
|
|
301
|
+
ac_pct=$((ac_completed * 100 / ac_total))
|
|
302
|
+
ac_info=" | $ac_completed/$ac_total ACs ($ac_pct%)"
|
|
303
|
+
fi
|
|
249
304
|
echo " $pri_badge $id$status_indicator"
|
|
250
|
-
echo " $bar $completed/$total tasks
|
|
305
|
+
echo " $bar $completed/$total tasks$ac_info"
|
|
251
306
|
done <<< "$ACTIVE"
|
|
252
307
|
echo ""
|
|
253
308
|
fi
|
|
254
309
|
|
|
255
|
-
#
|
|
256
|
-
|
|
310
|
+
# ── Section 4: Planning (no tasks yet) — collapsed single line ──
|
|
311
|
+
EMPTY_ACTIVE=$(jq -r '
|
|
257
312
|
.increments | to_entries[] |
|
|
258
|
-
select(.value.status == "
|
|
259
|
-
|
|
313
|
+
select((.value.status == "active" or .value.status == "planning" or .value.status == "planned") and
|
|
314
|
+
.value.tasks.total == 0 and .value.acs.total == 0) |
|
|
315
|
+
.key
|
|
316
|
+
' "$CACHE_FILE" 2>/dev/null)
|
|
317
|
+
|
|
318
|
+
EMPTY_COUNT=0
|
|
319
|
+
if [[ -n "$EMPTY_ACTIVE" ]]; then
|
|
320
|
+
EMPTY_LIST=""
|
|
321
|
+
while IFS= read -r id; do
|
|
322
|
+
[[ -z "$id" ]] && continue
|
|
323
|
+
EMPTY_COUNT=$((EMPTY_COUNT + 1))
|
|
324
|
+
[[ -n "$EMPTY_LIST" ]] && EMPTY_LIST="$EMPTY_LIST, "
|
|
325
|
+
EMPTY_LIST="$EMPTY_LIST$id"
|
|
326
|
+
done <<< "$EMPTY_ACTIVE"
|
|
327
|
+
echo "📝 Planning (no tasks): $EMPTY_LIST"
|
|
328
|
+
echo ""
|
|
329
|
+
fi
|
|
330
|
+
|
|
331
|
+
# ── Section 5: Backlog ──
|
|
332
|
+
BACKLOG=$(jq -r '
|
|
333
|
+
[.increments | to_entries[] |
|
|
334
|
+
select(.value.status == "backlog")] |
|
|
335
|
+
sort_by(.value.lastActivityEpoch // 0) | reverse | .[] |
|
|
336
|
+
"\(.key)|\(.value.tasks.completed)|\(.value.tasks.total)|\(.value.priority)"
|
|
260
337
|
' "$CACHE_FILE" 2>/dev/null)
|
|
261
338
|
|
|
262
339
|
BACKLOG_COUNT=0
|
|
263
340
|
if [[ -n "$BACKLOG" ]]; then
|
|
264
341
|
echo "📋 Backlog:"
|
|
265
|
-
while IFS='|' read -r id completed total
|
|
342
|
+
while IFS='|' read -r id completed total priority; do
|
|
266
343
|
[[ -z "$id" ]] && continue
|
|
267
344
|
BACKLOG_COUNT=$((BACKLOG_COUNT + 1))
|
|
268
345
|
if [[ "$total" -gt 0 ]]; then
|
|
@@ -270,7 +347,6 @@ if [[ -n "$BACKLOG" ]]; then
|
|
|
270
347
|
else
|
|
271
348
|
pct=0
|
|
272
349
|
fi
|
|
273
|
-
# Format priority badge
|
|
274
350
|
pri_badge=""
|
|
275
351
|
case "$priority" in P0|critical) pri_badge="🔴" ;; P1|high) pri_badge="🟠" ;; esac
|
|
276
352
|
echo " $pri_badge $id - $pct% planned"
|
|
@@ -278,48 +354,46 @@ if [[ -n "$BACKLOG" ]]; then
|
|
|
278
354
|
echo ""
|
|
279
355
|
fi
|
|
280
356
|
|
|
281
|
-
#
|
|
357
|
+
# ── Section 6: Paused ──
|
|
282
358
|
PAUSED=$(jq -r '
|
|
283
359
|
.increments | to_entries[] |
|
|
284
360
|
select(.value.status == "paused") |
|
|
285
|
-
"\(.key)|\(.value.tasks.completed)|\(.value.tasks.total)
|
|
361
|
+
"\(.key)|\(.value.tasks.completed)|\(.value.tasks.total)"
|
|
286
362
|
' "$CACHE_FILE" 2>/dev/null)
|
|
287
363
|
|
|
364
|
+
PAUSED_COUNT=0
|
|
288
365
|
if [[ -n "$PAUSED" ]]; then
|
|
289
|
-
|
|
366
|
+
while IFS='|' read -r id completed total; do
|
|
367
|
+
[[ -z "$id" ]] && continue
|
|
368
|
+
PAUSED_COUNT=$((PAUSED_COUNT + 1))
|
|
369
|
+
done <<< "$PAUSED"
|
|
290
370
|
echo "⏸️ Paused ($PAUSED_COUNT):"
|
|
291
|
-
while IFS='|' read -r id completed total
|
|
371
|
+
while IFS='|' read -r id completed total; do
|
|
292
372
|
[[ -z "$id" ]] && continue
|
|
293
373
|
if [[ "$total" -gt 0 ]]; then
|
|
294
374
|
pct=$((completed * 100 / total))
|
|
295
375
|
else
|
|
296
376
|
pct=0
|
|
297
377
|
fi
|
|
298
|
-
|
|
299
|
-
ac_pct=$((ac_completed * 100 / ac_total))
|
|
300
|
-
else
|
|
301
|
-
ac_pct=0
|
|
302
|
-
fi
|
|
303
|
-
echo " $id - $pct% tasks | $ac_pct% ACs"
|
|
378
|
+
echo " $id - $pct% tasks"
|
|
304
379
|
done <<< "$PAUSED"
|
|
305
380
|
echo ""
|
|
306
381
|
fi
|
|
307
382
|
|
|
308
|
-
# Summary
|
|
309
|
-
|
|
310
|
-
|
|
383
|
+
# ── Summary ──
|
|
384
|
+
TOTAL_ACTIONABLE=$((CLOSURE_COUNT + REVIEW_COUNT + ACTIVE_COUNT + EMPTY_COUNT + BACKLOG_COUNT + PAUSED_COUNT))
|
|
385
|
+
if [[ "$TOTAL_ACTIONABLE" -gt 0 ]]; then
|
|
311
386
|
echo "────────────────────────────────────────"
|
|
312
387
|
SUMMARY_PARTS=()
|
|
388
|
+
[[ "$CLOSURE_COUNT" -gt 0 ]] && SUMMARY_PARTS+=("$CLOSURE_COUNT needs closure")
|
|
313
389
|
[[ "$REVIEW_COUNT" -gt 0 ]] && SUMMARY_PARTS+=("$REVIEW_COUNT ready for review")
|
|
314
390
|
[[ "$ACTIVE_COUNT" -gt 0 ]] && SUMMARY_PARTS+=("$ACTIVE_COUNT active")
|
|
391
|
+
[[ "$EMPTY_COUNT" -gt 0 ]] && SUMMARY_PARTS+=("$EMPTY_COUNT planning")
|
|
315
392
|
[[ "$BACKLOG_COUNT" -gt 0 ]] && SUMMARY_PARTS+=("$BACKLOG_COUNT backlog")
|
|
316
|
-
PAUSED_COUNT=0
|
|
317
|
-
[[ -n "$PAUSED" ]] && PAUSED_COUNT=$(echo "$PAUSED" | grep -c '|' || echo 0)
|
|
318
393
|
[[ "$PAUSED_COUNT" -gt 0 ]] && SUMMARY_PARTS+=("$PAUSED_COUNT paused")
|
|
319
394
|
|
|
320
395
|
IFS=', '; echo "Summary: ${SUMMARY_PARTS[*]}"
|
|
321
396
|
else
|
|
322
|
-
# No work in progress
|
|
323
397
|
echo "No active increments."
|
|
324
398
|
COMPLETED_COUNT=$(jq '.summary.completed // 0' "$CACHE_FILE")
|
|
325
399
|
if [[ "$COMPLETED_COUNT" -gt 0 ]]; then
|
|
@@ -328,9 +402,11 @@ else
|
|
|
328
402
|
fi
|
|
329
403
|
|
|
330
404
|
echo ""
|
|
331
|
-
if [[ "$
|
|
405
|
+
if [[ "$CLOSURE_COUNT" -gt 0 ]]; then
|
|
406
|
+
echo "💡 Run /sw:done <id> to close completed increments"
|
|
407
|
+
elif [[ "$REVIEW_COUNT" -gt 0 ]]; then
|
|
332
408
|
echo "💡 Run /sw:done <id> to close reviewed increments"
|
|
333
|
-
elif [[ "$ACTIVE_COUNT" -eq 0 ]]; then
|
|
409
|
+
elif [[ "$ACTIVE_COUNT" -eq 0 ]] && [[ "$EMPTY_COUNT" -eq 0 ]]; then
|
|
334
410
|
echo "💡 Run /sw:increment to start new work"
|
|
335
411
|
else
|
|
336
412
|
echo "💡 For details: /sw:progress <incrementId>"
|
|
@@ -151,12 +151,30 @@ for increment_dir in "$INCREMENTS_DIR"/[0-9]*/; do
|
|
|
151
151
|
completed_acs="${completed_acs:-0}"
|
|
152
152
|
fi
|
|
153
153
|
|
|
154
|
-
#
|
|
154
|
+
# Collect file mtimes for stale detection and lastActivity
|
|
155
|
+
meta_mtime=0
|
|
156
|
+
tasks_mtime=0
|
|
157
|
+
spec_mtime=0
|
|
158
|
+
if [[ "$(uname)" == "Darwin" ]]; then
|
|
159
|
+
[[ -f "$metadata_file" ]] && meta_mtime=$(stat -f "%m" "$metadata_file" 2>/dev/null || echo "0")
|
|
160
|
+
[[ -f "$tasks_file" ]] && tasks_mtime=$(stat -f "%m" "$tasks_file" 2>/dev/null || echo "0")
|
|
161
|
+
[[ -f "$spec_file" ]] && spec_mtime=$(stat -f "%m" "$spec_file" 2>/dev/null || echo "0")
|
|
162
|
+
else
|
|
163
|
+
[[ -f "$metadata_file" ]] && meta_mtime=$(stat -c "%Y" "$metadata_file" 2>/dev/null || echo "0")
|
|
164
|
+
[[ -f "$tasks_file" ]] && tasks_mtime=$(stat -c "%Y" "$tasks_file" 2>/dev/null || echo "0")
|
|
165
|
+
[[ -f "$spec_file" ]] && spec_mtime=$(stat -c "%Y" "$spec_file" 2>/dev/null || echo "0")
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
# Derive lastActivity from MAX of all file mtimes (not just metadata.json)
|
|
169
|
+
max_mtime="$meta_mtime"
|
|
170
|
+
[[ "$tasks_mtime" -gt "$max_mtime" ]] && max_mtime="$tasks_mtime"
|
|
171
|
+
[[ "$spec_mtime" -gt "$max_mtime" ]] && max_mtime="$spec_mtime"
|
|
172
|
+
|
|
155
173
|
last_activity=""
|
|
156
174
|
if [[ "$(uname)" == "Darwin" ]]; then
|
|
157
|
-
last_activity=$(
|
|
175
|
+
last_activity=$(date -u -r "$max_mtime" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || echo "")
|
|
158
176
|
else
|
|
159
|
-
last_activity=$(
|
|
177
|
+
last_activity=$(date -u -d "@$max_mtime" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || echo "")
|
|
160
178
|
fi
|
|
161
179
|
|
|
162
180
|
# Build increment JSON
|
|
@@ -172,6 +190,7 @@ for increment_dir in "$INCREMENTS_DIR"/[0-9]*/; do
|
|
|
172
190
|
--argjson completed_acs "$completed_acs" \
|
|
173
191
|
--arg created_at "$created_at" \
|
|
174
192
|
--arg last_activity "$last_activity" \
|
|
193
|
+
--argjson last_activity_epoch "$max_mtime" \
|
|
175
194
|
--argjson user_stories "$user_stories" \
|
|
176
195
|
'{
|
|
177
196
|
status: $status,
|
|
@@ -183,26 +202,13 @@ for increment_dir in "$INCREMENTS_DIR"/[0-9]*/; do
|
|
|
183
202
|
acs: { total: $total_acs, completed: $completed_acs },
|
|
184
203
|
createdAt: $created_at,
|
|
185
204
|
lastActivity: $last_activity,
|
|
205
|
+
lastActivityEpoch: $last_activity_epoch,
|
|
186
206
|
userStories: $user_stories
|
|
187
207
|
}')
|
|
188
208
|
|
|
189
209
|
# Add to increments object
|
|
190
210
|
increments_json=$(echo "$increments_json" | jq --arg id "$increment_id" --argjson inc "$increment_json" '.[$id] = $inc')
|
|
191
211
|
|
|
192
|
-
# Collect mtimes for stale detection
|
|
193
|
-
meta_mtime=0
|
|
194
|
-
tasks_mtime=0
|
|
195
|
-
spec_mtime=0
|
|
196
|
-
if [[ "$(uname)" == "Darwin" ]]; then
|
|
197
|
-
[[ -f "$metadata_file" ]] && meta_mtime=$(stat -f "%m" "$metadata_file" 2>/dev/null || echo "0")
|
|
198
|
-
[[ -f "$tasks_file" ]] && tasks_mtime=$(stat -f "%m" "$tasks_file" 2>/dev/null || echo "0")
|
|
199
|
-
[[ -f "$spec_file" ]] && spec_mtime=$(stat -f "%m" "$spec_file" 2>/dev/null || echo "0")
|
|
200
|
-
else
|
|
201
|
-
[[ -f "$metadata_file" ]] && meta_mtime=$(stat -c "%Y" "$metadata_file" 2>/dev/null || echo "0")
|
|
202
|
-
[[ -f "$tasks_file" ]] && tasks_mtime=$(stat -c "%Y" "$tasks_file" 2>/dev/null || echo "0")
|
|
203
|
-
[[ -f "$spec_file" ]] && spec_mtime=$(stat -c "%Y" "$spec_file" 2>/dev/null || echo "0")
|
|
204
|
-
fi
|
|
205
|
-
|
|
206
212
|
mtimes_json=$(echo "$mtimes_json" | jq --arg id "$increment_id" \
|
|
207
213
|
--argjson meta "$meta_mtime" \
|
|
208
214
|
--argjson tasks "$tasks_mtime" \
|
|
@@ -43,10 +43,11 @@ If closing a SpecWeave framework increment, show post-closure reminders: update
|
|
|
43
43
|
|
|
44
44
|
### Step 3: Judge LLM Validation (MANDATORY)
|
|
45
45
|
|
|
46
|
-
1.
|
|
47
|
-
2.
|
|
48
|
-
3.
|
|
49
|
-
4.
|
|
46
|
+
1. **Consent check first**: Judge-LLM uses the Anthropic API (costs extra). Check `externalModels` in config. If consent not granted, ask user or skip to pattern matching fallback. See `/sw:judge-llm` consent section for full flow.
|
|
47
|
+
2. Invoke `Skill({ skill: "sw:judge-llm" })` with `--last-commit` (or `--staged`)
|
|
48
|
+
3. Uses ultrathink extended thinking via separate Opus API call
|
|
49
|
+
4. **APPROVED** -> continue | **CONCERNS** -> show, allow continuation | **REJECTED** -> STOP closure
|
|
50
|
+
5. No ANTHROPIC_API_KEY or consent denied -> falls back to pattern matching, does not block
|
|
50
51
|
|
|
51
52
|
### Step 4: Status Validation
|
|
52
53
|
|
|
@@ -53,6 +53,24 @@ allowed-tools: Read, Grep, Glob, Bash
|
|
|
53
53
|
/sw:judge-llm src/file.ts --verbose # Show progress to console
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
## External API Cost Consent (MANDATORY)
|
|
57
|
+
|
|
58
|
+
**This skill uses the Anthropic API directly (NOT your Claude Code subscription).** Each evaluation costs approximately $0.01-0.05 depending on code size.
|
|
59
|
+
|
|
60
|
+
**Before invoking the Anthropic API, you MUST check consent:**
|
|
61
|
+
|
|
62
|
+
1. Read `.specweave/config.json` → check `externalModels.consent` field
|
|
63
|
+
2. If `"always-allow"` → proceed silently
|
|
64
|
+
3. If `"never"` → skip API call, use in-session ultrathink evaluation instead
|
|
65
|
+
4. If `"ask"` (default):
|
|
66
|
+
- Check if `"anthropic"` is in `externalModels.allowedProviders`
|
|
67
|
+
- If YES → proceed silently (standing permission)
|
|
68
|
+
- If NO → **ASK USER**: "Judge-LLM will call the Anthropic API using your ANTHROPIC_API_KEY. This costs ~$0.01-0.05 per evaluation. Proceed? (yes/no/always)"
|
|
69
|
+
- "yes" → proceed this time only
|
|
70
|
+
- "no" → skip API call, use in-session ultrathink instead
|
|
71
|
+
- "always" → run: `grantStandingConsent('anthropic', projectRoot)` from `src/core/llm/consent.ts`, then proceed
|
|
72
|
+
5. No `ANTHROPIC_API_KEY` set → falls back to pattern matching automatically (no cost, no consent needed)
|
|
73
|
+
|
|
56
74
|
## Workflow
|
|
57
75
|
|
|
58
76
|
### Step 1: Gather Input
|
|
@@ -9,18 +9,16 @@ argument-hint: "[incrementId]"
|
|
|
9
9
|
|
|
10
10
|
!`s="progress"; for d in .specweave/skill-memories .claude/skill-memories "$HOME/.claude/skill-memories"; do p="$d/$s.md"; [ -f "$p" ] && awk '/^## Learnings$/{ok=1;next}/^## /{ok=0}ok' "$p" && break; done 2>/dev/null; true`
|
|
11
11
|
|
|
12
|
-
Shows detailed progress information for active increments including:
|
|
13
|
-
- **Task completion** with progress bars
|
|
14
|
-
- **Acceptance Criteria (AC)** completion percentages
|
|
15
|
-
- **Priority** indicators (P0/P1/P2)
|
|
16
|
-
- **Type** badges (feature/bug/hotfix/refactor)
|
|
17
|
-
- **Status** grouping (ready for review → active → paused)
|
|
18
|
-
|
|
19
12
|
## Hook Execution (Default)
|
|
20
13
|
|
|
21
14
|
This command is intercepted by the **UserPromptSubmit hook** for instant execution (<10ms). The hook reads from `.specweave/state/dashboard.json` cache.
|
|
22
15
|
|
|
23
|
-
**
|
|
16
|
+
**CRITICAL**: The hook output in `<system-reminder>` is ALREADY FORMATTED for the user. You MUST:
|
|
17
|
+
1. Present the hook output VERBATIM — copy it exactly as-is
|
|
18
|
+
2. Do NOT reformat into a markdown table
|
|
19
|
+
3. Do NOT re-summarize or paraphrase the data
|
|
20
|
+
4. Do NOT add interpretation unless the user asks a follow-up question
|
|
21
|
+
5. If the user asks "what should I work on next?" THEN add recommendations
|
|
24
22
|
|
|
25
23
|
## CLI Fallback
|
|
26
24
|
|
|
@@ -30,24 +28,12 @@ If hook output isn't displayed (rare), execute:
|
|
|
30
28
|
specweave status --verbose
|
|
31
29
|
```
|
|
32
30
|
|
|
33
|
-
Note: The CLI command is `specweave status` (with `progress` as an alias).
|
|
34
|
-
|
|
35
31
|
## Arguments
|
|
36
32
|
|
|
37
33
|
- `/sw:progress` - Show all active increments
|
|
38
34
|
- `/sw:progress 0042` - Show specific increment details (partial ID match supported)
|
|
39
35
|
|
|
40
|
-
## Data Shown
|
|
41
|
-
|
|
42
|
-
| Field | Description |
|
|
43
|
-
|-------|-------------|
|
|
44
|
-
| Tasks | `X/Y (Z%)` with progress bar |
|
|
45
|
-
| ACs | `A/B (C%)` acceptance criteria completion |
|
|
46
|
-
| Priority | P0 (critical), P1 (high), P2 (normal) |
|
|
47
|
-
| Type | feature, bug, hotfix, refactor, experiment |
|
|
48
|
-
| Status | ready_for_review, active, planning, paused |
|
|
49
|
-
|
|
50
36
|
## Related Commands
|
|
51
37
|
|
|
52
|
-
- `/sw:status` - Macro view (all increments grouped by status)
|
|
53
38
|
- `/sw:done <id>` - Close increment after review
|
|
39
|
+
- `/sw:increment` - Start new work
|