specweave 0.17.16 → 0.17.19
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 +405 -2495
- package/README.md +92 -2
- package/dist/locales/de/.gitkeep +0 -0
- package/dist/locales/de/cli.json +108 -0
- package/dist/locales/en/cli.json +287 -0
- package/dist/locales/en/errors.json +7 -0
- package/dist/locales/en/templates.json +6 -0
- package/dist/locales/es/.gitkeep +0 -0
- package/dist/locales/es/cli.json +41 -0
- package/dist/locales/fr/.gitkeep +0 -0
- package/dist/locales/fr/cli.json +108 -0
- package/dist/locales/ja/.gitkeep +0 -0
- package/dist/locales/ja/cli.json +108 -0
- package/dist/locales/ko/.gitkeep +0 -0
- package/dist/locales/ko/cli.json +108 -0
- package/dist/locales/pt/.gitkeep +0 -0
- package/dist/locales/pt/cli.json +108 -0
- package/dist/locales/ru/.gitkeep +0 -0
- package/dist/locales/ru/cli.json +269 -0
- package/dist/locales/zh/.gitkeep +0 -0
- package/dist/locales/zh/cli.json +108 -0
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.d.ts.map +1 -1
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.js +188 -36
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js +65 -6
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-status-sync.d.ts +54 -0
- package/dist/plugins/specweave-ado/lib/ado-status-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-status-sync.js +86 -0
- package/dist/plugins/specweave-ado/lib/ado-status-sync.js.map +1 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.d.ts +25 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.js +191 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/duplicate-detector.d.ts +139 -0
- package/dist/plugins/specweave-github/lib/duplicate-detector.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/duplicate-detector.js +389 -0
- package/dist/plugins/specweave-github/lib/duplicate-detector.js.map +1 -0
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.d.ts +26 -0
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.js +249 -0
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts +63 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.js +216 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-client.d.ts +1 -1
- package/dist/plugins/specweave-github/lib/github-client.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client.js +25 -13
- package/dist/plugins/specweave-github/lib/github-client.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts +83 -0
- package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-epic-sync.js +466 -0
- package/dist/plugins/specweave-github/lib/github-epic-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-status-sync.d.ts +43 -0
- package/dist/plugins/specweave-github/lib/github-status-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-status-sync.js +82 -0
- package/dist/plugins/specweave-github/lib/github-status-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/task-sync.d.ts +5 -0
- package/dist/plugins/specweave-github/lib/task-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/task-sync.js +38 -2
- package/dist/plugins/specweave-github/lib/task-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts +28 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js +156 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-epic-sync.d.ts +66 -0
- package/dist/plugins/specweave-jira/lib/jira-epic-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-epic-sync.js +274 -0
- package/dist/plugins/specweave-jira/lib/jira-epic-sync.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts +56 -0
- package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-status-sync.js +93 -0
- package/dist/plugins/specweave-jira/lib/jira-status-sync.js.map +1 -0
- package/dist/spec-parser.js +629 -0
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +107 -3
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.js +48 -3
- package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
- package/dist/src/core/deduplication/command-deduplicator.d.ts +166 -0
- package/dist/src/core/deduplication/command-deduplicator.d.ts.map +1 -0
- package/dist/src/core/deduplication/command-deduplicator.js +254 -0
- package/dist/src/core/deduplication/command-deduplicator.js.map +1 -0
- package/dist/src/core/living-docs/hierarchy-mapper.d.ts +142 -0
- package/dist/src/core/living-docs/hierarchy-mapper.d.ts.map +1 -0
- package/dist/src/core/living-docs/hierarchy-mapper.js +453 -0
- package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -0
- package/dist/src/core/living-docs/index.d.ts +10 -84
- package/dist/src/core/living-docs/index.d.ts.map +1 -1
- package/dist/src/core/living-docs/index.js +10 -164
- package/dist/src/core/living-docs/index.js.map +1 -1
- package/dist/src/core/living-docs/spec-distributor.d.ts +106 -0
- package/dist/src/core/living-docs/spec-distributor.d.ts.map +1 -0
- package/dist/src/core/living-docs/spec-distributor.js +823 -0
- package/dist/src/core/living-docs/spec-distributor.js.map +1 -0
- package/dist/src/core/living-docs/types.d.ts +201 -0
- package/dist/src/core/living-docs/types.d.ts.map +1 -0
- package/dist/src/core/living-docs/types.js +15 -0
- package/dist/src/core/living-docs/types.js.map +1 -0
- package/dist/src/core/logging/prompt-logger.d.ts +70 -0
- package/dist/src/core/logging/prompt-logger.d.ts.map +1 -0
- package/dist/src/core/logging/prompt-logger.js +247 -0
- package/dist/src/core/logging/prompt-logger.js.map +1 -0
- package/dist/src/core/status-line/status-line-manager.d.ts +15 -24
- package/dist/src/core/status-line/status-line-manager.d.ts.map +1 -1
- package/dist/src/core/status-line/status-line-manager.js +33 -70
- package/dist/src/core/status-line/status-line-manager.js.map +1 -1
- package/dist/src/core/status-line/types.d.ts +19 -31
- package/dist/src/core/status-line/types.d.ts.map +1 -1
- package/dist/src/core/status-line/types.js +5 -9
- package/dist/src/core/status-line/types.js.map +1 -1
- package/dist/src/core/sync/conflict-resolver.d.ts +66 -0
- package/dist/src/core/sync/conflict-resolver.d.ts.map +1 -0
- package/dist/src/core/sync/conflict-resolver.js +108 -0
- package/dist/src/core/sync/conflict-resolver.js.map +1 -0
- package/dist/src/core/sync/enhanced-content-builder.d.ts +55 -0
- package/dist/src/core/sync/enhanced-content-builder.d.ts.map +1 -0
- package/dist/src/core/sync/enhanced-content-builder.js +202 -0
- package/dist/src/core/sync/enhanced-content-builder.js.map +1 -0
- package/dist/src/core/sync/label-detector.d.ts +66 -0
- package/dist/src/core/sync/label-detector.d.ts.map +1 -0
- package/dist/src/core/sync/label-detector.js +211 -0
- package/dist/src/core/sync/label-detector.js.map +1 -0
- package/dist/src/core/sync/retry-logic.d.ts +64 -0
- package/dist/src/core/sync/retry-logic.d.ts.map +1 -0
- package/dist/src/core/sync/retry-logic.js +165 -0
- package/dist/src/core/sync/retry-logic.js.map +1 -0
- package/dist/src/core/sync/spec-content-sync.d.ts +88 -0
- package/dist/src/core/sync/spec-content-sync.d.ts.map +1 -0
- package/dist/src/core/sync/spec-content-sync.js +5 -0
- package/dist/src/core/sync/spec-content-sync.js.map +1 -0
- package/dist/src/core/sync/spec-increment-mapper.d.ts +100 -0
- package/dist/src/core/sync/spec-increment-mapper.d.ts.map +1 -0
- package/dist/src/core/sync/spec-increment-mapper.js +424 -0
- package/dist/src/core/sync/spec-increment-mapper.js.map +1 -0
- package/dist/src/core/sync/status-cache.d.ts +91 -0
- package/dist/src/core/sync/status-cache.d.ts.map +1 -0
- package/dist/src/core/sync/status-cache.js +140 -0
- package/dist/src/core/sync/status-cache.js.map +1 -0
- package/dist/src/core/sync/status-mapper.d.ts +69 -0
- package/dist/src/core/sync/status-mapper.d.ts.map +1 -0
- package/dist/src/core/sync/status-mapper.js +90 -0
- package/dist/src/core/sync/status-mapper.js.map +1 -0
- package/dist/src/core/sync/status-sync-engine.d.ts +162 -0
- package/dist/src/core/sync/status-sync-engine.d.ts.map +1 -0
- package/dist/src/core/sync/status-sync-engine.js +347 -0
- package/dist/src/core/sync/status-sync-engine.js.map +1 -0
- package/dist/src/core/sync/sync-event-logger.d.ts +99 -0
- package/dist/src/core/sync/sync-event-logger.d.ts.map +1 -0
- package/dist/src/core/sync/sync-event-logger.js +103 -0
- package/dist/src/core/sync/sync-event-logger.js.map +1 -0
- package/dist/src/core/sync/types.d.ts +52 -0
- package/dist/src/core/sync/types.d.ts.map +1 -0
- package/dist/src/core/sync/types.js +5 -0
- package/dist/src/core/sync/types.js.map +1 -0
- package/dist/src/core/sync/workflow-detector.d.ts +95 -0
- package/dist/src/core/sync/workflow-detector.d.ts.map +1 -0
- package/dist/src/core/sync/workflow-detector.js +175 -0
- package/dist/src/core/sync/workflow-detector.js.map +1 -0
- package/dist/src/core/types/config.d.ts +51 -0
- package/dist/src/core/types/config.d.ts.map +1 -1
- package/dist/src/core/types/config.js +47 -0
- package/dist/src/core/types/config.js.map +1 -1
- package/dist/src/core/types/increment-metadata.d.ts +4 -0
- package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
- package/dist/src/core/types/increment-metadata.js.map +1 -1
- package/dist/src/utils/github-url.d.ts +53 -0
- package/dist/src/utils/github-url.d.ts.map +1 -0
- package/dist/src/utils/github-url.js +90 -0
- package/dist/src/utils/github-url.js.map +1 -0
- package/dist/src/utils/spec-parser.d.ts +145 -0
- package/dist/src/utils/spec-parser.d.ts.map +1 -0
- package/dist/src/utils/spec-parser.js +640 -0
- package/dist/src/utils/spec-parser.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +1 -1
- package/plugins/specweave/agents/pm/AGENT.md +160 -13
- package/plugins/specweave/agents/pm/templates/increment-spec.md +158 -0
- package/plugins/specweave/agents/pm/templates/living-docs-spec.md +113 -0
- package/plugins/specweave/commands/specweave-done.md +163 -0
- package/plugins/specweave/commands/specweave.md +70 -405
- package/plugins/specweave/hooks/hooks.json +4 -0
- package/plugins/specweave/hooks/lib/sync-spec-content.sh +2 -2
- package/plugins/specweave/hooks/lib/update-status-line.sh +79 -111
- package/plugins/specweave/hooks/post-increment-planning.sh +133 -37
- package/plugins/specweave/hooks/pre-command-deduplication.sh +86 -0
- package/plugins/specweave/lib/hooks/sync-living-docs.js +139 -34
- package/plugins/specweave/lib/hooks/sync-living-docs.ts +234 -38
- package/plugins/specweave/skills/SKILLS-INDEX.md +4 -24
- package/plugins/specweave/skills/increment-planner/SKILL.md +94 -0
- package/plugins/specweave/skills/increment-work-router/SKILL.md +466 -0
- package/plugins/specweave-ado/commands/specweave-ado-sync-spec.md +1 -1
- package/plugins/specweave-ado/lib/ado-spec-content-sync.js +49 -5
- package/plugins/specweave-ado/lib/ado-spec-content-sync.ts +72 -6
- package/plugins/specweave-ado/lib/ado-status-sync.js +80 -0
- package/plugins/specweave-ado/lib/ado-status-sync.ts +121 -0
- package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
- package/plugins/specweave-github/commands/specweave-github-cleanup-duplicates.md +205 -0
- package/plugins/specweave-github/commands/specweave-github-sync-epic.md +248 -0
- package/plugins/specweave-github/commands/specweave-github-sync-spec.md +1 -1
- package/plugins/specweave-github/hooks/post-task-completion.sh +32 -0
- package/plugins/specweave-github/lib/duplicate-detector.js +370 -0
- package/plugins/specweave-github/lib/duplicate-detector.ts +525 -0
- package/plugins/specweave-github/lib/enhanced-github-sync.js +220 -0
- package/plugins/specweave-github/lib/enhanced-github-sync.ts +322 -0
- package/plugins/specweave-github/lib/epic-content-builder.js +227 -0
- package/plugins/specweave-github/lib/epic-content-builder.ts +317 -0
- package/plugins/specweave-github/lib/github-client.js +21 -10
- package/plugins/specweave-github/lib/github-client.ts +27 -16
- package/plugins/specweave-github/lib/github-epic-sync.js +488 -0
- package/plugins/specweave-github/lib/github-epic-sync.ts +715 -0
- package/plugins/specweave-github/lib/github-status-sync.js +71 -0
- package/plugins/specweave-github/lib/github-status-sync.ts +107 -0
- package/plugins/specweave-github/lib/task-sync.js +33 -2
- package/plugins/specweave-github/lib/task-sync.ts +44 -2
- package/plugins/specweave-jira/commands/specweave-jira-sync-epic.md +267 -0
- package/plugins/specweave-jira/commands/specweave-jira-sync-spec.md +1 -1
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +134 -0
- package/plugins/specweave-jira/lib/enhanced-jira-sync.ts +196 -0
- package/plugins/specweave-jira/lib/jira-epic-sync.js +304 -0
- package/plugins/specweave-jira/lib/jira-epic-sync.ts +459 -0
- package/plugins/specweave-jira/lib/jira-status-sync.js +79 -0
- package/plugins/specweave-jira/lib/jira-status-sync.ts +139 -0
- package/plugins/specweave-release/commands/specweave-release-platform.md +1 -1
- package/plugins/specweave-release/hooks/post-task-completion.sh +2 -2
- package/src/templates/AGENTS.md.template +88 -1
- package/src/templates/CLAUDE.md.template +49 -0
- package/plugins/specweave/skills/increment-quality-judge/SKILL.md +0 -524
- package/plugins/specweave/skills/plugin-installer/SKILL.md +0 -353
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: specweave-github:sync-epic
|
|
3
|
+
description: Sync SpecWeave Epic folder to GitHub (Milestone + Issues). Implements Universal Hierarchy architecture - Epic → Milestone, Increments → Issues.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Sync Epic to GitHub (Universal Hierarchy)
|
|
7
|
+
|
|
8
|
+
**Architecture**: Hierarchical sync using Epic folder structure
|
|
9
|
+
|
|
10
|
+
- **Epic (FS-001)** → **GitHub Milestone**
|
|
11
|
+
- **Increment (0001-core-framework)** → **GitHub Issue** (linked to Milestone)
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
/specweave-github:sync-epic <epic-id>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## What It Does
|
|
20
|
+
|
|
21
|
+
**Hierarchical Sync Process**:
|
|
22
|
+
|
|
23
|
+
1. **Load Epic folder** from `.specweave/docs/internal/specs/FS-XXX-name/`
|
|
24
|
+
2. **Parse Epic README.md** to get Epic metadata (title, increments, status)
|
|
25
|
+
3. **Create or update GitHub Milestone**:
|
|
26
|
+
- Title: `[FS-001] Epic Title`
|
|
27
|
+
- Description: Epic overview + progress stats
|
|
28
|
+
- State: Open (active/planning) or Closed (complete)
|
|
29
|
+
4. **Sync each increment as GitHub Issue**:
|
|
30
|
+
- Title: `[INC-0001-core-framework] Title`
|
|
31
|
+
- Body: Increment overview + link to tasks.md
|
|
32
|
+
- Milestone: Linked to Epic Milestone
|
|
33
|
+
- Labels: `increment`, `epic-sync`
|
|
34
|
+
5. **Update frontmatter** in Epic README.md and increment files
|
|
35
|
+
|
|
36
|
+
## Examples
|
|
37
|
+
|
|
38
|
+
### Sync Epic FS-001 (Core Framework Architecture)
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
/specweave-github:sync-epic FS-001
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Output**:
|
|
45
|
+
```
|
|
46
|
+
🔄 Syncing Epic FS-001 to GitHub...
|
|
47
|
+
📦 Epic: Core Framework Architecture
|
|
48
|
+
📊 Increments: 4
|
|
49
|
+
🚀 Creating GitHub Milestone...
|
|
50
|
+
✅ Created Milestone #10
|
|
51
|
+
|
|
52
|
+
📝 Syncing 4 increments...
|
|
53
|
+
✅ Created Issue #130 for 0001-core-framework
|
|
54
|
+
✅ Created Issue #131 for 0002-core-enhancements
|
|
55
|
+
✅ Created Issue #132 for 0004-plugin-architecture
|
|
56
|
+
✅ Created Issue #133 for 0005-cross-platform-cli
|
|
57
|
+
|
|
58
|
+
✅ Epic sync complete!
|
|
59
|
+
Milestone: https://github.com/owner/repo/milestone/10
|
|
60
|
+
Issues created: 4
|
|
61
|
+
Issues updated: 0
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Sync Epic with short ID
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
/specweave-github:sync-epic 031
|
|
68
|
+
# Resolves to FS-031
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Re-sync Epic (updates existing Milestone/Issues)
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
/specweave-github:sync-epic FS-001
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Output**:
|
|
78
|
+
```
|
|
79
|
+
🔄 Syncing Epic FS-001 to GitHub...
|
|
80
|
+
♻️ Updating existing Milestone #10...
|
|
81
|
+
✅ Updated Milestone #10
|
|
82
|
+
|
|
83
|
+
📝 Syncing 4 increments...
|
|
84
|
+
♻️ Updated Issue #130 for 0001-core-framework
|
|
85
|
+
♻️ Updated Issue #131 for 0002-core-enhancements
|
|
86
|
+
♻️ Updated Issue #132 for 0004-plugin-architecture
|
|
87
|
+
♻️ Updated Issue #133 for 0005-cross-platform-cli
|
|
88
|
+
|
|
89
|
+
✅ Epic sync complete!
|
|
90
|
+
Milestone: https://github.com/owner/repo/milestone/10
|
|
91
|
+
Issues created: 0
|
|
92
|
+
Issues updated: 4
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Arguments
|
|
96
|
+
|
|
97
|
+
- `<epic-id>` - Epic ID (e.g., `FS-001` or just `001`)
|
|
98
|
+
|
|
99
|
+
## What Gets Created
|
|
100
|
+
|
|
101
|
+
### GitHub Milestone (Epic-level)
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
Title: [FS-001] Core Framework Architecture
|
|
105
|
+
Description:
|
|
106
|
+
Epic: Core Framework Architecture
|
|
107
|
+
|
|
108
|
+
Progress: 4/4 increments (100%)
|
|
109
|
+
|
|
110
|
+
Priority: P0
|
|
111
|
+
Status: complete
|
|
112
|
+
|
|
113
|
+
State: Closed (if complete) or Open (if active/planning)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### GitHub Issues (Increment-level)
|
|
117
|
+
|
|
118
|
+
```markdown
|
|
119
|
+
Title: [INC-0001-core-framework] Core Framework
|
|
120
|
+
|
|
121
|
+
# Core Framework
|
|
122
|
+
|
|
123
|
+
Foundation framework with CLI, plugin system, and agent architecture...
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
**Increment**: 0001-core-framework
|
|
128
|
+
**Milestone**: See milestone for Epic progress
|
|
129
|
+
|
|
130
|
+
🤖 Auto-created by SpecWeave Epic Sync
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Labels**: `increment`, `epic-sync`
|
|
134
|
+
**Milestone**: Linked to Epic Milestone
|
|
135
|
+
|
|
136
|
+
## Frontmatter Updates
|
|
137
|
+
|
|
138
|
+
### Epic README.md (after sync)
|
|
139
|
+
|
|
140
|
+
```yaml
|
|
141
|
+
---
|
|
142
|
+
id: FS-001
|
|
143
|
+
title: "Core Framework Architecture"
|
|
144
|
+
external_tools:
|
|
145
|
+
github:
|
|
146
|
+
type: milestone
|
|
147
|
+
id: 10 # ← Added
|
|
148
|
+
url: https://github.com/.../milestone/10 # ← Added
|
|
149
|
+
increments:
|
|
150
|
+
- id: 0001-core-framework
|
|
151
|
+
external:
|
|
152
|
+
github: 130 # ← Added
|
|
153
|
+
- id: 0002-core-enhancements
|
|
154
|
+
external:
|
|
155
|
+
github: 131 # ← Added
|
|
156
|
+
---
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Increment file (0001-core-framework.md)
|
|
160
|
+
|
|
161
|
+
```yaml
|
|
162
|
+
---
|
|
163
|
+
id: 0001-core-framework
|
|
164
|
+
epic: FS-001
|
|
165
|
+
external:
|
|
166
|
+
github:
|
|
167
|
+
issue: 130 # ← Added
|
|
168
|
+
url: https://github.com/.../issues/130 # ← Added
|
|
169
|
+
---
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Benefits
|
|
173
|
+
|
|
174
|
+
✅ **Hierarchical tracking**: GitHub Milestones group related increments
|
|
175
|
+
✅ **Epic-level progress**: See completion percentage in Milestone
|
|
176
|
+
✅ **Automatic linking**: All Issues linked to Milestone
|
|
177
|
+
✅ **Idempotent**: Safe to re-run (updates existing Milestone/Issues)
|
|
178
|
+
✅ **Brownfield-ready**: Links existing GitHub Milestones/Issues
|
|
179
|
+
|
|
180
|
+
## Requirements
|
|
181
|
+
|
|
182
|
+
1. **GitHub CLI** (`gh`) installed and authenticated
|
|
183
|
+
2. **Git repository** with GitHub remote
|
|
184
|
+
3. **Write access** to repository (for creating Milestones/Issues)
|
|
185
|
+
4. **Epic folder exists** at `.specweave/docs/internal/specs/FS-XXX-name/`
|
|
186
|
+
|
|
187
|
+
## Architecture: Why Milestones?
|
|
188
|
+
|
|
189
|
+
**GitHub's Hierarchy**:
|
|
190
|
+
- GitHub Milestones = Epic-level grouping
|
|
191
|
+
- GitHub Issues = Increment-level work items
|
|
192
|
+
- GitHub Projects = Optional (cross-Epic tracking)
|
|
193
|
+
|
|
194
|
+
**Comparison with JIRA/ADO**:
|
|
195
|
+
- JIRA: Epic → Epic, Increment → Story (with Epic Link field)
|
|
196
|
+
- ADO: Epic → Feature, Increment → User Story (with Parent link)
|
|
197
|
+
- GitHub: Epic → Milestone, Increment → Issue (with Milestone link)
|
|
198
|
+
|
|
199
|
+
All three implement the same Universal Hierarchy, just with different terminology.
|
|
200
|
+
|
|
201
|
+
## Troubleshooting
|
|
202
|
+
|
|
203
|
+
**"Epic FS-001 not found"**:
|
|
204
|
+
- Check Epic folder exists: `ls .specweave/docs/internal/specs/`
|
|
205
|
+
- Verify Epic ID format: `FS-001-epic-name/`
|
|
206
|
+
|
|
207
|
+
**"Epic README.md missing YAML frontmatter"**:
|
|
208
|
+
- Ensure Epic was migrated with `migrate-to-epic-folders.ts`
|
|
209
|
+
- Frontmatter must start with `---` on line 1
|
|
210
|
+
|
|
211
|
+
**"Failed to create GitHub Milestone"**:
|
|
212
|
+
- Check GitHub CLI auth: `gh auth status`
|
|
213
|
+
- Verify write access: `gh repo view`
|
|
214
|
+
- Check rate limits: `gh api rate_limit`
|
|
215
|
+
|
|
216
|
+
**"Could not extract issue number"**:
|
|
217
|
+
- GitHub CLI output format may have changed
|
|
218
|
+
- Check CLI version: `gh --version` (need v2.0.0+)
|
|
219
|
+
|
|
220
|
+
## Related Commands
|
|
221
|
+
|
|
222
|
+
- `/specweave-github:sync-spec` - OLD (flat spec → project) - DEPRECATED for Epic architecture
|
|
223
|
+
- `/specweave-jira:sync-epic` - Sync to JIRA Epic + Stories
|
|
224
|
+
- `/specweave-ado:sync-epic` - Sync to ADO Feature + User Stories
|
|
225
|
+
|
|
226
|
+
## Implementation
|
|
227
|
+
|
|
228
|
+
**File**: `plugins/specweave-github/lib/github-epic-sync.ts`
|
|
229
|
+
|
|
230
|
+
**Core Class**: `GitHubEpicSync`
|
|
231
|
+
|
|
232
|
+
**Methods**:
|
|
233
|
+
- `syncEpicToGitHub(epicId)` - Main sync logic
|
|
234
|
+
- `createMilestone(epic)` - Create GitHub Milestone
|
|
235
|
+
- `updateMilestone(number, epic)` - Update existing Milestone
|
|
236
|
+
- `createIssue(increment, milestone)` - Create GitHub Issue
|
|
237
|
+
- `updateIssue(number, increment, milestone)` - Update existing Issue
|
|
238
|
+
- `updateEpicReadme(path, github)` - Update frontmatter
|
|
239
|
+
- `updateIncrementExternalLink(...)` - Update increment frontmatter
|
|
240
|
+
|
|
241
|
+
## Next Steps
|
|
242
|
+
|
|
243
|
+
After syncing Epic to GitHub:
|
|
244
|
+
|
|
245
|
+
1. **View Milestone progress**: `gh milestone view 10`
|
|
246
|
+
2. **List Issues in Milestone**: `gh issue list --milestone 10`
|
|
247
|
+
3. **Track completion**: GitHub automatically calculates Milestone progress
|
|
248
|
+
4. **Close Milestone**: When all increments complete, Milestone auto-closes
|
|
@@ -199,6 +199,38 @@ else
|
|
|
199
199
|
fi
|
|
200
200
|
fi
|
|
201
201
|
|
|
202
|
+
# ============================================================================
|
|
203
|
+
# EPIC GITHUB ISSUE SYNC (Update Epic issue with fresh task progress)
|
|
204
|
+
# ============================================================================
|
|
205
|
+
|
|
206
|
+
echo "[$(date)] [GitHub] 🔄 Checking for Epic GitHub issue update..." >> "$DEBUG_LOG" 2>/dev/null || true
|
|
207
|
+
|
|
208
|
+
# Find active increment ID
|
|
209
|
+
ACTIVE_INCREMENT=$(ls -t .specweave/increments/ | grep -v '^\.' | while read inc; do
|
|
210
|
+
if [ -f ".specweave/increments/$inc/metadata.json" ]; then
|
|
211
|
+
STATUS=$(grep -o '"status"[[:space:]]*:[[:space:]]*"[^"]*"' ".specweave/increments/$inc/metadata.json" 2>/dev/null | sed 's/.*"\([^"]*\)".*/\1/' || true)
|
|
212
|
+
if [ "$STATUS" = "active" ]; then
|
|
213
|
+
echo "$inc"
|
|
214
|
+
break
|
|
215
|
+
fi
|
|
216
|
+
fi
|
|
217
|
+
done | head -1)
|
|
218
|
+
|
|
219
|
+
if [ -n "$ACTIVE_INCREMENT" ]; then
|
|
220
|
+
echo "[$(date)] [GitHub] 🎯 Active increment: $ACTIVE_INCREMENT" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
221
|
+
|
|
222
|
+
# Run Epic sync script (silently, errors logged to debug log)
|
|
223
|
+
if [ -f "$PROJECT_ROOT/scripts/update-epic-github-issue.sh" ]; then
|
|
224
|
+
echo "[$(date)] [GitHub] 🚀 Updating Epic GitHub issue..." >> "$DEBUG_LOG" 2>/dev/null || true
|
|
225
|
+
"$PROJECT_ROOT/scripts/update-epic-github-issue.sh" "$ACTIVE_INCREMENT" >> "$DEBUG_LOG" 2>&1 || true
|
|
226
|
+
echo "[$(date)] [GitHub] ✅ Epic sync complete (see logs for details)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
227
|
+
else
|
|
228
|
+
echo "[$(date)] [GitHub] ⚠️ Epic sync script not found, skipping" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
229
|
+
fi
|
|
230
|
+
else
|
|
231
|
+
echo "[$(date)] [GitHub] ℹ️ No active increment found, skipping Epic sync" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
232
|
+
fi
|
|
233
|
+
|
|
202
234
|
# ============================================================================
|
|
203
235
|
# OUTPUT TO CLAUDE
|
|
204
236
|
# ============================================================================
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { execFileSync } from "child_process";
|
|
2
|
+
class DuplicateDetector {
|
|
3
|
+
/**
|
|
4
|
+
* PHASE 1: Detection (Before Creating Issue)
|
|
5
|
+
*
|
|
6
|
+
* Searches GitHub for existing issues matching the title pattern.
|
|
7
|
+
* This is the PRIMARY defense against duplicates.
|
|
8
|
+
*
|
|
9
|
+
* @param titlePattern - Pattern to search (e.g., "[FS-031]" or "[INC-0031]")
|
|
10
|
+
* @param incrementId - Optional increment ID for more precise matching
|
|
11
|
+
* @param repo - Optional repo (format: "owner/repo")
|
|
12
|
+
* @returns Existing issue if found, null otherwise
|
|
13
|
+
*/
|
|
14
|
+
static async checkBeforeCreate(titlePattern, incrementId, repo) {
|
|
15
|
+
console.log(`\u{1F50D} DETECTION: Checking for existing issue with pattern: ${titlePattern}`);
|
|
16
|
+
try {
|
|
17
|
+
const args = [
|
|
18
|
+
"issue",
|
|
19
|
+
"list",
|
|
20
|
+
"--search",
|
|
21
|
+
`"${titlePattern}" in:title`,
|
|
22
|
+
"--json",
|
|
23
|
+
"number,title,url,body,createdAt",
|
|
24
|
+
"--limit",
|
|
25
|
+
"20",
|
|
26
|
+
"--state",
|
|
27
|
+
"all"
|
|
28
|
+
// Check both open and closed
|
|
29
|
+
];
|
|
30
|
+
if (repo) {
|
|
31
|
+
args.push("--repo", repo);
|
|
32
|
+
}
|
|
33
|
+
const output = execFileSync("gh", args, { encoding: "utf-8" });
|
|
34
|
+
const issues = JSON.parse(output);
|
|
35
|
+
if (issues.length === 0) {
|
|
36
|
+
console.log(" \u2705 No existing issues found (safe to create)");
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
console.log(` \u{1F4CB} Found ${issues.length} issue(s) matching pattern`);
|
|
40
|
+
const exactMatch = issues.find(
|
|
41
|
+
(issue) => issue.title.includes(titlePattern)
|
|
42
|
+
);
|
|
43
|
+
if (exactMatch) {
|
|
44
|
+
console.log(` \u26A0\uFE0F DUPLICATE DETECTED: Issue #${exactMatch.number}`);
|
|
45
|
+
console.log(` \u{1F4CE} URL: ${exactMatch.url}`);
|
|
46
|
+
return exactMatch;
|
|
47
|
+
}
|
|
48
|
+
if (incrementId) {
|
|
49
|
+
const bodyMatch = issues.find(
|
|
50
|
+
(issue) => issue.body && (issue.body.includes(`**Increment**: ${incrementId}`) || issue.body.includes(incrementId))
|
|
51
|
+
);
|
|
52
|
+
if (bodyMatch) {
|
|
53
|
+
console.log(` \u26A0\uFE0F DUPLICATE DETECTED (body match): Issue #${bodyMatch.number}`);
|
|
54
|
+
console.log(` \u{1F4CE} URL: ${bodyMatch.url}`);
|
|
55
|
+
return bodyMatch;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
console.log(" \u2705 No exact match found (safe to create)");
|
|
59
|
+
return null;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.warn(` \u26A0\uFE0F Detection failed (continuing anyway): ${error.message}`);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* PHASE 2: Verification (After Creating Issue)
|
|
67
|
+
*
|
|
68
|
+
* Counts issues matching the pattern and identifies duplicates.
|
|
69
|
+
* This is the SECONDARY defense - catches duplicates that slipped through.
|
|
70
|
+
*
|
|
71
|
+
* @param titlePattern - Pattern to search (e.g., "[FS-031]")
|
|
72
|
+
* @param expectedCount - Expected number of issues (usually 1)
|
|
73
|
+
* @param repo - Optional repo (format: "owner/repo")
|
|
74
|
+
* @returns Verification result with duplicate list
|
|
75
|
+
*/
|
|
76
|
+
static async verifyAfterCreate(titlePattern, expectedCount = 1, repo) {
|
|
77
|
+
console.log(`
|
|
78
|
+
\u{1F50D} VERIFICATION: Checking issue count for pattern: ${titlePattern}`);
|
|
79
|
+
try {
|
|
80
|
+
const args = [
|
|
81
|
+
"issue",
|
|
82
|
+
"list",
|
|
83
|
+
"--search",
|
|
84
|
+
`"${titlePattern}" in:title`,
|
|
85
|
+
"--json",
|
|
86
|
+
"number,title,url,createdAt",
|
|
87
|
+
"--limit",
|
|
88
|
+
"50",
|
|
89
|
+
"--state",
|
|
90
|
+
"all"
|
|
91
|
+
];
|
|
92
|
+
if (repo) {
|
|
93
|
+
args.push("--repo", repo);
|
|
94
|
+
}
|
|
95
|
+
const output = execFileSync("gh", args, { encoding: "utf-8" });
|
|
96
|
+
const issues = JSON.parse(output);
|
|
97
|
+
const exactMatches = issues.filter(
|
|
98
|
+
(issue) => issue.title.includes(titlePattern)
|
|
99
|
+
);
|
|
100
|
+
const actualCount = exactMatches.length;
|
|
101
|
+
console.log(` Expected: ${expectedCount} issue(s)`);
|
|
102
|
+
console.log(` Actual: ${actualCount} issue(s)`);
|
|
103
|
+
if (actualCount === expectedCount) {
|
|
104
|
+
console.log(` \u2705 VERIFICATION PASSED: Count matches!`);
|
|
105
|
+
return {
|
|
106
|
+
success: true,
|
|
107
|
+
expectedCount,
|
|
108
|
+
actualCount,
|
|
109
|
+
duplicates: [],
|
|
110
|
+
message: "Verification passed"
|
|
111
|
+
};
|
|
112
|
+
} else if (actualCount > expectedCount) {
|
|
113
|
+
console.warn(` \u26A0\uFE0F VERIFICATION FAILED: ${actualCount - expectedCount} duplicate(s) detected!`);
|
|
114
|
+
const sorted = exactMatches.sort((a, b) => {
|
|
115
|
+
const dateA = new Date(a.createdAt || 0).getTime();
|
|
116
|
+
const dateB = new Date(b.createdAt || 0).getTime();
|
|
117
|
+
return dateA - dateB;
|
|
118
|
+
});
|
|
119
|
+
const duplicates = sorted.slice(1);
|
|
120
|
+
console.warn(` \u{1F4CB} Duplicate issues:`);
|
|
121
|
+
duplicates.forEach((dup) => {
|
|
122
|
+
console.warn(` - #${dup.number}: ${dup.title}`);
|
|
123
|
+
});
|
|
124
|
+
return {
|
|
125
|
+
success: false,
|
|
126
|
+
expectedCount,
|
|
127
|
+
actualCount,
|
|
128
|
+
duplicates,
|
|
129
|
+
message: `${duplicates.length} duplicate(s) found`
|
|
130
|
+
};
|
|
131
|
+
} else {
|
|
132
|
+
console.warn(` \u26A0\uFE0F VERIFICATION WARNING: Expected ${expectedCount} but found ${actualCount}`);
|
|
133
|
+
return {
|
|
134
|
+
success: false,
|
|
135
|
+
expectedCount,
|
|
136
|
+
actualCount,
|
|
137
|
+
duplicates: [],
|
|
138
|
+
message: "Count mismatch (fewer than expected)"
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.error(` \u274C Verification failed: ${error.message}`);
|
|
143
|
+
return {
|
|
144
|
+
success: false,
|
|
145
|
+
expectedCount,
|
|
146
|
+
actualCount: -1,
|
|
147
|
+
duplicates: [],
|
|
148
|
+
message: `Verification error: ${error.message}`
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* PHASE 3: Reflection (Auto-Correct Duplicates)
|
|
154
|
+
*
|
|
155
|
+
* Automatically closes duplicate issues and keeps the oldest one.
|
|
156
|
+
* This is the CLEANUP phase - fixes problems that occurred.
|
|
157
|
+
*
|
|
158
|
+
* @param duplicates - List of duplicate issues to close
|
|
159
|
+
* @param keepIssueNumber - Issue number to keep (usually the oldest)
|
|
160
|
+
* @param repo - Optional repo (format: "owner/repo")
|
|
161
|
+
* @returns Correction result with count of closed issues
|
|
162
|
+
*/
|
|
163
|
+
static async correctDuplicates(duplicates, keepIssueNumber, repo) {
|
|
164
|
+
if (duplicates.length === 0) {
|
|
165
|
+
return {
|
|
166
|
+
success: true,
|
|
167
|
+
duplicatesClosed: 0,
|
|
168
|
+
keptIssue: keepIssueNumber,
|
|
169
|
+
errors: []
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
console.log(`
|
|
173
|
+
\u{1F527} REFLECTION: Auto-correcting ${duplicates.length} duplicate(s)...`);
|
|
174
|
+
const errors = [];
|
|
175
|
+
let closed = 0;
|
|
176
|
+
for (const duplicate of duplicates) {
|
|
177
|
+
try {
|
|
178
|
+
console.log(` \u{1F5D1}\uFE0F Closing duplicate #${duplicate.number}...`);
|
|
179
|
+
const comment = `Duplicate of #${keepIssueNumber}
|
|
180
|
+
|
|
181
|
+
This issue was automatically closed by SpecWeave's Global Duplicate Detection System.
|
|
182
|
+
|
|
183
|
+
The original issue (#${keepIssueNumber}) should be used for tracking instead.
|
|
184
|
+
|
|
185
|
+
\u{1F916} Auto-closed by SpecWeave`;
|
|
186
|
+
const commentArgs = [
|
|
187
|
+
"issue",
|
|
188
|
+
"comment",
|
|
189
|
+
duplicate.number.toString(),
|
|
190
|
+
"--body",
|
|
191
|
+
comment
|
|
192
|
+
];
|
|
193
|
+
if (repo) {
|
|
194
|
+
commentArgs.push("--repo", repo);
|
|
195
|
+
}
|
|
196
|
+
execFileSync("gh", commentArgs, { encoding: "utf-8" });
|
|
197
|
+
const closeArgs = [
|
|
198
|
+
"issue",
|
|
199
|
+
"close",
|
|
200
|
+
duplicate.number.toString()
|
|
201
|
+
];
|
|
202
|
+
if (repo) {
|
|
203
|
+
closeArgs.push("--repo", repo);
|
|
204
|
+
}
|
|
205
|
+
execFileSync("gh", closeArgs, { encoding: "utf-8" });
|
|
206
|
+
console.log(` \u2705 Closed #${duplicate.number}`);
|
|
207
|
+
closed++;
|
|
208
|
+
} catch (error) {
|
|
209
|
+
const errorMsg = `Failed to close #${duplicate.number}: ${error.message}`;
|
|
210
|
+
console.error(` \u274C ${errorMsg}`);
|
|
211
|
+
errors.push(errorMsg);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
console.log(`
|
|
215
|
+
\u2705 REFLECTION COMPLETE: Kept #${keepIssueNumber}, closed ${closed}/${duplicates.length} duplicate(s)`);
|
|
216
|
+
return {
|
|
217
|
+
success: errors.length === 0,
|
|
218
|
+
duplicatesClosed: closed,
|
|
219
|
+
keptIssue: keepIssueNumber,
|
|
220
|
+
errors
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* ALL-IN-ONE: Create Issue with Full Protection
|
|
225
|
+
*
|
|
226
|
+
* This is the RECOMMENDED way to create GitHub issues in SpecWeave.
|
|
227
|
+
* Combines all 3 phases: Detection → Creation → Verification → Reflection
|
|
228
|
+
*
|
|
229
|
+
* GUARANTEES:
|
|
230
|
+
* - No duplicates will be created
|
|
231
|
+
* - Existing duplicates will be detected and closed
|
|
232
|
+
* - Idempotent: can run multiple times safely
|
|
233
|
+
*
|
|
234
|
+
* @param options - Create options (title, body, labels, etc.)
|
|
235
|
+
* @returns Create result with issue details and duplicate stats
|
|
236
|
+
*/
|
|
237
|
+
static async createWithProtection(options) {
|
|
238
|
+
const {
|
|
239
|
+
title,
|
|
240
|
+
body,
|
|
241
|
+
titlePattern,
|
|
242
|
+
incrementId,
|
|
243
|
+
labels = ["specweave"],
|
|
244
|
+
milestone,
|
|
245
|
+
assignees = [],
|
|
246
|
+
repo
|
|
247
|
+
} = options;
|
|
248
|
+
console.log(`
|
|
249
|
+
\u{1F6E1}\uFE0F Creating GitHub issue with FULL PROTECTION...`);
|
|
250
|
+
console.log(` Title: ${title}`);
|
|
251
|
+
console.log(` Pattern: ${titlePattern}`);
|
|
252
|
+
console.log(`
|
|
253
|
+
\u2501\u2501\u2501 PHASE 1: DETECTION \u2501\u2501\u2501`);
|
|
254
|
+
const existing = await this.checkBeforeCreate(titlePattern, incrementId, repo);
|
|
255
|
+
let issueNumber;
|
|
256
|
+
let issueUrl;
|
|
257
|
+
let wasReused = false;
|
|
258
|
+
if (existing) {
|
|
259
|
+
console.log(`
|
|
260
|
+
\u267B\uFE0F Using existing issue #${existing.number} (skipping creation)`);
|
|
261
|
+
issueNumber = existing.number;
|
|
262
|
+
issueUrl = existing.url;
|
|
263
|
+
wasReused = true;
|
|
264
|
+
} else {
|
|
265
|
+
console.log(`
|
|
266
|
+
\u2501\u2501\u2501 PHASE 2: CREATION \u2501\u2501\u2501`);
|
|
267
|
+
console.log(` Creating new GitHub issue...`);
|
|
268
|
+
try {
|
|
269
|
+
const args = [
|
|
270
|
+
"issue",
|
|
271
|
+
"create",
|
|
272
|
+
"--title",
|
|
273
|
+
title,
|
|
274
|
+
"--body",
|
|
275
|
+
body
|
|
276
|
+
];
|
|
277
|
+
if (repo) {
|
|
278
|
+
args.push("--repo", repo);
|
|
279
|
+
}
|
|
280
|
+
labels.forEach((label) => {
|
|
281
|
+
args.push("--label", label);
|
|
282
|
+
});
|
|
283
|
+
if (milestone) {
|
|
284
|
+
args.push("--milestone", milestone);
|
|
285
|
+
}
|
|
286
|
+
assignees.forEach((assignee) => {
|
|
287
|
+
args.push("--assignee", assignee);
|
|
288
|
+
});
|
|
289
|
+
const output = execFileSync("gh", args, { encoding: "utf-8" });
|
|
290
|
+
const match = output.match(/\/issues\/(\d+)/);
|
|
291
|
+
if (!match) {
|
|
292
|
+
throw new Error("Could not extract issue number from gh CLI output");
|
|
293
|
+
}
|
|
294
|
+
issueNumber = parseInt(match[1], 10);
|
|
295
|
+
issueUrl = output.trim();
|
|
296
|
+
console.log(` \u2705 Created issue #${issueNumber}`);
|
|
297
|
+
console.log(` \u{1F4CE} ${issueUrl}`);
|
|
298
|
+
} catch (error) {
|
|
299
|
+
throw new Error(`Failed to create GitHub issue: ${error.message}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
console.log(`
|
|
303
|
+
\u2501\u2501\u2501 PHASE 3: VERIFICATION \u2501\u2501\u2501`);
|
|
304
|
+
const verification = await this.verifyAfterCreate(titlePattern, 1, repo);
|
|
305
|
+
let duplicatesClosed = 0;
|
|
306
|
+
if (!verification.success && verification.duplicates.length > 0) {
|
|
307
|
+
console.log(`
|
|
308
|
+
\u2501\u2501\u2501 PHASE 4: REFLECTION \u2501\u2501\u2501`);
|
|
309
|
+
console.warn(` \u26A0\uFE0F ${verification.duplicates.length} duplicate(s) detected!`);
|
|
310
|
+
const correction = await this.correctDuplicates(
|
|
311
|
+
verification.duplicates,
|
|
312
|
+
issueNumber,
|
|
313
|
+
repo
|
|
314
|
+
);
|
|
315
|
+
duplicatesClosed = correction.duplicatesClosed;
|
|
316
|
+
if (correction.errors.length > 0) {
|
|
317
|
+
console.warn(` \u26A0\uFE0F Some duplicates could not be closed:`);
|
|
318
|
+
correction.errors.forEach((err) => console.warn(` - ${err}`));
|
|
319
|
+
}
|
|
320
|
+
} else if (verification.success) {
|
|
321
|
+
console.log(` \u2705 No duplicates detected!`);
|
|
322
|
+
}
|
|
323
|
+
console.log(`
|
|
324
|
+
\u2705 Issue creation complete!`);
|
|
325
|
+
console.log(` Issue: #${issueNumber}`);
|
|
326
|
+
console.log(` Duplicates found: ${verification.duplicates.length}`);
|
|
327
|
+
console.log(` Duplicates closed: ${duplicatesClosed}`);
|
|
328
|
+
console.log(` Reused existing: ${wasReused ? "Yes" : "No"}`);
|
|
329
|
+
return {
|
|
330
|
+
issue: {
|
|
331
|
+
number: issueNumber,
|
|
332
|
+
title,
|
|
333
|
+
url: issueUrl
|
|
334
|
+
},
|
|
335
|
+
duplicatesFound: verification.duplicates.length,
|
|
336
|
+
duplicatesClosed,
|
|
337
|
+
wasReused
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Utility: Extract title pattern from full title
|
|
342
|
+
*
|
|
343
|
+
* Examples:
|
|
344
|
+
* - "[FS-031] Feature Title" → "[FS-031]"
|
|
345
|
+
* - "[INC-0031] Increment Title" → "[INC-0031]"
|
|
346
|
+
*
|
|
347
|
+
* @param title - Full issue title
|
|
348
|
+
* @returns Extracted pattern or null
|
|
349
|
+
*/
|
|
350
|
+
static extractTitlePattern(title) {
|
|
351
|
+
const match = title.match(/^(\[[^\]]+\])/);
|
|
352
|
+
return match ? match[1] : null;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Utility: Check if GitHub CLI is available and authenticated
|
|
356
|
+
*
|
|
357
|
+
* @returns true if gh CLI is ready, false otherwise
|
|
358
|
+
*/
|
|
359
|
+
static checkGitHubCLI() {
|
|
360
|
+
try {
|
|
361
|
+
execFileSync("gh", ["auth", "status"], { encoding: "utf-8" });
|
|
362
|
+
return true;
|
|
363
|
+
} catch {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
export {
|
|
369
|
+
DuplicateDetector
|
|
370
|
+
};
|