specweave 0.26.2 → 0.26.4

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.
Files changed (113) hide show
  1. package/CLAUDE.md +172 -1413
  2. package/dist/src/cli/commands/plan/plan-orchestrator.js +2 -2
  3. package/dist/src/cli/commands/plan/plan-orchestrator.js.map +1 -1
  4. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.d.ts.map +1 -1
  5. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js +147 -55
  6. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js.map +1 -1
  7. package/dist/src/core/increment/completion-validator.d.ts +4 -0
  8. package/dist/src/core/increment/completion-validator.d.ts.map +1 -1
  9. package/dist/src/core/increment/completion-validator.js +36 -0
  10. package/dist/src/core/increment/completion-validator.js.map +1 -1
  11. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  12. package/dist/src/core/living-docs/living-docs-sync.js +47 -13
  13. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  14. package/dist/src/core/us-sync-throttle.d.ts +113 -0
  15. package/dist/src/core/us-sync-throttle.d.ts.map +1 -0
  16. package/dist/src/core/us-sync-throttle.js +195 -0
  17. package/dist/src/core/us-sync-throttle.js.map +1 -0
  18. package/dist/src/utils/external-tool-drift-detector.d.ts +76 -0
  19. package/dist/src/utils/external-tool-drift-detector.d.ts.map +1 -0
  20. package/dist/src/utils/external-tool-drift-detector.js +235 -0
  21. package/dist/src/utils/external-tool-drift-detector.js.map +1 -0
  22. package/package.json +4 -4
  23. package/plugins/specweave/hooks/post-task-completion.sh +6 -6
  24. package/plugins/specweave/hooks/pre-increment-start.sh +6 -1
  25. package/plugins/specweave/lib/hooks/us-completion-orchestrator.js +62 -89
  26. package/plugins/specweave/lib/hooks/us-completion-orchestrator.ts +215 -0
  27. package/plugins/specweave-ado/lib/ado-multi-project-sync.js +0 -1
  28. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
  29. package/plugins/specweave-release/hooks/post-task-completion.sh +5 -1
  30. package/plugins/specweave/agents/pm/AGENT.md.bak +0 -1893
  31. package/plugins/specweave/hooks/docs-changed.sh.backup +0 -79
  32. package/plugins/specweave/hooks/human-input-required.sh.backup +0 -75
  33. package/plugins/specweave/hooks/lib/migrate-increment-work.sh.bak +0 -245
  34. package/plugins/specweave/hooks/lib/sync-spec-content.sh.bak +0 -149
  35. package/plugins/specweave/hooks/lib/validate-spec-status.sh.bak +0 -163
  36. package/plugins/specweave/hooks/post-first-increment.sh.backup +0 -61
  37. package/plugins/specweave/hooks/post-first-increment.sh.bak +0 -61
  38. package/plugins/specweave/hooks/post-increment-change.sh.backup +0 -98
  39. package/plugins/specweave/hooks/post-increment-completion.sh.backup +0 -231
  40. package/plugins/specweave/hooks/post-increment-planning.sh.backup +0 -1048
  41. package/plugins/specweave/hooks/post-increment-status-change.sh.backup +0 -147
  42. package/plugins/specweave/hooks/post-spec-update.sh.backup +0 -158
  43. package/plugins/specweave/hooks/post-spec-update.sh.bak +0 -158
  44. package/plugins/specweave/hooks/post-user-story-complete.sh.backup +0 -179
  45. package/plugins/specweave/hooks/post-user-story-complete.sh.bak +0 -179
  46. package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +0 -83
  47. package/plugins/specweave/hooks/pre-command-deduplication.sh.bak +0 -83
  48. package/plugins/specweave/hooks/pre-implementation.sh.backup +0 -67
  49. package/plugins/specweave/hooks/pre-task-completion.sh.backup +0 -194
  50. package/plugins/specweave/hooks/pre-tool-use.sh.backup +0 -133
  51. package/plugins/specweave/hooks/user-prompt-submit.sh.backup +0 -386
  52. package/plugins/specweave/hooks/user-prompt-submit.sh.bak +0 -386
  53. package/plugins/specweave/lib/hooks/auto-transition.js.bak +0 -50
  54. package/plugins/specweave/lib/hooks/auto-transition.ts.bak +0 -84
  55. package/plugins/specweave/lib/hooks/git-diff-analyzer.d.js.bak +0 -0
  56. package/plugins/specweave/lib/hooks/git-diff-analyzer.d.ts.bak +0 -89
  57. package/plugins/specweave/lib/hooks/git-diff-analyzer.js.bak +0 -142
  58. package/plugins/specweave/lib/hooks/git-diff-analyzer.ts.bak +0 -269
  59. package/plugins/specweave/lib/hooks/invoke-translator-skill.d.js.bak +0 -0
  60. package/plugins/specweave/lib/hooks/invoke-translator-skill.d.ts.bak +0 -60
  61. package/plugins/specweave/lib/hooks/invoke-translator-skill.js.bak +0 -155
  62. package/plugins/specweave/lib/hooks/invoke-translator-skill.ts.bak +0 -264
  63. package/plugins/specweave/lib/hooks/prepare-reflection-context.d.js.bak +0 -0
  64. package/plugins/specweave/lib/hooks/prepare-reflection-context.d.ts.bak +0 -42
  65. package/plugins/specweave/lib/hooks/prepare-reflection-context.js.bak +0 -110
  66. package/plugins/specweave/lib/hooks/prepare-reflection-context.ts.bak +0 -178
  67. package/plugins/specweave/lib/hooks/reflection-config-loader.d.js.bak +0 -0
  68. package/plugins/specweave/lib/hooks/reflection-config-loader.d.ts.bak +0 -45
  69. package/plugins/specweave/lib/hooks/reflection-config-loader.js.bak +0 -92
  70. package/plugins/specweave/lib/hooks/reflection-config-loader.ts.bak +0 -156
  71. package/plugins/specweave/lib/hooks/reflection-parser.d.js.bak +0 -0
  72. package/plugins/specweave/lib/hooks/reflection-parser.d.ts.bak +0 -33
  73. package/plugins/specweave/lib/hooks/reflection-parser.js.bak +0 -301
  74. package/plugins/specweave/lib/hooks/reflection-parser.ts.bak +0 -484
  75. package/plugins/specweave/lib/hooks/reflection-prompt-builder.d.js.bak +0 -0
  76. package/plugins/specweave/lib/hooks/reflection-prompt-builder.d.ts.bak +0 -56
  77. package/plugins/specweave/lib/hooks/reflection-prompt-builder.js.bak +0 -182
  78. package/plugins/specweave/lib/hooks/reflection-prompt-builder.ts.bak +0 -306
  79. package/plugins/specweave/lib/hooks/reflection-storage.d.js.bak +0 -0
  80. package/plugins/specweave/lib/hooks/reflection-storage.d.ts.bak +0 -64
  81. package/plugins/specweave/lib/hooks/reflection-storage.js.bak +0 -231
  82. package/plugins/specweave/lib/hooks/reflection-storage.ts.bak +0 -369
  83. package/plugins/specweave/lib/hooks/run-self-reflection.d.js.bak +0 -0
  84. package/plugins/specweave/lib/hooks/run-self-reflection.d.ts.bak +0 -43
  85. package/plugins/specweave/lib/hooks/run-self-reflection.js.bak +0 -132
  86. package/plugins/specweave/lib/hooks/run-self-reflection.ts.bak +0 -258
  87. package/plugins/specweave/lib/hooks/sync-cache.js.bak +0 -294
  88. package/plugins/specweave/lib/hooks/sync-living-docs.d.js.bak +0 -1
  89. package/plugins/specweave/lib/hooks/sync-living-docs.d.ts.bak +0 -27
  90. package/plugins/specweave/lib/hooks/sync-living-docs.js.bak +0 -339
  91. package/plugins/specweave/lib/hooks/sync-us-tasks.js.bak +0 -476
  92. package/plugins/specweave/lib/hooks/translate-file.d.js.bak +0 -0
  93. package/plugins/specweave/lib/hooks/translate-file.d.ts.bak +0 -59
  94. package/plugins/specweave/lib/hooks/translate-file.js.bak +0 -289
  95. package/plugins/specweave/lib/hooks/translate-file.ts.bak +0 -428
  96. package/plugins/specweave/lib/hooks/translate-living-docs.d.js.bak +0 -0
  97. package/plugins/specweave/lib/hooks/translate-living-docs.d.ts.bak +0 -13
  98. package/plugins/specweave/lib/hooks/translate-living-docs.js.bak +0 -119
  99. package/plugins/specweave/lib/hooks/translate-living-docs.ts.bak +0 -224
  100. package/plugins/specweave/lib/hooks/update-ac-status.js.bak +0 -51
  101. package/plugins/specweave/lib/hooks/update-ac-status.ts.bak +0 -103
  102. package/plugins/specweave/lib/hooks/update-tasks-md.d.js.bak +0 -1
  103. package/plugins/specweave/lib/hooks/update-tasks-md.d.ts.bak +0 -29
  104. package/plugins/specweave/lib/hooks/update-tasks-md.js.bak +0 -296
  105. package/plugins/specweave/lib/hooks/update-tasks-md.ts.bak +0 -489
  106. package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +0 -353
  107. package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +0 -172
  108. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +0 -170
  109. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +0 -904
  110. package/plugins/specweave-github/hooks/post-task-completion.sh.backup +0 -258
  111. package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +0 -172
  112. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +0 -738
  113. package/plugins/specweave-release/hooks/post-task-completion.sh.backup +0 -110
@@ -0,0 +1,235 @@
1
+ /**
2
+ * External Tool Drift Detector
3
+ *
4
+ * Detects when external tools (GitHub/JIRA/ADO) haven't been synced recently,
5
+ * indicating potential drift between living docs and external systems.
6
+ *
7
+ * Used by:
8
+ * - /specweave:done (validation warning if drift > 24h)
9
+ * - Status line (show "sync pending" indicator)
10
+ * - /specweave:progress (show last sync time)
11
+ *
12
+ * See: ADR-0131 (External Tool Sync Context Detection)
13
+ */
14
+ import * as path from 'path';
15
+ import { promises as fs } from 'fs';
16
+ import { consoleLogger } from './logger.js';
17
+ // Drift thresholds (extracted constants for P0-5 fix)
18
+ const DRIFT_THRESHOLD_HOURS = 24; // Warning threshold
19
+ const WARNING_THRESHOLD_HOURS = 48; // Elevated warning
20
+ const CRITICAL_THRESHOLD_HOURS = 168; // 7 days - critical staleness
21
+ export class ExternalToolDriftDetector {
22
+ constructor(projectRoot, options = {}) {
23
+ this.projectRoot = projectRoot;
24
+ this.logger = options.logger ?? consoleLogger;
25
+ }
26
+ /**
27
+ * Validate increment ID format to prevent path traversal attacks
28
+ * P0-1: Path traversal vulnerability fix
29
+ *
30
+ * @param incrementId - Increment ID to validate
31
+ * @throws Error if increment ID format is invalid
32
+ */
33
+ validateIncrementId(incrementId) {
34
+ // Expected format: 4 digits + hyphen + kebab-case (e.g., "0053-safe-feature-deletion")
35
+ if (!/^\d{4}-[a-z0-9-]+$/.test(incrementId)) {
36
+ throw new Error(`Invalid increment ID format: ${incrementId}. Expected format: XXXX-kebab-case`);
37
+ }
38
+ // Additional safety: reject path traversal attempts
39
+ if (incrementId.includes('..') || incrementId.includes('/') || incrementId.includes('\\')) {
40
+ throw new Error(`Path traversal attempt detected in increment ID: ${incrementId}`);
41
+ }
42
+ }
43
+ /**
44
+ * Safely parse and validate metadata.json
45
+ * P0-2: JSON injection protection
46
+ *
47
+ * @param metadataPath - Path to metadata.json
48
+ * @returns Parsed metadata object
49
+ * @throws Error if JSON is invalid or malformed
50
+ */
51
+ async readAndValidateMetadata(metadataPath) {
52
+ const content = await fs.readFile(metadataPath, 'utf-8');
53
+ let metadata;
54
+ try {
55
+ metadata = JSON.parse(content);
56
+ }
57
+ catch (error) {
58
+ throw new Error(`Invalid JSON in metadata file: ${error instanceof Error ? error.message : 'Parse error'}`);
59
+ }
60
+ // Validate JSON structure
61
+ if (typeof metadata !== 'object' || metadata === null) {
62
+ throw new Error('Metadata must be a valid JSON object');
63
+ }
64
+ return metadata;
65
+ }
66
+ /**
67
+ * Get most recent sync time across ALL external tools
68
+ * P1-1: Check all tools instead of just first configured tool
69
+ *
70
+ * @param metadata - Parsed metadata.json
71
+ * @returns Most recent sync date or null if no syncs found
72
+ */
73
+ getMostRecentSync(metadata) {
74
+ const syncTimes = [];
75
+ // Collect all sync times from configured external tools
76
+ if (metadata.github?.lastSync) {
77
+ const date = this.parseSafeDate(metadata.github.lastSync);
78
+ if (date)
79
+ syncTimes.push(date);
80
+ }
81
+ if (metadata.jira?.lastSync) {
82
+ const date = this.parseSafeDate(metadata.jira.lastSync);
83
+ if (date)
84
+ syncTimes.push(date);
85
+ }
86
+ if (metadata.ado?.lastSync) {
87
+ const date = this.parseSafeDate(metadata.ado.lastSync);
88
+ if (date)
89
+ syncTimes.push(date);
90
+ }
91
+ if (syncTimes.length === 0) {
92
+ return null;
93
+ }
94
+ // Return most recent sync time (sort descending, take first)
95
+ return syncTimes.sort((a, b) => b.getTime() - a.getTime())[0];
96
+ }
97
+ /**
98
+ * Safely parse date string with validation
99
+ *
100
+ * @param dateString - ISO date string
101
+ * @returns Date object or null if invalid
102
+ */
103
+ parseSafeDate(dateString) {
104
+ if (typeof dateString !== 'string')
105
+ return null;
106
+ const date = new Date(dateString);
107
+ // Check if date is valid
108
+ if (isNaN(date.getTime())) {
109
+ this.logger.warn(`Invalid date format: ${dateString}`);
110
+ return null;
111
+ }
112
+ return date;
113
+ }
114
+ /**
115
+ * Detect drift for a specific increment
116
+ *
117
+ * @param incrementId - Increment ID (e.g., "0053-safe-feature-deletion")
118
+ * @returns Drift status with recommendations
119
+ */
120
+ async detectDrift(incrementId) {
121
+ try {
122
+ // P0-1: Validate increment ID to prevent path traversal
123
+ this.validateIncrementId(incrementId);
124
+ // P0-4: Use async fs operations (replaced existsSync/readFileSync)
125
+ const metadataPath = path.join(this.projectRoot, '.specweave/increments', incrementId, 'metadata.json');
126
+ // Check if metadata exists (async)
127
+ try {
128
+ await fs.access(metadataPath);
129
+ }
130
+ catch {
131
+ return {
132
+ hasDrift: false,
133
+ lastSyncTime: null,
134
+ hoursSinceSync: null,
135
+ externalToolsConfigured: false,
136
+ recommendation: 'No metadata found'
137
+ };
138
+ }
139
+ // P0-2: Safe JSON parsing with validation
140
+ const metadata = await this.readAndValidateMetadata(metadataPath);
141
+ // Check if external tools are configured
142
+ const hasGitHub = metadata.github && metadata.github.issues && metadata.github.issues.length > 0;
143
+ const hasJira = metadata.jira && metadata.jira.issues && metadata.jira.issues.length > 0;
144
+ const hasADO = metadata.ado && metadata.ado.workItems && metadata.ado.workItems.length > 0;
145
+ const externalToolsConfigured = hasGitHub || hasJira || hasADO;
146
+ if (!externalToolsConfigured) {
147
+ return {
148
+ hasDrift: false,
149
+ lastSyncTime: null,
150
+ hoursSinceSync: null,
151
+ externalToolsConfigured: false,
152
+ recommendation: 'No external tools configured'
153
+ };
154
+ }
155
+ // P1-1: Get most recent sync time across ALL external tools (not just first one)
156
+ const lastSyncTime = this.getMostRecentSync(metadata);
157
+ if (!lastSyncTime) {
158
+ return {
159
+ hasDrift: true,
160
+ lastSyncTime: null,
161
+ hoursSinceSync: null,
162
+ externalToolsConfigured: true,
163
+ recommendation: 'External tools never synced - run /specweave:sync-progress'
164
+ };
165
+ }
166
+ // Calculate hours since last sync
167
+ const now = new Date();
168
+ const hoursSinceSync = (now.getTime() - lastSyncTime.getTime()) / (1000 * 60 * 60);
169
+ // Use extracted constants (P0-5 fix)
170
+ const hasDrift = hoursSinceSync > DRIFT_THRESHOLD_HOURS;
171
+ let recommendation = '';
172
+ if (hasDrift) {
173
+ if (hoursSinceSync < WARNING_THRESHOLD_HOURS) {
174
+ recommendation = 'External tools synced 1-2 days ago - consider running /specweave:sync-progress';
175
+ }
176
+ else if (hoursSinceSync < CRITICAL_THRESHOLD_HOURS) {
177
+ recommendation = `External tools synced ${Math.floor(hoursSinceSync / 24)} days ago - run /specweave:sync-progress soon`;
178
+ }
179
+ else {
180
+ recommendation = `External tools synced ${Math.floor(hoursSinceSync / 168)} weeks ago - URGENT: run /specweave:sync-progress`;
181
+ }
182
+ }
183
+ else {
184
+ recommendation = `External tools synced ${Math.floor(hoursSinceSync)} hours ago - up to date`;
185
+ }
186
+ return {
187
+ hasDrift,
188
+ lastSyncTime,
189
+ hoursSinceSync,
190
+ externalToolsConfigured,
191
+ recommendation
192
+ };
193
+ }
194
+ catch (error) {
195
+ // P0-5: Don't mask errors - expose them in return value
196
+ this.logger.error(`Error detecting drift for ${incrementId}:`, error);
197
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
198
+ return {
199
+ hasDrift: false,
200
+ lastSyncTime: null,
201
+ hoursSinceSync: null,
202
+ externalToolsConfigured: false,
203
+ recommendation: `Error detecting drift: ${errorMessage}`,
204
+ error: errorMessage // Expose error for caller to handle
205
+ };
206
+ }
207
+ }
208
+ /**
209
+ * Get human-readable drift message for display
210
+ *
211
+ * @param drift - Drift status from detectDrift()
212
+ * @returns Formatted message with emoji indicator
213
+ */
214
+ getDriftMessage(drift) {
215
+ if (!drift.externalToolsConfigured) {
216
+ return '';
217
+ }
218
+ if (!drift.lastSyncTime) {
219
+ return '⚠️ External tools never synced';
220
+ }
221
+ if (drift.hasDrift) {
222
+ if (drift.hoursSinceSync && drift.hoursSinceSync < 48) {
223
+ return `⚠️ External sync: ${Math.floor(drift.hoursSinceSync)}h ago`;
224
+ }
225
+ else if (drift.hoursSinceSync && drift.hoursSinceSync < 168) {
226
+ return `🔴 External sync: ${Math.floor(drift.hoursSinceSync / 24)}d ago`;
227
+ }
228
+ else {
229
+ return `🔴 External sync: ${Math.floor((drift.hoursSinceSync || 0) / 168)}w ago (STALE)`;
230
+ }
231
+ }
232
+ return `✅ External sync: ${Math.floor(drift.hoursSinceSync || 0)}h ago`;
233
+ }
234
+ }
235
+ //# sourceMappingURL=external-tool-drift-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"external-tool-drift-detector.js","sourceRoot":"","sources":["../../../src/utils/external-tool-drift-detector.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specweave",
3
- "version": "0.26.2",
3
+ "version": "0.26.4",
4
4
  "description": "Spec-driven development framework for Claude Code. AI-native workflow with living documentation, intelligent agents, and multilingual support (9 languages). Enterprise-grade traceability with permanent specs and temporary increments.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -77,7 +77,7 @@
77
77
  "LICENSE"
78
78
  ],
79
79
  "dependencies": {
80
- "@anthropic-ai/sdk": "^0.20.0",
80
+ "@anthropic-ai/sdk": "^0.70.1",
81
81
  "@octokit/rest": "^22.0.1",
82
82
  "@types/handlebars": "^4.0.40",
83
83
  "axios": "^1.13.2",
@@ -86,9 +86,9 @@
86
86
  "glob": "^11.0.3",
87
87
  "gray-matter": "^4.0.3",
88
88
  "handlebars": "^4.7.8",
89
- "inquirer": "^12.10.0",
89
+ "inquirer": "^13.0.1",
90
90
  "js-yaml": "^4.1.0",
91
- "open": "^10.2.0",
91
+ "open": "^11.0.0",
92
92
  "ora": "^9.0.0",
93
93
  "yaml": "^2.3.4"
94
94
  },
@@ -455,12 +455,12 @@ fi
455
455
  # This prevents 27 duplicate comments (see root cause analysis)
456
456
  export SKIP_GITHUB_SYNC=true
457
457
 
458
- # EMERGENCY FIX (v0.25.1): Skip US sync in post-task-completion hook
459
- # CRITICAL: US sync triggers livingDocsSync.syncIncrement() which calls
460
- # syncToExternalTools() without respecting SKIP_GITHUB_SYNC, causing
461
- # Edit/Write operations that trigger new hook chains → infinite recursion → crash
462
- # See: ROOT-CAUSE-ANALYSIS-TODOWRITE-CRASH-2025-11-24.md
463
- export SKIP_US_SYNC=true
458
+ # v0.26.1: SKIP_US_SYNC removed - no longer needed!
459
+ # Automatic US sync restored with multi-layer protection:
460
+ # 1. SKIP_EXTERNAL_SYNC guard at LivingDocsSync layer (prevents recursion)
461
+ # 2. Smart throttle (60s window prevents spam)
462
+ # 3. fs.writeFile() confirmed to NOT trigger hooks (test validated)
463
+ # See: FS-WRITEFILE-HOOK-TEST-RESULTS-2025-11-24.md, ADR-0129
464
464
 
465
465
  # Run consolidated sync (single Node.js process handles ALL operations)
466
466
  if (cd "$PROJECT_ROOT" && node "$CONSOLIDATED_SCRIPT" "$CURRENT_INCREMENT") >> "$DEBUG_LOG" 2>&1; then
@@ -19,7 +19,12 @@
19
19
  # **See**: ADR-0062 (AC Embedding Architecture)
20
20
  #
21
21
 
22
- set -euo pipefail
22
+ # CRITICAL (v0.25.2): NEVER use 'set -e' in hooks - causes Claude Code crashes
23
+ # See: CLAUDE.md Section 9a (Hook Safety Checklist), ADR-0060 (Hook Performance)
24
+ # NOTE: This validation hook explicitly exits with code 1 on failures (controlled behavior)
25
+ set +e # Allow commands to fail without terminating script
26
+ set -u # Fail on undefined variables
27
+ set -o pipefail # Fail if any command in pipeline fails
23
28
 
24
29
  # Get increment path from environment or argument
25
30
  INCREMENT_PATH="${1:-}"
@@ -1,96 +1,67 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * User Story Completion Orchestrator
4
- *
5
- * Orchestrates the sync cascade when user stories become complete:
6
- * 1. Detect newly completed user stories (all ACs satisfied)
7
- * 2. Update living docs for completed USs
8
- * 3. Trigger external tool sync (GitHub/JIRA/ADO)
9
- *
10
- * CRITICAL: This is the bridge between AC-level completion and US-level sync.
11
- * Called by consolidated-sync.js after AC sync completes.
12
- *
13
- * Architecture:
14
- * - Uses USCompletionDetector to find newly completed USs
15
- * - Uses LivingDocsSync to update living docs (which triggers external tools)
16
- * - Non-blocking: Errors logged but don't break workflow
17
- *
18
- * Integration:
19
- * - Called from consolidated-sync.js (OPERATION 6)
20
- * - Runs after AC sync (ensures ACs are up-to-date)
21
- * - Skipped if SKIP_US_SYNC=true (performance optimization)
22
- */
23
-
24
- // Import REAL implementations
25
- import { USCompletionDetector } from '../../../../dist/src/core/us-completion-detector.js';
26
- import { LivingDocsSync } from '../../../../dist/src/core/living-docs/living-docs-sync.js';
27
- import { consoleLogger } from '../vendor/utils/logger.js';
28
-
29
- /**
30
- * Detect and sync newly completed user stories
31
- *
32
- * @param incrementId - Increment ID (e.g., "0053-safe-feature-deletion")
33
- * @returns Result object with success status and sync details
34
- */
35
- export async function syncCompletedUserStories(incrementId) {
2
+ import { USCompletionDetector } from "../../../../dist/src/core/us-completion-detector.js";
3
+ import { LivingDocsSync } from "../../../../dist/src/core/living-docs/living-docs-sync.js";
4
+ import { USSyncThrottle } from "../../../../dist/src/core/us-sync-throttle.js";
5
+ import { consoleLogger } from "../vendor/utils/logger.js";
6
+ async function syncCompletedUserStories(incrementId) {
36
7
  try {
37
- console.log(`\n🎯 [6/6] Detecting completed user stories for ${incrementId}...`);
38
-
8
+ console.log(`
9
+ \u{1F3AF} [6/6] Detecting completed user stories for ${incrementId}...`);
39
10
  const projectRoot = process.cwd();
40
-
41
- // Skip if disabled (performance optimization)
42
- if (process.env.SKIP_US_SYNC === 'true') {
43
- console.log('ℹ️ User story sync skipped (SKIP_US_SYNC=true)');
44
- return { success: true, message: 'Sync skipped', skipped: true };
11
+ const throttle = USSyncThrottle.getInstance({ projectRoot });
12
+ if (throttle.shouldSkipSync(incrementId)) {
13
+ const timeSinceLastSync = throttle.getTimeSinceLastSync(incrementId);
14
+ const secondsRemaining = Math.ceil((6e4 - timeSinceLastSync) / 1e3);
15
+ console.log(`\u23ED\uFE0F US sync throttled (last sync ${Math.floor(timeSinceLastSync / 1e3)}s ago)`);
16
+ console.log(` \u2139\uFE0F Sync will be available in ${secondsRemaining}s`);
17
+ console.log(` \u{1F4A1} Manual sync: /specweave:sync-progress ${incrementId}`);
18
+ return {
19
+ success: true,
20
+ message: "Sync throttled",
21
+ throttled: true,
22
+ skipped: true
23
+ };
45
24
  }
46
-
47
- // 1. Initialize US completion detector
48
25
  const detector = new USCompletionDetector(projectRoot, {
49
26
  logger: consoleLogger
50
27
  });
51
-
52
- // 2. Detect newly completed user stories
53
28
  const newlyCompleted = await detector.getNewlyCompletedUSs(incrementId);
54
-
55
29
  if (newlyCompleted.length === 0) {
56
- console.log('✅ No newly completed user stories detected (no sync needed)');
57
- return { success: true, newlyCompleted: [], message: 'No new completions' };
30
+ console.log("\u2705 No newly completed user stories detected (no sync needed)");
31
+ return {
32
+ success: true,
33
+ newlyCompleted: [],
34
+ message: "No new completions"
35
+ };
58
36
  }
59
-
60
- console.log(`\n🎉 DETECTED ${newlyCompleted.length} NEWLY COMPLETED USER STORIES:`);
37
+ console.log(`
38
+ \u{1F389} DETECTED ${newlyCompleted.length} NEWLY COMPLETED USER STORIES:`);
61
39
  for (const us of newlyCompleted) {
62
40
  console.log(` ${us.usId}: ${us.title} (${us.completedACs}/${us.totalACs} ACs complete)`);
63
41
  }
64
-
65
- // 3. Save completion state (mark USs as complete to prevent re-sync)
66
42
  const allCompletions = await detector.detectCompletions(incrementId);
67
43
  await detector.saveCompletionState(incrementId, allCompletions);
68
-
69
- // 4. Trigger living docs sync (which will sync to external tools)
70
- console.log(`\n📚 Syncing living docs for ${incrementId}...`);
71
-
44
+ console.log(`
45
+ \u{1F4DA} Syncing living docs for ${incrementId}...`);
72
46
  const livingDocsSync = new LivingDocsSync(projectRoot, {
73
47
  logger: consoleLogger
74
48
  });
75
-
76
49
  const syncResult = await livingDocsSync.syncIncrement(incrementId);
77
-
78
50
  if (syncResult.success) {
79
- console.log(`✅ Living docs synced successfully`);
51
+ console.log(`\u2705 Living docs synced successfully`);
80
52
  console.log(` Feature: ${syncResult.featureId}`);
81
53
  console.log(` Files updated: ${syncResult.filesCreated.length + syncResult.filesUpdated.length}`);
82
-
83
- // External tool sync happens automatically inside livingDocsSync.syncIncrement()
84
- // It calls syncToExternalTools() which handles GitHub/JIRA/ADO
85
- console.log(`\n📡 External tool sync completed (GitHub/JIRA/ADO updated if configured)`);
54
+ console.log(`
55
+ \u{1F4E1} External tool sync completed (GitHub/JIRA/ADO updated if configured)`);
56
+ throttle.recordSync(incrementId);
57
+ console.log(` \u23F1\uFE0F Throttle recorded (next sync allowed in 60s)`);
86
58
  } else {
87
- console.warn(`⚠️ Living docs sync had errors (see logs)`);
88
- console.warn(` Errors: ${syncResult.errors.join(', ')}`);
59
+ console.warn(`\u26A0\uFE0F Living docs sync had errors (see logs)`);
60
+ console.warn(` Errors: ${syncResult.errors.join(", ")}`);
89
61
  }
90
-
91
62
  return {
92
63
  success: true,
93
- newlyCompleted: newlyCompleted.map(us => ({
64
+ newlyCompleted: newlyCompleted.map((us) => ({
94
65
  usId: us.usId,
95
66
  title: us.title,
96
67
  totalACs: us.totalACs,
@@ -99,37 +70,39 @@ export async function syncCompletedUserStories(incrementId) {
99
70
  syncResult,
100
71
  message: `${newlyCompleted.length} user stories synced`
101
72
  };
102
-
103
73
  } catch (error) {
104
- // Non-blocking: Log error but don't break workflow
105
- console.error('❌ Error in US completion orchestrator:', error.message);
106
- return { success: false, error: error.message };
74
+ console.error("\u274C Error in US completion orchestrator:", error.message);
75
+ return {
76
+ success: false,
77
+ error: error.message,
78
+ message: "Orchestrator error"
79
+ };
107
80
  }
108
81
  }
109
-
110
- // CLI Interface (for manual testing)
111
82
  const isMainModule = import.meta.url === `file://${process.argv[1]}`;
112
83
  if (isMainModule) {
113
84
  const incrementId = process.argv[2];
114
-
115
85
  if (!incrementId) {
116
- console.error('Usage: node us-completion-orchestrator.js <increment-id>');
117
- console.error('Example: node us-completion-orchestrator.js 0053-safe-feature-deletion');
86
+ console.error("Usage: node us-completion-orchestrator.js <increment-id>");
87
+ console.error("Example: node us-completion-orchestrator.js 0053-safe-feature-deletion");
118
88
  process.exit(1);
119
89
  }
120
-
121
- syncCompletedUserStories(incrementId)
122
- .then(result => {
123
- if (result.success) {
124
- console.log('\n✅ US completion orchestration completed successfully');
125
- process.exit(0);
126
- } else {
127
- console.error('\n❌ US completion orchestration failed');
128
- process.exit(1);
90
+ syncCompletedUserStories(incrementId).then((result) => {
91
+ if (result.success) {
92
+ console.log("\n\u2705 US completion orchestration completed successfully");
93
+ if (result.throttled) {
94
+ console.log(" (Throttled - no sync performed)");
129
95
  }
130
- })
131
- .catch(error => {
132
- console.error('\n Fatal error:', error);
96
+ process.exit(0);
97
+ } else {
98
+ console.error("\n\u274C US completion orchestration failed");
133
99
  process.exit(1);
134
- });
100
+ }
101
+ }).catch((error) => {
102
+ console.error("\n\u274C Fatal error:", error);
103
+ process.exit(1);
104
+ });
135
105
  }
106
+ export {
107
+ syncCompletedUserStories
108
+ };