specweave 0.29.2 → 0.30.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/README.md +65 -16
- package/bin/specweave.js +16 -0
- package/dist/plugins/specweave-ado/lib/ado-spec-commit-sync.d.ts +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-commit-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-commit-sync.js +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-commit-sync.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.d.ts +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/conflict-resolver.d.ts +134 -0
- package/dist/plugins/specweave-ado/lib/conflict-resolver.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/conflict-resolver.js +423 -0
- package/dist/plugins/specweave-ado/lib/conflict-resolver.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +23 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js +95 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-commit-sync.d.ts +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-commit-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-commit-sync.js +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-commit-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.d.ts +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.js +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/types.d.ts +20 -0
- package/dist/plugins/specweave-github/lib/types.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-commit-sync.d.ts +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-commit-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-commit-sync.js +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-commit-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-content-sync.d.ts +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-content-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-content-sync.js +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-content-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/setup-wizard.d.ts +2 -2
- package/dist/plugins/specweave-jira/lib/setup-wizard.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/setup-wizard.js +1 -1
- package/dist/plugins/specweave-jira/lib/setup-wizard.js.map +1 -1
- package/dist/src/adapters/claude/adapter.js +1 -1
- package/dist/src/adapters/claude/adapter.js.map +1 -1
- package/dist/src/cli/commands/detect-project.js +1 -1
- package/dist/src/cli/commands/detect-project.js.map +1 -1
- package/dist/src/cli/commands/detect-specs.js +1 -1
- package/dist/src/cli/commands/detect-specs.js.map +1 -1
- package/dist/src/cli/commands/import-docs.js +1 -1
- package/dist/src/cli/commands/import-docs.js.map +1 -1
- 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 +31 -1
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/jobs.js +15 -1
- package/dist/src/cli/commands/jobs.js.map +1 -1
- package/dist/src/cli/commands/migrate-to-multiproject.js +1 -1
- package/dist/src/cli/commands/migrate-to-multiproject.js.map +1 -1
- package/dist/src/cli/commands/switch-project.js +1 -1
- package/dist/src/cli/commands/switch-project.js.map +1 -1
- package/dist/src/cli/commands/sync-scheduled.d.ts +38 -0
- package/dist/src/cli/commands/sync-scheduled.d.ts.map +1 -0
- package/dist/src/cli/commands/sync-scheduled.js +170 -0
- package/dist/src/cli/commands/sync-scheduled.js.map +1 -0
- package/dist/src/cli/helpers/init/external-import-grouping.d.ts +53 -0
- package/dist/src/cli/helpers/init/external-import-grouping.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/external-import-grouping.js +287 -0
- package/dist/src/cli/helpers/init/external-import-grouping.js.map +1 -0
- package/dist/src/cli/helpers/init/external-import.d.ts +1 -34
- package/dist/src/cli/helpers/init/external-import.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/external-import.js +15 -486
- package/dist/src/cli/helpers/init/external-import.js.map +1 -1
- package/dist/src/cli/helpers/init/living-docs-preflight.d.ts +58 -0
- package/dist/src/cli/helpers/init/living-docs-preflight.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/living-docs-preflight.js +290 -0
- package/dist/src/cli/helpers/init/living-docs-preflight.js.map +1 -0
- package/dist/src/cli/helpers/init/sync-profile-helpers.d.ts +48 -0
- package/dist/src/cli/helpers/init/sync-profile-helpers.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/sync-profile-helpers.js +238 -0
- package/dist/src/cli/helpers/init/sync-profile-helpers.js.map +1 -0
- package/dist/src/cli/helpers/init/types.d.ts +1 -0
- package/dist/src/cli/helpers/init/types.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado.js +113 -3
- package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.js +2 -287
- package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/sync-config-writer.d.ts +25 -0
- package/dist/src/cli/helpers/issue-tracker/sync-config-writer.d.ts.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/sync-config-writer.js +306 -0
- package/dist/src/cli/helpers/issue-tracker/sync-config-writer.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/types.d.ts +27 -0
- package/dist/src/cli/helpers/issue-tracker/types.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/types.js.map +1 -1
- package/dist/src/cli/workers/living-docs-worker.d.ts +16 -0
- package/dist/src/cli/workers/living-docs-worker.d.ts.map +1 -0
- package/dist/src/cli/workers/living-docs-worker.js +395 -0
- package/dist/src/cli/workers/living-docs-worker.js.map +1 -0
- package/dist/src/core/background/job-dependency.d.ts +45 -0
- package/dist/src/core/background/job-dependency.d.ts.map +1 -0
- package/dist/src/core/background/job-dependency.js +144 -0
- package/dist/src/core/background/job-dependency.js.map +1 -0
- package/dist/src/core/background/job-launcher.d.ts +21 -1
- package/dist/src/core/background/job-launcher.d.ts.map +1 -1
- package/dist/src/core/background/job-launcher.js +93 -0
- package/dist/src/core/background/job-launcher.js.map +1 -1
- package/dist/src/core/background/types.d.ts +73 -2
- package/dist/src/core/background/types.d.ts.map +1 -1
- package/dist/src/core/brownfield/importer.js +1 -1
- package/dist/src/core/brownfield/importer.js.map +1 -1
- package/dist/src/core/comment-builder.d.ts +1 -1
- package/dist/src/core/comment-builder.d.ts.map +1 -1
- package/dist/src/core/{cost-tracker.d.ts → cost/cost-tracker.d.ts} +2 -2
- package/dist/src/core/cost/cost-tracker.d.ts.map +1 -0
- package/dist/src/core/{cost-tracker.js → cost/cost-tracker.js} +2 -2
- package/dist/src/core/cost/cost-tracker.js.map +1 -0
- package/dist/src/core/credentials/credentials-manager.d.ts.map +1 -0
- package/dist/src/core/credentials/credentials-manager.js.map +1 -0
- package/dist/src/core/increment/discipline-checker.js +1 -1
- package/dist/src/core/increment/discipline-checker.js.map +1 -1
- package/dist/src/core/increment/increment-archiver.d.ts +9 -8
- package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
- package/dist/src/core/increment/increment-archiver.js +40 -42
- package/dist/src/core/increment/increment-archiver.js.map +1 -1
- package/dist/src/core/increment/increment-status.d.ts.map +1 -0
- package/dist/src/core/{increment-status.js → increment/increment-status.js} +1 -1
- package/dist/src/core/increment/increment-status.js.map +1 -0
- package/dist/src/core/increment/increment-utils.d.ts.map +1 -0
- package/dist/src/core/increment/increment-utils.js.map +1 -0
- package/dist/src/core/increment/metadata-validator.js +1 -1
- package/dist/src/core/increment/metadata-validator.js.map +1 -1
- package/dist/src/core/living-docs/checkpoint-manager.d.ts +120 -0
- package/dist/src/core/living-docs/checkpoint-manager.d.ts.map +1 -0
- package/dist/src/core/living-docs/checkpoint-manager.js +231 -0
- package/dist/src/core/living-docs/checkpoint-manager.js.map +1 -0
- package/dist/src/core/living-docs/discovery.d.ts +91 -0
- package/dist/src/core/living-docs/discovery.d.ts.map +1 -0
- package/dist/src/core/living-docs/discovery.js +656 -0
- package/dist/src/core/living-docs/discovery.js.map +1 -0
- package/dist/src/core/living-docs/feature-archiver.d.ts +23 -4
- package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -1
- package/dist/src/core/living-docs/feature-archiver.js +133 -30
- package/dist/src/core/living-docs/feature-archiver.js.map +1 -1
- package/dist/src/core/living-docs/foundation-builder.d.ts +37 -0
- package/dist/src/core/living-docs/foundation-builder.d.ts.map +1 -0
- package/dist/src/core/living-docs/foundation-builder.js +357 -0
- package/dist/src/core/living-docs/foundation-builder.js.map +1 -0
- package/dist/src/core/living-docs/living-docs-sync.d.ts +58 -0
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +204 -7
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
- package/dist/src/core/living-docs/module-analyzer.d.ts +97 -0
- package/dist/src/core/living-docs/module-analyzer.d.ts.map +1 -0
- package/dist/src/core/living-docs/module-analyzer.js +427 -0
- package/dist/src/core/living-docs/module-analyzer.js.map +1 -0
- package/dist/src/core/living-docs/suggestions-generator.d.ts +112 -0
- package/dist/src/core/living-docs/suggestions-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/suggestions-generator.js +362 -0
- package/dist/src/core/living-docs/suggestions-generator.js.map +1 -0
- package/dist/src/core/living-docs/workitem-matcher.d.ts +116 -0
- package/dist/src/core/living-docs/workitem-matcher.d.ts.map +1 -0
- package/dist/src/core/living-docs/workitem-matcher.js +356 -0
- package/dist/src/core/living-docs/workitem-matcher.js.map +1 -0
- package/dist/src/core/{plugin-loader.d.ts → plugins/plugin-loader.d.ts} +1 -1
- package/dist/src/core/plugins/plugin-loader.d.ts.map +1 -0
- package/dist/src/core/{plugin-loader.js → plugins/plugin-loader.js} +2 -2
- package/dist/src/core/plugins/plugin-loader.js.map +1 -0
- package/dist/src/core/{project-manager.d.ts → project/project-manager.d.ts} +1 -1
- package/dist/src/core/project/project-manager.d.ts.map +1 -0
- package/dist/src/core/{project-manager.js → project/project-manager.js} +3 -3
- package/dist/src/core/project/project-manager.js.map +1 -0
- package/dist/src/core/project/project-structure-detector.d.ts.map +1 -0
- package/dist/src/core/project/project-structure-detector.js.map +1 -0
- package/dist/src/core/{rfc-generator-v2.d.ts → rfc/rfc-generator-v2.d.ts} +1 -1
- package/dist/src/core/rfc/rfc-generator-v2.d.ts.map +1 -0
- package/dist/src/core/{rfc-generator-v2.js → rfc/rfc-generator-v2.js} +1 -1
- package/dist/src/core/rfc/rfc-generator-v2.js.map +1 -0
- package/dist/src/core/scheduler/index.d.ts +1 -0
- package/dist/src/core/scheduler/index.d.ts.map +1 -1
- package/dist/src/core/scheduler/index.js +1 -0
- package/dist/src/core/scheduler/index.js.map +1 -1
- package/dist/src/core/scheduler/scheduled-job.d.ts +1 -1
- package/dist/src/core/scheduler/scheduled-job.d.ts.map +1 -1
- package/dist/src/core/scheduler/scheduled-job.js.map +1 -1
- package/dist/src/core/scheduler/session-sync-executor.d.ts +138 -0
- package/dist/src/core/scheduler/session-sync-executor.d.ts.map +1 -0
- package/dist/src/core/scheduler/session-sync-executor.js +333 -0
- package/dist/src/core/scheduler/session-sync-executor.js.map +1 -0
- package/dist/src/core/{spec-content-sync.d.ts → specs/spec-content-sync.d.ts} +2 -2
- package/dist/src/core/specs/spec-content-sync.d.ts.map +1 -0
- package/dist/src/core/{spec-content-sync.js → specs/spec-content-sync.js} +1 -1
- package/dist/src/core/specs/spec-content-sync.js.map +1 -0
- package/dist/src/core/{spec-detector.d.ts → specs/spec-detector.d.ts} +1 -1
- package/dist/src/core/specs/spec-detector.d.ts.map +1 -0
- package/dist/src/core/{spec-detector.js → specs/spec-detector.js} +1 -1
- package/dist/src/core/specs/spec-detector.js.map +1 -0
- package/dist/src/core/{spec-identifier-detector.d.ts → specs/spec-identifier-detector.d.ts} +2 -2
- package/dist/src/core/specs/spec-identifier-detector.d.ts.map +1 -0
- package/dist/src/core/{spec-identifier-detector.js → specs/spec-identifier-detector.js} +1 -1
- package/dist/src/core/specs/spec-identifier-detector.js.map +1 -0
- package/dist/src/core/specs/spec-metadata-manager.js +1 -1
- package/dist/src/core/specs/spec-metadata-manager.js.map +1 -1
- package/dist/src/core/specs/spec-task-mapper.d.ts.map +1 -0
- package/dist/src/core/specs/spec-task-mapper.js.map +1 -0
- package/dist/src/core/sync/sync-audit-logger.d.ts +62 -0
- package/dist/src/core/sync/sync-audit-logger.d.ts.map +1 -1
- package/dist/src/core/sync/sync-audit-logger.js +59 -0
- package/dist/src/core/sync/sync-audit-logger.js.map +1 -1
- package/dist/src/importers/item-converter.d.ts +51 -27
- package/dist/src/importers/item-converter.d.ts.map +1 -1
- package/dist/src/importers/item-converter.js +219 -179
- package/dist/src/importers/item-converter.js.map +1 -1
- package/dist/src/importers/markdown-generator.d.ts +75 -0
- package/dist/src/importers/markdown-generator.d.ts.map +1 -0
- package/dist/src/importers/markdown-generator.js +250 -0
- package/dist/src/importers/markdown-generator.js.map +1 -0
- package/dist/src/integrations/ado/ado-client.d.ts +93 -0
- package/dist/src/integrations/ado/ado-client.d.ts.map +1 -1
- package/dist/src/integrations/ado/ado-client.js +172 -1
- package/dist/src/integrations/ado/ado-client.js.map +1 -1
- package/dist/src/integrations/jira/jira-client.d.ts +44 -0
- package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-client.js +119 -1
- package/dist/src/integrations/jira/jira-client.js.map +1 -1
- package/dist/src/integrations/jira/jira-incremental-mapper.js +2 -2
- package/dist/src/integrations/jira/jira-incremental-mapper.js.map +1 -1
- package/dist/src/integrations/jira/jira-mapper.js +1 -1
- package/dist/src/integrations/jira/jira-mapper.js.map +1 -1
- package/dist/src/living-docs/epic-id-allocator.d.ts +177 -0
- package/dist/src/living-docs/epic-id-allocator.d.ts.map +1 -0
- package/dist/src/living-docs/epic-id-allocator.js +363 -0
- package/dist/src/living-docs/epic-id-allocator.js.map +1 -0
- package/dist/src/sync/external-change-puller.d.ts +94 -0
- package/dist/src/sync/external-change-puller.d.ts.map +1 -0
- package/dist/src/sync/external-change-puller.js +181 -0
- package/dist/src/sync/external-change-puller.js.map +1 -0
- package/dist/src/sync/index.d.ts +4 -0
- package/dist/src/sync/index.d.ts.map +1 -1
- package/dist/src/sync/index.js +2 -0
- package/dist/src/sync/index.js.map +1 -1
- package/dist/src/sync/living-docs-updater.d.ts +93 -0
- package/dist/src/sync/living-docs-updater.d.ts.map +1 -0
- package/dist/src/sync/living-docs-updater.js +218 -0
- package/dist/src/sync/living-docs-updater.js.map +1 -0
- package/dist/src/testing/test-generator.js +1 -1
- package/dist/src/testing/test-generator.js.map +1 -1
- package/dist/src/utils/cost-reporter.d.ts +1 -1
- package/dist/src/utils/cost-reporter.d.ts.map +1 -1
- package/dist/src/utils/model-selection.js +4 -4
- package/dist/src/utils/model-selection.js.map +1 -1
- package/dist/src/utils/pricing-constants.d.ts +2 -2
- package/dist/src/utils/pricing-constants.js +2 -2
- package/package.json +1 -1
- package/plugins/specweave/.claude-plugin/plugin.json +1 -28
- package/plugins/specweave/agents/code-standards-detective/AGENT.md +3 -3
- package/plugins/specweave/agents/infrastructure/AGENT.md +4 -0
- package/plugins/specweave/agents/performance/AGENT.md +2 -2
- package/plugins/specweave/agents/reflective-reviewer/AGENT.md +3 -3
- package/plugins/specweave/agents/translator/AGENT.md +2 -2
- package/plugins/specweave/commands/specweave-costs.md +2 -2
- package/plugins/specweave/commands/specweave-do.md +2 -2
- package/plugins/specweave/hooks/lib/scheduler-startup.sh +59 -8
- package/plugins/specweave/skills/code-reviewer/SKILL.md +1 -1
- package/plugins/specweave/skills/role-orchestrator/README.md +1 -1
- package/plugins/specweave-ado/agents/ado-manager/AGENT.md +8 -2
- package/plugins/specweave-ado/agents/ado-multi-project-mapper/AGENT.md +2 -2
- package/plugins/specweave-ado/agents/ado-sync-judge/AGENT.md +2 -2
- package/plugins/specweave-ado/lib/ado-spec-commit-sync.d.ts +1 -1
- package/plugins/specweave-ado/lib/ado-spec-commit-sync.js +1 -1
- package/plugins/specweave-ado/lib/ado-spec-commit-sync.ts +1 -1
- package/plugins/specweave-ado/lib/ado-spec-content-sync.js +1 -1
- package/plugins/specweave-ado/lib/ado-spec-content-sync.ts +1 -1
- package/plugins/specweave-ado/lib/conflict-resolver.js +87 -0
- package/plugins/specweave-ado/lib/conflict-resolver.ts +124 -0
- package/plugins/specweave-ado/skills/specweave-ado-mapper/SKILL.md +1 -1
- package/plugins/specweave-backend/agents/database-optimizer/AGENT.md +3 -3
- package/plugins/specweave-backend/skills/dotnet-backend/SKILL.md +1 -1
- package/plugins/specweave-backend/skills/nodejs-backend/SKILL.md +1 -1
- package/plugins/specweave-backend/skills/python-backend/SKILL.md +1 -1
- package/plugins/specweave-confluent/agents/confluent-architect/AGENT.md +6 -1
- package/plugins/specweave-diagrams/agents/diagrams-architect/AGENT.md +2 -2
- package/plugins/specweave-diagrams/skills/diagrams-architect/SKILL.md +1 -1
- package/plugins/specweave-frontend/agents/frontend-architect/AGENT.md +4 -0
- package/plugins/specweave-github/agents/github-manager/AGENT.md +1 -1
- package/plugins/specweave-github/agents/github-task-splitter/AGENT.md +11 -1
- package/plugins/specweave-github/agents/user-story-updater/AGENT.md +6 -1
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +90 -0
- package/plugins/specweave-github/lib/github-client-v2.js +97 -0
- package/plugins/specweave-github/lib/github-client-v2.ts +115 -1
- package/plugins/specweave-github/lib/github-spec-commit-sync.d.ts +1 -1
- package/plugins/specweave-github/lib/github-spec-commit-sync.js +1 -1
- package/plugins/specweave-github/lib/github-spec-commit-sync.ts +1 -1
- package/plugins/specweave-github/lib/github-spec-content-sync.js +1 -1
- package/plugins/specweave-github/lib/github-spec-content-sync.ts +1 -1
- package/plugins/specweave-github/lib/types.ts +21 -0
- package/plugins/specweave-infrastructure/agents/devops/AGENT.md +1 -1
- package/plugins/specweave-infrastructure/agents/network-engineer/AGENT.md +3 -3
- package/plugins/specweave-infrastructure/agents/observability-engineer/AGENT.md +2 -2
- package/plugins/specweave-infrastructure/agents/performance-engineer/AGENT.md +3 -3
- package/plugins/specweave-infrastructure/agents/sre/AGENT.md +2 -2
- package/plugins/specweave-jira/agents/jira-manager/AGENT.md +11 -1
- package/plugins/specweave-jira/commands/import-projects.js +1 -1
- package/plugins/specweave-jira/commands/import-projects.ts +1 -1
- package/plugins/specweave-jira/lib/jira-spec-commit-sync.d.ts +1 -1
- package/plugins/specweave-jira/lib/jira-spec-commit-sync.js +1 -1
- package/plugins/specweave-jira/lib/jira-spec-commit-sync.ts +1 -1
- package/plugins/specweave-jira/lib/jira-spec-content-sync.js +1 -1
- package/plugins/specweave-jira/lib/jira-spec-content-sync.ts +1 -1
- package/plugins/specweave-jira/lib/setup-wizard.js +2 -2
- package/plugins/specweave-jira/lib/setup-wizard.ts +2 -2
- package/plugins/specweave-jira/skills/specweave-jira-mapper/SKILL.md +1 -1
- package/plugins/specweave-kafka/agents/kafka-architect/AGENT.md +5 -1
- package/plugins/specweave-kafka/agents/kafka-devops/AGENT.md +6 -1
- package/plugins/specweave-kafka/agents/kafka-observability/AGENT.md +6 -1
- package/plugins/specweave-kubernetes/agents/kubernetes-architect/AGENT.md +3 -3
- package/plugins/specweave-ml/agents/data-scientist/AGENT.md +2 -1
- package/plugins/specweave-ml/agents/ml-engineer/AGENT.md +2 -1
- package/plugins/specweave-ml/agents/mlops-engineer/AGENT.md +3 -3
- package/plugins/specweave-mobile/agents/mobile-architect/AGENT.md +6 -1
- package/plugins/specweave-payments/agents/payment-integration/AGENT.md +3 -3
- package/plugins/specweave-release/agents/release-manager/AGENT.md +6 -1
- package/plugins/specweave-release/commands/specweave-release-npm.md +8 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +135 -0
- package/plugins/specweave-testing/agents/qa-engineer/AGENT.md +5 -0
- package/src/templates/AGENTS.md.template +97 -0
- package/dist/src/core/cost-tracker.d.ts.map +0 -1
- package/dist/src/core/cost-tracker.js.map +0 -1
- package/dist/src/core/credentials-manager.d.ts.map +0 -1
- package/dist/src/core/credentials-manager.js.map +0 -1
- package/dist/src/core/increment-status.d.ts.map +0 -1
- package/dist/src/core/increment-status.js.map +0 -1
- package/dist/src/core/increment-utils.d.ts.map +0 -1
- package/dist/src/core/increment-utils.js.map +0 -1
- package/dist/src/core/plugin-loader.d.ts.map +0 -1
- package/dist/src/core/plugin-loader.js.map +0 -1
- package/dist/src/core/project-manager.d.ts.map +0 -1
- package/dist/src/core/project-manager.js.map +0 -1
- package/dist/src/core/project-structure-detector.d.ts.map +0 -1
- package/dist/src/core/project-structure-detector.js.map +0 -1
- package/dist/src/core/rfc-generator-v2.d.ts.map +0 -1
- package/dist/src/core/rfc-generator-v2.js.map +0 -1
- package/dist/src/core/spec-content-sync.d.ts.map +0 -1
- package/dist/src/core/spec-content-sync.js.map +0 -1
- package/dist/src/core/spec-detector.d.ts.map +0 -1
- package/dist/src/core/spec-detector.js.map +0 -1
- package/dist/src/core/spec-identifier-detector.d.ts.map +0 -1
- package/dist/src/core/spec-identifier-detector.js.map +0 -1
- package/dist/src/core/spec-task-mapper.d.ts.map +0 -1
- package/dist/src/core/spec-task-mapper.js.map +0 -1
- package/dist/src/utils/spec-parser.d.ts +0 -145
- package/dist/src/utils/spec-parser.d.ts.map +0 -1
- package/dist/src/utils/spec-parser.js +0 -640
- package/dist/src/utils/spec-parser.js.map +0 -1
- package/plugins/specweave/agents/pm/AGENT.md.bak2 +0 -1754
- package/plugins/specweave/hooks/hooks.json.bak +0 -72
- package/plugins/specweave/hooks/hooks.json.v1-backup +0 -16
- package/plugins/specweave/hooks/v2/hooks.json +0 -16
- /package/dist/src/core/{credentials-manager.d.ts → credentials/credentials-manager.d.ts} +0 -0
- /package/dist/src/core/{credentials-manager.js → credentials/credentials-manager.js} +0 -0
- /package/dist/src/core/{increment-status.d.ts → increment/increment-status.d.ts} +0 -0
- /package/dist/src/core/{increment-utils.d.ts → increment/increment-utils.d.ts} +0 -0
- /package/dist/src/core/{increment-utils.js → increment/increment-utils.js} +0 -0
- /package/dist/src/core/{project-structure-detector.d.ts → project/project-structure-detector.d.ts} +0 -0
- /package/dist/src/core/{project-structure-detector.js → project/project-structure-detector.js} +0 -0
- /package/dist/src/core/{spec-task-mapper.d.ts → specs/spec-task-mapper.d.ts} +0 -0
- /package/dist/src/core/{spec-task-mapper.js → specs/spec-task-mapper.js} +0 -0
|
@@ -7,8 +7,11 @@
|
|
|
7
7
|
import * as fs from '../utils/fs-native.js';
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import { FSIdAllocator } from '../living-docs/fs-id-allocator.js';
|
|
10
|
+
import { EpicIdAllocator } from '../living-docs/epic-id-allocator.js';
|
|
11
|
+
import { createExternalMetadata } from '../core/types/origin-metadata.js';
|
|
10
12
|
import { DuplicateDetector } from './duplicate-detector.js';
|
|
11
13
|
import { normalizeToProjectId } from '../utils/project-id-generator.js';
|
|
14
|
+
import { MarkdownGenerator } from './markdown-generator.js';
|
|
12
15
|
/**
|
|
13
16
|
* Convert external items to living docs User Stories
|
|
14
17
|
*
|
|
@@ -19,8 +22,13 @@ export class ItemConverter {
|
|
|
19
22
|
constructor(options) {
|
|
20
23
|
this.duplicateDetector = null;
|
|
21
24
|
this.fsIdAllocator = null;
|
|
25
|
+
this.epicIdAllocator = null;
|
|
22
26
|
/** Cache of created feature folders to avoid duplicates */
|
|
23
27
|
this.createdFeatures = new Map();
|
|
28
|
+
/** Cache of created epic folders to avoid duplicates */
|
|
29
|
+
this.createdEpics = new Map();
|
|
30
|
+
/** Markdown generator for living docs content */
|
|
31
|
+
this.markdownGen = new MarkdownGenerator();
|
|
24
32
|
this.options = {
|
|
25
33
|
enableDuplicateDetection: true,
|
|
26
34
|
// NOTE: projectId is intentionally NOT defaulted to 'default'
|
|
@@ -42,6 +50,10 @@ export class ItemConverter {
|
|
|
42
50
|
// Use projectId if provided, otherwise undefined (direct to specs/)
|
|
43
51
|
// Enable global collision detection for umbrella/multi-repo setups
|
|
44
52
|
this.fsIdAllocator = new FSIdAllocator(this.options.projectRoot, this.options.projectId, { globalCollisionDetection: this.options.enableGlobalCollisionDetection });
|
|
53
|
+
// Initialize Epic ID allocator for ADO Capabilities (v0.29.3+)
|
|
54
|
+
// Capabilities are the top-level items in ADO SAFe/Enterprise setups
|
|
55
|
+
// They go to _epics/EP-XXXE/ instead of FS-XXXE/
|
|
56
|
+
this.epicIdAllocator = new EpicIdAllocator(this.options.projectRoot, { globalCollisionDetection: this.options.enableGlobalCollisionDetection });
|
|
45
57
|
}
|
|
46
58
|
}
|
|
47
59
|
/**
|
|
@@ -55,14 +67,14 @@ export class ItemConverter {
|
|
|
55
67
|
// Generate US-ID with E suffix
|
|
56
68
|
const id = `US-${String(usId).padStart(3, '0')}E`;
|
|
57
69
|
// Map external status to SpecWeave status
|
|
58
|
-
const status = this.mapStatus(item.status);
|
|
70
|
+
const status = this.markdownGen.mapStatus(item.status);
|
|
59
71
|
// Extract acceptance criteria
|
|
60
72
|
const acceptanceCriteria = item.acceptanceCriteria || [];
|
|
61
73
|
// Generate origin badge
|
|
62
|
-
const originBadge = this.generateOriginBadge(item);
|
|
74
|
+
const originBadge = this.markdownGen.generateOriginBadge(item);
|
|
63
75
|
// Generate markdown content for living docs
|
|
64
76
|
// CRITICAL (2025-12-01): Include parent info for future re-import parent change detection
|
|
65
|
-
const markdown = this.
|
|
77
|
+
const markdown = this.markdownGen.generateUserStoryMarkdown({
|
|
66
78
|
id,
|
|
67
79
|
title: item.title,
|
|
68
80
|
description: item.description,
|
|
@@ -88,7 +100,7 @@ export class ItemConverter {
|
|
|
88
100
|
}
|
|
89
101
|
});
|
|
90
102
|
// Generate file path (with feature folder if allocated)
|
|
91
|
-
const fileName = this.generateFileName(id, item.title);
|
|
103
|
+
const fileName = this.markdownGen.generateFileName(id, item.title);
|
|
92
104
|
let filePath;
|
|
93
105
|
// Check if item should be auto-archived (older than threshold)
|
|
94
106
|
const shouldArchive = this.shouldAutoArchive(item.createdAt);
|
|
@@ -169,7 +181,9 @@ export class ItemConverter {
|
|
|
169
181
|
let featureId;
|
|
170
182
|
if (this.fsIdAllocator && this.options.enableFeatureAllocation && groupItems.length > 0) {
|
|
171
183
|
const firstItem = groupItems[0];
|
|
172
|
-
|
|
184
|
+
// CRITICAL FIX (2025-12-01): Pass ALL group items to check if entire group should be archived
|
|
185
|
+
// This prevents duplicate folders when all items are old
|
|
186
|
+
featureId = await this.allocateFeatureForGroup(firstItem, groupKey, groupItems);
|
|
173
187
|
}
|
|
174
188
|
// Convert each item in the group
|
|
175
189
|
for (let i = 0; i < groupItems.length; i++) {
|
|
@@ -210,44 +224,81 @@ export class ItemConverter {
|
|
|
210
224
|
}
|
|
211
225
|
/**
|
|
212
226
|
* Group items by feature (based on source repo, parent hierarchy, or labels)
|
|
213
|
-
* CRITICAL FIX (2025-12-01): For ADO, group by parent Capability/Epic to preserve hierarchy
|
|
214
|
-
* CRITICAL FIX (2025-12-01): ADO orphans (no parent) get INDIVIDUAL folders, not 'default' group
|
|
215
227
|
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
* -
|
|
219
|
-
*
|
|
228
|
+
* CRITICAL FIX (2025-12-02): Intelligent ADO Hierarchy Mapping for SAFe/Enterprise
|
|
229
|
+
*
|
|
230
|
+
* ADO Hierarchy (5-level SAFe): Capability → Epic → Feature → User Story → Task
|
|
231
|
+
*
|
|
232
|
+
* SpecWeave Mapping:
|
|
233
|
+
* - Capability (ADO 5th level) → SpecWeave Epic (_epics/EP-XXXE/)
|
|
234
|
+
* - Epic (ADO 4th level, child of Capability) → SpecWeave Feature (FS-XXXE/) with parent ref
|
|
235
|
+
* - Epic (ADO 4th level, standalone) → SpecWeave Epic (_epics/EP-XXXE/)
|
|
236
|
+
* - Feature (ADO 3rd level) → SpecWeave Feature (FS-XXXE/)
|
|
237
|
+
* - User Story → us-xxxe.md in parent Feature folder
|
|
238
|
+
*
|
|
239
|
+
* Group Key Prefixes:
|
|
240
|
+
* - "epic:" → Item goes to _epics/EP-XXXE/ (Capability or standalone Epic)
|
|
241
|
+
* - "feature:" → Item goes to FS-XXXE/ (Epic under Capability, or Feature)
|
|
242
|
+
* - "orphan:" → Individual FS-XXXE/ folder (no parent)
|
|
243
|
+
* - "missing-parent:" → Grouped by missing parent ID
|
|
220
244
|
*/
|
|
221
245
|
groupItemsByFeature(items) {
|
|
222
246
|
const groups = new Map();
|
|
223
|
-
//
|
|
224
|
-
// ADO items have adoProjectName set by the importer
|
|
225
|
-
// Previous bug: Orphan ADO items (no parentId, not epics) fell through to 'default' group
|
|
247
|
+
// Detect ADO items
|
|
226
248
|
const hasAdoItems = items.some(item => item.platform === 'ado' && item.adoProjectName);
|
|
227
|
-
|
|
228
|
-
// Process ADO items: with hierarchy (group by parent) OR orphans (individual folders)
|
|
249
|
+
// Process ADO items with intelligent hierarchy mapping
|
|
229
250
|
if (hasAdoItems) {
|
|
230
251
|
// Build a map of all items by ID for lookup
|
|
231
252
|
const itemById = new Map();
|
|
232
253
|
for (const item of items) {
|
|
233
254
|
itemById.set(item.id, item);
|
|
234
255
|
}
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
256
|
+
// CRITICAL: Separate Capability (top-level) from other feature-level items
|
|
257
|
+
// Capabilities go to _epics/, everything else goes to FS-XXXE/
|
|
258
|
+
const capabilityItems = items.filter(item => item.adoWorkItemType?.toLowerCase() === 'capability');
|
|
259
|
+
// Epics and Features are "feature-level" items
|
|
260
|
+
// But Epics with Capability parents become Features (FS-XXXE)
|
|
261
|
+
// Epics without Capability parents become Epics (_epics/EP-XXXE)
|
|
262
|
+
const epicItems = items.filter(item => item.adoWorkItemType?.toLowerCase() === 'epic');
|
|
263
|
+
const featureItems = items.filter(item => item.adoWorkItemType?.toLowerCase() === 'feature');
|
|
264
|
+
// Create groups for Capabilities → _epics/EP-XXXE/
|
|
265
|
+
for (const capItem of capabilityItems) {
|
|
266
|
+
const groupKey = `epic:${capItem.id}`;
|
|
267
|
+
if (!groups.has(groupKey)) {
|
|
268
|
+
groups.set(groupKey, []);
|
|
269
|
+
}
|
|
270
|
+
groups.get(groupKey).push(capItem);
|
|
271
|
+
}
|
|
272
|
+
// Process Epics: determine if they're children of Capabilities or standalone
|
|
273
|
+
for (const epicItem of epicItems) {
|
|
274
|
+
let groupKey;
|
|
275
|
+
// Check if Epic has a Capability parent
|
|
276
|
+
const hasCapabilityParent = epicItem.parentId &&
|
|
277
|
+
capabilityItems.some(cap => cap.id === epicItem.parentId);
|
|
278
|
+
if (hasCapabilityParent) {
|
|
279
|
+
// Epic with Capability parent → FS-XXXE/ (Feature level)
|
|
280
|
+
// The Epic becomes a Feature folder, with reference to parent Epic
|
|
281
|
+
groupKey = `feature:${epicItem.id}`;
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
// Standalone Epic (no Capability parent) → _epics/EP-XXXE/
|
|
285
|
+
groupKey = `epic:${epicItem.id}`;
|
|
286
|
+
}
|
|
287
|
+
if (!groups.has(groupKey)) {
|
|
288
|
+
groups.set(groupKey, []);
|
|
289
|
+
}
|
|
290
|
+
groups.get(groupKey).push(epicItem);
|
|
291
|
+
}
|
|
292
|
+
// Create groups for Features → FS-XXXE/
|
|
242
293
|
for (const featureItem of featureItems) {
|
|
243
294
|
const groupKey = `feature:${featureItem.id}`;
|
|
244
295
|
if (!groups.has(groupKey)) {
|
|
245
296
|
groups.set(groupKey, []);
|
|
246
297
|
}
|
|
247
|
-
// The feature item itself goes first (used to generate FEATURE.md)
|
|
248
298
|
groups.get(groupKey).push(featureItem);
|
|
249
299
|
}
|
|
250
300
|
// Assign child items (User Stories, Tasks, Bugs) to their parent groups
|
|
301
|
+
const featureLevelTypes = new Set(['capability', 'epic', 'feature']);
|
|
251
302
|
for (const item of items) {
|
|
252
303
|
const witType = item.adoWorkItemType?.toLowerCase() || item.type;
|
|
253
304
|
if (featureLevelTypes.has(witType)) {
|
|
@@ -259,29 +310,40 @@ export class ItemConverter {
|
|
|
259
310
|
// Try to find parent in our items
|
|
260
311
|
const parent = itemById.get(item.parentId);
|
|
261
312
|
if (parent) {
|
|
262
|
-
// Find the
|
|
313
|
+
// Find the appropriate ancestor group
|
|
263
314
|
let current = parent;
|
|
264
315
|
while (current) {
|
|
265
316
|
const currentType = current.adoWorkItemType?.toLowerCase() || current.type;
|
|
266
|
-
if (
|
|
317
|
+
// Check if this ancestor is a Capability (→ epic group)
|
|
318
|
+
if (currentType === 'capability') {
|
|
319
|
+
groupKey = `epic:${current.id}`;
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
// Check if this ancestor is an Epic
|
|
323
|
+
if (currentType === 'epic') {
|
|
324
|
+
// Check if Epic has Capability parent (→ feature group)
|
|
325
|
+
const epicHasCapParent = current.parentId &&
|
|
326
|
+
capabilityItems.some(cap => cap.id === current.parentId);
|
|
327
|
+
groupKey = epicHasCapParent
|
|
328
|
+
? `feature:${current.id}`
|
|
329
|
+
: `epic:${current.id}`;
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
// Check if this ancestor is a Feature (→ feature group)
|
|
333
|
+
if (currentType === 'feature') {
|
|
267
334
|
groupKey = `feature:${current.id}`;
|
|
268
335
|
break;
|
|
269
336
|
}
|
|
270
337
|
current = current.parentId ? itemById.get(current.parentId) : undefined;
|
|
271
338
|
}
|
|
272
339
|
}
|
|
273
|
-
|
|
274
|
-
|
|
340
|
+
else {
|
|
341
|
+
// Parent NOT in dataset - group by parentId so siblings stay together
|
|
342
|
+
groupKey = `missing-parent:${item.parentId}`;
|
|
343
|
+
}
|
|
275
344
|
}
|
|
276
|
-
//
|
|
277
|
-
// folders per item. This applies when:
|
|
278
|
-
// - Item has no parentId (true orphan)
|
|
279
|
-
// - Item has parentId but parent NOT in dataset (orphan with missing parent)
|
|
280
|
-
// Each orphan gets its own feature folder with US title as FEATURE.md header
|
|
281
|
-
// UNIVERSAL: Applies to ALL platforms (ADO, JIRA, GitHub) for consistent handling
|
|
345
|
+
// For TRUE orphan items (no parentId at all), create INDIVIDUAL folders
|
|
282
346
|
if (!groupKey) {
|
|
283
|
-
// Each orphan gets its own feature folder with unique group key
|
|
284
|
-
// This ensures consistent behavior across all platforms
|
|
285
347
|
groupKey = `orphan:${item.id}`;
|
|
286
348
|
}
|
|
287
349
|
if (!groups.has(groupKey)) {
|
|
@@ -303,23 +365,79 @@ export class ItemConverter {
|
|
|
303
365
|
}
|
|
304
366
|
/**
|
|
305
367
|
* Allocate a feature ID for a group of items
|
|
306
|
-
*
|
|
368
|
+
*
|
|
369
|
+
* CRITICAL FIX (2025-12-02): Handle "epic:" groups using EpicIdAllocator
|
|
370
|
+
*
|
|
371
|
+
* Group Key Handling:
|
|
372
|
+
* - "epic:..." → Use EpicIdAllocator, create in _epics/EP-XXXE/
|
|
373
|
+
* - "feature:..." → Use FSIdAllocator, create in FS-XXXE/
|
|
374
|
+
* - "orphan:..." → Use FSIdAllocator, create in FS-XXXE/
|
|
375
|
+
* - Other → Use FSIdAllocator
|
|
376
|
+
*
|
|
377
|
+
* @returns Feature ID (FS-XXXE) or Epic ID (EP-XXXE)
|
|
307
378
|
*/
|
|
308
|
-
async allocateFeatureForGroup(firstItem, groupKey) {
|
|
309
|
-
//
|
|
310
|
-
|
|
311
|
-
|
|
379
|
+
async allocateFeatureForGroup(firstItem, groupKey, allGroupItems) {
|
|
380
|
+
// CRITICAL: Detect if this is an "epic:" group (Capability or standalone Epic)
|
|
381
|
+
const isEpicGroup = groupKey.startsWith('epic:');
|
|
382
|
+
// Check cache based on group type
|
|
383
|
+
if (isEpicGroup) {
|
|
384
|
+
if (this.createdEpics.has(groupKey)) {
|
|
385
|
+
return this.createdEpics.get(groupKey);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
if (this.createdFeatures.has(groupKey)) {
|
|
390
|
+
return this.createdFeatures.get(groupKey);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
// For "epic:" groups, use EpicIdAllocator
|
|
394
|
+
if (isEpicGroup) {
|
|
395
|
+
if (!this.epicIdAllocator) {
|
|
396
|
+
throw new Error('EpicIdAllocator not initialized');
|
|
397
|
+
}
|
|
398
|
+
// Create external epic item for allocation
|
|
399
|
+
const epicItem = {
|
|
400
|
+
externalId: firstItem.id,
|
|
401
|
+
title: firstItem.title,
|
|
402
|
+
createdAt: firstItem.createdAt.toISOString(),
|
|
403
|
+
externalUrl: firstItem.url,
|
|
404
|
+
workItemType: firstItem.adoWorkItemType || 'Capability'
|
|
405
|
+
};
|
|
406
|
+
// Allocate Epic ID
|
|
407
|
+
const allocation = await this.epicIdAllocator.allocateId(epicItem);
|
|
408
|
+
const epicId = allocation.id;
|
|
409
|
+
// Create Epic folder in _epics/
|
|
410
|
+
const metadata = createExternalMetadata({
|
|
411
|
+
id: epicId,
|
|
412
|
+
source: firstItem.platform,
|
|
413
|
+
externalId: firstItem.id,
|
|
414
|
+
externalUrl: firstItem.url,
|
|
415
|
+
externalTitle: firstItem.title
|
|
416
|
+
});
|
|
417
|
+
await this.epicIdAllocator.createEpicFolder(epicId, epicItem, metadata);
|
|
418
|
+
// Cache for reuse
|
|
419
|
+
this.createdEpics.set(groupKey, epicId);
|
|
420
|
+
// Notify callback
|
|
421
|
+
if (this.options.onFeatureCreated) {
|
|
422
|
+
this.options.onFeatureCreated(epicId, path.join(this.options.specsDir, '_epics', epicId));
|
|
423
|
+
}
|
|
424
|
+
return epicId;
|
|
312
425
|
}
|
|
426
|
+
// For "feature:" and other groups, use FSIdAllocator
|
|
313
427
|
if (!this.fsIdAllocator) {
|
|
314
428
|
throw new Error('FSIdAllocator not initialized');
|
|
315
429
|
}
|
|
316
|
-
// For ADO
|
|
430
|
+
// For ADO feature groups, check if it's an Epic with Capability parent
|
|
431
|
+
// If so, include parent reference in the feature folder
|
|
317
432
|
const isAdoFeatureGroup = groupKey.startsWith('feature:');
|
|
318
433
|
const featureLevelTypes = new Set(['capability', 'epic', 'feature']);
|
|
319
434
|
const isFeatureLevelItem = isAdoFeatureGroup &&
|
|
320
435
|
featureLevelTypes.has(firstItem.adoWorkItemType?.toLowerCase() || firstItem.type);
|
|
436
|
+
// Check if this Epic has a Capability parent
|
|
437
|
+
const isEpicWithCapParent = firstItem.adoWorkItemType?.toLowerCase() === 'epic' &&
|
|
438
|
+
firstItem.parentId &&
|
|
439
|
+
this.createdEpics.has(`epic:${firstItem.parentId}`);
|
|
321
440
|
// Create external work item for allocation
|
|
322
|
-
// For ADO feature groups, use the Capability/Epic's title as the feature name
|
|
323
441
|
const workItem = {
|
|
324
442
|
externalId: firstItem.id,
|
|
325
443
|
title: isFeatureLevelItem ? firstItem.title : (firstItem.sourceRepo || firstItem.title),
|
|
@@ -329,8 +447,15 @@ export class ItemConverter {
|
|
|
329
447
|
// Allocate feature ID
|
|
330
448
|
const allocation = await this.fsIdAllocator.allocateId(workItem);
|
|
331
449
|
const featureId = allocation.id;
|
|
450
|
+
// Check if ALL items in the group should be archived
|
|
451
|
+
const shouldArchiveFeature = this.shouldArchiveEntireGroup(allGroupItems || [firstItem]);
|
|
332
452
|
// Create feature folder with FEATURE.md
|
|
333
|
-
|
|
453
|
+
// If Epic with Capability parent, include parent reference
|
|
454
|
+
const parentEpicId = isEpicWithCapParent
|
|
455
|
+
? this.createdEpics.get(`epic:${firstItem.parentId}`)
|
|
456
|
+
: undefined;
|
|
457
|
+
const featurePath = await this.createFeatureFolder(featureId, firstItem, groupKey, shouldArchiveFeature, parentEpicId // Pass parent Epic ID for reference
|
|
458
|
+
);
|
|
334
459
|
// Cache for reuse
|
|
335
460
|
this.createdFeatures.set(groupKey, featureId);
|
|
336
461
|
// Notify callback
|
|
@@ -339,6 +464,29 @@ export class ItemConverter {
|
|
|
339
464
|
}
|
|
340
465
|
return featureId;
|
|
341
466
|
}
|
|
467
|
+
/**
|
|
468
|
+
* Check if ALL items in a group should be archived
|
|
469
|
+
* CRITICAL FIX (2025-12-01): Prevents duplicate folder creation
|
|
470
|
+
*
|
|
471
|
+
* If ALL items are old (should be archived), the feature folder itself
|
|
472
|
+
* should be created in _archive/ to avoid having an empty FS-XXX/ folder
|
|
473
|
+
* in the main location while all content is in _archive/FS-XXX/
|
|
474
|
+
*
|
|
475
|
+
* @param items - All items in the feature group
|
|
476
|
+
* @returns True if ALL items should be archived
|
|
477
|
+
*/
|
|
478
|
+
shouldArchiveEntireGroup(items) {
|
|
479
|
+
if (items.length === 0) {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
// Check if auto-archiving is disabled
|
|
483
|
+
const threshold = this.options.autoArchiveAfterDays;
|
|
484
|
+
if (!threshold || threshold <= 0) {
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
// ALL items must be old enough to archive the entire group
|
|
488
|
+
return items.every(item => this.shouldAutoArchive(item.createdAt));
|
|
489
|
+
}
|
|
342
490
|
/**
|
|
343
491
|
* Get base directory for specs, handling both 1-level and 2-level structures
|
|
344
492
|
*
|
|
@@ -374,12 +522,23 @@ export class ItemConverter {
|
|
|
374
522
|
}
|
|
375
523
|
/**
|
|
376
524
|
* Create feature folder with FEATURE.md
|
|
377
|
-
*
|
|
525
|
+
*
|
|
526
|
+
* CRITICAL FIX (2025-12-02): Support parent Epic reference for ADO Epic→Feature mapping
|
|
527
|
+
*
|
|
528
|
+
* @param featureId - Feature ID (e.g., "FS-001E")
|
|
529
|
+
* @param firstItem - First item in the group (used for metadata)
|
|
530
|
+
* @param groupKey - Group key for caching
|
|
531
|
+
* @param shouldArchive - If true, create feature folder in _archive/ directory
|
|
532
|
+
* @param parentEpicId - Optional parent Epic ID (EP-XXXE) for hierarchy reference
|
|
378
533
|
*/
|
|
379
|
-
async createFeatureFolder(featureId, firstItem, groupKey) {
|
|
534
|
+
async createFeatureFolder(featureId, firstItem, groupKey, shouldArchive = false, parentEpicId) {
|
|
380
535
|
// Use 2-level or 1-level structure based on externalContainer
|
|
381
536
|
const baseDir = this.getBaseDirectory();
|
|
382
|
-
|
|
537
|
+
// CRITICAL FIX (2025-12-01): Create in _archive if all items in group are old
|
|
538
|
+
// This prevents duplicate folders: FS-XXX/ (empty) + _archive/FS-XXX/ (with content)
|
|
539
|
+
const featurePath = shouldArchive
|
|
540
|
+
? path.join(baseDir, '_archive', featureId)
|
|
541
|
+
: path.join(baseDir, featureId);
|
|
383
542
|
// Create directory
|
|
384
543
|
fs.mkdirSync(featurePath, { recursive: true });
|
|
385
544
|
// Check if this is a feature-level item or orphan
|
|
@@ -443,6 +602,11 @@ export class ItemConverter {
|
|
|
443
602
|
}
|
|
444
603
|
// Include external metadata for feature-level items and all orphans
|
|
445
604
|
const includeExternalMetadata = isAdoFeatureLevelItem || isOrphanGroup;
|
|
605
|
+
// Build parent Epic reference if available (for ADO Epic → Feature mapping)
|
|
606
|
+
const hasParentEpic = parentEpicId !== undefined;
|
|
607
|
+
const parentEpicLink = hasParentEpic
|
|
608
|
+
? `[${parentEpicId}](../_epics/${parentEpicId}/EPIC.md)`
|
|
609
|
+
: '';
|
|
446
610
|
const featureContent = `---
|
|
447
611
|
id: ${featureId}
|
|
448
612
|
title: ${featureTitle}
|
|
@@ -453,6 +617,7 @@ ${firstItem.adoProjectName ? `ado_project: ${firstItem.adoProjectName}` : ''}
|
|
|
453
617
|
${firstItem.adoAreaPath ? `ado_area_path: ${firstItem.adoAreaPath}` : ''}
|
|
454
618
|
${includeExternalMetadata ? `work_item_type: ${witTypeLabel}` : ''}
|
|
455
619
|
${includeExternalMetadata ? `external_id: ${firstItem.id}` : ''}
|
|
620
|
+
${hasParentEpic ? `parent_epic: ${parentEpicId}` : ''}
|
|
456
621
|
${isOrphanGroup ? `orphan: true` : ''}
|
|
457
622
|
created: ${new Date().toISOString()}
|
|
458
623
|
---
|
|
@@ -460,6 +625,7 @@ created: ${new Date().toISOString()}
|
|
|
460
625
|
# ${featureTitle}
|
|
461
626
|
|
|
462
627
|
**Origin**: 🔗 ${externalLink || `Imported from ${platformLabel}`}
|
|
628
|
+
${hasParentEpic ? `\n**Parent Epic**: ${parentEpicLink}` : ''}
|
|
463
629
|
|
|
464
630
|
## Description
|
|
465
631
|
|
|
@@ -467,6 +633,7 @@ ${description}
|
|
|
467
633
|
|
|
468
634
|
${firstItem.sourceRepo ? `**Source Repository**: ${firstItem.sourceRepo}` : ''}
|
|
469
635
|
${firstItem.adoAreaPath ? `**Area Path**: ${firstItem.adoAreaPath}` : ''}
|
|
636
|
+
${hasParentEpic ? `\n> **Hierarchy**: This feature belongs to Epic ${parentEpicId} (Capability in Azure DevOps).` : ''}
|
|
470
637
|
${isOrphanGroup ? `\n> **Note**: This feature was created from an orphan item (no parent Epic/Feature in ${platformLabel} or parent not imported).` : ''}
|
|
471
638
|
|
|
472
639
|
## User Stories
|
|
@@ -478,6 +645,7 @@ User stories in this ${witTypeLabel.toLowerCase()} will be listed here.
|
|
|
478
645
|
- **Created**: ${new Date().toISOString()}
|
|
479
646
|
- **Source**: ${platformLabel}
|
|
480
647
|
${includeExternalMetadata ? `- **External ID**: ${firstItem.id}` : ''}
|
|
648
|
+
${hasParentEpic ? `- **Parent Epic**: ${parentEpicId}` : ''}
|
|
481
649
|
${isOrphanGroup ? `- **Type**: Orphan (no parent Epic)` : ''}
|
|
482
650
|
`;
|
|
483
651
|
// Write FEATURE.md
|
|
@@ -485,18 +653,7 @@ ${isOrphanGroup ? `- **Type**: Orphan (no parent Epic)` : ''}
|
|
|
485
653
|
fs.writeFileSync(featureFile, featureContent, 'utf-8');
|
|
486
654
|
return featurePath;
|
|
487
655
|
}
|
|
488
|
-
|
|
489
|
-
* Map external status to SpecWeave status
|
|
490
|
-
*/
|
|
491
|
-
mapStatus(externalStatus) {
|
|
492
|
-
const statusMap = {
|
|
493
|
-
'open': 'Open',
|
|
494
|
-
'in-progress': 'In Progress',
|
|
495
|
-
'completed': 'Completed',
|
|
496
|
-
'closed': 'Completed'
|
|
497
|
-
};
|
|
498
|
-
return statusMap[externalStatus] || 'Open';
|
|
499
|
-
}
|
|
656
|
+
// mapStatus moved to MarkdownGenerator
|
|
500
657
|
/**
|
|
501
658
|
* Check if an item should be auto-archived based on creation date
|
|
502
659
|
*
|
|
@@ -643,126 +800,9 @@ ${isOrphanGroup ? `- **Type**: Orphan (no parent Epic)` : ''}
|
|
|
643
800
|
// Folder cleanup is best-effort, don't fail on errors
|
|
644
801
|
}
|
|
645
802
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
generateOriginBadge(item) {
|
|
650
|
-
const platformEmoji = {
|
|
651
|
-
'github': '🔗',
|
|
652
|
-
'jira': '🔗',
|
|
653
|
-
'ado': '🔗'
|
|
654
|
-
};
|
|
655
|
-
const platformName = {
|
|
656
|
-
'github': 'GitHub',
|
|
657
|
-
'jira': 'JIRA',
|
|
658
|
-
'ado': 'Azure DevOps'
|
|
659
|
-
};
|
|
660
|
-
const emoji = platformEmoji[item.platform] || '🔗';
|
|
661
|
-
const name = platformName[item.platform] || item.platform;
|
|
662
|
-
// Extract issue/ticket number from external ID
|
|
663
|
-
// v0.29+ format: github#owner/repo#123 → extract "123"
|
|
664
|
-
// Legacy format: github#123 → extract "123"
|
|
665
|
-
let issueNumber = item.id;
|
|
666
|
-
const newFormatMatch = item.id.match(/#(\d+)$/);
|
|
667
|
-
if (newFormatMatch) {
|
|
668
|
-
issueNumber = newFormatMatch[1];
|
|
669
|
-
}
|
|
670
|
-
else {
|
|
671
|
-
issueNumber = item.id.replace(/^(GITHUB|JIRA|ADO)-/, '');
|
|
672
|
-
}
|
|
673
|
-
return `${emoji} [${name} #${issueNumber}](${item.url})`;
|
|
674
|
-
}
|
|
675
|
-
/**
|
|
676
|
-
* Generate markdown content for living docs User Story
|
|
677
|
-
* CRITICAL (2025-12-01): Includes parent tracking info for re-import hierarchy updates
|
|
678
|
-
*/
|
|
679
|
-
generateMarkdown(data) {
|
|
680
|
-
const parts = [];
|
|
681
|
-
// Title
|
|
682
|
-
parts.push(`# ${data.id}: ${data.title}`);
|
|
683
|
-
parts.push('');
|
|
684
|
-
// Origin badge
|
|
685
|
-
parts.push(`**Origin**: ${data.originBadge}`);
|
|
686
|
-
parts.push('');
|
|
687
|
-
// Status and Priority
|
|
688
|
-
parts.push(`**Status**: ${data.status}`);
|
|
689
|
-
if (data.priority) {
|
|
690
|
-
parts.push(`**Priority**: ${data.priority}`);
|
|
691
|
-
}
|
|
692
|
-
parts.push('');
|
|
693
|
-
// Description
|
|
694
|
-
parts.push('## Description');
|
|
695
|
-
parts.push('');
|
|
696
|
-
parts.push(data.description || 'No description provided.');
|
|
697
|
-
parts.push('');
|
|
698
|
-
// Acceptance Criteria
|
|
699
|
-
if (data.acceptanceCriteria.length > 0) {
|
|
700
|
-
parts.push('## Acceptance Criteria');
|
|
701
|
-
parts.push('');
|
|
702
|
-
data.acceptanceCriteria.forEach((ac, index) => {
|
|
703
|
-
const acId = `AC-${data.id.replace('E', '')}-${String(index + 1).padStart(2, '0')}`;
|
|
704
|
-
parts.push(`- [ ] **${acId}**: ${ac}`);
|
|
705
|
-
});
|
|
706
|
-
parts.push('');
|
|
707
|
-
}
|
|
708
|
-
// Tasks
|
|
709
|
-
parts.push('## Tasks');
|
|
710
|
-
parts.push('');
|
|
711
|
-
parts.push('> **Note**: This User Story was imported from an external tool.');
|
|
712
|
-
parts.push('> Create tasks manually when ready to implement.');
|
|
713
|
-
parts.push('');
|
|
714
|
-
// Metadata (frontmatter-style at bottom)
|
|
715
|
-
parts.push('---');
|
|
716
|
-
parts.push('');
|
|
717
|
-
parts.push('## External Metadata');
|
|
718
|
-
parts.push('');
|
|
719
|
-
parts.push(`- **External ID**: ${data.metadata.externalId}`);
|
|
720
|
-
parts.push(`- **External URL**: ${data.metadata.externalUrl}`);
|
|
721
|
-
parts.push(`- **Platform**: ${data.metadata.externalPlatform}`);
|
|
722
|
-
parts.push(`- **Imported At**: ${data.metadata.importedAt}`);
|
|
723
|
-
parts.push(`- **Created At**: ${data.metadata.createdAt}`);
|
|
724
|
-
parts.push(`- **Updated At**: ${data.metadata.updatedAt}`);
|
|
725
|
-
if (data.metadata.labels.length > 0) {
|
|
726
|
-
parts.push(`- **Labels**: ${data.metadata.labels.join(', ')}`);
|
|
727
|
-
}
|
|
728
|
-
if (data.metadata.sourceRepo) {
|
|
729
|
-
parts.push(`- **Source Repository**: ${data.metadata.sourceRepo}`);
|
|
730
|
-
}
|
|
731
|
-
// Parent tracking for re-import hierarchy updates (CRITICAL for parent change detection)
|
|
732
|
-
if (data.metadata.featureId) {
|
|
733
|
-
parts.push(`- **Feature ID**: ${data.metadata.featureId}`);
|
|
734
|
-
}
|
|
735
|
-
if (data.metadata.parentId) {
|
|
736
|
-
parts.push(`- **Parent ID**: ${data.metadata.parentId}`);
|
|
737
|
-
}
|
|
738
|
-
if (data.metadata.isOrphan) {
|
|
739
|
-
parts.push(`- **Orphan**: true (no parent in external tool)`);
|
|
740
|
-
}
|
|
741
|
-
if (data.metadata.adoWorkItemType) {
|
|
742
|
-
parts.push(`- **ADO Work Item Type**: ${data.metadata.adoWorkItemType}`);
|
|
743
|
-
}
|
|
744
|
-
if (data.metadata.adoAreaPath) {
|
|
745
|
-
parts.push(`- **ADO Area Path**: ${data.metadata.adoAreaPath}`);
|
|
746
|
-
}
|
|
747
|
-
return parts.join('\n');
|
|
748
|
-
}
|
|
749
|
-
/**
|
|
750
|
-
* Generate file name for living docs User Story
|
|
751
|
-
*
|
|
752
|
-
* Format: us-001e-title-here.md
|
|
753
|
-
*/
|
|
754
|
-
generateFileName(usId, title) {
|
|
755
|
-
// Convert US-001E to us-001e
|
|
756
|
-
const idPart = usId.toLowerCase();
|
|
757
|
-
// Convert title to kebab-case
|
|
758
|
-
const titlePart = title
|
|
759
|
-
.toLowerCase()
|
|
760
|
-
.replace(/[^a-z0-9\s-]/g, '') // Remove special chars
|
|
761
|
-
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
|
762
|
-
.replace(/-+/g, '-') // Remove duplicate hyphens
|
|
763
|
-
.slice(0, 50); // Limit to 50 chars
|
|
764
|
-
return `${idPart}-${titlePart}.md`;
|
|
765
|
-
}
|
|
803
|
+
// generateOriginBadge moved to MarkdownGenerator
|
|
804
|
+
// generateMarkdown moved to MarkdownGenerator
|
|
805
|
+
// generateFileName moved to MarkdownGenerator
|
|
766
806
|
/**
|
|
767
807
|
* Validate that no increments were created
|
|
768
808
|
*
|