kspec 1.0.21 → 1.0.22
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/package.json +1 -1
- package/src/index.js +97 -3
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -411,6 +411,34 @@ function getCurrentTask(folder) {
|
|
|
411
411
|
return null;
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
+
// Check if spec.md has been modified after spec-lite.md
|
|
415
|
+
function isSpecStale(folder) {
|
|
416
|
+
const specFile = path.join(folder, 'spec.md');
|
|
417
|
+
const specLiteFile = path.join(folder, 'spec-lite.md');
|
|
418
|
+
|
|
419
|
+
if (!fs.existsSync(specFile)) return false;
|
|
420
|
+
if (!fs.existsSync(specLiteFile)) return true; // No spec-lite means stale
|
|
421
|
+
|
|
422
|
+
const specMtime = fs.statSync(specFile).mtime;
|
|
423
|
+
const specLiteMtime = fs.statSync(specLiteFile).mtime;
|
|
424
|
+
|
|
425
|
+
return specMtime > specLiteMtime;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Check staleness and prompt user before proceeding
|
|
429
|
+
async function checkStaleness(folder) {
|
|
430
|
+
if (!isSpecStale(folder)) return true; // Not stale, proceed
|
|
431
|
+
|
|
432
|
+
console.log('\n⚠️ spec.md has been modified since spec-lite.md was generated.');
|
|
433
|
+
console.log(' This may cause outdated information to be used.\n');
|
|
434
|
+
console.log(' Options:');
|
|
435
|
+
console.log(' 1. Run `kspec refresh` to update spec-lite.md first (recommended)');
|
|
436
|
+
console.log(' 2. Continue anyway with potentially stale context\n');
|
|
437
|
+
|
|
438
|
+
const proceed = await confirm('Continue with potentially stale spec?');
|
|
439
|
+
return proceed;
|
|
440
|
+
}
|
|
441
|
+
|
|
414
442
|
function refreshContext() {
|
|
415
443
|
const contextFile = path.join(KSPEC_DIR, 'CONTEXT.md');
|
|
416
444
|
const current = getCurrentSpec();
|
|
@@ -429,6 +457,7 @@ No active spec. Run: \`kspec spec "Feature Name"\`
|
|
|
429
457
|
const specName = path.basename(current);
|
|
430
458
|
const stats = getTaskStats(current);
|
|
431
459
|
const currentTask = getCurrentTask(current);
|
|
460
|
+
const stale = isSpecStale(current);
|
|
432
461
|
|
|
433
462
|
// Read spec-lite if exists
|
|
434
463
|
const specLiteFile = path.join(current, 'spec-lite.md');
|
|
@@ -460,8 +489,17 @@ No active spec. Run: \`kspec spec "Feature Name"\`
|
|
|
460
489
|
## Current Spec
|
|
461
490
|
**${specName}**
|
|
462
491
|
Path: \`${current}\`
|
|
492
|
+
`;
|
|
493
|
+
|
|
494
|
+
if (stale) {
|
|
495
|
+
content += `
|
|
496
|
+
**WARNING: spec.md has been modified since spec-lite.md was generated.**
|
|
497
|
+
Run \`kspec refresh\` to update spec-lite.md with latest changes.
|
|
463
498
|
|
|
464
499
|
`;
|
|
500
|
+
} else {
|
|
501
|
+
content += '\n';
|
|
502
|
+
}
|
|
465
503
|
|
|
466
504
|
if (stats) {
|
|
467
505
|
content += `## Progress
|
|
@@ -1077,6 +1115,9 @@ Report created subtasks with their URLs.`, 'kspec-jira');
|
|
|
1077
1115
|
|
|
1078
1116
|
async tasks(args) {
|
|
1079
1117
|
const folder = getOrSelectSpec(args.join(' '));
|
|
1118
|
+
|
|
1119
|
+
if (!await checkStaleness(folder)) return;
|
|
1120
|
+
|
|
1080
1121
|
log(`Generating tasks: ${folder}`);
|
|
1081
1122
|
|
|
1082
1123
|
await chat(`Generate tasks from specification.
|
|
@@ -1093,8 +1134,11 @@ Create ${folder}/tasks.md with:
|
|
|
1093
1134
|
|
|
1094
1135
|
async 'verify-tasks'(args) {
|
|
1095
1136
|
const folder = getOrSelectSpec(args.join(' '));
|
|
1137
|
+
|
|
1138
|
+
if (!await checkStaleness(folder)) return;
|
|
1139
|
+
|
|
1096
1140
|
const stats = getTaskStats(folder);
|
|
1097
|
-
|
|
1141
|
+
|
|
1098
1142
|
log(`Verifying tasks: ${folder}`);
|
|
1099
1143
|
if (stats) log(`Progress: ${stats.done}/${stats.total} tasks completed`);
|
|
1100
1144
|
|
|
@@ -1110,6 +1154,9 @@ Report: X/Y tasks done, gaps found, coverage assessment.`, 'kspec-verify');
|
|
|
1110
1154
|
|
|
1111
1155
|
async build(args) {
|
|
1112
1156
|
const folder = getOrSelectSpec(args.join(' '));
|
|
1157
|
+
|
|
1158
|
+
if (!await checkStaleness(folder)) return;
|
|
1159
|
+
|
|
1113
1160
|
const stats = getTaskStats(folder);
|
|
1114
1161
|
|
|
1115
1162
|
log(`Building: ${folder}`);
|
|
@@ -1144,6 +1191,9 @@ NEVER delete .kiro or .kspec folders.`, 'kspec-build');
|
|
|
1144
1191
|
|
|
1145
1192
|
async verify(args) {
|
|
1146
1193
|
const folder = getOrSelectSpec(args.join(' '));
|
|
1194
|
+
|
|
1195
|
+
if (!await checkStaleness(folder)) return;
|
|
1196
|
+
|
|
1147
1197
|
const stats = getTaskStats(folder);
|
|
1148
1198
|
|
|
1149
1199
|
log(`Verifying implementation: ${folder}`);
|
|
@@ -1159,11 +1209,54 @@ NEVER delete .kiro or .kspec folders.`, 'kspec-build');
|
|
|
1159
1209
|
|
|
1160
1210
|
Report:
|
|
1161
1211
|
- Requirements: X/Y implemented
|
|
1162
|
-
- Tasks: X/Y completed
|
|
1212
|
+
- Tasks: X/Y completed
|
|
1163
1213
|
- Tests: PASS/FAIL
|
|
1164
1214
|
- Gaps: [list any]`, 'kspec-verify');
|
|
1165
1215
|
},
|
|
1166
1216
|
|
|
1217
|
+
async refresh(args) {
|
|
1218
|
+
const folder = getOrSelectSpec(args.join(' '));
|
|
1219
|
+
const specFile = path.join(folder, 'spec.md');
|
|
1220
|
+
const specLiteFile = path.join(folder, 'spec-lite.md');
|
|
1221
|
+
|
|
1222
|
+
if (!fs.existsSync(specFile)) {
|
|
1223
|
+
die(`No spec.md found in ${folder}`);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
const stale = isSpecStale(folder);
|
|
1227
|
+
if (!stale && !args.includes('--force')) {
|
|
1228
|
+
log('spec-lite.md is up to date with spec.md');
|
|
1229
|
+
log('Use --force to regenerate anyway');
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
log(`Refreshing spec-lite.md from ${folder}/spec.md...`);
|
|
1234
|
+
|
|
1235
|
+
await chat(`Regenerate spec-lite.md from the updated spec.md.
|
|
1236
|
+
|
|
1237
|
+
Spec folder: ${folder}
|
|
1238
|
+
|
|
1239
|
+
WORKFLOW:
|
|
1240
|
+
1. Read ${specFile} carefully - this is the source of truth
|
|
1241
|
+
2. Create a NEW ${specLiteFile} that:
|
|
1242
|
+
- Summarizes ALL key requirements (under 500 words)
|
|
1243
|
+
- Captures the current tech stack and versions
|
|
1244
|
+
- Includes critical constraints and acceptance criteria
|
|
1245
|
+
- Preserves any Jira issue references
|
|
1246
|
+
3. This spec-lite.md will be used for context restoration after AI compression
|
|
1247
|
+
|
|
1248
|
+
IMPORTANT:
|
|
1249
|
+
- Read the FULL spec.md before generating spec-lite.md
|
|
1250
|
+
- Ensure ALL tech stack details are captured (versions matter!)
|
|
1251
|
+
- This replaces the old spec-lite.md completely
|
|
1252
|
+
|
|
1253
|
+
After updating, confirm what changed.`, 'kspec-spec');
|
|
1254
|
+
|
|
1255
|
+
// Update CONTEXT.md with new spec-lite
|
|
1256
|
+
refreshContext();
|
|
1257
|
+
log('Context refreshed with updated spec');
|
|
1258
|
+
},
|
|
1259
|
+
|
|
1167
1260
|
async done(args) {
|
|
1168
1261
|
const folder = getOrSelectSpec(args.join(' '));
|
|
1169
1262
|
const stats = getTaskStats(folder);
|
|
@@ -1347,6 +1440,7 @@ Jira Integration (requires Atlassian MCP):
|
|
|
1347
1440
|
Create subtasks under specific issue
|
|
1348
1441
|
|
|
1349
1442
|
Other:
|
|
1443
|
+
kspec refresh Regenerate spec-lite.md after editing spec.md
|
|
1350
1444
|
kspec context Refresh/view context file
|
|
1351
1445
|
kspec review [target] Code review
|
|
1352
1446
|
kspec list List all specs
|
|
@@ -1389,4 +1483,4 @@ async function run(args) {
|
|
|
1389
1483
|
}
|
|
1390
1484
|
}
|
|
1391
1485
|
|
|
1392
|
-
module.exports = { run, commands, loadConfig, detectCli, requireCli, agentTemplates, getTaskStats, refreshContext, getCurrentSpec, getCurrentTask, checkForUpdates, compareVersions, hasAtlassianMcp, getMcpConfig, slugify, generateSlug };
|
|
1486
|
+
module.exports = { run, commands, loadConfig, detectCli, requireCli, agentTemplates, getTaskStats, refreshContext, getCurrentSpec, getCurrentTask, checkForUpdates, compareVersions, hasAtlassianMcp, getMcpConfig, slugify, generateSlug, isSpecStale };
|