lee-spec-kit 0.7.7 → 0.7.9
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/dist/index.js +149 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/en/common/README.md +21 -5
- package/templates/en/common/agents/agents.md +14 -12
- package/templates/en/common/agents/skills/create-issue.md +14 -6
- package/templates/en/common/agents/skills/create-pr.md +15 -6
- package/templates/en/common/agents/skills/execute-task.md +4 -3
- package/templates/en/common/features/feature-base/tasks.md +9 -5
- package/templates/ko/common/README.md +21 -5
- package/templates/ko/common/agents/agents.md +14 -12
- package/templates/ko/common/agents/skills/create-issue.md +14 -6
- package/templates/ko/common/agents/skills/create-pr.md +15 -6
- package/templates/ko/common/agents/skills/execute-task.md +4 -3
- package/templates/ko/common/features/feature-base/tasks.md +9 -5
package/dist/index.js
CHANGED
|
@@ -8738,6 +8738,7 @@ var DEFAULT_MANAGED_DOC_DIRS = [
|
|
|
8738
8738
|
"scripts"
|
|
8739
8739
|
];
|
|
8740
8740
|
var DEFAULT_MANAGED_DOC_FILES = [
|
|
8741
|
+
"AGENTS.md",
|
|
8741
8742
|
"README.md",
|
|
8742
8743
|
".lee-spec-kit.json",
|
|
8743
8744
|
".gitignore"
|
|
@@ -17874,8 +17875,105 @@ function parseTaskLine(line, index = -1) {
|
|
|
17874
17875
|
title: match[4]
|
|
17875
17876
|
};
|
|
17876
17877
|
}
|
|
17878
|
+
function countLeadingSpaces(line) {
|
|
17879
|
+
const match = line.match(/^(\s*)/);
|
|
17880
|
+
return match?.[1]?.length ?? 0;
|
|
17881
|
+
}
|
|
17882
|
+
function findTaskBlockEnd(lines, taskLineIndex) {
|
|
17883
|
+
let endIndex = lines.length;
|
|
17884
|
+
for (let index = taskLineIndex + 1; index < lines.length; index++) {
|
|
17885
|
+
if (parseTaskLine(lines[index]) || /^\s*##\s+/.test(lines[index])) {
|
|
17886
|
+
endIndex = index;
|
|
17887
|
+
break;
|
|
17888
|
+
}
|
|
17889
|
+
}
|
|
17890
|
+
return endIndex;
|
|
17891
|
+
}
|
|
17892
|
+
function isPlaceholderTaskItem(text) {
|
|
17893
|
+
const normalized = text.trim();
|
|
17894
|
+
return normalized === "" || normalized === "-" || /^todo$/i.test(normalized);
|
|
17895
|
+
}
|
|
17896
|
+
function parseTaskSectionItems(lines, taskLineIndex, headingPattern) {
|
|
17897
|
+
if (taskLineIndex < 0 || taskLineIndex >= lines.length) return void 0;
|
|
17898
|
+
const endIndex = findTaskBlockEnd(lines, taskLineIndex);
|
|
17899
|
+
let sectionHeaderIndex = -1;
|
|
17900
|
+
let sectionHeaderIndent = 0;
|
|
17901
|
+
for (let index = taskLineIndex + 1; index < endIndex; index++) {
|
|
17902
|
+
if (headingPattern.test(lines[index])) {
|
|
17903
|
+
sectionHeaderIndex = index;
|
|
17904
|
+
sectionHeaderIndent = countLeadingSpaces(lines[index]);
|
|
17905
|
+
break;
|
|
17906
|
+
}
|
|
17907
|
+
}
|
|
17908
|
+
if (sectionHeaderIndex === -1) return void 0;
|
|
17909
|
+
const items = [];
|
|
17910
|
+
let placeholderCount = 0;
|
|
17911
|
+
for (let index = sectionHeaderIndex + 1; index < endIndex; index++) {
|
|
17912
|
+
const line = lines[index];
|
|
17913
|
+
if (!line.trim()) continue;
|
|
17914
|
+
const indent = countLeadingSpaces(line);
|
|
17915
|
+
if (indent <= sectionHeaderIndent && /^\s*-\s+/.test(line)) {
|
|
17916
|
+
break;
|
|
17917
|
+
}
|
|
17918
|
+
const match = line.match(/^\s*-\s+(.+?)\s*$/);
|
|
17919
|
+
if (!match) continue;
|
|
17920
|
+
const text = match[1].trim();
|
|
17921
|
+
items.push(text);
|
|
17922
|
+
if (isPlaceholderTaskItem(text)) placeholderCount++;
|
|
17923
|
+
}
|
|
17924
|
+
if (items.length === 0) return void 0;
|
|
17925
|
+
return { items, placeholderCount };
|
|
17926
|
+
}
|
|
17927
|
+
function parseTaskAcceptance(lines, taskLineIndex) {
|
|
17928
|
+
return parseTaskSectionItems(lines, taskLineIndex, /^\s*-\s+Acceptance:\s*$/);
|
|
17929
|
+
}
|
|
17930
|
+
function parseTaskChecklist(lines, taskLineIndex) {
|
|
17931
|
+
if (taskLineIndex < 0 || taskLineIndex >= lines.length) return void 0;
|
|
17932
|
+
const endIndex = findTaskBlockEnd(lines, taskLineIndex);
|
|
17933
|
+
let checklistHeaderIndex = -1;
|
|
17934
|
+
let checklistHeaderIndent = 0;
|
|
17935
|
+
for (let index = taskLineIndex + 1; index < endIndex; index++) {
|
|
17936
|
+
if (/^\s*-\s+Checklist:\s*$/.test(lines[index])) {
|
|
17937
|
+
checklistHeaderIndex = index;
|
|
17938
|
+
checklistHeaderIndent = countLeadingSpaces(lines[index]);
|
|
17939
|
+
break;
|
|
17940
|
+
}
|
|
17941
|
+
}
|
|
17942
|
+
if (checklistHeaderIndex === -1) return void 0;
|
|
17943
|
+
let total = 0;
|
|
17944
|
+
let checked = 0;
|
|
17945
|
+
let placeholderCount = 0;
|
|
17946
|
+
for (let index = checklistHeaderIndex + 1; index < endIndex; index++) {
|
|
17947
|
+
const line = lines[index];
|
|
17948
|
+
if (!line.trim()) continue;
|
|
17949
|
+
const indent = countLeadingSpaces(line);
|
|
17950
|
+
if (indent <= checklistHeaderIndent && /^\s*-\s+/.test(line)) {
|
|
17951
|
+
break;
|
|
17952
|
+
}
|
|
17953
|
+
const match = line.match(/^\s*-\s*\[([ xX])\]\s+/);
|
|
17954
|
+
if (!match) continue;
|
|
17955
|
+
total++;
|
|
17956
|
+
if (match[1].toLowerCase() === "x") checked++;
|
|
17957
|
+
const text = line.replace(/^\s*-\s*\[[ xX]\]\s+/, "").trim();
|
|
17958
|
+
if (isPlaceholderTaskItem(text)) placeholderCount++;
|
|
17959
|
+
}
|
|
17960
|
+
if (total === 0) return void 0;
|
|
17961
|
+
return { total, checked, unchecked: total - checked, placeholderCount };
|
|
17962
|
+
}
|
|
17877
17963
|
|
|
17878
17964
|
// src/commands/task-run.ts
|
|
17965
|
+
function ensureTaskDetailsReady(lines, task) {
|
|
17966
|
+
const acceptance = parseTaskAcceptance(lines, task.index);
|
|
17967
|
+
const checklist = parseTaskChecklist(lines, task.index);
|
|
17968
|
+
const acceptanceHasPlaceholder = !acceptance || acceptance.items.length === 0 || acceptance.placeholderCount > 0;
|
|
17969
|
+
const checklistHasPlaceholder = !checklist || checklist.total === 0 || checklist.placeholderCount > 0;
|
|
17970
|
+
if (acceptanceHasPlaceholder || checklistHasPlaceholder) {
|
|
17971
|
+
throw createCliError(
|
|
17972
|
+
"PRECONDITION_FAILED",
|
|
17973
|
+
`Task "${task.taskId}" still contains Acceptance/Checklist placeholder content. Fill concrete Acceptance items and Checklist items before running task-run.`
|
|
17974
|
+
);
|
|
17975
|
+
}
|
|
17976
|
+
}
|
|
17879
17977
|
function buildTaskRunPrompt(input) {
|
|
17880
17978
|
const shared = [
|
|
17881
17979
|
"Read `spec.md`, `plan.md`, and `tasks.md` before editing code.",
|
|
@@ -17883,8 +17981,8 @@ function buildTaskRunPrompt(input) {
|
|
|
17883
17981
|
"Use additional helper agents only when parallel analysis is clearly worth the extra slot cost.",
|
|
17884
17982
|
"Keep one writer for overlapping files; do not let multiple sub-agents edit the same files concurrently.",
|
|
17885
17983
|
"If helper-agent quota is exhausted, continue the task in the main agent instead of blocking progress.",
|
|
17886
|
-
"Update the assigned task status and verification notes in `tasks.md` before leaving this task.",
|
|
17887
|
-
"Mark the task `DONE` only after code changes and verification are complete."
|
|
17984
|
+
"Update the assigned task status, task-local checklist boxes, and verification notes in `tasks.md` before leaving this task.",
|
|
17985
|
+
"Mark the task `DONE` only after code changes and verification are complete. `task-complete` will reject the transition if checklist items remain unchecked."
|
|
17888
17986
|
];
|
|
17889
17987
|
if (input.lang === "ko") {
|
|
17890
17988
|
return [
|
|
@@ -17972,6 +18070,7 @@ async function runTaskRun(featureName, options) {
|
|
|
17972
18070
|
`Task "${requestedTaskId}" is already DONE.`
|
|
17973
18071
|
);
|
|
17974
18072
|
}
|
|
18073
|
+
ensureTaskDetailsReady(lines, resolvedTask);
|
|
17975
18074
|
const mode = resolvedTask.status === "TODO" ? "start" : "continue";
|
|
17976
18075
|
let tasksUpdated = false;
|
|
17977
18076
|
if (resolvedTask.status === "TODO") {
|
|
@@ -18116,6 +18215,13 @@ async function runTaskComplete(featureName, options) {
|
|
|
18116
18215
|
`Task "${requestedTaskId}" must be DOING/REVIEW before marking it DONE.`
|
|
18117
18216
|
);
|
|
18118
18217
|
}
|
|
18218
|
+
const checklist = parseTaskChecklist(lines, resolvedTask.index);
|
|
18219
|
+
if (checklist && checklist.unchecked > 0) {
|
|
18220
|
+
throw createCliError(
|
|
18221
|
+
"PRECONDITION_FAILED",
|
|
18222
|
+
`Task "${requestedTaskId}" still has unchecked checklist items (${checklist.checked}/${checklist.total}).`
|
|
18223
|
+
);
|
|
18224
|
+
}
|
|
18119
18225
|
lines[resolvedTask.index] = setTaskStatus2(resolvedTask, "DONE");
|
|
18120
18226
|
await fs.writeFile(tasksPath, lines.join("\n"), "utf-8");
|
|
18121
18227
|
const payload = {
|
|
@@ -18174,6 +18280,21 @@ function taskCompleteCommand(program2) {
|
|
|
18174
18280
|
}
|
|
18175
18281
|
);
|
|
18176
18282
|
}
|
|
18283
|
+
function collectRepeatableOption(value, previous = []) {
|
|
18284
|
+
return [...previous, value];
|
|
18285
|
+
}
|
|
18286
|
+
function normalizeTaskDetailItems(values, flagName) {
|
|
18287
|
+
const normalized = (values || []).map((value) => value.trim()).filter(Boolean);
|
|
18288
|
+
for (const value of normalized) {
|
|
18289
|
+
if (value === "-" || /^todo$/i.test(value)) {
|
|
18290
|
+
throw createCliError(
|
|
18291
|
+
"INVALID_ARGUMENT",
|
|
18292
|
+
`${flagName} must contain concrete text, not placeholder values like "-" or "TODO".`
|
|
18293
|
+
);
|
|
18294
|
+
}
|
|
18295
|
+
}
|
|
18296
|
+
return normalized;
|
|
18297
|
+
}
|
|
18177
18298
|
function escapeRegExp8(value) {
|
|
18178
18299
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18179
18300
|
}
|
|
@@ -18239,9 +18360,9 @@ function formatTaskBlock(input) {
|
|
|
18239
18360
|
`- [TODO][${input.ref}] ${input.taskId} ${input.title}`,
|
|
18240
18361
|
` - Date: ${input.recordedAt}`,
|
|
18241
18362
|
" - Acceptance:",
|
|
18242
|
-
" - -",
|
|
18363
|
+
...input.acceptanceItems.length > 0 ? input.acceptanceItems.map((item) => ` - ${item}`) : [" - -"],
|
|
18243
18364
|
" - Checklist:",
|
|
18244
|
-
" - [ ] -"
|
|
18365
|
+
...input.checklistItems.length > 0 ? input.checklistItems.map((item) => ` - [ ] ${item}`) : [" - [ ] -"]
|
|
18245
18366
|
];
|
|
18246
18367
|
}
|
|
18247
18368
|
async function resolveTaskFeature(featureName, component) {
|
|
@@ -18272,6 +18393,11 @@ async function runTaskAdd(featureName, options) {
|
|
|
18272
18393
|
if (!title) {
|
|
18273
18394
|
throw createCliError("INVALID_ARGUMENT", "`--title` must not be empty.");
|
|
18274
18395
|
}
|
|
18396
|
+
const acceptanceItems = normalizeTaskDetailItems(
|
|
18397
|
+
options.acceptance,
|
|
18398
|
+
"--acceptance"
|
|
18399
|
+
);
|
|
18400
|
+
const checklistItems = normalizeTaskDetailItems(options.check, "--check");
|
|
18275
18401
|
const ref = normalizeTaskRef(options.ref);
|
|
18276
18402
|
if (isPrdRequirementId(ref)) {
|
|
18277
18403
|
const { definitions } = await scanPrdRequirements(ctx.fs, ctx.config.docsDir);
|
|
@@ -18311,7 +18437,14 @@ async function runTaskAdd(featureName, options) {
|
|
|
18311
18437
|
nextSectionHeadingIndex
|
|
18312
18438
|
);
|
|
18313
18439
|
const recordedAt = getLocalDateString();
|
|
18314
|
-
const blockLines = formatTaskBlock({
|
|
18440
|
+
const blockLines = formatTaskBlock({
|
|
18441
|
+
ref,
|
|
18442
|
+
taskId,
|
|
18443
|
+
title,
|
|
18444
|
+
recordedAt,
|
|
18445
|
+
acceptanceItems,
|
|
18446
|
+
checklistItems
|
|
18447
|
+
});
|
|
18315
18448
|
const shouldPrefixBlank = insertIndex > taskListHeadingIndex + 1 && (lines[insertIndex - 1] || "").trim() !== "";
|
|
18316
18449
|
const shouldSuffixBlank = insertIndex < lines.length && (lines[insertIndex] || "").trim() !== "";
|
|
18317
18450
|
const insertLines = [
|
|
@@ -18342,7 +18475,17 @@ async function runTaskAdd(featureName, options) {
|
|
|
18342
18475
|
}
|
|
18343
18476
|
function taskCommand(program2) {
|
|
18344
18477
|
const task = program2.command("task").description("Manage tasks");
|
|
18345
|
-
task.command("add [feature-name]").description("Append a new task to the end of Task List").requiredOption("--title <title>", "Task title").requiredOption("--ref <ref>", "Requirement ref: NON-PRD or PRD-FR-001").option(
|
|
18478
|
+
task.command("add [feature-name]").description("Append a new task to the end of Task List").requiredOption("--title <title>", "Task title").requiredOption("--ref <ref>", "Requirement ref: NON-PRD or PRD-FR-001").option(
|
|
18479
|
+
"--acceptance <text>",
|
|
18480
|
+
"Acceptance item. Repeat to add more than one.",
|
|
18481
|
+
collectRepeatableOption,
|
|
18482
|
+
[]
|
|
18483
|
+
).option(
|
|
18484
|
+
"--check <text>",
|
|
18485
|
+
"Checklist item. Repeat to add more than one.",
|
|
18486
|
+
collectRepeatableOption,
|
|
18487
|
+
[]
|
|
18488
|
+
).option("--component <component>", "Component name for multi projects").option("--json", "Output JSON").action(async (featureName, options) => {
|
|
18346
18489
|
try {
|
|
18347
18490
|
await runTaskAdd(featureName, options);
|
|
18348
18491
|
} catch (error) {
|