specweave 1.0.235 → 1.0.239
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/README.md +89 -193
- package/dist/plugins/specweave-github/lib/github-ac-comment-poster.d.ts +37 -0
- package/dist/plugins/specweave-github/lib/github-ac-comment-poster.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-ac-comment-poster.js +176 -0
- package/dist/plugins/specweave-github/lib/github-ac-comment-poster.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-batch-sync.d.ts +36 -0
- package/dist/plugins/specweave-github/lib/github-batch-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-batch-sync.js +115 -0
- package/dist/plugins/specweave-github/lib/github-batch-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-board-resolver-v2.d.ts +37 -0
- package/dist/plugins/specweave-github/lib/github-board-resolver-v2.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-board-resolver-v2.js +56 -0
- package/dist/plugins/specweave-github/lib/github-board-resolver-v2.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-conflict-resolver.d.ts +68 -0
- package/dist/plugins/specweave-github/lib/github-conflict-resolver.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-conflict-resolver.js +102 -0
- package/dist/plugins/specweave-github/lib/github-conflict-resolver.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-cross-repo-sync.d.ts +64 -0
- package/dist/plugins/specweave-github/lib/github-cross-repo-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-cross-repo-sync.js +162 -0
- package/dist/plugins/specweave-github/lib/github-cross-repo-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-field-sync.d.ts +50 -0
- package/dist/plugins/specweave-github/lib/github-field-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-field-sync.js +107 -0
- package/dist/plugins/specweave-github/lib/github-field-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-graphql-client.d.ts +53 -0
- package/dist/plugins/specweave-github/lib/github-graphql-client.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-graphql-client.js +138 -0
- package/dist/plugins/specweave-github/lib/github-graphql-client.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-issue-body-generator.d.ts +40 -0
- package/dist/plugins/specweave-github/lib/github-issue-body-generator.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-issue-body-generator.js +50 -0
- package/dist/plugins/specweave-github/lib/github-issue-body-generator.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-issue-body-parser.d.ts +30 -0
- package/dist/plugins/specweave-github/lib/github-issue-body-parser.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-issue-body-parser.js +75 -0
- package/dist/plugins/specweave-github/lib/github-issue-body-parser.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-pull-sync.d.ts +94 -0
- package/dist/plugins/specweave-github/lib/github-pull-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-pull-sync.js +232 -0
- package/dist/plugins/specweave-github/lib/github-pull-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-push-sync.d.ts +50 -0
- package/dist/plugins/specweave-github/lib/github-push-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-push-sync.js +114 -0
- package/dist/plugins/specweave-github/lib/github-push-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-rate-limiter.d.ts +53 -0
- package/dist/plugins/specweave-github/lib/github-rate-limiter.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-rate-limiter.js +109 -0
- package/dist/plugins/specweave-github/lib/github-rate-limiter.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-spec-frontmatter-updater.d.ts +21 -0
- package/dist/plugins/specweave-github/lib/github-spec-frontmatter-updater.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-spec-frontmatter-updater.js +161 -0
- package/dist/plugins/specweave-github/lib/github-spec-frontmatter-updater.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-sync-orchestrator.d.ts +46 -0
- package/dist/plugins/specweave-github/lib/github-sync-orchestrator.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-sync-orchestrator.js +99 -0
- package/dist/plugins/specweave-github/lib/github-sync-orchestrator.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-us-auto-closer.d.ts +43 -0
- package/dist/plugins/specweave-github/lib/github-us-auto-closer.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-us-auto-closer.js +153 -0
- package/dist/plugins/specweave-github/lib/github-us-auto-closer.js.map +1 -0
- package/dist/plugins/specweave-github/lib/index.d.ts +1 -4
- package/dist/plugins/specweave-github/lib/index.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/index.js +1 -4
- package/dist/plugins/specweave-github/lib/index.js.map +1 -1
- package/dist/plugins/specweave-testing/lib/playwright-ci-defaults.d.ts +7 -0
- package/dist/plugins/specweave-testing/lib/playwright-ci-defaults.d.ts.map +1 -0
- package/dist/plugins/specweave-testing/lib/playwright-ci-defaults.js +15 -0
- package/dist/plugins/specweave-testing/lib/playwright-ci-defaults.js.map +1 -0
- package/dist/plugins/specweave-testing/lib/playwright-cli-detector.d.ts +10 -0
- package/dist/plugins/specweave-testing/lib/playwright-cli-detector.d.ts.map +1 -0
- package/dist/plugins/specweave-testing/lib/playwright-cli-detector.js +36 -0
- package/dist/plugins/specweave-testing/lib/playwright-cli-detector.js.map +1 -0
- package/dist/plugins/specweave-testing/lib/playwright-cli-runner.d.ts +25 -0
- package/dist/plugins/specweave-testing/lib/playwright-cli-runner.d.ts.map +1 -0
- package/dist/plugins/specweave-testing/lib/playwright-cli-runner.js +57 -0
- package/dist/plugins/specweave-testing/lib/playwright-cli-runner.js.map +1 -0
- package/dist/plugins/specweave-testing/lib/playwright-routing.d.ts +7 -0
- package/dist/plugins/specweave-testing/lib/playwright-routing.d.ts.map +1 -0
- package/dist/plugins/specweave-testing/lib/playwright-routing.js +17 -0
- package/dist/plugins/specweave-testing/lib/playwright-routing.js.map +1 -0
- package/dist/src/cli/commands/auto.d.ts.map +1 -1
- package/dist/src/cli/commands/auto.js +1 -2
- package/dist/src/cli/commands/auto.js.map +1 -1
- package/dist/src/cli/commands/cancel-auto.js +1 -2
- package/dist/src/cli/commands/cancel-auto.js.map +1 -1
- package/dist/src/cli/commands/living-docs.js +2 -2
- package/dist/src/cli/commands/living-docs.js.map +1 -1
- package/dist/src/cli/commands/update.d.ts.map +1 -1
- package/dist/src/cli/commands/update.js +1 -2
- package/dist/src/cli/commands/update.js.map +1 -1
- package/dist/src/core/config/types.d.ts +8 -0
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.js +3 -0
- package/dist/src/core/config/types.js.map +1 -1
- package/dist/src/core/types/sync-profile.d.ts +72 -0
- package/dist/src/core/types/sync-profile.d.ts.map +1 -1
- package/dist/src/core/types/sync-profile.js +6 -0
- package/dist/src/core/types/sync-profile.js.map +1 -1
- package/package.json +2 -2
- package/plugins/specweave/hooks/hooks.json +2 -2
- package/plugins/specweave/hooks/startup-health-check.sh +1 -1
- package/plugins/specweave/hooks/stop-auto-v5.sh +166 -0
- package/plugins/specweave/hooks/user-prompt-submit.sh +10 -0
- package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +21 -1
- package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +1 -1
- package/plugins/specweave/skills/auto/SKILL.md +71 -251
- package/plugins/specweave/skills/team-build/SKILL.md +370 -0
- package/plugins/specweave/skills/team-merge/SKILL.md +123 -0
- package/plugins/specweave/skills/team-orchestrate/SKILL.md +800 -0
- package/plugins/specweave/skills/team-status/SKILL.md +89 -0
- package/plugins/specweave-github/MULTI-PROJECT-SYNC-ARCHITECTURE.md +94 -8
- package/plugins/specweave-github/commands/sync.md +17 -3
- package/plugins/specweave-github/hooks/github-ac-sync-handler.sh +255 -0
- package/plugins/specweave-github/hooks/github-auto-create-handler.sh +455 -0
- package/plugins/specweave-github/lib/github-ac-comment-poster.js +150 -0
- package/plugins/specweave-github/lib/github-ac-comment-poster.ts +245 -0
- package/plugins/specweave-github/lib/github-batch-sync.js +93 -0
- package/plugins/specweave-github/lib/github-batch-sync.ts +152 -0
- package/plugins/specweave-github/lib/github-board-resolver-v2.js +47 -0
- package/plugins/specweave-github/lib/github-board-resolver-v2.ts +73 -0
- package/plugins/specweave-github/lib/github-conflict-resolver.js +90 -0
- package/plugins/specweave-github/lib/github-conflict-resolver.ts +154 -0
- package/plugins/specweave-github/lib/github-cross-repo-sync.js +168 -0
- package/plugins/specweave-github/lib/github-cross-repo-sync.ts +252 -0
- package/plugins/specweave-github/lib/github-field-sync.js +116 -0
- package/plugins/specweave-github/lib/github-field-sync.ts +165 -0
- package/plugins/specweave-github/lib/github-graphql-client.js +129 -0
- package/plugins/specweave-github/lib/github-graphql-client.ts +181 -0
- package/plugins/specweave-github/lib/github-issue-body-generator.js +30 -0
- package/plugins/specweave-github/lib/github-issue-body-generator.ts +76 -0
- package/plugins/specweave-github/lib/github-issue-body-parser.js +55 -0
- package/plugins/specweave-github/lib/github-issue-body-parser.ts +92 -0
- package/plugins/specweave-github/lib/github-pull-sync.js +185 -0
- package/plugins/specweave-github/lib/github-pull-sync.ts +343 -0
- package/plugins/specweave-github/lib/github-push-sync.js +119 -0
- package/plugins/specweave-github/lib/github-push-sync.ts +174 -0
- package/plugins/specweave-github/lib/github-rate-limiter.js +96 -0
- package/plugins/specweave-github/lib/github-rate-limiter.ts +143 -0
- package/plugins/specweave-github/lib/github-spec-frontmatter-updater.js +117 -0
- package/plugins/specweave-github/lib/github-spec-frontmatter-updater.ts +180 -0
- package/plugins/specweave-github/lib/github-sync-orchestrator.js +84 -0
- package/plugins/specweave-github/lib/github-sync-orchestrator.ts +156 -0
- package/plugins/specweave-github/lib/github-us-auto-closer.js +134 -0
- package/plugins/specweave-github/lib/github-us-auto-closer.ts +226 -0
- package/plugins/specweave-github/lib/index.js +1 -7
- package/plugins/specweave-github/lib/index.ts +1 -4
- package/plugins/specweave-github/skills/github-sync/SKILL.md +76 -4
- package/plugins/specweave-testing/commands/e2e-setup.md +18 -0
- package/plugins/specweave-testing/commands/ui-automate.md +2 -0
- package/plugins/specweave-testing/commands/ui-inspect.md +8 -0
- package/plugins/specweave-testing/lib/playwright-ci-defaults.d.ts +6 -0
- package/plugins/specweave-testing/lib/playwright-ci-defaults.js +14 -0
- package/plugins/specweave-testing/lib/playwright-ci-defaults.ts +24 -0
- package/plugins/specweave-testing/lib/playwright-cli-detector.js +33 -0
- package/plugins/specweave-testing/lib/playwright-cli-detector.ts +48 -0
- package/plugins/specweave-testing/lib/playwright-cli-runner.js +58 -0
- package/plugins/specweave-testing/lib/playwright-cli-runner.ts +80 -0
- package/plugins/specweave-testing/lib/playwright-routing.js +16 -0
- package/plugins/specweave-testing/lib/playwright-routing.ts +38 -0
- package/plugins/specweave-testing/skills/e2e-testing/SKILL.md +38 -0
- package/src/templates/CLAUDE.md.template +7 -0
- package/src/templates/config.json.template +9 -1
- package/dist/plugins/specweave-github/lib/subtask-sync.d.ts +0 -51
- package/dist/plugins/specweave-github/lib/subtask-sync.d.ts.map +0 -1
- package/dist/plugins/specweave-github/lib/subtask-sync.js +0 -147
- package/dist/plugins/specweave-github/lib/subtask-sync.js.map +0 -1
- package/dist/plugins/specweave-github/lib/task-parser.d.ts +0 -37
- package/dist/plugins/specweave-github/lib/task-parser.d.ts.map +0 -1
- package/dist/plugins/specweave-github/lib/task-parser.js +0 -211
- package/dist/plugins/specweave-github/lib/task-parser.js.map +0 -1
- package/dist/plugins/specweave-github/lib/task-sync.d.ts +0 -56
- package/dist/plugins/specweave-github/lib/task-sync.d.ts.map +0 -1
- package/dist/plugins/specweave-github/lib/task-sync.js +0 -375
- package/dist/plugins/specweave-github/lib/task-sync.js.map +0 -1
- package/plugins/specweave/hooks/validate-completion-conditions.sh +0 -474
- package/plugins/specweave-github/lib/subtask-sync.d.ts +0 -51
- package/plugins/specweave-github/lib/subtask-sync.d.ts.map +0 -1
- package/plugins/specweave-github/lib/subtask-sync.js +0 -154
- package/plugins/specweave-github/lib/subtask-sync.js.map +0 -1
- package/plugins/specweave-github/lib/subtask-sync.ts +0 -225
- package/plugins/specweave-github/lib/task-parser.d.js +0 -0
- package/plugins/specweave-github/lib/task-parser.d.ts +0 -37
- package/plugins/specweave-github/lib/task-parser.d.ts.map +0 -1
- package/plugins/specweave-github/lib/task-parser.js +0 -195
- package/plugins/specweave-github/lib/task-parser.js.map +0 -1
- package/plugins/specweave-github/lib/task-parser.ts +0 -246
- package/plugins/specweave-github/lib/task-sync.d.js +0 -0
- package/plugins/specweave-github/lib/task-sync.d.ts +0 -51
- package/plugins/specweave-github/lib/task-sync.d.ts.map +0 -1
- package/plugins/specweave-github/lib/task-sync.js +0 -415
- package/plugins/specweave-github/lib/task-sync.js.map +0 -1
- package/plugins/specweave-github/lib/task-sync.ts +0 -451
- package/plugins/specweave-github/skills/github-issue-tracker/SKILL.md +0 -496
- /package/plugins/specweave/hooks/{stop-auto.sh → _archive/stop-auto-v4-legacy.sh} +0 -0
- /package/plugins/{specweave-github/lib/subtask-sync.d.js → specweave-testing/lib/playwright-ci-defaults.d.js} +0 -0
|
@@ -0,0 +1,455 @@
|
|
|
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 (10s 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" != "/" ]] && [[ ! -d "$PROJECT_ROOT/.specweave" ]]; do
|
|
30
|
+
PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
|
|
31
|
+
done
|
|
32
|
+
[[ ! -d "$PROJECT_ROOT/.specweave" ]] && 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
|
+
command -v gh &>/dev/null || { log "gh CLI not found"; exit 0; }
|
|
55
|
+
command -v jq &>/dev/null || { log "jq not found"; exit 0; }
|
|
56
|
+
|
|
57
|
+
# Check GitHub sync enabled
|
|
58
|
+
GH_ENABLED=$(jq -r '.sync.github.enabled // false' "$CONFIG_PATH" 2>/dev/null)
|
|
59
|
+
[[ "$GH_ENABLED" != "true" ]] && { log "GitHub sync not enabled"; exit 0; }
|
|
60
|
+
|
|
61
|
+
# Check auto-create enabled (either autoSync OR auto_create_github_issue)
|
|
62
|
+
AUTO_SYNC=$(jq -r '.sync.autoSync // false' "$CONFIG_PATH" 2>/dev/null)
|
|
63
|
+
AUTO_CREATE=$(jq -r '.hooks.post_increment_planning.auto_create_github_issue // false' "$CONFIG_PATH" 2>/dev/null)
|
|
64
|
+
if [[ "$AUTO_SYNC" != "true" ]] && [[ "$AUTO_CREATE" != "true" ]]; then
|
|
65
|
+
log "Auto-sync disabled (autoSync=$AUTO_SYNC, auto_create=$AUTO_CREATE)"
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Get owner/repo
|
|
70
|
+
OWNER=$(jq -r '.sync.github.owner // ""' "$CONFIG_PATH" 2>/dev/null)
|
|
71
|
+
REPO=$(jq -r '.sync.github.repo // ""' "$CONFIG_PATH" 2>/dev/null)
|
|
72
|
+
if [[ -z "$OWNER" ]] || [[ -z "$REPO" ]]; then
|
|
73
|
+
log "GitHub owner/repo not configured"
|
|
74
|
+
exit 0
|
|
75
|
+
fi
|
|
76
|
+
REPO_SLUG="$OWNER/$REPO"
|
|
77
|
+
|
|
78
|
+
# Get project name from config for labels
|
|
79
|
+
PROJECT_NAME=$(jq -r '.project.name // "specweave"' "$CONFIG_PATH" 2>/dev/null)
|
|
80
|
+
PROJECT_LABEL="project:${PROJECT_NAME}"
|
|
81
|
+
|
|
82
|
+
# ============================================================================
|
|
83
|
+
# CIRCUIT BREAKER
|
|
84
|
+
# ============================================================================
|
|
85
|
+
|
|
86
|
+
CB_FILE="$STATE_DIR/.hook-circuit-breaker-github-auto-create"
|
|
87
|
+
if [[ -f "$CB_FILE" ]]; then
|
|
88
|
+
CB_COUNT=$(cat "$CB_FILE" 2>/dev/null || echo 0)
|
|
89
|
+
if (( CB_COUNT >= 3 )); then
|
|
90
|
+
log "Circuit breaker OPEN ($CB_COUNT failures). Skipping. Delete $CB_FILE to reset."
|
|
91
|
+
exit 0
|
|
92
|
+
fi
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# ============================================================================
|
|
96
|
+
# DEBOUNCE (10s window — spec.md may be written multiple times during planning)
|
|
97
|
+
# ============================================================================
|
|
98
|
+
|
|
99
|
+
if [[ "${SPECWEAVE_SKIP_DEBOUNCE:-0}" != "1" ]]; then
|
|
100
|
+
DEBOUNCE_FILE="$STATE_DIR/.github-auto-create-pending-$INC_ID"
|
|
101
|
+
if [[ -f "$DEBOUNCE_FILE" ]]; then
|
|
102
|
+
SIGNAL_AGE=$(($(date +%s) - $(stat -f "%m" "$DEBOUNCE_FILE" 2>/dev/null || echo 0)))
|
|
103
|
+
if (( SIGNAL_AGE < 10 )); then
|
|
104
|
+
log "Debounce: signal age ${SIGNAL_AGE}s < 10s. Deferring."
|
|
105
|
+
exit 0
|
|
106
|
+
fi
|
|
107
|
+
fi
|
|
108
|
+
echo "$(date +%s)" > "$DEBOUNCE_FILE" 2>/dev/null || true
|
|
109
|
+
sleep 10
|
|
110
|
+
# Check if a newer invocation took over
|
|
111
|
+
if [[ -f "$DEBOUNCE_FILE" ]]; then
|
|
112
|
+
CURRENT_SIGNAL=$(cat "$DEBOUNCE_FILE" 2>/dev/null || echo 0)
|
|
113
|
+
NOW=$(date +%s)
|
|
114
|
+
SIGNAL_AGE=$((NOW - CURRENT_SIGNAL))
|
|
115
|
+
if (( SIGNAL_AGE > 12 )); then
|
|
116
|
+
log "Debounce: newer invocation detected. Exiting."
|
|
117
|
+
exit 0
|
|
118
|
+
fi
|
|
119
|
+
fi
|
|
120
|
+
rm -f "$DEBOUNCE_FILE" 2>/dev/null || true
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
# ============================================================================
|
|
124
|
+
# CHECK IF ISSUES ALREADY EXIST
|
|
125
|
+
# ============================================================================
|
|
126
|
+
|
|
127
|
+
if [[ -f "$META_PATH" ]]; then
|
|
128
|
+
# Check if there are actual issues with issueNumber values (not just empty {})
|
|
129
|
+
ISSUE_COUNT=$(jq '[.externalLinks.github.issues // {} | to_entries[] | select(.value.issueNumber != null)] | length' "$META_PATH" 2>/dev/null || echo 0)
|
|
130
|
+
if (( ISSUE_COUNT > 0 )); then
|
|
131
|
+
log "Issues already exist for $INC_ID ($ISSUE_COUNT). Skipping."
|
|
132
|
+
exit 0
|
|
133
|
+
fi
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# ============================================================================
|
|
137
|
+
# PARSE USER STORIES FROM SPEC.MD
|
|
138
|
+
# ============================================================================
|
|
139
|
+
|
|
140
|
+
# Get default priority from metadata.json
|
|
141
|
+
DEFAULT_PRIORITY="P2"
|
|
142
|
+
if [[ -f "$META_PATH" ]]; then
|
|
143
|
+
META_PRIORITY=$(jq -r '.priority // "P2"' "$META_PATH" 2>/dev/null)
|
|
144
|
+
[[ -n "$META_PRIORITY" ]] && DEFAULT_PRIORITY="$META_PRIORITY"
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
log "Parsing user stories from $SPEC_PATH..."
|
|
148
|
+
|
|
149
|
+
# Arrays to hold parsed user stories
|
|
150
|
+
declare -a US_IDS US_TITLES US_PRIORITIES
|
|
151
|
+
US_COUNT=0
|
|
152
|
+
|
|
153
|
+
while IFS= read -r line; do
|
|
154
|
+
# Match: ### US-001: Title Here (P1) — with priority
|
|
155
|
+
if [[ "$line" =~ ^###[[:space:]]+(US-[0-9]+):[[:space:]]+(.+)[[:space:]]+\((P[0-9])\)[[:space:]]*$ ]]; then
|
|
156
|
+
US_IDS+=("${BASH_REMATCH[1]}")
|
|
157
|
+
US_TITLES+=("${BASH_REMATCH[2]}")
|
|
158
|
+
US_PRIORITIES+=("${BASH_REMATCH[3]}")
|
|
159
|
+
((US_COUNT++))
|
|
160
|
+
# Match: ### US-001: Title Here — without priority
|
|
161
|
+
elif [[ "$line" =~ ^###[[:space:]]+(US-[0-9]+):[[:space:]]+(.+)[[:space:]]*$ ]]; then
|
|
162
|
+
US_IDS+=("${BASH_REMATCH[1]}")
|
|
163
|
+
US_TITLES+=("${BASH_REMATCH[2]}")
|
|
164
|
+
# Try to extract priority from metadata.json or default to P2
|
|
165
|
+
US_PRIORITIES+=("${DEFAULT_PRIORITY:-P2}")
|
|
166
|
+
((US_COUNT++))
|
|
167
|
+
fi
|
|
168
|
+
done < "$SPEC_PATH"
|
|
169
|
+
|
|
170
|
+
if (( US_COUNT == 0 )); then
|
|
171
|
+
log "No user stories found in spec.md. Skipping."
|
|
172
|
+
exit 0
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
log "Found $US_COUNT user stories. Creating GitHub issues..."
|
|
176
|
+
|
|
177
|
+
# Get increment title from frontmatter
|
|
178
|
+
INC_TITLE=$(grep -m1 '^title:' "$SPEC_PATH" | sed 's/^title:[[:space:]]*//' | sed 's/^"//' | sed 's/"$//')
|
|
179
|
+
|
|
180
|
+
# ============================================================================
|
|
181
|
+
# DERIVE FEATURE ID (ADR-0187: FS-{number} from increment number)
|
|
182
|
+
# ============================================================================
|
|
183
|
+
# Extract numeric prefix from increment ID: "0192-github-sync-v2-multi-repo" → "0192"
|
|
184
|
+
INC_NUM=$(echo "$INC_ID" | grep -oE '^[0-9]+')
|
|
185
|
+
# Strip leading zeros: "0192" → "192", "0001" → "1"
|
|
186
|
+
FEATURE_NUM=$(echo "$INC_NUM" | sed 's/^0*//')
|
|
187
|
+
[[ -z "$FEATURE_NUM" ]] && FEATURE_NUM="0"
|
|
188
|
+
FEATURE_ID="FS-${FEATURE_NUM}"
|
|
189
|
+
log "Derived Feature ID: $FEATURE_ID (from increment $INC_ID)"
|
|
190
|
+
|
|
191
|
+
# ============================================================================
|
|
192
|
+
# CREATE MILESTONE (one per increment, idempotent)
|
|
193
|
+
# ============================================================================
|
|
194
|
+
|
|
195
|
+
# Milestone uses Feature ID format: "FS-192: Short Title"
|
|
196
|
+
MILESTONE_TITLE="${FEATURE_ID}"
|
|
197
|
+
MILESTONE_NUM=""
|
|
198
|
+
|
|
199
|
+
# Check existing milestones (list all, including closed)
|
|
200
|
+
EXISTING_MS=$(gh api "repos/$REPO_SLUG/milestones?state=all&per_page=100" --jq ".[] | select(.title == \"$MILESTONE_TITLE\") | .number" 2>/dev/null || echo "")
|
|
201
|
+
if [[ -n "$EXISTING_MS" ]]; then
|
|
202
|
+
MILESTONE_NUM="$EXISTING_MS"
|
|
203
|
+
log "Using existing milestone #$MILESTONE_NUM"
|
|
204
|
+
else
|
|
205
|
+
MS_RESULT=$(gh api "repos/$REPO_SLUG/milestones" \
|
|
206
|
+
-f "title=$MILESTONE_TITLE" \
|
|
207
|
+
-f "description=$INC_TITLE" \
|
|
208
|
+
--method POST --jq '.number' 2>/dev/null || echo "")
|
|
209
|
+
if [[ -n "$MS_RESULT" ]] && [[ "$MS_RESULT" =~ ^[0-9]+$ ]]; then
|
|
210
|
+
MILESTONE_NUM="$MS_RESULT"
|
|
211
|
+
log "Created milestone #$MILESTONE_NUM: $MILESTONE_TITLE"
|
|
212
|
+
else
|
|
213
|
+
log "Warning: Failed to create milestone (continuing without it)"
|
|
214
|
+
fi
|
|
215
|
+
fi
|
|
216
|
+
|
|
217
|
+
# ============================================================================
|
|
218
|
+
# ENSURE REQUIRED LABELS EXIST (idempotent)
|
|
219
|
+
# ============================================================================
|
|
220
|
+
|
|
221
|
+
ensure_label() {
|
|
222
|
+
local label_name="$1"
|
|
223
|
+
local label_color="${2:-ededed}"
|
|
224
|
+
local label_desc="${3:-}"
|
|
225
|
+
gh label create "$label_name" --repo "$REPO_SLUG" --color "$label_color" \
|
|
226
|
+
--description "$label_desc" --force 2>/dev/null || true
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
# Core labels
|
|
230
|
+
ensure_label "user-story" "0075ca" "User story from SpecWeave spec"
|
|
231
|
+
ensure_label "specweave" "6f42c1" "Managed by SpecWeave"
|
|
232
|
+
ensure_label "$PROJECT_LABEL" "c5def5" "$PROJECT_NAME project"
|
|
233
|
+
ensure_label "status:active" "0e8a16" "Active work item"
|
|
234
|
+
|
|
235
|
+
# Priority labels (collect unique priorities from parsed user stories)
|
|
236
|
+
for i in $(seq 0 $((US_COUNT - 1))); do
|
|
237
|
+
PRIO_LABEL="priority:$(echo "${US_PRIORITIES[$i]}" | tr '[:upper:]' '[:lower:]')"
|
|
238
|
+
case "${US_PRIORITIES[$i]}" in
|
|
239
|
+
P1) ensure_label "$PRIO_LABEL" "b60205" "Critical priority" ;;
|
|
240
|
+
P2) ensure_label "$PRIO_LABEL" "fbca04" "Medium priority" ;;
|
|
241
|
+
P3) ensure_label "$PRIO_LABEL" "0e8a16" "Low priority" ;;
|
|
242
|
+
*) ensure_label "$PRIO_LABEL" "ededed" "Priority label" ;;
|
|
243
|
+
esac
|
|
244
|
+
done
|
|
245
|
+
|
|
246
|
+
log "Labels ensured"
|
|
247
|
+
|
|
248
|
+
# ============================================================================
|
|
249
|
+
# EXTRACT ACCEPTANCE CRITERIA FOR EACH USER STORY
|
|
250
|
+
# ============================================================================
|
|
251
|
+
|
|
252
|
+
# Function to extract ACs for a given US from spec.md
|
|
253
|
+
extract_acs_for_us() {
|
|
254
|
+
local us_id="$1"
|
|
255
|
+
local in_section=false
|
|
256
|
+
local in_ac=false
|
|
257
|
+
local acs=""
|
|
258
|
+
|
|
259
|
+
while IFS= read -r line; do
|
|
260
|
+
# Start of our US section
|
|
261
|
+
if [[ "$line" =~ ^###[[:space:]]+"$us_id": ]]; then
|
|
262
|
+
in_section=true
|
|
263
|
+
continue
|
|
264
|
+
fi
|
|
265
|
+
# Start of next US section — stop
|
|
266
|
+
if $in_section && [[ "$line" =~ ^###[[:space:]]+US- ]]; then
|
|
267
|
+
break
|
|
268
|
+
fi
|
|
269
|
+
# Inside our section, look for AC lines
|
|
270
|
+
if $in_section; then
|
|
271
|
+
if [[ "$line" =~ ^\*\*Acceptance[[:space:]]Criteria ]]; then
|
|
272
|
+
in_ac=true
|
|
273
|
+
continue
|
|
274
|
+
fi
|
|
275
|
+
if $in_ac && [[ "$line" =~ ^-[[:space:]]+\[(.)\][[:space:]]+\*\*([^*]+)\*\*:[[:space:]]*(.*) ]]; then
|
|
276
|
+
local checked="${BASH_REMATCH[1]}"
|
|
277
|
+
local ac_id="${BASH_REMATCH[2]}"
|
|
278
|
+
local ac_desc="${BASH_REMATCH[3]}"
|
|
279
|
+
if [[ "$checked" == "x" ]]; then
|
|
280
|
+
acs+="- [x] **${ac_id}**: ${ac_desc}"$'\n'
|
|
281
|
+
else
|
|
282
|
+
acs+="- [ ] **${ac_id}**: ${ac_desc}"$'\n'
|
|
283
|
+
fi
|
|
284
|
+
fi
|
|
285
|
+
# Stop ACs at next section or empty line after ACs
|
|
286
|
+
if $in_ac && [[ "$line" =~ ^---$ ]]; then
|
|
287
|
+
break
|
|
288
|
+
fi
|
|
289
|
+
fi
|
|
290
|
+
done < "$SPEC_PATH"
|
|
291
|
+
|
|
292
|
+
echo "$acs"
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
# Function to extract description for a given US
|
|
296
|
+
extract_desc_for_us() {
|
|
297
|
+
local us_id="$1"
|
|
298
|
+
local in_section=false
|
|
299
|
+
local desc=""
|
|
300
|
+
local collecting=false
|
|
301
|
+
|
|
302
|
+
while IFS= read -r line; do
|
|
303
|
+
if [[ "$line" =~ ^###[[:space:]]+"$us_id": ]]; then
|
|
304
|
+
in_section=true
|
|
305
|
+
continue
|
|
306
|
+
fi
|
|
307
|
+
if $in_section && [[ "$line" =~ ^###[[:space:]]+US- ]]; then
|
|
308
|
+
break
|
|
309
|
+
fi
|
|
310
|
+
if $in_section; then
|
|
311
|
+
# Collect the As a/I want/So that lines
|
|
312
|
+
if [[ "$line" =~ ^\*\*As\ a\*\* ]] || [[ "$line" =~ ^\*\*I\ want\*\* ]] || [[ "$line" =~ ^\*\*So\ that\*\* ]]; then
|
|
313
|
+
desc+="$line"$'\n'
|
|
314
|
+
fi
|
|
315
|
+
fi
|
|
316
|
+
done < "$SPEC_PATH"
|
|
317
|
+
|
|
318
|
+
echo "$desc"
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
# ============================================================================
|
|
322
|
+
# CREATE ISSUES
|
|
323
|
+
# ============================================================================
|
|
324
|
+
|
|
325
|
+
CREATED_JSON="{}"
|
|
326
|
+
ERRORS=0
|
|
327
|
+
|
|
328
|
+
for i in $(seq 0 $((US_COUNT - 1))); do
|
|
329
|
+
US_ID="${US_IDS[$i]}"
|
|
330
|
+
US_TITLE="${US_TITLES[$i]}"
|
|
331
|
+
US_PRIORITY="${US_PRIORITIES[$i]}"
|
|
332
|
+
|
|
333
|
+
ISSUE_TITLE="[${FEATURE_ID}][${US_ID}] ${US_TITLE}"
|
|
334
|
+
|
|
335
|
+
# Check if issue already exists (by title prefix search — match both old and new format)
|
|
336
|
+
EXISTING=$(gh issue list --repo "$REPO_SLUG" --search "[${FEATURE_ID}][${US_ID}] in:title" --json number,url --limit 1 2>/dev/null || echo "[]")
|
|
337
|
+
EXISTING_NUM=$(echo "$EXISTING" | jq -r '.[0].number // empty' 2>/dev/null)
|
|
338
|
+
|
|
339
|
+
if [[ -n "$EXISTING_NUM" ]]; then
|
|
340
|
+
EXISTING_URL=$(echo "$EXISTING" | jq -r '.[0].url // empty' 2>/dev/null)
|
|
341
|
+
log "Issue already exists for $US_ID: #$EXISTING_NUM"
|
|
342
|
+
CREATED_JSON=$(echo "$CREATED_JSON" | jq --arg us "$US_ID" --arg num "$EXISTING_NUM" --arg url "$EXISTING_URL" \
|
|
343
|
+
'. + {($us): {"issueNumber": ($num | tonumber), "issueUrl": $url, "status": "existing"}}' 2>/dev/null)
|
|
344
|
+
continue
|
|
345
|
+
fi
|
|
346
|
+
|
|
347
|
+
# Build issue body
|
|
348
|
+
US_DESC=$(extract_desc_for_us "$US_ID")
|
|
349
|
+
US_ACS=$(extract_acs_for_us "$US_ID")
|
|
350
|
+
|
|
351
|
+
ISSUE_BODY="## Description
|
|
352
|
+
|
|
353
|
+
${US_DESC}
|
|
354
|
+
**Priority**: ${US_PRIORITY}
|
|
355
|
+
|
|
356
|
+
## Acceptance Criteria
|
|
357
|
+
|
|
358
|
+
<!-- specweave:ac-start -->
|
|
359
|
+
${US_ACS}<!-- specweave:ac-end -->
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
**Feature**: ${FEATURE_ID} | **Increment**: \`${INC_ID}\`
|
|
363
|
+
<!-- specweave:sync feature=${FEATURE_ID} increment=${INC_ID} us=${US_ID} -->"
|
|
364
|
+
|
|
365
|
+
# Create the issue
|
|
366
|
+
CREATE_ARGS=(
|
|
367
|
+
"issue" "create"
|
|
368
|
+
"--repo" "$REPO_SLUG"
|
|
369
|
+
"--title" "$ISSUE_TITLE"
|
|
370
|
+
"--body" "$ISSUE_BODY"
|
|
371
|
+
"--label" "user-story"
|
|
372
|
+
"--label" "specweave"
|
|
373
|
+
"--label" "$PROJECT_LABEL"
|
|
374
|
+
"--label" "status:active"
|
|
375
|
+
"--label" "priority:$(echo "$US_PRIORITY" | tr '[:upper:]' '[:lower:]')"
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
# Add milestone if available
|
|
379
|
+
if [[ -n "$MILESTONE_NUM" ]]; then
|
|
380
|
+
CREATE_ARGS+=("--milestone" "$MILESTONE_TITLE")
|
|
381
|
+
fi
|
|
382
|
+
|
|
383
|
+
RESULT=$(gh "${CREATE_ARGS[@]}" 2>&1)
|
|
384
|
+
CREATE_EXIT=$?
|
|
385
|
+
|
|
386
|
+
if [[ $CREATE_EXIT -ne 0 ]]; then
|
|
387
|
+
log "ERROR creating issue for $US_ID: $RESULT"
|
|
388
|
+
((ERRORS++))
|
|
389
|
+
|
|
390
|
+
# Update circuit breaker
|
|
391
|
+
CB_VAL=$(cat "$CB_FILE" 2>/dev/null || echo 0)
|
|
392
|
+
echo "$((CB_VAL + 1))" > "$CB_FILE" 2>/dev/null || true
|
|
393
|
+
continue
|
|
394
|
+
fi
|
|
395
|
+
|
|
396
|
+
# Parse issue URL from result (gh outputs the URL on success)
|
|
397
|
+
ISSUE_URL=$(echo "$RESULT" | grep -oE 'https://github.com/[^ ]+' | head -1)
|
|
398
|
+
ISSUE_NUM=$(echo "$ISSUE_URL" | grep -oE '[0-9]+$')
|
|
399
|
+
|
|
400
|
+
if [[ -n "$ISSUE_NUM" ]]; then
|
|
401
|
+
log "Created issue #$ISSUE_NUM for $US_ID: $ISSUE_URL"
|
|
402
|
+
CREATED_JSON=$(echo "$CREATED_JSON" | jq --arg us "$US_ID" --arg num "$ISSUE_NUM" --arg url "$ISSUE_URL" \
|
|
403
|
+
'. + {($us): {"issueNumber": ($num | tonumber), "issueUrl": $url, "status": "created"}}' 2>/dev/null)
|
|
404
|
+
fi
|
|
405
|
+
done
|
|
406
|
+
|
|
407
|
+
# ============================================================================
|
|
408
|
+
# UPDATE METADATA.JSON WITH EXTERNAL LINKS
|
|
409
|
+
# ============================================================================
|
|
410
|
+
|
|
411
|
+
if [[ -f "$META_PATH" ]]; then
|
|
412
|
+
UPDATED_META=$(jq --argjson issues "$CREATED_JSON" --arg ms "${MILESTONE_NUM:-}" \
|
|
413
|
+
'.externalLinks.github = {
|
|
414
|
+
"issues": $issues,
|
|
415
|
+
"milestone": (if $ms != "" then ($ms | tonumber) else null end),
|
|
416
|
+
"syncedAt": (now | strftime("%Y-%m-%dT%H:%M:%SZ"))
|
|
417
|
+
}' "$META_PATH" 2>/dev/null)
|
|
418
|
+
|
|
419
|
+
if [[ -n "$UPDATED_META" ]]; then
|
|
420
|
+
echo "$UPDATED_META" > "$META_PATH" 2>/dev/null
|
|
421
|
+
log "Updated metadata.json with GitHub issue links"
|
|
422
|
+
fi
|
|
423
|
+
fi
|
|
424
|
+
|
|
425
|
+
# ============================================================================
|
|
426
|
+
# UPDATE SYNC METADATA
|
|
427
|
+
# ============================================================================
|
|
428
|
+
|
|
429
|
+
SYNC_META="$PROJECT_ROOT/.specweave/sync-metadata.json"
|
|
430
|
+
if [[ -f "$SYNC_META" ]]; then
|
|
431
|
+
jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
432
|
+
'.github.lastSync = $ts | .github.lastSyncResult = "success"' \
|
|
433
|
+
"$SYNC_META" > "${SYNC_META}.tmp" 2>/dev/null && \
|
|
434
|
+
mv "${SYNC_META}.tmp" "$SYNC_META" 2>/dev/null
|
|
435
|
+
fi
|
|
436
|
+
|
|
437
|
+
# Reset circuit breaker on success
|
|
438
|
+
if (( ERRORS == 0 )); then
|
|
439
|
+
echo "0" > "$CB_FILE" 2>/dev/null || true
|
|
440
|
+
fi
|
|
441
|
+
|
|
442
|
+
CREATED_COUNT=$(echo "$CREATED_JSON" | jq 'to_entries | map(select(.value.status == "created")) | length' 2>/dev/null || echo 0)
|
|
443
|
+
EXISTING_COUNT=$(echo "$CREATED_JSON" | jq 'to_entries | map(select(.value.status == "existing")) | length' 2>/dev/null || echo 0)
|
|
444
|
+
|
|
445
|
+
log "Done: $CREATED_COUNT created, $EXISTING_COUNT existing, $ERRORS errors"
|
|
446
|
+
|
|
447
|
+
# Output summary for user visibility (will appear as hook output)
|
|
448
|
+
if (( CREATED_COUNT > 0 )); then
|
|
449
|
+
echo ""
|
|
450
|
+
echo " [GitHub Sync] Auto-created $CREATED_COUNT issue(s) for increment $INC_ID"
|
|
451
|
+
echo " Milestone: $MILESTONE_TITLE"
|
|
452
|
+
echo ""
|
|
453
|
+
fi
|
|
454
|
+
|
|
455
|
+
exit 0
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
2
|
+
import { execFileNoThrow } from "../../../src/utils/execFileNoThrow.js";
|
|
3
|
+
import { pushSyncUserStories } from "./github-push-sync.js";
|
|
4
|
+
async function postACProgressComments(incrementId, affectedUSIds, specPath, options) {
|
|
5
|
+
const result = { posted: [], errors: [] };
|
|
6
|
+
if (affectedUSIds.length === 0) {
|
|
7
|
+
return result;
|
|
8
|
+
}
|
|
9
|
+
let content;
|
|
10
|
+
try {
|
|
11
|
+
content = await readFile(specPath, "utf-8");
|
|
12
|
+
} catch (err) {
|
|
13
|
+
result.errors.push({
|
|
14
|
+
usId: affectedUSIds[0],
|
|
15
|
+
error: err instanceof Error ? err.message : String(err)
|
|
16
|
+
});
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
const issueLinks = parseIssueLinks(content);
|
|
20
|
+
const repoSlug = `${options.owner}/${options.repo}`;
|
|
21
|
+
const env = options.token ? { GH_TOKEN: options.token } : void 0;
|
|
22
|
+
for (const usId of affectedUSIds) {
|
|
23
|
+
const link = issueLinks[usId];
|
|
24
|
+
if (!link) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const acStates = parseACStatesForUS(content, usId);
|
|
28
|
+
const commentBody = buildProgressCommentForUS(incrementId, usId, acStates);
|
|
29
|
+
const execResult = await execFileNoThrow(
|
|
30
|
+
"gh",
|
|
31
|
+
["issue", "comment", String(link.issueNumber), "--body", commentBody, "-R", repoSlug],
|
|
32
|
+
env ? { env } : {}
|
|
33
|
+
);
|
|
34
|
+
if (execResult.success) {
|
|
35
|
+
result.posted.push({ usId, issueNumber: link.issueNumber });
|
|
36
|
+
} else {
|
|
37
|
+
result.errors.push({
|
|
38
|
+
usId,
|
|
39
|
+
error: execResult.stderr || "Unknown error posting comment"
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const usForSync = buildUserStoryForSync(content, usId, acStates, incrementId);
|
|
43
|
+
if (usForSync) {
|
|
44
|
+
try {
|
|
45
|
+
await pushSyncUserStories([usForSync], {
|
|
46
|
+
owner: options.owner,
|
|
47
|
+
repo: options.repo,
|
|
48
|
+
token: options.token
|
|
49
|
+
});
|
|
50
|
+
} catch {
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
function parseIssueLinks(content) {
|
|
57
|
+
const links = {};
|
|
58
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
59
|
+
if (!fmMatch) return links;
|
|
60
|
+
const frontmatter = fmMatch[1];
|
|
61
|
+
const usBlockMatch = frontmatter.match(/userStories:\s*\n((?:\s{6,}[\s\S]*?)(?=\n\s{0,3}\S|$))/);
|
|
62
|
+
if (!usBlockMatch) return links;
|
|
63
|
+
const usBlock = usBlockMatch[1];
|
|
64
|
+
const usEntries = usBlock.match(/^\s+(US-\d+):\s*\n((?:\s+\w[\s\S]*?)(?=\n\s+US-|\s*$))/gm);
|
|
65
|
+
if (!usEntries) return links;
|
|
66
|
+
for (const entry of usEntries) {
|
|
67
|
+
const idMatch = entry.match(/(US-\d+):/);
|
|
68
|
+
const numMatch = entry.match(/issueNumber:\s*(\d+)/);
|
|
69
|
+
const urlMatch = entry.match(/issueUrl:\s*"([^"]+)"/);
|
|
70
|
+
if (idMatch && numMatch) {
|
|
71
|
+
links[idMatch[1]] = {
|
|
72
|
+
issueNumber: parseInt(numMatch[1], 10),
|
|
73
|
+
issueUrl: urlMatch ? urlMatch[1] : ""
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return links;
|
|
78
|
+
}
|
|
79
|
+
function parseACStatesForUS(content, usId) {
|
|
80
|
+
const states = [];
|
|
81
|
+
const usNum = String(parseInt(usId.replace("US-", ""), 10));
|
|
82
|
+
const acPattern = new RegExp(
|
|
83
|
+
`- \\[([ x])\\] \\*\\*AC-US${usNum}-(\\d+)\\*\\*:\\s*(.+)`,
|
|
84
|
+
"g"
|
|
85
|
+
);
|
|
86
|
+
let match;
|
|
87
|
+
while ((match = acPattern.exec(content)) !== null) {
|
|
88
|
+
states.push({
|
|
89
|
+
id: `AC-US${usNum}-${match[2]}`,
|
|
90
|
+
description: match[3].trim(),
|
|
91
|
+
completed: match[1] === "x"
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return states;
|
|
95
|
+
}
|
|
96
|
+
function buildUserStoryForSync(content, usId, acStates, incrementId) {
|
|
97
|
+
const usNum = String(parseInt(usId.replace("US-", ""), 10)).padStart(3, "0");
|
|
98
|
+
const titleMatch = content.match(new RegExp(`### US-${usNum}:\\s*(.+)`));
|
|
99
|
+
const title = titleMatch ? titleMatch[1].trim() : usId;
|
|
100
|
+
return {
|
|
101
|
+
id: usId,
|
|
102
|
+
title,
|
|
103
|
+
description: "",
|
|
104
|
+
priority: "P1",
|
|
105
|
+
status: "in-progress",
|
|
106
|
+
acceptanceCriteria: acStates.map((ac) => ({
|
|
107
|
+
id: ac.id,
|
|
108
|
+
description: ac.description,
|
|
109
|
+
completed: ac.completed
|
|
110
|
+
})),
|
|
111
|
+
specId: incrementId
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function buildProgressCommentForUS(incrementId, usId, acStates) {
|
|
115
|
+
const total = acStates.length;
|
|
116
|
+
const completed = acStates.filter((ac) => ac.completed).length;
|
|
117
|
+
const percentage = total > 0 ? Math.round(completed / total * 100) : 0;
|
|
118
|
+
let comment = `**Progress Update** \u2014 ${usId} (Increment ${incrementId})
|
|
119
|
+
|
|
120
|
+
`;
|
|
121
|
+
comment += `**Status**: ${completed}/${total} ACs complete (${percentage}%)
|
|
122
|
+
|
|
123
|
+
`;
|
|
124
|
+
if (completed > 0) {
|
|
125
|
+
comment += `**Completed**:
|
|
126
|
+
`;
|
|
127
|
+
for (const ac of acStates.filter((a) => a.completed)) {
|
|
128
|
+
comment += `- [x] **${ac.id}**: ${ac.description}
|
|
129
|
+
`;
|
|
130
|
+
}
|
|
131
|
+
comment += "\n";
|
|
132
|
+
}
|
|
133
|
+
if (completed < total) {
|
|
134
|
+
comment += `**Remaining**:
|
|
135
|
+
`;
|
|
136
|
+
for (const ac of acStates.filter((a) => !a.completed)) {
|
|
137
|
+
comment += `- [ ] **${ac.id}**: ${ac.description}
|
|
138
|
+
`;
|
|
139
|
+
}
|
|
140
|
+
comment += "\n";
|
|
141
|
+
}
|
|
142
|
+
comment += `---
|
|
143
|
+
`;
|
|
144
|
+
comment += `Auto-synced by SpecWeave | ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
|
|
145
|
+
`;
|
|
146
|
+
return comment;
|
|
147
|
+
}
|
|
148
|
+
export {
|
|
149
|
+
postACProgressComments
|
|
150
|
+
};
|