lee-spec-kit 0.6.42 → 0.6.43

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 CHANGED
@@ -497,7 +497,8 @@ var koContext = {
497
497
  "context.actionDetail.issueCreateFromDoc": "Ready \uC0C1\uD0DC issue.md\uB85C \uC774\uC288\uB97C \uC0DD\uC131\uD558\uACE0 \uBC88\uD638\uB97C \uB3D9\uAE30\uD654\uD558\uC138\uC694",
498
498
  "context.actionDetail.taskExecute": "\uD604\uC7AC \uD0DC\uC2A4\uD06C\uB97C \uC9C4\uD589\uD558\uC138\uC694",
499
499
  "context.actionDetail.taskExecuteRun": "\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent) \uC791\uC5C5 handoff\uB97C \uC900\uBE44\uD558\uACE0 \uD0DC\uC2A4\uD06C\uB97C \uC2DC\uC791\uD558\uC138\uC694: {task}. (TODO\uBA74 DOING\uC73C\uB85C \uBCC0\uACBD)",
500
- "context.actionDetail.taskExecuteContinue": "\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent) \uC791\uC5C5 handoff\uB97C \uC900\uBE44\uD574 \uC9C4\uD589 \uC911\uC778 \uD0DC\uC2A4\uD06C\uB97C \uC774\uC5B4\uAC00\uC138\uC694: {task}",
500
+ "context.actionDetail.taskExecuteContinue": "\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent) \uC791\uC5C5 handoff\uB97C \uC900\uBE44\uD574 \uC9C4\uD589 \uC911\uC778 \uD0DC\uC2A4\uD06C\uB97C \uB9C8\uBB34\uB9AC\uD558\uC138\uC694: {task}. (\uC644\uB8CC \uD6C4 \uACB0\uACFC/\uAC80\uC99D\uC744 \uACF5\uC720\uD558\uACE0 DONE\uC73C\uB85C \uBCC0\uACBD)",
501
+ "context.actionDetail.taskExecuteComplete": "\uD604\uC7AC \uD0DC\uC2A4\uD06C\uB97C \uC644\uB8CC \uCC98\uB9AC\uD558\uC138\uC694: {task}. (DOING\uC744 DONE\uC73C\uB85C \uBCC0\uACBD)",
501
502
  "context.actionDetail.reviewFixCommit": "\uD574\uACB0\uD55C \uB9AC\uBDF0 \uD56D\uBAA9 \uC694\uC57D\uC73C\uB85C \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B\uC744 \uB9CC\uB4DC\uC138\uC694",
502
503
  "context.actionDetail.prePrReviewRun": "\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent)\uB85C PR \uC804 \uB9AC\uBDF0\uB97C \uC2E4\uD589\uD574 `review-trace.json`\uC744 \uC900\uBE44\uD558\uC138\uC694",
503
504
  "context.actionDetail.prePrReviewRecord": "PR \uC804 \uB9AC\uBDF0 evidence\uB97C decisions.md\uC640 tasks.md\uC5D0 \uAE30\uB85D\uD558\uC138\uC694",
@@ -1049,7 +1050,8 @@ var enContext = {
1049
1050
  "context.actionDetail.issueCreateFromDoc": "Create GitHub Issue from ready issue.md and sync Issue",
1050
1051
  "context.actionDetail.taskExecute": "Proceed with the current task",
1051
1052
  "context.actionDetail.taskExecuteRun": "Prepare helper agent/sub-agent task handoff and start the task: {task}. (TODO becomes DOING)",
1052
- "context.actionDetail.taskExecuteContinue": "Prepare helper agent/sub-agent handoff and continue the in-progress task: {task}",
1053
+ "context.actionDetail.taskExecuteContinue": "Prepare helper agent/sub-agent handoff and wrap up the in-progress task: {task}. (Share outcome/verification, then mark it DONE)",
1054
+ "context.actionDetail.taskExecuteComplete": "Mark the current task as complete: {task}. (Change DOING to DONE)",
1053
1055
  "context.actionDetail.reviewFixCommit": "Create a review-fix commit with resolved feedback summary",
1054
1056
  "context.actionDetail.prePrReviewRun": "Run the pre-PR review via a helper agent/sub-agent and prepare `review-trace.json`",
1055
1057
  "context.actionDetail.prePrReviewRecord": "Record pre-PR review evidence into decisions.md and tasks.md",
@@ -3553,6 +3555,7 @@ function resolvePrePrReviewPolicy(workflow) {
3553
3555
  function getPrePrReviewPrompt(lang, skills, fallbackText) {
3554
3556
  if (lang === "ko") {
3555
3557
  return `PR \uC0DD\uC131 \uC804 \uC0AC\uC804 \uCF54\uB4DC\uB9AC\uBDF0\uB97C \uC9C4\uD589\uD558\uC138\uC694.
3558
+ 0. \uAC19\uC740 feature/pre-PR \uB9AC\uBDF0\uB97C \uB2F4\uB2F9\uD558\uB358 \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8\uAC00 \uC774\uBBF8 \uC788\uC73C\uBA74 \uC0C8\uB85C \uB9CC\uB4E4\uC9C0 \uB9D0\uACE0 \uC7AC\uC0AC\uC6A9\uD558\uC138\uC694. \uAE30\uBCF8\uC740 \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8 1\uAC1C\uC785\uB2C8\uB2E4.
3556
3559
  1. \`spec.md\`, \`plan.md\`, \`tasks.md\`\uB97C \uC77D\uACE0 feature \uBAA9\uD45C/\uBC94\uC704/\uC644\uB8CC \uAE30\uC900\uC744 \uBA3C\uC800 \uC694\uC57D\uD558\uC138\uC694.
3557
3560
  2. \uB9AC\uBDF0 \uBC94\uC704\uB97C \uBD84\uB9AC\uD574 \uD655\uC778\uD558\uC138\uC694.
3558
3561
  - main \uAE30\uC900: 'git diff --name-only $(git merge-base HEAD origin/main)..HEAD'
@@ -3561,11 +3564,14 @@ function getPrePrReviewPrompt(lang, skills, fallbackText) {
3561
3564
  4. \uD655\uC778\uB41C \uAC01 \uD30C\uC77C\uC5D0 \uB300\uD574 risk, security, perf, maintainability \uD3C9\uAC00\uC640 \uAD6C\uCCB4\uC801\uC778 fileLine \uC704\uCE58\uAC00 \uD3EC\uD568\uB41C 'review-trace.json' \uC99D\uAC70 \uD30C\uC77C\uC744 \uC0DD\uC131\uD558\uC138\uC694.
3562
3565
  5. \uAE30\uBCF8 \uBCA0\uC774\uC2A4\uB77C\uC778\uC740 '${fallbackText}'\uC774\uBA70, 'create-pr' \uBB38\uC11C\uC758 'Pre-PR \uAE30\uBCF8 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8' \uC139\uC158\uC744 \uC218\uD589\uD558\uC138\uC694.
3563
3566
  6. \uC6B0\uC120\uC21C\uC704 \uC2A4\uD0AC: ${skills.length > 0 ? skills.join(", ") : "\uC5C6\uC74C"} \uB85C \uC2EC\uD654 \uAC80\uD1A0\uB97C \uC9C4\uD589\uD558\uC138\uC694.
3564
- 7. \uCD94\uAC00 \uAC80\uC99D\uC774 \uAF2D \uD544\uC694\uD560 \uB54C\uB9CC audit/\uD0C0\uAE43 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uACE0, \uC2E4\uD589\uD588\uB2E4\uBA74 \`commandsExecuted\`\uC5D0 \uAE30\uB85D\uD558\uC138\uC694.
3565
- 8. \uC9C0\uC801\uC0AC\uD56D\uC774 \uB0A8\uC544 \uC788\uC73C\uBA74 \uBA3C\uC800 'npx lee-spec-kit pre-pr-review <feature> --evidence review-trace.json --decision changes_requested' \uB85C \uAE30\uB85D\uD558\uC138\uC694. \uB2E8, \`workflow.prePrReview.evidenceMode=any\` \uC774\uACE0 \uC2E4\uD589 \uC99D\uAC70 \uAC15\uC81C\uAC00 \uC5C6\uC73C\uBA74 \`--evidence\` \uC5C6\uC774 \uC9C1\uC811 \uAE30\uB85D\uD574\uB3C4 \uB429\uB2C8\uB2E4.
3566
- 9. \uC218\uC815/\uC7AC\uAC80\uC99D \uD6C4 \uCD5C\uC885 \uC2B9\uC778 \uC2DC\uC810\uC5D0 'npx lee-spec-kit pre-pr-review <feature> --decision approve' \uB97C \uC2E4\uD589\uD558\uC138\uC694. \`path_required\` \uC815\uCC45\uC77C \uB54C\uB9CC \`--evidence review-trace.json\` \uC744 \uD568\uAED8 \uBD99\uC774\uC138\uC694.`;
3567
+ 7. \uCD94\uAC00 \uAC80\uC99D\uC774 \uAF2D \uD544\uC694\uD560 \uB54C\uB9CC audit/\uD0C0\uAE43 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uC138\uC694. \uBCC4\uB3C4 \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8 \uCD94\uAC00 \uC0DD\uC131\uB3C4 \uAF2D \uD544\uC694\uD560 \uB54C\uB9CC \uD558\uC138\uC694.
3568
+ 8. \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8 \uD55C\uB3C4\uC5D0 \uAC78\uB9AC\uBA74 \uBA54\uC778 \uC5D0\uC774\uC804\uD2B8\uC5D0\uC11C \uB9AC\uBDF0\uB97C \uC774\uC5B4\uAC00\uACE0, \uC774\uBBF8 \uC218\uC9D1\uD55C \uACB0\uACFC\uB9CC \uC815\uB9AC\uD574\uB3C4 \uB429\uB2C8\uB2E4.
3569
+ 9. \uC2E4\uD589\uD55C \uBA85\uB839\uC774 \uC788\uC73C\uBA74 \`commandsExecuted\`\uC5D0 \uAE30\uB85D\uD558\uC138\uC694.
3570
+ 10. \uC9C0\uC801\uC0AC\uD56D\uC774 \uB0A8\uC544 \uC788\uC73C\uBA74 \uBA3C\uC800 'npx lee-spec-kit pre-pr-review <feature> --evidence review-trace.json --decision changes_requested' \uB85C \uAE30\uB85D\uD558\uC138\uC694. \uB2E8, \`workflow.prePrReview.evidenceMode=any\` \uC774\uACE0 \uC2E4\uD589 \uC99D\uAC70 \uAC15\uC81C\uAC00 \uC5C6\uC73C\uBA74 \`--evidence\` \uC5C6\uC774 \uC9C1\uC811 \uAE30\uB85D\uD574\uB3C4 \uB429\uB2C8\uB2E4.
3571
+ 11. \uC218\uC815/\uC7AC\uAC80\uC99D \uD6C4 \uCD5C\uC885 \uC2B9\uC778 \uC2DC\uC810\uC5D0 'npx lee-spec-kit pre-pr-review <feature> --decision approve' \uB97C \uC2E4\uD589\uD558\uC138\uC694. \`path_required\` \uC815\uCC45\uC77C \uB54C\uB9CC \`--evidence review-trace.json\` \uC744 \uD568\uAED8 \uBD99\uC774\uC138\uC694.`;
3567
3572
  }
3568
3573
  return `Conduct a pre-PR code review.
3574
+ 0. Reuse the existing helper/sub-agent for this feature review if one already exists. Default to a single helper agent.
3569
3575
  1. Read \`spec.md\`, \`plan.md\`, and \`tasks.md\` first, then summarize the feature goal, scope, and done criteria.
3570
3576
  2. Split and check the review scope.
3571
3577
  - Main scope: 'git diff --name-only $(git merge-base HEAD origin/main)..HEAD'
@@ -3574,15 +3580,17 @@ function getPrePrReviewPrompt(lang, skills, fallbackText) {
3574
3580
  4. Generate a 'review-trace.json' file for all changed files, including \`findingCount\`, \`blockingFindings\`, evaluations for risk, security, perf, maintainability, and specific fileLine locators.
3575
3581
  5. The baseline is '${fallbackText}'. Always perform the 'Pre-PR Core Checklist' section of the 'create-pr' document.
3576
3582
  6. Priority skills: ${skills.length > 0 ? skills.join(", ") : "None"} for deeper technical review.
3577
- 7. Run extra audit/targeted verification only when the review needs more evidence, and record those commands in \`commandsExecuted\` when used.
3578
- 8. If unresolved findings remain, first record them with 'npx lee-spec-kit pre-pr-review <feature> --evidence review-trace.json --decision changes_requested'. When \`workflow.prePrReview.evidenceMode=any\` and execution evidence is not enforced, direct record mode without \`--evidence\` is also valid.
3579
- 9. After fixes and re-validation, run 'npx lee-spec-kit pre-pr-review <feature> --decision approve' for final pre-PR approval. Add \`--evidence review-trace.json\` only when the active evidence policy requires a path.`;
3583
+ 7. Run extra audit/targeted verification only when the review truly needs more evidence. Spawn additional helper agents only when necessary.
3584
+ 8. If helper-agent quota is exhausted, continue the review in the main agent and just keep the evidence consistent.
3585
+ 9. Record commands in \`commandsExecuted\` only when you actually ran them.
3586
+ 10. If unresolved findings remain, first record them with 'npx lee-spec-kit pre-pr-review <feature> --evidence review-trace.json --decision changes_requested'. When \`workflow.prePrReview.evidenceMode=any\` and execution evidence is not enforced, direct record mode without \`--evidence\` is also valid.
3587
+ 11. After fixes and re-validation, run 'npx lee-spec-kit pre-pr-review <feature> --decision approve' for final pre-PR approval. Add \`--evidence review-trace.json\` only when the active evidence policy requires a path.`;
3580
3588
  }
3581
3589
  function getCodeReviewPrompt(lang) {
3582
3590
  if (lang === "ko") {
3583
- return `\uB9AC\uBDF0 \uCF54\uBA58\uD2B8\uB97C \uD655\uC778/\uBD84\uC11D\uD55C \uB4A4 \uD544\uC694\uD55C \uC218\uC815\uC744 \uC9C4\uD589\uD558\uC138\uC694. PR \uC0C1\uD0DC\uB294 Review\uB97C \uC720\uC9C0\uD558\uACE0 'PR \uB9AC\uBDF0 Evidence/Decision'\uC744 \uCD5C\uC2E0\uC73C\uB85C \uAE30\uB85D\uD558\uC138\uC694. \uC6D0\uACA9 \uBC18\uC601(push)\uC740 \uBA85\uC2DC\uC801\uC778 \uBA38\uC9C0 \uC2B9\uC778(\uB77C\uBCA8) \uD6C4, \uB85C\uCEEC \uBE0C\uB79C\uCE58\uAC00 upstream\uBCF4\uB2E4 \uC55E\uC120 \uACBD\uC6B0\uC5D0\uB9CC \uC9C4\uD589\uD558\uC138\uC694.`;
3591
+ return `\uB9AC\uBDF0 \uCF54\uBA58\uD2B8\uB97C \uD655\uC778/\uBD84\uC11D\uD55C \uB4A4 \uD544\uC694\uD55C \uC218\uC815\uC744 \uC9C4\uD589\uD558\uC138\uC694. \uAE30\uC874 review \uB2F4\uB2F9 \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8\uAC00 \uC788\uC73C\uBA74 \uC7AC\uC0AC\uC6A9\uD558\uACE0, \uAE30\uBCF8\uC740 1\uAC1C\uB9CC \uC0AC\uC6A9\uD558\uC138\uC694. \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8 \uD55C\uB3C4\uC5D0 \uAC78\uB9AC\uBA74 \uBA54\uC778 \uC5D0\uC774\uC804\uD2B8\uC5D0\uC11C \uC774\uC5B4\uAC00\uC138\uC694. PR \uC0C1\uD0DC\uB294 Review\uB97C \uC720\uC9C0\uD558\uACE0 'PR \uB9AC\uBDF0 Evidence/Decision'\uC744 \uCD5C\uC2E0\uC73C\uB85C \uAE30\uB85D\uD558\uC138\uC694. \uC6D0\uACA9 \uBC18\uC601(push)\uC740 \uBA85\uC2DC\uC801\uC778 \uBA38\uC9C0 \uC2B9\uC778(\uB77C\uBCA8) \uD6C4, \uB85C\uCEEC \uBE0C\uB79C\uCE58\uAC00 upstream\uBCF4\uB2E4 \uC55E\uC120 \uACBD\uC6B0\uC5D0\uB9CC \uC9C4\uD589\uD558\uC138\uC694.`;
3584
3592
  }
3585
- return `Review and analyze comments, then make necessary fixes. Keep PR status as Review and record the latest 'PR Review Evidence/Decision'. Push changes only after explicit approval (label) and only if the local branch is ahead of upstream.`;
3593
+ return `Review and analyze comments, then make necessary fixes. Reuse the existing review helper agent if one already exists, and default to a single helper agent. If helper-agent quota is exhausted, continue in the main agent. Keep PR status as Review and record the latest 'PR Review Evidence/Decision'. Push changes only after explicit approval (label) and only if the local branch is ahead of upstream.`;
3586
3594
  }
3587
3595
 
3588
3596
  // src/utils/context/steps.ts
@@ -3694,6 +3702,13 @@ function buildTaskRunCommandArgs(feature, taskId) {
3694
3702
  }
3695
3703
  return commandArgs;
3696
3704
  }
3705
+ function buildTaskCompleteCommandArgs(feature, taskId) {
3706
+ const commandArgs = ["task-complete", feature.folderName, "--task", taskId];
3707
+ if (feature.type && feature.type !== "single") {
3708
+ commandArgs.push("--component", feature.type);
3709
+ }
3710
+ return commandArgs;
3711
+ }
3697
3712
  function buildCodeReviewRunCommandArgs(feature) {
3698
3713
  const commandArgs = ["code-review-run", feature.folderName];
3699
3714
  if (feature.type && feature.type !== "single") {
@@ -3849,7 +3864,7 @@ function countDoneTransitionsInLatestTasksCommit(ctx, feature) {
3849
3864
  if (!diff.trim()) return 0;
3850
3865
  const removedByTask = /* @__PURE__ */ new Map();
3851
3866
  const addedByTask = /* @__PURE__ */ new Map();
3852
- const parseTaskLine2 = (line) => {
3867
+ const parseTaskLine3 = (line) => {
3853
3868
  const match = line.match(
3854
3869
  /^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]\s+(.+?)\s*$/i
3855
3870
  );
@@ -3864,7 +3879,7 @@ function countDoneTransitionsInLatestTasksCommit(ctx, feature) {
3864
3879
  for (const line of diff.split("\n")) {
3865
3880
  if (line.startsWith("---") || line.startsWith("+++")) continue;
3866
3881
  if (line.startsWith("-")) {
3867
- const parsed = parseTaskLine2(line.slice(1));
3882
+ const parsed = parseTaskLine3(line.slice(1));
3868
3883
  if (!parsed) continue;
3869
3884
  const existing = removedByTask.get(parsed.key) || /* @__PURE__ */ new Set();
3870
3885
  existing.add(parsed.status);
@@ -3872,7 +3887,7 @@ function countDoneTransitionsInLatestTasksCommit(ctx, feature) {
3872
3887
  continue;
3873
3888
  }
3874
3889
  if (line.startsWith("+")) {
3875
- const parsed = parseTaskLine2(line.slice(1));
3890
+ const parsed = parseTaskLine3(line.slice(1));
3876
3891
  if (!parsed) continue;
3877
3892
  const existing = addedByTask.get(parsed.key) || /* @__PURE__ */ new Set();
3878
3893
  existing.add(parsed.status);
@@ -4073,14 +4088,14 @@ function getStepDefinitions(ctx) {
4073
4088
  operationType: "local",
4074
4089
  requiresUserCheck: true,
4075
4090
  taskExecutePhase: "complete",
4076
- uiDetailKey: "context.actionDetail.taskExecuteContinue",
4091
+ uiDetailKey: "context.actionDetail.taskExecuteComplete",
4077
4092
  uiDetailParams: {
4078
4093
  task: resolveTaskUiLabel(f.activeTask)
4079
4094
  },
4080
4095
  scope: "docs",
4081
4096
  cwd: f.git.docsGitCwd,
4082
4097
  cmd: buildSelfCliCommand(
4083
- buildTaskRunCommandArgs(
4098
+ buildTaskCompleteCommandArgs(
4084
4099
  f,
4085
4100
  f.activeTask?.id || `T-${f.folderName}-active`
4086
4101
  )
@@ -4747,9 +4762,9 @@ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
4747
4762
  actions: (f) => getTaskExecuteFinalizeActions(f)
4748
4763
  },
4749
4764
  {
4750
- id: "task_running",
4751
- phase: "running",
4752
- owner: "subagent",
4765
+ id: "task_complete",
4766
+ phase: "finalize",
4767
+ owner: "main",
4753
4768
  category: "task_execute",
4754
4769
  when: (f) => isTaskExecuteCurrent(f) && !!f.activeTask,
4755
4770
  actions: (f) => getTaskExecuteRunningActions(f)
@@ -5206,7 +5221,7 @@ function applyApprovalPolicy(step, actions, approval, currentSubstatePhase) {
5206
5221
  function applyTaskExecutePhaseCheck(action, requiresUserCheck, policy, explicitlyRequired = false, currentSubstatePhase) {
5207
5222
  if (policy !== "start_only") return requiresUserCheck;
5208
5223
  if (action.category !== "task_execute") return requiresUserCheck;
5209
- const isCompletionPhase = currentSubstatePhase === "running" || !currentSubstatePhase && action.taskExecutePhase === "complete";
5224
+ const isCompletionPhase = currentSubstatePhase === "running" || currentSubstatePhase === "finalize" || !currentSubstatePhase && action.taskExecutePhase === "complete";
5210
5225
  if (!isCompletionPhase) return requiresUserCheck;
5211
5226
  if (explicitlyRequired) return requiresUserCheck;
5212
5227
  return false;
@@ -8271,7 +8286,7 @@ function toActionOptions(actions, lang) {
8271
8286
  const summary = getActionSummary(action, lang);
8272
8287
  const detail = buildActionDetail(action, lang);
8273
8288
  const requiresRequestText = action.category === "user_request_replan";
8274
- const replyExample = requiresRequestText ? `${label}, <your request>` : `${label} OK`;
8289
+ const replyExample = requiresRequestText ? `${label}, <your request>` : label;
8275
8290
  return {
8276
8291
  label,
8277
8292
  summary,
@@ -15928,6 +15943,9 @@ async function runPrePrReviewRun(featureName, options) {
15928
15943
  fallback: policy.fallback,
15929
15944
  handoffOnly: true,
15930
15945
  advancesWorkflow: false,
15946
+ reuseKey: `pre-pr:${featureRef}`,
15947
+ suggestedParallelism: 1,
15948
+ fallbackToMainAgentWhenQuotaExceeded: true,
15931
15949
  nextStepRequirement: "generate_review_trace_then_record",
15932
15950
  evidenceFile: "review-trace.json",
15933
15951
  prompt,
@@ -15949,6 +15967,18 @@ async function runPrePrReviewRun(featureName, options) {
15949
15967
  config.lang === "ko" ? "\uC774 \uBA85\uB839\uC740 \uB9AC\uBDF0 handoff\uB9CC \uC900\uBE44\uD569\uB2C8\uB2E4. review-trace.json\uC744 \uC9C1\uC811 \uC0DD\uC131\uD558\uAC70\uB098 \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC0C1\uD0DC\uB97C \uBC14\uB85C \uB118\uAE30\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4." : "This command only prepares the review handoff. It does not generate review-trace.json or advance workflow state by itself."
15950
15968
  )
15951
15969
  );
15970
+ console.log(
15971
+ chalk8.gray(
15972
+ config.lang === "ko" ? "- \uAE30\uC874 pre-PR \uB9AC\uBDF0 \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8\uAC00 \uC788\uC73C\uBA74 \uC7AC\uC0AC\uC6A9\uD558\uACE0, \uAE30\uBCF8\uC740 1\uAC1C\uB9CC \uC0AC\uC6A9\uD558\uC138\uC694." : "- Reuse the existing pre-PR helper agent if one already exists; default to a single helper agent."
15973
+ )
15974
+ );
15975
+ console.log(
15976
+ chalk8.gray(
15977
+ config.lang === "ko" ? "- \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8 \uD55C\uB3C4\uC5D0 \uAC78\uB9AC\uBA74 \uBA54\uC778 \uC5D0\uC774\uC804\uD2B8\uC5D0\uC11C \uB9AC\uBDF0\uB97C \uC774\uC5B4\uAC00\uC138\uC694." : "- If helper-agent quota is exhausted, continue the review in the main agent."
15978
+ )
15979
+ );
15980
+ console.log(`Reuse key: pre-pr:${featureRef}`);
15981
+ console.log(`Suggested parallelism: 1`);
15952
15982
  console.log(`Evidence file: review-trace.json`);
15953
15983
  console.log(`Record changes requested: ${changesRequestedCommand}`);
15954
15984
  console.log(`Record approval: ${approveCommand}`);
@@ -16181,6 +16211,9 @@ async function runCodeReviewRun(featureName, options) {
16181
16211
  owner: "subagent",
16182
16212
  handoffOnly: true,
16183
16213
  advancesWorkflow: false,
16214
+ reuseKey: `code-review:${feature.folderName}`,
16215
+ suggestedParallelism: 1,
16216
+ fallbackToMainAgentWhenQuotaExceeded: true,
16184
16217
  nextMainState: "code_review_finalize",
16185
16218
  tasksPath: path12.join(feature.path, "tasks.md"),
16186
16219
  decisionsPath: path12.join(feature.path, "decisions.md"),
@@ -16200,6 +16233,13 @@ async function runCodeReviewRun(featureName, options) {
16200
16233
  );
16201
16234
  console.log(chalk8.gray(`- substate: ${payload.substateId}`));
16202
16235
  console.log(chalk8.gray(`- owner: ${payload.owner}`));
16236
+ console.log(chalk8.gray(`- reuse key: ${payload.reuseKey}`));
16237
+ console.log(chalk8.gray(`- suggested parallelism: ${payload.suggestedParallelism}`));
16238
+ console.log(
16239
+ chalk8.gray(
16240
+ `- quota fallback: ${payload.fallbackToMainAgentWhenQuotaExceeded ? "continue in main agent" : "none"}`
16241
+ )
16242
+ );
16203
16243
  console.log(chalk8.gray(`- next main state: ${payload.nextMainState}`));
16204
16244
  console.log(chalk8.gray(`- tasks.md: ${payload.tasksPath}`));
16205
16245
  console.log(chalk8.gray(`- decisions.md: ${payload.decisionsPath}`));
@@ -16451,9 +16491,10 @@ function parseTaskLine(line) {
16451
16491
  function buildTaskRunPrompt(input) {
16452
16492
  const shared = [
16453
16493
  "Read `spec.md`, `plan.md`, and `tasks.md` before editing code.",
16454
- "Use sub-agents by default for code analysis and discovery work.",
16455
- "Parallelize impact analysis, test-location search, and existing-pattern discovery when those can be done independently.",
16494
+ "Reuse the existing helper/sub-agent for this task if one already exists. Default to a single helper agent.",
16495
+ "Use additional helper agents only when parallel analysis is clearly worth the extra slot cost.",
16456
16496
  "Keep one writer for overlapping files; do not let multiple sub-agents edit the same files concurrently.",
16497
+ "If helper-agent quota is exhausted, continue the task in the main agent instead of blocking progress.",
16457
16498
  "Update the assigned task status and verification notes in `tasks.md` before leaving this task.",
16458
16499
  "Mark the task `DONE` only after code changes and verification are complete."
16459
16500
  ];
@@ -16464,9 +16505,10 @@ function buildTaskRunPrompt(input) {
16464
16505
  `- Task: ${input.taskId} ${input.title}`,
16465
16506
  input.mode === "start" ? "- \uC774 \uBA85\uB839\uC740 `tasks.md`\uC758 \uD604\uC7AC task\uB97C `DOING`\uC73C\uB85C \uBC14\uAFB8\uACE0, \uC774\uD6C4 \uAD6C\uD604 handoff prompt\uB97C \uC900\uBE44\uD569\uB2C8\uB2E4." : "- \uC774 \uBA85\uB839\uC740 \uC9C4\uD589 \uC911 task\uC758 \uAD6C\uD604 handoff prompt\uB97C \uB2E4\uC2DC \uC900\uBE44\uD569\uB2C8\uB2E4.",
16466
16507
  "- \uBA3C\uC800 `spec.md`, `plan.md`, `tasks.md`\uB97C \uC77D\uACE0 \uBC94\uC704\uC640 \uC644\uB8CC \uAE30\uC900\uC744 \uC815\uB9AC\uD558\uC138\uC694.",
16467
- "- \uCF54\uB4DC \uBD84\uC11D\uACFC \uD0D0\uC0C9 \uC791\uC5C5\uC740 \uAE30\uBCF8\uC801\uC73C\uB85C \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uB97C \uC0AC\uC6A9\uD558\uC138\uC694.",
16468
- "- \uC601\uD5A5 \uBC94\uC704 \uBD84\uC11D, \uD14C\uC2A4\uD2B8 \uC704\uCE58 \uD0D0\uC0C9, \uAE30\uC874 \uD328\uD134 \uC870\uC0AC\uCC98\uB7FC \uB3C5\uB9BD\uC801\uC778 \uBD84\uC11D\uC740 \uBCD1\uB82C\uB85C \uC218\uD589\uD558\uC138\uC694.",
16508
+ "- \uAE30\uC874\uC5D0 \uC774 task\uB97C \uB9E1\uB358 \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8\uAC00 \uC788\uC73C\uBA74 \uC7AC\uC0AC\uC6A9\uD558\uACE0, \uAE30\uBCF8\uC740 1\uAC1C\uB9CC \uC0AC\uC6A9\uD558\uC138\uC694.",
16509
+ "- \uC601\uD5A5 \uBC94\uC704 \uBD84\uC11D, \uD14C\uC2A4\uD2B8 \uC704\uCE58 \uD0D0\uC0C9, \uAE30\uC874 \uD328\uD134 \uC870\uC0AC\uAC00 \uC815\uB9D0 \uB3C5\uB9BD\uC801\uC77C \uB54C\uB9CC \uCD94\uAC00 \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8\uB97C \uC0AC\uC6A9\uD558\uC138\uC694.",
16469
16510
  "- \uAC19\uC740 \uD30C\uC77C\uAD70\uC744 \uC218\uC815\uD558\uB294 \uC791\uC131\uC790\uB294 \uD55C \uBA85\uB9CC \uB450\uC138\uC694.",
16511
+ "- \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8 \uD55C\uB3C4\uC5D0 \uAC78\uB9AC\uBA74 \uBA54\uC778 \uC5D0\uC774\uC804\uD2B8\uC5D0\uC11C \uAD6C\uD604\uC744 \uC774\uC5B4\uAC00\uC138\uC694.",
16470
16512
  "- \uC774 task\uB97C \uB9C8\uCE58\uAE30 \uC804 `tasks.md`\uC5D0 \uC0C1\uD0DC\uC640 \uAC80\uC99D \uBA54\uBAA8\uB97C \uBC18\uC601\uD558\uC138\uC694.",
16471
16513
  "- \uCF54\uB4DC \uBCC0\uACBD\uACFC \uAC80\uC99D\uC774 \uB05D\uB0AC\uC744 \uB54C\uB9CC task\uB97C `DONE`\uC73C\uB85C \uD45C\uC2DC\uD558\uC138\uC694."
16472
16514
  ].join("\n");
@@ -16565,7 +16607,11 @@ async function runTaskRun(featureName, options) {
16565
16607
  mode,
16566
16608
  substateId: mode === "start" ? "task_run" : "task_running",
16567
16609
  owner: "subagent",
16568
- nextMainState: "task_finalize",
16610
+ handoffOnly: true,
16611
+ reuseKey: `task:${feature.folderName}:${resolvedTask.taskId}`,
16612
+ suggestedParallelism: 1,
16613
+ fallbackToMainAgentWhenQuotaExceeded: true,
16614
+ nextMainState: "task_complete",
16569
16615
  tasksUpdated,
16570
16616
  tasksPath,
16571
16617
  prompt,
@@ -16579,6 +16625,13 @@ async function runTaskRun(featureName, options) {
16579
16625
  console.log();
16580
16626
  console.log(chalk8.gray(`- substate: ${payload.substateId}`));
16581
16627
  console.log(chalk8.gray(`- owner: ${payload.owner}`));
16628
+ console.log(chalk8.gray(`- reuse key: ${payload.reuseKey}`));
16629
+ console.log(chalk8.gray(`- suggested parallelism: ${payload.suggestedParallelism}`));
16630
+ console.log(
16631
+ chalk8.gray(
16632
+ `- quota fallback: ${payload.fallbackToMainAgentWhenQuotaExceeded ? "continue in main agent" : "none"}`
16633
+ )
16634
+ );
16582
16635
  console.log(chalk8.gray(`- next main state: ${payload.nextMainState}`));
16583
16636
  if (tasksUpdated) {
16584
16637
  console.log();
@@ -16614,6 +16667,146 @@ function taskRunCommand(program2) {
16614
16667
  }
16615
16668
  });
16616
16669
  }
16670
+ function parseTaskLine2(line) {
16671
+ const match = line.match(
16672
+ /^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]\[[^\]]+\](?:\[[^\]]+\])*\s+(T-[A-Za-z0-9-]+)\s+(.+?)\s*$/
16673
+ );
16674
+ if (!match) return null;
16675
+ return {
16676
+ index: -1,
16677
+ raw: line,
16678
+ status: match[1],
16679
+ taskId: match[2],
16680
+ title: match[3]
16681
+ };
16682
+ }
16683
+ function setTaskStatus2(line, nextStatus) {
16684
+ return line.raw.replace(
16685
+ /^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]/,
16686
+ `- [${nextStatus}]`
16687
+ );
16688
+ }
16689
+ async function resolveTaskCompleteContext(featureName, options) {
16690
+ const config = await getConfig(process.cwd());
16691
+ if (!config) {
16692
+ throw createCliError(
16693
+ "CONFIG_NOT_FOUND",
16694
+ "No lee-spec-kit config found in this workspace."
16695
+ );
16696
+ }
16697
+ const ctx = await createCliContext({ cwd: process.cwd() });
16698
+ const state = await resolveContextSelection(ctx, featureName, {
16699
+ component: resolveComponentOption(options.component)
16700
+ });
16701
+ if (state.status !== "single_matched" || !state.matchedFeature) {
16702
+ throw createCliError(
16703
+ "CONTEXT_SELECTION_REQUIRED",
16704
+ "task-complete requires a single matched feature. Pass <feature-name> explicitly."
16705
+ );
16706
+ }
16707
+ return {
16708
+ config,
16709
+ feature: state.matchedFeature
16710
+ };
16711
+ }
16712
+ async function runTaskComplete(featureName, options) {
16713
+ const { feature } = await resolveTaskCompleteContext(featureName, options);
16714
+ const tasksPath = path12.join(feature.path, "tasks.md");
16715
+ if (!await fs.pathExists(tasksPath)) {
16716
+ throw createCliError(
16717
+ "PRECONDITION_FAILED",
16718
+ `tasks.md not found for feature: ${feature.folderName}`
16719
+ );
16720
+ }
16721
+ const content = await fs.readFile(tasksPath, "utf-8");
16722
+ const lines = content.split("\n");
16723
+ const requestedTaskId = options.task?.trim() || feature.activeTask?.id || feature.nextTodoTask?.id;
16724
+ if (!requestedTaskId) {
16725
+ throw createCliError(
16726
+ "PRECONDITION_FAILED",
16727
+ "No active task is available for task-complete."
16728
+ );
16729
+ }
16730
+ const resolvedTask = lines.map((line, index) => {
16731
+ const parsed = parseTaskLine2(line);
16732
+ return parsed ? { ...parsed, index } : null;
16733
+ }).find((entry) => entry?.taskId === requestedTaskId);
16734
+ if (!resolvedTask) {
16735
+ throw createCliError(
16736
+ "INVALID_ARGUMENT",
16737
+ `Task "${requestedTaskId}" was not found in tasks.md.`
16738
+ );
16739
+ }
16740
+ if (resolvedTask.status === "DONE") {
16741
+ throw createCliError(
16742
+ "PRECONDITION_FAILED",
16743
+ `Task "${requestedTaskId}" is already DONE.`
16744
+ );
16745
+ }
16746
+ if (resolvedTask.status !== "DOING" && resolvedTask.status !== "REVIEW") {
16747
+ throw createCliError(
16748
+ "PRECONDITION_FAILED",
16749
+ `Task "${requestedTaskId}" must be DOING/REVIEW before marking it DONE.`
16750
+ );
16751
+ }
16752
+ lines[resolvedTask.index] = setTaskStatus2(resolvedTask, "DONE");
16753
+ await fs.writeFile(tasksPath, lines.join("\n"), "utf-8");
16754
+ const payload = {
16755
+ status: "ok",
16756
+ reasonCode: "TASK_COMPLETED",
16757
+ feature: feature.folderName,
16758
+ taskId: resolvedTask.taskId,
16759
+ title: resolvedTask.title,
16760
+ previousStatus: resolvedTask.status,
16761
+ nextStatus: "DONE",
16762
+ substateId: "task_complete",
16763
+ owner: "main",
16764
+ nextMainState: "task_finalize",
16765
+ tasksUpdated: true,
16766
+ tasksPath,
16767
+ recordedAt: getLocalDateString()
16768
+ };
16769
+ if (options.json) {
16770
+ console.log(JSON.stringify(payload, null, 2));
16771
+ return;
16772
+ }
16773
+ console.log(
16774
+ chalk8.green(
16775
+ `Marked task ${resolvedTask.taskId} as DONE for ${feature.folderName}.`
16776
+ )
16777
+ );
16778
+ console.log(chalk8.gray(`- tasks.md updated: ${tasksPath}`));
16779
+ console.log(chalk8.gray(`- status: ${resolvedTask.status} -> DONE`));
16780
+ console.log(chalk8.gray(`- next main state: ${payload.nextMainState}`));
16781
+ }
16782
+ function taskCompleteCommand(program2) {
16783
+ program2.command("task-complete [feature-name]").description("Mark the active DOING/REVIEW task as DONE").option("--component <component>", "Component name for multi projects").option("--task <task-id>", "Explicit task id to mark DONE").option("--json", "Output JSON").action(
16784
+ async (featureName, options) => {
16785
+ try {
16786
+ await runTaskComplete(featureName, options);
16787
+ } catch (error) {
16788
+ const config = await getConfig(process.cwd());
16789
+ const lang = config?.lang ?? DEFAULT_LANG;
16790
+ const cliError = toCliError(error);
16791
+ const suggestions = getCliErrorSuggestions(cliError.code, lang);
16792
+ if (options.json) {
16793
+ console.log(
16794
+ JSON.stringify({
16795
+ status: "error",
16796
+ reasonCode: cliError.code,
16797
+ error: cliError.message,
16798
+ suggestions
16799
+ })
16800
+ );
16801
+ } else {
16802
+ console.error(chalk8.red(`[${cliError.code}] ${cliError.message}`));
16803
+ printCliErrorSuggestions(suggestions, lang);
16804
+ }
16805
+ process.exitCode = 1;
16806
+ }
16807
+ }
16808
+ );
16809
+ }
16617
16810
  function isBannerDisabled() {
16618
16811
  const v = (process.env.LEE_SPEC_KIT_NO_BANNER || "").trim();
16619
16812
  return v === "1";
@@ -16797,6 +16990,7 @@ onboardCommand(program);
16797
16990
  prePrReviewCommand(program);
16798
16991
  codeReviewRunCommand(program);
16799
16992
  taskRunCommand(program);
16993
+ taskCompleteCommand(program);
16800
16994
  requirementsCommand(program);
16801
16995
  await program.parseAsync();
16802
16996
  //# sourceMappingURL=index.js.map