specweave 0.28.32 β 0.28.34
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/dist/plugins/specweave-github/lib/github-client-v2.d.ts +14 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js +57 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/src/cli/helpers/init/external-import.js +4 -0
- package/dist/src/cli/helpers/init/external-import.js.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +15 -0
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
- package/dist/src/importers/import-coordinator.d.ts +1 -0
- package/dist/src/importers/import-coordinator.d.ts.map +1 -1
- package/dist/src/importers/import-coordinator.js +3 -0
- package/dist/src/importers/import-coordinator.js.map +1 -1
- package/dist/src/living-docs/fs-id-allocator.d.ts +11 -0
- package/dist/src/living-docs/fs-id-allocator.d.ts.map +1 -1
- package/dist/src/living-docs/fs-id-allocator.js +24 -0
- package/dist/src/living-docs/fs-id-allocator.js.map +1 -1
- package/dist/src/sync/github-reconciler.d.ts +94 -0
- package/dist/src/sync/github-reconciler.d.ts.map +1 -0
- package/dist/src/sync/github-reconciler.js +435 -0
- package/dist/src/sync/github-reconciler.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/hooks/hooks.json +10 -0
- package/plugins/specweave/hooks/post-increment-status-change.sh +123 -32
- package/plugins/specweave/hooks/session-start-reconcile.sh +139 -0
- package/plugins/specweave/lib/hooks/close-github-issues-abandoned.js +37 -0
- package/plugins/specweave/lib/hooks/close-github-issues-abandoned.ts +59 -0
- package/plugins/specweave/lib/hooks/reopen-github-issues.js +37 -0
- package/plugins/specweave/lib/hooks/reopen-github-issues.ts +59 -0
- package/plugins/specweave/lib/hooks/session-start-reconcile-worker.js +36 -0
- package/plugins/specweave/lib/hooks/session-start-reconcile-worker.ts +58 -0
- package/plugins/specweave/lib/vendor/sync/github-reconciler.d.ts +94 -0
- package/plugins/specweave/lib/vendor/sync/github-reconciler.js +435 -0
- package/plugins/specweave/lib/vendor/sync/github-reconciler.js.map +1 -0
- package/plugins/specweave-github/commands/specweave-github-reconcile.md +110 -0
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +24 -0
- package/plugins/specweave-github/lib/github-client-v2.js +55 -0
- package/plugins/specweave-github/lib/github-client-v2.ts +68 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +36 -0
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
|
-
# SpecWeave Post-Increment-Status-Change Hook
|
|
3
|
+
# SpecWeave Post-Increment-Status-Change Hook (v0.28.33)
|
|
4
4
|
# Runs automatically after increment status changes (pause/resume/abandon)
|
|
5
5
|
#
|
|
6
6
|
# Trigger: /specweave:pause, /specweave:resume, /specweave:abandon commands
|
|
7
|
-
# Purpose:
|
|
7
|
+
# Purpose: Sync GitHub issue state with increment status
|
|
8
8
|
#
|
|
9
9
|
# What it does:
|
|
10
10
|
# 1. Detects status change (paused, resumed, abandoned)
|
|
11
11
|
# 2. Posts comment to GitHub issue
|
|
12
|
-
# 3.
|
|
12
|
+
# 3. NEW (v0.28.33): Reopens issues when resumed, closes when abandoned
|
|
13
13
|
#
|
|
14
14
|
# Usage:
|
|
15
15
|
# ./post-increment-status-change.sh <incrementId> <newStatus> <reason>
|
|
@@ -43,6 +43,7 @@ cd "$PROJECT_ROOT" 2>/dev/null || true
|
|
|
43
43
|
# Configuration
|
|
44
44
|
LOGS_DIR=".specweave/logs"
|
|
45
45
|
DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
|
|
46
|
+
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
46
47
|
|
|
47
48
|
mkdir -p "$LOGS_DIR" 2>/dev/null || true
|
|
48
49
|
|
|
@@ -61,7 +62,7 @@ echo "[$(date)] π Status changed: $NEW_STATUS" >> "$DEBUG_LOG" 2>/dev/null ||
|
|
|
61
62
|
|
|
62
63
|
# Validate status
|
|
63
64
|
case "$NEW_STATUS" in
|
|
64
|
-
paused|resumed|abandoned)
|
|
65
|
+
paused|resumed|abandoned|active|in-progress)
|
|
65
66
|
;;
|
|
66
67
|
*)
|
|
67
68
|
echo "[$(date)] β οΈ Unknown status: $NEW_STATUS (skipping sync)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
@@ -89,14 +90,9 @@ if [ ! -f "$METADATA_FILE" ]; then
|
|
|
89
90
|
exit 0
|
|
90
91
|
fi
|
|
91
92
|
|
|
92
|
-
# Extract GitHub issue number
|
|
93
|
+
# Extract GitHub issue number (main increment issue)
|
|
93
94
|
GITHUB_ISSUE=$(jq -r '.github.issue // empty' "$METADATA_FILE" 2>/dev/null)
|
|
94
95
|
|
|
95
|
-
if [ -z "$GITHUB_ISSUE" ]; then
|
|
96
|
-
echo "[$(date)] βΉοΈ No GitHub issue linked, skipping sync" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
97
|
-
exit 0
|
|
98
|
-
fi
|
|
99
|
-
|
|
100
96
|
# Detect repository
|
|
101
97
|
GITHUB_REMOTE=$(git remote get-url origin 2>/dev/null || echo "")
|
|
102
98
|
REPO_MATCH=$(echo "$GITHUB_REMOTE" | grep -o 'github\.com[:/][^/]*/[^/]*' | sed 's/github\.com[:/]//')
|
|
@@ -106,29 +102,122 @@ if [ -z "$REPO_MATCH" ]; then
|
|
|
106
102
|
exit 0
|
|
107
103
|
fi
|
|
108
104
|
|
|
109
|
-
|
|
105
|
+
# ============================================================================
|
|
106
|
+
# STATUS-BASED ACTIONS (NEW in v0.28.33)
|
|
107
|
+
# ============================================================================
|
|
110
108
|
|
|
111
|
-
# Build comment based on status
|
|
112
|
-
EMOJI=""
|
|
113
|
-
TITLE=""
|
|
114
109
|
case "$NEW_STATUS" in
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
110
|
+
resumed|active|in-progress)
|
|
111
|
+
# ========================================================================
|
|
112
|
+
# REOPEN GitHub Issues (NEW)
|
|
113
|
+
# ========================================================================
|
|
114
|
+
# When increment is resumed, reopen all closed GitHub issues
|
|
115
|
+
echo "[$(date)] βΆοΈ Status is $NEW_STATUS - checking if issues need reopening" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
116
|
+
|
|
117
|
+
if command -v node &> /dev/null; then
|
|
118
|
+
# Find reopen script
|
|
119
|
+
REOPEN_SCRIPT=""
|
|
120
|
+
if [ -f "$PROJECT_ROOT/plugins/specweave/lib/hooks/reopen-github-issues.js" ]; then
|
|
121
|
+
REOPEN_SCRIPT="$PROJECT_ROOT/plugins/specweave/lib/hooks/reopen-github-issues.js"
|
|
122
|
+
elif [ -f "$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/reopen-github-issues.js" ]; then
|
|
123
|
+
REOPEN_SCRIPT="$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/reopen-github-issues.js"
|
|
124
|
+
elif [ -f "$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/reopen-github-issues.js" ]; then
|
|
125
|
+
REOPEN_SCRIPT="$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/reopen-github-issues.js"
|
|
126
|
+
elif [ -n "${CLAUDE_PLUGIN_ROOT}" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/lib/hooks/reopen-github-issues.js" ]; then
|
|
127
|
+
REOPEN_SCRIPT="${CLAUDE_PLUGIN_ROOT}/lib/hooks/reopen-github-issues.js"
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
if [ -n "$REOPEN_SCRIPT" ]; then
|
|
131
|
+
# Load GITHUB_TOKEN from .env
|
|
132
|
+
if [ -f "$PROJECT_ROOT/.env" ]; then
|
|
133
|
+
GITHUB_TOKEN_FROM_ENV=$(grep -E '^GITHUB_TOKEN=' "$PROJECT_ROOT/.env" 2>/dev/null | head -1 | cut -d'=' -f2- | sed 's/^["'\'']//' | sed 's/["'\'']$//')
|
|
134
|
+
if [ -n "$GITHUB_TOKEN_FROM_ENV" ]; then
|
|
135
|
+
export GITHUB_TOKEN="$GITHUB_TOKEN_FROM_ENV"
|
|
136
|
+
fi
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
echo "βΆοΈ Reopening GitHub issues for resumed increment..."
|
|
140
|
+
(cd "$PROJECT_ROOT" && node "$REOPEN_SCRIPT" "$INCREMENT_ID" "${REASON:-Increment resumed}") 2>&1 | tee -a "$DEBUG_LOG" || {
|
|
141
|
+
echo "[$(date)] β οΈ Failed to reopen issues (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
142
|
+
}
|
|
143
|
+
else
|
|
144
|
+
echo "[$(date)] β οΈ reopen-github-issues.js not found" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
145
|
+
fi
|
|
146
|
+
fi
|
|
122
147
|
;;
|
|
148
|
+
|
|
123
149
|
abandoned)
|
|
124
|
-
|
|
125
|
-
|
|
150
|
+
# ========================================================================
|
|
151
|
+
# CLOSE GitHub Issues on Abandon (NEW)
|
|
152
|
+
# ========================================================================
|
|
153
|
+
echo "[$(date)] ποΈ Status is abandoned - closing GitHub issues" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
154
|
+
|
|
155
|
+
if command -v node &> /dev/null; then
|
|
156
|
+
# Find close script
|
|
157
|
+
CLOSE_SCRIPT=""
|
|
158
|
+
if [ -f "$PROJECT_ROOT/plugins/specweave/lib/hooks/close-github-issues-abandoned.js" ]; then
|
|
159
|
+
CLOSE_SCRIPT="$PROJECT_ROOT/plugins/specweave/lib/hooks/close-github-issues-abandoned.js"
|
|
160
|
+
elif [ -f "$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/close-github-issues-abandoned.js" ]; then
|
|
161
|
+
CLOSE_SCRIPT="$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/close-github-issues-abandoned.js"
|
|
162
|
+
elif [ -f "$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/close-github-issues-abandoned.js" ]; then
|
|
163
|
+
CLOSE_SCRIPT="$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/close-github-issues-abandoned.js"
|
|
164
|
+
elif [ -n "${CLAUDE_PLUGIN_ROOT}" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/lib/hooks/close-github-issues-abandoned.js" ]; then
|
|
165
|
+
CLOSE_SCRIPT="${CLAUDE_PLUGIN_ROOT}/lib/hooks/close-github-issues-abandoned.js"
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
if [ -n "$CLOSE_SCRIPT" ]; then
|
|
169
|
+
# Load GITHUB_TOKEN from .env
|
|
170
|
+
if [ -f "$PROJECT_ROOT/.env" ]; then
|
|
171
|
+
GITHUB_TOKEN_FROM_ENV=$(grep -E '^GITHUB_TOKEN=' "$PROJECT_ROOT/.env" 2>/dev/null | head -1 | cut -d'=' -f2- | sed 's/^["'\'']//' | sed 's/["'\'']$//')
|
|
172
|
+
if [ -n "$GITHUB_TOKEN_FROM_ENV" ]; then
|
|
173
|
+
export GITHUB_TOKEN="$GITHUB_TOKEN_FROM_ENV"
|
|
174
|
+
fi
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
echo "ποΈ Closing GitHub issues for abandoned increment..."
|
|
178
|
+
(cd "$PROJECT_ROOT" && node "$CLOSE_SCRIPT" "$INCREMENT_ID" "${REASON:-Increment abandoned}") 2>&1 | tee -a "$DEBUG_LOG" || {
|
|
179
|
+
echo "[$(date)] β οΈ Failed to close issues (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
180
|
+
}
|
|
181
|
+
else
|
|
182
|
+
echo "[$(date)] β οΈ close-github-issues-abandoned.js not found" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
183
|
+
fi
|
|
184
|
+
fi
|
|
126
185
|
;;
|
|
127
|
-
esac
|
|
128
186
|
|
|
129
|
-
|
|
187
|
+
paused)
|
|
188
|
+
# Paused: Just post comment (no close/reopen)
|
|
189
|
+
echo "[$(date)] βΈοΈ Status is paused - posting comment only" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
190
|
+
;;
|
|
191
|
+
esac
|
|
130
192
|
|
|
131
|
-
|
|
193
|
+
# ============================================================================
|
|
194
|
+
# POST COMMENT TO MAIN ISSUE (always, for all status changes)
|
|
195
|
+
# ============================================================================
|
|
196
|
+
|
|
197
|
+
if [ -n "$GITHUB_ISSUE" ]; then
|
|
198
|
+
echo "[$(date)] π Posting status change to GitHub issue #$GITHUB_ISSUE" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
199
|
+
|
|
200
|
+
# Build comment based on status
|
|
201
|
+
EMOJI=""
|
|
202
|
+
TITLE=""
|
|
203
|
+
case "$NEW_STATUS" in
|
|
204
|
+
paused)
|
|
205
|
+
EMOJI="βΈοΈ"
|
|
206
|
+
TITLE="Increment Paused"
|
|
207
|
+
;;
|
|
208
|
+
resumed|active|in-progress)
|
|
209
|
+
EMOJI="βΆοΈ"
|
|
210
|
+
TITLE="Increment Resumed"
|
|
211
|
+
;;
|
|
212
|
+
abandoned)
|
|
213
|
+
EMOJI="ποΈ"
|
|
214
|
+
TITLE="Increment Abandoned"
|
|
215
|
+
;;
|
|
216
|
+
esac
|
|
217
|
+
|
|
218
|
+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
219
|
+
|
|
220
|
+
COMMENT="$EMOJI **$TITLE**
|
|
132
221
|
|
|
133
222
|
**Reason**: ${REASON:-Not specified}
|
|
134
223
|
|
|
@@ -137,15 +226,17 @@ COMMENT="$EMOJI **$TITLE**
|
|
|
137
226
|
---
|
|
138
227
|
π€ Auto-updated by SpecWeave"
|
|
139
228
|
|
|
140
|
-
# Post comment
|
|
141
|
-
echo "$COMMENT" | gh issue comment "$GITHUB_ISSUE" --body-file - 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
|
|
142
|
-
|
|
143
|
-
}
|
|
229
|
+
# Post comment
|
|
230
|
+
echo "$COMMENT" | gh issue comment "$GITHUB_ISSUE" --body-file - 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
|
|
231
|
+
echo "[$(date)] β οΈ Failed to post comment (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
232
|
+
}
|
|
144
233
|
|
|
145
|
-
echo "[$(date)] β
Status change synced to GitHub" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
234
|
+
echo "[$(date)] β
Status change synced to GitHub" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
235
|
+
else
|
|
236
|
+
echo "[$(date)] βΉοΈ No main GitHub issue linked" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
237
|
+
fi
|
|
146
238
|
|
|
147
239
|
# Update status line cache (status changed - may affect which increment is "current")
|
|
148
|
-
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
149
240
|
bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
|
|
150
241
|
|
|
151
242
|
# Return success (non-blocking)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# SpecWeave Session Start Reconcile Hook (NEW in v0.28.33)
|
|
4
|
+
#
|
|
5
|
+
# Runs on Claude Code session start to detect and fix GitHub status drift.
|
|
6
|
+
# This is a lightweight hook that runs in background to not block startup.
|
|
7
|
+
#
|
|
8
|
+
# Features:
|
|
9
|
+
# - Debounced: Only runs if >1 hour since last reconcile
|
|
10
|
+
# - Non-blocking: Runs in background, errors logged but don't crash
|
|
11
|
+
# - Optional: Controlled by config.sync.github.autoReconcileOnSessionStart
|
|
12
|
+
#
|
|
13
|
+
# Trigger: SessionStart hook event
|
|
14
|
+
|
|
15
|
+
set +e # Prevent crashes
|
|
16
|
+
|
|
17
|
+
# EMERGENCY KILL SWITCH
|
|
18
|
+
if [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]; then
|
|
19
|
+
exit 0
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Find project root
|
|
23
|
+
find_project_root() {
|
|
24
|
+
local dir="$PWD"
|
|
25
|
+
while [[ "$dir" != "/" ]]; do
|
|
26
|
+
if [[ -d "$dir/.specweave" ]]; then
|
|
27
|
+
echo "$dir"
|
|
28
|
+
return 0
|
|
29
|
+
fi
|
|
30
|
+
dir=$(dirname "$dir")
|
|
31
|
+
done
|
|
32
|
+
echo "$PWD"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
PROJECT_ROOT=$(find_project_root)
|
|
36
|
+
CONFIG_FILE="$PROJECT_ROOT/.specweave/config.json"
|
|
37
|
+
STATE_DIR="$PROJECT_ROOT/.specweave/state"
|
|
38
|
+
LAST_RECONCILE_FILE="$STATE_DIR/.last-reconcile-timestamp"
|
|
39
|
+
DEBUG_LOG="$PROJECT_ROOT/.specweave/logs/hooks-debug.log"
|
|
40
|
+
|
|
41
|
+
mkdir -p "$STATE_DIR" 2>/dev/null || true
|
|
42
|
+
mkdir -p "$(dirname "$DEBUG_LOG")" 2>/dev/null || true
|
|
43
|
+
|
|
44
|
+
# Consume stdin (required for hooks)
|
|
45
|
+
cat > /dev/null
|
|
46
|
+
|
|
47
|
+
echo "[$(date)] session-start-reconcile: Hook fired" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
48
|
+
|
|
49
|
+
# ============================================================================
|
|
50
|
+
# CHECK IF AUTO-RECONCILE IS ENABLED
|
|
51
|
+
# ============================================================================
|
|
52
|
+
|
|
53
|
+
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
54
|
+
echo "[$(date)] session-start-reconcile: No config.json - skipping" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
55
|
+
exit 0
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Check config (requires jq)
|
|
59
|
+
if ! command -v jq &> /dev/null; then
|
|
60
|
+
echo "[$(date)] session-start-reconcile: jq not found - skipping" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
61
|
+
exit 0
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Check if auto-reconcile is enabled
|
|
65
|
+
AUTO_RECONCILE=$(jq -r '.sync.github.autoReconcileOnSessionStart // false' "$CONFIG_FILE" 2>/dev/null)
|
|
66
|
+
|
|
67
|
+
if [[ "$AUTO_RECONCILE" != "true" ]]; then
|
|
68
|
+
echo "[$(date)] session-start-reconcile: Disabled in config - skipping" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
69
|
+
exit 0
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
echo "[$(date)] session-start-reconcile: Auto-reconcile enabled" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
73
|
+
|
|
74
|
+
# ============================================================================
|
|
75
|
+
# DEBOUNCE: Only run if >1 hour since last reconcile
|
|
76
|
+
# ============================================================================
|
|
77
|
+
|
|
78
|
+
DEBOUNCE_SECONDS=3600 # 1 hour
|
|
79
|
+
|
|
80
|
+
if [[ -f "$LAST_RECONCILE_FILE" ]]; then
|
|
81
|
+
LAST_TIMESTAMP=$(cat "$LAST_RECONCILE_FILE" 2>/dev/null || echo "0")
|
|
82
|
+
CURRENT_TIMESTAMP=$(date +%s)
|
|
83
|
+
ELAPSED=$((CURRENT_TIMESTAMP - LAST_TIMESTAMP))
|
|
84
|
+
|
|
85
|
+
if [[ $ELAPSED -lt $DEBOUNCE_SECONDS ]]; then
|
|
86
|
+
REMAINING=$((DEBOUNCE_SECONDS - ELAPSED))
|
|
87
|
+
echo "[$(date)] session-start-reconcile: Debounced - last run ${ELAPSED}s ago (wait ${REMAINING}s)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
88
|
+
exit 0
|
|
89
|
+
fi
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
echo "[$(date)] session-start-reconcile: Debounce passed - running reconcile" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
93
|
+
|
|
94
|
+
# Update timestamp BEFORE running (prevents concurrent runs)
|
|
95
|
+
date +%s > "$LAST_RECONCILE_FILE"
|
|
96
|
+
|
|
97
|
+
# ============================================================================
|
|
98
|
+
# RUN RECONCILE IN BACKGROUND (non-blocking)
|
|
99
|
+
# ============================================================================
|
|
100
|
+
|
|
101
|
+
if ! command -v node &> /dev/null; then
|
|
102
|
+
echo "[$(date)] session-start-reconcile: Node.js not found - skipping" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
103
|
+
exit 0
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# Find reconcile script
|
|
107
|
+
RECONCILE_SCRIPT=""
|
|
108
|
+
if [[ -f "$PROJECT_ROOT/plugins/specweave/lib/hooks/session-start-reconcile-worker.js" ]]; then
|
|
109
|
+
RECONCILE_SCRIPT="$PROJECT_ROOT/plugins/specweave/lib/hooks/session-start-reconcile-worker.js"
|
|
110
|
+
elif [[ -f "$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/session-start-reconcile-worker.js" ]]; then
|
|
111
|
+
RECONCILE_SCRIPT="$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/session-start-reconcile-worker.js"
|
|
112
|
+
elif [[ -f "$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/session-start-reconcile-worker.js" ]]; then
|
|
113
|
+
RECONCILE_SCRIPT="$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/session-start-reconcile-worker.js"
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
if [[ -z "$RECONCILE_SCRIPT" ]]; then
|
|
117
|
+
echo "[$(date)] session-start-reconcile: Worker script not found - skipping" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
118
|
+
exit 0
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
# Load GITHUB_TOKEN from .env
|
|
122
|
+
if [[ -f "$PROJECT_ROOT/.env" ]]; then
|
|
123
|
+
GITHUB_TOKEN_FROM_ENV=$(grep -E '^GITHUB_TOKEN=' "$PROJECT_ROOT/.env" 2>/dev/null | head -1 | cut -d'=' -f2- | sed 's/^["'\'']//' | sed 's/["'\'']$//')
|
|
124
|
+
if [[ -n "$GITHUB_TOKEN_FROM_ENV" ]]; then
|
|
125
|
+
export GITHUB_TOKEN="$GITHUB_TOKEN_FROM_ENV"
|
|
126
|
+
fi
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
# Run in background with nohup (fully detached)
|
|
130
|
+
# Output goes to debug log, doesn't block session start
|
|
131
|
+
echo "[$(date)] session-start-reconcile: Starting background reconcile" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
132
|
+
|
|
133
|
+
(
|
|
134
|
+
cd "$PROJECT_ROOT" && \
|
|
135
|
+
node "$RECONCILE_SCRIPT" >> "$DEBUG_LOG" 2>&1
|
|
136
|
+
) &
|
|
137
|
+
|
|
138
|
+
# Immediately exit (don't wait for background job)
|
|
139
|
+
exit 0
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { GitHubReconciler } from "../vendor/sync/github-reconciler.js";
|
|
3
|
+
import { consoleLogger } from "../vendor/utils/logger.js";
|
|
4
|
+
async function main() {
|
|
5
|
+
const incrementId = process.argv[2];
|
|
6
|
+
const reason = process.argv[3] || "Increment abandoned";
|
|
7
|
+
if (!incrementId) {
|
|
8
|
+
console.error("Usage: node close-github-issues-abandoned.js <increment-id> [reason]");
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
console.log("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
12
|
+
console.log(`\u{1F5D1}\uFE0F CLOSING GITHUB ISSUES (ABANDONED): ${incrementId}`);
|
|
13
|
+
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
14
|
+
const projectRoot = process.cwd();
|
|
15
|
+
const result = await GitHubReconciler.closeAbandonedIncrementIssues(
|
|
16
|
+
projectRoot,
|
|
17
|
+
incrementId,
|
|
18
|
+
reason,
|
|
19
|
+
consoleLogger
|
|
20
|
+
);
|
|
21
|
+
console.log("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
22
|
+
if (result.closed > 0) {
|
|
23
|
+
console.log(`\u2705 Closed ${result.closed} GitHub issue(s)`);
|
|
24
|
+
} else {
|
|
25
|
+
console.log("\u2139\uFE0F No issues needed closing (all already closed or none linked)");
|
|
26
|
+
}
|
|
27
|
+
if (result.errors.length > 0) {
|
|
28
|
+
console.log(`\u26A0\uFE0F ${result.errors.length} error(s):`);
|
|
29
|
+
result.errors.forEach((err) => console.log(` - ${err}`));
|
|
30
|
+
}
|
|
31
|
+
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
main().catch((error) => {
|
|
35
|
+
console.error("\u274C Fatal error:", error);
|
|
36
|
+
process.exit(0);
|
|
37
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Close GitHub Issues for Abandoned Increment (NEW in v0.28.33)
|
|
4
|
+
*
|
|
5
|
+
* Called by post-increment-status-change.sh when an increment is abandoned.
|
|
6
|
+
* Closes all GitHub issues (main + User Stories) with abandon comment.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node close-github-issues-abandoned.js <increment-id> [reason]
|
|
10
|
+
*
|
|
11
|
+
* Environment:
|
|
12
|
+
* GITHUB_TOKEN - Required for GitHub API access
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { GitHubReconciler } from '../vendor/sync/github-reconciler.js';
|
|
16
|
+
import { consoleLogger } from '../vendor/utils/logger.js';
|
|
17
|
+
|
|
18
|
+
async function main(): Promise<void> {
|
|
19
|
+
const incrementId = process.argv[2];
|
|
20
|
+
const reason = process.argv[3] || 'Increment abandoned';
|
|
21
|
+
|
|
22
|
+
if (!incrementId) {
|
|
23
|
+
console.error('Usage: node close-github-issues-abandoned.js <increment-id> [reason]');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log('\nβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ');
|
|
28
|
+
console.log(`ποΈ CLOSING GITHUB ISSUES (ABANDONED): ${incrementId}`);
|
|
29
|
+
console.log('βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ');
|
|
30
|
+
|
|
31
|
+
const projectRoot = process.cwd();
|
|
32
|
+
|
|
33
|
+
const result = await GitHubReconciler.closeAbandonedIncrementIssues(
|
|
34
|
+
projectRoot,
|
|
35
|
+
incrementId,
|
|
36
|
+
reason,
|
|
37
|
+
consoleLogger
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
console.log('\nβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ');
|
|
41
|
+
if (result.closed > 0) {
|
|
42
|
+
console.log(`β
Closed ${result.closed} GitHub issue(s)`);
|
|
43
|
+
} else {
|
|
44
|
+
console.log('βΉοΈ No issues needed closing (all already closed or none linked)');
|
|
45
|
+
}
|
|
46
|
+
if (result.errors.length > 0) {
|
|
47
|
+
console.log(`β οΈ ${result.errors.length} error(s):`);
|
|
48
|
+
result.errors.forEach(err => console.log(` - ${err}`));
|
|
49
|
+
}
|
|
50
|
+
console.log('βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n');
|
|
51
|
+
|
|
52
|
+
// Exit 0 to not block hook chain
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
main().catch((error) => {
|
|
57
|
+
console.error('β Fatal error:', error);
|
|
58
|
+
process.exit(0); // Non-blocking
|
|
59
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { GitHubReconciler } from "../vendor/sync/github-reconciler.js";
|
|
3
|
+
import { consoleLogger } from "../vendor/utils/logger.js";
|
|
4
|
+
async function main() {
|
|
5
|
+
const incrementId = process.argv[2];
|
|
6
|
+
const reason = process.argv[3] || "Increment resumed";
|
|
7
|
+
if (!incrementId) {
|
|
8
|
+
console.error("Usage: node reopen-github-issues.js <increment-id> [reason]");
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
console.log("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
12
|
+
console.log(`\u25B6\uFE0F REOPENING GITHUB ISSUES: ${incrementId}`);
|
|
13
|
+
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
14
|
+
const projectRoot = process.cwd();
|
|
15
|
+
const result = await GitHubReconciler.reopenIncrementIssues(
|
|
16
|
+
projectRoot,
|
|
17
|
+
incrementId,
|
|
18
|
+
reason,
|
|
19
|
+
consoleLogger
|
|
20
|
+
);
|
|
21
|
+
console.log("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
22
|
+
if (result.reopened > 0) {
|
|
23
|
+
console.log(`\u2705 Reopened ${result.reopened} GitHub issue(s)`);
|
|
24
|
+
} else {
|
|
25
|
+
console.log("\u2139\uFE0F No issues needed reopening (all already open or none linked)");
|
|
26
|
+
}
|
|
27
|
+
if (result.errors.length > 0) {
|
|
28
|
+
console.log(`\u26A0\uFE0F ${result.errors.length} error(s):`);
|
|
29
|
+
result.errors.forEach((err) => console.log(` - ${err}`));
|
|
30
|
+
}
|
|
31
|
+
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
main().catch((error) => {
|
|
35
|
+
console.error("\u274C Fatal error:", error);
|
|
36
|
+
process.exit(0);
|
|
37
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Reopen GitHub Issues Hook (NEW in v0.28.33)
|
|
4
|
+
*
|
|
5
|
+
* Called by post-increment-status-change.sh when an increment is resumed.
|
|
6
|
+
* Reopens all GitHub issues (main + User Stories) for the increment.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node reopen-github-issues.js <increment-id> [reason]
|
|
10
|
+
*
|
|
11
|
+
* Environment:
|
|
12
|
+
* GITHUB_TOKEN - Required for GitHub API access
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { GitHubReconciler } from '../vendor/sync/github-reconciler.js';
|
|
16
|
+
import { consoleLogger } from '../vendor/utils/logger.js';
|
|
17
|
+
|
|
18
|
+
async function main(): Promise<void> {
|
|
19
|
+
const incrementId = process.argv[2];
|
|
20
|
+
const reason = process.argv[3] || 'Increment resumed';
|
|
21
|
+
|
|
22
|
+
if (!incrementId) {
|
|
23
|
+
console.error('Usage: node reopen-github-issues.js <increment-id> [reason]');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log('\nβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ');
|
|
28
|
+
console.log(`βΆοΈ REOPENING GITHUB ISSUES: ${incrementId}`);
|
|
29
|
+
console.log('βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ');
|
|
30
|
+
|
|
31
|
+
const projectRoot = process.cwd();
|
|
32
|
+
|
|
33
|
+
const result = await GitHubReconciler.reopenIncrementIssues(
|
|
34
|
+
projectRoot,
|
|
35
|
+
incrementId,
|
|
36
|
+
reason,
|
|
37
|
+
consoleLogger
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
console.log('\nβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ');
|
|
41
|
+
if (result.reopened > 0) {
|
|
42
|
+
console.log(`β
Reopened ${result.reopened} GitHub issue(s)`);
|
|
43
|
+
} else {
|
|
44
|
+
console.log('βΉοΈ No issues needed reopening (all already open or none linked)');
|
|
45
|
+
}
|
|
46
|
+
if (result.errors.length > 0) {
|
|
47
|
+
console.log(`β οΈ ${result.errors.length} error(s):`);
|
|
48
|
+
result.errors.forEach(err => console.log(` - ${err}`));
|
|
49
|
+
}
|
|
50
|
+
console.log('βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n');
|
|
51
|
+
|
|
52
|
+
// Exit 0 to not block hook chain
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
main().catch((error) => {
|
|
57
|
+
console.error('β Fatal error:', error);
|
|
58
|
+
process.exit(0); // Non-blocking
|
|
59
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { GitHubReconciler } from "../vendor/sync/github-reconciler.js";
|
|
3
|
+
const silentLogger = {
|
|
4
|
+
log: () => {
|
|
5
|
+
},
|
|
6
|
+
// Suppress normal logs
|
|
7
|
+
error: (msg, ...args) => console.error(`[reconcile] ${msg}`, ...args)
|
|
8
|
+
};
|
|
9
|
+
async function main() {
|
|
10
|
+
console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] session-start-reconcile-worker: Starting...`);
|
|
11
|
+
const projectRoot = process.cwd();
|
|
12
|
+
try {
|
|
13
|
+
const reconciler = new GitHubReconciler({
|
|
14
|
+
projectRoot,
|
|
15
|
+
dryRun: false,
|
|
16
|
+
logger: silentLogger
|
|
17
|
+
});
|
|
18
|
+
const result = await reconciler.reconcile();
|
|
19
|
+
if (result.mismatches > 0) {
|
|
20
|
+
console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] session-start-reconcile-worker: Fixed ${result.closed} closed, ${result.reopened} reopened`);
|
|
21
|
+
} else {
|
|
22
|
+
console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] session-start-reconcile-worker: No drift detected (${result.scanned} increments)`);
|
|
23
|
+
}
|
|
24
|
+
if (result.errors.length > 0) {
|
|
25
|
+
console.error(`[${(/* @__PURE__ */ new Date()).toISOString()}] session-start-reconcile-worker: ${result.errors.length} error(s)`);
|
|
26
|
+
result.errors.forEach((err) => console.error(` - ${err}`));
|
|
27
|
+
}
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error(`[${(/* @__PURE__ */ new Date()).toISOString()}] session-start-reconcile-worker: Error - ${error.message}`);
|
|
30
|
+
}
|
|
31
|
+
console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] session-start-reconcile-worker: Complete`);
|
|
32
|
+
}
|
|
33
|
+
main().catch((error) => {
|
|
34
|
+
console.error(`[${(/* @__PURE__ */ new Date()).toISOString()}] session-start-reconcile-worker: Fatal error - ${error}`);
|
|
35
|
+
process.exit(0);
|
|
36
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Session Start Reconcile Worker (NEW in v0.28.33)
|
|
4
|
+
*
|
|
5
|
+
* Lightweight background worker that runs GitHub reconciliation on session start.
|
|
6
|
+
* Called by session-start-reconcile.sh in detached mode.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Silent mode: Minimal output, errors logged to debug log
|
|
10
|
+
* - Non-blocking: Parent shell exits immediately
|
|
11
|
+
* - Idempotent: Safe to run multiple times
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { GitHubReconciler } from '../vendor/sync/github-reconciler.js';
|
|
15
|
+
|
|
16
|
+
// Silent logger - only logs errors
|
|
17
|
+
const silentLogger = {
|
|
18
|
+
log: () => {}, // Suppress normal logs
|
|
19
|
+
error: (msg: string, ...args: any[]) => console.error(`[reconcile] ${msg}`, ...args),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
async function main(): Promise<void> {
|
|
23
|
+
console.log(`[${new Date().toISOString()}] session-start-reconcile-worker: Starting...`);
|
|
24
|
+
|
|
25
|
+
const projectRoot = process.cwd();
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const reconciler = new GitHubReconciler({
|
|
29
|
+
projectRoot,
|
|
30
|
+
dryRun: false,
|
|
31
|
+
logger: silentLogger as any,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const result = await reconciler.reconcile();
|
|
35
|
+
|
|
36
|
+
// Log summary
|
|
37
|
+
if (result.mismatches > 0) {
|
|
38
|
+
console.log(`[${new Date().toISOString()}] session-start-reconcile-worker: Fixed ${result.closed} closed, ${result.reopened} reopened`);
|
|
39
|
+
} else {
|
|
40
|
+
console.log(`[${new Date().toISOString()}] session-start-reconcile-worker: No drift detected (${result.scanned} increments)`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (result.errors.length > 0) {
|
|
44
|
+
console.error(`[${new Date().toISOString()}] session-start-reconcile-worker: ${result.errors.length} error(s)`);
|
|
45
|
+
result.errors.forEach(err => console.error(` - ${err}`));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
} catch (error: any) {
|
|
49
|
+
console.error(`[${new Date().toISOString()}] session-start-reconcile-worker: Error - ${error.message}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
console.log(`[${new Date().toISOString()}] session-start-reconcile-worker: Complete`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
main().catch((error) => {
|
|
56
|
+
console.error(`[${new Date().toISOString()}] session-start-reconcile-worker: Fatal error - ${error}`);
|
|
57
|
+
process.exit(0); // Non-blocking
|
|
58
|
+
});
|