specweave 0.21.3 → 0.22.2
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 +409 -6
- package/README.md +38 -8
- package/bin/specweave.js +5 -8
- package/dist/plugins/specweave-github/lib/CodeValidator.d.ts +101 -0
- package/dist/plugins/specweave-github/lib/CodeValidator.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/CodeValidator.js +219 -0
- package/dist/plugins/specweave-github/lib/CodeValidator.js.map +1 -0
- package/dist/plugins/specweave-github/lib/ThreeLayerSyncManager.d.ts +182 -0
- package/dist/plugins/specweave-github/lib/ThreeLayerSyncManager.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/ThreeLayerSyncManager.js +603 -0
- package/dist/plugins/specweave-github/lib/ThreeLayerSyncManager.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +10 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js +26 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/plugins/specweave-github/lib/task-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/task-sync.js +7 -0
- package/dist/plugins/specweave-github/lib/task-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/types.d.ts +34 -0
- package/dist/plugins/specweave-github/lib/types.d.ts.map +1 -1
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +60 -5
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/migrate-to-profiles.d.ts +1 -0
- package/dist/src/cli/commands/migrate-to-profiles.d.ts.map +1 -1
- package/dist/src/cli/commands/migrate-to-profiles.js +12 -1
- package/dist/src/cli/commands/migrate-to-profiles.js.map +1 -1
- package/dist/src/cli/commands/next-command.d.ts +52 -0
- package/dist/src/cli/commands/next-command.d.ts.map +1 -0
- package/dist/src/cli/commands/next-command.js +204 -0
- package/dist/src/cli/commands/next-command.js.map +1 -0
- package/dist/src/cli/commands/sync-specs.d.ts +16 -0
- package/dist/src/cli/commands/sync-specs.d.ts.map +1 -0
- package/dist/src/cli/commands/sync-specs.js +130 -0
- package/dist/src/cli/commands/sync-specs.js.map +1 -0
- package/dist/src/cli/count-tasks.d.ts +20 -0
- package/dist/src/cli/count-tasks.d.ts.map +1 -0
- package/dist/src/cli/count-tasks.js +50 -0
- package/dist/src/cli/count-tasks.js.map +1 -0
- package/dist/src/config/ConfigManager.d.ts.map +1 -1
- package/dist/src/config/ConfigManager.js +2 -1
- package/dist/src/config/ConfigManager.js.map +1 -1
- package/dist/src/config/types.d.ts +58 -58
- package/dist/src/core/cicd/state-manager.d.ts +8 -0
- package/dist/src/core/cicd/state-manager.d.ts.map +1 -1
- package/dist/src/core/cicd/state-manager.js +60 -15
- package/dist/src/core/cicd/state-manager.js.map +1 -1
- package/dist/src/core/cost-tracker.d.ts.map +1 -1
- package/dist/src/core/cost-tracker.js +2 -1
- package/dist/src/core/cost-tracker.js.map +1 -1
- package/dist/src/core/iac/template-engine.d.ts.map +1 -1
- package/dist/src/core/iac/template-engine.js +28 -0
- package/dist/src/core/iac/template-engine.js.map +1 -1
- package/dist/src/core/iac/template-generator.d.ts +53 -0
- package/dist/src/core/iac/template-generator.d.ts.map +1 -0
- package/dist/src/core/iac/template-generator.js +125 -0
- package/dist/src/core/iac/template-generator.js.map +1 -0
- package/dist/src/core/increment/status-auto-transition.js +3 -3
- package/dist/src/core/increment/status-auto-transition.js.map +1 -1
- package/dist/src/core/living-docs/CodeValidator.js +1 -1
- package/dist/src/core/living-docs/CodeValidator.js.map +1 -1
- package/dist/src/core/living-docs/CompletionPropagator.d.ts.map +1 -1
- package/dist/src/core/living-docs/CompletionPropagator.js +4 -3
- package/dist/src/core/living-docs/CompletionPropagator.js.map +1 -1
- package/dist/src/core/living-docs/SpecDistributor.d.ts +5 -0
- package/dist/src/core/living-docs/SpecDistributor.d.ts.map +1 -1
- package/dist/src/core/living-docs/SpecDistributor.js +12 -0
- package/dist/src/core/living-docs/SpecDistributor.js.map +1 -1
- package/dist/src/core/living-docs/content-distributor.d.ts.map +1 -1
- package/dist/src/core/living-docs/content-distributor.js +11 -1
- package/dist/src/core/living-docs/content-distributor.js.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.d.ts +166 -0
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -0
- package/dist/src/core/living-docs/living-docs-sync.js +726 -0
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -0
- package/dist/src/core/living-docs/project-detector.d.ts.map +1 -1
- package/dist/src/core/living-docs/project-detector.js +38 -0
- package/dist/src/core/living-docs/project-detector.js.map +1 -1
- package/dist/src/core/living-docs/task-project-specific-generator.d.ts +7 -3
- package/dist/src/core/living-docs/task-project-specific-generator.d.ts.map +1 -1
- package/dist/src/core/living-docs/task-project-specific-generator.js +40 -24
- package/dist/src/core/living-docs/task-project-specific-generator.js.map +1 -1
- package/dist/src/core/plugin-loader.d.ts +7 -0
- package/dist/src/core/plugin-loader.d.ts.map +1 -1
- package/dist/src/core/plugin-loader.js +18 -1
- package/dist/src/core/plugin-loader.js.map +1 -1
- package/dist/src/core/serverless/platform-data-loader.d.ts +8 -0
- package/dist/src/core/serverless/platform-data-loader.d.ts.map +1 -1
- package/dist/src/core/serverless/platform-data-loader.js +14 -0
- package/dist/src/core/serverless/platform-data-loader.js.map +1 -1
- package/dist/src/core/serverless/types.d.ts +1 -1
- package/dist/src/core/serverless/types.d.ts.map +1 -1
- package/dist/src/core/status-line/status-line-manager.d.ts +6 -2
- package/dist/src/core/status-line/status-line-manager.d.ts.map +1 -1
- package/dist/src/core/status-line/status-line-manager.js +11 -5
- package/dist/src/core/status-line/status-line-manager.js.map +1 -1
- package/dist/src/core/status-line/task-counter.d.ts +69 -0
- package/dist/src/core/status-line/task-counter.d.ts.map +1 -0
- package/dist/src/core/status-line/task-counter.js +107 -0
- package/dist/src/core/status-line/task-counter.js.map +1 -0
- package/dist/src/core/types/config.d.ts +23 -0
- package/dist/src/core/types/config.d.ts.map +1 -1
- package/dist/src/core/types/config.js +10 -0
- package/dist/src/core/types/config.js.map +1 -1
- package/dist/src/core/workflow/autonomous-executor.d.ts +111 -0
- package/dist/src/core/workflow/autonomous-executor.d.ts.map +1 -0
- package/dist/src/core/workflow/autonomous-executor.js +275 -0
- package/dist/src/core/workflow/autonomous-executor.js.map +1 -0
- package/dist/src/core/workflow/backlog-scanner.d.ts +94 -0
- package/dist/src/core/workflow/backlog-scanner.d.ts.map +1 -0
- package/dist/src/core/workflow/backlog-scanner.js +170 -0
- package/dist/src/core/workflow/backlog-scanner.js.map +1 -0
- package/dist/src/core/workflow/command-invoker.d.ts +86 -0
- package/dist/src/core/workflow/command-invoker.d.ts.map +1 -0
- package/dist/src/core/workflow/command-invoker.js +131 -0
- package/dist/src/core/workflow/command-invoker.js.map +1 -0
- package/dist/src/core/workflow/cost-estimator.d.ts +120 -0
- package/dist/src/core/workflow/cost-estimator.d.ts.map +1 -0
- package/dist/src/core/workflow/cost-estimator.js +222 -0
- package/dist/src/core/workflow/cost-estimator.js.map +1 -0
- package/dist/src/core/workflow/index.d.ts +20 -0
- package/dist/src/core/workflow/index.d.ts.map +1 -0
- package/dist/src/core/workflow/index.js +24 -0
- package/dist/src/core/workflow/index.js.map +1 -0
- package/dist/src/core/workflow/state-manager.d.ts +107 -0
- package/dist/src/core/workflow/state-manager.d.ts.map +1 -0
- package/dist/src/core/workflow/state-manager.js +126 -0
- package/dist/src/core/workflow/state-manager.js.map +1 -0
- package/dist/src/core/workflow/workflow-orchestrator.d.ts +93 -0
- package/dist/src/core/workflow/workflow-orchestrator.d.ts.map +1 -0
- package/dist/src/core/workflow/workflow-orchestrator.js +195 -0
- package/dist/src/core/workflow/workflow-orchestrator.js.map +1 -0
- package/dist/src/init/ArchitecturePresenter.d.ts +47 -0
- package/dist/src/init/ArchitecturePresenter.d.ts.map +1 -0
- package/dist/src/init/ArchitecturePresenter.js +180 -0
- package/dist/src/init/ArchitecturePresenter.js.map +1 -0
- package/dist/src/init/InitFlow.d.ts.map +1 -1
- package/dist/src/init/InitFlow.js +30 -1
- package/dist/src/init/InitFlow.js.map +1 -1
- package/dist/src/init/architecture/CostEstimator.d.ts +52 -0
- package/dist/src/init/architecture/CostEstimator.d.ts.map +1 -0
- package/dist/src/init/architecture/CostEstimator.js +107 -0
- package/dist/src/init/architecture/CostEstimator.js.map +1 -0
- package/dist/src/init/architecture/InfrastructureMapper.d.ts +41 -0
- package/dist/src/init/architecture/InfrastructureMapper.d.ts.map +1 -0
- package/dist/src/init/architecture/InfrastructureMapper.js +140 -0
- package/dist/src/init/architecture/InfrastructureMapper.js.map +1 -0
- package/dist/src/init/architecture/ProjectGenerator.d.ts +44 -0
- package/dist/src/init/architecture/ProjectGenerator.d.ts.map +1 -0
- package/dist/src/init/architecture/ProjectGenerator.js +216 -0
- package/dist/src/init/architecture/ProjectGenerator.js.map +1 -0
- package/dist/src/init/architecture/types.d.ts +10 -10
- package/dist/src/init/research/src/config/types.d.ts +8 -8
- package/dist/src/metrics/dora-calculator.js +2 -2
- package/dist/src/metrics/dora-calculator.js.map +1 -1
- package/dist/src/utils/pricing-constants.d.ts +5 -2
- package/dist/src/utils/pricing-constants.d.ts.map +1 -1
- package/dist/src/utils/pricing-constants.js +3 -2
- package/dist/src/utils/pricing-constants.js.map +1 -1
- package/package.json +9 -8
- package/plugins/specweave/agents/infrastructure/AGENT.md +88 -46
- package/plugins/specweave/agents/pm/AGENT.md +58 -1
- package/plugins/specweave/commands/specweave-archive-features.md +1 -1
- package/plugins/specweave/commands/specweave-archive-increments.md +1 -1
- package/plugins/specweave/commands/specweave-check-hooks.md +5 -0
- package/plugins/specweave/commands/specweave-done.md +12 -0
- package/plugins/specweave/commands/specweave-plan.md +1 -1
- package/plugins/specweave/commands/specweave-progress.md +108 -379
- package/plugins/specweave/commands/specweave-reopen.md +1 -1
- package/plugins/specweave/commands/specweave-restore-feature.md +1 -1
- package/plugins/specweave/commands/specweave-sync-specs.md +20 -48
- package/plugins/specweave/hooks/lib/update-status-line.sh +44 -35
- package/plugins/specweave/hooks/lib/validate-spec-status.sh +163 -0
- package/plugins/specweave/hooks/user-prompt-submit.sh +17 -35
- package/plugins/specweave/lib/hooks/update-tasks-md.js +52 -9
- package/plugins/specweave/lib/hooks/update-tasks-md.ts +77 -16
- package/plugins/specweave/templates/iac/aws-lambda/defaults.json +24 -0
- package/plugins/specweave/templates/iac/aws-lambda/templates/README.md.hbs +260 -0
- package/plugins/specweave/templates/iac/aws-lambda/templates/environments/dev.tfvars.hbs +34 -0
- package/plugins/specweave/templates/iac/aws-lambda/templates/environments/prod.tfvars.hbs +37 -0
- package/plugins/specweave/templates/iac/aws-lambda/templates/environments/staging.tfvars.hbs +35 -0
- package/plugins/specweave/templates/iac/aws-lambda/templates/outputs.tf.hbs +77 -0
- package/plugins/specweave/templates/iac/aws-lambda/templates/providers.tf.hbs +36 -0
- package/plugins/specweave/templates/iac/aws-lambda/templates/variables.tf.hbs +115 -0
- package/plugins/specweave/templates/iac/azure-functions/defaults.json +25 -0
- package/plugins/specweave/templates/iac/azure-functions/templates/README.md.hbs +268 -0
- package/plugins/specweave/templates/iac/azure-functions/templates/environments/dev.tfvars.hbs +34 -0
- package/plugins/specweave/templates/iac/azure-functions/templates/environments/prod.tfvars.hbs +46 -0
- package/plugins/specweave/templates/iac/azure-functions/templates/environments/staging.tfvars.hbs +34 -0
- package/plugins/specweave/templates/iac/azure-functions/templates/main.tf.hbs +225 -0
- package/plugins/specweave/templates/iac/azure-functions/templates/outputs.tf.hbs +89 -0
- package/plugins/specweave/templates/iac/azure-functions/templates/provider.tf.hbs +27 -0
- package/plugins/specweave/templates/iac/azure-functions/templates/providers.tf.hbs +35 -0
- package/plugins/specweave/templates/iac/azure-functions/templates/variables.tf.hbs +124 -0
- package/plugins/specweave/templates/iac/firebase/defaults.json +29 -0
- package/plugins/specweave/templates/iac/firebase/templates/README.md.hbs +35 -0
- package/plugins/specweave/templates/iac/firebase/templates/environments/dev.tfvars.hbs +7 -0
- package/plugins/specweave/templates/iac/firebase/templates/environments/prod.tfvars.hbs +7 -0
- package/plugins/specweave/templates/iac/firebase/templates/environments/staging.tfvars.hbs +7 -0
- package/plugins/specweave/templates/iac/firebase/templates/main.tf.hbs +90 -0
- package/plugins/specweave/templates/iac/firebase/templates/outputs.tf.hbs +15 -0
- package/plugins/specweave/templates/iac/firebase/templates/providers.tf.hbs +23 -0
- package/plugins/specweave/templates/iac/firebase/templates/variables.tf.hbs +42 -0
- package/plugins/specweave/templates/iac/gcp-cloud-functions/defaults.json +26 -0
- package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/README.md.hbs +299 -0
- package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/environments/dev.tfvars.hbs +36 -0
- package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/environments/prod.tfvars.hbs +48 -0
- package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/environments/staging.tfvars.hbs +41 -0
- package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/main.tf.hbs +192 -0
- package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/outputs.tf.hbs +66 -0
- package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/providers.tf.hbs +25 -0
- package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/variables.tf.hbs +119 -0
- package/plugins/specweave/templates/iac/supabase/defaults.json +15 -0
- package/plugins/specweave/templates/iac/supabase/templates/README.md.hbs +46 -0
- package/plugins/specweave/templates/iac/supabase/templates/main.tf.hbs +50 -0
- package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
- package/plugins/specweave-github/agents/github-manager/AGENT.md +39 -7
- package/plugins/specweave-github/commands/specweave-github-create-issue.md +5 -5
- package/plugins/specweave-github/lib/CodeValidator.js +195 -0
- package/plugins/specweave-github/lib/CodeValidator.ts +284 -0
- package/plugins/specweave-github/lib/ThreeLayerSyncManager.js +545 -0
- package/plugins/specweave-github/lib/ThreeLayerSyncManager.ts +809 -0
- package/plugins/specweave-github/lib/github-client-v2.js +29 -0
- package/plugins/specweave-github/lib/github-client-v2.ts +30 -0
- package/plugins/specweave-github/lib/task-sync.js +4 -0
- package/plugins/specweave-github/lib/task-sync.ts +7 -0
- package/plugins/specweave-github/lib/types.ts +38 -0
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +3222 -0
- package/src/templates/AGENTS.md.template +22 -1
- package/src/templates/CLAUDE.md.template +31 -0
- package/dist/src/core/living-docs/ThreeLayerSyncManager.d.ts +0 -116
- package/dist/src/core/living-docs/ThreeLayerSyncManager.d.ts.map +0 -1
- package/dist/src/core/living-docs/ThreeLayerSyncManager.js +0 -356
- package/dist/src/core/living-docs/ThreeLayerSyncManager.js.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: specweave:sync-specs
|
|
3
|
-
description: Sync
|
|
3
|
+
description: Sync increment specifications to living docs structure. Auto-generates feature IDs for greenfield increments (FS-XXX). Use after completing an increment to make it visible in living docs.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Sync
|
|
6
|
+
# Sync Increment Specifications to Living Docs
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Syncs increment specs to living docs structure for stakeholder visibility. Auto-generates feature IDs for greenfield increments.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -85,58 +85,30 @@ fi
|
|
|
85
85
|
|
|
86
86
|
---
|
|
87
87
|
|
|
88
|
-
## STEP 3: Execute Spec
|
|
88
|
+
## STEP 3: Execute Spec Sync
|
|
89
89
|
|
|
90
|
-
### 3.1 Run
|
|
90
|
+
### 3.1 Run Sync Command
|
|
91
91
|
|
|
92
|
-
**Execute the
|
|
92
|
+
**Execute the sync using the CLI command**:
|
|
93
93
|
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
node -e "
|
|
97
|
-
import('./dist/src/core/living-docs/spec-distributor.js').then(async ({ SpecDistributor }) => {
|
|
98
|
-
const distributor = new SpecDistributor(process.cwd());
|
|
94
|
+
```typescript
|
|
95
|
+
import { syncSpecs } from './dist/src/cli/commands/sync-specs.js';
|
|
99
96
|
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
// Parse arguments
|
|
98
|
+
const args = process.argv.slice(2); // e.g., ['0040', '--dry-run']
|
|
102
99
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
console.log('');
|
|
107
|
-
console.log('✅ Distribution successful!');
|
|
108
|
-
console.log(' 📊 Total stories: ' + result.totalStories);
|
|
109
|
-
console.log(' 📁 Total files created: ' + result.totalFiles);
|
|
110
|
-
console.log(' 🎯 Feature ID: ' + result.specId);
|
|
111
|
-
|
|
112
|
-
if (result.epicPath) {
|
|
113
|
-
console.log(' 📂 Feature path: ' + result.epicPath);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (result.userStoryPaths && result.userStoryPaths.length > 0) {
|
|
117
|
-
console.log(' 📝 User stories created:');
|
|
118
|
-
result.userStoryPaths.forEach(p => console.log(' • ' + p));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (result.warnings && result.warnings.length > 0) {
|
|
122
|
-
console.log('');
|
|
123
|
-
console.log('⚠️ Warnings:');
|
|
124
|
-
result.warnings.forEach(w => console.log(' • ' + w));
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Update acceptance criteria status based on completed tasks
|
|
128
|
-
console.log('');
|
|
129
|
-
console.log('📊 Updating acceptance criteria status...');
|
|
130
|
-
await distributor.updateAcceptanceCriteriaStatus('${INCREMENT_ID}');
|
|
131
|
-
|
|
132
|
-
} catch (error) {
|
|
133
|
-
console.error('❌ Distribution failed:', error.message);
|
|
134
|
-
process.exit(1);
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
"
|
|
100
|
+
// Execute sync
|
|
101
|
+
await syncSpecs(args);
|
|
138
102
|
```
|
|
139
103
|
|
|
104
|
+
**This will**:
|
|
105
|
+
1. Auto-generate feature ID for greenfield increments (FS-040, FS-041, etc.)
|
|
106
|
+
2. Parse spec.md for user stories and acceptance criteria
|
|
107
|
+
3. Create living docs structure:
|
|
108
|
+
- `.specweave/docs/internal/specs/_features/FS-XXX/FEATURE.md`
|
|
109
|
+
- `.specweave/docs/internal/specs/specweave/FS-XXX/README.md`
|
|
110
|
+
- `.specweave/docs/internal/specs/specweave/FS-XXX/us-*.md`
|
|
111
|
+
|
|
140
112
|
---
|
|
141
113
|
|
|
142
114
|
## STEP 4: Report Distribution Results
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
# Shows: [increment-name] ████░░░░ X/Y tasks (Z open)
|
|
7
7
|
#
|
|
8
8
|
# Logic:
|
|
9
|
-
# 1. Scan all
|
|
9
|
+
# 1. Scan all spec.md for status=active/planning (SOURCE OF TRUTH!)
|
|
10
10
|
# 2. Take first (oldest) as current increment
|
|
11
|
-
# 3. Count all active/
|
|
11
|
+
# 3. Count all active/planning as openCount
|
|
12
12
|
# 4. Parse current increment's tasks.md for progress
|
|
13
13
|
# 5. Write to cache
|
|
14
14
|
#
|
|
@@ -37,19 +37,24 @@ TMP_FILE="$PROJECT_ROOT/.specweave/state/.status-line-tmp.txt"
|
|
|
37
37
|
# Ensure state directory exists
|
|
38
38
|
mkdir -p "$PROJECT_ROOT/.specweave/state"
|
|
39
39
|
|
|
40
|
-
# Step 1: Find all open increments (active/
|
|
40
|
+
# Step 1: Find all open increments (active/planning)
|
|
41
|
+
# Read from spec.md (source of truth), not metadata.json
|
|
42
|
+
# ONLY accepts official IncrementStatus enum values
|
|
41
43
|
# Write to temp file: "timestamp increment_id"
|
|
42
44
|
> "$TMP_FILE"
|
|
43
45
|
|
|
44
46
|
if [[ -d "$INCREMENTS_DIR" ]]; then
|
|
45
|
-
for
|
|
46
|
-
if [[ -f "$
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
for spec_file in "$INCREMENTS_DIR"/*/spec.md; do
|
|
48
|
+
if [[ -f "$spec_file" ]]; then
|
|
49
|
+
# Parse YAML frontmatter for status (source of truth)
|
|
50
|
+
status=$(grep -m1 "^status:" "$spec_file" 2>/dev/null | cut -d: -f2 | tr -d ' ' || echo "")
|
|
51
|
+
|
|
52
|
+
# Check if increment is open (active, planning, or in-progress)
|
|
53
|
+
# ONLY accepts official IncrementStatus enum values
|
|
54
|
+
if [[ "$status" == "active" ]] || [[ "$status" == "planning" ]] || [[ "$status" == "in-progress" ]]; then
|
|
55
|
+
increment_id=$(basename "$(dirname "$spec_file")")
|
|
56
|
+
# Parse created date from spec.md YAML frontmatter
|
|
57
|
+
created=$(grep -m1 "^created:" "$spec_file" 2>/dev/null | cut -d: -f2- | tr -d ' ' || echo "1970-01-01")
|
|
53
58
|
|
|
54
59
|
# Write to temp file
|
|
55
60
|
echo "$created $increment_id" >> "$TMP_FILE"
|
|
@@ -85,33 +90,37 @@ COMPLETED_TASKS=0
|
|
|
85
90
|
PERCENTAGE=0
|
|
86
91
|
|
|
87
92
|
if [[ -f "$TASKS_FILE" ]]; then
|
|
88
|
-
#
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
93
|
+
# Use TaskCounter CLI for accurate counting (fixes overcounting bug)
|
|
94
|
+
COUNT_TASKS_CLI="$PROJECT_ROOT/dist/src/cli/count-tasks.js"
|
|
95
|
+
|
|
96
|
+
if [[ -f "$COUNT_TASKS_CLI" ]]; then
|
|
97
|
+
# Call CLI and parse JSON output
|
|
98
|
+
TASK_COUNTS=$(node "$COUNT_TASKS_CLI" "$TASKS_FILE" 2>/dev/null || echo '{"total":0,"completed":0,"percentage":0}')
|
|
99
|
+
TOTAL_TASKS=$(echo "$TASK_COUNTS" | jq -r '.total' 2>/dev/null || echo 0)
|
|
100
|
+
COMPLETED_TASKS=$(echo "$TASK_COUNTS" | jq -r '.completed' 2>/dev/null || echo 0)
|
|
101
|
+
PERCENTAGE=$(echo "$TASK_COUNTS" | jq -r '.percentage' 2>/dev/null || echo 0)
|
|
102
|
+
else
|
|
103
|
+
# Fallback to legacy counting if CLI not available (graceful degradation)
|
|
104
|
+
# Count total tasks (## T- or ### T- headings)
|
|
105
|
+
TOTAL_TASKS=$(grep -cE '^##+ T-' "$TASKS_FILE" 2>/dev/null || echo 0)
|
|
106
|
+
TOTAL_TASKS=$(echo "$TOTAL_TASKS" | tr -d '\n\r ' || echo 0)
|
|
107
|
+
|
|
108
|
+
# Count completed tasks - use most reliable single marker (**Completed**: format)
|
|
109
|
+
COMPLETED_TASKS=$(grep -c '\*\*Completed\*\*:' "$TASKS_FILE" 2>/dev/null || echo 0)
|
|
110
|
+
COMPLETED_TASKS=$(echo "$COMPLETED_TASKS" | tr -d '\n\r ' || echo 0)
|
|
111
|
+
|
|
112
|
+
# Calculate percentage
|
|
113
|
+
if [[ $TOTAL_TASKS -gt 0 ]]; then
|
|
114
|
+
PERCENTAGE=$((COMPLETED_TASKS * 100 / TOTAL_TASKS))
|
|
115
|
+
fi
|
|
110
116
|
fi
|
|
111
117
|
fi
|
|
112
118
|
|
|
113
|
-
# Step 5: Extract increment
|
|
114
|
-
|
|
119
|
+
# Step 5: Extract increment ID and name
|
|
120
|
+
# Format: XXXX-name where XXXX is 4-digit prefix (brackets added by manager)
|
|
121
|
+
INCREMENT_ID=$(echo "$CURRENT_INCREMENT" | grep -oE '^[0-9]{4}')
|
|
122
|
+
INCREMENT_NAME_ONLY=$(echo "$CURRENT_INCREMENT" | sed 's/^[0-9]\{4\}-//')
|
|
123
|
+
INCREMENT_NAME="$INCREMENT_ID-$INCREMENT_NAME_ONLY"
|
|
115
124
|
|
|
116
125
|
# Step 6: Write cache
|
|
117
126
|
jq -n \
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# validate-spec-status.sh
|
|
4
|
+
#
|
|
5
|
+
# Validates that spec.md status values match IncrementStatus enum.
|
|
6
|
+
# Prevents vocabulary drift and ensures status line hook compatibility.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# bash validate-spec-status.sh <increment-id>
|
|
10
|
+
# bash validate-spec-status.sh --all # Validate all increments
|
|
11
|
+
#
|
|
12
|
+
# Exit codes:
|
|
13
|
+
# 0 = Valid
|
|
14
|
+
# 1 = Invalid status found
|
|
15
|
+
# 2 = Error (file not found, etc.)
|
|
16
|
+
#
|
|
17
|
+
# Requires: bash 4.0+ (for associative arrays)
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
# Find project root
|
|
22
|
+
find_project_root() {
|
|
23
|
+
local dir="$PWD"
|
|
24
|
+
while [[ "$dir" != "/" ]]; do
|
|
25
|
+
if [[ -d "$dir/.specweave" ]]; then
|
|
26
|
+
echo "$dir"
|
|
27
|
+
return 0
|
|
28
|
+
fi
|
|
29
|
+
dir=$(dirname "$dir")
|
|
30
|
+
done
|
|
31
|
+
echo "$PWD"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
PROJECT_ROOT=$(find_project_root)
|
|
35
|
+
INCREMENTS_DIR="$PROJECT_ROOT/.specweave/increments"
|
|
36
|
+
|
|
37
|
+
# Valid IncrementStatus enum values (from src/core/types/increment-metadata.ts)
|
|
38
|
+
VALID_STATUSES=("planning" "active" "backlog" "paused" "completed" "abandoned")
|
|
39
|
+
|
|
40
|
+
# Get suggested correction for invalid status
|
|
41
|
+
get_correction() {
|
|
42
|
+
local status="$1"
|
|
43
|
+
case "$status" in
|
|
44
|
+
"planned") echo "planning" ;;
|
|
45
|
+
"in-progress"|"in_progress") echo "active" ;;
|
|
46
|
+
"done") echo "completed" ;;
|
|
47
|
+
"cancelled"|"canceled") echo "abandoned" ;;
|
|
48
|
+
*) echo "" ;;
|
|
49
|
+
esac
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Validate a single spec file
|
|
53
|
+
validate_spec() {
|
|
54
|
+
local spec_file="$1"
|
|
55
|
+
local increment_id=$(basename "$(dirname "$spec_file")")
|
|
56
|
+
|
|
57
|
+
if [[ ! -f "$spec_file" ]]; then
|
|
58
|
+
echo "❌ Error: spec.md not found for increment $increment_id"
|
|
59
|
+
return 2
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# Extract status from YAML frontmatter
|
|
63
|
+
local status=$(grep -m1 "^status:" "$spec_file" 2>/dev/null | cut -d: -f2 | tr -d ' "' || echo "")
|
|
64
|
+
|
|
65
|
+
if [[ -z "$status" ]]; then
|
|
66
|
+
echo "⚠️ Warning: No status field in $increment_id/spec.md"
|
|
67
|
+
return 0 # Not an error, just a warning
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# Check if status is valid
|
|
71
|
+
local is_valid=0
|
|
72
|
+
for valid_status in "${VALID_STATUSES[@]}"; do
|
|
73
|
+
if [[ "$status" == "$valid_status" ]]; then
|
|
74
|
+
is_valid=1
|
|
75
|
+
break
|
|
76
|
+
fi
|
|
77
|
+
done
|
|
78
|
+
|
|
79
|
+
if [[ $is_valid -eq 1 ]]; then
|
|
80
|
+
echo "✅ $increment_id: status '$status' is valid"
|
|
81
|
+
return 0
|
|
82
|
+
else
|
|
83
|
+
# Invalid status found - suggest correction
|
|
84
|
+
echo ""
|
|
85
|
+
echo "❌ Invalid status in $increment_id/spec.md"
|
|
86
|
+
echo " Found: '$status'"
|
|
87
|
+
echo ""
|
|
88
|
+
|
|
89
|
+
# Check if we have a suggested correction
|
|
90
|
+
local correction=$(get_correction "$status")
|
|
91
|
+
if [[ -n "$correction" ]]; then
|
|
92
|
+
echo " 💡 Did you mean: '$correction'?"
|
|
93
|
+
echo ""
|
|
94
|
+
echo " Fix:"
|
|
95
|
+
echo " sed -i '' 's/^status: $status/status: $correction/' $spec_file"
|
|
96
|
+
else
|
|
97
|
+
echo " Valid statuses: ${VALID_STATUSES[*]}"
|
|
98
|
+
fi
|
|
99
|
+
echo ""
|
|
100
|
+
|
|
101
|
+
return 1
|
|
102
|
+
fi
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# Validate all increments
|
|
106
|
+
validate_all() {
|
|
107
|
+
local exit_code=0
|
|
108
|
+
local total=0
|
|
109
|
+
local valid=0
|
|
110
|
+
local invalid=0
|
|
111
|
+
|
|
112
|
+
echo "Validating all increments in $INCREMENTS_DIR"
|
|
113
|
+
echo ""
|
|
114
|
+
|
|
115
|
+
for spec_file in "$INCREMENTS_DIR"/*/spec.md; do
|
|
116
|
+
if [[ -f "$spec_file" ]]; then
|
|
117
|
+
total=$((total + 1))
|
|
118
|
+
if validate_spec "$spec_file"; then
|
|
119
|
+
valid=$((valid + 1))
|
|
120
|
+
else
|
|
121
|
+
invalid=$((invalid + 1))
|
|
122
|
+
exit_code=1
|
|
123
|
+
fi
|
|
124
|
+
fi
|
|
125
|
+
done
|
|
126
|
+
|
|
127
|
+
echo ""
|
|
128
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
129
|
+
echo "Summary: $total increments checked"
|
|
130
|
+
echo " ✅ Valid: $valid"
|
|
131
|
+
echo " ❌ Invalid: $invalid"
|
|
132
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
133
|
+
|
|
134
|
+
if [[ $invalid -gt 0 ]]; then
|
|
135
|
+
echo ""
|
|
136
|
+
echo "⚠️ Please fix invalid status values to use official IncrementStatus enum."
|
|
137
|
+
echo ""
|
|
138
|
+
echo "Valid statuses:"
|
|
139
|
+
for status in "${VALID_STATUSES[@]}"; do
|
|
140
|
+
echo " - $status"
|
|
141
|
+
done
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
return $exit_code
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Main logic
|
|
148
|
+
if [[ $# -eq 0 ]]; then
|
|
149
|
+
echo "Usage: $0 <increment-id> | --all"
|
|
150
|
+
echo ""
|
|
151
|
+
echo "Examples:"
|
|
152
|
+
echo " $0 0042-test-infrastructure-cleanup"
|
|
153
|
+
echo " $0 --all"
|
|
154
|
+
exit 2
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
if [[ "$1" == "--all" ]]; then
|
|
158
|
+
validate_all
|
|
159
|
+
else
|
|
160
|
+
INCREMENT_ID="$1"
|
|
161
|
+
SPEC_FILE="$INCREMENTS_DIR/$INCREMENT_ID/spec.md"
|
|
162
|
+
validate_spec "$SPEC_FILE"
|
|
163
|
+
fi
|
|
@@ -303,44 +303,26 @@ if [[ -d ".specweave/increments" ]]; then
|
|
|
303
303
|
done)
|
|
304
304
|
|
|
305
305
|
if [[ -n "$ACTIVE_INCREMENT" ]]; then
|
|
306
|
-
#
|
|
307
|
-
|
|
308
|
-
if [[ -f "$
|
|
309
|
-
#
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if [[ -n "$STATUS_LINE" ]]; then
|
|
323
|
-
CONTEXT="✓ $STATUS_LINE"
|
|
306
|
+
# Simple status: parse tasks.md for completion
|
|
307
|
+
TASKS_FILE=".specweave/increments/$ACTIVE_INCREMENT/tasks.md"
|
|
308
|
+
if [[ -f "$TASKS_FILE" ]]; then
|
|
309
|
+
# Count tasks (headers with T-NNN format - both ### and ####)
|
|
310
|
+
TOTAL_TASKS=$(grep -cE '^#{3,4}\s*T-[0-9]' "$TASKS_FILE" 2>/dev/null | tr -d '\n' || echo "0")
|
|
311
|
+
# Count completed (various formats)
|
|
312
|
+
COMPLETED_TASKS=$(grep -cE '(✅ COMPLETE|\[COMPLETED\]|\[x\] Completed)' "$TASKS_FILE" 2>/dev/null | tr -d '\n' || echo "0")
|
|
313
|
+
|
|
314
|
+
# Ensure valid numbers
|
|
315
|
+
TOTAL_TASKS=${TOTAL_TASKS:-0}
|
|
316
|
+
COMPLETED_TASKS=${COMPLETED_TASKS:-0}
|
|
317
|
+
|
|
318
|
+
if [[ "$TOTAL_TASKS" -gt 0 ]] 2>/dev/null; then
|
|
319
|
+
PERCENTAGE=$(( COMPLETED_TASKS * 100 / TOTAL_TASKS ))
|
|
320
|
+
CONTEXT="✓ Active: $ACTIVE_INCREMENT ($COMPLETED_TASKS/$TOTAL_TASKS tasks, $PERCENTAGE%)"
|
|
324
321
|
else
|
|
325
|
-
|
|
326
|
-
TASKS_FILE=".specweave/increments/$ACTIVE_INCREMENT/tasks.md"
|
|
327
|
-
if [[ -f "$TASKS_FILE" ]]; then
|
|
328
|
-
TASK_STATS=$(grep -E "^\s*-\s*\[[ x]\]" "$TASKS_FILE" 2>/dev/null | wc -l | xargs || echo "0")
|
|
329
|
-
COMPLETED_TASKS=$(grep -E "^\s*-\s*\[x\]" "$TASKS_FILE" 2>/dev/null | wc -l | xargs || echo "0")
|
|
330
|
-
|
|
331
|
-
if [[ "$TASK_STATS" -gt 0 ]]; then
|
|
332
|
-
PERCENTAGE=$(( COMPLETED_TASKS * 100 / TASK_STATS ))
|
|
333
|
-
CONTEXT="✓ Active Increment: $ACTIVE_INCREMENT ($PERCENTAGE% complete, $COMPLETED_TASKS/$TASK_STATS tasks)"
|
|
334
|
-
else
|
|
335
|
-
CONTEXT="✓ Active Increment: $ACTIVE_INCREMENT"
|
|
336
|
-
fi
|
|
337
|
-
else
|
|
338
|
-
CONTEXT="✓ Active Increment: $ACTIVE_INCREMENT"
|
|
339
|
-
fi
|
|
322
|
+
CONTEXT="✓ Active: $ACTIVE_INCREMENT"
|
|
340
323
|
fi
|
|
341
324
|
else
|
|
342
|
-
|
|
343
|
-
CONTEXT="✓ Active Increment: $ACTIVE_INCREMENT"
|
|
325
|
+
CONTEXT="✓ Active: $ACTIVE_INCREMENT"
|
|
344
326
|
fi
|
|
345
327
|
fi
|
|
346
328
|
fi
|
|
@@ -20,13 +20,22 @@ async function updateTasksMd(incrementId) {
|
|
|
20
20
|
console.log(`\u{1F4D6} Read tasks.md (${lines.length} lines)`);
|
|
21
21
|
const tasks = parseTaskStatus(lines);
|
|
22
22
|
console.log(`\u{1F4CB} Found ${tasks.length} tasks`);
|
|
23
|
-
const completedTasks = detectCompletedTasks(lines);
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const { completedTasks, fixes } = detectCompletedTasks(lines);
|
|
24
|
+
let updatedContent = originalContent;
|
|
25
|
+
let autoFixedCount = 0;
|
|
26
|
+
if (fixes.length > 0) {
|
|
27
|
+
console.log(`\u{1F527} Auto-fixing ${fixes.length} task consistency issue(s)...`);
|
|
28
|
+
updatedContent = applyConsistencyFixes(originalContent, fixes);
|
|
29
|
+
autoFixedCount = fixes.length;
|
|
30
|
+
console.log("\u2705 Task consistency auto-fixed");
|
|
31
|
+
}
|
|
32
|
+
if (completedTasks.length === 0 && fixes.length === 0) {
|
|
33
|
+
console.log("\u2705 No new task completions or consistency fixes needed");
|
|
26
34
|
return;
|
|
27
35
|
}
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
if (completedTasks.length > 0) {
|
|
37
|
+
console.log(`\u{1F3AF} Detected ${completedTasks.length} completed task(s):`, completedTasks);
|
|
38
|
+
}
|
|
30
39
|
for (const taskId of completedTasks) {
|
|
31
40
|
updatedContent = markTaskComplete(updatedContent, taskId);
|
|
32
41
|
}
|
|
@@ -38,6 +47,9 @@ async function updateTasksMd(incrementId) {
|
|
|
38
47
|
updatedContent = updateProgressHeader(updatedContent, completedCount, totalTasks, progress);
|
|
39
48
|
await fs.writeFile(tasksPath, updatedContent, "utf-8");
|
|
40
49
|
console.log(`\u2705 Updated ${tasksPath}`);
|
|
50
|
+
if (autoFixedCount > 0) {
|
|
51
|
+
console.log(`\u{1F527} Auto-fixed ${autoFixedCount} consistency issue(s)`);
|
|
52
|
+
}
|
|
41
53
|
console.log(` Completed: ${completedCount}/${totalTasks}`);
|
|
42
54
|
console.log(` Progress: ${progress}%
|
|
43
55
|
`);
|
|
@@ -78,6 +90,7 @@ function parseTaskStatus(lines) {
|
|
|
78
90
|
}
|
|
79
91
|
function detectCompletedTasks(lines) {
|
|
80
92
|
const completedTasks = [];
|
|
93
|
+
const fixes = [];
|
|
81
94
|
const warnings = [];
|
|
82
95
|
const taskPattern = /^###\s+(T-\d+[-A-Z]*):?\s+(.+)/;
|
|
83
96
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -99,17 +112,33 @@ function detectCompletedTasks(lines) {
|
|
|
99
112
|
completedTasks.push(taskId);
|
|
100
113
|
}
|
|
101
114
|
} else {
|
|
115
|
+
fixes.push({
|
|
116
|
+
taskId,
|
|
117
|
+
lineNumber: i,
|
|
118
|
+
action: "remove-complete-marker",
|
|
119
|
+
currentLine: line
|
|
120
|
+
});
|
|
102
121
|
warnings.push(`${taskId}: Header has \u2705 COMPLETE but not all checkboxes checked`);
|
|
103
122
|
}
|
|
104
123
|
continue;
|
|
105
124
|
}
|
|
106
125
|
if (hasCompleteMarker && !implementationSection) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
126
|
+
fixes.push({
|
|
127
|
+
taskId,
|
|
128
|
+
lineNumber: i,
|
|
129
|
+
action: "remove-complete-marker",
|
|
130
|
+
currentLine: line
|
|
131
|
+
});
|
|
132
|
+
warnings.push(`${taskId}: Header has \u2705 COMPLETE but no implementation section to verify`);
|
|
110
133
|
continue;
|
|
111
134
|
}
|
|
112
135
|
if (!hasCompleteMarker && implementationSection && allCheckboxesComplete) {
|
|
136
|
+
fixes.push({
|
|
137
|
+
taskId,
|
|
138
|
+
lineNumber: i,
|
|
139
|
+
action: "add-complete-marker",
|
|
140
|
+
currentLine: line
|
|
141
|
+
});
|
|
113
142
|
warnings.push(`${taskId}: All checkboxes checked but header missing \u2705 COMPLETE`);
|
|
114
143
|
if (!completedTasks.includes(taskId)) {
|
|
115
144
|
completedTasks.push(taskId);
|
|
@@ -131,7 +160,21 @@ function detectCompletedTasks(lines) {
|
|
|
131
160
|
warnings.forEach((w) => console.warn(` ${w}`));
|
|
132
161
|
console.warn("");
|
|
133
162
|
}
|
|
134
|
-
return completedTasks;
|
|
163
|
+
return { completedTasks, fixes };
|
|
164
|
+
}
|
|
165
|
+
function applyConsistencyFixes(content, fixes) {
|
|
166
|
+
const lines = content.split("\n");
|
|
167
|
+
for (const fix of fixes.reverse()) {
|
|
168
|
+
const line = lines[fix.lineNumber];
|
|
169
|
+
if (fix.action === "remove-complete-marker") {
|
|
170
|
+
const fixed = line.replace(/\s*✅\s*COMPLETE\s*/g, "");
|
|
171
|
+
lines[fix.lineNumber] = fixed;
|
|
172
|
+
} else if (fix.action === "add-complete-marker") {
|
|
173
|
+
const fixed = line.trim() + " \u2705 COMPLETE";
|
|
174
|
+
lines[fix.lineNumber] = fixed;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return lines.join("\n");
|
|
135
178
|
}
|
|
136
179
|
function findNextTaskStart(lines, startIndex) {
|
|
137
180
|
const taskPattern = /^###\s+T-\d+/;
|