lee-spec-kit 0.6.5 → 0.6.6
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 +65 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,8 +7,8 @@ import prompts from 'prompts';
|
|
|
7
7
|
import chalk6 from 'chalk';
|
|
8
8
|
import { glob } from 'glob';
|
|
9
9
|
import { spawn, execSync, spawnSync, execFileSync } from 'child_process';
|
|
10
|
-
import { createHash } from 'crypto';
|
|
11
10
|
import os from 'os';
|
|
11
|
+
import { createHash } from 'crypto';
|
|
12
12
|
|
|
13
13
|
var getFilename = () => fileURLToPath(import.meta.url);
|
|
14
14
|
var getDirname = () => path18.dirname(getFilename());
|
|
@@ -396,8 +396,8 @@ var I18N = {
|
|
|
396
396
|
issueCreateAndWrite: "`npx lee-spec-kit docs get create-issue --json`\uC73C\uB85C \uC808\uCC28\uB97C \uD655\uC778\uD55C \uB4A4, `npx lee-spec-kit github issue {featureRef} --json`\uC73C\uB85C \uCD08\uC548\uC744 \uC0DD\uC131\uD558\uC138\uC694. \uBAA9\uD45C/\uC644\uB8CC \uAE30\uC900\uC744 \uAC80\uD1A0\xB7\uBCF4\uC644\uD558\uACE0 \uC0AC\uC6A9\uC790 \uC2B9\uC778(OK) \uD6C4 `--create --confirm OK`\uB85C \uC0DD\uC131\uD55C \uB2E4\uC74C, spec.md/tasks.md\uC758 \uC774\uC288 \uBC88\uD638\uB97C \uCC44\uC6B0\uACE0 \uBB38\uC11C \uCEE4\uBC0B\uC744 \uC900\uBE44\uD558\uC138\uC694.",
|
|
397
397
|
docsCommitIssueUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(#{issueNumber}): {folderName} \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8"',
|
|
398
398
|
docsCommitUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs: {folderName} \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8"',
|
|
399
|
-
projectCommitIssueUpdate: 'cd "{projectGitCwd}" && git add
|
|
400
|
-
projectCommitUpdate: 'cd "{projectGitCwd}" && git add
|
|
399
|
+
projectCommitIssueUpdate: 'cd "{projectGitCwd}" && (git diff --cached --quiet && echo "\uC2A4\uD14C\uC774\uC9D5\uB41C \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC774\uBC88 \uD0DC\uC2A4\uD06C\uC5D0\uC11C \uC218\uC815\uD55C \uD30C\uC77C\uB9CC \uC120\uD0DD\uD574 git add [files] \uD6C4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694." && exit 1 || git commit -m "feat(#{issueNumber}): {commitTopic}")',
|
|
400
|
+
projectCommitUpdate: 'cd "{projectGitCwd}" && (git diff --cached --quiet && echo "\uC2A4\uD14C\uC774\uC9D5\uB41C \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC774\uBC88 \uD0DC\uC2A4\uD06C\uC5D0\uC11C \uC218\uC815\uD55C \uD30C\uC77C\uB9CC \uC120\uD0DD\uD574 git add [files] \uD6C4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694." && exit 1 || git commit -m "feat({folderName}): {commitTopic}")',
|
|
401
401
|
standaloneNeedsProjectRoot: "standalone \uBAA8\uB4DC\uC5D0\uC11C\uB294 projectRoot \uC124\uC815\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. (npx lee-spec-kit config --project-root ...)",
|
|
402
402
|
createBranch: 'cd "{projectGitCwd}" && git checkout -b feat/{issueNumber}-{slug}',
|
|
403
403
|
tasksAllDoneButNoChecklist: '\uBAA8\uB4E0 \uD0DC\uC2A4\uD06C\uAC00 DONE\uC774\uC9C0\uB9CC \uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 \uC139\uC158\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. tasks.md\uC758 "\uC644\uB8CC \uC870\uAC74" \uC139\uC158\uC744 \uCD94\uAC00/\uD655\uC778\uD558\uC138\uC694.',
|
|
@@ -772,8 +772,8 @@ var I18N = {
|
|
|
772
772
|
issueCreateAndWrite: "Review procedure with `npx lee-spec-kit docs get create-issue --json`, then generate a draft via `npx lee-spec-kit github issue {featureRef} --json`. Refine goals/completion criteria, get explicit user OK, run `--create --confirm OK`, then update issue number in spec.md/tasks.md and prepare a docs commit.",
|
|
773
773
|
docsCommitIssueUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(#{issueNumber}): {folderName} docs update"',
|
|
774
774
|
docsCommitUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs: {folderName} docs update"',
|
|
775
|
-
projectCommitIssueUpdate: 'cd "{projectGitCwd}" && git add
|
|
776
|
-
projectCommitUpdate: 'cd "{projectGitCwd}" && git add
|
|
775
|
+
projectCommitIssueUpdate: 'cd "{projectGitCwd}" && (git diff --cached --quiet && echo "No staged files. Stage only files changed in this task with git add [files], then run again." && exit 1 || git commit -m "feat(#{issueNumber}): {commitTopic}")',
|
|
776
|
+
projectCommitUpdate: 'cd "{projectGitCwd}" && (git diff --cached --quiet && echo "No staged files. Stage only files changed in this task with git add [files], then run again." && exit 1 || git commit -m "feat({folderName}): {commitTopic}")',
|
|
777
777
|
standaloneNeedsProjectRoot: "Standalone mode requires projectRoot. (npx lee-spec-kit config --project-root ...)",
|
|
778
778
|
createBranch: 'cd "{projectGitCwd}" && git checkout -b feat/{issueNumber}-{slug}',
|
|
779
779
|
tasksAllDoneButNoChecklist: 'All tasks are DONE, but no completion checklist section was found. Add/verify the "Completion Criteria" section in tasks.md.',
|
|
@@ -1211,6 +1211,27 @@ function getInitLockPath(targetDir) {
|
|
|
1211
1211
|
`.lee-spec-kit.${path18.basename(targetDir)}.lock`
|
|
1212
1212
|
);
|
|
1213
1213
|
}
|
|
1214
|
+
function getTempProjectLockPath(cwd) {
|
|
1215
|
+
const key = createHash("sha1").update(path18.resolve(cwd)).digest("hex");
|
|
1216
|
+
return path18.join(os.tmpdir(), "lee-spec-kit-locks", `${key}.project.lock`);
|
|
1217
|
+
}
|
|
1218
|
+
function getProjectExecutionLockPath(cwd) {
|
|
1219
|
+
try {
|
|
1220
|
+
const out = execFileSync(
|
|
1221
|
+
"git",
|
|
1222
|
+
["rev-parse", "--git-path", "lee-spec-kit.project.lock"],
|
|
1223
|
+
{
|
|
1224
|
+
cwd,
|
|
1225
|
+
encoding: "utf-8",
|
|
1226
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
1227
|
+
}
|
|
1228
|
+
).trim();
|
|
1229
|
+
if (!out) return getTempProjectLockPath(cwd);
|
|
1230
|
+
return path18.isAbsolute(out) ? out : path18.resolve(cwd, out);
|
|
1231
|
+
} catch {
|
|
1232
|
+
return getTempProjectLockPath(cwd);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1214
1235
|
async function isStaleLock(lockPath, staleMs) {
|
|
1215
1236
|
try {
|
|
1216
1237
|
const stat = await fs14.stat(lockPath);
|
|
@@ -2473,6 +2494,21 @@ function formatSkillList(skills) {
|
|
|
2473
2494
|
function getFindingsPolicyText(lang, blockOnFindings) {
|
|
2474
2495
|
return blockOnFindings ? tr(lang, "messages", "prePrReviewFindingsBlock") : tr(lang, "messages", "prePrReviewFindingsWarn");
|
|
2475
2496
|
}
|
|
2497
|
+
function normalizeCommitTopicText(value) {
|
|
2498
|
+
return value.replace(/\s+/g, " ").trim();
|
|
2499
|
+
}
|
|
2500
|
+
function toShellSafeCommitTopic(value) {
|
|
2501
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\$/g, "\\$").replace(/`/g, "\\`");
|
|
2502
|
+
}
|
|
2503
|
+
function resolveProjectCommitTopic(feature) {
|
|
2504
|
+
const raw = feature.activeTask?.title || feature.lastDoneTask?.title || feature.nextTodoTask?.title || feature.folderName;
|
|
2505
|
+
const withoutTaskId = normalizeCommitTopicText(raw).replace(
|
|
2506
|
+
/^T-[A-Za-z0-9-]+\s+/,
|
|
2507
|
+
""
|
|
2508
|
+
);
|
|
2509
|
+
const topic = withoutTaskId || normalizeCommitTopicText(feature.folderName);
|
|
2510
|
+
return toShellSafeCommitTopic(topic);
|
|
2511
|
+
}
|
|
2476
2512
|
function getStepDefinitions(lang, workflow) {
|
|
2477
2513
|
const workflowPolicy = resolveWorkflowPolicy(workflow);
|
|
2478
2514
|
const prePrReviewPolicy = resolvePrePrReviewPolicy(workflow);
|
|
@@ -2796,10 +2832,12 @@ function getStepDefinitions(lang, workflow) {
|
|
|
2796
2832
|
cmd: f.issueNumber ? tr(lang, "messages", "projectCommitIssueUpdate", {
|
|
2797
2833
|
projectGitCwd: f.git.projectGitCwd,
|
|
2798
2834
|
issueNumber: f.issueNumber,
|
|
2799
|
-
folderName: f.folderName
|
|
2835
|
+
folderName: f.folderName,
|
|
2836
|
+
commitTopic: resolveProjectCommitTopic(f)
|
|
2800
2837
|
}) : tr(lang, "messages", "projectCommitUpdate", {
|
|
2801
2838
|
projectGitCwd: f.git.projectGitCwd,
|
|
2802
|
-
folderName: f.folderName
|
|
2839
|
+
folderName: f.folderName,
|
|
2840
|
+
commitTopic: resolveProjectCommitTopic(f)
|
|
2803
2841
|
})
|
|
2804
2842
|
}
|
|
2805
2843
|
];
|
|
@@ -2880,10 +2918,12 @@ function getStepDefinitions(lang, workflow) {
|
|
|
2880
2918
|
cmd: f.issueNumber ? tr(lang, "messages", "projectCommitIssueUpdate", {
|
|
2881
2919
|
projectGitCwd: f.git.projectGitCwd,
|
|
2882
2920
|
issueNumber: f.issueNumber,
|
|
2883
|
-
folderName: f.folderName
|
|
2921
|
+
folderName: f.folderName,
|
|
2922
|
+
commitTopic: resolveProjectCommitTopic(f)
|
|
2884
2923
|
}) : tr(lang, "messages", "projectCommitUpdate", {
|
|
2885
2924
|
projectGitCwd: f.git.projectGitCwd,
|
|
2886
|
-
folderName: f.folderName
|
|
2925
|
+
folderName: f.folderName,
|
|
2926
|
+
commitTopic: resolveProjectCommitTopic(f)
|
|
2887
2927
|
})
|
|
2888
2928
|
}
|
|
2889
2929
|
];
|
|
@@ -3399,6 +3439,7 @@ async function resolveComponentStatusPaths(projectGitCwd, component, workflow) {
|
|
|
3399
3439
|
function parseTasks(content) {
|
|
3400
3440
|
const summary = { total: 0, todo: 0, doing: 0, done: 0 };
|
|
3401
3441
|
let activeTask;
|
|
3442
|
+
let lastDoneTask;
|
|
3402
3443
|
let nextTodoTask;
|
|
3403
3444
|
const lines = content.split("\n");
|
|
3404
3445
|
let inCodeBlock = false;
|
|
@@ -3419,11 +3460,14 @@ function parseTasks(content) {
|
|
|
3419
3460
|
if (!activeTask && (status === "DOING" || status === "REVIEW")) {
|
|
3420
3461
|
activeTask = { status, title };
|
|
3421
3462
|
}
|
|
3463
|
+
if (status === "DONE") {
|
|
3464
|
+
lastDoneTask = { status: "DONE", title };
|
|
3465
|
+
}
|
|
3422
3466
|
if (!nextTodoTask && status === "TODO") {
|
|
3423
3467
|
nextTodoTask = { status: "TODO", title };
|
|
3424
3468
|
}
|
|
3425
3469
|
}
|
|
3426
|
-
return { summary, activeTask, nextTodoTask };
|
|
3470
|
+
return { summary, activeTask, lastDoneTask, nextTodoTask };
|
|
3427
3471
|
}
|
|
3428
3472
|
function parseCompletionChecklist(content) {
|
|
3429
3473
|
const lines = content.split("\n");
|
|
@@ -3480,6 +3524,7 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
3480
3524
|
const tasksExists = await fs14.pathExists(tasksPath);
|
|
3481
3525
|
const tasksSummary = { total: 0, todo: 0, doing: 0, done: 0 };
|
|
3482
3526
|
let activeTask;
|
|
3527
|
+
let lastDoneTask;
|
|
3483
3528
|
let nextTodoTask;
|
|
3484
3529
|
let tasksDocStatus;
|
|
3485
3530
|
let tasksDocStatusFieldExists = false;
|
|
@@ -3492,12 +3537,18 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
3492
3537
|
let prePrReviewFieldExists = false;
|
|
3493
3538
|
if (tasksExists) {
|
|
3494
3539
|
const content = await fs14.readFile(tasksPath, "utf-8");
|
|
3495
|
-
const {
|
|
3540
|
+
const {
|
|
3541
|
+
summary,
|
|
3542
|
+
activeTask: active,
|
|
3543
|
+
lastDoneTask: lastDone,
|
|
3544
|
+
nextTodoTask: nextTodo
|
|
3545
|
+
} = parseTasks(content);
|
|
3496
3546
|
tasksSummary.total = summary.total;
|
|
3497
3547
|
tasksSummary.todo = summary.todo;
|
|
3498
3548
|
tasksSummary.doing = summary.doing;
|
|
3499
3549
|
tasksSummary.done = summary.done;
|
|
3500
3550
|
activeTask = active;
|
|
3551
|
+
lastDoneTask = lastDone;
|
|
3501
3552
|
nextTodoTask = nextTodo;
|
|
3502
3553
|
completionChecklist = parseCompletionChecklist(content);
|
|
3503
3554
|
if (!issueNumber) {
|
|
@@ -3671,6 +3722,7 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
3671
3722
|
tasksDocStatus,
|
|
3672
3723
|
tasks: tasksSummary,
|
|
3673
3724
|
activeTask,
|
|
3725
|
+
lastDoneTask,
|
|
3674
3726
|
nextTodoTask,
|
|
3675
3727
|
completionChecklist,
|
|
3676
3728
|
prePrReview: {
|
|
@@ -4936,7 +4988,7 @@ function getCommandExecutionLockPath(action, config) {
|
|
|
4936
4988
|
if (action.scope === "docs") {
|
|
4937
4989
|
return getDocsLockPath(config.docsDir);
|
|
4938
4990
|
}
|
|
4939
|
-
return
|
|
4991
|
+
return getProjectExecutionLockPath(action.cwd);
|
|
4940
4992
|
}
|
|
4941
4993
|
function contextCommand(program2) {
|
|
4942
4994
|
program2.command("context [feature-name]").description("Show current feature context and next actions").option("--json", "Output in JSON format for agents").option("--repo <repo>", "Component name for multi projects").option("--component <component>", "Component name for multi projects").option("--all", "Include completed features when auto-detecting").option("--done", "Show completed (workflow-done) features only").option("--approve <reply>", "Approve one labeled option: A or A OK").option("--execute", "Execute approved option when it is a command").option(
|
|
@@ -5330,7 +5382,7 @@ async function runContext(featureName, options) {
|
|
|
5330
5382
|
if (f.issueNumber) {
|
|
5331
5383
|
console.log(` \u2022 Issue: #${f.issueNumber}`);
|
|
5332
5384
|
}
|
|
5333
|
-
console.log(` \u2022 Path: ${
|
|
5385
|
+
console.log(` \u2022 Path: ${path.relative(cwd, f.path)}`);
|
|
5334
5386
|
if (f.git.projectBranch) {
|
|
5335
5387
|
console.log(` \u2022 Project Branch: ${f.git.projectBranch}`);
|
|
5336
5388
|
}
|