specweave 0.32.2 → 0.32.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +39 -0
- package/bin/specweave.js +34 -0
- package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.d.ts +100 -0
- package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.js +291 -0
- package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.d.ts +103 -0
- package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.js +310 -0
- package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-permission-gate.d.ts +126 -0
- package/dist/plugins/specweave-jira/lib/jira-permission-gate.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-permission-gate.js +207 -0
- package/dist/plugins/specweave-jira/lib/jira-permission-gate.js.map +1 -0
- package/dist/src/adapters/codex/README.md +1 -1
- package/dist/src/adapters/codex/adapter.js +1 -1
- package/dist/src/cli/commands/archive.d.ts +2 -0
- package/dist/src/cli/commands/archive.d.ts.map +1 -1
- package/dist/src/cli/commands/archive.js +33 -0
- package/dist/src/cli/commands/archive.js.map +1 -1
- package/dist/src/cli/commands/context.d.ts +92 -0
- package/dist/src/cli/commands/context.d.ts.map +1 -0
- package/dist/src/cli/commands/context.js +205 -0
- package/dist/src/cli/commands/context.js.map +1 -0
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +111 -69
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/helpers/init/external-import.d.ts +3 -0
- package/dist/src/cli/helpers/init/external-import.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/external-import.js +17 -4
- package/dist/src/cli/helpers/init/external-import.js.map +1 -1
- package/dist/src/cli/helpers/init/index.d.ts +1 -0
- package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/index.js +2 -0
- package/dist/src/cli/helpers/init/index.js.map +1 -1
- package/dist/src/cli/helpers/init/jira-ado-auto-detect.d.ts +70 -0
- package/dist/src/cli/helpers/init/jira-ado-auto-detect.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/jira-ado-auto-detect.js +214 -4
- package/dist/src/cli/helpers/init/jira-ado-auto-detect.js.map +1 -1
- package/dist/src/cli/helpers/init/living-docs-preflight.d.ts +4 -0
- package/dist/src/cli/helpers/init/living-docs-preflight.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/living-docs-preflight.js +34 -3
- package/dist/src/cli/helpers/init/living-docs-preflight.js.map +1 -1
- package/dist/src/cli/helpers/init/testing-config.d.ts +3 -0
- package/dist/src/cli/helpers/init/testing-config.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/testing-config.js +9 -2
- package/dist/src/cli/helpers/init/testing-config.js.map +1 -1
- package/dist/src/cli/helpers/init/translation-config.d.ts +3 -0
- package/dist/src/cli/helpers/init/translation-config.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/translation-config.js +21 -4
- package/dist/src/cli/helpers/init/translation-config.js.map +1 -1
- package/dist/src/cli/helpers/init/wizard-navigation.d.ts +45 -0
- package/dist/src/cli/helpers/init/wizard-navigation.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/wizard-navigation.js +97 -0
- package/dist/src/cli/helpers/init/wizard-navigation.js.map +1 -0
- package/dist/src/core/increment/increment-archiver.d.ts +25 -4
- package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
- package/dist/src/core/increment/increment-archiver.js +64 -20
- package/dist/src/core/increment/increment-archiver.js.map +1 -1
- package/dist/src/core/increment/increment-utils.d.ts +65 -0
- package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
- package/dist/src/core/increment/increment-utils.js +114 -0
- package/dist/src/core/increment/increment-utils.js.map +1 -1
- package/dist/src/core/living-docs/feature-archiver.d.ts +4 -0
- package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -1
- package/dist/src/core/living-docs/feature-archiver.js +32 -10
- package/dist/src/core/living-docs/feature-archiver.js.map +1 -1
- package/dist/src/core/living-docs/feature-id-manager.d.ts.map +1 -1
- package/dist/src/core/living-docs/feature-id-manager.js +7 -3
- package/dist/src/core/living-docs/feature-id-manager.js.map +1 -1
- package/dist/src/core/living-docs/governance/ecosystem-detector.d.ts +38 -0
- package/dist/src/core/living-docs/governance/ecosystem-detector.d.ts.map +1 -0
- package/dist/src/core/living-docs/governance/ecosystem-detector.js +325 -0
- package/dist/src/core/living-docs/governance/ecosystem-detector.js.map +1 -0
- package/dist/src/core/living-docs/governance/frontend-standards-parser.d.ts +74 -0
- package/dist/src/core/living-docs/governance/frontend-standards-parser.d.ts.map +1 -0
- package/dist/src/core/living-docs/governance/frontend-standards-parser.js +366 -0
- package/dist/src/core/living-docs/governance/frontend-standards-parser.js.map +1 -0
- package/dist/src/core/living-docs/governance/go-standards-parser.d.ts +64 -0
- package/dist/src/core/living-docs/governance/go-standards-parser.d.ts.map +1 -0
- package/dist/src/core/living-docs/governance/go-standards-parser.js +229 -0
- package/dist/src/core/living-docs/governance/go-standards-parser.js.map +1 -0
- package/dist/src/core/living-docs/governance/index.d.ts +50 -0
- package/dist/src/core/living-docs/governance/index.d.ts.map +1 -0
- package/dist/src/core/living-docs/governance/index.js +56 -0
- package/dist/src/core/living-docs/governance/index.js.map +1 -0
- package/dist/src/core/living-docs/governance/java-standards-parser.d.ts +89 -0
- package/dist/src/core/living-docs/governance/java-standards-parser.d.ts.map +1 -0
- package/dist/src/core/living-docs/governance/java-standards-parser.js +356 -0
- package/dist/src/core/living-docs/governance/java-standards-parser.js.map +1 -0
- package/dist/src/core/living-docs/governance/python-standards-parser.d.ts +83 -0
- package/dist/src/core/living-docs/governance/python-standards-parser.d.ts.map +1 -0
- package/dist/src/core/living-docs/governance/python-standards-parser.js +347 -0
- package/dist/src/core/living-docs/governance/python-standards-parser.js.map +1 -0
- package/dist/src/core/living-docs/governance/standards-generator.d.ts +38 -0
- package/dist/src/core/living-docs/governance/standards-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/governance/standards-generator.js +476 -0
- package/dist/src/core/living-docs/governance/standards-generator.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js +54 -2
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts +5 -1
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js +358 -30
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts +44 -0
- package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.d.ts +6 -3
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +17 -8
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
- package/dist/src/core/living-docs/module-analyzer.d.ts +22 -0
- package/dist/src/core/living-docs/module-analyzer.d.ts.map +1 -1
- package/dist/src/core/living-docs/module-analyzer.js +123 -19
- package/dist/src/core/living-docs/module-analyzer.js.map +1 -1
- package/dist/src/core/llm/provider-factory.js +2 -2
- package/dist/src/core/llm/provider-factory.js.map +1 -1
- package/dist/src/core/llm/providers/anthropic-provider.js +1 -1
- package/dist/src/core/llm/providers/bedrock-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/bedrock-provider.js +8 -4
- package/dist/src/core/llm/providers/bedrock-provider.js.map +1 -1
- package/dist/src/importers/jira-importer.d.ts +14 -0
- package/dist/src/importers/jira-importer.d.ts.map +1 -1
- package/dist/src/importers/jira-importer.js +75 -0
- package/dist/src/importers/jira-importer.js.map +1 -1
- package/dist/src/integrations/jira/jira-token-provider.d.ts +93 -0
- package/dist/src/integrations/jira/jira-token-provider.d.ts.map +1 -0
- package/dist/src/integrations/jira/jira-token-provider.js +160 -0
- package/dist/src/integrations/jira/jira-token-provider.js.map +1 -0
- package/dist/src/sync/ado-reconciler.d.ts +92 -0
- package/dist/src/sync/ado-reconciler.d.ts.map +1 -0
- package/dist/src/sync/ado-reconciler.js +335 -0
- package/dist/src/sync/ado-reconciler.js.map +1 -0
- package/dist/src/sync/jira-reconciler.d.ts +106 -0
- package/dist/src/sync/jira-reconciler.d.ts.map +1 -0
- package/dist/src/sync/jira-reconciler.js +405 -0
- package/dist/src/sync/jira-reconciler.js.map +1 -0
- package/dist/src/types/model-selection.d.ts +6 -4
- package/dist/src/types/model-selection.d.ts.map +1 -1
- package/dist/src/types/model-selection.js +3 -1
- package/dist/src/types/model-selection.js.map +1 -1
- package/dist/src/utils/external-tool-drift-detector.d.ts +1 -1
- package/dist/src/utils/external-tool-drift-detector.d.ts.map +1 -1
- package/dist/src/utils/external-tool-drift-detector.js +5 -4
- package/dist/src/utils/external-tool-drift-detector.js.map +1 -1
- package/dist/src/utils/feature-id-derivation.d.ts +8 -3
- package/dist/src/utils/feature-id-derivation.d.ts.map +1 -1
- package/dist/src/utils/feature-id-derivation.js +14 -6
- package/dist/src/utils/feature-id-derivation.js.map +1 -1
- package/dist/src/utils/model-selection.d.ts +3 -4
- package/dist/src/utils/model-selection.d.ts.map +1 -1
- package/dist/src/utils/model-selection.js +3 -4
- package/dist/src/utils/model-selection.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/agents/code-standards-detective/AGENT.md +48 -0
- package/plugins/specweave/commands/specweave-costs.md +4 -4
- package/plugins/specweave/commands/specweave-do.md +9 -9
- package/plugins/specweave/commands/specweave-done.md +13 -0
- package/plugins/specweave/commands/specweave-validate.md +27 -1
- package/plugins/specweave/hooks/hooks.json +10 -0
- package/plugins/specweave/hooks/spec-project-validator.sh +80 -25
- package/plugins/specweave/hooks/v2/guards/increment-duplicate-guard.sh +135 -0
- package/plugins/specweave/scripts/read-costs.sh +3 -3
- package/plugins/specweave/skills/code-standards-analyzer/SKILL.md +58 -6
- package/plugins/specweave/skills/increment-planner/SKILL.md +56 -25
- package/plugins/specweave/skills/increment-planner/templates/spec-multi-project.md +4 -2
- package/plugins/specweave/skills/increment-planner/templates/spec-single-project.md +2 -1
- package/plugins/specweave/skills/increment-planner/templates/tasks-multi-project.md +1 -1
- package/plugins/specweave/skills/increment-planner/templates/tasks-single-project.md +1 -1
- package/plugins/specweave-ado/commands/cleanup-duplicates.md +212 -0
- package/plugins/specweave-ado/commands/reconcile.md +120 -0
- package/plugins/specweave-ado/lib/ado-duplicate-detector.js +279 -0
- package/plugins/specweave-ado/lib/ado-duplicate-detector.ts +407 -0
- package/plugins/specweave-github/agents/github-manager/AGENT.md +2 -2
- package/plugins/specweave-infrastructure/skills/hetzner-provisioner/README.md +1 -1
- package/plugins/specweave-jira/agents/jira-manager/AGENT.md +1 -1
- package/plugins/specweave-jira/agents/jira-multi-project-mapper/AGENT.md +530 -0
- package/plugins/specweave-jira/agents/jira-sync-judge/AGENT.md +438 -0
- package/plugins/specweave-jira/commands/cleanup-duplicates.md +219 -0
- package/plugins/specweave-jira/commands/close.md +297 -0
- package/plugins/specweave-jira/commands/create.md +198 -0
- package/plugins/specweave-jira/commands/reconcile.md +123 -0
- package/plugins/specweave-jira/commands/status.md +215 -0
- package/plugins/specweave-jira/lib/jira-duplicate-detector.js +296 -0
- package/plugins/specweave-jira/lib/jira-duplicate-detector.ts +434 -0
- package/plugins/specweave-jira/lib/jira-permission-gate.js +160 -0
- package/plugins/specweave-jira/lib/jira-permission-gate.ts +276 -0
- package/plugins/specweave-jira/lib/jira-profile-resolver.js +222 -0
- package/plugins/specweave-jira/lib/jira-profile-resolver.ts +427 -0
- package/plugins/specweave-jira/reference/jira-specweave-mapping.md +16 -11
- package/plugins/specweave-release/commands/specweave-release-npm.md +140 -14
package/CLAUDE.md
CHANGED
|
@@ -129,6 +129,45 @@ const config = detectStructureLevel(projectRoot);
|
|
|
129
129
|
|
|
130
130
|
**Pre-tool-use hook `features-folder-guard.sh` BLOCKS writes to `_features/` (v0.33.0+).**
|
|
131
131
|
|
|
132
|
+
### 2e. NEVER Create Duplicate Increment IDs (v0.33.0+)
|
|
133
|
+
|
|
134
|
+
**Increment numbers MUST be unique across ALL directories!**
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
❌ FORBIDDEN (Bug pattern from 2025-12-07):
|
|
138
|
+
0121-ado-jira-feature-parity-p2-p3/ ← exists
|
|
139
|
+
0121-intelligent-living-docs-content/ ← DUPLICATE!
|
|
140
|
+
|
|
141
|
+
❌ ALSO FORBIDDEN (0001 and 0001E share SAME base number):
|
|
142
|
+
0001-internal-feature/
|
|
143
|
+
0001E-external-fix/ ← COLLISION! Same base number!
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**VALIDATION RULES:**
|
|
147
|
+
```
|
|
148
|
+
✅ ALWAYS use IncrementNumberManager.generateUniqueIncrementId()
|
|
149
|
+
✅ ALWAYS use IncrementNumberManager.validateUnique() before creating
|
|
150
|
+
✅ Check ALL directories: active, _archive, _abandoned, _paused
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**API (v0.33.0+):**
|
|
154
|
+
```typescript
|
|
155
|
+
import { IncrementNumberManager } from './core/increment/increment-utils.js';
|
|
156
|
+
|
|
157
|
+
// Safe generation with validation:
|
|
158
|
+
const id = IncrementNumberManager.generateUniqueIncrementId('feature-name');
|
|
159
|
+
// → "0122-feature-name" (guaranteed unique)
|
|
160
|
+
|
|
161
|
+
// Manual validation:
|
|
162
|
+
IncrementNumberManager.validateUnique('0121-new-name'); // throws if duplicate!
|
|
163
|
+
|
|
164
|
+
// Find duplicates:
|
|
165
|
+
IncrementNumberManager.findDuplicates('0121');
|
|
166
|
+
// → ["0121-ado-jira-feature (active)", "0121-intelligent-living-docs (active)"]
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Pre-tool-use hook `increment-duplicate-guard.sh` BLOCKS duplicate increment creation (v0.33.0+).**
|
|
170
|
+
|
|
132
171
|
### 3. Protected Directories
|
|
133
172
|
|
|
134
173
|
**NEVER delete**: `.specweave/docs/`, `.specweave/increments/`
|
package/bin/specweave.js
CHANGED
|
@@ -115,6 +115,7 @@ program
|
|
|
115
115
|
.option('--pattern <pattern>', 'Archive increments matching pattern (regex)')
|
|
116
116
|
.option('--archive-completed', 'Archive all completed increments')
|
|
117
117
|
.option('--no-preserve-active', 'Allow archiving active/paused increments (dangerous!)')
|
|
118
|
+
.option('--external', 'Archive external living docs (FS-XXXE features imported from ADO/JIRA/GitHub)')
|
|
118
119
|
.option('--dry-run', 'Preview what would be archived without moving files')
|
|
119
120
|
.action(async (incrementIds, options) => {
|
|
120
121
|
const { archiveCommand } = await import('../dist/src/cli/commands/archive.js');
|
|
@@ -358,6 +359,36 @@ program
|
|
|
358
359
|
await syncCmd.parseAsync(['node', 'sync-scheduled', ...process.argv.slice(3)], { from: 'user' });
|
|
359
360
|
});
|
|
360
361
|
|
|
362
|
+
// Context command - Get project/board context for increment planning
|
|
363
|
+
const contextCmd = program
|
|
364
|
+
.command('context')
|
|
365
|
+
.description('Get project/board context for increment planning');
|
|
366
|
+
|
|
367
|
+
contextCmd
|
|
368
|
+
.command('projects')
|
|
369
|
+
.description('List available projects and structure level')
|
|
370
|
+
.action(async () => {
|
|
371
|
+
const { contextProjectsCommand } = await import('../dist/src/cli/commands/context.js');
|
|
372
|
+
await contextProjectsCommand();
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
contextCmd
|
|
376
|
+
.command('boards')
|
|
377
|
+
.description('List available boards for a project (2-level structures)')
|
|
378
|
+
.option('-p, --project <id>', 'Project ID to filter boards')
|
|
379
|
+
.action(async (options) => {
|
|
380
|
+
const { contextBoardsCommand } = await import('../dist/src/cli/commands/context.js');
|
|
381
|
+
await contextBoardsCommand(options);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
contextCmd
|
|
385
|
+
.command('select')
|
|
386
|
+
.description('Interactive project/board selection (auto-selects if single option)')
|
|
387
|
+
.action(async () => {
|
|
388
|
+
const { contextSelectCommand } = await import('../dist/src/cli/commands/context.js');
|
|
389
|
+
await contextSelectCommand();
|
|
390
|
+
});
|
|
391
|
+
|
|
361
392
|
// Help text
|
|
362
393
|
program.on('--help', () => {
|
|
363
394
|
console.log('');
|
|
@@ -396,6 +427,9 @@ program.on('--help', () => {
|
|
|
396
427
|
console.log(' $ specweave cache --rebuild # Rebuild dashboard cache');
|
|
397
428
|
console.log(' $ specweave cache --debug # Show cache debug info');
|
|
398
429
|
console.log(' $ specweave validate-jira --env .env.prod # Validate with custom .env file');
|
|
430
|
+
console.log(' $ specweave context projects # Get available projects as JSON');
|
|
431
|
+
console.log(' $ specweave context boards --project myapp # Get boards for a project');
|
|
432
|
+
console.log(' $ specweave context select # Interactive project/board selection');
|
|
399
433
|
console.log('');
|
|
400
434
|
console.log('Supported AI Tools:');
|
|
401
435
|
console.log(' - Claude Code (full automation) - Native skills, agents, hooks');
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Azure DevOps Duplicate Detector (v0.33.0)
|
|
3
|
+
*
|
|
4
|
+
* Implements 3-phase duplicate protection for ADO work items:
|
|
5
|
+
* 1. Detection: Check before create
|
|
6
|
+
* 2. Verification: Count check after create
|
|
7
|
+
* 3. Reflection: Auto-close duplicates
|
|
8
|
+
*
|
|
9
|
+
* Mirrors the GitHub DuplicateDetector pattern for consistency.
|
|
10
|
+
*/
|
|
11
|
+
import { Logger } from '../../../src/utils/logger.js';
|
|
12
|
+
export interface AdoWorkItem {
|
|
13
|
+
id: number;
|
|
14
|
+
title: string;
|
|
15
|
+
state: string;
|
|
16
|
+
createdDate: string;
|
|
17
|
+
url?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface DuplicateGroup {
|
|
20
|
+
title: string;
|
|
21
|
+
items: AdoWorkItem[];
|
|
22
|
+
keepItem: AdoWorkItem;
|
|
23
|
+
duplicates: AdoWorkItem[];
|
|
24
|
+
}
|
|
25
|
+
export interface DetectionResult {
|
|
26
|
+
found: boolean;
|
|
27
|
+
existingItem?: AdoWorkItem;
|
|
28
|
+
count: number;
|
|
29
|
+
}
|
|
30
|
+
export interface VerificationResult {
|
|
31
|
+
success: boolean;
|
|
32
|
+
expectedCount: number;
|
|
33
|
+
actualCount: number;
|
|
34
|
+
duplicates: AdoWorkItem[];
|
|
35
|
+
}
|
|
36
|
+
export interface CleanupResult {
|
|
37
|
+
closedCount: number;
|
|
38
|
+
keptCount: number;
|
|
39
|
+
errors: string[];
|
|
40
|
+
}
|
|
41
|
+
export declare class AdoDuplicateDetector {
|
|
42
|
+
private org;
|
|
43
|
+
private project;
|
|
44
|
+
private pat;
|
|
45
|
+
private logger;
|
|
46
|
+
constructor(options?: {
|
|
47
|
+
org?: string;
|
|
48
|
+
project?: string;
|
|
49
|
+
pat?: string;
|
|
50
|
+
logger?: Logger;
|
|
51
|
+
});
|
|
52
|
+
/**
|
|
53
|
+
* Phase 1: Check if work item exists before creating
|
|
54
|
+
*/
|
|
55
|
+
checkBeforeCreate(titlePattern: string, incrementId?: string): Promise<DetectionResult>;
|
|
56
|
+
/**
|
|
57
|
+
* Phase 2: Verify count after creation
|
|
58
|
+
*/
|
|
59
|
+
verifyAfterCreate(titlePattern: string, expectedCount?: number): Promise<VerificationResult>;
|
|
60
|
+
/**
|
|
61
|
+
* Phase 3: Auto-close duplicates
|
|
62
|
+
*/
|
|
63
|
+
closeDuplicates(duplicates: AdoWorkItem[], keepItemId: number): Promise<CleanupResult>;
|
|
64
|
+
/**
|
|
65
|
+
* Full cleanup: Find and close all duplicates for a feature
|
|
66
|
+
*/
|
|
67
|
+
cleanupFeatureDuplicates(featureId: string, dryRun?: boolean): Promise<{
|
|
68
|
+
groups: DuplicateGroup[];
|
|
69
|
+
totalItems: number;
|
|
70
|
+
duplicateCount: number;
|
|
71
|
+
closedCount: number;
|
|
72
|
+
}>;
|
|
73
|
+
/**
|
|
74
|
+
* Group work items by title
|
|
75
|
+
*/
|
|
76
|
+
private groupByTitle;
|
|
77
|
+
/**
|
|
78
|
+
* Search for work items using WIQL
|
|
79
|
+
*/
|
|
80
|
+
private searchWorkItems;
|
|
81
|
+
/**
|
|
82
|
+
* Get work item details by IDs
|
|
83
|
+
*/
|
|
84
|
+
private getWorkItemDetails;
|
|
85
|
+
/**
|
|
86
|
+
* Close a work item with duplicate comment
|
|
87
|
+
*/
|
|
88
|
+
private closeWorkItem;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Convenience function for quick duplicate cleanup
|
|
92
|
+
*/
|
|
93
|
+
export declare function cleanupAdoDuplicates(featureId: string, dryRun?: boolean): Promise<{
|
|
94
|
+
groups: DuplicateGroup[];
|
|
95
|
+
totalItems: number;
|
|
96
|
+
duplicateCount: number;
|
|
97
|
+
closedCount: number;
|
|
98
|
+
}>;
|
|
99
|
+
export default AdoDuplicateDetector;
|
|
100
|
+
//# sourceMappingURL=ado-duplicate-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ado-duplicate-detector.d.ts","sourceRoot":"","sources":["../../../../plugins/specweave-ado/lib/ado-duplicate-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,EAAiB,MAAM,8BAA8B,CAAC;AAErE,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,WAAW,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,WAAW,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,GAAE;QACnB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;KACZ;IAON;;OAEG;IACG,iBAAiB,CACrB,YAAY,EAAE,MAAM,EACpB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,eAAe,CAAC;IAoB3B;;OAEG;IACG,iBAAiB,CACrB,YAAY,EAAE,MAAM,EACpB,aAAa,GAAE,MAAU,GACxB,OAAO,CAAC,kBAAkB,CAAC;IAmC9B;;OAEG;IACG,eAAe,CACnB,UAAU,EAAE,WAAW,EAAE,EACzB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC;IAqBzB;;OAEG;IACG,wBAAwB,CAC5B,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,OAAe,GACtB,OAAO,CAAC;QACT,MAAM,EAAE,cAAc,EAAE,CAAC;QACzB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IAyEF;;OAEG;IACH,OAAO,CAAC,YAAY;IA0BpB;;OAEG;YACW,eAAe;IAwC7B;;OAEG;YACW,kBAAkB;IA2BhC;;OAEG;YACW,aAAa;CAwC5B;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,OAAe,GACtB,OAAO,CAAC;IACT,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,CAGD;AAED,eAAe,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Azure DevOps Duplicate Detector (v0.33.0)
|
|
3
|
+
*
|
|
4
|
+
* Implements 3-phase duplicate protection for ADO work items:
|
|
5
|
+
* 1. Detection: Check before create
|
|
6
|
+
* 2. Verification: Count check after create
|
|
7
|
+
* 3. Reflection: Auto-close duplicates
|
|
8
|
+
*
|
|
9
|
+
* Mirrors the GitHub DuplicateDetector pattern for consistency.
|
|
10
|
+
*/
|
|
11
|
+
import { consoleLogger } from '../../../src/utils/logger.js';
|
|
12
|
+
export class AdoDuplicateDetector {
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
this.org = options.org || process.env.AZURE_DEVOPS_ORG || '';
|
|
15
|
+
this.project = options.project || process.env.AZURE_DEVOPS_PROJECT || '';
|
|
16
|
+
this.pat = options.pat || process.env.AZURE_DEVOPS_PAT || process.env.AZURE_DEVOPS_TOKEN || '';
|
|
17
|
+
this.logger = options.logger || consoleLogger;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Phase 1: Check if work item exists before creating
|
|
21
|
+
*/
|
|
22
|
+
async checkBeforeCreate(titlePattern, incrementId) {
|
|
23
|
+
try {
|
|
24
|
+
const items = await this.searchWorkItems(titlePattern);
|
|
25
|
+
if (items.length > 0) {
|
|
26
|
+
return {
|
|
27
|
+
found: true,
|
|
28
|
+
existingItem: items[0],
|
|
29
|
+
count: items.length,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return { found: false, count: 0 };
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
this.logger.log(`⚠️ Detection check failed: ${error.message}`);
|
|
36
|
+
// Graceful degradation - continue anyway
|
|
37
|
+
return { found: false, count: 0 };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Phase 2: Verify count after creation
|
|
42
|
+
*/
|
|
43
|
+
async verifyAfterCreate(titlePattern, expectedCount = 1) {
|
|
44
|
+
try {
|
|
45
|
+
const items = await this.searchWorkItems(titlePattern);
|
|
46
|
+
if (items.length > expectedCount) {
|
|
47
|
+
// Duplicates detected!
|
|
48
|
+
const sorted = items.sort((a, b) => a.id - b.id // Sort by ID (lowest = oldest)
|
|
49
|
+
);
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
expectedCount,
|
|
53
|
+
actualCount: items.length,
|
|
54
|
+
duplicates: sorted.slice(expectedCount), // All items after expected count
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
success: true,
|
|
59
|
+
expectedCount,
|
|
60
|
+
actualCount: items.length,
|
|
61
|
+
duplicates: [],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
this.logger.log(`⚠️ Verification check failed: ${error.message}`);
|
|
66
|
+
return {
|
|
67
|
+
success: true, // Assume success on error
|
|
68
|
+
expectedCount,
|
|
69
|
+
actualCount: expectedCount,
|
|
70
|
+
duplicates: [],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Phase 3: Auto-close duplicates
|
|
76
|
+
*/
|
|
77
|
+
async closeDuplicates(duplicates, keepItemId) {
|
|
78
|
+
const result = {
|
|
79
|
+
closedCount: 0,
|
|
80
|
+
keptCount: 1,
|
|
81
|
+
errors: [],
|
|
82
|
+
};
|
|
83
|
+
for (const item of duplicates) {
|
|
84
|
+
try {
|
|
85
|
+
await this.closeWorkItem(item.id, keepItemId);
|
|
86
|
+
result.closedCount++;
|
|
87
|
+
this.logger.log(` ✅ Closed #${item.id} (duplicate of #${keepItemId})`);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
result.errors.push(`#${item.id}: ${error.message}`);
|
|
91
|
+
this.logger.log(` ❌ Failed to close #${item.id}: ${error.message}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Full cleanup: Find and close all duplicates for a feature
|
|
98
|
+
*/
|
|
99
|
+
async cleanupFeatureDuplicates(featureId, dryRun = false) {
|
|
100
|
+
// 1. Search for all work items with feature ID
|
|
101
|
+
const searchPattern = `[${featureId}]`;
|
|
102
|
+
const items = await this.searchWorkItems(searchPattern);
|
|
103
|
+
this.logger.log(`\n🔍 Scanning for duplicates in Feature ${featureId}...`);
|
|
104
|
+
this.logger.log(` Found ${items.length} total work items`);
|
|
105
|
+
// 2. Group by title
|
|
106
|
+
const groups = this.groupByTitle(items);
|
|
107
|
+
const duplicateGroups = groups.filter(g => g.duplicates.length > 0);
|
|
108
|
+
if (duplicateGroups.length === 0) {
|
|
109
|
+
this.logger.log(` ✅ No duplicates found!`);
|
|
110
|
+
return {
|
|
111
|
+
groups: [],
|
|
112
|
+
totalItems: items.length,
|
|
113
|
+
duplicateCount: 0,
|
|
114
|
+
closedCount: 0,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
this.logger.log(` Detected ${duplicateGroups.length} duplicate groups:\n`);
|
|
118
|
+
// 3. Display groups
|
|
119
|
+
for (let i = 0; i < duplicateGroups.length; i++) {
|
|
120
|
+
const group = duplicateGroups[i];
|
|
121
|
+
this.logger.log(` 📋 Group ${i + 1}: "${group.title.substring(0, 50)}..."`);
|
|
122
|
+
this.logger.log(` - #${group.keepItem.id} (KEEP) - Created ${group.keepItem.createdDate.split('T')[0]}`);
|
|
123
|
+
for (const dup of group.duplicates) {
|
|
124
|
+
this.logger.log(` - #${dup.id} (CLOSE) - Created ${dup.createdDate.split('T')[0]} - DUPLICATE`);
|
|
125
|
+
}
|
|
126
|
+
this.logger.log('');
|
|
127
|
+
}
|
|
128
|
+
const totalDuplicates = duplicateGroups.reduce((sum, g) => sum + g.duplicates.length, 0);
|
|
129
|
+
if (dryRun) {
|
|
130
|
+
this.logger.log(`\n✅ Dry run complete!`);
|
|
131
|
+
this.logger.log(` Total work items: ${items.length}`);
|
|
132
|
+
this.logger.log(` Duplicate groups: ${duplicateGroups.length}`);
|
|
133
|
+
this.logger.log(` Work items to close: ${totalDuplicates}`);
|
|
134
|
+
this.logger.log(`\n⚠️ This was a DRY RUN - no changes made.`);
|
|
135
|
+
return {
|
|
136
|
+
groups: duplicateGroups,
|
|
137
|
+
totalItems: items.length,
|
|
138
|
+
duplicateCount: totalDuplicates,
|
|
139
|
+
closedCount: 0,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
// 4. Close duplicates
|
|
143
|
+
let closedCount = 0;
|
|
144
|
+
this.logger.log(`🗑️ Closing duplicates...`);
|
|
145
|
+
for (const group of duplicateGroups) {
|
|
146
|
+
const result = await this.closeDuplicates(group.duplicates, group.keepItem.id);
|
|
147
|
+
closedCount += result.closedCount;
|
|
148
|
+
}
|
|
149
|
+
this.logger.log(`\n✅ Cleanup complete!`);
|
|
150
|
+
this.logger.log(` Closed: ${closedCount} duplicates`);
|
|
151
|
+
this.logger.log(` Kept: ${duplicateGroups.length} original work items`);
|
|
152
|
+
return {
|
|
153
|
+
groups: duplicateGroups,
|
|
154
|
+
totalItems: items.length,
|
|
155
|
+
duplicateCount: totalDuplicates,
|
|
156
|
+
closedCount,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Group work items by title
|
|
161
|
+
*/
|
|
162
|
+
groupByTitle(items) {
|
|
163
|
+
const titleMap = new Map();
|
|
164
|
+
for (const item of items) {
|
|
165
|
+
const existing = titleMap.get(item.title) || [];
|
|
166
|
+
existing.push(item);
|
|
167
|
+
titleMap.set(item.title, existing);
|
|
168
|
+
}
|
|
169
|
+
const groups = [];
|
|
170
|
+
for (const [title, groupItems] of titleMap) {
|
|
171
|
+
// Sort by ID (lowest = oldest)
|
|
172
|
+
const sorted = groupItems.sort((a, b) => a.id - b.id);
|
|
173
|
+
groups.push({
|
|
174
|
+
title,
|
|
175
|
+
items: sorted,
|
|
176
|
+
keepItem: sorted[0],
|
|
177
|
+
duplicates: sorted.slice(1),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return groups;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Search for work items using WIQL
|
|
184
|
+
*/
|
|
185
|
+
async searchWorkItems(titlePattern) {
|
|
186
|
+
if (!this.org || !this.pat) {
|
|
187
|
+
throw new Error('ADO credentials not configured');
|
|
188
|
+
}
|
|
189
|
+
const wiql = {
|
|
190
|
+
query: `SELECT [System.Id], [System.Title], [System.State], [System.CreatedDate]
|
|
191
|
+
FROM WorkItems
|
|
192
|
+
WHERE [System.TeamProject] = '${this.project}'
|
|
193
|
+
AND [System.Title] CONTAINS '${titlePattern}'
|
|
194
|
+
ORDER BY [System.Id] ASC`,
|
|
195
|
+
};
|
|
196
|
+
const auth = Buffer.from(`:${this.pat}`).toString('base64');
|
|
197
|
+
const url = `https://dev.azure.com/${this.org}/${this.project}/_apis/wit/wiql?api-version=7.1`;
|
|
198
|
+
const response = await fetch(url, {
|
|
199
|
+
method: 'POST',
|
|
200
|
+
headers: {
|
|
201
|
+
Authorization: `Basic ${auth}`,
|
|
202
|
+
'Content-Type': 'application/json',
|
|
203
|
+
},
|
|
204
|
+
body: JSON.stringify(wiql),
|
|
205
|
+
});
|
|
206
|
+
if (!response.ok) {
|
|
207
|
+
throw new Error(`WIQL query failed: ${response.status}`);
|
|
208
|
+
}
|
|
209
|
+
const data = await response.json();
|
|
210
|
+
const workItemIds = data.workItems?.map((wi) => wi.id) || [];
|
|
211
|
+
if (workItemIds.length === 0) {
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
// Fetch full work item details
|
|
215
|
+
return this.getWorkItemDetails(workItemIds);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Get work item details by IDs
|
|
219
|
+
*/
|
|
220
|
+
async getWorkItemDetails(ids) {
|
|
221
|
+
if (ids.length === 0)
|
|
222
|
+
return [];
|
|
223
|
+
const auth = Buffer.from(`:${this.pat}`).toString('base64');
|
|
224
|
+
const url = `https://dev.azure.com/${this.org}/_apis/wit/workitems?ids=${ids.join(',')}&api-version=7.1`;
|
|
225
|
+
const response = await fetch(url, {
|
|
226
|
+
headers: {
|
|
227
|
+
Authorization: `Basic ${auth}`,
|
|
228
|
+
'Content-Type': 'application/json',
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
if (!response.ok) {
|
|
232
|
+
throw new Error(`Failed to get work items: ${response.status}`);
|
|
233
|
+
}
|
|
234
|
+
const data = await response.json();
|
|
235
|
+
return (data.value || []).map((wi) => ({
|
|
236
|
+
id: wi.id,
|
|
237
|
+
title: wi.fields['System.Title'],
|
|
238
|
+
state: wi.fields['System.State'],
|
|
239
|
+
createdDate: wi.fields['System.CreatedDate'],
|
|
240
|
+
url: wi._links?.html?.href,
|
|
241
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Close a work item with duplicate comment
|
|
245
|
+
*/
|
|
246
|
+
async closeWorkItem(workItemId, originalId) {
|
|
247
|
+
const auth = Buffer.from(`:${this.pat}`).toString('base64');
|
|
248
|
+
const url = `https://dev.azure.com/${this.org}/_apis/wit/workitems/${workItemId}?api-version=7.1`;
|
|
249
|
+
const comment = `## Duplicate of #${originalId}
|
|
250
|
+
|
|
251
|
+
This work item was automatically closed by SpecWeave cleanup because it is a duplicate.
|
|
252
|
+
|
|
253
|
+
The original work item (#${originalId}) contains the same content and should be used for tracking instead.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
🤖 Auto-closed by SpecWeave Duplicate Cleanup`;
|
|
257
|
+
const operations = [
|
|
258
|
+
{
|
|
259
|
+
op: 'add',
|
|
260
|
+
path: '/fields/System.State',
|
|
261
|
+
value: 'Closed',
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
op: 'add',
|
|
265
|
+
path: '/fields/System.History',
|
|
266
|
+
value: comment,
|
|
267
|
+
},
|
|
268
|
+
];
|
|
269
|
+
const response = await fetch(url, {
|
|
270
|
+
method: 'PATCH',
|
|
271
|
+
headers: {
|
|
272
|
+
Authorization: `Basic ${auth}`,
|
|
273
|
+
'Content-Type': 'application/json-patch+json',
|
|
274
|
+
},
|
|
275
|
+
body: JSON.stringify(operations),
|
|
276
|
+
});
|
|
277
|
+
if (!response.ok) {
|
|
278
|
+
const error = await response.text();
|
|
279
|
+
throw new Error(`Failed to close work item: ${response.status} - ${error}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Convenience function for quick duplicate cleanup
|
|
285
|
+
*/
|
|
286
|
+
export async function cleanupAdoDuplicates(featureId, dryRun = false) {
|
|
287
|
+
const detector = new AdoDuplicateDetector();
|
|
288
|
+
return detector.cleanupFeatureDuplicates(featureId, dryRun);
|
|
289
|
+
}
|
|
290
|
+
export default AdoDuplicateDetector;
|
|
291
|
+
//# sourceMappingURL=ado-duplicate-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ado-duplicate-detector.js","sourceRoot":"","sources":["../../../../plugins/specweave-ado/lib/ado-duplicate-detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JIRA Duplicate Detector (v0.33.0)
|
|
3
|
+
*
|
|
4
|
+
* Implements 3-phase duplicate protection for JIRA issues:
|
|
5
|
+
* 1. Detection: Check before create
|
|
6
|
+
* 2. Verification: Count check after create
|
|
7
|
+
* 3. Reflection: Auto-close duplicates
|
|
8
|
+
*
|
|
9
|
+
* Mirrors the GitHub DuplicateDetector pattern for consistency.
|
|
10
|
+
*/
|
|
11
|
+
import { Logger } from '../../../src/utils/logger.js';
|
|
12
|
+
export interface JiraIssue {
|
|
13
|
+
key: string;
|
|
14
|
+
summary: string;
|
|
15
|
+
status: string;
|
|
16
|
+
created: string;
|
|
17
|
+
url?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface DuplicateGroup {
|
|
20
|
+
summary: string;
|
|
21
|
+
issues: JiraIssue[];
|
|
22
|
+
keepIssue: JiraIssue;
|
|
23
|
+
duplicates: JiraIssue[];
|
|
24
|
+
}
|
|
25
|
+
export interface DetectionResult {
|
|
26
|
+
found: boolean;
|
|
27
|
+
existingIssue?: JiraIssue;
|
|
28
|
+
count: number;
|
|
29
|
+
}
|
|
30
|
+
export interface VerificationResult {
|
|
31
|
+
success: boolean;
|
|
32
|
+
expectedCount: number;
|
|
33
|
+
actualCount: number;
|
|
34
|
+
duplicates: JiraIssue[];
|
|
35
|
+
}
|
|
36
|
+
export interface CleanupResult {
|
|
37
|
+
closedCount: number;
|
|
38
|
+
keptCount: number;
|
|
39
|
+
errors: string[];
|
|
40
|
+
}
|
|
41
|
+
export declare class JiraDuplicateDetector {
|
|
42
|
+
private domain;
|
|
43
|
+
private auth;
|
|
44
|
+
private logger;
|
|
45
|
+
constructor(options?: {
|
|
46
|
+
domain?: string;
|
|
47
|
+
email?: string;
|
|
48
|
+
token?: string;
|
|
49
|
+
logger?: Logger;
|
|
50
|
+
});
|
|
51
|
+
/**
|
|
52
|
+
* Phase 1: Check if issue exists before creating
|
|
53
|
+
*/
|
|
54
|
+
checkBeforeCreate(summaryPattern: string, incrementId?: string): Promise<DetectionResult>;
|
|
55
|
+
/**
|
|
56
|
+
* Phase 2: Verify count after creation
|
|
57
|
+
*/
|
|
58
|
+
verifyAfterCreate(summaryPattern: string, expectedCount?: number): Promise<VerificationResult>;
|
|
59
|
+
/**
|
|
60
|
+
* Phase 3: Auto-close duplicates
|
|
61
|
+
*/
|
|
62
|
+
closeDuplicates(duplicates: JiraIssue[], keepIssueKey: string): Promise<CleanupResult>;
|
|
63
|
+
/**
|
|
64
|
+
* Full cleanup: Find and close all duplicates for a feature
|
|
65
|
+
*/
|
|
66
|
+
cleanupFeatureDuplicates(featureId: string, dryRun?: boolean): Promise<{
|
|
67
|
+
groups: DuplicateGroup[];
|
|
68
|
+
totalIssues: number;
|
|
69
|
+
duplicateCount: number;
|
|
70
|
+
closedCount: number;
|
|
71
|
+
}>;
|
|
72
|
+
/**
|
|
73
|
+
* Group issues by summary
|
|
74
|
+
*/
|
|
75
|
+
private groupBySummary;
|
|
76
|
+
/**
|
|
77
|
+
* Search for issues using JQL
|
|
78
|
+
*/
|
|
79
|
+
private searchIssues;
|
|
80
|
+
/**
|
|
81
|
+
* Close an issue with duplicate comment
|
|
82
|
+
*/
|
|
83
|
+
private closeIssue;
|
|
84
|
+
/**
|
|
85
|
+
* Get available transitions for an issue
|
|
86
|
+
*/
|
|
87
|
+
private getTransitions;
|
|
88
|
+
/**
|
|
89
|
+
* Add duplicate comment to issue
|
|
90
|
+
*/
|
|
91
|
+
private addComment;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Convenience function for quick duplicate cleanup
|
|
95
|
+
*/
|
|
96
|
+
export declare function cleanupJiraDuplicates(featureId: string, dryRun?: boolean): Promise<{
|
|
97
|
+
groups: DuplicateGroup[];
|
|
98
|
+
totalIssues: number;
|
|
99
|
+
duplicateCount: number;
|
|
100
|
+
closedCount: number;
|
|
101
|
+
}>;
|
|
102
|
+
export default JiraDuplicateDetector;
|
|
103
|
+
//# sourceMappingURL=jira-duplicate-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jira-duplicate-detector.d.ts","sourceRoot":"","sources":["../../../../plugins/specweave-jira/lib/jira-duplicate-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,EAAiB,MAAM,8BAA8B,CAAC;AAErE,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,SAAS,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,SAAS,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,GAAE;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACZ;IAQN;;OAEG;IACG,iBAAiB,CACrB,cAAc,EAAE,MAAM,EACtB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,eAAe,CAAC;IAoB3B;;OAEG;IACG,iBAAiB,CACrB,cAAc,EAAE,MAAM,EACtB,aAAa,GAAE,MAAU,GACxB,OAAO,CAAC,kBAAkB,CAAC;IAmC9B;;OAEG;IACG,eAAe,CACnB,UAAU,EAAE,SAAS,EAAE,EACvB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,aAAa,CAAC;IAqBzB;;OAEG;IACG,wBAAwB,CAC5B,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,OAAe,GACtB,OAAO,CAAC;QACT,MAAM,EAAE,cAAc,EAAE,CAAC;QACzB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IAyEF;;OAEG;IACH,OAAO,CAAC,cAAc;IA4BtB;;OAEG;YACW,YAAY;IA6B1B;;OAEG;YACW,UAAU;IAwCxB;;OAEG;YACW,cAAc;IAkB5B;;OAEG;YACW,UAAU;CA0CzB;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,OAAe,GACtB,OAAO,CAAC;IACT,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,CAGD;AAED,eAAe,qBAAqB,CAAC"}
|