specweave 0.32.9 ā 0.33.2
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 +106 -1
- package/dist/src/cli/add-child-pid.d.ts +11 -0
- package/dist/src/cli/add-child-pid.d.ts.map +1 -0
- package/dist/src/cli/add-child-pid.js +42 -0
- package/dist/src/cli/add-child-pid.js.map +1 -0
- package/dist/src/cli/add-child-process.d.ts +15 -0
- package/dist/src/cli/add-child-process.d.ts.map +1 -0
- package/dist/src/cli/add-child-process.js +40 -0
- package/dist/src/cli/add-child-process.js.map +1 -0
- package/dist/src/cli/check-watchdog.d.ts +15 -0
- package/dist/src/cli/check-watchdog.d.ts.map +1 -0
- package/dist/src/cli/check-watchdog.js +47 -0
- package/dist/src/cli/check-watchdog.js.map +1 -0
- package/dist/src/cli/cleanup-zombies.d.ts +14 -0
- package/dist/src/cli/cleanup-zombies.d.ts.map +1 -0
- package/dist/src/cli/cleanup-zombies.js +268 -0
- package/dist/src/cli/cleanup-zombies.js.map +1 -0
- package/dist/src/cli/commands/jobs.js +9 -2
- package/dist/src/cli/commands/jobs.js.map +1 -1
- package/dist/src/cli/find-session-by-pid.d.ts +14 -0
- package/dist/src/cli/find-session-by-pid.d.ts.map +1 -0
- package/dist/src/cli/find-session-by-pid.js +45 -0
- package/dist/src/cli/find-session-by-pid.js.map +1 -0
- package/dist/src/cli/get-stale-sessions.d.ts +17 -0
- package/dist/src/cli/get-stale-sessions.d.ts.map +1 -0
- package/dist/src/cli/get-stale-sessions.js +36 -0
- package/dist/src/cli/get-stale-sessions.js.map +1 -0
- package/dist/src/cli/register-session.d.ts +16 -0
- package/dist/src/cli/register-session.d.ts.map +1 -0
- package/dist/src/cli/register-session.js +48 -0
- package/dist/src/cli/register-session.js.map +1 -0
- package/dist/src/cli/remove-session.d.ts +11 -0
- package/dist/src/cli/remove-session.d.ts.map +1 -0
- package/dist/src/cli/remove-session.js +36 -0
- package/dist/src/cli/remove-session.js.map +1 -0
- package/dist/src/cli/update-heartbeat.d.ts +11 -0
- package/dist/src/cli/update-heartbeat.d.ts.map +1 -0
- package/dist/src/cli/update-heartbeat.js +36 -0
- package/dist/src/cli/update-heartbeat.js.map +1 -0
- package/dist/src/cli/workers/living-docs-worker.js +6 -0
- package/dist/src/cli/workers/living-docs-worker.js.map +1 -1
- package/dist/src/config/types.d.ts +1208 -203
- package/dist/src/config/types.d.ts.map +1 -1
- package/dist/src/core/background/job-launcher.d.ts.map +1 -1
- package/dist/src/core/background/job-launcher.js +7 -4
- package/dist/src/core/background/job-launcher.js.map +1 -1
- package/dist/src/core/background/job-manager.d.ts +25 -0
- package/dist/src/core/background/job-manager.d.ts.map +1 -1
- package/dist/src/core/background/job-manager.js +136 -15
- package/dist/src/core/background/job-manager.js.map +1 -1
- package/dist/src/core/increment/increment-utils.d.ts +26 -1
- package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
- package/dist/src/core/increment/increment-utils.js +66 -4
- package/dist/src/core/increment/increment-utils.js.map +1 -1
- package/dist/src/core/increment/status-change-sync-trigger.d.ts +3 -1
- package/dist/src/core/increment/status-change-sync-trigger.d.ts.map +1 -1
- package/dist/src/core/increment/status-change-sync-trigger.js +5 -2
- package/dist/src/core/increment/status-change-sync-trigger.js.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js +48 -12
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.d.ts +70 -0
- package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.js +188 -0
- package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.d.ts +33 -0
- package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.js +290 -0
- package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js +114 -11
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.d.ts +23 -0
- package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.js +283 -0
- package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.d.ts +44 -0
- package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.js +61 -0
- package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.d.ts +126 -0
- package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.js +378 -0
- package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js +57 -0
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.d.ts +82 -0
- package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.js +430 -0
- package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.d.ts +84 -0
- package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.js +387 -0
- package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/report-writer.d.ts +61 -0
- package/dist/src/core/living-docs/intelligent-analyzer/report-writer.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/report-writer.js +174 -0
- package/dist/src/core/living-docs/intelligent-analyzer/report-writer.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts.map +1 -1
- package/dist/src/core/living-docs/module-analyzer.d.ts +3 -0
- package/dist/src/core/living-docs/module-analyzer.d.ts.map +1 -1
- package/dist/src/core/living-docs/module-analyzer.js +40 -1
- package/dist/src/core/living-docs/module-analyzer.js.map +1 -1
- package/dist/src/core/qa/qa-runner.js +1 -1
- package/dist/src/core/qa/qa-runner.js.map +1 -1
- package/dist/src/core/scheduler/session-sync-executor.js +1 -1
- package/dist/src/core/scheduler/session-sync-executor.js.map +1 -1
- package/dist/src/core/status-line/status-line-updater.d.ts +1 -1
- package/dist/src/core/status-line/status-line-updater.d.ts.map +1 -1
- package/dist/src/core/status-line/status-line-updater.js +4 -3
- package/dist/src/core/status-line/status-line-updater.js.map +1 -1
- package/dist/src/importers/jira-importer.d.ts.map +1 -1
- package/dist/src/importers/jira-importer.js +26 -10
- package/dist/src/importers/jira-importer.js.map +1 -1
- package/dist/src/init/architecture/types.d.ts +140 -33
- package/dist/src/init/architecture/types.d.ts.map +1 -1
- package/dist/src/init/compliance/types.d.ts +27 -30
- package/dist/src/init/compliance/types.d.ts.map +1 -1
- package/dist/src/init/repo/types.d.ts +34 -11
- package/dist/src/init/repo/types.d.ts.map +1 -1
- package/dist/src/init/research/src/config/types.d.ts +82 -15
- package/dist/src/init/research/src/config/types.d.ts.map +1 -1
- package/dist/src/init/research/types.d.ts +93 -38
- package/dist/src/init/research/types.d.ts.map +1 -1
- package/dist/src/init/team/types.d.ts +42 -4
- package/dist/src/init/team/types.d.ts.map +1 -1
- package/dist/src/sync/ado-reconciler.js +1 -1
- package/dist/src/sync/ado-reconciler.js.map +1 -1
- package/dist/src/sync/github-reconciler.js +1 -1
- package/dist/src/sync/github-reconciler.js.map +1 -1
- package/dist/src/sync/jira-reconciler.js +1 -1
- package/dist/src/sync/jira-reconciler.js.map +1 -1
- package/dist/src/types/session.d.ts +65 -0
- package/dist/src/types/session.d.ts.map +1 -0
- package/dist/src/types/session.js +8 -0
- package/dist/src/types/session.js.map +1 -0
- package/dist/src/utils/lock-manager.d.ts +48 -0
- package/dist/src/utils/lock-manager.d.ts.map +1 -0
- package/dist/src/utils/lock-manager.js +195 -0
- package/dist/src/utils/lock-manager.js.map +1 -0
- package/dist/src/utils/notification-manager.d.ts +45 -0
- package/dist/src/utils/notification-manager.d.ts.map +1 -0
- package/dist/src/utils/notification-manager.js +130 -0
- package/dist/src/utils/notification-manager.js.map +1 -0
- package/dist/src/utils/platform-utils.d.ts +136 -0
- package/dist/src/utils/platform-utils.d.ts.map +1 -0
- package/dist/src/utils/platform-utils.js +366 -0
- package/dist/src/utils/platform-utils.js.map +1 -0
- package/dist/src/utils/session-registry.d.ts +142 -0
- package/dist/src/utils/session-registry.d.ts.map +1 -0
- package/dist/src/utils/session-registry.js +480 -0
- package/dist/src/utils/session-registry.js.map +1 -0
- package/package.json +5 -2
- package/plugins/specweave/commands/specweave-living-docs.md +42 -0
- package/plugins/specweave/hooks/hooks.json +10 -0
- package/plugins/specweave/hooks/lib/update-active-increment.sh +2 -2
- package/plugins/specweave/hooks/lib/update-status-line.sh +1 -1
- package/plugins/specweave/hooks/post-increment-status-change.sh +3 -3
- package/plugins/specweave/hooks/post-metadata-change.sh +1 -1
- package/plugins/specweave/hooks/universal/hook-wrapper.cmd +26 -26
- package/plugins/specweave/hooks/universal/session-start.cmd +16 -16
- package/plugins/specweave/hooks/universal/session-start.ps1 +16 -16
- package/plugins/specweave/hooks/user-prompt-submit.sh +2 -2
- package/plugins/specweave/hooks/v2/guards/increment-root-guard.sh +61 -0
- package/plugins/specweave/hooks/v2/session-end.sh +69 -0
- package/plugins/specweave/hooks/v2/session-start.sh +81 -0
- package/plugins/specweave/lib/vendor/sync/github-reconciler.js +1 -1
- package/plugins/specweave/lib/vendor/sync/github-reconciler.js.map +1 -1
- package/plugins/specweave/scripts/heartbeat.sh +110 -0
- package/plugins/specweave/scripts/progress.js +34 -4
- package/plugins/specweave/scripts/read-jobs.sh +1 -1
- package/plugins/specweave/scripts/read-progress.sh +50 -5
- package/plugins/specweave/scripts/read-workflow.sh +1 -1
- package/plugins/specweave/scripts/session-watchdog.sh +65 -0
- package/plugins/specweave/scripts/status.js +28 -11
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +738 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1107 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Heartbeat Background Process
|
|
4
|
+
#
|
|
5
|
+
# Purpose:
|
|
6
|
+
# - Updates session heartbeat timestamp every 5 seconds
|
|
7
|
+
# - Polls parent process existence
|
|
8
|
+
# - Self-terminates when parent dies
|
|
9
|
+
# - Removes session from registry on exit
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# bash heartbeat.sh <session-id>
|
|
13
|
+
#
|
|
14
|
+
# Background Usage:
|
|
15
|
+
# nohup bash heartbeat.sh <session-id> > .specweave/logs/heartbeat-<session-id>.log 2>&1 &
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
# ============================================================================
|
|
20
|
+
# Configuration
|
|
21
|
+
# ============================================================================
|
|
22
|
+
|
|
23
|
+
SESSION_ID="${1:-}"
|
|
24
|
+
INTERVAL=5 # Heartbeat interval in seconds
|
|
25
|
+
PROJECT_ROOT="${PWD}"
|
|
26
|
+
REGISTRY_FILE="${PROJECT_ROOT}/.specweave/state/.session-registry.json"
|
|
27
|
+
LOG_FILE="${PROJECT_ROOT}/.specweave/logs/heartbeat-${SESSION_ID}.log"
|
|
28
|
+
|
|
29
|
+
# ============================================================================
|
|
30
|
+
# Validation
|
|
31
|
+
# ============================================================================
|
|
32
|
+
|
|
33
|
+
if [[ -z "$SESSION_ID" ]]; then
|
|
34
|
+
echo "ā ERROR: SESSION_ID required"
|
|
35
|
+
echo "Usage: bash heartbeat.sh <session-id>"
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# Ensure logs directory exists
|
|
40
|
+
mkdir -p "$(dirname "$LOG_FILE")"
|
|
41
|
+
|
|
42
|
+
# ============================================================================
|
|
43
|
+
# Helper Functions
|
|
44
|
+
# ============================================================================
|
|
45
|
+
|
|
46
|
+
log() {
|
|
47
|
+
local level="$1"
|
|
48
|
+
shift
|
|
49
|
+
echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] [$level] [${SESSION_ID}] $*" >> "$LOG_FILE"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Cross-platform parent process existence check
|
|
53
|
+
check_parent_alive() {
|
|
54
|
+
local ppid="$PPID"
|
|
55
|
+
|
|
56
|
+
if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then
|
|
57
|
+
# Windows: Use tasklist
|
|
58
|
+
tasklist /FI "PID eq $ppid" 2>/dev/null | grep -q "$ppid"
|
|
59
|
+
return $?
|
|
60
|
+
else
|
|
61
|
+
# macOS/Linux: Use kill -0
|
|
62
|
+
kill -0 "$ppid" 2>/dev/null
|
|
63
|
+
return $?
|
|
64
|
+
fi
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# Update heartbeat timestamp via Node.js CLI
|
|
68
|
+
update_heartbeat() {
|
|
69
|
+
node "${PROJECT_ROOT}/dist/src/cli/update-heartbeat.js" "$SESSION_ID" 2>&1 | \
|
|
70
|
+
grep -v "^$" | head -5 >> "$LOG_FILE" || true
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Cleanup session on exit
|
|
74
|
+
cleanup_session() {
|
|
75
|
+
log "INFO" "Parent process died, cleaning up session..."
|
|
76
|
+
|
|
77
|
+
# Remove session from registry
|
|
78
|
+
node "${PROJECT_ROOT}/dist/src/cli/remove-session.js" "$SESSION_ID" 2>&1 | \
|
|
79
|
+
grep -v "^$" | head -5 >> "$LOG_FILE" || true
|
|
80
|
+
|
|
81
|
+
log "INFO" "Session cleanup complete, exiting"
|
|
82
|
+
exit 0
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# ============================================================================
|
|
86
|
+
# Main Loop
|
|
87
|
+
# ============================================================================
|
|
88
|
+
|
|
89
|
+
log "INFO" "Heartbeat process started (parent PID: $PPID, interval: ${INTERVAL}s)"
|
|
90
|
+
|
|
91
|
+
# Trap signals for graceful shutdown
|
|
92
|
+
trap cleanup_session SIGTERM SIGINT
|
|
93
|
+
|
|
94
|
+
while true; do
|
|
95
|
+
# Check if parent process still exists
|
|
96
|
+
if ! check_parent_alive; then
|
|
97
|
+
log "WARN" "Parent process (PID: $PPID) no longer exists"
|
|
98
|
+
cleanup_session
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# Update heartbeat timestamp
|
|
102
|
+
if update_heartbeat; then
|
|
103
|
+
log "DEBUG" "Heartbeat updated"
|
|
104
|
+
else
|
|
105
|
+
log "ERROR" "Failed to update heartbeat"
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# Sleep for interval
|
|
109
|
+
sleep "$INTERVAL"
|
|
110
|
+
done
|
|
@@ -118,11 +118,24 @@ if (specificId) {
|
|
|
118
118
|
|
|
119
119
|
// Show all active increments
|
|
120
120
|
const increments = incrementFolders.map(parseIncrement);
|
|
121
|
-
const
|
|
121
|
+
const readyForReview = increments.filter(i => i.status === 'ready_for_review');
|
|
122
|
+
const active = increments.filter(i => ['active', 'planning', 'backlog'].includes(i.status));
|
|
122
123
|
const paused = increments.filter(i => i.status === 'paused');
|
|
123
124
|
|
|
124
125
|
console.log('\nš Increment Progress\n');
|
|
125
126
|
|
|
127
|
+
// Show ready_for_review FIRST (needs attention!)
|
|
128
|
+
if (readyForReview.length > 0) {
|
|
129
|
+
console.log(`š Ready for Review (${readyForReview.length}):`);
|
|
130
|
+
for (const inc of readyForReview) {
|
|
131
|
+
const bar = createProgressBar(inc.percentage, 15);
|
|
132
|
+
console.log(` ${inc.id}`);
|
|
133
|
+
console.log(` ${bar} ${inc.completedTasks}/${inc.totalTasks} (${inc.percentage}%)`);
|
|
134
|
+
console.log(` ā /specweave:done ${inc.id}`);
|
|
135
|
+
}
|
|
136
|
+
console.log('');
|
|
137
|
+
}
|
|
138
|
+
|
|
126
139
|
if (active.length > 0) {
|
|
127
140
|
console.log(`š Active (${active.length}):`);
|
|
128
141
|
for (const inc of active) {
|
|
@@ -141,16 +154,31 @@ if (paused.length > 0) {
|
|
|
141
154
|
console.log('');
|
|
142
155
|
}
|
|
143
156
|
|
|
144
|
-
|
|
157
|
+
// Summary section
|
|
158
|
+
if (readyForReview.length > 0 || active.length > 0 || paused.length > 0) {
|
|
159
|
+
console.log('ā'.repeat(40));
|
|
160
|
+
const parts = [];
|
|
161
|
+
if (readyForReview.length > 0) parts.push(`${readyForReview.length} ready for review`);
|
|
162
|
+
if (active.length > 0) parts.push(`${active.length} active`);
|
|
163
|
+
if (paused.length > 0) parts.push(`${paused.length} paused`);
|
|
164
|
+
console.log(`Summary: ${parts.join(', ')}`);
|
|
165
|
+
console.log('');
|
|
166
|
+
|
|
167
|
+
if (readyForReview.length > 0) {
|
|
168
|
+
console.log('š” Run /specweave:done <id> to close reviewed increments');
|
|
169
|
+
} else {
|
|
170
|
+
console.log('š” For details: /specweave:progress <incrementId>');
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
145
173
|
console.log('No active increments.');
|
|
146
174
|
const completed = increments.filter(i => i.status === 'completed');
|
|
147
175
|
if (completed.length > 0) {
|
|
148
176
|
console.log(`${completed.length} completed increment(s).`);
|
|
149
177
|
}
|
|
178
|
+
console.log('');
|
|
179
|
+
console.log('š” Run /specweave:increment to start new work');
|
|
150
180
|
}
|
|
151
181
|
|
|
152
|
-
console.log('š” For details: /specweave:progress <incrementId>');
|
|
153
|
-
|
|
154
182
|
// Helpers
|
|
155
183
|
function createProgressBar(pct, width = 20) {
|
|
156
184
|
const filled = Math.round((pct / 100) * width);
|
|
@@ -162,6 +190,8 @@ function formatStatus(status) {
|
|
|
162
190
|
const icons = {
|
|
163
191
|
'active': 'š active',
|
|
164
192
|
'planning': 'š planning',
|
|
193
|
+
'backlog': 'š backlog',
|
|
194
|
+
'ready_for_review': 'š ready for review',
|
|
165
195
|
'paused': 'āøļø paused',
|
|
166
196
|
'completed': 'ā
completed',
|
|
167
197
|
'abandoned': 'ā abandoned'
|
|
@@ -110,7 +110,7 @@ if [[ -f "$CACHE_FILE" ]] && jq -e '.' "$CACHE_FILE" >/dev/null 2>&1; then
|
|
|
110
110
|
# Show active increments
|
|
111
111
|
ACTIVE=$(jq -r '
|
|
112
112
|
.increments | to_entries[] |
|
|
113
|
-
select(.value.status == "active" or .value.status == "planning" or .value.status == "
|
|
113
|
+
select(.value.status == "active" or .value.status == "planning" or .value.status == "backlog" or .value.status == "ready_for_review") |
|
|
114
114
|
"\(.key)|\(.value.status)|\(.value.tasks.completed)|\(.value.tasks.total)"
|
|
115
115
|
' "$CACHE_FILE" 2>/dev/null)
|
|
116
116
|
|
|
@@ -126,10 +126,36 @@ echo ""
|
|
|
126
126
|
echo "š Increment Progress"
|
|
127
127
|
echo ""
|
|
128
128
|
|
|
129
|
-
# Get
|
|
129
|
+
# Get ready_for_review increments FIRST (needs attention!)
|
|
130
|
+
READY_FOR_REVIEW=$(jq -r '
|
|
131
|
+
.increments | to_entries[] |
|
|
132
|
+
select(.value.status == "ready_for_review") |
|
|
133
|
+
"\(.key)|\(.value.tasks.completed)|\(.value.tasks.total)"
|
|
134
|
+
' "$CACHE_FILE" 2>/dev/null)
|
|
135
|
+
|
|
136
|
+
REVIEW_COUNT=0
|
|
137
|
+
if [[ -n "$READY_FOR_REVIEW" ]]; then
|
|
138
|
+
echo "š Ready for Review:"
|
|
139
|
+
while IFS='|' read -r id completed total; do
|
|
140
|
+
[[ -z "$id" ]] && continue
|
|
141
|
+
REVIEW_COUNT=$((REVIEW_COUNT + 1))
|
|
142
|
+
if [[ "$total" -gt 0 ]]; then
|
|
143
|
+
pct=$((completed * 100 / total))
|
|
144
|
+
else
|
|
145
|
+
pct=0
|
|
146
|
+
fi
|
|
147
|
+
bar=$(progress_bar "$pct" 15)
|
|
148
|
+
echo " $id"
|
|
149
|
+
echo " $bar $completed/$total ($pct%)"
|
|
150
|
+
echo " ā /specweave:done $id"
|
|
151
|
+
done <<< "$READY_FOR_REVIEW"
|
|
152
|
+
echo ""
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# Get active increments (excluding ready_for_review)
|
|
130
156
|
ACTIVE=$(jq -r '
|
|
131
157
|
.increments | to_entries[] |
|
|
132
|
-
select(.value.status == "active" or .value.status == "planning" or .value.status == "
|
|
158
|
+
select(.value.status == "active" or .value.status == "planning" or .value.status == "backlog") |
|
|
133
159
|
"\(.key)|\(.value.tasks.completed)|\(.value.tasks.total)"
|
|
134
160
|
' "$CACHE_FILE" 2>/dev/null)
|
|
135
161
|
|
|
@@ -173,8 +199,20 @@ if [[ -n "$PAUSED" ]]; then
|
|
|
173
199
|
echo ""
|
|
174
200
|
fi
|
|
175
201
|
|
|
176
|
-
#
|
|
177
|
-
if [[ "$ACTIVE_COUNT" -
|
|
202
|
+
# Summary section
|
|
203
|
+
if [[ "$REVIEW_COUNT" -gt 0 ]] || [[ "$ACTIVE_COUNT" -gt 0 ]] || [[ -n "$PAUSED" ]]; then
|
|
204
|
+
# Has work - show summary
|
|
205
|
+
echo "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"
|
|
206
|
+
SUMMARY_PARTS=()
|
|
207
|
+
[[ "$REVIEW_COUNT" -gt 0 ]] && SUMMARY_PARTS+=("$REVIEW_COUNT ready for review")
|
|
208
|
+
[[ "$ACTIVE_COUNT" -gt 0 ]] && SUMMARY_PARTS+=("$ACTIVE_COUNT active")
|
|
209
|
+
PAUSED_COUNT=0
|
|
210
|
+
[[ -n "$PAUSED" ]] && PAUSED_COUNT=$(echo "$PAUSED" | grep -c '|' || echo 0)
|
|
211
|
+
[[ "$PAUSED_COUNT" -gt 0 ]] && SUMMARY_PARTS+=("$PAUSED_COUNT paused")
|
|
212
|
+
|
|
213
|
+
IFS=', '; echo "Summary: ${SUMMARY_PARTS[*]}"
|
|
214
|
+
else
|
|
215
|
+
# No work in progress
|
|
178
216
|
echo "No active increments."
|
|
179
217
|
COMPLETED_COUNT=$(jq '.summary.completed // 0' "$CACHE_FILE")
|
|
180
218
|
if [[ "$COMPLETED_COUNT" -gt 0 ]]; then
|
|
@@ -182,4 +220,11 @@ if [[ "$ACTIVE_COUNT" -eq 0 ]] && [[ -z "$PAUSED" ]]; then
|
|
|
182
220
|
fi
|
|
183
221
|
fi
|
|
184
222
|
|
|
185
|
-
echo "
|
|
223
|
+
echo ""
|
|
224
|
+
if [[ "$REVIEW_COUNT" -gt 0 ]]; then
|
|
225
|
+
echo "š” Run /specweave:done <id> to close reviewed increments"
|
|
226
|
+
elif [[ "$ACTIVE_COUNT" -eq 0 ]]; then
|
|
227
|
+
echo "š” Run /specweave:increment to start new work"
|
|
228
|
+
else
|
|
229
|
+
echo "š” For details: /specweave:progress <incrementId>"
|
|
230
|
+
fi
|
|
@@ -64,7 +64,7 @@ else
|
|
|
64
64
|
# Find first active increment
|
|
65
65
|
ACTIVE_ID=$(jq -r '
|
|
66
66
|
.increments | to_entries[] |
|
|
67
|
-
select(.value.status == "active" or .value.status == "
|
|
67
|
+
select(.value.status == "active" or .value.status == "planning" or .value.status == "backlog" or .value.status == "ready_for_review") |
|
|
68
68
|
.key
|
|
69
69
|
' "$CACHE_FILE" 2>/dev/null | head -1)
|
|
70
70
|
fi
|
|
@@ -12,6 +12,9 @@ SPECWEAVE_ROOT="${SPECWEAVE_ROOT:-.specweave}"
|
|
|
12
12
|
SIGNAL_FILE="${SPECWEAVE_ROOT}/state/.session-stuck"
|
|
13
13
|
HEARTBEAT_FILE="${SPECWEAVE_ROOT}/state/.heartbeat"
|
|
14
14
|
DAEMON_MODE=false
|
|
15
|
+
PROJECT_ROOT="${PWD}"
|
|
16
|
+
SESSION_ID="watchdog-$$-$(date +%s)"
|
|
17
|
+
COORDINATION_THRESHOLD=30 # Heartbeat threshold for active watchdog detection
|
|
15
18
|
|
|
16
19
|
# Colors
|
|
17
20
|
RED='\033[0;31m'
|
|
@@ -176,15 +179,77 @@ check_session_health() {
|
|
|
176
179
|
fi
|
|
177
180
|
}
|
|
178
181
|
|
|
182
|
+
# Coordination Functions
|
|
183
|
+
check_active_watchdog() {
|
|
184
|
+
# Check if another watchdog is running via session registry
|
|
185
|
+
node "${PROJECT_ROOT}/dist/src/cli/check-watchdog.js" 2>/dev/null || echo ""
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
register_watchdog() {
|
|
189
|
+
# Register this watchdog in session registry
|
|
190
|
+
node "${PROJECT_ROOT}/dist/src/cli/register-session.js" "$SESSION_ID" $$ "watchdog" 2>&1 | \
|
|
191
|
+
grep -v "^$" | head -3
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
update_watchdog_heartbeat() {
|
|
195
|
+
# Update watchdog heartbeat
|
|
196
|
+
node "${PROJECT_ROOT}/dist/src/cli/update-heartbeat.js" "$SESSION_ID" 2>/dev/null || true
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
cleanup_watchdog() {
|
|
200
|
+
# Remove watchdog from registry on exit
|
|
201
|
+
node "${PROJECT_ROOT}/dist/src/cli/remove-session.js" "$SESSION_ID" 2>&1 | \
|
|
202
|
+
grep -v "^$" | head -3
|
|
203
|
+
log "Watchdog cleanup complete"
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
run_cleanup_service() {
|
|
207
|
+
# Run zombie process cleanup
|
|
208
|
+
node "${PROJECT_ROOT}/dist/src/cli/cleanup-zombies.js" 60 2>&1 | \
|
|
209
|
+
grep -v "^$" | head -10 || true
|
|
210
|
+
}
|
|
211
|
+
|
|
179
212
|
# Main execution
|
|
180
213
|
if [[ "$DAEMON_MODE" == "true" ]]; then
|
|
214
|
+
# Coordination check before starting daemon
|
|
215
|
+
active_watchdog=$(check_active_watchdog)
|
|
216
|
+
|
|
217
|
+
if [[ -n "$active_watchdog" ]]; then
|
|
218
|
+
log "${YELLOW}Watchdog already active (PID: $active_watchdog)${NC}"
|
|
219
|
+
log "Exiting to avoid duplicate watchdogs"
|
|
220
|
+
exit 0
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
# Register as watchdog
|
|
224
|
+
register_watchdog
|
|
225
|
+
|
|
226
|
+
# Trap signals for graceful shutdown
|
|
227
|
+
trap cleanup_watchdog SIGTERM SIGINT EXIT
|
|
228
|
+
|
|
181
229
|
log "Starting session watchdog daemon (interval: ${CHECK_INTERVAL}s, threshold: ${STUCK_THRESHOLD_SECONDS}s)"
|
|
230
|
+
log "Watchdog session: $SESSION_ID (PID: $$)"
|
|
182
231
|
log "Press Ctrl+C to stop"
|
|
183
232
|
|
|
184
233
|
while true; do
|
|
234
|
+
# Update own heartbeat
|
|
235
|
+
update_watchdog_heartbeat
|
|
236
|
+
|
|
237
|
+
# Check session health
|
|
185
238
|
check_session_health || true
|
|
239
|
+
|
|
240
|
+
# Run cleanup service
|
|
241
|
+
run_cleanup_service
|
|
242
|
+
|
|
243
|
+
# Check if parent process still exists (if we have a parent session)
|
|
244
|
+
if ! kill -0 $PPID 2>/dev/null; then
|
|
245
|
+
log "${YELLOW}Parent process died, exiting watchdog${NC}"
|
|
246
|
+
break
|
|
247
|
+
fi
|
|
248
|
+
|
|
186
249
|
sleep "$CHECK_INTERVAL"
|
|
187
250
|
done
|
|
251
|
+
|
|
252
|
+
cleanup_watchdog
|
|
188
253
|
else
|
|
189
254
|
log "Running single health check..."
|
|
190
255
|
check_session_health
|
|
@@ -86,7 +86,7 @@ for (const folder of incrementFolders) {
|
|
|
86
86
|
// Display
|
|
87
87
|
console.log('\nš SpecWeave Status Overview\n');
|
|
88
88
|
|
|
89
|
-
const statusOrder = ['active', 'planning', 'in-progress', '
|
|
89
|
+
const statusOrder = ['ready_for_review', 'active', 'planning', 'in-progress', 'paused', 'backlog', 'completed', 'abandoned'];
|
|
90
90
|
const statusIcons = {
|
|
91
91
|
'active': 'š',
|
|
92
92
|
'planning': 'š',
|
|
@@ -105,12 +105,16 @@ for (const status of statusOrder) {
|
|
|
105
105
|
if (items && items.length > 0) {
|
|
106
106
|
hasOutput = true;
|
|
107
107
|
const icon = statusIcons[status] || 'ā¢';
|
|
108
|
-
console.log(`${icon} ${status.replace(
|
|
109
|
-
|
|
108
|
+
console.log(`${icon} ${status.replace(/_/g, ' ')} (${items.length}):`);
|
|
109
|
+
|
|
110
110
|
// Show first 5, summarize rest
|
|
111
111
|
const toShow = items.slice(0, 5);
|
|
112
112
|
for (const item of toShow) {
|
|
113
113
|
console.log(` ${item}`);
|
|
114
|
+
// Add action hint for ready_for_review
|
|
115
|
+
if (status === 'ready_for_review') {
|
|
116
|
+
console.log(` ā /specweave:done ${item}`);
|
|
117
|
+
}
|
|
114
118
|
}
|
|
115
119
|
if (items.length > 5) {
|
|
116
120
|
console.log(` ... and ${items.length - 5} more`);
|
|
@@ -140,15 +144,28 @@ if (!hasOutput) {
|
|
|
140
144
|
|
|
141
145
|
// Summary line
|
|
142
146
|
const total = incrementFolders.length;
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
(byStatus['
|
|
147
|
+
const reviewCount = byStatus['ready_for_review']?.length || 0;
|
|
148
|
+
const activeCount = (byStatus['active']?.length || 0) +
|
|
149
|
+
(byStatus['planning']?.length || 0) +
|
|
150
|
+
(byStatus['backlog']?.length || 0);
|
|
146
151
|
const completedCount = byStatus['completed']?.length || 0;
|
|
147
152
|
|
|
148
153
|
console.log('ā'.repeat(40));
|
|
149
|
-
|
|
154
|
+
const summaryParts = [`Total: ${total}`];
|
|
155
|
+
if (reviewCount > 0) summaryParts.push(`Review: ${reviewCount}`);
|
|
156
|
+
summaryParts.push(`Active: ${activeCount}`);
|
|
157
|
+
summaryParts.push(`Completed: ${completedCount}`);
|
|
158
|
+
if (archivedCount > 0) summaryParts.push(`Archived: ${archivedCount}`);
|
|
159
|
+
console.log(summaryParts.join(' | '));
|
|
150
160
|
console.log('');
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
console.log('
|
|
161
|
+
|
|
162
|
+
// Context-aware command hints
|
|
163
|
+
if (reviewCount > 0) {
|
|
164
|
+
console.log('š” Next step: /specweave:done <id> to close reviewed increment(s)');
|
|
165
|
+
} else if (activeCount > 0) {
|
|
166
|
+
console.log('š” Commands:');
|
|
167
|
+
console.log(' /specweave:progress Show task progress');
|
|
168
|
+
console.log(' /specweave:do Execute current tasks');
|
|
169
|
+
} else {
|
|
170
|
+
console.log('š” Run /specweave:increment to start new work');
|
|
171
|
+
}
|