specweave 0.32.2 → 0.32.5
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 +51 -9
- 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-multiproject.js +1 -1
- package/dist/src/cli/commands/init-multiproject.js.map +1 -1
- 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/commands/migrate-to-multiproject.js +2 -2
- package/dist/src/cli/commands/migrate-to-multiproject.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/cross-project-sync.d.ts +97 -0
- package/dist/src/core/living-docs/cross-project-sync.d.ts.map +1 -0
- package/dist/src/core/living-docs/cross-project-sync.js +135 -0
- package/dist/src/core/living-docs/cross-project-sync.js.map +1 -0
- package/dist/src/core/living-docs/external-sync-orchestrator.d.ts +106 -0
- package/dist/src/core/living-docs/external-sync-orchestrator.d.ts.map +1 -0
- package/dist/src/core/living-docs/external-sync-orchestrator.js +146 -0
- package/dist/src/core/living-docs/external-sync-orchestrator.js.map +1 -0
- 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 +7 -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 +94 -10
- 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/living-docs/sync-helpers/generators.d.ts +8 -1
- package/dist/src/core/living-docs/sync-helpers/generators.d.ts.map +1 -1
- package/dist/src/core/living-docs/sync-helpers/generators.js +18 -1
- package/dist/src/core/living-docs/sync-helpers/generators.js.map +1 -1
- package/dist/src/core/living-docs/sync-helpers/index.d.ts +1 -1
- package/dist/src/core/living-docs/sync-helpers/index.d.ts.map +1 -1
- package/dist/src/core/living-docs/sync-helpers/index.js.map +1 -1
- package/dist/src/core/living-docs/sync-helpers/parsers.d.ts +3 -1
- package/dist/src/core/living-docs/sync-helpers/parsers.d.ts.map +1 -1
- package/dist/src/core/living-docs/sync-helpers/parsers.js +24 -2
- package/dist/src/core/living-docs/sync-helpers/parsers.js.map +1 -1
- package/dist/src/core/living-docs/types.d.ts +6 -0
- package/dist/src/core/living-docs/types.d.ts.map +1 -1
- package/dist/src/core/living-docs/validators/index.d.ts +7 -0
- package/dist/src/core/living-docs/validators/index.d.ts.map +1 -0
- package/dist/src/core/living-docs/validators/index.js +7 -0
- package/dist/src/core/living-docs/validators/index.js.map +1 -0
- package/dist/src/core/living-docs/validators/project-validator.d.ts +92 -0
- package/dist/src/core/living-docs/validators/project-validator.d.ts.map +1 -0
- package/dist/src/core/living-docs/validators/project-validator.js +142 -0
- package/dist/src/core/living-docs/validators/project-validator.js.map +1 -0
- 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/core/project/project-manager.d.ts.map +1 -1
- package/dist/src/core/project/project-manager.js +19 -17
- package/dist/src/core/project/project-manager.js.map +1 -1
- package/dist/src/core/types/config.d.ts +4 -2
- package/dist/src/core/types/config.d.ts.map +1 -1
- package/dist/src/core/types/config.js.map +1 -1
- package/dist/src/core/types/increment-metadata.d.ts +34 -0
- package/dist/src/core/types/increment-metadata.d.ts.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/cross-cutting-detector.d.ts +66 -0
- package/dist/src/utils/cross-cutting-detector.d.ts.map +1 -0
- package/dist/src/utils/cross-cutting-detector.js +179 -0
- package/dist/src/utils/cross-cutting-detector.js.map +1 -0
- 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/dist/src/utils/project-detection.d.ts +12 -8
- package/dist/src/utils/project-detection.d.ts.map +1 -1
- package/dist/src/utils/project-detection.js +13 -19
- package/dist/src/utils/project-detection.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-status.md +64 -0
- package/plugins/specweave/commands/specweave-validate.md +27 -1
- package/plugins/specweave/hooks/hooks.json +11 -1
- package/plugins/specweave/hooks/spec-project-validator.sh +81 -25
- package/plugins/specweave/hooks/v2/guards/increment-duplicate-guard.sh +135 -0
- package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +34 -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 +135 -29
- 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/skills/spec-generator/SKILL.md +78 -7
- 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/plugins/specweave/commands/specweave-switch-project.md +0 -168
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-Project Sync Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Handles syncing increments that span multiple projects.
|
|
5
|
+
* Groups user stories by their target project and syncs each group
|
|
6
|
+
* to the appropriate project folder in living docs.
|
|
7
|
+
*
|
|
8
|
+
* @module core/living-docs/cross-project-sync
|
|
9
|
+
* @since v0.33.0
|
|
10
|
+
*/
|
|
11
|
+
import * as path from 'node:path';
|
|
12
|
+
import { consoleLogger } from '../../utils/logger.js';
|
|
13
|
+
import { ensureDir } from '../../utils/fs-native.js';
|
|
14
|
+
/**
|
|
15
|
+
* Cross-Project Sync Orchestrator
|
|
16
|
+
*/
|
|
17
|
+
export class CrossProjectSync {
|
|
18
|
+
constructor(projectRoot, options = {}) {
|
|
19
|
+
this.projectRoot = projectRoot;
|
|
20
|
+
this.logger = options.logger ?? consoleLogger;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Group user stories by their target project
|
|
24
|
+
*
|
|
25
|
+
* @param userStories - User stories to group
|
|
26
|
+
* @param defaultProject - Default project for USs without explicit project
|
|
27
|
+
* @returns Map of project ID to user stories
|
|
28
|
+
*/
|
|
29
|
+
groupByProject(userStories, defaultProject) {
|
|
30
|
+
const groups = new Map();
|
|
31
|
+
for (const us of userStories) {
|
|
32
|
+
const project = us.project || defaultProject;
|
|
33
|
+
if (!groups.has(project)) {
|
|
34
|
+
groups.set(project, []);
|
|
35
|
+
}
|
|
36
|
+
groups.get(project).push(us);
|
|
37
|
+
}
|
|
38
|
+
return groups;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Check if an increment is cross-project (spans multiple projects)
|
|
42
|
+
*/
|
|
43
|
+
isCrossProject(userStories, defaultProject) {
|
|
44
|
+
const groups = this.groupByProject(userStories, defaultProject);
|
|
45
|
+
return groups.size > 1;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get the specs folder path for a project
|
|
49
|
+
*/
|
|
50
|
+
getProjectSpecsPath(projectId) {
|
|
51
|
+
return path.join(this.projectRoot, '.specweave/docs/internal/specs', projectId);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get the feature folder path for a project and feature
|
|
55
|
+
*/
|
|
56
|
+
getFeaturePath(projectId, featureId) {
|
|
57
|
+
return path.join(this.getProjectSpecsPath(projectId), featureId);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Ensure project specs folder exists
|
|
61
|
+
*/
|
|
62
|
+
async ensureProjectFolder(projectId) {
|
|
63
|
+
const folderPath = this.getProjectSpecsPath(projectId);
|
|
64
|
+
await ensureDir(folderPath);
|
|
65
|
+
return folderPath;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Ensure feature folder exists within a project
|
|
69
|
+
*/
|
|
70
|
+
async ensureFeatureFolder(projectId, featureId) {
|
|
71
|
+
const folderPath = this.getFeaturePath(projectId, featureId);
|
|
72
|
+
await ensureDir(folderPath);
|
|
73
|
+
return folderPath;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Generate cross-references section for FEATURE.md
|
|
77
|
+
*
|
|
78
|
+
* @param featureId - The feature ID (e.g., "FS-125")
|
|
79
|
+
* @param allProjects - All projects this feature spans
|
|
80
|
+
* @param currentProject - The current project being generated
|
|
81
|
+
* @returns Markdown content for "Related Projects" section
|
|
82
|
+
*/
|
|
83
|
+
generateCrossReferences(featureId, allProjects, currentProject) {
|
|
84
|
+
const otherProjects = allProjects.filter(p => p !== currentProject);
|
|
85
|
+
if (otherProjects.length === 0) {
|
|
86
|
+
return '';
|
|
87
|
+
}
|
|
88
|
+
const lines = [
|
|
89
|
+
'',
|
|
90
|
+
'## Related Projects',
|
|
91
|
+
'',
|
|
92
|
+
'This feature spans multiple projects:',
|
|
93
|
+
''
|
|
94
|
+
];
|
|
95
|
+
for (const projectId of otherProjects) {
|
|
96
|
+
// Relative path from current project to other project
|
|
97
|
+
const relativePath = `../../${projectId}/${featureId}/`;
|
|
98
|
+
lines.push(`- [${projectId}](${relativePath})`);
|
|
99
|
+
}
|
|
100
|
+
lines.push('');
|
|
101
|
+
return lines.join('\n');
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Generate cross-reference frontmatter for US files
|
|
105
|
+
*/
|
|
106
|
+
generateRelatedProjectsFrontmatter(allProjects, currentProject) {
|
|
107
|
+
const otherProjects = allProjects.filter(p => p !== currentProject);
|
|
108
|
+
if (otherProjects.length === 0) {
|
|
109
|
+
return '';
|
|
110
|
+
}
|
|
111
|
+
return `related_projects: [${otherProjects.join(', ')}]`;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Log cross-project sync summary
|
|
115
|
+
*/
|
|
116
|
+
logSyncSummary(result) {
|
|
117
|
+
this.logger.log('');
|
|
118
|
+
this.logger.log('📦 Cross-Project Sync Summary:');
|
|
119
|
+
this.logger.log(` Projects: ${result.projectCount}`);
|
|
120
|
+
this.logger.log(` User Stories: ${result.totalUserStories}`);
|
|
121
|
+
for (const project of result.projects) {
|
|
122
|
+
const status = project.success ? '✅' : '❌';
|
|
123
|
+
this.logger.log(` ${status} ${project.projectId}: ${project.userStories.length} USs`);
|
|
124
|
+
if (project.errors.length > 0) {
|
|
125
|
+
for (const error of project.errors) {
|
|
126
|
+
this.logger.warn(` ⚠️ ${error}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (result.crossReferences.length > 0) {
|
|
131
|
+
this.logger.log(` 🔗 Cross-references: ${result.crossReferences.length} links created`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=cross-project-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-project-sync.js","sourceRoot":"","sources":["../../../../src/core/living-docs/cross-project-sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External Sync Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Coordinates syncing user stories to multiple external tools based on
|
|
5
|
+
* per-US project targeting. Groups USs by their target project and
|
|
6
|
+
* syncs each group to the appropriate external system.
|
|
7
|
+
*
|
|
8
|
+
* @module core/living-docs/external-sync-orchestrator
|
|
9
|
+
* @since v0.33.0
|
|
10
|
+
*/
|
|
11
|
+
import type { UserStoryData } from './types.js';
|
|
12
|
+
import type { USExternalRefsMap } from '../types/increment-metadata.js';
|
|
13
|
+
import { Logger } from '../../utils/logger.js';
|
|
14
|
+
/**
|
|
15
|
+
* Project mapping configuration
|
|
16
|
+
*/
|
|
17
|
+
export interface ProjectMapping {
|
|
18
|
+
github?: {
|
|
19
|
+
owner: string;
|
|
20
|
+
repo: string;
|
|
21
|
+
};
|
|
22
|
+
jira?: {
|
|
23
|
+
project: string;
|
|
24
|
+
board?: string;
|
|
25
|
+
};
|
|
26
|
+
ado?: {
|
|
27
|
+
project: string;
|
|
28
|
+
areaPath?: string;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Result of syncing a single US to an external tool
|
|
33
|
+
*/
|
|
34
|
+
export interface USSyncResult {
|
|
35
|
+
usId: string;
|
|
36
|
+
provider: 'github' | 'jira' | 'ado';
|
|
37
|
+
success: boolean;
|
|
38
|
+
issueNumber?: number | string;
|
|
39
|
+
url?: string;
|
|
40
|
+
error?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Result of syncing all USs in a project group
|
|
44
|
+
*/
|
|
45
|
+
export interface ProjectSyncResult {
|
|
46
|
+
projectId: string;
|
|
47
|
+
provider: 'github' | 'jira' | 'ado';
|
|
48
|
+
success: boolean;
|
|
49
|
+
synced: USSyncResult[];
|
|
50
|
+
errors: string[];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Result of complete cross-project sync
|
|
54
|
+
*/
|
|
55
|
+
export interface CrossProjectExternalSyncResult {
|
|
56
|
+
success: boolean;
|
|
57
|
+
projects: ProjectSyncResult[];
|
|
58
|
+
externalRefs: USExternalRefsMap;
|
|
59
|
+
summary: {
|
|
60
|
+
totalUSs: number;
|
|
61
|
+
syncedUSs: number;
|
|
62
|
+
failedUSs: number;
|
|
63
|
+
projectCount: number;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* External Sync Orchestrator
|
|
68
|
+
*/
|
|
69
|
+
export declare class ExternalSyncOrchestrator {
|
|
70
|
+
private projectRoot;
|
|
71
|
+
private logger;
|
|
72
|
+
private projectMappings;
|
|
73
|
+
constructor(projectRoot: string, options?: {
|
|
74
|
+
logger?: Logger;
|
|
75
|
+
});
|
|
76
|
+
/**
|
|
77
|
+
* Load project mappings from config
|
|
78
|
+
*/
|
|
79
|
+
loadProjectMappings(): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Get project mapping for a project ID
|
|
82
|
+
*/
|
|
83
|
+
getProjectMapping(projectId: string): ProjectMapping | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* Group user stories by their target project
|
|
86
|
+
*/
|
|
87
|
+
groupUSsByProject(userStories: UserStoryData[], defaultProject: string): Map<string, UserStoryData[]>;
|
|
88
|
+
/**
|
|
89
|
+
* Get preferred provider for a US
|
|
90
|
+
* Priority: US.externalProvider > first available in mapping
|
|
91
|
+
*/
|
|
92
|
+
getPreferredProvider(us: UserStoryData, mapping: ProjectMapping): 'github' | 'jira' | 'ado' | null;
|
|
93
|
+
/**
|
|
94
|
+
* Build external refs map from sync results
|
|
95
|
+
*/
|
|
96
|
+
buildExternalRefsMap(results: ProjectSyncResult[]): USExternalRefsMap;
|
|
97
|
+
/**
|
|
98
|
+
* Log sync summary
|
|
99
|
+
*/
|
|
100
|
+
logSyncSummary(result: CrossProjectExternalSyncResult): void;
|
|
101
|
+
/**
|
|
102
|
+
* Create placeholder sync result for dry-run or when sync is skipped
|
|
103
|
+
*/
|
|
104
|
+
createPlaceholderResult(userStories: UserStoryData[], defaultProject: string): CrossProjectExternalSyncResult;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=external-sync-orchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-sync-orchestrator.d.ts","sourceRoot":"","sources":["../../../../src/core/living-docs/external-sync-orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAiB,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACvF,OAAO,EAAE,MAAM,EAAiB,MAAM,uBAAuB,CAAC;AAG9D;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,IAAI,CAAC,EAAE;QACL,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,GAAG,CAAC,EAAE;QACJ,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,YAAY,EAAE,iBAAiB,CAAC;IAChC,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;GAEG;AACH,qBAAa,wBAAwB;IACnC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,eAAe,CAAsC;gBAEjD,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO;IAKlE;;OAEG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAa1C;;OAEG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAIhE;;OAEG;IACH,iBAAiB,CACf,WAAW,EAAE,aAAa,EAAE,EAC5B,cAAc,EAAE,MAAM,GACrB,GAAG,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC;IAe/B;;;OAGG;IACH,oBAAoB,CAClB,EAAE,EAAE,aAAa,EACjB,OAAO,EAAE,cAAc,GACtB,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,IAAI;IAcnC;;OAEG;IACH,oBAAoB,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,iBAAiB;IAwBrE;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,8BAA8B,GAAG,IAAI;IAkB5D;;OAEG;IACH,uBAAuB,CACrB,WAAW,EAAE,aAAa,EAAE,EAC5B,cAAc,EAAE,MAAM,GACrB,8BAA8B;CA0BlC"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External Sync Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Coordinates syncing user stories to multiple external tools based on
|
|
5
|
+
* per-US project targeting. Groups USs by their target project and
|
|
6
|
+
* syncs each group to the appropriate external system.
|
|
7
|
+
*
|
|
8
|
+
* @module core/living-docs/external-sync-orchestrator
|
|
9
|
+
* @since v0.33.0
|
|
10
|
+
*/
|
|
11
|
+
import * as path from 'node:path';
|
|
12
|
+
import { consoleLogger } from '../../utils/logger.js';
|
|
13
|
+
import { pathExists, readJson } from './sync-helpers/file-utils.js';
|
|
14
|
+
/**
|
|
15
|
+
* External Sync Orchestrator
|
|
16
|
+
*/
|
|
17
|
+
export class ExternalSyncOrchestrator {
|
|
18
|
+
constructor(projectRoot, options = {}) {
|
|
19
|
+
this.projectMappings = {};
|
|
20
|
+
this.projectRoot = projectRoot;
|
|
21
|
+
this.logger = options.logger ?? consoleLogger;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Load project mappings from config
|
|
25
|
+
*/
|
|
26
|
+
async loadProjectMappings() {
|
|
27
|
+
const configPath = path.join(this.projectRoot, '.specweave/config.json');
|
|
28
|
+
if (await pathExists(configPath)) {
|
|
29
|
+
try {
|
|
30
|
+
const config = await readJson(configPath);
|
|
31
|
+
this.projectMappings = config.projectMappings || {};
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
this.logger.warn('Failed to load projectMappings from config');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get project mapping for a project ID
|
|
40
|
+
*/
|
|
41
|
+
getProjectMapping(projectId) {
|
|
42
|
+
return this.projectMappings[projectId];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Group user stories by their target project
|
|
46
|
+
*/
|
|
47
|
+
groupUSsByProject(userStories, defaultProject) {
|
|
48
|
+
const groups = new Map();
|
|
49
|
+
for (const us of userStories) {
|
|
50
|
+
const project = us.project || defaultProject;
|
|
51
|
+
if (!groups.has(project)) {
|
|
52
|
+
groups.set(project, []);
|
|
53
|
+
}
|
|
54
|
+
groups.get(project).push(us);
|
|
55
|
+
}
|
|
56
|
+
return groups;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get preferred provider for a US
|
|
60
|
+
* Priority: US.externalProvider > first available in mapping
|
|
61
|
+
*/
|
|
62
|
+
getPreferredProvider(us, mapping) {
|
|
63
|
+
// Explicit preference in US
|
|
64
|
+
if (us.externalProvider && mapping[us.externalProvider]) {
|
|
65
|
+
return us.externalProvider;
|
|
66
|
+
}
|
|
67
|
+
// Fall back to first available in mapping
|
|
68
|
+
if (mapping.github)
|
|
69
|
+
return 'github';
|
|
70
|
+
if (mapping.jira)
|
|
71
|
+
return 'jira';
|
|
72
|
+
if (mapping.ado)
|
|
73
|
+
return 'ado';
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Build external refs map from sync results
|
|
78
|
+
*/
|
|
79
|
+
buildExternalRefsMap(results) {
|
|
80
|
+
const refsMap = {};
|
|
81
|
+
for (const projectResult of results) {
|
|
82
|
+
for (const usResult of projectResult.synced) {
|
|
83
|
+
if (!usResult.success)
|
|
84
|
+
continue;
|
|
85
|
+
if (!refsMap[usResult.usId]) {
|
|
86
|
+
refsMap[usResult.usId] = {};
|
|
87
|
+
}
|
|
88
|
+
refsMap[usResult.usId][usResult.provider] = {
|
|
89
|
+
provider: usResult.provider,
|
|
90
|
+
issueNumber: usResult.issueNumber,
|
|
91
|
+
url: usResult.url,
|
|
92
|
+
targetProject: projectResult.projectId,
|
|
93
|
+
lastSynced: new Date().toISOString()
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return refsMap;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Log sync summary
|
|
101
|
+
*/
|
|
102
|
+
logSyncSummary(result) {
|
|
103
|
+
this.logger.log('');
|
|
104
|
+
this.logger.log('🔗 Cross-Project External Sync Summary:');
|
|
105
|
+
this.logger.log(` Projects: ${result.summary.projectCount}`);
|
|
106
|
+
this.logger.log(` User Stories: ${result.summary.syncedUSs}/${result.summary.totalUSs} synced`);
|
|
107
|
+
for (const project of result.projects) {
|
|
108
|
+
const status = project.success ? '✅' : '❌';
|
|
109
|
+
this.logger.log(` ${status} ${project.projectId} (${project.provider}): ${project.synced.length} USs`);
|
|
110
|
+
if (project.errors.length > 0) {
|
|
111
|
+
for (const error of project.errors) {
|
|
112
|
+
this.logger.warn(` ⚠️ ${error}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Create placeholder sync result for dry-run or when sync is skipped
|
|
119
|
+
*/
|
|
120
|
+
createPlaceholderResult(userStories, defaultProject) {
|
|
121
|
+
const groups = this.groupUSsByProject(userStories, defaultProject);
|
|
122
|
+
return {
|
|
123
|
+
success: true,
|
|
124
|
+
projects: [...groups.entries()].map(([projectId, stories]) => ({
|
|
125
|
+
projectId,
|
|
126
|
+
provider: 'github',
|
|
127
|
+
success: true,
|
|
128
|
+
synced: stories.map(us => ({
|
|
129
|
+
usId: us.id,
|
|
130
|
+
provider: 'github',
|
|
131
|
+
success: false,
|
|
132
|
+
error: 'Sync skipped (dry-run or SKIP_EXTERNAL_SYNC)'
|
|
133
|
+
})),
|
|
134
|
+
errors: []
|
|
135
|
+
})),
|
|
136
|
+
externalRefs: {},
|
|
137
|
+
summary: {
|
|
138
|
+
totalUSs: userStories.length,
|
|
139
|
+
syncedUSs: 0,
|
|
140
|
+
failedUSs: 0,
|
|
141
|
+
projectCount: groups.size
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=external-sync-orchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-sync-orchestrator.js","sourceRoot":"","sources":["../../../../src/core/living-docs/external-sync-orchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -106,7 +106,11 @@ export declare class FeatureArchiver {
|
|
|
106
106
|
* Examples:
|
|
107
107
|
* - "0041-living-docs-test-fixes" → "FS-041"
|
|
108
108
|
* - "0123-my-feature" → "FS-123"
|
|
109
|
+
* - "0111E-external-issue" → "FS-111E" (external increment → external feature)
|
|
109
110
|
* - "temp-experiment" → null (no number)
|
|
111
|
+
*
|
|
112
|
+
* CRITICAL (v0.33.0): External increments (with E suffix like 0111E-...)
|
|
113
|
+
* MUST map to external features (FS-111E), not internal ones (FS-111).
|
|
110
114
|
*/
|
|
111
115
|
private inferFeatureIdFromIncrement;
|
|
112
116
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-archiver.d.ts","sourceRoot":"","sources":["../../../../src/core/living-docs/feature-archiver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAkBH,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,qCAAqC,CAAC,EAAE,OAAO,CAAC;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,gBAAgB,CAAmB;gBAE/B,OAAO,EAAE,MAAM;IAU3B;;OAEG;IACG,eAAe,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,oBAAoB,CAAC;IA0DzF;;;OAGG;YACW,yBAAyB;
|
|
1
|
+
{"version":3,"file":"feature-archiver.d.ts","sourceRoot":"","sources":["../../../../src/core/living-docs/feature-archiver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAkBH,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,qCAAqC,CAAC,EAAE,OAAO,CAAC;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,gBAAgB,CAAmB;gBAE/B,OAAO,EAAE,MAAM;IAU3B;;OAEG;IACG,eAAe,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,oBAAoB,CAAC;IA0DzF;;;OAGG;YACW,yBAAyB;IA0FvC;;OAEG;YACW,sBAAsB;IA+CpC;;OAEG;YACW,uBAAuB;IA2ErC;;;OAGG;YACW,6BAA6B;IA0B3C;;OAEG;YACW,cAAc;IAwB5B;;;OAGG;YACW,iBAAiB;IA2E/B;;;OAGG;YACW,cAAc;IAM5B;;;;OAIG;YACW,sBAAsB;IA4CpC;;OAEG;YACW,WAAW;IAmBzB;;;;;;;;;;;;;OAaG;YACW,mBAAmB;IAgIjC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,2BAA2B;IAgBnC;;;OAGG;YACW,iBAAiB;IAiC/B;;;OAGG;YACW,sBAAsB;IAqBpC;;;OAGG;YACW,iBAAiB;IAwB/B;;;OAGG;YACW,wBAAwB;IAiBtC;;;;;;;;;;OAUG;YACW,2BAA2B;IAgFzC;;OAEG;YACW,oBAAoB;IAgBlC;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBhD;;OAEG;YACW,0BAA0B;IAyBxC;;;OAGG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BtD;;;OAGG;YACW,6BAA6B;IAyB3C;;;OAGG;IACG,eAAe,IAAI,OAAO,CAAC;QAC/B,QAAQ,EAAE;YACR,MAAM,EAAE,MAAM,CAAC;YACf,QAAQ,EAAE,MAAM,CAAC;SAClB,CAAC;QACF,KAAK,EAAE;YACL,MAAM,EAAE,MAAM,CAAC;YACf,QAAQ,EAAE,MAAM,CAAC;SAClB,CAAC;QACF,QAAQ,EAAE;YACR,CAAC,GAAG,EAAE,MAAM,GAAG;gBACb,MAAM,EAAE,MAAM,CAAC;gBACf,QAAQ,EAAE,MAAM,CAAC;aAClB,CAAC;SACH,CAAC;KACH,CAAC;IA6CF;;;;;;;;;;;;;;OAcG;IACG,uBAAuB,CAAC,OAAO,GAAE;QACrC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,WAAW,CAAC,EAAE,OAAO,CAAC;KAClB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAsGtC;;;;;OAKG;YACW,sBAAsB;IAgDpC;;OAEG;YACW,uBAAuB;IAuBrC;;;;OAIG;YACW,qBAAqB;IA2BnC;;;;;;;;OAQG;IACG,iBAAiB,IAAI,OAAO,CAAC;QACjC,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,MAAM,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;IA2DF;;;;;;;;;;OAUG;YACW,gCAAgC;CA6C/C"}
|
|
@@ -100,6 +100,14 @@ export class FeatureArchiver {
|
|
|
100
100
|
// Empty array .every() returns true, so we must explicitly check length
|
|
101
101
|
// Archive orphaned features ONLY if option is set
|
|
102
102
|
const isOrphaned = linkedIncrements.length === 0 && options.archiveOrphanedFeatures;
|
|
103
|
+
// v0.33.0: External features (FS-XXXE) are NOT orphaned - they're imported from
|
|
104
|
+
// external tools and should be handled by archiveExternalFeatures() separately!
|
|
105
|
+
// Skip external features in the normal archiving flow.
|
|
106
|
+
const isExternalFeature = featureId.endsWith('E');
|
|
107
|
+
if (isExternalFeature && linkedIncrements.length === 0) {
|
|
108
|
+
console.log(`⏭️ Skipping ${featureId}: external feature (use --external flag to archive)`);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
103
111
|
// Check if all linked increments are archived (EXACT MATCH, not partial)
|
|
104
112
|
// CRITICAL: Use exact match (===) not .includes() to avoid false positives
|
|
105
113
|
// SAFETY: If no linked increments and archiveOrphanedFeatures is false, skip
|
|
@@ -557,17 +565,24 @@ export class FeatureArchiver {
|
|
|
557
565
|
* Examples:
|
|
558
566
|
* - "0041-living-docs-test-fixes" → "FS-041"
|
|
559
567
|
* - "0123-my-feature" → "FS-123"
|
|
568
|
+
* - "0111E-external-issue" → "FS-111E" (external increment → external feature)
|
|
560
569
|
* - "temp-experiment" → null (no number)
|
|
570
|
+
*
|
|
571
|
+
* CRITICAL (v0.33.0): External increments (with E suffix like 0111E-...)
|
|
572
|
+
* MUST map to external features (FS-111E), not internal ones (FS-111).
|
|
561
573
|
*/
|
|
562
574
|
inferFeatureIdFromIncrement(increment) {
|
|
563
|
-
// Extract 4-digit number prefix
|
|
564
|
-
|
|
575
|
+
// Extract 4-digit number prefix and optional E suffix
|
|
576
|
+
// Pattern: NNNN or NNNNE (e.g., 0041, 0111E)
|
|
577
|
+
const match = increment.match(/^(\d{4})(E)?/);
|
|
565
578
|
if (!match) {
|
|
566
579
|
return null; // Can't infer (no number prefix)
|
|
567
580
|
}
|
|
568
581
|
const number = parseInt(match[1], 10);
|
|
569
|
-
|
|
570
|
-
|
|
582
|
+
const isExternal = match[2] === 'E';
|
|
583
|
+
// Convert to feature ID format: 41 → "FS-041", 111E → "FS-111E"
|
|
584
|
+
const featureId = `FS-${number.toString().padStart(3, '0')}`;
|
|
585
|
+
return isExternal ? `${featureId}E` : featureId;
|
|
571
586
|
}
|
|
572
587
|
/**
|
|
573
588
|
* Get features linked to an epic
|
|
@@ -687,16 +702,23 @@ export class FeatureArchiver {
|
|
|
687
702
|
}
|
|
688
703
|
try {
|
|
689
704
|
const content = await fs.readFile(featureMdPath, 'utf-8');
|
|
690
|
-
// Extract increment number from Implementation History
|
|
691
|
-
|
|
705
|
+
// Extract increment number and optional E suffix from Implementation History
|
|
706
|
+
// CRITICAL (v0.33.0): Handle external increments like [0111E-name](...)
|
|
707
|
+
const match = content.match(/\[(\d{4})(E)?-[^\]]+\]\([^)]*increments/);
|
|
692
708
|
if (!match) {
|
|
693
709
|
continue; // No increment reference found
|
|
694
710
|
}
|
|
695
711
|
const referencedIncrementNum = parseInt(match[1]);
|
|
696
|
-
const
|
|
697
|
-
//
|
|
698
|
-
|
|
699
|
-
|
|
712
|
+
const isExternalIncrement = match[2] === 'E';
|
|
713
|
+
// Parse current feature ID - handle both FS-111 and FS-111E
|
|
714
|
+
const currentFeatureNum = parseInt(featureId.replace('FS-', '').replace('E', ''));
|
|
715
|
+
const isCurrentExternal = featureId.endsWith('E');
|
|
716
|
+
// Check if feature ID matches referenced increment (both number AND external status)
|
|
717
|
+
const numberMismatch = referencedIncrementNum !== currentFeatureNum;
|
|
718
|
+
const externalMismatch = isExternalIncrement !== isCurrentExternal;
|
|
719
|
+
if (numberMismatch || externalMismatch) {
|
|
720
|
+
const baseId = `FS-${referencedIncrementNum.toString().padStart(3, '0')}`;
|
|
721
|
+
const expectedFeatureId = isExternalIncrement ? `${baseId}E` : baseId;
|
|
700
722
|
console.log(` ⚠️ Mismatch: ${featureId} references increment ${match[1]}`);
|
|
701
723
|
console.log(` Renaming ${featureId} → ${expectedFeatureId}`);
|
|
702
724
|
// Rename the feature folder
|