specweave 1.0.464 → 1.0.466
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/plugins/specweave/lib/vendor/core/ac-test-validator-cli.d.ts +16 -0
- package/dist/plugins/specweave/lib/vendor/core/ac-test-validator-cli.js +139 -0
- package/dist/plugins/specweave/lib/vendor/core/ac-test-validator-cli.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/core/ac-test-validator.d.ts +111 -0
- package/dist/plugins/specweave/lib/vendor/core/ac-test-validator.js +304 -0
- package/dist/plugins/specweave/lib/vendor/core/ac-test-validator.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/ac-status-manager.d.ts +115 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/ac-status-manager.js +359 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/ac-status-manager.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/active-increment-manager.d.ts +121 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/active-increment-manager.js +273 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/active-increment-manager.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.d.ts +72 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.js +237 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/duplicate-detector.d.ts +52 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js +281 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +278 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +925 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/status-auto-transition.d.ts +113 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/status-auto-transition.js +317 -0
- package/dist/plugins/specweave/lib/vendor/core/increment/status-auto-transition.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +442 -0
- package/dist/plugins/specweave/lib/vendor/core/types/increment-metadata.js +246 -0
- package/dist/plugins/specweave/lib/vendor/core/types/increment-metadata.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/core/universal-auto-create.d.ts +64 -0
- package/dist/plugins/specweave/lib/vendor/core/universal-auto-create.js +228 -0
- package/dist/plugins/specweave/lib/vendor/core/universal-auto-create.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/generators/spec/task-parser.d.ts +95 -0
- package/dist/plugins/specweave/lib/vendor/generators/spec/task-parser.js +300 -0
- package/dist/plugins/specweave/lib/vendor/generators/spec/task-parser.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/sync/config.d.ts +73 -0
- package/dist/plugins/specweave/lib/vendor/sync/config.js +132 -0
- package/dist/plugins/specweave/lib/vendor/sync/config.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/sync/github-reconciler.d.ts +163 -0
- package/dist/plugins/specweave/lib/vendor/sync/github-reconciler.js +898 -0
- package/dist/plugins/specweave/lib/vendor/sync/github-reconciler.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/sync/provider-router.d.ts +86 -0
- package/dist/plugins/specweave/lib/vendor/sync/provider-router.js +147 -0
- package/dist/plugins/specweave/lib/vendor/sync/provider-router.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/sync/status-mapper.d.ts +120 -0
- package/dist/plugins/specweave/lib/vendor/sync/status-mapper.js +164 -0
- package/dist/plugins/specweave/lib/vendor/sync/status-mapper.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/utils/auth-helpers.d.ts +151 -0
- package/dist/plugins/specweave/lib/vendor/utils/auth-helpers.js +359 -0
- package/dist/plugins/specweave/lib/vendor/utils/auth-helpers.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/utils/chalk-fallback.d.ts +38 -0
- package/dist/plugins/specweave/lib/vendor/utils/chalk-fallback.js +118 -0
- package/dist/plugins/specweave/lib/vendor/utils/chalk-fallback.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/utils/clean-env.d.ts +47 -0
- package/dist/plugins/specweave/lib/vendor/utils/clean-env.js +63 -0
- package/dist/plugins/specweave/lib/vendor/utils/clean-env.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/utils/credential-masker.d.ts +118 -0
- package/dist/plugins/specweave/lib/vendor/utils/credential-masker.js +275 -0
- package/dist/plugins/specweave/lib/vendor/utils/credential-masker.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/utils/execFileNoThrow.d.ts +99 -0
- package/dist/plugins/specweave/lib/vendor/utils/execFileNoThrow.js +149 -0
- package/dist/plugins/specweave/lib/vendor/utils/execFileNoThrow.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/utils/feature-id-derivation.d.ts +63 -0
- package/dist/plugins/specweave/lib/vendor/utils/feature-id-derivation.js +85 -0
- package/dist/plugins/specweave/lib/vendor/utils/feature-id-derivation.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/utils/fs-native.d.ts +219 -0
- package/dist/plugins/specweave/lib/vendor/utils/fs-native.js +397 -0
- package/dist/plugins/specweave/lib/vendor/utils/fs-native.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/utils/logger.d.ts +56 -0
- package/dist/plugins/specweave/lib/vendor/utils/logger.js +123 -0
- package/dist/plugins/specweave/lib/vendor/utils/logger.js.map +1 -0
- package/dist/plugins/specweave/lib/vendor/utils/translation.d.ts +187 -0
- package/dist/plugins/specweave/lib/vendor/utils/translation.js +414 -0
- package/dist/plugins/specweave/lib/vendor/utils/translation.js.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js +1 -1
- package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.js +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-ac-checkbox-sync.js +2 -2
- package/dist/plugins/specweave-github/lib/github-ac-checkbox-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.js +13 -4
- package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-ac-checkbox-sync.js +1 -1
- package/dist/plugins/specweave-jira/lib/jira-ac-checkbox-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-sync.js +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-sync.js.map +1 -1
- package/dist/src/sync/spec-to-living-docs-sync.js +1 -1
- package/dist/src/sync/spec-to-living-docs-sync.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/lib/vendor/utils/auth-helpers.d.ts +151 -0
- package/plugins/specweave/lib/vendor/utils/auth-helpers.js +359 -0
- package/plugins/specweave/lib/vendor/utils/auth-helpers.js.map +1 -0
- package/plugins/specweave/skills/team-lead/SKILL.md +150 -56
- package/plugins/specweave/skills/team-lead/agents/backend.md +13 -9
- package/plugins/specweave/skills/team-lead/agents/database.md +13 -9
- package/plugins/specweave/skills/team-lead/agents/frontend.md +12 -8
- package/plugins/specweave/skills/team-lead/agents/security.md +13 -9
- package/plugins/specweave/skills/team-lead/agents/testing.md +12 -8
- package/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js +1 -1
- package/plugins/specweave-ado/lib/ado-ac-checkbox-sync.ts +1 -1
- package/plugins/specweave-ado/lib/ado-spec-sync.js +1 -1
- package/plugins/specweave-ado/lib/ado-spec-sync.ts +1 -1
- package/plugins/specweave-github/lib/github-ac-checkbox-sync.js +1 -1
- package/plugins/specweave-github/lib/github-ac-checkbox-sync.ts +2 -2
- package/plugins/specweave-github/lib/github-feature-sync.js +11 -3
- package/plugins/specweave-github/lib/github-feature-sync.ts +13 -4
- package/plugins/specweave-jira/lib/jira-ac-checkbox-sync.js +1 -1
- package/plugins/specweave-jira/lib/jira-ac-checkbox-sync.ts +1 -1
- package/plugins/specweave-jira/lib/jira-spec-sync.js +1 -1
- package/plugins/specweave-jira/lib/jira-spec-sync.ts +1 -1
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Active Increment Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages the `.specweave/state/active-increment.json` file which tracks
|
|
5
|
+
* the currently active increment for status line display.
|
|
6
|
+
*
|
|
7
|
+
* **Critical**: This file MUST be updated whenever increment status changes:
|
|
8
|
+
* - When new increment is created → Set as active
|
|
9
|
+
* - When increment is completed → Clear or set to next active
|
|
10
|
+
* - When increment is paused → Set to next active (or clear if none)
|
|
11
|
+
* - When increment is resumed → Set as active
|
|
12
|
+
*
|
|
13
|
+
* Part of increment 0021: Fix Active Increment Tracking
|
|
14
|
+
*/
|
|
15
|
+
import * as fs from '../../utils/fs-native.js';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import { IncrementStatus } from '../types/increment-metadata.js';
|
|
18
|
+
import { MetadataManager } from './metadata-manager.js';
|
|
19
|
+
/**
|
|
20
|
+
* Active Increment Manager
|
|
21
|
+
*
|
|
22
|
+
* Central authority for managing which increment is currently active.
|
|
23
|
+
*/
|
|
24
|
+
export class ActiveIncrementManager {
|
|
25
|
+
constructor(rootDir = process.cwd()) {
|
|
26
|
+
this.rootDir = rootDir;
|
|
27
|
+
this.stateFile = path.join(rootDir, '.specweave/state/active-increment.json');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get all currently active increment IDs
|
|
31
|
+
* Returns empty array if no increments are active
|
|
32
|
+
*
|
|
33
|
+
* **NEW**: Returns array of ALL active increments (max 2)
|
|
34
|
+
*/
|
|
35
|
+
getActive() {
|
|
36
|
+
try {
|
|
37
|
+
if (!fs.existsSync(this.stateFile)) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
const content = fs.readFileSync(this.stateFile, 'utf-8');
|
|
41
|
+
const state = JSON.parse(content);
|
|
42
|
+
// Backwards compatibility: Support old format
|
|
43
|
+
if (state.id && !state.ids) {
|
|
44
|
+
return [state.id];
|
|
45
|
+
}
|
|
46
|
+
return state.ids || [];
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
// File read/parse error = no active increments
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get the primary active increment (first in list)
|
|
55
|
+
* Returns null if no increments are active
|
|
56
|
+
*
|
|
57
|
+
* This maintains backwards compatibility with code expecting a single ID
|
|
58
|
+
*/
|
|
59
|
+
getPrimary() {
|
|
60
|
+
const active = this.getActive();
|
|
61
|
+
return active.length > 0 ? active[0] : null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Add an increment to the active list
|
|
65
|
+
* Validates that the increment exists and is actually active
|
|
66
|
+
*
|
|
67
|
+
* **NEW**: Adds to list instead of replacing (max 2)
|
|
68
|
+
* @param skipValidation - Skip validation (used during lazy initialization to prevent circular dependency)
|
|
69
|
+
*/
|
|
70
|
+
addActive(incrementId, skipValidation = false) {
|
|
71
|
+
// Validate increment exists and is active (unless skipValidation is true)
|
|
72
|
+
if (!skipValidation) {
|
|
73
|
+
const metadata = MetadataManager.read(incrementId, this.rootDir);
|
|
74
|
+
// Validate increment is actually active
|
|
75
|
+
if (metadata.status !== IncrementStatus.ACTIVE) {
|
|
76
|
+
throw new Error(`Cannot add ${incrementId} as active: status is ${metadata.status}, not active`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Get current active list
|
|
80
|
+
const currentActive = this.getActive();
|
|
81
|
+
// Don't add if already in list
|
|
82
|
+
if (currentActive.includes(incrementId)) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Add to list (max 2)
|
|
86
|
+
const newActive = [...currentActive, incrementId].slice(0, 2);
|
|
87
|
+
// Write state
|
|
88
|
+
const state = {
|
|
89
|
+
ids: newActive,
|
|
90
|
+
lastUpdated: new Date().toISOString()
|
|
91
|
+
};
|
|
92
|
+
this.writeState(state);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Remove an increment from the active list
|
|
96
|
+
*/
|
|
97
|
+
removeActive(incrementId) {
|
|
98
|
+
const currentActive = this.getActive();
|
|
99
|
+
const newActive = currentActive.filter(id => id !== incrementId);
|
|
100
|
+
const state = {
|
|
101
|
+
ids: newActive,
|
|
102
|
+
lastUpdated: new Date().toISOString()
|
|
103
|
+
};
|
|
104
|
+
this.writeState(state);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Set the active increment (legacy method for backwards compatibility)
|
|
108
|
+
* Now delegates to addActive()
|
|
109
|
+
* @param skipValidation - Skip validation (used during lazy initialization to prevent circular dependency)
|
|
110
|
+
*/
|
|
111
|
+
setActive(incrementId, skipValidation = false) {
|
|
112
|
+
this.addActive(incrementId, skipValidation);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Clear all active increments
|
|
116
|
+
*/
|
|
117
|
+
clearActive() {
|
|
118
|
+
const state = {
|
|
119
|
+
ids: [],
|
|
120
|
+
lastUpdated: new Date().toISOString()
|
|
121
|
+
};
|
|
122
|
+
this.writeState(state);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Smart update: Rebuild active list from metadata
|
|
126
|
+
*
|
|
127
|
+
* This is called when:
|
|
128
|
+
* - An increment is completed
|
|
129
|
+
* - An increment is paused
|
|
130
|
+
* - An increment is abandoned
|
|
131
|
+
*
|
|
132
|
+
* Logic:
|
|
133
|
+
* 1. Scan all increments for status=active
|
|
134
|
+
* 2. Update cache to match reality
|
|
135
|
+
* 3. Max 2 increments (sorted by lastActivity)
|
|
136
|
+
*/
|
|
137
|
+
smartUpdate() {
|
|
138
|
+
// Use getByStatus directly to avoid caching circular dependency
|
|
139
|
+
// MetadataManager.getActive() uses this cache, so we can't call it here
|
|
140
|
+
const activeIncrements = MetadataManager.getByStatus(IncrementStatus.ACTIVE);
|
|
141
|
+
if (activeIncrements.length > 0) {
|
|
142
|
+
// Sort by lastActivity (most recent first)
|
|
143
|
+
const sorted = activeIncrements.sort((a, b) => {
|
|
144
|
+
const aTime = new Date(a.lastActivity).getTime();
|
|
145
|
+
const bTime = new Date(b.lastActivity).getTime();
|
|
146
|
+
return bTime - aTime; // Descending
|
|
147
|
+
});
|
|
148
|
+
// Take max 2
|
|
149
|
+
const activeIds = sorted.slice(0, 2).map(m => m.id);
|
|
150
|
+
const state = {
|
|
151
|
+
ids: activeIds,
|
|
152
|
+
lastUpdated: new Date().toISOString()
|
|
153
|
+
};
|
|
154
|
+
this.writeState(state);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
// No active increments
|
|
158
|
+
this.clearActive();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Validate that all active increment pointers are correct
|
|
163
|
+
* Does NOT fix stale pointers - caller should call smartUpdate() if needed
|
|
164
|
+
*
|
|
165
|
+
* Returns true if all valid, false if any invalid
|
|
166
|
+
*/
|
|
167
|
+
validate() {
|
|
168
|
+
const currentActive = this.getActive();
|
|
169
|
+
// No active increments = valid (nothing to validate)
|
|
170
|
+
if (currentActive.length === 0) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
let hasStale = false;
|
|
174
|
+
for (const incrementId of currentActive) {
|
|
175
|
+
try {
|
|
176
|
+
// Check if increment still exists
|
|
177
|
+
const metadata = MetadataManager.read(incrementId);
|
|
178
|
+
// Check if increment is actually active
|
|
179
|
+
if (metadata.status !== IncrementStatus.ACTIVE) {
|
|
180
|
+
// Stale pointer! Mark for fix
|
|
181
|
+
console.warn(`⚠️ Active increment pointer is stale: ${incrementId} is ${metadata.status}`);
|
|
182
|
+
hasStale = true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
// Increment doesn't exist = stale pointer
|
|
187
|
+
console.warn(`⚠️ Active increment pointer is invalid: ${incrementId} not found`);
|
|
188
|
+
hasStale = true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Return false if stale found, but DON'T auto-fix (prevents circular dependency)
|
|
192
|
+
// Caller should call smartUpdate() if needed
|
|
193
|
+
return !hasStale;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Validate and auto-repair the active increment state
|
|
197
|
+
*
|
|
198
|
+
* This is the recommended method to call on startup to ensure
|
|
199
|
+
* the active-increment.json is in sync with metadata.json files.
|
|
200
|
+
*
|
|
201
|
+
* Part of 0168: Status Single Source of Truth
|
|
202
|
+
*
|
|
203
|
+
* @returns Object with validation result and any repairs made
|
|
204
|
+
*/
|
|
205
|
+
validateAndRepair() {
|
|
206
|
+
const details = [];
|
|
207
|
+
const currentActive = this.getActive();
|
|
208
|
+
// Track what needs repair
|
|
209
|
+
const staleIds = [];
|
|
210
|
+
const missingIds = [];
|
|
211
|
+
// Check each currently "active" increment
|
|
212
|
+
for (const incrementId of currentActive) {
|
|
213
|
+
try {
|
|
214
|
+
const metadata = MetadataManager.read(incrementId, this.rootDir);
|
|
215
|
+
if (metadata.status !== IncrementStatus.ACTIVE) {
|
|
216
|
+
staleIds.push(incrementId);
|
|
217
|
+
details.push(`Stale: ${incrementId} is ${metadata.status}, not active`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
missingIds.push(incrementId);
|
|
222
|
+
details.push(`Missing: ${incrementId} not found`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// Check for active increments NOT in our list (missed additions)
|
|
226
|
+
// Note: We use getByStatus directly to scan all metadata files
|
|
227
|
+
// This avoids potential caching issues with getActive()
|
|
228
|
+
const actuallyActive = MetadataManager.getByStatus(IncrementStatus.ACTIVE);
|
|
229
|
+
const actuallyActiveIds = actuallyActive.map(m => m.id);
|
|
230
|
+
const missingFromList = actuallyActiveIds.filter(id => !currentActive.includes(id));
|
|
231
|
+
for (const id of missingFromList) {
|
|
232
|
+
details.push(`Missing from list: ${id} is active but not tracked`);
|
|
233
|
+
}
|
|
234
|
+
// If anything is wrong, repair by calling smartUpdate
|
|
235
|
+
const needsRepair = staleIds.length > 0 || missingIds.length > 0 || missingFromList.length > 0;
|
|
236
|
+
if (needsRepair) {
|
|
237
|
+
this.smartUpdate();
|
|
238
|
+
details.push('Auto-repaired: Rebuilt active increment list from metadata');
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
valid: !needsRepair,
|
|
242
|
+
repaired: needsRepair,
|
|
243
|
+
details
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Write state file atomically (temp file → rename)
|
|
248
|
+
*/
|
|
249
|
+
writeState(state) {
|
|
250
|
+
// Ensure state directory exists
|
|
251
|
+
const stateDir = path.dirname(this.stateFile);
|
|
252
|
+
if (!fs.existsSync(stateDir)) {
|
|
253
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
254
|
+
}
|
|
255
|
+
// Atomic write: temp file → rename
|
|
256
|
+
const tempFile = `${this.stateFile}.tmp`;
|
|
257
|
+
fs.writeFileSync(tempFile, JSON.stringify(state, null, 2), 'utf-8');
|
|
258
|
+
fs.renameSync(tempFile, this.stateFile);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Get state file path (for testing)
|
|
262
|
+
*/
|
|
263
|
+
getStateFilePath() {
|
|
264
|
+
return this.stateFile;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Check if state file exists (for testing)
|
|
268
|
+
*/
|
|
269
|
+
exists() {
|
|
270
|
+
return fs.existsSync(this.stateFile);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=active-increment-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"active-increment-manager.js","sourceRoot":"","sources":["../../../../src/core/increment/active-increment-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-Transition Manager
|
|
3
|
+
*
|
|
4
|
+
* Automatically transitions increment status based on lifecycle events:
|
|
5
|
+
* - spec.md created → BACKLOG → PLANNING
|
|
6
|
+
* - tasks.md created → PLANNING/BACKLOG → ACTIVE
|
|
7
|
+
* - first task started → PLANNING → ACTIVE
|
|
8
|
+
*
|
|
9
|
+
* Part of increment 0039: Ultra-Smart Next Command
|
|
10
|
+
*/
|
|
11
|
+
import { IncrementStatus } from '../types/increment-metadata.js';
|
|
12
|
+
export interface TransitionEvent {
|
|
13
|
+
incrementId: string;
|
|
14
|
+
from: IncrementStatus;
|
|
15
|
+
to: IncrementStatus;
|
|
16
|
+
trigger: 'spec-created' | 'tasks-created' | 'task-started' | 'auto-correct';
|
|
17
|
+
timestamp: string;
|
|
18
|
+
}
|
|
19
|
+
export interface AutoTransitionResult {
|
|
20
|
+
transitioned: boolean;
|
|
21
|
+
from?: IncrementStatus;
|
|
22
|
+
to?: IncrementStatus;
|
|
23
|
+
reason: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Manages automatic status transitions based on increment artifacts
|
|
27
|
+
*/
|
|
28
|
+
export declare class AutoTransitionManager {
|
|
29
|
+
private projectRoot;
|
|
30
|
+
constructor(projectRoot: string);
|
|
31
|
+
/**
|
|
32
|
+
* Handle spec.md creation event
|
|
33
|
+
* BACKLOG → PLANNING
|
|
34
|
+
*/
|
|
35
|
+
handleSpecCreated(incrementId: string): Promise<AutoTransitionResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Handle tasks.md creation event
|
|
38
|
+
* PLANNING/BACKLOG → ACTIVE
|
|
39
|
+
*
|
|
40
|
+
* CRITICAL FIX (2025-11-24): Also trigger living docs sync if feature_id is null
|
|
41
|
+
* In single-prompt scenarios, increment may be created with "active" status
|
|
42
|
+
* directly, bypassing the normal transition flow. When tasks.md is created,
|
|
43
|
+
* we know spec.md exists and can safely sync living docs.
|
|
44
|
+
*/
|
|
45
|
+
handleTasksCreated(incrementId: string): Promise<AutoTransitionResult>;
|
|
46
|
+
/**
|
|
47
|
+
* Trigger living docs sync if feature_id is missing
|
|
48
|
+
*
|
|
49
|
+
* Called when tasks.md is created but increment is already ACTIVE.
|
|
50
|
+
* At this point spec.md exists, so sync will succeed.
|
|
51
|
+
*/
|
|
52
|
+
private triggerLivingDocsSyncIfNeeded;
|
|
53
|
+
/**
|
|
54
|
+
* Handle first task started event
|
|
55
|
+
* PLANNING → ACTIVE
|
|
56
|
+
*/
|
|
57
|
+
handleTaskStarted(incrementId: string): Promise<AutoTransitionResult>;
|
|
58
|
+
/**
|
|
59
|
+
* Detect increment phase based on artifacts (spec.md, plan.md, tasks.md)
|
|
60
|
+
*/
|
|
61
|
+
detectPhase(incrementId: string): Promise<IncrementStatus>;
|
|
62
|
+
/**
|
|
63
|
+
* Auto-correct status based on artifacts
|
|
64
|
+
* Useful for fixing increments with invalid "planned"/"planning" statuses
|
|
65
|
+
*/
|
|
66
|
+
autoCorrect(incrementId: string, force?: boolean): Promise<AutoTransitionResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Get transition event for logging
|
|
69
|
+
*/
|
|
70
|
+
createTransitionEvent(incrementId: string, from: IncrementStatus, to: IncrementStatus, trigger: TransitionEvent['trigger']): TransitionEvent;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=auto-transition-manager.d.ts.map
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-Transition Manager
|
|
3
|
+
*
|
|
4
|
+
* Automatically transitions increment status based on lifecycle events:
|
|
5
|
+
* - spec.md created → BACKLOG → PLANNING
|
|
6
|
+
* - tasks.md created → PLANNING/BACKLOG → ACTIVE
|
|
7
|
+
* - first task started → PLANNING → ACTIVE
|
|
8
|
+
*
|
|
9
|
+
* Part of increment 0039: Ultra-Smart Next Command
|
|
10
|
+
*/
|
|
11
|
+
import { IncrementStatus, validateTransition } from '../types/increment-metadata.js';
|
|
12
|
+
import { MetadataManager } from './metadata-manager.js';
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
/**
|
|
16
|
+
* Manages automatic status transitions based on increment artifacts
|
|
17
|
+
*/
|
|
18
|
+
export class AutoTransitionManager {
|
|
19
|
+
constructor(projectRoot) {
|
|
20
|
+
this.projectRoot = projectRoot;
|
|
21
|
+
// Note: MetadataManager uses process.cwd() internally
|
|
22
|
+
// In production, this manager should be instantiated from the project root
|
|
23
|
+
// In tests, we use integration tests with real file system
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Handle spec.md creation event
|
|
27
|
+
* BACKLOG → PLANNING
|
|
28
|
+
*/
|
|
29
|
+
async handleSpecCreated(incrementId) {
|
|
30
|
+
try {
|
|
31
|
+
const metadata = MetadataManager.read(incrementId);
|
|
32
|
+
// Only transition from BACKLOG to PLANNING
|
|
33
|
+
if (metadata.status === IncrementStatus.BACKLOG) {
|
|
34
|
+
validateTransition(metadata.status, IncrementStatus.PLANNING);
|
|
35
|
+
MetadataManager.updateStatus(incrementId, IncrementStatus.PLANNING);
|
|
36
|
+
return {
|
|
37
|
+
transitioned: true,
|
|
38
|
+
from: IncrementStatus.BACKLOG,
|
|
39
|
+
to: IncrementStatus.PLANNING,
|
|
40
|
+
reason: 'spec.md created - planning phase started'
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
transitioned: false,
|
|
45
|
+
reason: `Already in ${metadata.status} - no transition needed`
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return {
|
|
50
|
+
transitioned: false,
|
|
51
|
+
reason: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Handle tasks.md creation event
|
|
57
|
+
* PLANNING/BACKLOG → ACTIVE
|
|
58
|
+
*
|
|
59
|
+
* CRITICAL FIX (2025-11-24): Also trigger living docs sync if feature_id is null
|
|
60
|
+
* In single-prompt scenarios, increment may be created with "active" status
|
|
61
|
+
* directly, bypassing the normal transition flow. When tasks.md is created,
|
|
62
|
+
* we know spec.md exists and can safely sync living docs.
|
|
63
|
+
*/
|
|
64
|
+
async handleTasksCreated(incrementId) {
|
|
65
|
+
try {
|
|
66
|
+
const metadata = MetadataManager.read(incrementId);
|
|
67
|
+
// Transition to ACTIVE from PLANNING or BACKLOG
|
|
68
|
+
if (metadata.status === IncrementStatus.PLANNING || metadata.status === IncrementStatus.BACKLOG) {
|
|
69
|
+
const from = metadata.status;
|
|
70
|
+
validateTransition(from, IncrementStatus.ACTIVE);
|
|
71
|
+
MetadataManager.updateStatus(incrementId, IncrementStatus.ACTIVE);
|
|
72
|
+
// Note: updateStatus() triggers StatusChangeSyncTrigger which handles living docs sync
|
|
73
|
+
return {
|
|
74
|
+
transitioned: true,
|
|
75
|
+
from,
|
|
76
|
+
to: IncrementStatus.ACTIVE,
|
|
77
|
+
reason: 'tasks.md created - execution phase started'
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// CRITICAL FIX: Even if already ACTIVE, check if living docs need sync
|
|
81
|
+
// This handles single-prompt scenarios where increment was created with "active" status
|
|
82
|
+
if (metadata.status === IncrementStatus.ACTIVE) {
|
|
83
|
+
await this.triggerLivingDocsSyncIfNeeded(incrementId);
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
transitioned: false,
|
|
87
|
+
reason: `Already in ${metadata.status} - no transition needed`
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
return {
|
|
92
|
+
transitioned: false,
|
|
93
|
+
reason: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Trigger living docs sync if feature_id is missing
|
|
99
|
+
*
|
|
100
|
+
* Called when tasks.md is created but increment is already ACTIVE.
|
|
101
|
+
* At this point spec.md exists, so sync will succeed.
|
|
102
|
+
*/
|
|
103
|
+
async triggerLivingDocsSyncIfNeeded(incrementId) {
|
|
104
|
+
try {
|
|
105
|
+
const metadataPath = path.join(this.projectRoot, '.specweave/increments', incrementId, 'metadata.json');
|
|
106
|
+
if (!fs.existsSync(metadataPath)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const content = fs.readFileSync(metadataPath, 'utf-8');
|
|
110
|
+
const metadata = JSON.parse(content);
|
|
111
|
+
// Only sync if feature_id is missing
|
|
112
|
+
if (!metadata.feature_id) {
|
|
113
|
+
console.log(`📚 Increment ${incrementId} missing feature_id - triggering living docs sync...`);
|
|
114
|
+
// Dynamic import to avoid circular dependency
|
|
115
|
+
const { LivingDocsSync } = await import('../living-docs/living-docs-sync.js');
|
|
116
|
+
const sync = new LivingDocsSync(this.projectRoot);
|
|
117
|
+
const result = await sync.syncIncrement(incrementId);
|
|
118
|
+
if (result.success) {
|
|
119
|
+
console.log(`✅ Living docs synced for ${incrementId} → ${result.featureId}`);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
console.warn(`⚠️ Living docs sync completed with errors for ${incrementId}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
// Non-fatal - log warning but don't fail the transition
|
|
128
|
+
console.warn(`⚠️ Failed to check/trigger living docs sync: ${error}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Handle first task started event
|
|
133
|
+
* PLANNING → ACTIVE
|
|
134
|
+
*/
|
|
135
|
+
async handleTaskStarted(incrementId) {
|
|
136
|
+
try {
|
|
137
|
+
const metadata = MetadataManager.read(incrementId);
|
|
138
|
+
// Force transition to ACTIVE if still in PLANNING
|
|
139
|
+
if (metadata.status === IncrementStatus.PLANNING) {
|
|
140
|
+
validateTransition(metadata.status, IncrementStatus.ACTIVE);
|
|
141
|
+
MetadataManager.updateStatus(incrementId, IncrementStatus.ACTIVE);
|
|
142
|
+
return {
|
|
143
|
+
transitioned: true,
|
|
144
|
+
from: IncrementStatus.PLANNING,
|
|
145
|
+
to: IncrementStatus.ACTIVE,
|
|
146
|
+
reason: 'task execution started - moved to active'
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
transitioned: false,
|
|
151
|
+
reason: `Already in ${metadata.status} - no transition needed`
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
return {
|
|
156
|
+
transitioned: false,
|
|
157
|
+
reason: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Detect increment phase based on artifacts (spec.md, plan.md, tasks.md)
|
|
163
|
+
*/
|
|
164
|
+
async detectPhase(incrementId) {
|
|
165
|
+
const incrementPath = path.join(this.projectRoot, '.specweave/increments', incrementId);
|
|
166
|
+
const hasSpec = fs.existsSync(path.join(incrementPath, 'spec.md'));
|
|
167
|
+
const hasPlan = fs.existsSync(path.join(incrementPath, 'plan.md'));
|
|
168
|
+
const hasTasks = fs.existsSync(path.join(incrementPath, 'tasks.md'));
|
|
169
|
+
// Artifact-based phase detection
|
|
170
|
+
if (hasTasks) {
|
|
171
|
+
return IncrementStatus.ACTIVE; // Tasks exist → execution phase
|
|
172
|
+
}
|
|
173
|
+
else if (hasSpec || hasPlan) {
|
|
174
|
+
return IncrementStatus.PLANNING; // Spec/plan exist → planning phase
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
return IncrementStatus.BACKLOG; // Nothing exists → backlog
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Auto-correct status based on artifacts
|
|
182
|
+
* Useful for fixing increments with invalid "planned"/"planning" statuses
|
|
183
|
+
*/
|
|
184
|
+
async autoCorrect(incrementId, force = false) {
|
|
185
|
+
try {
|
|
186
|
+
const metadata = MetadataManager.read(incrementId);
|
|
187
|
+
const detectedPhase = await this.detectPhase(incrementId);
|
|
188
|
+
// Check for status mismatch
|
|
189
|
+
if (metadata.status !== detectedPhase) {
|
|
190
|
+
// Validate transition is allowed
|
|
191
|
+
try {
|
|
192
|
+
validateTransition(metadata.status, detectedPhase);
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
if (!force) {
|
|
196
|
+
return {
|
|
197
|
+
transitioned: false,
|
|
198
|
+
reason: `Cannot auto-correct: ${error instanceof Error ? error.message : String(error)}`
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// Force mode: skip validation
|
|
202
|
+
}
|
|
203
|
+
const from = metadata.status;
|
|
204
|
+
MetadataManager.updateStatus(incrementId, detectedPhase);
|
|
205
|
+
return {
|
|
206
|
+
transitioned: true,
|
|
207
|
+
from,
|
|
208
|
+
to: detectedPhase,
|
|
209
|
+
reason: `Auto-corrected based on artifacts (${force ? 'forced' : 'validated'})`
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
transitioned: false,
|
|
214
|
+
reason: `Status already correct (${metadata.status})`
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
return {
|
|
219
|
+
transitioned: false,
|
|
220
|
+
reason: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Get transition event for logging
|
|
226
|
+
*/
|
|
227
|
+
createTransitionEvent(incrementId, from, to, trigger) {
|
|
228
|
+
return {
|
|
229
|
+
incrementId,
|
|
230
|
+
from,
|
|
231
|
+
to,
|
|
232
|
+
trigger,
|
|
233
|
+
timestamp: new Date().toISOString()
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=auto-transition-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-transition-manager.js","sourceRoot":"","sources":["../../../../src/core/increment/auto-transition-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACrF,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAiB7B;;GAEG;AACH,MAAM,OAAO,qBAAqB;IAGhC,YAAY,WAAmB;QAC7B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,sDAAsD;QACtD,2EAA2E;QAC3E,2DAA2D;IAC7D,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,iBAAiB,CAAC,WAAmB;QACzC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAEnD,2CAA2C;YAC3C,IAAI,QAAQ,CAAC,MAAM,KAAK,eAAe,CAAC,OAAO,EAAE,CAAC;gBAChD,kBAAkB,CAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;gBAE9D,eAAe,CAAC,YAAY,CAAC,WAAW,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;gBAEpE,OAAO;oBACL,YAAY,EAAE,IAAI;oBAClB,IAAI,EAAE,eAAe,CAAC,OAAO;oBAC7B,EAAE,EAAE,eAAe,CAAC,QAAQ;oBAC5B,MAAM,EAAE,0CAA0C;iBACnD,CAAC;YACJ,CAAC;YAED,OAAO;gBACL,YAAY,EAAE,KAAK;gBACnB,MAAM,EAAE,cAAc,QAAQ,CAAC,MAAM,yBAAyB;aAC/D,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,YAAY,EAAE,KAAK;gBACnB,MAAM,EAAE,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;aAC3E,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,kBAAkB,CAAC,WAAmB;QAC1C,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAEnD,gDAAgD;YAChD,IAAI,QAAQ,CAAC,MAAM,KAAK,eAAe,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,eAAe,CAAC,OAAO,EAAE,CAAC;gBAChG,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC;gBAC7B,kBAAkB,CAAC,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;gBAEjD,eAAe,CAAC,YAAY,CAAC,WAAW,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;gBAClE,uFAAuF;gBAEvF,OAAO;oBACL,YAAY,EAAE,IAAI;oBAClB,IAAI;oBACJ,EAAE,EAAE,eAAe,CAAC,MAAM;oBAC1B,MAAM,EAAE,4CAA4C;iBACrD,CAAC;YACJ,CAAC;YAED,uEAAuE;YACvE,wFAAwF;YACxF,IAAI,QAAQ,CAAC,MAAM,KAAK,eAAe,CAAC,MAAM,EAAE,CAAC;gBAC/C,MAAM,IAAI,CAAC,6BAA6B,CAAC,WAAW,CAAC,CAAC;YACxD,CAAC;YAED,OAAO;gBACL,YAAY,EAAE,KAAK;gBACnB,MAAM,EAAE,cAAc,QAAQ,CAAC,MAAM,yBAAyB;aAC/D,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,YAAY,EAAE,KAAK;gBACnB,MAAM,EAAE,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;aAC3E,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,6BAA6B,CAAC,WAAmB;QAC7D,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAC5B,IAAI,CAAC,WAAW,EAChB,uBAAuB,EACvB,WAAW,EACX,eAAe,CAChB,CAAC;YAEF,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;gBACjC,OAAO;YACT,CAAC;YAED,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;YACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAErC,qCAAqC;YACrC,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;gBACzB,OAAO,CAAC,GAAG,CAAC,gBAAgB,WAAW,sDAAsD,CAAC,CAAC;gBAE/F,8CAA8C;gBAC9C,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,oCAAoC,CAAC,CAAC;gBAC9E,MAAM,IAAI,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBAElD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;gBAErD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACnB,OAAO,CAAC,GAAG,CAAC,4BAA4B,WAAW,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;gBAC/E,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,IAAI,CAAC,kDAAkD,WAAW,EAAE,CAAC,CAAC;gBAChF,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,wDAAwD;YACxD,OAAO,CAAC,IAAI,CAAC,iDAAiD,KAAK,EAAE,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,iBAAiB,CAAC,WAAmB;QACzC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAEnD,kDAAkD;YAClD,IAAI,QAAQ,CAAC,MAAM,KAAK,eAAe,CAAC,QAAQ,EAAE,CAAC;gBACjD,kBAAkB,CAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;gBAE5D,eAAe,CAAC,YAAY,CAAC,WAAW,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;gBAElE,OAAO;oBACL,YAAY,EAAE,IAAI;oBAClB,IAAI,EAAE,eAAe,CAAC,QAAQ;oBAC9B,EAAE,EAAE,eAAe,CAAC,MAAM;oBAC1B,MAAM,EAAE,0CAA0C;iBACnD,CAAC;YACJ,CAAC;YAED,OAAO;gBACL,YAAY,EAAE,KAAK;gBACnB,MAAM,EAAE,cAAc,QAAQ,CAAC,MAAM,yBAAyB;aAC/D,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,YAAY,EAAE,KAAK;gBACnB,MAAM,EAAE,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;aAC3E,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CAAC,WAAmB;QACnC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,uBAAuB,EAAE,WAAW,CAAC,CAAC;QAExF,MAAM,OAAO,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC;QACnE,MAAM,OAAO,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC;QACnE,MAAM,QAAQ,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC;QAErE,iCAAiC;QACjC,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,eAAe,CAAC,MAAM,CAAC,CAAE,gCAAgC;QAClE,CAAC;aAAM,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;YAC9B,OAAO,eAAe,CAAC,QAAQ,CAAC,CAAE,mCAAmC;QACvE,CAAC;aAAM,CAAC;YACN,OAAO,eAAe,CAAC,OAAO,CAAC,CAAE,2BAA2B;QAC9D,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,WAAW,CAAC,WAAmB,EAAE,QAAiB,KAAK;QAC3D,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACnD,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;YAE1D,4BAA4B;YAC5B,IAAI,QAAQ,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;gBACtC,iCAAiC;gBACjC,IAAI,CAAC;oBACH,kBAAkB,CAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;gBACrD,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,IAAI,CAAC,KAAK,EAAE,CAAC;wBACX,OAAO;4BACL,YAAY,EAAE,KAAK;4BACnB,MAAM,EAAE,wBAAwB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;yBACzF,CAAC;oBACJ,CAAC;oBACD,8BAA8B;gBAChC,CAAC;gBAED,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC;gBAC7B,eAAe,CAAC,YAAY,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;gBAEzD,OAAO;oBACL,YAAY,EAAE,IAAI;oBAClB,IAAI;oBACJ,EAAE,EAAE,aAAa;oBACjB,MAAM,EAAE,sCAAsC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,GAAG;iBAChF,CAAC;YACJ,CAAC;YAED,OAAO;gBACL,YAAY,EAAE,KAAK;gBACnB,MAAM,EAAE,2BAA2B,QAAQ,CAAC,MAAM,GAAG;aACtD,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,YAAY,EAAE,KAAK;gBACnB,MAAM,EAAE,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;aAC3E,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,qBAAqB,CACnB,WAAmB,EACnB,IAAqB,EACrB,EAAmB,EACnB,OAAmC;QAEnC,OAAO;YACL,WAAW;YACX,IAAI;YACJ,EAAE;YACF,OAAO;YACP,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duplicate Detector - Scan filesystem and detect duplicate increments
|
|
3
|
+
*
|
|
4
|
+
* Detects increments that exist in multiple locations (active, archive, abandoned)
|
|
5
|
+
* or have the same increment number with different names.
|
|
6
|
+
*
|
|
7
|
+
* Part of increment 0033: Duplicate Increment Prevention System
|
|
8
|
+
*/
|
|
9
|
+
import { IncrementStatus } from '../types/increment-metadata.js';
|
|
10
|
+
/**
|
|
11
|
+
* Report of all duplicates found
|
|
12
|
+
*/
|
|
13
|
+
export interface DuplicateReport {
|
|
14
|
+
duplicates: Duplicate[];
|
|
15
|
+
totalChecked: number;
|
|
16
|
+
duplicateCount: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A duplicate increment (same number in multiple locations or with different names)
|
|
20
|
+
*/
|
|
21
|
+
export interface Duplicate {
|
|
22
|
+
incrementNumber: string;
|
|
23
|
+
locations: IncrementLocation[];
|
|
24
|
+
recommendedWinner: IncrementLocation;
|
|
25
|
+
losingVersions: IncrementLocation[];
|
|
26
|
+
resolutionReason: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Information about an increment location
|
|
30
|
+
*/
|
|
31
|
+
export interface IncrementLocation {
|
|
32
|
+
path: string;
|
|
33
|
+
name: string;
|
|
34
|
+
status: IncrementStatus;
|
|
35
|
+
lastActivity: string;
|
|
36
|
+
fileCount: number;
|
|
37
|
+
totalSize: number;
|
|
38
|
+
hasReports: boolean;
|
|
39
|
+
hasGitHubLink: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Scan all increment folders and detect duplicates
|
|
43
|
+
*/
|
|
44
|
+
export declare function detectAllDuplicates(rootDir: string): Promise<DuplicateReport>;
|
|
45
|
+
/**
|
|
46
|
+
* Detect duplicates for a specific increment number
|
|
47
|
+
*
|
|
48
|
+
* Returns ALL increments that have the given number, even if there's only one.
|
|
49
|
+
* This is used for validation before creating a new increment.
|
|
50
|
+
*/
|
|
51
|
+
export declare function detectDuplicatesByNumber(incrementNumber: string, rootDir: string): Promise<IncrementLocation[]>;
|
|
52
|
+
//# sourceMappingURL=duplicate-detector.d.ts.map
|