specweave 1.0.550 → 1.0.552
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 +1 -1
- package/bin/specweave.js +23 -1
- package/dist/src/cli/commands/hook.d.ts +15 -0
- package/dist/src/cli/commands/hook.d.ts.map +1 -0
- package/dist/src/cli/commands/hook.js +61 -0
- package/dist/src/cli/commands/hook.js.map +1 -0
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +5 -0
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/refresh-plugins.d.ts.map +1 -1
- package/dist/src/cli/commands/refresh-plugins.js +11 -1
- package/dist/src/cli/commands/refresh-plugins.js.map +1 -1
- package/dist/src/cli/commands/sync-setup.d.ts.map +1 -1
- package/dist/src/cli/commands/sync-setup.js +7 -3
- package/dist/src/cli/commands/sync-setup.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.d.ts +9 -0
- package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.js +9 -3
- package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.js.map +1 -1
- package/dist/src/config/types.d.ts +2 -2
- package/dist/src/core/config/types.d.ts +18 -2
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.js.map +1 -1
- package/dist/src/core/hooks/handlers/hook-router.d.ts +19 -0
- package/dist/src/core/hooks/handlers/hook-router.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/hook-router.js +75 -0
- package/dist/src/core/hooks/handlers/hook-router.js.map +1 -0
- package/dist/src/core/hooks/handlers/index.d.ts +10 -0
- package/dist/src/core/hooks/handlers/index.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/index.js +9 -0
- package/dist/src/core/hooks/handlers/index.js.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.d.ts +11 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.js +73 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.js.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use.d.ts +11 -0
- package/dist/src/core/hooks/handlers/post-tool-use.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use.js +76 -0
- package/dist/src/core/hooks/handlers/post-tool-use.js.map +1 -0
- package/dist/src/core/hooks/handlers/pre-compact.d.ts +11 -0
- package/dist/src/core/hooks/handlers/pre-compact.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/pre-compact.js +77 -0
- package/dist/src/core/hooks/handlers/pre-compact.js.map +1 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.d.ts +11 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.js +318 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.js.map +1 -0
- package/dist/src/core/hooks/handlers/session-start.d.ts +9 -0
- package/dist/src/core/hooks/handlers/session-start.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/session-start.js +111 -0
- package/dist/src/core/hooks/handlers/session-start.js.map +1 -0
- package/dist/src/core/hooks/handlers/stop-auto.d.ts +16 -0
- package/dist/src/core/hooks/handlers/stop-auto.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/stop-auto.js +122 -0
- package/dist/src/core/hooks/handlers/stop-auto.js.map +1 -0
- package/dist/src/core/hooks/handlers/stop-reflect.d.ts +14 -0
- package/dist/src/core/hooks/handlers/stop-reflect.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/stop-reflect.js +43 -0
- package/dist/src/core/hooks/handlers/stop-reflect.js.map +1 -0
- package/dist/src/core/hooks/handlers/stop-sync.d.ts +15 -0
- package/dist/src/core/hooks/handlers/stop-sync.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/stop-sync.js +68 -0
- package/dist/src/core/hooks/handlers/stop-sync.js.map +1 -0
- package/dist/src/core/hooks/handlers/types.d.ts +63 -0
- package/dist/src/core/hooks/handlers/types.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/types.js +27 -0
- package/dist/src/core/hooks/handlers/types.js.map +1 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.d.ts +14 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.js +173 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.js.map +1 -0
- package/dist/src/core/hooks/handlers/utils.d.ts +25 -0
- package/dist/src/core/hooks/handlers/utils.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/utils.js +64 -0
- package/dist/src/core/hooks/handlers/utils.js.map +1 -0
- package/dist/src/core/increment/completion-validator.d.ts.map +1 -1
- package/dist/src/core/increment/completion-validator.js +32 -0
- package/dist/src/core/increment/completion-validator.js.map +1 -1
- package/dist/src/init/research/types.d.ts +1 -1
- package/dist/src/sync/sync-target-resolver.js.map +1 -1
- package/dist/src/utils/lock-manager.d.ts.map +1 -1
- package/dist/src/utils/lock-manager.js +5 -0
- package/dist/src/utils/lock-manager.js.map +1 -1
- package/dist/src/utils/plugin-copier.d.ts +10 -0
- package/dist/src/utils/plugin-copier.d.ts.map +1 -1
- package/dist/src/utils/plugin-copier.js +63 -35
- package/dist/src/utils/plugin-copier.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/agents/sw-closer.md +3 -2
- package/plugins/specweave/hooks/hooks.json +10 -10
- package/plugins/specweave/skills/code-reviewer/SKILL.md +180 -16
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-comments.md +83 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-silent-failures.md +19 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-spec-compliance.md +19 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-tests.md +101 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-types.md +20 -0
- package/plugins/specweave/skills/done/SKILL.md +56 -21
- package/plugins/specweave/skills/grill/SKILL.md +1 -1
- package/plugins/specweave/skills/team-lead/agents/reviewer-logic.md +19 -0
- package/plugins/specweave/skills/team-lead/agents/reviewer-performance.md +20 -0
- package/plugins/specweave/skills/team-lead/agents/reviewer-security.md +20 -0
- package/src/templates/CLAUDE.md.template +7 -4
- package/plugins/specweave/hooks/README.md +0 -493
- package/plugins/specweave/hooks/_archive/stop-auto-v4-legacy.sh +0 -1319
- package/plugins/specweave/hooks/lib/common-setup.sh +0 -144
- package/plugins/specweave/hooks/lib/hook-errors.sh +0 -414
- package/plugins/specweave/hooks/lib/migrate-increment-work.sh +0 -245
- package/plugins/specweave/hooks/lib/resolve-package.sh +0 -146
- package/plugins/specweave/hooks/lib/scheduler-startup.sh +0 -135
- package/plugins/specweave/hooks/lib/score-increment.sh +0 -87
- package/plugins/specweave/hooks/lib/sync-spec-content.sh +0 -193
- package/plugins/specweave/hooks/lib/update-active-increment.sh +0 -95
- package/plugins/specweave/hooks/lib/update-status-line.sh +0 -233
- package/plugins/specweave/hooks/lib/validate-spec-status.sh +0 -171
- package/plugins/specweave/hooks/llm-judge-validator.sh +0 -219
- package/plugins/specweave/hooks/log-decision.sh +0 -168
- package/plugins/specweave/hooks/pre-compact.sh +0 -64
- package/plugins/specweave/hooks/startup-health-check.sh +0 -64
- package/plugins/specweave/hooks/stop-auto-v5.sh +0 -276
- package/plugins/specweave/hooks/stop-reflect.sh +0 -336
- package/plugins/specweave/hooks/stop-sync.sh +0 -283
- package/plugins/specweave/hooks/tests/test-auto-context-integration.sh +0 -126
- package/plugins/specweave/hooks/tests/test-stop-auto-enriched.sh +0 -128
- package/plugins/specweave/hooks/universal/dispatcher.mjs +0 -336
- package/plugins/specweave/hooks/universal/fail-fast-wrapper.sh +0 -325
- package/plugins/specweave/hooks/universal/hook-wrapper.cmd +0 -26
- package/plugins/specweave/hooks/universal/hook-wrapper.sh +0 -69
- package/plugins/specweave/hooks/universal/run-hook.sh +0 -20
- package/plugins/specweave/hooks/universal/session-start.cmd +0 -16
- package/plugins/specweave/hooks/universal/session-start.ps1 +0 -16
- package/plugins/specweave/hooks/user-prompt-submit.sh +0 -2550
- package/plugins/specweave/hooks/v2/detectors/lifecycle-detector.sh +0 -87
- package/plugins/specweave/hooks/v2/detectors/us-completion-detector.sh +0 -186
- package/plugins/specweave/hooks/v2/dispatchers/post-tool-use-analytics.sh +0 -83
- package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +0 -447
- package/plugins/specweave/hooks/v2/dispatchers/pre-tool-use.sh +0 -104
- package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +0 -270
- package/plugins/specweave/hooks/v2/guards/completion-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/increment-duplicate-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/increment-existence-guard.sh +0 -240
- package/plugins/specweave/hooks/v2/guards/interview-enforcement-guard.sh +0 -171
- package/plugins/specweave/hooks/v2/guards/metadata-json-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/skill-chain-enforcement-guard.sh +0 -222
- package/plugins/specweave/hooks/v2/guards/spec-template-enforcement-guard.sh +0 -21
- package/plugins/specweave/hooks/v2/guards/spec-validation-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/status-completion-guard.sh +0 -84
- package/plugins/specweave/hooks/v2/guards/task-ac-sync-guard.sh +0 -475
- package/plugins/specweave/hooks/v2/guards/tdd-enforcement-guard.sh +0 -268
- package/plugins/specweave/hooks/v2/handlers/ac-sync-dispatcher.sh +0 -332
- package/plugins/specweave/hooks/v2/handlers/ac-validation-handler.sh +0 -50
- package/plugins/specweave/hooks/v2/handlers/github-sync-handler.sh +0 -347
- package/plugins/specweave/hooks/v2/handlers/living-docs-handler.sh +0 -83
- package/plugins/specweave/hooks/v2/handlers/living-specs-handler.sh +0 -268
- package/plugins/specweave/hooks/v2/handlers/project-bridge-handler.sh +0 -104
- package/plugins/specweave/hooks/v2/handlers/status-line-handler.sh +0 -165
- package/plugins/specweave/hooks/v2/handlers/status-update.sh +0 -61
- package/plugins/specweave/hooks/v2/handlers/universal-auto-create-dispatcher.sh +0 -270
- package/plugins/specweave/hooks/v2/integrations/ado-post-living-docs-update.sh +0 -367
- package/plugins/specweave/hooks/v2/integrations/ado-post-task.sh +0 -179
- package/plugins/specweave/hooks/v2/integrations/github-auto-create-handler.sh +0 -553
- package/plugins/specweave/hooks/v2/integrations/github-post-task.sh +0 -345
- package/plugins/specweave/hooks/v2/integrations/jira-post-task.sh +0 -180
- package/plugins/specweave/hooks/v2/lib/check-provider-enabled.sh +0 -52
- package/plugins/specweave/hooks/v2/queue/enqueue.sh +0 -81
- package/plugins/specweave/hooks/v2/session-end.sh +0 -139
- package/plugins/specweave/hooks/validate-skill-activations.sh +0 -227
|
@@ -1,553 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# github-auto-create-handler.sh — Auto-creates GitHub issues from spec.md user stories
|
|
3
|
-
#
|
|
4
|
-
# Triggered by post-tool-use.sh when spec.md is created/updated.
|
|
5
|
-
# Pure bash using gh CLI — no TypeScript compilation required.
|
|
6
|
-
#
|
|
7
|
-
# Features:
|
|
8
|
-
# - Idempotent: checks existing issues before creating
|
|
9
|
-
# - Updates metadata.json with external links
|
|
10
|
-
# - Debounce (30s window to batch rapid spec edits)
|
|
11
|
-
# - Circuit breaker (3 consecutive failures → auto-disable)
|
|
12
|
-
# - Non-blocking (all errors → exit 0)
|
|
13
|
-
#
|
|
14
|
-
# Usage: github-auto-create-handler.sh <INCREMENT_ID>
|
|
15
|
-
# Called by: safe_run_background in post-tool-use.sh
|
|
16
|
-
|
|
17
|
-
set +e # Never crash Claude Code
|
|
18
|
-
|
|
19
|
-
[[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]] && exit 0
|
|
20
|
-
|
|
21
|
-
# ============================================================================
|
|
22
|
-
# ARGS & PROJECT ROOT
|
|
23
|
-
# ============================================================================
|
|
24
|
-
|
|
25
|
-
INC_ID="$1"
|
|
26
|
-
[[ -z "$INC_ID" ]] && exit 0
|
|
27
|
-
|
|
28
|
-
PROJECT_ROOT="$PWD"
|
|
29
|
-
while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -f "$PROJECT_ROOT/.specweave/config.json" ]]; do
|
|
30
|
-
PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
|
|
31
|
-
done
|
|
32
|
-
[[ ! -f "$PROJECT_ROOT/.specweave/config.json" ]] && exit 0
|
|
33
|
-
|
|
34
|
-
SPEC_PATH="$PROJECT_ROOT/.specweave/increments/$INC_ID/spec.md"
|
|
35
|
-
META_PATH="$PROJECT_ROOT/.specweave/increments/$INC_ID/metadata.json"
|
|
36
|
-
CONFIG_PATH="$PROJECT_ROOT/.specweave/config.json"
|
|
37
|
-
STATE_DIR="$PROJECT_ROOT/.specweave/state"
|
|
38
|
-
LOGS_DIR="$PROJECT_ROOT/.specweave/logs"
|
|
39
|
-
LOG_FILE="$LOGS_DIR/github-auto-create.log"
|
|
40
|
-
|
|
41
|
-
mkdir -p "$STATE_DIR" "$LOGS_DIR" 2>/dev/null || true
|
|
42
|
-
|
|
43
|
-
log() {
|
|
44
|
-
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [github-auto-create] $1" >> "$LOG_FILE" 2>/dev/null || true
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
# ============================================================================
|
|
48
|
-
# PRECONDITIONS
|
|
49
|
-
# ============================================================================
|
|
50
|
-
|
|
51
|
-
[[ ! -f "$SPEC_PATH" ]] && exit 0
|
|
52
|
-
[[ ! -f "$CONFIG_PATH" ]] && exit 0
|
|
53
|
-
|
|
54
|
-
# Template guard: skip if spec.md still contains placeholder markers
|
|
55
|
-
grep -q '\[Story Title\]' "$SPEC_PATH" && { log "Skipping: spec.md still contains [Story Title] template markers"; exit 0; }
|
|
56
|
-
|
|
57
|
-
command -v gh &>/dev/null || { log "gh CLI not found"; exit 0; }
|
|
58
|
-
command -v jq &>/dev/null || { log "jq not found"; exit 0; }
|
|
59
|
-
|
|
60
|
-
# Check GitHub sync enabled (shared 3-method detection)
|
|
61
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
62
|
-
SHARED_LIB_PATHS=(
|
|
63
|
-
"$SCRIPT_DIR/../../specweave/hooks/v2/lib/check-provider-enabled.sh"
|
|
64
|
-
"$SCRIPT_DIR/../../../specweave/hooks/v2/lib/check-provider-enabled.sh"
|
|
65
|
-
)
|
|
66
|
-
for _lib in "${SHARED_LIB_PATHS[@]}"; do
|
|
67
|
-
[[ -f "$_lib" ]] && { source "$_lib"; break; }
|
|
68
|
-
done
|
|
69
|
-
|
|
70
|
-
if type check_provider_enabled &>/dev/null; then
|
|
71
|
-
check_provider_enabled "$CONFIG_PATH" "github" || { log "GitHub sync not enabled"; exit 0; }
|
|
72
|
-
else
|
|
73
|
-
GH_ENABLED=$(jq -r '.sync.github.enabled // false' "$CONFIG_PATH" 2>/dev/null)
|
|
74
|
-
[[ "$GH_ENABLED" != "true" ]] && { log "GitHub sync not enabled"; exit 0; }
|
|
75
|
-
fi
|
|
76
|
-
|
|
77
|
-
# Check auto-create enabled
|
|
78
|
-
# Two flags control auto-creation behavior:
|
|
79
|
-
# - sync.autoSync (boolean): Global auto-sync toggle. When true, enables ALL auto-sync
|
|
80
|
-
# operations including issue creation, progress sync, and status updates.
|
|
81
|
-
# - hooks.post_increment_planning.auto_create_github_issue (boolean): Fine-grained toggle
|
|
82
|
-
# for ONLY the auto-create-issue behavior in post-increment-planning hooks.
|
|
83
|
-
#
|
|
84
|
-
# Precedence: auto_create_github_issue (specific) > autoSync (global)
|
|
85
|
-
# If BOTH are set, auto_create_github_issue takes precedence for issue creation.
|
|
86
|
-
# If ONLY autoSync is true, issue creation is enabled (as part of the global auto-sync).
|
|
87
|
-
AUTO_SYNC=$(jq -r '.sync.autoSync // false' "$CONFIG_PATH" 2>/dev/null)
|
|
88
|
-
AUTO_CREATE=$(jq -r '.hooks.post_increment_planning.auto_create_github_issue // false' "$CONFIG_PATH" 2>/dev/null)
|
|
89
|
-
|
|
90
|
-
# Warn if only one flag is set (likely misconfiguration)
|
|
91
|
-
if [[ "$AUTO_SYNC" == "true" ]] && [[ "$AUTO_CREATE" == "false" ]]; then
|
|
92
|
-
log "Warning: sync.autoSync=true but auto_create_github_issue=false. Issue creation enabled via autoSync. Set auto_create_github_issue=true to be explicit."
|
|
93
|
-
elif [[ "$AUTO_SYNC" == "false" ]] && [[ "$AUTO_CREATE" == "true" ]]; then
|
|
94
|
-
log "Warning: auto_create_github_issue=true but sync.autoSync=false. Only issue creation is enabled; other sync operations are disabled."
|
|
95
|
-
fi
|
|
96
|
-
|
|
97
|
-
if [[ "$AUTO_SYNC" != "true" ]] && [[ "$AUTO_CREATE" != "true" ]]; then
|
|
98
|
-
log "Auto-sync disabled (autoSync=$AUTO_SYNC, auto_create=$AUTO_CREATE)"
|
|
99
|
-
exit 0
|
|
100
|
-
fi
|
|
101
|
-
|
|
102
|
-
# Log which flag triggered activation
|
|
103
|
-
if [[ "$AUTO_CREATE" == "true" ]]; then
|
|
104
|
-
log "Auto-create enabled via hooks.post_increment_planning.auto_create_github_issue"
|
|
105
|
-
else
|
|
106
|
-
log "Auto-create enabled via sync.autoSync (global toggle)"
|
|
107
|
-
fi
|
|
108
|
-
|
|
109
|
-
# Get owner/repo
|
|
110
|
-
OWNER=$(jq -r '.sync.github.owner // ""' "$CONFIG_PATH" 2>/dev/null)
|
|
111
|
-
REPO=$(jq -r '.sync.github.repo // ""' "$CONFIG_PATH" 2>/dev/null)
|
|
112
|
-
if [[ -z "$OWNER" ]] || [[ -z "$REPO" ]]; then
|
|
113
|
-
log "GitHub owner/repo not configured"
|
|
114
|
-
exit 0
|
|
115
|
-
fi
|
|
116
|
-
REPO_SLUG="$OWNER/$REPO"
|
|
117
|
-
|
|
118
|
-
# Get project name from config for labels
|
|
119
|
-
PROJECT_NAME=$(jq -r '.project.name // "specweave"' "$CONFIG_PATH" 2>/dev/null)
|
|
120
|
-
PROJECT_LABEL="project:${PROJECT_NAME}"
|
|
121
|
-
|
|
122
|
-
# ============================================================================
|
|
123
|
-
# CIRCUIT BREAKER
|
|
124
|
-
# ============================================================================
|
|
125
|
-
|
|
126
|
-
CB_FILE="$STATE_DIR/.hook-circuit-breaker-github-auto-create"
|
|
127
|
-
if [[ -f "$CB_FILE" ]]; then
|
|
128
|
-
CB_COUNT=$(cat "$CB_FILE" 2>/dev/null || echo 0)
|
|
129
|
-
if (( CB_COUNT >= 3 )); then
|
|
130
|
-
log "Circuit breaker OPEN ($CB_COUNT failures). Skipping. Delete $CB_FILE to reset."
|
|
131
|
-
exit 0
|
|
132
|
-
fi
|
|
133
|
-
fi
|
|
134
|
-
|
|
135
|
-
# ============================================================================
|
|
136
|
-
# DEBOUNCE (30s window — spec.md may be written multiple times during planning)
|
|
137
|
-
# ============================================================================
|
|
138
|
-
|
|
139
|
-
if [[ "${SPECWEAVE_SKIP_DEBOUNCE:-0}" != "1" ]]; then
|
|
140
|
-
DEBOUNCE_FILE="$STATE_DIR/.github-auto-create-pending-$INC_ID"
|
|
141
|
-
if [[ -f "$DEBOUNCE_FILE" ]]; then
|
|
142
|
-
# POSIX-portable: use perl for mtime (works on macOS and Linux)
|
|
143
|
-
FILE_MTIME=$(perl -e 'print((stat($ARGV[0]))[9])' "$DEBOUNCE_FILE" 2>/dev/null || echo 0)
|
|
144
|
-
SIGNAL_AGE=$(($(date +%s) - FILE_MTIME))
|
|
145
|
-
if (( SIGNAL_AGE < 30 )); then
|
|
146
|
-
log "Debounce: signal age ${SIGNAL_AGE}s < 30s. Deferring."
|
|
147
|
-
exit 0
|
|
148
|
-
fi
|
|
149
|
-
fi
|
|
150
|
-
echo "$(date +%s)" > "$DEBOUNCE_FILE" 2>/dev/null || true
|
|
151
|
-
sleep 30
|
|
152
|
-
# Check if a newer invocation took over
|
|
153
|
-
if [[ -f "$DEBOUNCE_FILE" ]]; then
|
|
154
|
-
CURRENT_SIGNAL=$(cat "$DEBOUNCE_FILE" 2>/dev/null || echo 0)
|
|
155
|
-
NOW=$(date +%s)
|
|
156
|
-
SIGNAL_AGE=$((NOW - CURRENT_SIGNAL))
|
|
157
|
-
if (( SIGNAL_AGE > 32 )); then
|
|
158
|
-
log "Debounce: newer invocation detected. Exiting."
|
|
159
|
-
exit 0
|
|
160
|
-
fi
|
|
161
|
-
fi
|
|
162
|
-
rm -f "$DEBOUNCE_FILE" 2>/dev/null || true
|
|
163
|
-
fi
|
|
164
|
-
|
|
165
|
-
# ============================================================================
|
|
166
|
-
# CHECK IF ISSUES ALREADY EXIST
|
|
167
|
-
# ============================================================================
|
|
168
|
-
|
|
169
|
-
if [[ -f "$META_PATH" ]]; then
|
|
170
|
-
# Check BOTH new (externalLinks) and old (github.issues[]) formats for existing issues
|
|
171
|
-
ISSUE_COUNT=$(jq '
|
|
172
|
-
[
|
|
173
|
-
(.externalLinks.github.issues // {} | to_entries[] | select(.value.issueNumber != null)),
|
|
174
|
-
(.github.issues // [] | .[] | select(.number != null))
|
|
175
|
-
] | length
|
|
176
|
-
' "$META_PATH" 2>/dev/null || echo 0)
|
|
177
|
-
if (( ISSUE_COUNT > 0 )); then
|
|
178
|
-
log "Issues already exist for $INC_ID ($ISSUE_COUNT). Skipping."
|
|
179
|
-
exit 0
|
|
180
|
-
fi
|
|
181
|
-
fi
|
|
182
|
-
|
|
183
|
-
# ============================================================================
|
|
184
|
-
# PARSE USER STORIES FROM SPEC.MD
|
|
185
|
-
# ============================================================================
|
|
186
|
-
|
|
187
|
-
# Get default priority from metadata.json
|
|
188
|
-
DEFAULT_PRIORITY="P2"
|
|
189
|
-
if [[ -f "$META_PATH" ]]; then
|
|
190
|
-
META_PRIORITY=$(jq -r '.priority // "P2"' "$META_PATH" 2>/dev/null)
|
|
191
|
-
[[ -n "$META_PRIORITY" ]] && DEFAULT_PRIORITY="$META_PRIORITY"
|
|
192
|
-
fi
|
|
193
|
-
|
|
194
|
-
log "Parsing user stories from $SPEC_PATH..."
|
|
195
|
-
|
|
196
|
-
# Arrays to hold parsed user stories
|
|
197
|
-
declare -a US_IDS US_TITLES US_PRIORITIES
|
|
198
|
-
US_COUNT=0
|
|
199
|
-
|
|
200
|
-
while IFS= read -r line; do
|
|
201
|
-
# Match: ### US-001: Title Here (P1) — with priority
|
|
202
|
-
if [[ "$line" =~ ^###[[:space:]]+(US-[0-9]+):[[:space:]]+(.+)[[:space:]]+\((P[0-9])\)[[:space:]]*$ ]]; then
|
|
203
|
-
US_IDS+=("${BASH_REMATCH[1]}")
|
|
204
|
-
US_TITLES+=("${BASH_REMATCH[2]}")
|
|
205
|
-
US_PRIORITIES+=("${BASH_REMATCH[3]}")
|
|
206
|
-
((US_COUNT++))
|
|
207
|
-
# Match: ### US-001: Title Here — without priority
|
|
208
|
-
elif [[ "$line" =~ ^###[[:space:]]+(US-[0-9]+):[[:space:]]+(.+)[[:space:]]*$ ]]; then
|
|
209
|
-
US_IDS+=("${BASH_REMATCH[1]}")
|
|
210
|
-
US_TITLES+=("${BASH_REMATCH[2]}")
|
|
211
|
-
# Try to extract priority from metadata.json or default to P2
|
|
212
|
-
US_PRIORITIES+=("${DEFAULT_PRIORITY:-P2}")
|
|
213
|
-
((US_COUNT++))
|
|
214
|
-
fi
|
|
215
|
-
done < "$SPEC_PATH"
|
|
216
|
-
|
|
217
|
-
if (( US_COUNT == 0 )); then
|
|
218
|
-
log "No user stories found in spec.md. Skipping."
|
|
219
|
-
exit 0
|
|
220
|
-
fi
|
|
221
|
-
|
|
222
|
-
log "Found $US_COUNT user stories. Creating GitHub issues..."
|
|
223
|
-
|
|
224
|
-
# Get increment title from frontmatter
|
|
225
|
-
INC_TITLE=$(grep -m1 '^title:' "$SPEC_PATH" | sed 's/^title:[[:space:]]*//' | sed 's/^"//' | sed 's/"$//')
|
|
226
|
-
|
|
227
|
-
# ============================================================================
|
|
228
|
-
# DERIVE FEATURE ID (ADR-0187: FS-{number} from increment number)
|
|
229
|
-
# ============================================================================
|
|
230
|
-
# Extract numeric prefix from increment ID: "0192-github-sync-v2-multi-repo" → "0192"
|
|
231
|
-
INC_NUM=$(echo "$INC_ID" | grep -oE '^[0-9]+')
|
|
232
|
-
# Strip leading zeros: "0192" → "192", "0001" → "1"
|
|
233
|
-
FEATURE_NUM=$(echo "$INC_NUM" | sed 's/^0*//')
|
|
234
|
-
[[ -z "$FEATURE_NUM" ]] && FEATURE_NUM="0"
|
|
235
|
-
FEATURE_ID="FS-${FEATURE_NUM}"
|
|
236
|
-
log "Derived Feature ID: $FEATURE_ID (from increment $INC_ID)"
|
|
237
|
-
|
|
238
|
-
# ============================================================================
|
|
239
|
-
# CREATE MILESTONE (one per increment, idempotent)
|
|
240
|
-
# ============================================================================
|
|
241
|
-
|
|
242
|
-
# Milestone uses Feature ID format: "FS-192: Short Title"
|
|
243
|
-
MILESTONE_TITLE="${FEATURE_ID}"
|
|
244
|
-
MILESTONE_NUM=""
|
|
245
|
-
|
|
246
|
-
# Check existing milestones (list all, including closed)
|
|
247
|
-
EXISTING_MS=$(gh api "repos/$REPO_SLUG/milestones?state=all&per_page=100" --jq ".[] | select(.title == \"$MILESTONE_TITLE\") | .number" 2>/dev/null || echo "")
|
|
248
|
-
if [[ -n "$EXISTING_MS" ]]; then
|
|
249
|
-
MILESTONE_NUM="$EXISTING_MS"
|
|
250
|
-
log "Using existing milestone #$MILESTONE_NUM"
|
|
251
|
-
else
|
|
252
|
-
MS_RESULT=$(gh api "repos/$REPO_SLUG/milestones" \
|
|
253
|
-
-f "title=$MILESTONE_TITLE" \
|
|
254
|
-
-f "description=$INC_TITLE" \
|
|
255
|
-
--method POST --jq '.number' 2>/dev/null || echo "")
|
|
256
|
-
if [[ -n "$MS_RESULT" ]] && [[ "$MS_RESULT" =~ ^[0-9]+$ ]]; then
|
|
257
|
-
MILESTONE_NUM="$MS_RESULT"
|
|
258
|
-
log "Created milestone #$MILESTONE_NUM: $MILESTONE_TITLE"
|
|
259
|
-
else
|
|
260
|
-
log "Warning: Failed to create milestone (continuing without it)"
|
|
261
|
-
fi
|
|
262
|
-
fi
|
|
263
|
-
|
|
264
|
-
# ============================================================================
|
|
265
|
-
# ENSURE REQUIRED LABELS EXIST (idempotent)
|
|
266
|
-
# ============================================================================
|
|
267
|
-
|
|
268
|
-
ensure_label() {
|
|
269
|
-
local label_name="$1"
|
|
270
|
-
local label_color="${2:-ededed}"
|
|
271
|
-
local label_desc="${3:-}"
|
|
272
|
-
gh label create "$label_name" --repo "$REPO_SLUG" --color "$label_color" \
|
|
273
|
-
--description "$label_desc" --force 2>/dev/null || true
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
# Core labels
|
|
277
|
-
ensure_label "user-story" "0075ca" "User story from SpecWeave spec"
|
|
278
|
-
ensure_label "specweave" "6f42c1" "Managed by SpecWeave"
|
|
279
|
-
ensure_label "$PROJECT_LABEL" "c5def5" "$PROJECT_NAME project"
|
|
280
|
-
ensure_label "status:active" "0e8a16" "Active work item"
|
|
281
|
-
ensure_label "status:completed" "6f42c1" "Completed work item"
|
|
282
|
-
ensure_label "status:not_started" "fbca04" "User story not started"
|
|
283
|
-
|
|
284
|
-
# Priority labels (collect unique priorities from parsed user stories)
|
|
285
|
-
for i in $(seq 0 $((US_COUNT - 1))); do
|
|
286
|
-
PRIO_LABEL="priority:$(echo "${US_PRIORITIES[$i]}" | tr '[:upper:]' '[:lower:]')"
|
|
287
|
-
case "${US_PRIORITIES[$i]}" in
|
|
288
|
-
P1) ensure_label "$PRIO_LABEL" "b60205" "Critical priority" ;;
|
|
289
|
-
P2) ensure_label "$PRIO_LABEL" "fbca04" "Medium priority" ;;
|
|
290
|
-
P3) ensure_label "$PRIO_LABEL" "0e8a16" "Low priority" ;;
|
|
291
|
-
*) ensure_label "$PRIO_LABEL" "ededed" "Priority label" ;;
|
|
292
|
-
esac
|
|
293
|
-
done
|
|
294
|
-
|
|
295
|
-
log "Labels ensured"
|
|
296
|
-
|
|
297
|
-
# ============================================================================
|
|
298
|
-
# EXTRACT ACCEPTANCE CRITERIA FOR EACH USER STORY
|
|
299
|
-
# ============================================================================
|
|
300
|
-
|
|
301
|
-
# Function to extract ACs for a given US from spec.md
|
|
302
|
-
# Handles both formats:
|
|
303
|
-
# - [ ] **AC-US1-01**: Description (bold AC ID)
|
|
304
|
-
# - [ ] AC-US1-01: Description (plain AC ID)
|
|
305
|
-
extract_acs_for_us() {
|
|
306
|
-
local us_id="$1"
|
|
307
|
-
local in_section=false
|
|
308
|
-
local in_ac=false
|
|
309
|
-
local acs=""
|
|
310
|
-
|
|
311
|
-
while IFS= read -r line; do
|
|
312
|
-
# Start of our US section
|
|
313
|
-
if [[ "$line" =~ ^###[[:space:]]+"$us_id": ]] || [[ "$line" =~ ^###[[:space:]]+"$us_id"[[:space:]] ]]; then
|
|
314
|
-
in_section=true
|
|
315
|
-
continue
|
|
316
|
-
fi
|
|
317
|
-
# Start of next US section or next ## section — stop
|
|
318
|
-
if $in_section && { [[ "$line" =~ ^###[[:space:]]+US- ]] || [[ "$line" =~ ^##[[:space:]] ]]; }; then
|
|
319
|
-
break
|
|
320
|
-
fi
|
|
321
|
-
# Inside our section, look for AC lines
|
|
322
|
-
if $in_section; then
|
|
323
|
-
# Acceptance Criteria heading: both bold (**AC**) and heading (#### AC) formats
|
|
324
|
-
if [[ "$line" =~ ^\*\*Acceptance[[:space:]]Criteria ]] || [[ "$line" =~ ^#+[[:space:]]+Acceptance[[:space:]]Criteria ]]; then
|
|
325
|
-
in_ac=true
|
|
326
|
-
continue
|
|
327
|
-
fi
|
|
328
|
-
# Format 1: Bold AC ID — - [ ] **AC-US1-01**: Description
|
|
329
|
-
if $in_ac && [[ "$line" =~ ^-[[:space:]]+\[(.)\][[:space:]]+\*\*([^*]+)\*\*:[[:space:]]*(.*) ]]; then
|
|
330
|
-
local checked="${BASH_REMATCH[1]}"
|
|
331
|
-
local ac_id="${BASH_REMATCH[2]}"
|
|
332
|
-
local ac_desc="${BASH_REMATCH[3]}"
|
|
333
|
-
if [[ "$checked" == "x" ]]; then
|
|
334
|
-
acs+="- [x] **${ac_id}**: ${ac_desc}"$'\n'
|
|
335
|
-
else
|
|
336
|
-
acs+="- [ ] **${ac_id}**: ${ac_desc}"$'\n'
|
|
337
|
-
fi
|
|
338
|
-
continue
|
|
339
|
-
fi
|
|
340
|
-
# Format 2: Plain AC ID — - [ ] AC-US1-01: Description
|
|
341
|
-
if $in_ac && [[ "$line" =~ ^-[[:space:]]+\[(.)\][[:space:]]+(AC-[^:]+):[[:space:]]*(.*) ]]; then
|
|
342
|
-
local checked="${BASH_REMATCH[1]}"
|
|
343
|
-
local ac_id="${BASH_REMATCH[2]}"
|
|
344
|
-
local ac_desc="${BASH_REMATCH[3]}"
|
|
345
|
-
if [[ "$checked" == "x" ]]; then
|
|
346
|
-
acs+="- [x] **${ac_id}**: ${ac_desc}"$'\n'
|
|
347
|
-
else
|
|
348
|
-
acs+="- [ ] **${ac_id}**: ${ac_desc}"$'\n'
|
|
349
|
-
fi
|
|
350
|
-
continue
|
|
351
|
-
fi
|
|
352
|
-
# Stop ACs at section divider or next heading
|
|
353
|
-
if $in_ac && [[ "$line" =~ ^---$ ]]; then
|
|
354
|
-
break
|
|
355
|
-
fi
|
|
356
|
-
fi
|
|
357
|
-
done < "$SPEC_PATH"
|
|
358
|
-
|
|
359
|
-
echo "$acs"
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
# Function to extract description for a given US
|
|
363
|
-
# Collects ALL text between the US heading and the Acceptance Criteria / next section
|
|
364
|
-
extract_desc_for_us() {
|
|
365
|
-
local us_id="$1"
|
|
366
|
-
local in_section=false
|
|
367
|
-
local desc=""
|
|
368
|
-
|
|
369
|
-
while IFS= read -r line; do
|
|
370
|
-
if [[ "$line" =~ ^###[[:space:]]+"$us_id": ]]; then
|
|
371
|
-
in_section=true
|
|
372
|
-
continue
|
|
373
|
-
fi
|
|
374
|
-
if $in_section; then
|
|
375
|
-
# Stop at next US heading, next ## section, or Acceptance Criteria
|
|
376
|
-
# Match ## but NOT ### or ####: ^##[[:space:]] catches "## Heading"
|
|
377
|
-
# Match ### US- for next user story: ^###[[:space:]]+US-
|
|
378
|
-
if [[ "$line" =~ ^###[[:space:]]+US- ]] || [[ "$line" =~ ^##[[:space:]] ]]; then
|
|
379
|
-
break
|
|
380
|
-
fi
|
|
381
|
-
# Acceptance Criteria heading: both bold (**AC**) and heading (#### AC) formats
|
|
382
|
-
if [[ "$line" =~ ^\*\*Acceptance[[:space:]]Criteria ]] || [[ "$line" =~ ^#+[[:space:]]+Acceptance[[:space:]]Criteria ]]; then
|
|
383
|
-
break
|
|
384
|
-
fi
|
|
385
|
-
# Collect non-empty lines as description
|
|
386
|
-
if [[ -n "$line" ]]; then
|
|
387
|
-
desc+="$line"$'\n'
|
|
388
|
-
fi
|
|
389
|
-
fi
|
|
390
|
-
done < "$SPEC_PATH"
|
|
391
|
-
|
|
392
|
-
echo "$desc"
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
# ============================================================================
|
|
396
|
-
# CREATE ISSUES
|
|
397
|
-
# ============================================================================
|
|
398
|
-
|
|
399
|
-
CREATED_JSON="{}"
|
|
400
|
-
ERRORS=0
|
|
401
|
-
|
|
402
|
-
for i in $(seq 0 $((US_COUNT - 1))); do
|
|
403
|
-
US_ID="${US_IDS[$i]}"
|
|
404
|
-
US_TITLE="${US_TITLES[$i]}"
|
|
405
|
-
US_PRIORITY="${US_PRIORITIES[$i]}"
|
|
406
|
-
|
|
407
|
-
ISSUE_TITLE="[${FEATURE_ID}][${US_ID}] ${US_TITLE}"
|
|
408
|
-
|
|
409
|
-
# Check if issue already exists (by title prefix search — match both old and new format)
|
|
410
|
-
EXISTING=$(gh issue list --repo "$REPO_SLUG" --search "[${FEATURE_ID}][${US_ID}] in:title" --json number,url --limit 1 2>/dev/null || echo "[]")
|
|
411
|
-
EXISTING_NUM=$(echo "$EXISTING" | jq -r '.[0].number // empty' 2>/dev/null)
|
|
412
|
-
|
|
413
|
-
if [[ -n "$EXISTING_NUM" ]]; then
|
|
414
|
-
EXISTING_URL=$(echo "$EXISTING" | jq -r '.[0].url // empty' 2>/dev/null)
|
|
415
|
-
log "Issue already exists for $US_ID: #$EXISTING_NUM"
|
|
416
|
-
CREATED_JSON=$(echo "$CREATED_JSON" | jq --arg us "$US_ID" --arg num "$EXISTING_NUM" --arg url "$EXISTING_URL" \
|
|
417
|
-
'. + {($us): {"issueNumber": ($num | tonumber), "issueUrl": $url, "status": "existing"}}' 2>/dev/null)
|
|
418
|
-
continue
|
|
419
|
-
fi
|
|
420
|
-
|
|
421
|
-
# Build issue body
|
|
422
|
-
US_DESC=$(extract_desc_for_us "$US_ID")
|
|
423
|
-
# Ensure blank line after description for proper markdown paragraph separation
|
|
424
|
-
[[ -n "$US_DESC" ]] && US_DESC="${US_DESC}"$'\n'
|
|
425
|
-
US_ACS=$(extract_acs_for_us "$US_ID")
|
|
426
|
-
|
|
427
|
-
ISSUE_BODY="## Description
|
|
428
|
-
|
|
429
|
-
${US_DESC}
|
|
430
|
-
**Priority**: ${US_PRIORITY}
|
|
431
|
-
|
|
432
|
-
## Acceptance Criteria
|
|
433
|
-
|
|
434
|
-
<!-- specweave:ac-start -->
|
|
435
|
-
${US_ACS}<!-- specweave:ac-end -->
|
|
436
|
-
|
|
437
|
-
---
|
|
438
|
-
**Feature**: ${FEATURE_ID} | **Increment**: \`${INC_ID}\`
|
|
439
|
-
<!-- specweave:sync feature=${FEATURE_ID} increment=${INC_ID} us=${US_ID} -->"
|
|
440
|
-
|
|
441
|
-
# Create the issue
|
|
442
|
-
CREATE_ARGS=(
|
|
443
|
-
"issue" "create"
|
|
444
|
-
"--repo" "$REPO_SLUG"
|
|
445
|
-
"--title" "$ISSUE_TITLE"
|
|
446
|
-
"--body" "$ISSUE_BODY"
|
|
447
|
-
"--label" "user-story"
|
|
448
|
-
"--label" "specweave"
|
|
449
|
-
"--label" "$PROJECT_LABEL"
|
|
450
|
-
"--label" "status:active"
|
|
451
|
-
"--label" "priority:$(echo "$US_PRIORITY" | tr '[:upper:]' '[:lower:]')"
|
|
452
|
-
)
|
|
453
|
-
|
|
454
|
-
# Add milestone if available
|
|
455
|
-
if [[ -n "$MILESTONE_NUM" ]]; then
|
|
456
|
-
CREATE_ARGS+=("--milestone" "$MILESTONE_TITLE")
|
|
457
|
-
fi
|
|
458
|
-
|
|
459
|
-
RESULT=$(gh "${CREATE_ARGS[@]}" 2>&1)
|
|
460
|
-
CREATE_EXIT=$?
|
|
461
|
-
|
|
462
|
-
if [[ $CREATE_EXIT -ne 0 ]]; then
|
|
463
|
-
log "ERROR creating issue for $US_ID: $RESULT"
|
|
464
|
-
((ERRORS++))
|
|
465
|
-
|
|
466
|
-
# Update circuit breaker
|
|
467
|
-
CB_VAL=$(cat "$CB_FILE" 2>/dev/null || echo 0)
|
|
468
|
-
echo "$((CB_VAL + 1))" > "$CB_FILE" 2>/dev/null || true
|
|
469
|
-
continue
|
|
470
|
-
fi
|
|
471
|
-
|
|
472
|
-
# Parse issue URL from result (gh outputs the URL on success)
|
|
473
|
-
ISSUE_URL=$(echo "$RESULT" | grep -oE 'https://github.com/[^ ]+' | head -1)
|
|
474
|
-
ISSUE_NUM=$(echo "$ISSUE_URL" | grep -oE '[0-9]+$')
|
|
475
|
-
|
|
476
|
-
if [[ -n "$ISSUE_NUM" ]]; then
|
|
477
|
-
log "Created issue #$ISSUE_NUM for $US_ID: $ISSUE_URL"
|
|
478
|
-
CREATED_JSON=$(echo "$CREATED_JSON" | jq --arg us "$US_ID" --arg num "$ISSUE_NUM" --arg url "$ISSUE_URL" \
|
|
479
|
-
'. + {($us): {"issueNumber": ($num | tonumber), "issueUrl": $url, "status": "created"}}' 2>/dev/null)
|
|
480
|
-
fi
|
|
481
|
-
done
|
|
482
|
-
|
|
483
|
-
# ============================================================================
|
|
484
|
-
# UPDATE METADATA.JSON WITH EXTERNAL LINKS
|
|
485
|
-
# ============================================================================
|
|
486
|
-
|
|
487
|
-
if [[ -f "$META_PATH" ]]; then
|
|
488
|
-
UPDATED_META=$(jq --argjson issues "$CREATED_JSON" --arg ms "${MILESTONE_NUM:-}" \
|
|
489
|
-
'.externalLinks.github = {
|
|
490
|
-
"issues": $issues,
|
|
491
|
-
"milestone": (if $ms != "" then ($ms | tonumber) else null end),
|
|
492
|
-
"syncedAt": (now | strftime("%Y-%m-%dT%H:%M:%SZ"))
|
|
493
|
-
}' "$META_PATH" 2>/dev/null)
|
|
494
|
-
|
|
495
|
-
if [[ -n "$UPDATED_META" ]]; then
|
|
496
|
-
echo "$UPDATED_META" > "$META_PATH" 2>/dev/null
|
|
497
|
-
log "Updated metadata.json with GitHub issue links (externalLinks format)"
|
|
498
|
-
|
|
499
|
-
# v1.0.240 FIX: Also write OLD format (metadata.github.issues[]) for reconciler compatibility
|
|
500
|
-
# The reconciler and closure flows read metadata.github.issues[], not externalLinks
|
|
501
|
-
OLD_FORMAT_JSON=$(echo "$CREATED_JSON" | jq '[to_entries[] | select(.value.issueNumber != null) | {
|
|
502
|
-
userStory: .key,
|
|
503
|
-
number: .value.issueNumber,
|
|
504
|
-
url: .value.issueUrl,
|
|
505
|
-
createdAt: (now | strftime("%Y-%m-%dT%H:%M:%SZ"))
|
|
506
|
-
}]' 2>/dev/null)
|
|
507
|
-
|
|
508
|
-
if [[ -n "$OLD_FORMAT_JSON" ]] && [[ "$OLD_FORMAT_JSON" != "[]" ]]; then
|
|
509
|
-
UPDATED_META_V2=$(jq --argjson old_issues "$OLD_FORMAT_JSON" '
|
|
510
|
-
if .github == null then .github = {} else . end |
|
|
511
|
-
.github.issues = ((.github.issues // []) + $old_issues | unique_by(.userStory)) |
|
|
512
|
-
.github.lastSync = (now | strftime("%Y-%m-%dT%H:%M:%SZ"))
|
|
513
|
-
' "$META_PATH" 2>/dev/null)
|
|
514
|
-
|
|
515
|
-
if [[ -n "$UPDATED_META_V2" ]]; then
|
|
516
|
-
echo "$UPDATED_META_V2" > "$META_PATH" 2>/dev/null
|
|
517
|
-
log "Also backfilled old-format github.issues[] for reconciler"
|
|
518
|
-
fi
|
|
519
|
-
fi
|
|
520
|
-
fi
|
|
521
|
-
fi
|
|
522
|
-
|
|
523
|
-
# ============================================================================
|
|
524
|
-
# UPDATE SYNC METADATA
|
|
525
|
-
# ============================================================================
|
|
526
|
-
|
|
527
|
-
SYNC_META="$PROJECT_ROOT/.specweave/sync-metadata.json"
|
|
528
|
-
if [[ -f "$SYNC_META" ]]; then
|
|
529
|
-
jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
530
|
-
'.github.lastSync = $ts | .github.lastSyncResult = "success"' \
|
|
531
|
-
"$SYNC_META" > "${SYNC_META}.tmp" 2>/dev/null && \
|
|
532
|
-
mv "${SYNC_META}.tmp" "$SYNC_META" 2>/dev/null
|
|
533
|
-
fi
|
|
534
|
-
|
|
535
|
-
# Reset circuit breaker on success
|
|
536
|
-
if (( ERRORS == 0 )); then
|
|
537
|
-
echo "0" > "$CB_FILE" 2>/dev/null || true
|
|
538
|
-
fi
|
|
539
|
-
|
|
540
|
-
CREATED_COUNT=$(echo "$CREATED_JSON" | jq 'to_entries | map(select(.value.status == "created")) | length' 2>/dev/null || echo 0)
|
|
541
|
-
EXISTING_COUNT=$(echo "$CREATED_JSON" | jq 'to_entries | map(select(.value.status == "existing")) | length' 2>/dev/null || echo 0)
|
|
542
|
-
|
|
543
|
-
log "Done: $CREATED_COUNT created, $EXISTING_COUNT existing, $ERRORS errors"
|
|
544
|
-
|
|
545
|
-
# Output summary for user visibility (will appear as hook output)
|
|
546
|
-
if (( CREATED_COUNT > 0 )); then
|
|
547
|
-
echo ""
|
|
548
|
-
echo " [GitHub Sync] Auto-created $CREATED_COUNT issue(s) for increment $INC_ID"
|
|
549
|
-
echo " Milestone: $MILESTONE_TITLE"
|
|
550
|
-
echo ""
|
|
551
|
-
fi
|
|
552
|
-
|
|
553
|
-
exit 0
|