specweave 0.17.15 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +405 -2495
- package/README.md +92 -2
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.d.ts.map +1 -1
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.js +188 -36
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-status-sync.d.ts +54 -0
- package/dist/plugins/specweave-ado/lib/ado-status-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-status-sync.js +86 -0
- package/dist/plugins/specweave-ado/lib/ado-status-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/duplicate-detector.d.ts +139 -0
- package/dist/plugins/specweave-github/lib/duplicate-detector.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/duplicate-detector.js +389 -0
- package/dist/plugins/specweave-github/lib/duplicate-detector.js.map +1 -0
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.d.ts +26 -0
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.js +249 -0
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-client.d.ts +1 -1
- package/dist/plugins/specweave-github/lib/github-client.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client.js +25 -13
- package/dist/plugins/specweave-github/lib/github-client.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts +83 -0
- package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-epic-sync.js +451 -0
- package/dist/plugins/specweave-github/lib/github-epic-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-status-sync.d.ts +43 -0
- package/dist/plugins/specweave-github/lib/github-status-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-status-sync.js +82 -0
- package/dist/plugins/specweave-github/lib/github-status-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/task-sync.d.ts +5 -0
- package/dist/plugins/specweave-github/lib/task-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/task-sync.js +38 -2
- package/dist/plugins/specweave-github/lib/task-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-epic-sync.d.ts +66 -0
- package/dist/plugins/specweave-jira/lib/jira-epic-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-epic-sync.js +274 -0
- package/dist/plugins/specweave-jira/lib/jira-epic-sync.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts +56 -0
- package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-status-sync.js +93 -0
- package/dist/plugins/specweave-jira/lib/jira-status-sync.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.js +48 -3
- package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
- package/dist/src/core/living-docs/hierarchy-mapper.d.ts +142 -0
- package/dist/src/core/living-docs/hierarchy-mapper.d.ts.map +1 -0
- package/dist/src/core/living-docs/hierarchy-mapper.js +453 -0
- package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -0
- package/dist/src/core/living-docs/index.d.ts +10 -84
- package/dist/src/core/living-docs/index.d.ts.map +1 -1
- package/dist/src/core/living-docs/index.js +10 -164
- package/dist/src/core/living-docs/index.js.map +1 -1
- package/dist/src/core/living-docs/spec-distributor.d.ts +106 -0
- package/dist/src/core/living-docs/spec-distributor.d.ts.map +1 -0
- package/dist/src/core/living-docs/spec-distributor.js +823 -0
- package/dist/src/core/living-docs/spec-distributor.js.map +1 -0
- package/dist/src/core/living-docs/types.d.ts +201 -0
- package/dist/src/core/living-docs/types.d.ts.map +1 -0
- package/dist/src/core/living-docs/types.js +15 -0
- package/dist/src/core/living-docs/types.js.map +1 -0
- package/dist/src/core/logging/prompt-logger.d.ts +70 -0
- package/dist/src/core/logging/prompt-logger.d.ts.map +1 -0
- package/dist/src/core/logging/prompt-logger.js +247 -0
- package/dist/src/core/logging/prompt-logger.js.map +1 -0
- package/dist/src/core/status-line/status-line-manager.d.ts +15 -24
- package/dist/src/core/status-line/status-line-manager.d.ts.map +1 -1
- package/dist/src/core/status-line/status-line-manager.js +33 -70
- package/dist/src/core/status-line/status-line-manager.js.map +1 -1
- package/dist/src/core/status-line/types.d.ts +19 -31
- package/dist/src/core/status-line/types.d.ts.map +1 -1
- package/dist/src/core/status-line/types.js +5 -9
- package/dist/src/core/status-line/types.js.map +1 -1
- package/dist/src/core/sync/conflict-resolver.d.ts +66 -0
- package/dist/src/core/sync/conflict-resolver.d.ts.map +1 -0
- package/dist/src/core/sync/conflict-resolver.js +108 -0
- package/dist/src/core/sync/conflict-resolver.js.map +1 -0
- package/dist/src/core/sync/enhanced-content-builder.d.ts +77 -0
- package/dist/src/core/sync/enhanced-content-builder.d.ts.map +1 -0
- package/dist/src/core/sync/enhanced-content-builder.js +199 -0
- package/dist/src/core/sync/enhanced-content-builder.js.map +1 -0
- package/dist/src/core/sync/label-detector.d.ts +66 -0
- package/dist/src/core/sync/label-detector.d.ts.map +1 -0
- package/dist/src/core/sync/label-detector.js +211 -0
- package/dist/src/core/sync/label-detector.js.map +1 -0
- package/dist/src/core/sync/retry-logic.d.ts +64 -0
- package/dist/src/core/sync/retry-logic.d.ts.map +1 -0
- package/dist/src/core/sync/retry-logic.js +165 -0
- package/dist/src/core/sync/retry-logic.js.map +1 -0
- package/dist/src/core/sync/spec-increment-mapper.d.ts +100 -0
- package/dist/src/core/sync/spec-increment-mapper.d.ts.map +1 -0
- package/dist/src/core/sync/spec-increment-mapper.js +424 -0
- package/dist/src/core/sync/spec-increment-mapper.js.map +1 -0
- package/dist/src/core/sync/status-cache.d.ts +91 -0
- package/dist/src/core/sync/status-cache.d.ts.map +1 -0
- package/dist/src/core/sync/status-cache.js +140 -0
- package/dist/src/core/sync/status-cache.js.map +1 -0
- package/dist/src/core/sync/status-mapper.d.ts +69 -0
- package/dist/src/core/sync/status-mapper.d.ts.map +1 -0
- package/dist/src/core/sync/status-mapper.js +90 -0
- package/dist/src/core/sync/status-mapper.js.map +1 -0
- package/dist/src/core/sync/status-sync-engine.d.ts +162 -0
- package/dist/src/core/sync/status-sync-engine.d.ts.map +1 -0
- package/dist/src/core/sync/status-sync-engine.js +347 -0
- package/dist/src/core/sync/status-sync-engine.js.map +1 -0
- package/dist/src/core/sync/sync-event-logger.d.ts +99 -0
- package/dist/src/core/sync/sync-event-logger.d.ts.map +1 -0
- package/dist/src/core/sync/sync-event-logger.js +103 -0
- package/dist/src/core/sync/sync-event-logger.js.map +1 -0
- package/dist/src/core/sync/workflow-detector.d.ts +95 -0
- package/dist/src/core/sync/workflow-detector.d.ts.map +1 -0
- package/dist/src/core/sync/workflow-detector.js +175 -0
- package/dist/src/core/sync/workflow-detector.js.map +1 -0
- package/dist/src/core/types/config.d.ts.map +1 -1
- package/dist/src/core/types/config.js +31 -0
- package/dist/src/core/types/config.js.map +1 -1
- package/dist/src/utils/github-url.d.ts +53 -0
- package/dist/src/utils/github-url.d.ts.map +1 -0
- package/dist/src/utils/github-url.js +90 -0
- package/dist/src/utils/github-url.js.map +1 -0
- package/dist/src/utils/plugin-validator.d.ts +9 -0
- package/dist/src/utils/plugin-validator.d.ts.map +1 -1
- package/dist/src/utils/plugin-validator.js +86 -19
- package/dist/src/utils/plugin-validator.js.map +1 -1
- package/dist/src/utils/spec-parser.d.ts +145 -0
- package/dist/src/utils/spec-parser.d.ts.map +1 -0
- package/dist/src/utils/spec-parser.js +640 -0
- package/dist/src/utils/spec-parser.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/agents/pm/AGENT.md +1 -1
- package/plugins/specweave/agents/pm/templates/increment-spec.md +158 -0
- package/plugins/specweave/agents/pm/templates/living-docs-spec.md +113 -0
- package/plugins/specweave/commands/specweave-done.md +163 -0
- package/plugins/specweave/hooks/lib/update-status-line.sh +79 -111
- package/plugins/specweave/hooks/post-increment-planning.sh +107 -35
- package/plugins/specweave/lib/hooks/sync-living-docs.js +139 -34
- package/plugins/specweave/lib/hooks/sync-living-docs.ts +234 -38
- package/plugins/specweave/skills/SKILLS-INDEX.md +4 -24
- package/plugins/specweave/skills/increment-planner/SKILL.md +94 -0
- package/plugins/specweave/skills/increment-work-router/SKILL.md +466 -0
- package/plugins/specweave/skills/plugin-validator/SKILL.md +16 -13
- package/plugins/specweave-ado/lib/ado-status-sync.js +80 -0
- package/plugins/specweave-ado/lib/ado-status-sync.ts +121 -0
- package/plugins/specweave-github/commands/specweave-github-cleanup-duplicates.md +205 -0
- package/plugins/specweave-github/commands/specweave-github-sync-epic.md +248 -0
- package/plugins/specweave-github/lib/duplicate-detector.js +370 -0
- package/plugins/specweave-github/lib/duplicate-detector.ts +525 -0
- package/plugins/specweave-github/lib/enhanced-github-sync.js +220 -0
- package/plugins/specweave-github/lib/enhanced-github-sync.ts +322 -0
- package/plugins/specweave-github/lib/github-client.js +21 -10
- package/plugins/specweave-github/lib/github-client.ts +27 -16
- package/plugins/specweave-github/lib/github-epic-sync.js +489 -0
- package/plugins/specweave-github/lib/github-epic-sync.ts +690 -0
- package/plugins/specweave-github/lib/github-status-sync.js +71 -0
- package/plugins/specweave-github/lib/github-status-sync.ts +107 -0
- package/plugins/specweave-github/lib/task-sync.js +33 -2
- package/plugins/specweave-github/lib/task-sync.ts +44 -2
- package/plugins/specweave-jira/commands/specweave-jira-sync-epic.md +267 -0
- package/plugins/specweave-jira/lib/enhanced-jira-sync.ts.disabled +222 -0
- package/plugins/specweave-jira/lib/jira-epic-sync.js +304 -0
- package/plugins/specweave-jira/lib/jira-epic-sync.ts +459 -0
- package/plugins/specweave-jira/lib/jira-status-sync.js +79 -0
- package/plugins/specweave-jira/lib/jira-status-sync.ts +139 -0
- package/src/templates/AGENTS.md.template +88 -1
- package/src/templates/CLAUDE.md.template +49 -0
- package/plugins/specweave/skills/increment-quality-judge/SKILL.md +0 -524
- package/plugins/specweave/skills/plugin-installer/SKILL.md +0 -353
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-cache.js","sourceRoot":"","sources":["../../../../src/core/sync/status-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAYH;;;;GAIG;AACH,MAAM,OAAO,WAAW;IAItB,YAAY,OAAsB;QAChC,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,UAAU,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,oBAAoB;IACvE,CAAC;IAED;;;;;OAKG;IACH,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAElC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,mBAAmB;QACnB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,GAAG,GAAG,KAAK,CAAC,SAAS,CAAC;QAElC,IAAI,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;YACpB,wCAAwC;YACxC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IAED;;;;;;OAMG;IACH,GAAG,CAAC,GAAW,EAAE,KAAQ,EAAE,GAAY;QACrC,MAAM,KAAK,GAAkB;YAC3B,KAAK;YACL,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,UAAU;SAC5B,CAAC;QAEF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED;;;;;OAKG;IACH,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC;IACrC,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,GAAW;QAChB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED;;;;OAIG;IACH,IAAI;QACF,iCAAiC;QACjC,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAED;;;;OAIG;IACH,OAAO;QACL,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,YAAY,GAAa,EAAE,CAAC;QAElC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAChD,MAAM,GAAG,GAAG,GAAG,GAAG,KAAK,CAAC,SAAS,CAAC;YAClC,IAAI,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;gBACpB,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;QAED,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;YAC/B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,QAAQ;QACN,IAAI,CAAC,OAAO,EAAE,CAAC;QAEf,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;QAC3C,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,eAAe,GAAG,GAAG,CAAC;QAE1B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACxC,IAAI,KAAK,CAAC,SAAS,GAAG,eAAe,EAAE,CAAC;gBACtC,eAAe,GAAG,KAAK,CAAC,SAAS,CAAC;YACpC,CAAC;QACH,CAAC;QAED,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI;YACrB,cAAc,EAAE,GAAG,GAAG,eAAe;SACtC,CAAC;IACJ,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status Mapper
|
|
3
|
+
*
|
|
4
|
+
* Maps SpecWeave increment statuses to tool-specific statuses for
|
|
5
|
+
* GitHub, JIRA, and Azure DevOps.
|
|
6
|
+
*
|
|
7
|
+
* Supports both simple string mappings and complex mappings with
|
|
8
|
+
* labels/tags for more granular status representation.
|
|
9
|
+
*/
|
|
10
|
+
export type ExternalTool = 'github' | 'jira' | 'ado';
|
|
11
|
+
export type SpecWeaveStatus = 'planning' | 'active' | 'paused' | 'completed' | 'abandoned';
|
|
12
|
+
export interface StatusMappingConfig {
|
|
13
|
+
state: string;
|
|
14
|
+
labels?: string[];
|
|
15
|
+
tags?: string[];
|
|
16
|
+
}
|
|
17
|
+
export type StatusMappingValue = string | StatusMappingConfig;
|
|
18
|
+
export interface ToolStatusMappings {
|
|
19
|
+
planning: StatusMappingValue;
|
|
20
|
+
active: StatusMappingValue;
|
|
21
|
+
paused: StatusMappingValue;
|
|
22
|
+
completed: StatusMappingValue;
|
|
23
|
+
abandoned: StatusMappingValue;
|
|
24
|
+
}
|
|
25
|
+
export interface StatusSyncConfig {
|
|
26
|
+
enabled: boolean;
|
|
27
|
+
autoSync?: boolean;
|
|
28
|
+
promptUser?: boolean;
|
|
29
|
+
conflictResolution?: 'prompt' | 'last-write-wins' | 'specweave-wins' | 'external-wins';
|
|
30
|
+
mappings: {
|
|
31
|
+
github?: ToolStatusMappings;
|
|
32
|
+
jira?: ToolStatusMappings;
|
|
33
|
+
ado?: ToolStatusMappings;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export interface ValidationResult {
|
|
37
|
+
valid: boolean;
|
|
38
|
+
errors: string[];
|
|
39
|
+
}
|
|
40
|
+
export interface SyncConfig {
|
|
41
|
+
sync: {
|
|
42
|
+
statusSync: StatusSyncConfig;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export declare class StatusMapper {
|
|
46
|
+
private config;
|
|
47
|
+
constructor(config: SyncConfig);
|
|
48
|
+
/**
|
|
49
|
+
* Map SpecWeave status to external tool status
|
|
50
|
+
*/
|
|
51
|
+
mapToExternal(specweaveStatus: string, tool: ExternalTool): StatusMappingConfig;
|
|
52
|
+
/**
|
|
53
|
+
* Map external tool status to SpecWeave status (reverse lookup)
|
|
54
|
+
*/
|
|
55
|
+
mapFromExternal(externalStatus: string, tool: ExternalTool): SpecWeaveStatus | null;
|
|
56
|
+
/**
|
|
57
|
+
* Validate status mapping configuration
|
|
58
|
+
*/
|
|
59
|
+
validate(): ValidationResult;
|
|
60
|
+
/**
|
|
61
|
+
* Get all required SpecWeave statuses
|
|
62
|
+
*/
|
|
63
|
+
getRequiredStatuses(): SpecWeaveStatus[];
|
|
64
|
+
/**
|
|
65
|
+
* Get all supported external tools
|
|
66
|
+
*/
|
|
67
|
+
getSupportedTools(): ExternalTool[];
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=status-mapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-mapper.d.ts","sourceRoot":"","sources":["../../../../src/core/sync/status-mapper.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC;AAErD,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC;AAE3F,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,mBAAmB,CAAC;AAE9D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,SAAS,EAAE,kBAAkB,CAAC;IAC9B,SAAS,EAAE,kBAAkB,CAAC;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kBAAkB,CAAC,EAAE,QAAQ,GAAG,iBAAiB,GAAG,gBAAgB,GAAG,eAAe,CAAC;IACvF,QAAQ,EAAE;QACR,MAAM,CAAC,EAAE,kBAAkB,CAAC;QAC5B,IAAI,CAAC,EAAE,kBAAkB,CAAC;QAC1B,GAAG,CAAC,EAAE,kBAAkB,CAAC;KAC1B,CAAC;CACH;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE;QACJ,UAAU,EAAE,gBAAgB,CAAC;KAC9B,CAAC;CACH;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAa;gBAEf,MAAM,EAAE,UAAU;IAI9B;;OAEG;IACI,aAAa,CAClB,eAAe,EAAE,MAAM,EACvB,IAAI,EAAE,YAAY,GACjB,mBAAmB;IAqBtB;;OAEG;IACI,eAAe,CACpB,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,YAAY,GACjB,eAAe,GAAG,IAAI;IAkBzB;;OAEG;IACI,QAAQ,IAAI,gBAAgB;IA+BnC;;OAEG;IACI,mBAAmB,IAAI,eAAe,EAAE;IAI/C;;OAEG;IACI,iBAAiB,IAAI,YAAY,EAAE;CAG3C"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status Mapper
|
|
3
|
+
*
|
|
4
|
+
* Maps SpecWeave increment statuses to tool-specific statuses for
|
|
5
|
+
* GitHub, JIRA, and Azure DevOps.
|
|
6
|
+
*
|
|
7
|
+
* Supports both simple string mappings and complex mappings with
|
|
8
|
+
* labels/tags for more granular status representation.
|
|
9
|
+
*/
|
|
10
|
+
export class StatusMapper {
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Map SpecWeave status to external tool status
|
|
16
|
+
*/
|
|
17
|
+
mapToExternal(specweaveStatus, tool) {
|
|
18
|
+
const mappings = this.config.sync.statusSync.mappings[tool];
|
|
19
|
+
if (!mappings) {
|
|
20
|
+
throw new Error(`No status mappings configured for ${tool}`);
|
|
21
|
+
}
|
|
22
|
+
const mapping = mappings[specweaveStatus];
|
|
23
|
+
if (!mapping) {
|
|
24
|
+
throw new Error(`No mapping for status "${specweaveStatus}" in ${tool}`);
|
|
25
|
+
}
|
|
26
|
+
// Normalize to StatusMappingConfig
|
|
27
|
+
if (typeof mapping === 'string') {
|
|
28
|
+
return { state: mapping };
|
|
29
|
+
}
|
|
30
|
+
return mapping;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Map external tool status to SpecWeave status (reverse lookup)
|
|
34
|
+
*/
|
|
35
|
+
mapFromExternal(externalStatus, tool) {
|
|
36
|
+
const mappings = this.config.sync.statusSync.mappings[tool];
|
|
37
|
+
if (!mappings) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
for (const [specweaveStatus, mapping] of Object.entries(mappings)) {
|
|
41
|
+
const state = typeof mapping === 'string' ? mapping : mapping.state;
|
|
42
|
+
if (state === externalStatus) {
|
|
43
|
+
return specweaveStatus;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null; // Unknown external status
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Validate status mapping configuration
|
|
50
|
+
*/
|
|
51
|
+
validate() {
|
|
52
|
+
const errors = [];
|
|
53
|
+
const requiredStatuses = [
|
|
54
|
+
'planning',
|
|
55
|
+
'active',
|
|
56
|
+
'paused',
|
|
57
|
+
'completed',
|
|
58
|
+
'abandoned'
|
|
59
|
+
];
|
|
60
|
+
for (const tool of ['github', 'jira', 'ado']) {
|
|
61
|
+
const mappings = this.config.sync.statusSync.mappings[tool];
|
|
62
|
+
if (!mappings) {
|
|
63
|
+
errors.push(`No mappings configured for ${tool}`);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
for (const status of requiredStatuses) {
|
|
67
|
+
if (!mappings[status]) {
|
|
68
|
+
errors.push(`Missing mapping for "${status}" in ${tool}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
valid: errors.length === 0,
|
|
74
|
+
errors
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get all required SpecWeave statuses
|
|
79
|
+
*/
|
|
80
|
+
getRequiredStatuses() {
|
|
81
|
+
return ['planning', 'active', 'paused', 'completed', 'abandoned'];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get all supported external tools
|
|
85
|
+
*/
|
|
86
|
+
getSupportedTools() {
|
|
87
|
+
return ['github', 'jira', 'ado'];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=status-mapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-mapper.js","sourceRoot":"","sources":["../../../../src/core/sync/status-mapper.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA6CH,MAAM,OAAO,YAAY;IAGvB,YAAY,MAAkB;QAC5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;OAEG;IACI,aAAa,CAClB,eAAuB,EACvB,IAAkB;QAElB,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAE5D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,EAAE,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,OAAO,GAAG,QAAQ,CAAC,eAAkC,CAAC,CAAC;QAE7D,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,0BAA0B,eAAe,QAAQ,IAAI,EAAE,CAAC,CAAC;QAC3E,CAAC;QAED,mCAAmC;QACnC,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAChC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QAC5B,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACI,eAAe,CACpB,cAAsB,EACtB,IAAkB;QAElB,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAE5D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,IAAI,CAAC;QACd,CAAC;QAED,KAAK,MAAM,CAAC,eAAe,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YAClE,MAAM,KAAK,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;YAEpE,IAAI,KAAK,KAAK,cAAc,EAAE,CAAC;gBAC7B,OAAO,eAAkC,CAAC;YAC5C,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC,CAAC,0BAA0B;IACzC,CAAC;IAED;;OAEG;IACI,QAAQ;QACb,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,MAAM,gBAAgB,GAAsB;YAC1C,UAAU;YACV,QAAQ;YACR,QAAQ;YACR,WAAW;YACX,WAAW;SACZ,CAAC;QAEF,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAU,EAAE,CAAC;YACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAE5D,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,CAAC,8BAA8B,IAAI,EAAE,CAAC,CAAC;gBAClD,SAAS;YACX,CAAC;YAED,KAAK,MAAM,MAAM,IAAI,gBAAgB,EAAE,CAAC;gBACtC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;oBACtB,MAAM,CAAC,IAAI,CAAC,wBAAwB,MAAM,QAAQ,IAAI,EAAE,CAAC,CAAC;gBAC5D,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO;YACL,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;YAC1B,MAAM;SACP,CAAC;IACJ,CAAC;IAED;;OAEG;IACI,mBAAmB;QACxB,OAAO,CAAC,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;IACpE,CAAC;IAED;;OAEG;IACI,iBAAiB;QACtB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;CACF"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status Sync Engine
|
|
3
|
+
*
|
|
4
|
+
* Core orchestration logic for status synchronization between SpecWeave
|
|
5
|
+
* increments and external tools (GitHub, JIRA, Azure DevOps).
|
|
6
|
+
*
|
|
7
|
+
* Responsibilities:
|
|
8
|
+
* - Orchestrate bidirectional status sync
|
|
9
|
+
* - Detect and resolve conflicts
|
|
10
|
+
* - Map statuses between SpecWeave and external tools
|
|
11
|
+
* - Respect user preferences (autoSync, promptUser, conflictResolution)
|
|
12
|
+
*/
|
|
13
|
+
import { StatusMappingConfig, ExternalTool, SpecWeaveStatus } from './status-mapper';
|
|
14
|
+
import { StatusConflict, ConflictResolution, ConflictResolutionStrategy } from './conflict-resolver';
|
|
15
|
+
export type SyncDirection = 'to-external' | 'from-external' | 'bidirectional';
|
|
16
|
+
export type SyncAction = 'no-sync-needed' | 'sync-to-external' | 'sync-from-external';
|
|
17
|
+
export interface SyncInput {
|
|
18
|
+
incrementId: string;
|
|
19
|
+
tool: ExternalTool;
|
|
20
|
+
localStatus: SpecWeaveStatus;
|
|
21
|
+
remoteStatus: string;
|
|
22
|
+
localTimestamp: string;
|
|
23
|
+
remoteTimestamp: string;
|
|
24
|
+
}
|
|
25
|
+
export interface SyncResult {
|
|
26
|
+
success: boolean;
|
|
27
|
+
direction: SyncDirection;
|
|
28
|
+
action: SyncAction;
|
|
29
|
+
conflict: StatusConflict | null;
|
|
30
|
+
resolution: ConflictResolution | null;
|
|
31
|
+
externalMapping: StatusMappingConfig | null;
|
|
32
|
+
error?: string;
|
|
33
|
+
wasAutomatic?: boolean;
|
|
34
|
+
wasPrompted?: boolean;
|
|
35
|
+
}
|
|
36
|
+
export interface StatusSyncConfig {
|
|
37
|
+
sync: {
|
|
38
|
+
statusSync: {
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
autoSync?: boolean;
|
|
41
|
+
promptUser?: boolean;
|
|
42
|
+
conflictResolution?: ConflictResolutionStrategy;
|
|
43
|
+
mappings: {
|
|
44
|
+
github?: any;
|
|
45
|
+
jira?: any;
|
|
46
|
+
ado?: any;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Bulk sync input (multiple increments)
|
|
53
|
+
*/
|
|
54
|
+
export type BulkSyncInput = SyncInput;
|
|
55
|
+
/**
|
|
56
|
+
* Bulk sync options
|
|
57
|
+
*/
|
|
58
|
+
export interface BulkSyncOptions {
|
|
59
|
+
batchSize?: number;
|
|
60
|
+
delayMs?: number;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Individual sync result with increment ID
|
|
64
|
+
*/
|
|
65
|
+
export interface BulkSyncItemResult extends SyncResult {
|
|
66
|
+
incrementId: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Bulk sync result
|
|
70
|
+
*/
|
|
71
|
+
export interface BulkSyncResult {
|
|
72
|
+
totalItems: number;
|
|
73
|
+
successCount: number;
|
|
74
|
+
failureCount: number;
|
|
75
|
+
results: BulkSyncItemResult[];
|
|
76
|
+
duration: string;
|
|
77
|
+
}
|
|
78
|
+
export declare class StatusSyncEngine {
|
|
79
|
+
private mapper;
|
|
80
|
+
private resolver;
|
|
81
|
+
private config;
|
|
82
|
+
constructor(config: StatusSyncConfig);
|
|
83
|
+
/**
|
|
84
|
+
* Sync SpecWeave status to external tool (one-way: SpecWeave → External)
|
|
85
|
+
*
|
|
86
|
+
* @param input - Sync input with local/remote statuses
|
|
87
|
+
* @returns Sync result with conflict resolution details
|
|
88
|
+
*/
|
|
89
|
+
syncToExternal(input: SyncInput): Promise<SyncResult>;
|
|
90
|
+
/**
|
|
91
|
+
* Sync external status to SpecWeave (one-way: External → SpecWeave)
|
|
92
|
+
*
|
|
93
|
+
* @param input - Sync input with local/remote statuses
|
|
94
|
+
* @returns Sync result with conflict resolution details
|
|
95
|
+
*/
|
|
96
|
+
syncFromExternal(input: SyncInput): Promise<SyncResult>;
|
|
97
|
+
/**
|
|
98
|
+
* Bidirectional sync (SpecWeave ↔ External)
|
|
99
|
+
*
|
|
100
|
+
* Detects conflict and resolves based on strategy.
|
|
101
|
+
* Syncs in the direction indicated by resolution.
|
|
102
|
+
*
|
|
103
|
+
* @param input - Sync input with local/remote statuses
|
|
104
|
+
* @returns Sync result with conflict resolution details
|
|
105
|
+
*/
|
|
106
|
+
bidirectionalSync(input: SyncInput): Promise<SyncResult>;
|
|
107
|
+
/**
|
|
108
|
+
* Bulk sync multiple increments to external tools
|
|
109
|
+
*
|
|
110
|
+
* Batches requests to avoid rate limiting.
|
|
111
|
+
* Adds delays between batches.
|
|
112
|
+
* Returns aggregate results with success/failure counts.
|
|
113
|
+
*
|
|
114
|
+
* @param inputs - Array of sync inputs
|
|
115
|
+
* @param options - Bulk sync options (batch size, delay)
|
|
116
|
+
* @returns Bulk sync result
|
|
117
|
+
*/
|
|
118
|
+
bulkSyncToExternal(inputs: BulkSyncInput[], options?: BulkSyncOptions): Promise<BulkSyncResult>;
|
|
119
|
+
/**
|
|
120
|
+
* Check if auto-sync is enabled
|
|
121
|
+
*
|
|
122
|
+
* @returns True if auto-sync is enabled
|
|
123
|
+
*/
|
|
124
|
+
isAutoSyncEnabled(): boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Check if user should be prompted
|
|
127
|
+
*
|
|
128
|
+
* @returns True if user should be prompted (default: true)
|
|
129
|
+
*/
|
|
130
|
+
shouldPromptUser(): boolean;
|
|
131
|
+
/**
|
|
132
|
+
* Execute automatic sync without prompting
|
|
133
|
+
*
|
|
134
|
+
* Validates that auto-sync is enabled.
|
|
135
|
+
* Handles errors gracefully (doesn't throw, returns error in result).
|
|
136
|
+
*
|
|
137
|
+
* @param input - Sync input
|
|
138
|
+
* @returns Sync result with wasAutomatic flag
|
|
139
|
+
*/
|
|
140
|
+
executeAutoSync(input: SyncInput): Promise<SyncResult>;
|
|
141
|
+
/**
|
|
142
|
+
* Calculate batches from input array
|
|
143
|
+
*
|
|
144
|
+
* @param inputs - Array of inputs
|
|
145
|
+
* @param batchSize - Size of each batch
|
|
146
|
+
* @returns Array of batches
|
|
147
|
+
*/
|
|
148
|
+
private calculateBatches;
|
|
149
|
+
/**
|
|
150
|
+
* Delay execution for specified milliseconds
|
|
151
|
+
*
|
|
152
|
+
* @param ms - Milliseconds to delay
|
|
153
|
+
*/
|
|
154
|
+
private delay;
|
|
155
|
+
/**
|
|
156
|
+
* Validate that status synchronization is enabled
|
|
157
|
+
*
|
|
158
|
+
* @throws Error if sync is disabled
|
|
159
|
+
*/
|
|
160
|
+
private validateSyncEnabled;
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=status-sync-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-sync-engine.d.ts","sourceRoot":"","sources":["../../../../src/core/sync/status-sync-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status Sync Engine
|
|
3
|
+
*
|
|
4
|
+
* Core orchestration logic for status synchronization between SpecWeave
|
|
5
|
+
* increments and external tools (GitHub, JIRA, Azure DevOps).
|
|
6
|
+
*
|
|
7
|
+
* Responsibilities:
|
|
8
|
+
* - Orchestrate bidirectional status sync
|
|
9
|
+
* - Detect and resolve conflicts
|
|
10
|
+
* - Map statuses between SpecWeave and external tools
|
|
11
|
+
* - Respect user preferences (autoSync, promptUser, conflictResolution)
|
|
12
|
+
*/
|
|
13
|
+
import { StatusMapper } from './status-mapper';
|
|
14
|
+
import { ConflictResolver } from './conflict-resolver';
|
|
15
|
+
export class StatusSyncEngine {
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.config = config;
|
|
18
|
+
this.mapper = new StatusMapper(config);
|
|
19
|
+
this.resolver = new ConflictResolver();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Sync SpecWeave status to external tool (one-way: SpecWeave → External)
|
|
23
|
+
*
|
|
24
|
+
* @param input - Sync input with local/remote statuses
|
|
25
|
+
* @returns Sync result with conflict resolution details
|
|
26
|
+
*/
|
|
27
|
+
async syncToExternal(input) {
|
|
28
|
+
this.validateSyncEnabled();
|
|
29
|
+
const result = {
|
|
30
|
+
success: false,
|
|
31
|
+
direction: 'to-external',
|
|
32
|
+
action: 'no-sync-needed',
|
|
33
|
+
conflict: null,
|
|
34
|
+
resolution: null,
|
|
35
|
+
externalMapping: null
|
|
36
|
+
};
|
|
37
|
+
try {
|
|
38
|
+
// Detect conflict
|
|
39
|
+
const conflict = await this.resolver.detect({
|
|
40
|
+
incrementId: input.incrementId,
|
|
41
|
+
local: input.localStatus,
|
|
42
|
+
remote: input.remoteStatus,
|
|
43
|
+
tool: input.tool,
|
|
44
|
+
localTimestamp: input.localTimestamp,
|
|
45
|
+
remoteTimestamp: input.remoteTimestamp
|
|
46
|
+
});
|
|
47
|
+
result.conflict = conflict;
|
|
48
|
+
// No conflict - statuses already match
|
|
49
|
+
if (!conflict) {
|
|
50
|
+
result.success = true;
|
|
51
|
+
result.action = 'no-sync-needed';
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
// Resolve conflict
|
|
55
|
+
const strategy = this.config.sync.statusSync.conflictResolution || 'last-write-wins';
|
|
56
|
+
const resolution = await this.resolver.resolve(conflict, strategy);
|
|
57
|
+
result.resolution = resolution;
|
|
58
|
+
// Determine action based on resolution
|
|
59
|
+
if (resolution.action === 'use-local') {
|
|
60
|
+
result.action = 'sync-to-external';
|
|
61
|
+
result.externalMapping = this.mapper.mapToExternal(input.localStatus, input.tool);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// Resolution says use-remote, but we're syncing TO external
|
|
65
|
+
// This means external is already correct, no sync needed
|
|
66
|
+
result.action = 'no-sync-needed';
|
|
67
|
+
}
|
|
68
|
+
result.success = true;
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
result.success = false;
|
|
73
|
+
result.error = error instanceof Error ? error.message : 'Unknown error';
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Sync external status to SpecWeave (one-way: External → SpecWeave)
|
|
79
|
+
*
|
|
80
|
+
* @param input - Sync input with local/remote statuses
|
|
81
|
+
* @returns Sync result with conflict resolution details
|
|
82
|
+
*/
|
|
83
|
+
async syncFromExternal(input) {
|
|
84
|
+
this.validateSyncEnabled();
|
|
85
|
+
const result = {
|
|
86
|
+
success: false,
|
|
87
|
+
direction: 'from-external',
|
|
88
|
+
action: 'no-sync-needed',
|
|
89
|
+
conflict: null,
|
|
90
|
+
resolution: null,
|
|
91
|
+
externalMapping: null
|
|
92
|
+
};
|
|
93
|
+
try {
|
|
94
|
+
// Detect conflict
|
|
95
|
+
const conflict = await this.resolver.detect({
|
|
96
|
+
incrementId: input.incrementId,
|
|
97
|
+
local: input.localStatus,
|
|
98
|
+
remote: input.remoteStatus,
|
|
99
|
+
tool: input.tool,
|
|
100
|
+
localTimestamp: input.localTimestamp,
|
|
101
|
+
remoteTimestamp: input.remoteTimestamp
|
|
102
|
+
});
|
|
103
|
+
result.conflict = conflict;
|
|
104
|
+
// No conflict - statuses already match
|
|
105
|
+
if (!conflict) {
|
|
106
|
+
result.success = true;
|
|
107
|
+
result.action = 'no-sync-needed';
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
// Resolve conflict
|
|
111
|
+
const strategy = this.config.sync.statusSync.conflictResolution || 'last-write-wins';
|
|
112
|
+
const resolution = await this.resolver.resolve(conflict, strategy);
|
|
113
|
+
result.resolution = resolution;
|
|
114
|
+
// Determine action based on resolution
|
|
115
|
+
if (resolution.action === 'use-remote') {
|
|
116
|
+
result.action = 'sync-from-external';
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Resolution says use-local, but we're syncing FROM external
|
|
120
|
+
// This means local is already correct, no sync needed
|
|
121
|
+
result.action = 'no-sync-needed';
|
|
122
|
+
}
|
|
123
|
+
result.success = true;
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
result.success = false;
|
|
128
|
+
result.error = error instanceof Error ? error.message : 'Unknown error';
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Bidirectional sync (SpecWeave ↔ External)
|
|
134
|
+
*
|
|
135
|
+
* Detects conflict and resolves based on strategy.
|
|
136
|
+
* Syncs in the direction indicated by resolution.
|
|
137
|
+
*
|
|
138
|
+
* @param input - Sync input with local/remote statuses
|
|
139
|
+
* @returns Sync result with conflict resolution details
|
|
140
|
+
*/
|
|
141
|
+
async bidirectionalSync(input) {
|
|
142
|
+
this.validateSyncEnabled();
|
|
143
|
+
const result = {
|
|
144
|
+
success: false,
|
|
145
|
+
direction: 'bidirectional',
|
|
146
|
+
action: 'no-sync-needed',
|
|
147
|
+
conflict: null,
|
|
148
|
+
resolution: null,
|
|
149
|
+
externalMapping: null
|
|
150
|
+
};
|
|
151
|
+
try {
|
|
152
|
+
// Detect conflict
|
|
153
|
+
const conflict = await this.resolver.detect({
|
|
154
|
+
incrementId: input.incrementId,
|
|
155
|
+
local: input.localStatus,
|
|
156
|
+
remote: input.remoteStatus,
|
|
157
|
+
tool: input.tool,
|
|
158
|
+
localTimestamp: input.localTimestamp,
|
|
159
|
+
remoteTimestamp: input.remoteTimestamp
|
|
160
|
+
});
|
|
161
|
+
result.conflict = conflict;
|
|
162
|
+
// No conflict - statuses already match
|
|
163
|
+
if (!conflict) {
|
|
164
|
+
result.success = true;
|
|
165
|
+
result.action = 'no-sync-needed';
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
// Resolve conflict
|
|
169
|
+
const strategy = this.config.sync.statusSync.conflictResolution || 'last-write-wins';
|
|
170
|
+
const resolution = await this.resolver.resolve(conflict, strategy);
|
|
171
|
+
result.resolution = resolution;
|
|
172
|
+
// Determine action based on resolution
|
|
173
|
+
if (resolution.action === 'use-local') {
|
|
174
|
+
result.action = 'sync-to-external';
|
|
175
|
+
result.externalMapping = this.mapper.mapToExternal(input.localStatus, input.tool);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
result.action = 'sync-from-external';
|
|
179
|
+
}
|
|
180
|
+
result.success = true;
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
result.success = false;
|
|
185
|
+
result.error = error instanceof Error ? error.message : 'Unknown error';
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Bulk sync multiple increments to external tools
|
|
191
|
+
*
|
|
192
|
+
* Batches requests to avoid rate limiting.
|
|
193
|
+
* Adds delays between batches.
|
|
194
|
+
* Returns aggregate results with success/failure counts.
|
|
195
|
+
*
|
|
196
|
+
* @param inputs - Array of sync inputs
|
|
197
|
+
* @param options - Bulk sync options (batch size, delay)
|
|
198
|
+
* @returns Bulk sync result
|
|
199
|
+
*/
|
|
200
|
+
async bulkSyncToExternal(inputs, options) {
|
|
201
|
+
const startTime = Date.now();
|
|
202
|
+
const batchSize = options?.batchSize || 5;
|
|
203
|
+
const delayMs = options?.delayMs || 1000;
|
|
204
|
+
const results = [];
|
|
205
|
+
let successCount = 0;
|
|
206
|
+
let failureCount = 0;
|
|
207
|
+
// Calculate batches
|
|
208
|
+
const batches = this.calculateBatches(inputs, batchSize);
|
|
209
|
+
// Process each batch
|
|
210
|
+
for (let i = 0; i < batches.length; i++) {
|
|
211
|
+
const batch = batches[i];
|
|
212
|
+
// Process all items in batch concurrently
|
|
213
|
+
const batchResults = await Promise.allSettled(batch.map(input => this.syncToExternal(input)));
|
|
214
|
+
// Collect results
|
|
215
|
+
for (let j = 0; j < batchResults.length; j++) {
|
|
216
|
+
const result = batchResults[j];
|
|
217
|
+
const input = batch[j];
|
|
218
|
+
if (result.status === 'fulfilled') {
|
|
219
|
+
results.push({
|
|
220
|
+
...result.value,
|
|
221
|
+
incrementId: input.incrementId
|
|
222
|
+
});
|
|
223
|
+
if (result.value.success) {
|
|
224
|
+
successCount++;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
failureCount++;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
// Rejection
|
|
232
|
+
results.push({
|
|
233
|
+
incrementId: input.incrementId,
|
|
234
|
+
success: false,
|
|
235
|
+
direction: 'to-external',
|
|
236
|
+
action: 'no-sync-needed',
|
|
237
|
+
conflict: null,
|
|
238
|
+
resolution: null,
|
|
239
|
+
externalMapping: null,
|
|
240
|
+
error: result.reason.message || 'Unknown error'
|
|
241
|
+
});
|
|
242
|
+
failureCount++;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// Add delay between batches (except after last batch)
|
|
246
|
+
if (i < batches.length - 1) {
|
|
247
|
+
await this.delay(delayMs);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
const duration = `${Date.now() - startTime}ms`;
|
|
251
|
+
return {
|
|
252
|
+
totalItems: inputs.length,
|
|
253
|
+
successCount,
|
|
254
|
+
failureCount,
|
|
255
|
+
results,
|
|
256
|
+
duration
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Check if auto-sync is enabled
|
|
261
|
+
*
|
|
262
|
+
* @returns True if auto-sync is enabled
|
|
263
|
+
*/
|
|
264
|
+
isAutoSyncEnabled() {
|
|
265
|
+
return this.config.sync.statusSync.autoSync === true;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Check if user should be prompted
|
|
269
|
+
*
|
|
270
|
+
* @returns True if user should be prompted (default: true)
|
|
271
|
+
*/
|
|
272
|
+
shouldPromptUser() {
|
|
273
|
+
// Default to true if not specified
|
|
274
|
+
return this.config.sync.statusSync.promptUser !== false;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Execute automatic sync without prompting
|
|
278
|
+
*
|
|
279
|
+
* Validates that auto-sync is enabled.
|
|
280
|
+
* Handles errors gracefully (doesn't throw, returns error in result).
|
|
281
|
+
*
|
|
282
|
+
* @param input - Sync input
|
|
283
|
+
* @returns Sync result with wasAutomatic flag
|
|
284
|
+
*/
|
|
285
|
+
async executeAutoSync(input) {
|
|
286
|
+
// Validate auto-sync is enabled
|
|
287
|
+
if (!this.isAutoSyncEnabled()) {
|
|
288
|
+
throw new Error('Auto-sync is disabled');
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
const result = await this.syncToExternal(input);
|
|
292
|
+
return {
|
|
293
|
+
...result,
|
|
294
|
+
wasAutomatic: true,
|
|
295
|
+
wasPrompted: false
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
// Handle errors gracefully - return error in result
|
|
300
|
+
// Don't throw to avoid blocking increment completion
|
|
301
|
+
return {
|
|
302
|
+
success: false,
|
|
303
|
+
direction: 'to-external',
|
|
304
|
+
action: 'no-sync-needed',
|
|
305
|
+
conflict: null,
|
|
306
|
+
resolution: null,
|
|
307
|
+
externalMapping: null,
|
|
308
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
309
|
+
wasAutomatic: true,
|
|
310
|
+
wasPrompted: false
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Calculate batches from input array
|
|
316
|
+
*
|
|
317
|
+
* @param inputs - Array of inputs
|
|
318
|
+
* @param batchSize - Size of each batch
|
|
319
|
+
* @returns Array of batches
|
|
320
|
+
*/
|
|
321
|
+
calculateBatches(inputs, batchSize) {
|
|
322
|
+
const batches = [];
|
|
323
|
+
for (let i = 0; i < inputs.length; i += batchSize) {
|
|
324
|
+
batches.push(inputs.slice(i, i + batchSize));
|
|
325
|
+
}
|
|
326
|
+
return batches;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Delay execution for specified milliseconds
|
|
330
|
+
*
|
|
331
|
+
* @param ms - Milliseconds to delay
|
|
332
|
+
*/
|
|
333
|
+
delay(ms) {
|
|
334
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Validate that status synchronization is enabled
|
|
338
|
+
*
|
|
339
|
+
* @throws Error if sync is disabled
|
|
340
|
+
*/
|
|
341
|
+
validateSyncEnabled() {
|
|
342
|
+
if (!this.config.sync.statusSync.enabled) {
|
|
343
|
+
throw new Error('Status synchronization is disabled');
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
//# sourceMappingURL=status-sync-engine.js.map
|