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,281 @@
|
|
|
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 * as fs from '../../utils/fs-native.js';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import { glob } from 'glob';
|
|
12
|
+
/**
|
|
13
|
+
* Scan all increment folders and detect duplicates
|
|
14
|
+
*/
|
|
15
|
+
export async function detectAllDuplicates(rootDir) {
|
|
16
|
+
const incrementsDir = path.join(rootDir, '.specweave', 'increments');
|
|
17
|
+
// Check if increments directory exists
|
|
18
|
+
if (!await fs.pathExists(incrementsDir)) {
|
|
19
|
+
return {
|
|
20
|
+
duplicates: [],
|
|
21
|
+
totalChecked: 0,
|
|
22
|
+
duplicateCount: 0
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// Scan all locations in parallel
|
|
26
|
+
const [active, archived, abandoned] = await Promise.all([
|
|
27
|
+
scanDirectory(incrementsDir, false),
|
|
28
|
+
scanDirectory(path.join(incrementsDir, '_archive'), false),
|
|
29
|
+
scanDirectory(path.join(incrementsDir, '_abandoned'), false)
|
|
30
|
+
]);
|
|
31
|
+
// Group by increment number
|
|
32
|
+
const byNumber = new Map();
|
|
33
|
+
const allIncrements = [...active, ...archived, ...abandoned];
|
|
34
|
+
for (const inc of allIncrements) {
|
|
35
|
+
const number = extractIncrementNumber(inc.name);
|
|
36
|
+
if (!number)
|
|
37
|
+
continue; // Skip non-increment folders
|
|
38
|
+
if (!byNumber.has(number)) {
|
|
39
|
+
byNumber.set(number, []);
|
|
40
|
+
}
|
|
41
|
+
byNumber.get(number).push(inc);
|
|
42
|
+
}
|
|
43
|
+
// Find duplicates (increment number exists in >1 location or >1 name)
|
|
44
|
+
const duplicates = [];
|
|
45
|
+
byNumber.forEach((locations, number) => {
|
|
46
|
+
if (locations.length > 1) {
|
|
47
|
+
// Duplicate found!
|
|
48
|
+
const winner = selectWinner(locations);
|
|
49
|
+
duplicates.push({
|
|
50
|
+
incrementNumber: number,
|
|
51
|
+
locations,
|
|
52
|
+
recommendedWinner: winner,
|
|
53
|
+
losingVersions: locations.filter(l => l !== winner),
|
|
54
|
+
resolutionReason: explainWinner(winner, locations)
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
duplicates,
|
|
60
|
+
totalChecked: allIncrements.length,
|
|
61
|
+
duplicateCount: duplicates.length
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Detect duplicates for a specific increment number
|
|
66
|
+
*
|
|
67
|
+
* Returns ALL increments that have the given number, even if there's only one.
|
|
68
|
+
* This is used for validation before creating a new increment.
|
|
69
|
+
*/
|
|
70
|
+
export async function detectDuplicatesByNumber(incrementNumber, rootDir) {
|
|
71
|
+
const incrementsDir = path.join(rootDir, '.specweave', 'increments');
|
|
72
|
+
// Check if increments directory exists
|
|
73
|
+
if (!await fs.pathExists(incrementsDir)) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
// Normalize increment number (pad to 4 digits)
|
|
77
|
+
const normalizedNumber = incrementNumber.padStart(4, '0');
|
|
78
|
+
// Scan all locations in parallel
|
|
79
|
+
const [active, archived, abandoned] = await Promise.all([
|
|
80
|
+
scanDirectory(incrementsDir, false),
|
|
81
|
+
scanDirectory(path.join(incrementsDir, '_archive'), false),
|
|
82
|
+
scanDirectory(path.join(incrementsDir, '_abandoned'), false)
|
|
83
|
+
]);
|
|
84
|
+
// Combine all increments and filter by number
|
|
85
|
+
const allIncrements = [...active, ...archived, ...abandoned];
|
|
86
|
+
const matchingIncrements = allIncrements.filter(inc => {
|
|
87
|
+
const number = extractIncrementNumber(inc.name);
|
|
88
|
+
return number === normalizedNumber;
|
|
89
|
+
});
|
|
90
|
+
return matchingIncrements;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Scan a directory for increment folders
|
|
94
|
+
*/
|
|
95
|
+
async function scanDirectory(dir, throwOnError = false) {
|
|
96
|
+
try {
|
|
97
|
+
// Check if directory exists
|
|
98
|
+
if (!await fs.pathExists(dir)) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
// Find all directories matching increment pattern (####-*)
|
|
102
|
+
const pattern = path.join(dir, '[0-9][0-9][0-9][0-9]-*');
|
|
103
|
+
const allPaths = await glob(pattern);
|
|
104
|
+
const locations = [];
|
|
105
|
+
for (const incPath of allPaths) {
|
|
106
|
+
try {
|
|
107
|
+
const stats = await fs.stat(incPath);
|
|
108
|
+
if (!stats.isDirectory())
|
|
109
|
+
continue;
|
|
110
|
+
const name = path.basename(incPath);
|
|
111
|
+
// Skip nested .specweave folders
|
|
112
|
+
if (incPath.includes('.specweave/increments/.specweave')) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
// Read metadata
|
|
116
|
+
const metadataPath = path.join(incPath, 'metadata.json');
|
|
117
|
+
let metadata = {
|
|
118
|
+
status: 'unknown',
|
|
119
|
+
lastActivity: new Date(stats.mtime).toISOString()
|
|
120
|
+
};
|
|
121
|
+
if (await fs.pathExists(metadataPath)) {
|
|
122
|
+
try {
|
|
123
|
+
metadata = await fs.readJson(metadataPath);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
// Skip corrupted metadata (will be caught by validation)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Count files recursively
|
|
130
|
+
const allFiles = await glob(path.join(incPath, '**/*'));
|
|
131
|
+
const fileCount = allFiles.filter(async (f) => {
|
|
132
|
+
try {
|
|
133
|
+
const fstats = await fs.stat(f);
|
|
134
|
+
return fstats.isFile();
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}).length;
|
|
140
|
+
// Calculate total size
|
|
141
|
+
let totalSize = 0;
|
|
142
|
+
for (const file of allFiles) {
|
|
143
|
+
try {
|
|
144
|
+
const fstats = await fs.stat(file);
|
|
145
|
+
if (fstats.isFile()) {
|
|
146
|
+
totalSize += fstats.size;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Skip files we can't read
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Check for reports folder
|
|
154
|
+
const reportsDir = path.join(incPath, 'reports');
|
|
155
|
+
const hasReports = await fs.pathExists(reportsDir);
|
|
156
|
+
// Check for GitHub link in metadata
|
|
157
|
+
const hasGitHubLink = metadata.github && metadata.github.issue;
|
|
158
|
+
locations.push({
|
|
159
|
+
path: incPath,
|
|
160
|
+
name,
|
|
161
|
+
status: metadata.status || 'unknown',
|
|
162
|
+
lastActivity: metadata.lastActivity || new Date(stats.mtime).toISOString(),
|
|
163
|
+
fileCount,
|
|
164
|
+
totalSize,
|
|
165
|
+
hasReports,
|
|
166
|
+
hasGitHubLink
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
// Skip increments we can't read
|
|
171
|
+
if (throwOnError)
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return locations;
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
if (throwOnError)
|
|
179
|
+
throw error;
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Extract increment number from increment name (e.g., "0001-feature" → "0001")
|
|
185
|
+
*/
|
|
186
|
+
function extractIncrementNumber(name) {
|
|
187
|
+
const match = name.match(/^(\d{3,4})E?-/);
|
|
188
|
+
return match ? match[1].padStart(4, '0') : null;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Status priority for winner selection (higher = better)
|
|
192
|
+
*/
|
|
193
|
+
const STATUS_PRIORITY = {
|
|
194
|
+
active: 5,
|
|
195
|
+
completed: 4,
|
|
196
|
+
paused: 3,
|
|
197
|
+
backlog: 2,
|
|
198
|
+
abandoned: 1,
|
|
199
|
+
unknown: 0
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* Location priority for winner selection (higher = better)
|
|
203
|
+
*/
|
|
204
|
+
const LOCATION_PRIORITY = {
|
|
205
|
+
active: 3,
|
|
206
|
+
archive: 2,
|
|
207
|
+
abandoned: 1
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* Get location type from path
|
|
211
|
+
*/
|
|
212
|
+
function getLocationType(loc) {
|
|
213
|
+
if (loc.path.includes('_abandoned'))
|
|
214
|
+
return 'abandoned';
|
|
215
|
+
if (loc.path.includes('_archive'))
|
|
216
|
+
return 'archive';
|
|
217
|
+
return 'active';
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Get status priority for a location
|
|
221
|
+
*/
|
|
222
|
+
function getStatusPriority(loc) {
|
|
223
|
+
return STATUS_PRIORITY[loc.status] ?? 0;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Get location priority for a location
|
|
227
|
+
*/
|
|
228
|
+
function getLocationPriority(loc) {
|
|
229
|
+
return LOCATION_PRIORITY[getLocationType(loc)];
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Select winning version based on priority rules
|
|
233
|
+
*/
|
|
234
|
+
function selectWinner(locations) {
|
|
235
|
+
const sorted = [...locations].sort((a, b) => {
|
|
236
|
+
// 1. Status priority
|
|
237
|
+
const statusDiff = getStatusPriority(b) - getStatusPriority(a);
|
|
238
|
+
if (statusDiff !== 0)
|
|
239
|
+
return statusDiff;
|
|
240
|
+
// 2. Most recent activity
|
|
241
|
+
const timeDiff = new Date(b.lastActivity).getTime() - new Date(a.lastActivity).getTime();
|
|
242
|
+
if (timeDiff !== 0)
|
|
243
|
+
return timeDiff;
|
|
244
|
+
// 3. Most complete (more files)
|
|
245
|
+
const fileDiff = b.fileCount - a.fileCount;
|
|
246
|
+
if (fileDiff !== 0)
|
|
247
|
+
return fileDiff;
|
|
248
|
+
// 4. Location preference (active > _archive > _abandoned)
|
|
249
|
+
return getLocationPriority(b) - getLocationPriority(a);
|
|
250
|
+
});
|
|
251
|
+
return sorted[0];
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Explain why this version won
|
|
255
|
+
*/
|
|
256
|
+
function explainWinner(winner, all) {
|
|
257
|
+
const reasons = [];
|
|
258
|
+
const winnerStatusPriority = getStatusPriority(winner);
|
|
259
|
+
const winnerTime = new Date(winner.lastActivity).getTime();
|
|
260
|
+
const winnerLocation = getLocationType(winner);
|
|
261
|
+
const winnerLocationPriority = getLocationPriority(winner);
|
|
262
|
+
// Check status
|
|
263
|
+
if (all.some(loc => loc !== winner && getStatusPriority(loc) < winnerStatusPriority)) {
|
|
264
|
+
reasons.push(`Higher status (${winner.status})`);
|
|
265
|
+
}
|
|
266
|
+
// Check recency
|
|
267
|
+
if (all.some(loc => loc !== winner && new Date(loc.lastActivity).getTime() < winnerTime)) {
|
|
268
|
+
const date = new Date(winner.lastActivity).toISOString().split('T')[0];
|
|
269
|
+
reasons.push(`Most recent activity (${date})`);
|
|
270
|
+
}
|
|
271
|
+
// Check completeness
|
|
272
|
+
if (all.some(loc => loc !== winner && loc.fileCount < winner.fileCount)) {
|
|
273
|
+
reasons.push(`Most complete (${winner.fileCount} files)`);
|
|
274
|
+
}
|
|
275
|
+
// Check location
|
|
276
|
+
if (all.some(loc => loc !== winner && getLocationPriority(loc) < winnerLocationPriority)) {
|
|
277
|
+
reasons.push(`In ${winnerLocation} location`);
|
|
278
|
+
}
|
|
279
|
+
return reasons.length > 0 ? reasons.join(', ') : 'Default selection';
|
|
280
|
+
}
|
|
281
|
+
//# sourceMappingURL=duplicate-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duplicate-detector.js","sourceRoot":"","sources":["../../../../src/core/increment/duplicate-detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata Manager
|
|
3
|
+
*
|
|
4
|
+
* Handles CRUD operations for increment metadata (status, type, timestamps).
|
|
5
|
+
* Part of increment 0007: Smart Status Management
|
|
6
|
+
*/
|
|
7
|
+
import { IncrementMetadata, IncrementMetadataExtended, IncrementMetadataV2, IncrementStatus, IncrementType, SyncTarget, PrRef } from '../types/increment-metadata.js';
|
|
8
|
+
import { Logger } from '../../utils/logger.js';
|
|
9
|
+
/**
|
|
10
|
+
* Error thrown when metadata operations fail
|
|
11
|
+
*/
|
|
12
|
+
export declare class MetadataError extends Error {
|
|
13
|
+
incrementId: string;
|
|
14
|
+
cause?: Error | undefined;
|
|
15
|
+
constructor(message: string, incrementId: string, cause?: Error | undefined);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Metadata Manager
|
|
19
|
+
*
|
|
20
|
+
* Provides CRUD operations and queries for increment metadata
|
|
21
|
+
*/
|
|
22
|
+
export declare class MetadataManager {
|
|
23
|
+
/**
|
|
24
|
+
* Logger instance (injectable for testing)
|
|
25
|
+
*/
|
|
26
|
+
private static logger;
|
|
27
|
+
/**
|
|
28
|
+
* Set logger instance (primarily for testing with silentLogger)
|
|
29
|
+
*
|
|
30
|
+
* @param logger - Logger instance to use
|
|
31
|
+
*/
|
|
32
|
+
static setLogger(logger: Logger): void;
|
|
33
|
+
/**
|
|
34
|
+
* Get metadata file path for increment
|
|
35
|
+
*
|
|
36
|
+
* Uses resolveEffectiveRoot() to find the umbrella root in multi-repo setups,
|
|
37
|
+
* or the nearest project root in single-repo setups.
|
|
38
|
+
*
|
|
39
|
+
* SECURITY: Validates increment ID to prevent path traversal attacks.
|
|
40
|
+
*/
|
|
41
|
+
private static getMetadataPath;
|
|
42
|
+
/**
|
|
43
|
+
* Get increment directory path
|
|
44
|
+
*
|
|
45
|
+
* Uses resolveEffectiveRoot() to find the umbrella root in multi-repo setups,
|
|
46
|
+
* or the nearest project root in single-repo setups.
|
|
47
|
+
*
|
|
48
|
+
* SECURITY: Validates increment ID to prevent path traversal attacks.
|
|
49
|
+
*/
|
|
50
|
+
private static getIncrementPath;
|
|
51
|
+
/**
|
|
52
|
+
* Check if metadata file exists
|
|
53
|
+
*/
|
|
54
|
+
static exists(incrementId: string, rootDir?: string): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Read metadata from file
|
|
57
|
+
* Creates default metadata if file doesn't exist (lazy initialization)
|
|
58
|
+
*/
|
|
59
|
+
static read(incrementId: string, rootDir?: string): IncrementMetadata;
|
|
60
|
+
/**
|
|
61
|
+
* Reserved increment IDs that cannot be used
|
|
62
|
+
* These are status values, special folders, and state files
|
|
63
|
+
*/
|
|
64
|
+
private static readonly RESERVED_INCREMENT_IDS;
|
|
65
|
+
/**
|
|
66
|
+
* Validate increment ID is not a reserved name
|
|
67
|
+
* Throws if ID is reserved
|
|
68
|
+
*/
|
|
69
|
+
private static validateNotReserved;
|
|
70
|
+
/**
|
|
71
|
+
* Validate increment before creation (check for duplicates and reserved names)
|
|
72
|
+
* Throws if duplicates exist in other locations or ID is reserved
|
|
73
|
+
*/
|
|
74
|
+
static validateBeforeCreate(incrementId: string, rootDir?: string): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Write metadata to file
|
|
77
|
+
* Uses atomic write (temp file → rename)
|
|
78
|
+
*
|
|
79
|
+
* @param incrementId - Increment ID
|
|
80
|
+
* @param metadata - Metadata to write
|
|
81
|
+
* @param rootDir - Optional root directory (defaults to process.cwd())
|
|
82
|
+
*/
|
|
83
|
+
static write(incrementId: string, metadata: IncrementMetadata, rootDir?: string): void;
|
|
84
|
+
/**
|
|
85
|
+
* Delete metadata file
|
|
86
|
+
*/
|
|
87
|
+
static delete(incrementId: string, rootDir?: string): void;
|
|
88
|
+
/**
|
|
89
|
+
* Update increment status
|
|
90
|
+
* Validates transition and updates timestamps
|
|
91
|
+
*
|
|
92
|
+
* **CRITICAL**: Also updates active increment state automatically!
|
|
93
|
+
*
|
|
94
|
+
* NOTE: This method is now SYNCHRONOUS to ensure spec.md is updated
|
|
95
|
+
* before returning. This prevents race conditions in tests and ensures
|
|
96
|
+
* data consistency.
|
|
97
|
+
*/
|
|
98
|
+
static updateStatus(incrementId: string, newStatus: IncrementStatus, reason?: string, rootDir?: string): IncrementMetadata;
|
|
99
|
+
/**
|
|
100
|
+
* Update spec.md status synchronously (used by updateStatus)
|
|
101
|
+
*
|
|
102
|
+
* This is a private helper to avoid async/await in updateStatus() which would
|
|
103
|
+
* break backward compatibility with callers expecting sync behavior.
|
|
104
|
+
*/
|
|
105
|
+
private static updateSpecMdStatusSync;
|
|
106
|
+
/**
|
|
107
|
+
* Update increment type
|
|
108
|
+
*/
|
|
109
|
+
static updateType(incrementId: string, type: IncrementType): IncrementMetadata;
|
|
110
|
+
/**
|
|
111
|
+
* Touch increment (update lastActivity)
|
|
112
|
+
*/
|
|
113
|
+
static touch(incrementId: string): IncrementMetadata;
|
|
114
|
+
/**
|
|
115
|
+
* Get all increments
|
|
116
|
+
*
|
|
117
|
+
* Uses resolveEffectiveRoot() to find umbrella root in multi-repo setups.
|
|
118
|
+
*/
|
|
119
|
+
static getAll(): IncrementMetadata[];
|
|
120
|
+
/**
|
|
121
|
+
* Get increments by status
|
|
122
|
+
*/
|
|
123
|
+
static getByStatus(status: IncrementStatus): IncrementMetadata[];
|
|
124
|
+
/**
|
|
125
|
+
* Get active increments (FAST: cache-first strategy)
|
|
126
|
+
*
|
|
127
|
+
* **PERFORMANCE UPGRADE**: Uses ActiveIncrementManager cache instead of scanning all increments
|
|
128
|
+
* - Old: Scan 31 metadata files (~50ms)
|
|
129
|
+
* - New: Read 1 cache file + 1-2 metadata files (~5ms) = **10x faster**
|
|
130
|
+
*
|
|
131
|
+
* Fallback to full scan if cache is stale or missing
|
|
132
|
+
*/
|
|
133
|
+
static getActive(): IncrementMetadata[];
|
|
134
|
+
/**
|
|
135
|
+
* Get backlog increments
|
|
136
|
+
*/
|
|
137
|
+
static getBacklog(): IncrementMetadata[];
|
|
138
|
+
/**
|
|
139
|
+
* Get paused increments
|
|
140
|
+
*/
|
|
141
|
+
static getPaused(): IncrementMetadata[];
|
|
142
|
+
/**
|
|
143
|
+
* Get completed increments
|
|
144
|
+
*/
|
|
145
|
+
static getCompleted(): IncrementMetadata[];
|
|
146
|
+
/**
|
|
147
|
+
* Get abandoned increments
|
|
148
|
+
*/
|
|
149
|
+
static getAbandoned(): IncrementMetadata[];
|
|
150
|
+
/**
|
|
151
|
+
* Get increments by type
|
|
152
|
+
*/
|
|
153
|
+
static getByType(type: IncrementType): IncrementMetadata[];
|
|
154
|
+
/**
|
|
155
|
+
* Get stale increments (paused >7 days or active >30 days)
|
|
156
|
+
*/
|
|
157
|
+
static getStale(): IncrementMetadata[];
|
|
158
|
+
/**
|
|
159
|
+
* Get increments that should be auto-abandoned (experiments inactive >14 days)
|
|
160
|
+
*/
|
|
161
|
+
static getShouldAutoAbandon(): IncrementMetadata[];
|
|
162
|
+
/**
|
|
163
|
+
* Get extended metadata with computed fields (progress, age, etc.)
|
|
164
|
+
*/
|
|
165
|
+
static getExtended(incrementId: string): IncrementMetadataExtended;
|
|
166
|
+
/**
|
|
167
|
+
* Validate and auto-correct metadata schema issues.
|
|
168
|
+
*
|
|
169
|
+
* Fixes:
|
|
170
|
+
* - id: short numeric IDs (e.g. "0399") expanded to full slug using folderName
|
|
171
|
+
* - type: non-standard aliases mapped to canonical enum values
|
|
172
|
+
* - created/createdAt: legacy field renamed
|
|
173
|
+
* - externalLinks: ensured to exist (defaults to {})
|
|
174
|
+
* - status/priority/testMode/coverageTarget: sensible defaults applied
|
|
175
|
+
*
|
|
176
|
+
* @returns corrected metadata, whether any correction was made, and warnings
|
|
177
|
+
*/
|
|
178
|
+
private static validateMetadataSchema;
|
|
179
|
+
/**
|
|
180
|
+
* Validate metadata schema
|
|
181
|
+
*/
|
|
182
|
+
static validate(metadata: IncrementMetadata): boolean;
|
|
183
|
+
/**
|
|
184
|
+
* Check if status transition is allowed
|
|
185
|
+
*/
|
|
186
|
+
static canTransition(from: IncrementStatus, to: IncrementStatus): boolean;
|
|
187
|
+
/**
|
|
188
|
+
* Get human-readable status transition error message
|
|
189
|
+
*/
|
|
190
|
+
static getTransitionError(from: IncrementStatus, to: IncrementStatus): string;
|
|
191
|
+
/**
|
|
192
|
+
* Set the external tool sync target for an increment
|
|
193
|
+
*
|
|
194
|
+
* This explicitly specifies which sync profile the increment uses.
|
|
195
|
+
* The sync target provides audit trail and deterministic sync behavior.
|
|
196
|
+
*
|
|
197
|
+
* @param incrementId - Increment ID
|
|
198
|
+
* @param syncTarget - Sync target configuration
|
|
199
|
+
* @param rootDir - Optional root directory
|
|
200
|
+
* @returns Updated metadata
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* MetadataManager.setSyncTarget('0142-feature', {
|
|
205
|
+
* profileId: 'github-frontend',
|
|
206
|
+
* provider: 'github',
|
|
207
|
+
* derivedFrom: 'project-mapping',
|
|
208
|
+
* setAt: new Date().toISOString(),
|
|
209
|
+
* sourceProjectId: 'frontend-app'
|
|
210
|
+
* });
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
static setSyncTarget(incrementId: string, syncTarget: SyncTarget, rootDir?: string): IncrementMetadataV2;
|
|
214
|
+
/**
|
|
215
|
+
* Get the sync target for an increment
|
|
216
|
+
*
|
|
217
|
+
* @param incrementId - Increment ID
|
|
218
|
+
* @param rootDir - Optional root directory
|
|
219
|
+
* @returns Sync target or undefined if not set
|
|
220
|
+
*/
|
|
221
|
+
static getSyncTarget(incrementId: string, rootDir?: string): SyncTarget | undefined;
|
|
222
|
+
/**
|
|
223
|
+
* Clear the sync target for an increment
|
|
224
|
+
*
|
|
225
|
+
* Use this when the external tool configuration changes and
|
|
226
|
+
* the increment needs to be re-resolved.
|
|
227
|
+
*
|
|
228
|
+
* @param incrementId - Increment ID
|
|
229
|
+
* @param rootDir - Optional root directory
|
|
230
|
+
* @returns Updated metadata
|
|
231
|
+
*/
|
|
232
|
+
static clearSyncTarget(incrementId: string, rootDir?: string): IncrementMetadataV2;
|
|
233
|
+
/**
|
|
234
|
+
* Check if increment has a sync target configured
|
|
235
|
+
*
|
|
236
|
+
* @param incrementId - Increment ID
|
|
237
|
+
* @param rootDir - Optional root directory
|
|
238
|
+
* @returns true if sync target is set
|
|
239
|
+
*/
|
|
240
|
+
static hasSyncTarget(incrementId: string, rootDir?: string): boolean;
|
|
241
|
+
/**
|
|
242
|
+
* Add a pull request reference to an increment (v1.0.437+)
|
|
243
|
+
*
|
|
244
|
+
* Appends a PrRef to the prRefs array. Deduplicates by repoSlug
|
|
245
|
+
* (or by branch name for single-repo increments without repoSlug).
|
|
246
|
+
*
|
|
247
|
+
* @param incrementId - Increment ID
|
|
248
|
+
* @param ref - Pull request reference to add
|
|
249
|
+
* @param rootDir - Optional root directory
|
|
250
|
+
* @returns Updated metadata
|
|
251
|
+
*/
|
|
252
|
+
static addPrRef(incrementId: string, ref: PrRef, rootDir?: string): IncrementMetadataV2;
|
|
253
|
+
/**
|
|
254
|
+
* Get pull request references for an increment
|
|
255
|
+
*
|
|
256
|
+
* @param incrementId - Increment ID
|
|
257
|
+
* @param rootDir - Optional root directory
|
|
258
|
+
* @returns Array of PR refs, or empty array if none
|
|
259
|
+
*/
|
|
260
|
+
static getPrRefs(incrementId: string, rootDir?: string): PrRef[];
|
|
261
|
+
/**
|
|
262
|
+
* Get all increments with a specific sync provider
|
|
263
|
+
*
|
|
264
|
+
* Useful for bulk operations on all GitHub/JIRA/ADO synced increments.
|
|
265
|
+
*
|
|
266
|
+
* @param provider - Provider type ('github', 'jira', 'ado')
|
|
267
|
+
* @returns Array of increments with that provider configured
|
|
268
|
+
*/
|
|
269
|
+
static getByProvider(provider: 'github' | 'jira' | 'ado'): IncrementMetadataV2[];
|
|
270
|
+
/**
|
|
271
|
+
* Get all increments with a specific sync profile
|
|
272
|
+
*
|
|
273
|
+
* @param profileId - Profile ID from config.sync.profiles
|
|
274
|
+
* @returns Array of increments using that profile
|
|
275
|
+
*/
|
|
276
|
+
static getByProfile(profileId: string): IncrementMetadataV2[];
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=metadata-manager.d.ts.map
|