specweave 0.16.1 → 0.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/specweave.js +5 -1
- package/package.json +1 -1
- package/plugins/specweave/hooks/post-increment-planning.sh +50 -5
- package/plugins/specweave/skills/increment-planner/SKILL.md +12 -5
- package/plugins/specweave/skills/increment-planner/scripts/feature-utils.js +26 -5
- package/src/templates/CLAUDE.md.template +8 -9
package/bin/specweave.js
CHANGED
|
@@ -182,8 +182,12 @@ program
|
|
|
182
182
|
.description('Validate increment discipline compliance (WIP limits, hard cap)')
|
|
183
183
|
.option('-v, --verbose', 'Show detailed increment information')
|
|
184
184
|
.option('--json', 'Output results as JSON')
|
|
185
|
-
.option('--project-root <path>', 'Project root directory'
|
|
185
|
+
.option('--project-root <path>', 'Project root directory')
|
|
186
186
|
.action(async (options) => {
|
|
187
|
+
// Set default project-root at runtime, not module load time
|
|
188
|
+
if (!options.projectRoot) {
|
|
189
|
+
options.projectRoot = process.cwd();
|
|
190
|
+
}
|
|
187
191
|
const { checkDisciplineCommand } = await import('../dist/cli/commands/check-discipline.js');
|
|
188
192
|
await checkDisciplineCommand(options);
|
|
189
193
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specweave",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.3",
|
|
4
4
|
"description": "Spec-driven development framework for Claude Code. AI-native workflow with living documentation, intelligent agents, and multilingual support (9 languages). Enterprise-grade traceability with permanent specs and temporary increments.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -347,16 +347,52 @@ create_github_issue() {
|
|
|
347
347
|
}
|
|
348
348
|
' "$tasks_file")
|
|
349
349
|
|
|
350
|
-
# Detect repository from
|
|
351
|
-
local repo
|
|
350
|
+
# Detect repository from profile-based config
|
|
351
|
+
local repo=""
|
|
352
|
+
local owner=""
|
|
353
|
+
local repo_name=""
|
|
352
354
|
|
|
355
|
+
# First, check if increment has a specific githubProfile in metadata
|
|
356
|
+
local metadata_file="$2/metadata.json"
|
|
357
|
+
local profile_id=""
|
|
358
|
+
|
|
359
|
+
if [ -f "$metadata_file" ]; then
|
|
360
|
+
profile_id=$(cat "$metadata_file" 2>/dev/null | grep -o '"githubProfile"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"\([^"]*\)".*/\1/')
|
|
361
|
+
log_debug "Found githubProfile in metadata: $profile_id"
|
|
362
|
+
fi
|
|
363
|
+
|
|
364
|
+
# If no profile in metadata, use activeProfile from config
|
|
365
|
+
if [ -z "$profile_id" ]; then
|
|
366
|
+
profile_id=$(cat "$CONFIG_FILE" 2>/dev/null | grep -o '"activeProfile"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"\([^"]*\)".*/\1/')
|
|
367
|
+
log_debug "Using activeProfile from config: $profile_id"
|
|
368
|
+
fi
|
|
369
|
+
|
|
370
|
+
if [ -n "$profile_id" ]; then
|
|
371
|
+
# Extract owner and repo from the profile
|
|
372
|
+
local profile_section=$(cat "$CONFIG_FILE" 2>/dev/null | awk "/$profile_id/,/^[[:space:]]*\}/{print}")
|
|
373
|
+
owner=$(echo "$profile_section" | grep -o '"owner"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"\([^"]*\)".*/\1/')
|
|
374
|
+
repo_name=$(echo "$profile_section" | grep -o '"repo"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"\([^"]*\)".*/\1/')
|
|
375
|
+
|
|
376
|
+
if [ -n "$owner" ] && [ -n "$repo_name" ]; then
|
|
377
|
+
repo="$owner/$repo_name"
|
|
378
|
+
log_debug "Using repo from profile: $repo"
|
|
379
|
+
fi
|
|
380
|
+
fi
|
|
381
|
+
|
|
382
|
+
# Fallback to git remote detection if no profile config found
|
|
383
|
+
if [ -z "$repo" ]; then
|
|
384
|
+
repo=$(git remote get-url origin 2>/dev/null | sed 's/.*github\.com[:/]\(.*\)\.git/\1/' | sed 's/.*github\.com[:/]\(.*\)/\1/')
|
|
385
|
+
log_debug "Fallback to git remote: $repo"
|
|
386
|
+
fi
|
|
387
|
+
|
|
388
|
+
# Legacy fallback to old config format
|
|
353
389
|
if [ -z "$repo" ]; then
|
|
354
|
-
# Fallback to config
|
|
355
390
|
repo=$(cat "$CONFIG_FILE" 2>/dev/null | grep -A 5 '"sync"' | grep -o '"repo"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"\([^"]*\)".*/\1/')
|
|
391
|
+
log_debug "Legacy fallback to old config: $repo"
|
|
356
392
|
fi
|
|
357
393
|
|
|
358
394
|
if [ -z "$repo" ]; then
|
|
359
|
-
log_error "Could not detect GitHub repository"
|
|
395
|
+
log_error "Could not detect GitHub repository from profile or git remote"
|
|
360
396
|
return 1
|
|
361
397
|
fi
|
|
362
398
|
|
|
@@ -444,7 +480,15 @@ EOF
|
|
|
444
480
|
# Update existing metadata.json with github section
|
|
445
481
|
if command -v jq >/dev/null 2>&1; then
|
|
446
482
|
local temp_metadata=$(mktemp)
|
|
447
|
-
|
|
483
|
+
local jq_update=". + {\"github\": {\"issue\": $issue_number, \"url\": \"$issue_url\", \"synced\": \"$current_timestamp\"}"
|
|
484
|
+
|
|
485
|
+
# Add profile ID if we have it
|
|
486
|
+
if [ -n "$profile_id" ]; then
|
|
487
|
+
jq_update="$jq_update, \"githubProfile\": \"$profile_id\""
|
|
488
|
+
jq_update=". + {\"github\": {\"issue\": $issue_number, \"url\": \"$issue_url\", \"synced\": \"$current_timestamp\"}, \"githubProfile\": \"$profile_id\"}"
|
|
489
|
+
fi
|
|
490
|
+
|
|
491
|
+
jq "$jq_update" "$metadata_file" > "$temp_metadata"
|
|
448
492
|
mv "$temp_metadata" "$metadata_file"
|
|
449
493
|
else
|
|
450
494
|
# Fallback: manual JSON construction (less reliable)
|
|
@@ -455,6 +499,7 @@ EOF
|
|
|
455
499
|
"status": "active",
|
|
456
500
|
"type": "feature",
|
|
457
501
|
"created": "$current_timestamp",
|
|
502
|
+
"githubProfile": "$profile_id",
|
|
458
503
|
"github": {
|
|
459
504
|
"issue": $issue_number,
|
|
460
505
|
"url": "$issue_url",
|
|
@@ -149,12 +149,19 @@ User: "I want to build real-time price tracking"
|
|
|
149
149
|
↓
|
|
150
150
|
increment-planner skill
|
|
151
151
|
↓
|
|
152
|
-
STEP 1:
|
|
152
|
+
STEP 1: Determine increment number and check for duplicates
|
|
153
|
+
├─ Use the Bash tool to run: node plugins/specweave/skills/increment-planner/scripts/feature-utils.js next
|
|
154
|
+
├─ Get next available increment number (e.g., "0021")
|
|
155
|
+
├─ Get short name from user description
|
|
156
|
+
├─ Check if increment already exists using: node plugins/specweave/skills/increment-planner/scripts/feature-utils.js check-increment {number}
|
|
157
|
+
└─ If duplicate found, STOP and tell user: "Increment {number} already exists! Please use the existing increment."
|
|
158
|
+
↓
|
|
159
|
+
STEP 2: Scan existing docs
|
|
153
160
|
├─ Read .specweave/docs/internal/strategy/ (existing requirements)
|
|
154
161
|
├─ Read .specweave/docs/internal/architecture/adr/ (existing decisions)
|
|
155
162
|
└─ Pass existing context to agents
|
|
156
163
|
↓
|
|
157
|
-
STEP
|
|
164
|
+
STEP 3: Invoke PM Agent (🚨 MANDATORY - USE TASK TOOL)
|
|
158
165
|
|
|
159
166
|
YOU MUST USE THE TASK TOOL - DO NOT SKIP:
|
|
160
167
|
|
|
@@ -206,7 +213,7 @@ Task(
|
|
|
206
213
|
|
|
207
214
|
Wait for PM agent to complete!
|
|
208
215
|
↓
|
|
209
|
-
STEP
|
|
216
|
+
STEP 4: Invoke Architect Agent (🚨 MANDATORY - USE TASK TOOL)
|
|
210
217
|
|
|
211
218
|
YOU MUST USE THE TASK TOOL - DO NOT SKIP:
|
|
212
219
|
|
|
@@ -239,7 +246,7 @@ Task(
|
|
|
239
246
|
|
|
240
247
|
Wait for Architect agent to complete!
|
|
241
248
|
↓
|
|
242
|
-
STEP
|
|
249
|
+
STEP 5: Invoke Test-Aware Planner Agent (🚨 MANDATORY - USE TASK TOOL)
|
|
243
250
|
|
|
244
251
|
YOU MUST USE THE TASK TOOL - DO NOT SKIP:
|
|
245
252
|
|
|
@@ -275,7 +282,7 @@ Task(
|
|
|
275
282
|
|
|
276
283
|
Wait for test-aware-planner agent to complete!
|
|
277
284
|
↓
|
|
278
|
-
STEP
|
|
285
|
+
STEP 6: Validate Living Docs and Increment Files
|
|
279
286
|
├─ Check .specweave/docs/internal/specs/spec-{number}-{name}/spec.md exists (SOURCE OF TRUTH)
|
|
280
287
|
├─ Check living spec.md contains ALL user stories, requirements, AC-IDs (with AC-IDs)
|
|
281
288
|
├─ Check .specweave/docs/internal/architecture/adr/ has ≥3 ADRs
|
|
@@ -3,8 +3,12 @@
|
|
|
3
3
|
* Supports increment-planner skill with auto-numbering and name generation
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
8
12
|
|
|
9
13
|
/**
|
|
10
14
|
* Stop words to filter from feature descriptions
|
|
@@ -198,7 +202,7 @@ function parseFeatureDescription(description) {
|
|
|
198
202
|
};
|
|
199
203
|
}
|
|
200
204
|
|
|
201
|
-
|
|
205
|
+
export {
|
|
202
206
|
generateShortName,
|
|
203
207
|
getNextFeatureNumber,
|
|
204
208
|
featureExists,
|
|
@@ -210,8 +214,8 @@ module.exports = {
|
|
|
210
214
|
STOP_WORDS
|
|
211
215
|
};
|
|
212
216
|
|
|
213
|
-
// CLI usage
|
|
214
|
-
if (
|
|
217
|
+
// CLI usage - check if this file is being run directly
|
|
218
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
215
219
|
const args = process.argv.slice(2);
|
|
216
220
|
|
|
217
221
|
if (args.length === 0) {
|
|
@@ -219,6 +223,7 @@ if (require.main === module) {
|
|
|
219
223
|
console.log(' node feature-utils.js shortname "feature description"');
|
|
220
224
|
console.log(' node feature-utils.js next [features-dir]');
|
|
221
225
|
console.log(' node feature-utils.js parse "feature description"');
|
|
226
|
+
console.log(' node feature-utils.js check-increment <number> [features-dir]');
|
|
222
227
|
process.exit(0);
|
|
223
228
|
}
|
|
224
229
|
|
|
@@ -243,6 +248,22 @@ if (require.main === module) {
|
|
|
243
248
|
}
|
|
244
249
|
break;
|
|
245
250
|
|
|
251
|
+
case 'check-increment':
|
|
252
|
+
if (args[1]) {
|
|
253
|
+
const incrementNumber = args[1];
|
|
254
|
+
const checkDir = args[2] || '.specweave/increments';
|
|
255
|
+
if (incrementNumberExists(incrementNumber, checkDir)) {
|
|
256
|
+
console.error(`ERROR: Increment ${incrementNumber} already exists!`);
|
|
257
|
+
process.exit(1);
|
|
258
|
+
} else {
|
|
259
|
+
console.log(`OK: Increment ${incrementNumber} is available`);
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
console.error('Error: Increment number required');
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
break;
|
|
266
|
+
|
|
246
267
|
default:
|
|
247
268
|
console.error(`Unknown command: ${command}`);
|
|
248
269
|
process.exit(1);
|
|
@@ -208,11 +208,10 @@ Both approaches work perfectly - use whichever feels more natural!
|
|
|
208
208
|
### Increment Structure
|
|
209
209
|
|
|
210
210
|
```
|
|
211
|
-
.specweave/increments/0001-user-auth/
|
|
211
|
+
.specweave/increments/0001-user-auth/ # ⚠️ ID must be unique!
|
|
212
212
|
├── spec.md # WHAT & WHY
|
|
213
213
|
├── plan.md # HOW
|
|
214
|
-
├── tasks.md # Implementation steps
|
|
215
|
-
├── tests.md # Test strategy
|
|
214
|
+
├── tasks.md # Implementation steps with embedded tests
|
|
216
215
|
├── context-manifest.yaml # Selective context loading
|
|
217
216
|
├── logs/ # ✅ Execution logs, errors, AI sessions
|
|
218
217
|
├── scripts/ # ✅ Helper scripts, migrations, setup
|
|
@@ -278,12 +277,11 @@ Config: Auto-detected from project files
|
|
|
278
277
|
│ │ │ ├── operations/ # Runbooks, monitoring
|
|
279
278
|
│ │ │ └── governance/ # Security, compliance
|
|
280
279
|
│ │ └── public/ # Published docs
|
|
281
|
-
│ ├── increments/ # Features (auto-numbered)
|
|
282
|
-
│ │ └── 0001-feature-name/
|
|
283
|
-
│ │ ├── spec.md
|
|
284
|
-
│ │ ├── plan.md
|
|
285
|
-
│ │ ├── tasks.md
|
|
286
|
-
│ │ ├── tests.md
|
|
280
|
+
│ ├── increments/ # Features (auto-numbered, UNIQUE IDs)
|
|
281
|
+
│ │ └── 0001-feature-name/ # ⚠️ Each ID must be unique (0001-9999)
|
|
282
|
+
│ │ ├── spec.md # What we're building
|
|
283
|
+
│ │ ├── plan.md # How we'll build it
|
|
284
|
+
│ │ ├── tasks.md # Tasks with embedded tests
|
|
287
285
|
│ │ ├── logs/ # ✅ Put logs here
|
|
288
286
|
│ │ ├── scripts/ # ✅ Put scripts here
|
|
289
287
|
│ │ └── reports/ # ✅ Put reports here
|
|
@@ -436,6 +434,7 @@ Plugins are detected and suggested during `specweave init` based on:
|
|
|
436
434
|
4. **Validated**: Every increment validated before closure
|
|
437
435
|
5. **Traceable**: All work traces back to specs and requirements
|
|
438
436
|
6. **Clean Organization**: All supporting files in increment folders, never root
|
|
437
|
+
7. **No Duplicate Increments**: Each increment must have a unique 4-digit ID (0001-9999)
|
|
439
438
|
|
|
440
439
|
---
|
|
441
440
|
|