lee-spec-kit 0.6.42 → 0.7.0

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
@@ -468,7 +468,8 @@ var koContext = {
468
468
  "context.suggestionCommandHint": "\uB77C\uBCA8 \uCC38\uACE0 \uBA85\uB839: {command}",
469
469
  "context.suggestionFinalPrompt": "\uD604\uC7AC \uCD94\uCC9C \uB77C\uBCA8: {labels}. \uC751\uB2F5\uC740 \uB77C\uBCA8 \uD1A0\uD070 \uD3EC\uD568 \uD615\uC2DD\uC73C\uB85C \uD574\uC8FC\uC138\uC694. (\uC608: {example}, `A \uC9C4\uD589\uD574`)",
470
470
  "context.autoRunUnavailable": "\uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8\uC5D0\uC11C\uB294 \uC790\uB3D9 \uC2E4\uD589\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
471
- "context.autoRunSummary": "config \uAE30\uC900\uC73C\uB85C \uC2B9\uC778 \uD544\uC694 \uCE74\uD14C\uACE0\uB9AC \uC804\uAE4C\uC9C0 \uC5F0\uC18D \uC2E4\uD589\uD558\uC138\uC694: {categories}",
471
+ "context.autoRunSummary": "config \uAE30\uC900\uC73C\uB85C \uD604\uC7AC \uBC14\uB85C \uC790\uB3D9 \uC2E4\uD589\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uC2B9\uC778 \uD544\uC694 \uCE74\uD14C\uACE0\uB9AC \uC804\uAE4C\uC9C0 \uC5F0\uC18D \uC2E4\uD589\uD558\uC138\uC694: {categories}",
472
+ "context.autoRunManualBoundary": "\uC790\uB3D9 \uC2E4\uD589 \uC124\uC815\uC740 \uAC00\uB2A5\uD558\uC9C0\uB9CC, \uD604\uC7AC \uB2E8\uACC4\uB294 \uBA3C\uC800 \uC218\uB3D9\uC73C\uB85C \uCC98\uB9AC\uD574\uC57C \uD569\uB2C8\uB2E4: {detail}",
472
473
  "context.autoRunCommandHint": "\uC790\uB3D9 \uC2E4\uD589 \uBA85\uB839(config \uAC8C\uC774\uD2B8): {command}",
473
474
  "context.subAgentOrchestrationHint": "\uBA54\uC778 \uC5D0\uC774\uC804\uD2B8\uAC00 \uC804\uCCB4 \uD750\uB984\uACFC \uC2B9\uC778\uC744 \uAD00\uB9AC\uD569\uB2C8\uB2E4. \uD604\uC7AC \uB2E8\uACC4\uC758 owner\uAC00 `subagent`\uBA74 \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8\uC5D0 \uB9E1\uAE30\uACE0, `main`\uC774\uBA74 \uBA54\uC778\uC5D0\uC11C \uC9C4\uD589\uD558\uC138\uC694.",
474
475
  "context.commandDetail.branchCreateWithWorktree": "({scope}) worktree {worktree}\uB97C \uC0AC\uC6A9\uD574 \uBE0C\uB79C\uCE58 {branch}\uB97C \uC0DD\uC131\uD558\uAC70\uB098 \uC7AC\uC0AC\uC6A9\uD558\uC138\uC694",
@@ -476,9 +477,9 @@ var koContext = {
476
477
  "context.commandDetail.branchCreateGeneric": "({scope}) feature \uBE0C\uB79C\uCE58\uC6A9 worktree\uB97C \uC0DD\uC131\uD558\uAC70\uB098 \uC7AC\uC0AC\uC6A9\uD558\uC138\uC694",
477
478
  "context.commandDetail.codeReviewMergeAfterOk": "({scope}) \uBA85\uC2DC\uC801 \uC2B9\uC778 \uD6C4 PR\uC744 \uBA38\uC9C0\uD558\uC138\uC694",
478
479
  "context.commandDetail.codeReviewPushFix": "({scope}) \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B\uC744 push\uD558\uC138\uC694",
479
- "context.commandDetail.prePrReviewRun": "({scope}) \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",
480
+ "context.commandDetail.prePrReviewRun": "({scope}) \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent) PR \uC804 \uB9AC\uBDF0 handoff\uB9CC \uC900\uBE44\uD569\uB2C8\uB2E4. \uC0C1\uD0DC \uC804\uC9C4\uC744 \uC704\uD574 evidence\uB294 \uBCC4\uB3C4\uB85C \uAE30\uB85D\uD574\uC57C \uD569\uB2C8\uB2E4",
480
481
  "context.commandDetail.prePrReviewRecord": "({scope}) Pre-PR \uB9AC\uBDF0 evidence\uB97C decisions.md\uC640 tasks.md\uC5D0 \uAE30\uB85D\uD558\uC138\uC694",
481
- "context.commandDetail.codeReviewRun": "({scope}) \uB9AC\uBDF0 \uCF54\uBA58\uD2B8\uB97C \uD655\uC778\uD558\uACE0, \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent)\uB85C \uC218\uC815 \uC791\uC5C5/evidence \uC815\uB9AC\uB97C \uC9C4\uD589\uD558\uC138\uC694",
482
+ "context.commandDetail.codeReviewRun": "({scope}) \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent) \uB9AC\uBDF0 \uC218\uC815 handoff\uB9CC \uC900\uBE44\uD569\uB2C8\uB2E4. delegated work \uD6C4 PR Review Evidence/Decision\uC744 \uAE30\uB85D\uD574\uC57C \uB2E4\uC74C\uC73C\uB85C \uC9C4\uD589\uB429\uB2C8\uB2E4",
482
483
  "context.actionSummary.runDocsCommand": "\uBB38\uC11C \uC791\uC5C5 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uC138\uC694",
483
484
  "context.actionSummary.runProjectCommand": "\uD504\uB85C\uC81D\uD2B8 \uC791\uC5C5 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uC138\uC694",
484
485
  "context.actionDetail.featureFolder": "Feature \uD3F4\uB354\uC640 \uAE30\uBCF8 \uBB38\uC11C \uACE8\uACA9\uC744 \uC900\uBE44\uD558\uC138\uC694",
@@ -497,11 +498,12 @@ var koContext = {
497
498
  "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
499
  "context.actionDetail.taskExecute": "\uD604\uC7AC \uD0DC\uC2A4\uD06C\uB97C \uC9C4\uD589\uD558\uC138\uC694",
499
500
  "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}",
501
+ "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)",
502
+ "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
503
  "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
- "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",
504
+ "context.actionDetail.prePrReviewRun": "\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent) PR \uC804 \uB9AC\uBDF0 handoff\uB9CC \uC900\uBE44\uD569\uB2C8\uB2E4. \uC0C1\uD0DC \uC804\uC9C4\uC744 \uC704\uD574 evidence\uB294 \uBCC4\uB3C4\uB85C \uAE30\uB85D\uD574\uC57C \uD569\uB2C8\uB2E4",
503
505
  "context.actionDetail.prePrReviewRecord": "PR \uC804 \uB9AC\uBDF0 evidence\uB97C decisions.md\uC640 tasks.md\uC5D0 \uAE30\uB85D\uD558\uC138\uC694",
504
- "context.actionDetail.codeReviewRun": "\uB9AC\uBDF0 \uCF54\uBA58\uD2B8\uB97C \uD655\uC778\uD558\uACE0, \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent)\uB85C \uC218\uC815 \uC791\uC5C5/evidence \uC815\uB9AC\uB97C \uC9C4\uD589\uD558\uC138\uC694",
506
+ "context.actionDetail.codeReviewRun": "\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent) \uB9AC\uBDF0 \uC218\uC815 handoff\uB9CC \uC900\uBE44\uD569\uB2C8\uB2E4. delegated work \uD6C4 PR Review Evidence/Decision\uC744 \uAE30\uB85D\uD574\uC57C \uB2E4\uC74C\uC73C\uB85C \uC9C4\uD589\uB429\uB2C8\uB2E4",
505
507
  "context.actionDetail.prCreate": "PR\uC744 \uC0DD\uC131\uD558\uACE0 tasks \uAE30.md\uC758 PR \uC815\uBCF4\uB97C \uB9DE\uCD94\uC138\uC694",
506
508
  "context.actionDetail.prCreateRequiredSequence": "PR 2\uB2E8\uACC4(\uCD08\uC548/\uC2B9\uC778 \uD6C4 \uC0DD\uC131/\uB3D9\uAE30\uD654)\uB97C \uC21C\uC11C\uB300\uB85C \uC644\uB8CC\uD558\uC138\uC694",
507
509
  "context.actionDetail.prCreatePrepareFromDoc": "pr.md \uCD08\uC548\uC744 \uBCF4\uC644\uD558\uACE0 \uC0C1\uD0DC\uB97C Ready\uB85C \uC124\uC815\uD558\uC138\uC694",
@@ -623,8 +625,9 @@ var koMessages = {
623
625
  taskCommitGateReasonDoneCount: "\uCD5C\uC2E0 tasks.md \uCEE4\uBC0B\uC5D0\uC11C DONE \uC804\uD658\uC774 {count}\uAC74 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
624
626
  taskCommitGateReasonMismatchLastDone: "\uCD5C\uADFC \uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uCEE4\uBC0B\uC774 \uC9C1\uC804 \uC644\uB8CC \uD0DC\uC2A4\uD06C\uC640 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4",
625
627
  prLegacyAsk: "tasks.md\uC5D0 PR/PR \uC0C1\uD0DC \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uD15C\uD50C\uB9BF\uC744 \uCD5C\uC2E0 \uD3EC\uB9F7\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD560\uAE4C\uC694? (\uD655\uC778 \uD544\uC694)",
626
- prePrReviewFieldMissing: "tasks.md\uC5D0 `PR \uC804 \uB9AC\uBDF0` \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. `- **PR \uC804 \uB9AC\uBDF0**: Pending | Done` \uD56D\uBAA9\uC744 \uCD94\uAC00\uD558\uACE0 \uB2E4\uC2DC context\uB97C \uC2E4\uD589\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
628
+ prePrReviewFieldMissing: "tasks.md\uC5D0 `PR \uC804 \uB9AC\uBDF0` \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. `- **PR \uC804 \uB9AC\uBDF0**: Pending | Running | Done` \uD56D\uBAA9\uC744 \uCD94\uAC00\uD558\uACE0 \uB2E4\uC2DC context\uB97C \uC2E4\uD589\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
627
629
  prePrReviewRun: "\uCF54\uB4DC \uB9AC\uBDF0 \uC5D0\uC774\uC804\uD2B8\uB97C \uC2E4\uD589\uD574 `spec.md`/`plan.md`/`tasks.md` \uB300\uBE44 \uAD6C\uD604 \uC801\uD569\uC131\uC744 \uAC80\uD1A0\uD558\uACE0, `Summary`/`Feature Intent Summary`/`Implementation Fit`/`Missing Cases`/`Spec Alignment Checked`/`Finding Count`/`Blocking Findings`/`Findings`/`Residual Risks`\uAC00 \uD3EC\uD568\uB41C `review-trace.json`\uC744 \uC0DD\uC131\uD55C \uB4A4 `pre-pr-review`\uB85C \uB9AC\uBDF0 \uACB0\uACFC\uB97C \uAE30\uB85D\uD558\uC138\uC694. `pre-pr-review-run` \uC790\uCCB4\uB294 evidence\uB97C \uC0DD\uC131\uD558\uAC70\uB098 \uC0C1\uD0DC\uB97C \uBC14\uB85C \uB118\uAE30\uC9C0 \uC54A\uC73C\uBA70, \uD604\uC7AC evidence \uC815\uCC45\uC774 \uACBD\uB85C\uB97C \uC694\uAD6C\uD560 \uB54C\uB9CC `--evidence review-trace.json`\uC744 \uD568\uAED8 \uC0AC\uC6A9\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
630
+ prePrReviewRunning: "Pre-PR \uB9AC\uBDF0 handoff\uAC00 \uC774\uBBF8 \uC9C4\uD589 \uC911\uC785\uB2C8\uB2E4. delegated review\uB97C \uB9C8\uCE58\uACE0 `review-trace.json`\uC744 \uB9CC\uB4E0 \uB4A4 `pre-pr-review`\uB85C \uACB0\uACFC\uB97C \uAE30\uB85D\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
628
631
  prePrReviewEvidenceMissing: "tasks.md\uC758 `PR \uC804 \uB9AC\uBDF0 Evidence`\uAC00 \uBE44\uC5B4\uC788\uAC70\uB098 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC2E4\uC81C \uD30C\uC77C \uACBD\uB85C\uC640 `Pre-PR Review Log`(\uB610\uB294 `PR \uC804 \uB9AC\uBDF0 \uB85C\uADF8`)\uC5D0 placeholder\uAC00 \uC544\uB2CC `Summary`/`Feature Intent Summary`/`Implementation Fit`/`Missing Cases`/`Spec Alignment Checked`/`Finding Count`/`Blocking Findings`/`Decision`/`Findings`(\uB610\uB294 \uBA85\uC2DC\uC801 `0 findings`)/`Residual Risks`\uB97C \uAE30\uB85D\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
629
632
  prePrReviewDecisionMissing: "tasks.md\uC758 `PR \uC804 \uB9AC\uBDF0 Decision`\uC774 \uBE44\uC5B4\uC788\uAC70\uB098 \uACB0\uC815 \uD615\uC2DD\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `\uACB0\uC815: ...`(\uB610\uB294 `decision: ...`) \uD615\uC2DD\uC73C\uB85C \uAE30\uB85D\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
630
633
  prePrReviewFixRequired: "\uD604\uC7AC `PR \uC804 \uB9AC\uBDF0 Decision`\uC774 `{decision}`\uC785\uB2C8\uB2E4. PR \uC0DD\uC131 \uB2E8\uACC4\uB85C \uC774\uB3D9\uD558\uAE30 \uC804\uC5D0 pre-PR \uC9C0\uC801\uC0AC\uD56D\uC744 \uCF54\uB4DC\uC5D0 \uBC18\uC601\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
@@ -633,6 +636,7 @@ var koMessages = {
633
636
  prReviewEvidenceMissing: "tasks.md\uC758 `PR \uB9AC\uBDF0 Evidence`\uAC00 \uBE44\uC5B4\uC788\uAC70\uB098 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. `\uC694\uC57D: ...`(\uB610\uB294 `summary: ...`) \uD615\uC2DD\uC73C\uB85C \uAE30\uB85D\uD558\uAC70\uB098 `PR Review Log`(\uB610\uB294 `PR \uB9AC\uBDF0 \uB85C\uADF8`)\uC758 `Summary`/`Decision`\uC774 \uC788\uB294 \uD30C\uC77C \uACBD\uB85C\uB97C \uC9C0\uC815\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
634
637
  prReviewDecisionFieldMissing: "tasks.md\uC5D0 `PR \uB9AC\uBDF0 Decision` \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. `- **PR \uB9AC\uBDF0 Decision**: -` \uD56D\uBAA9\uC744 \uCD94\uAC00\uD558\uACE0 \uB2E4\uC2DC \uC9C4\uD589\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
635
638
  prReviewDecisionMissing: "tasks.md\uC758 `PR \uB9AC\uBDF0 Decision`\uC774 \uBE44\uC5B4\uC788\uAC70\uB098 \uACB0\uC815 \uD615\uC2DD\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `\uACB0\uC815: ...`(\uB610\uB294 `decision: ...`) \uD615\uC2DD\uC73C\uB85C \uAE30\uB85D\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
639
+ prReviewRunning: "PR \uB9AC\uBDF0 handoff\uAC00 \uC774\uBBF8 \uC9C4\uD589 \uC911\uC785\uB2C8\uB2E4. delegated review/fix \uC791\uC5C5\uC744 \uB9C8\uCE5C \uB4A4 `PR \uB9AC\uBDF0 Evidence`\uC640 `PR \uB9AC\uBDF0 Decision`\uC744 \uCD5C\uC2E0\uC73C\uB85C \uAE30\uB85D\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
636
640
  prCreate: "PR \uBCF8\uBB38 \uD15C\uD50C\uB9BF\uC744 \uC0DD\uC131\uD574 \uBCC0\uACBD \uC0AC\uD56D/\uD14C\uC2A4\uD2B8 \uC139\uC158\uC744 \uAC80\uD1A0\xB7\uBCF4\uC644\uD558\uACE0, \uBA85\uC2DC\uC801 \uC9C4\uD589 \uC2B9\uC778(\uB77C\uBCA8 \uC81C\uACF5) \uD6C4 PR\uC744 \uC0DD\uC131\uD558\uC138\uC694. \uC774\uD6C4 tasks.md\uC5D0 PR \uB9C1\uD06C\uB97C \uAE30\uB85D\uD558\uC138\uC694.",
637
641
  prCreatePrepareFromDoc: "`pr.md`\uB97C \uAE30\uC900\uC73C\uB85C PR \uC81C\uBAA9/\uBCF8\uBB38/\uB77C\uBCA8 \uCD08\uC548\uC744 \uBCF4\uC644\uD558\uACE0 \uC9C4\uD589 \uC2B9\uC778\uC744 \uBC1B\uC544 \uD655\uC778 \uD6C4 \uC0C1\uD0DC\uB97C `Ready`\uB85C \uBCC0\uACBD\uD558\uC138\uC694.",
638
642
  prCreateExecuteFromDoc: "`pr.md` \uC0C1\uD0DC\uAC00 `Ready`\uC774\uBA74 PR\uC744 \uC0DD\uC131\uD558\uACE0, \uC0DD\uC131\uB41C PR \uB9C1\uD06C/PR \uC0C1\uD0DC\uB97C `tasks.md`\uC5D0 \uAE30\uB85D\uD558\uC138\uC694. (`pr.md`\uB294 \uC0C1\uD0DC `Ready`\uB9CC \uC720\uC9C0)",
@@ -673,7 +677,7 @@ var koWarnings = {
673
677
  featureScopeSplitSuggested: "Feature \uBC94\uC704\uAC00 \uB2E8\uC77C \uC774\uC288\uB85C \uCC98\uB9AC\uD558\uAE30\uC5D0 \uD07D\uB2C8\uB2E4. (tasks: {taskCount}, decisions \uC904\uC218: {decisionsLineCount}; \uBD84\uD560 \uC81C\uC548 \uAE30\uC900: tasks {taskThreshold}\uAC1C \uB610\uB294 decisions {decisionsThreshold}\uC904) \uD604\uC7AC \uAD8C\uC7A5 \uBD84\uD560: {recommendedIssues}\uAC1C \uC774\uC288 (4\uBD84\uD560 \uD558\uB4DC \uAE30\uC900: tasks >= {recommendFourTaskThreshold} \uB610\uB294 decisions \uC904\uC218 >= {recommendFourDecisionsThreshold}).",
674
678
  legacyTasksDocStatusField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. `\uBB38\uC11C \uC0C1\uD0DC` \uD544\uB4DC(Draft/Review/Approved)\uB97C \uCD94\uAC00\uD574 \uD0DC\uC2A4\uD06C \uC2B9\uC778 \uB2E8\uACC4\uB97C \uD65C\uC131\uD654\uD558\uC138\uC694.",
675
679
  legacyTasksPrFields: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR` \uBC0F `PR \uC0C1\uD0DC` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694.",
676
- legacyTasksPrePrReviewField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR \uC804 \uB9AC\uBDF0` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694. (`- **PR \uC804 \uB9AC\uBDF0**: Pending | Done`)",
680
+ legacyTasksPrePrReviewField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR \uC804 \uB9AC\uBDF0` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694. (`- **PR \uC804 \uB9AC\uBDF0**: Pending | Running | Done`)",
677
681
  legacyTasksPrePrEvidenceField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR \uC804 \uB9AC\uBDF0 Evidence` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694.",
678
682
  legacyTasksPrePrDecisionField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR \uC804 \uB9AC\uBDF0 Decision` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694. (`- **PR \uC804 \uB9AC\uBDF0 Decision**: \uACB0\uC815: ...`)",
679
683
  legacyTasksPrReviewEvidenceField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. \uB9AC\uBDF0 \uB2E8\uACC4 \uC804\uC5D0 `PR \uB9AC\uBDF0 Evidence` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694.",
@@ -690,7 +694,7 @@ var koWarnings = {
690
694
  workflowPrRemoteChangesRequested: "\uC6D0\uACA9 PR\uC5D0\uC11C \uBCC0\uACBD \uC694\uCCAD \uB610\uB294 \uCD94\uAC00 \uB9AC\uBDF0\uAC00 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uCF54\uBA58\uD2B8 \uBC18\uC601 \uD6C4 push\uD558\uACE0 \uB2E4\uC2DC \uD655\uC778\uD558\uC138\uC694.",
691
695
  workflowPrRemoteChecksFailing: "\uC6D0\uACA9 PR \uCCB4\uD06C \uC2E4\uD328\uAC00 {count}\uAC74 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC2E4\uD328 \uC6D0\uC778\uC744 \uD574\uACB0 \uD6C4 \uB2E4\uC2DC \uD655\uC778\uD558\uC138\uC694.",
692
696
  workflowPrRemoteChecksPending: "\uC6D0\uACA9 PR \uCCB4\uD06C \uB300\uAE30\uAC00 {count}\uAC74 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uCCB4\uD06C \uC644\uB8CC \uD6C4 \uB2E4\uC2DC \uD655\uC778\uD558\uC138\uC694.",
693
- workflowPrePrReviewMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0` \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. (tasks.md\uC5D0 `- **PR \uC804 \uB9AC\uBDF0**: Pending | Done`\uC744 \uCD94\uAC00\uD558\uC138\uC694.)",
697
+ workflowPrePrReviewMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0` \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. (tasks.md\uC5D0 `- **PR \uC804 \uB9AC\uBDF0**: Pending | Running | Done`\uC744 \uCD94\uAC00\uD558\uC138\uC694.)",
694
698
  workflowPrePrReviewNotDone: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0`\uAC00 Done\uC774 \uC544\uB2D9\uB2C8\uB2E4. (\uC0AC\uC804 \uCF54\uB4DC\uB9AC\uBDF0 \uD6C4 Done\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.)",
695
699
  workflowPrePrEvidenceMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Evidence`\uAC00 \uBE44\uC5B4\uC788\uAC70\uB098 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. (`Pre-PR Review Log`/`PR \uC804 \uB9AC\uBDF0 \uB85C\uADF8`\uC5D0 placeholder\uAC00 \uC544\uB2CC `Summary`/`Feature Intent Summary`/`Implementation Fit`/`Missing Cases`/`Spec Alignment Checked`/`Finding Count`/`Blocking Findings`/`Decision`/`Findings`(\uB610\uB294 \uBA85\uC2DC\uC801 `0 findings`)/`Residual Risks`\uAC00 \uC788\uB294 \uC2E4\uC81C \uACBD\uB85C\uB97C \uAE30\uB85D\uD558\uC138\uC694.)",
696
700
  workflowPrePrDecisionMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Decision`\uC774 \uBE44\uC5B4\uC788\uAC70\uB098 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. (`decision: approve|changes_requested|blocked ...` \uD615\uC2DD)",
@@ -1020,7 +1024,8 @@ var enContext = {
1020
1024
  "context.suggestionCommandHint": "Reference command: {command}",
1021
1025
  "context.suggestionFinalPrompt": "Recommended labels now: {labels}. Please reply with a format that includes a label token. (e.g. {example}, `A proceed`)",
1022
1026
  "context.autoRunUnavailable": "Auto-run is not available in the current context.",
1023
- "context.autoRunSummary": "Run continuously by config until approval-required categories appear: {categories}",
1027
+ "context.autoRunSummary": "Auto-run can execute now by config until approval-required categories appear: {categories}",
1028
+ "context.autoRunManualBoundary": "Auto-run is configured, but the current step must be handled manually first: {detail}",
1024
1029
  "context.autoRunCommandHint": "Auto-run command (config-based gate): {command}",
1025
1030
  "context.subAgentOrchestrationHint": "The main agent manages overall flow and approvals. If the current state owner is `subagent`, hand execution to a helper agent; if it is `main`, keep it in the main agent.",
1026
1031
  "context.commandDetail.branchCreateWithWorktree": "({scope}) create or reuse worktree {worktree} for branch {branch}",
@@ -1028,9 +1033,9 @@ var enContext = {
1028
1033
  "context.commandDetail.branchCreateGeneric": "({scope}) create or reuse feature branch worktree",
1029
1034
  "context.commandDetail.codeReviewMergeAfterOk": "({scope}) merge PR after explicit OK",
1030
1035
  "context.commandDetail.codeReviewPushFix": "({scope}) push review-fix commits",
1031
- "context.commandDetail.prePrReviewRun": "({scope}) run the pre-PR review via a helper agent/sub-agent and prepare `review-trace.json`",
1036
+ "context.commandDetail.prePrReviewRun": "({scope}) prepare a helper agent/sub-agent pre-PR review handoff; record evidence separately to advance state",
1032
1037
  "context.commandDetail.prePrReviewRecord": "({scope}) record pre-PR review evidence into decisions.md + tasks.md",
1033
- "context.commandDetail.codeReviewRun": "({scope}) check PR review comments, then use a helper agent/sub-agent for the follow-up fixes and evidence summary",
1038
+ "context.commandDetail.codeReviewRun": "({scope}) prepare a helper agent/sub-agent review-fix handoff only; update PR Review Evidence/Decision after the delegated work",
1034
1039
  "context.actionSummary.runDocsCommand": "Run docs command",
1035
1040
  "context.actionSummary.runProjectCommand": "Run project command",
1036
1041
  "context.actionDetail.featureFolder": "Prepare feature folder and baseline docs",
@@ -1049,11 +1054,12 @@ var enContext = {
1049
1054
  "context.actionDetail.issueCreateFromDoc": "Create GitHub Issue from ready issue.md and sync Issue",
1050
1055
  "context.actionDetail.taskExecute": "Proceed with the current task",
1051
1056
  "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}",
1057
+ "context.actionDetail.taskExecuteContinue": "Prepare helper agent/sub-agent handoff and wrap up the in-progress task: {task}. (Share outcome/verification, then mark it DONE)",
1058
+ "context.actionDetail.taskExecuteComplete": "Mark the current task as complete: {task}. (Change DOING to DONE)",
1053
1059
  "context.actionDetail.reviewFixCommit": "Create a review-fix commit with resolved feedback summary",
1054
- "context.actionDetail.prePrReviewRun": "Run the pre-PR review via a helper agent/sub-agent and prepare `review-trace.json`",
1060
+ "context.actionDetail.prePrReviewRun": "Prepare a helper agent/sub-agent pre-PR review handoff; record evidence separately to advance state",
1055
1061
  "context.actionDetail.prePrReviewRecord": "Record pre-PR review evidence into decisions.md and tasks.md",
1056
- "context.actionDetail.codeReviewRun": "Check PR review comments, then use a helper agent/sub-agent for the follow-up fixes and evidence summary",
1062
+ "context.actionDetail.codeReviewRun": "Prepare a helper agent/sub-agent review-fix handoff only; update PR Review Evidence/Decision after the delegated work",
1057
1063
  "context.actionDetail.prCreate": "Create PR and sync PR fields in tasks.md",
1058
1064
  "context.actionDetail.prCreateRequiredSequence": "Complete PR 2-step flow: prepare draft + OK, then create and sync",
1059
1065
  "context.actionDetail.prCreatePrepareFromDoc": "Refine pr.md draft and set Status to Ready",
@@ -1175,8 +1181,9 @@ var enMessages = {
1175
1181
  taskCommitGateReasonDoneCount: "DONE transitions detected in the latest tasks.md commit ({count})",
1176
1182
  taskCommitGateReasonMismatchLastDone: "The latest project code commit does not match the last completed task",
1177
1183
  prLegacyAsk: "tasks.md is missing PR/PR Status fields. Update to the latest template format? (CHECK required)",
1178
- prePrReviewFieldMissing: "tasks.md is missing the `Pre-PR Review` field. Add `- **Pre-PR Review**: Pending | Done` and run context again. (CHECK required)",
1184
+ prePrReviewFieldMissing: "tasks.md is missing the `Pre-PR Review` field. Add `- **Pre-PR Review**: Pending | Running | Done` and run context again. (CHECK required)",
1179
1185
  prePrReviewRun: "Run the code review agent, compare the implementation against `spec.md`/`plan.md`/`tasks.md`, and generate `review-trace.json` with `Summary`, `Feature Intent Summary`, `Implementation Fit`, `Missing Cases`, `Spec Alignment Checked`, `Finding Count`, `Blocking Findings`, `Findings`, and `Residual Risks`. Then record findings with `pre-pr-review`; `pre-pr-review-run` itself does not generate evidence or advance state, and you should use `--evidence review-trace.json` only when the active evidence policy requires a path. (CHECK required)",
1186
+ prePrReviewRunning: "Pre-PR review handoff is already in progress. Finish the delegated review, generate `review-trace.json`, then record the result with `pre-pr-review`. (CHECK required)",
1180
1187
  prePrReviewEvidenceMissing: "tasks.md `Pre-PR Evidence` is empty/invalid. Point to a real file and include a `Pre-PR Review Log` section with non-placeholder `Summary`, `Feature Intent Summary`, `Implementation Fit`, `Missing Cases`, `Spec Alignment Checked`, `Finding Count`, `Blocking Findings`, `Decision`, `Findings` (or explicit `0 findings`), and `Residual Risks`. (CHECK required)",
1181
1188
  prePrReviewDecisionMissing: "tasks.md `Pre-PR Decision` is empty/placeholder or missing decision format. Record it as `decision: ...` (or `\uACB0\uC815: ...`). (CHECK required)",
1182
1189
  prePrReviewFixRequired: "Current `Pre-PR Decision` is `{decision}`. Apply the requested fixes from pre-PR findings before moving to PR creation. (CHECK required)",
@@ -1185,6 +1192,7 @@ var enMessages = {
1185
1192
  prReviewEvidenceMissing: "tasks.md `PR Review Evidence` is empty/invalid. Use `summary: ...` (or `\uC694\uC57D: ...`), or point to a file containing `PR Review Log` with non-placeholder `Summary` and `Decision`. (CHECK required)",
1186
1193
  prReviewDecisionFieldMissing: "tasks.md is missing the `PR Review Decision` field. Add `- **PR Review Decision**: -` and continue. (CHECK required)",
1187
1194
  prReviewDecisionMissing: "tasks.md `PR Review Decision` is empty/placeholder or missing decision format. Record it as `decision: ...` (or `\uACB0\uC815: ...`). (CHECK required)",
1195
+ prReviewRunning: "PR review handoff is already in progress. Complete the delegated review/fix work, then update `PR Review Evidence` and `PR Review Decision`. (CHECK required)",
1188
1196
  prCreate: "Generate the PR body template, refine changes/tests sections, get explicit progress approval (label), create the PR, then record the PR link in tasks.md.",
1189
1197
  prCreatePrepareFromDoc: "Use `pr.md` to refine PR title/body/labels draft, get explicit progress approval, then set status to `Ready`.",
1190
1198
  prCreateExecuteFromDoc: "When `pr.md` status is `Ready`, create the PR and record the PR link/status in `tasks.md`. (Keep only `pr.md` status as `Ready`.)",
@@ -1225,7 +1233,7 @@ var enWarnings = {
1225
1233
  featureScopeSplitSuggested: "Feature scope may be too large for one issue (tasks: {taskCount}, decisions lines: {decisionsLineCount}; suggest split at {taskThreshold} tasks or {decisionsThreshold} decision lines). Current recommendation: split into {recommendedIssues} issue units (hard 4-way rule: tasks >= {recommendFourTaskThreshold} or decisions lines >= {recommendFourDecisionsThreshold}).",
1226
1234
  legacyTasksDocStatusField: "Legacy tasks.md format detected. Add a `Doc Status` field (Draft/Review/Approved) to enable tasks approval.",
1227
1235
  legacyTasksPrFields: "Legacy tasks.md format detected. Add `PR` and `PR Status` fields before PR steps.",
1228
- legacyTasksPrePrReviewField: "Legacy tasks.md format detected. Add `Pre-PR Review` before PR steps. (`- **Pre-PR Review**: Pending | Done`)",
1236
+ legacyTasksPrePrReviewField: "Legacy tasks.md format detected. Add `Pre-PR Review` before PR steps. (`- **Pre-PR Review**: Pending | Running | Done`)",
1229
1237
  legacyTasksPrePrEvidenceField: "Legacy tasks.md format detected. Add `Pre-PR Evidence` before PR steps.",
1230
1238
  legacyTasksPrePrDecisionField: "Legacy tasks.md format detected. Add `Pre-PR Decision` before PR steps. (`- **Pre-PR Decision**: decision: ...`)",
1231
1239
  legacyTasksPrReviewEvidenceField: "Legacy tasks.md format detected. Add `PR Review Evidence` before review iteration.",
@@ -1242,7 +1250,7 @@ var enWarnings = {
1242
1250
  workflowPrRemoteChangesRequested: "Remote PR shows changes requested or additional review required. Address comments, push, then re-check.",
1243
1251
  workflowPrRemoteChecksFailing: "Remote PR has {count} failing check(s). Fix failures, then re-check.",
1244
1252
  workflowPrRemoteChecksPending: "Remote PR has {count} pending check(s). Wait for checks to complete, then re-check.",
1245
- workflowPrePrReviewMissing: "Implementation is done but `Pre-PR Review` is missing. (Add `- **Pre-PR Review**: Pending | Done` in tasks.md.)",
1253
+ workflowPrePrReviewMissing: "Implementation is done but `Pre-PR Review` is missing. (Add `- **Pre-PR Review**: Pending | Running | Done` in tasks.md.)",
1246
1254
  workflowPrePrReviewNotDone: "Implementation is done but `Pre-PR Review` is not Done. (Run pre-PR review, then update it to Done.)",
1247
1255
  workflowPrePrEvidenceMissing: "Implementation is done but `Pre-PR Evidence` is empty/invalid. (Point to an existing file and include `Pre-PR Review Log` with non-placeholder `Summary`, `Feature Intent Summary`, `Implementation Fit`, `Missing Cases`, `Spec Alignment Checked`, `Finding Count`, `Blocking Findings`, `Decision`, `Findings` (or explicit `0 findings`), and `Residual Risks`.)",
1248
1256
  workflowPrePrDecisionMissing: "Implementation is done but `Pre-PR Decision` is empty/invalid. (Use `decision: approve|changes_requested|blocked ...`.)",
@@ -3553,6 +3561,7 @@ function resolvePrePrReviewPolicy(workflow) {
3553
3561
  function getPrePrReviewPrompt(lang, skills, fallbackText) {
3554
3562
  if (lang === "ko") {
3555
3563
  return `PR \uC0DD\uC131 \uC804 \uC0AC\uC804 \uCF54\uB4DC\uB9AC\uBDF0\uB97C \uC9C4\uD589\uD558\uC138\uC694.
3564
+ 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
3565
  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
3566
  2. \uB9AC\uBDF0 \uBC94\uC704\uB97C \uBD84\uB9AC\uD574 \uD655\uC778\uD558\uC138\uC694.
3558
3567
  - main \uAE30\uC900: 'git diff --name-only $(git merge-base HEAD origin/main)..HEAD'
@@ -3561,11 +3570,14 @@ function getPrePrReviewPrompt(lang, skills, fallbackText) {
3561
3570
  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
3571
  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
3572
  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.`;
3573
+ 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.
3574
+ 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.
3575
+ 9. \uC2E4\uD589\uD55C \uBA85\uB839\uC774 \uC788\uC73C\uBA74 \`commandsExecuted\`\uC5D0 \uAE30\uB85D\uD558\uC138\uC694.
3576
+ 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.
3577
+ 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
3578
  }
3568
3579
  return `Conduct a pre-PR code review.
3580
+ 0. Reuse the existing helper/sub-agent for this feature review if one already exists. Default to a single helper agent.
3569
3581
  1. Read \`spec.md\`, \`plan.md\`, and \`tasks.md\` first, then summarize the feature goal, scope, and done criteria.
3570
3582
  2. Split and check the review scope.
3571
3583
  - Main scope: 'git diff --name-only $(git merge-base HEAD origin/main)..HEAD'
@@ -3574,15 +3586,17 @@ function getPrePrReviewPrompt(lang, skills, fallbackText) {
3574
3586
  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
3587
  5. The baseline is '${fallbackText}'. Always perform the 'Pre-PR Core Checklist' section of the 'create-pr' document.
3576
3588
  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.`;
3589
+ 7. Run extra audit/targeted verification only when the review truly needs more evidence. Spawn additional helper agents only when necessary.
3590
+ 8. If helper-agent quota is exhausted, continue the review in the main agent and just keep the evidence consistent.
3591
+ 9. Record commands in \`commandsExecuted\` only when you actually ran them.
3592
+ 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.
3593
+ 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
3594
  }
3581
3595
  function getCodeReviewPrompt(lang) {
3582
3596
  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.`;
3597
+ 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
3598
  }
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.`;
3599
+ 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
3600
  }
3587
3601
 
3588
3602
  // src/utils/context/steps.ts
@@ -3621,7 +3635,7 @@ function isPrePrReviewSatisfied(feature, prePrReviewPolicy) {
3621
3635
  return true;
3622
3636
  }
3623
3637
  function isFeatureDone(feature, workflowPolicy, prePrReviewPolicy) {
3624
- return feature.specStatus === "Approved" && feature.planStatus === "Approved" && !feature.git.docsHasUncommittedChanges && !feature.git.projectHasUncommittedChanges && feature.docs.tasksExists && feature.tasks.total > 0 && feature.tasks.total === feature.tasks.done && isCompletionChecklistDone(feature) && isTasksDocApproved(feature) && (!workflowPolicy.requireIssue || !!feature.issueNumber) && (!workflowPolicy.requirePr || isPrMetadataConfigured(feature) && !!feature.pr.link) && (!workflowPolicy.requireMerge || feature.pr.status === "Approved") && isPrePrReviewSatisfied(feature, prePrReviewPolicy);
3638
+ return feature.specStatus === "Approved" && feature.planStatus === "Approved" && !feature.git.docsHasCommitRequiredChanges && !feature.git.projectHasUncommittedChanges && feature.docs.tasksExists && feature.tasks.total > 0 && feature.tasks.total === feature.tasks.done && isCompletionChecklistDone(feature) && isTasksDocApproved(feature) && (!workflowPolicy.requireIssue || !!feature.issueNumber) && (!workflowPolicy.requirePr || isPrMetadataConfigured(feature) && !!feature.pr.link) && (!workflowPolicy.requireMerge || feature.pr.status === "Approved") && isPrePrReviewSatisfied(feature, prePrReviewPolicy);
3625
3639
  }
3626
3640
  function getPrReviewRemoteBlockReasons(feature, lang) {
3627
3641
  const remote = feature.pr.remote;
@@ -3694,6 +3708,13 @@ function buildTaskRunCommandArgs(feature, taskId) {
3694
3708
  }
3695
3709
  return commandArgs;
3696
3710
  }
3711
+ function buildTaskCompleteCommandArgs(feature, taskId) {
3712
+ const commandArgs = ["task-complete", feature.folderName, "--task", taskId];
3713
+ if (feature.type && feature.type !== "single") {
3714
+ commandArgs.push("--component", feature.type);
3715
+ }
3716
+ return commandArgs;
3717
+ }
3697
3718
  function buildCodeReviewRunCommandArgs(feature) {
3698
3719
  const commandArgs = ["code-review-run", feature.folderName];
3699
3720
  if (feature.type && feature.type !== "single") {
@@ -3849,7 +3870,7 @@ function countDoneTransitionsInLatestTasksCommit(ctx, feature) {
3849
3870
  if (!diff.trim()) return 0;
3850
3871
  const removedByTask = /* @__PURE__ */ new Map();
3851
3872
  const addedByTask = /* @__PURE__ */ new Map();
3852
- const parseTaskLine2 = (line) => {
3873
+ const parseTaskLine3 = (line) => {
3853
3874
  const match = line.match(
3854
3875
  /^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]\s+(.+?)\s*$/i
3855
3876
  );
@@ -3864,7 +3885,7 @@ function countDoneTransitionsInLatestTasksCommit(ctx, feature) {
3864
3885
  for (const line of diff.split("\n")) {
3865
3886
  if (line.startsWith("---") || line.startsWith("+++")) continue;
3866
3887
  if (line.startsWith("-")) {
3867
- const parsed = parseTaskLine2(line.slice(1));
3888
+ const parsed = parseTaskLine3(line.slice(1));
3868
3889
  if (!parsed) continue;
3869
3890
  const existing = removedByTask.get(parsed.key) || /* @__PURE__ */ new Set();
3870
3891
  existing.add(parsed.status);
@@ -3872,7 +3893,7 @@ function countDoneTransitionsInLatestTasksCommit(ctx, feature) {
3872
3893
  continue;
3873
3894
  }
3874
3895
  if (line.startsWith("+")) {
3875
- const parsed = parseTaskLine2(line.slice(1));
3896
+ const parsed = parseTaskLine3(line.slice(1));
3876
3897
  if (!parsed) continue;
3877
3898
  const existing = addedByTask.get(parsed.key) || /* @__PURE__ */ new Set();
3878
3899
  existing.add(parsed.status);
@@ -3953,7 +3974,7 @@ function getStepDefinitions(ctx) {
3953
3974
  const isTaskExecuteCurrent = (f) => f.docs.tasksExists && f.tasks.total > 0 && (f.tasks.done < f.tasks.total || !isCompletionChecklistDone(f)) && isTasksDocApproved(f) && (!workflowPolicy.requireBranch || f.git.onExpectedBranch || f.tasks.done === f.tasks.total);
3954
3975
  const isTaskExecuteWorktreeBlocked = (f) => isTaskExecuteCurrent(f) && workflowPolicy.requireWorktree && f.tasks.done < f.tasks.total && !!f.issueNumber && !f.git.projectInManagedWorktree;
3955
3976
  const isTaskExecuteFinalize = (f) => isTaskExecuteCurrent(f) && f.tasks.total === f.tasks.done && !isCompletionChecklistDone(f);
3956
- const isTaskExecuteCommitPending = (f) => isTaskExecuteCurrent(f) && (isTaskExecuteFinalize(f) && (f.git.docsHasUncommittedChanges || f.git.projectHasUncommittedChanges) || !!f.nextTodoTask && (f.git.docsHasUncommittedChanges || f.git.projectHasUncommittedChanges));
3977
+ const isTaskExecuteCommitPending = (f) => isTaskExecuteCurrent(f) && (isTaskExecuteFinalize(f) && (f.git.docsHasCommitRequiredChanges || f.git.projectHasUncommittedChanges) || !!f.nextTodoTask && (f.git.docsHasCommitRequiredChanges || f.git.projectHasUncommittedChanges));
3957
3978
  const isTaskExecuteStrictGateBlocked = (f) => {
3958
3979
  if (!isTaskExecuteCurrent(f) || !f.nextTodoTask) return false;
3959
3980
  if (taskCommitGatePolicy === "off" || !f.lastDoneTask || !f.git.docsGitCwd) {
@@ -4041,7 +4062,7 @@ function getStepDefinitions(ctx) {
4041
4062
  ];
4042
4063
  };
4043
4064
  const getTaskExecuteFinalizeActions = (f) => {
4044
- if (f.git.docsHasUncommittedChanges || f.git.projectHasUncommittedChanges) {
4065
+ if (f.git.docsHasCommitRequiredChanges || f.git.projectHasUncommittedChanges) {
4045
4066
  return getTaskExecuteCommitPendingActions(f);
4046
4067
  }
4047
4068
  const actions = [
@@ -4073,14 +4094,14 @@ function getStepDefinitions(ctx) {
4073
4094
  operationType: "local",
4074
4095
  requiresUserCheck: true,
4075
4096
  taskExecutePhase: "complete",
4076
- uiDetailKey: "context.actionDetail.taskExecuteContinue",
4097
+ uiDetailKey: "context.actionDetail.taskExecuteComplete",
4077
4098
  uiDetailParams: {
4078
4099
  task: resolveTaskUiLabel(f.activeTask)
4079
4100
  },
4080
4101
  scope: "docs",
4081
4102
  cwd: f.git.docsGitCwd,
4082
4103
  cmd: buildSelfCliCommand(
4083
- buildTaskRunCommandArgs(
4104
+ buildTaskCompleteCommandArgs(
4084
4105
  f,
4085
4106
  f.activeTask?.id || `T-${f.folderName}-active`
4086
4107
  )
@@ -4088,7 +4109,7 @@ function getStepDefinitions(ctx) {
4088
4109
  }
4089
4110
  ];
4090
4111
  const getTaskExecuteCommitPendingActions = (f) => {
4091
- if (f.git.docsHasUncommittedChanges) {
4112
+ if (f.git.docsHasCommitRequiredChanges) {
4092
4113
  return [
4093
4114
  {
4094
4115
  type: "command",
@@ -4213,10 +4234,10 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
4213
4234
  }
4214
4235
  ];
4215
4236
  };
4216
- const isPostTaskSyncCurrent = (f) => isImplementationDone(f) && (f.git.docsHasUncommittedChanges || f.git.projectHasUncommittedChanges);
4217
- const isPostTaskSyncDocs = (f) => isPostTaskSyncCurrent(f) && f.git.docsHasUncommittedChanges;
4218
- const isPostTaskSyncReviewFix = (f) => isPostTaskSyncCurrent(f) && !f.git.docsHasUncommittedChanges && f.git.projectHasUncommittedChanges && (isReviewIterationPhase(f, workflowPolicy) || isPrePrFixIterationPhase(f, workflowPolicy, prePrReviewPolicy));
4219
- const isPostTaskSyncProject = (f) => isPostTaskSyncCurrent(f) && !f.git.docsHasUncommittedChanges && f.git.projectHasUncommittedChanges && !isPostTaskSyncReviewFix(f);
4237
+ const isPostTaskSyncCurrent = (f) => isImplementationDone(f) && (f.git.docsHasCommitRequiredChanges || f.git.projectHasUncommittedChanges);
4238
+ const isPostTaskSyncDocs = (f) => isPostTaskSyncCurrent(f) && f.git.docsHasCommitRequiredChanges;
4239
+ const isPostTaskSyncReviewFix = (f) => isPostTaskSyncCurrent(f) && !f.git.docsHasCommitRequiredChanges && f.git.projectHasUncommittedChanges && (isReviewIterationPhase(f, workflowPolicy) || isPrePrFixIterationPhase(f, workflowPolicy, prePrReviewPolicy));
4240
+ const isPostTaskSyncProject = (f) => isPostTaskSyncCurrent(f) && !f.git.docsHasCommitRequiredChanges && f.git.projectHasUncommittedChanges && !isPostTaskSyncReviewFix(f);
4220
4241
  const getPostTaskSyncDocsActions = (f) => [
4221
4242
  {
4222
4243
  type: "command",
@@ -4288,10 +4309,11 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
4288
4309
  }
4289
4310
  ];
4290
4311
  };
4291
- const isPrePrReviewCurrent = (f) => prePrReviewPolicy.enabled && workflowPolicy.requirePr && f.docs.tasksExists && f.tasks.total > 0 && f.tasks.total === f.tasks.done && isCompletionChecklistDone(f) && !f.git.docsHasUncommittedChanges && !f.git.projectHasUncommittedChanges && (!isPrMetadataConfigured(f) || !f.pr.link) && !isPrePrReviewSatisfied(f, prePrReviewPolicy);
4312
+ const isPrePrReviewCurrent = (f) => prePrReviewPolicy.enabled && workflowPolicy.requirePr && f.docs.tasksExists && f.tasks.total > 0 && f.tasks.total === f.tasks.done && isCompletionChecklistDone(f) && !f.git.docsHasCommitRequiredChanges && !f.git.projectHasUncommittedChanges && (!isPrMetadataConfigured(f) || !f.pr.link) && !isPrePrReviewSatisfied(f, prePrReviewPolicy);
4292
4313
  const isPrePrReviewMetadataMissing = (f) => isPrePrReviewCurrent(f) && !f.docs.prePrReviewFieldExists;
4293
4314
  const isPrePrReviewFixRequired = (f) => isPrePrReviewCurrent(f) && !!f.prePrReview.decisionOutcome && f.prePrReview.decisionOutcome !== "approve";
4294
- const isPrePrReviewRun = (f) => isPrePrReviewCurrent(f) && f.docs.prePrReviewFieldExists && !isPrePrReviewFixRequired(f) && !resolvePrePrReviewEvidencePath(f) && (prePrReviewPolicy.evidenceMode === "path_required" || prePrReviewPolicy.enforceExecutionEvidence);
4315
+ const isPrePrReviewRun = (f) => isPrePrReviewCurrent(f) && f.docs.prePrReviewFieldExists && !isPrePrReviewFixRequired(f) && f.prePrReview.status !== "Running" && !resolvePrePrReviewEvidencePath(f) && (prePrReviewPolicy.evidenceMode === "path_required" || prePrReviewPolicy.enforceExecutionEvidence);
4316
+ const isPrePrReviewRunning = (f) => isPrePrReviewCurrent(f) && f.docs.prePrReviewFieldExists && !isPrePrReviewFixRequired(f) && f.prePrReview.status === "Running" && !resolvePrePrReviewEvidencePath(f) && (prePrReviewPolicy.evidenceMode === "path_required" || prePrReviewPolicy.enforceExecutionEvidence);
4295
4317
  const isPrePrReviewRecord = (f) => isPrePrReviewCurrent(f) && f.docs.prePrReviewFieldExists && !isPrePrReviewFixRequired(f) && (!!resolvePrePrReviewEvidencePath(f) || prePrReviewPolicy.evidenceMode === "any" && !prePrReviewPolicy.enforceExecutionEvidence);
4296
4318
  const getPrePrReviewMetadataActions = () => [
4297
4319
  {
@@ -4336,6 +4358,14 @@ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
4336
4358
  cmd: buildSelfCliCommand(buildPrePrReviewRunCommandArgs(f))
4337
4359
  }
4338
4360
  ];
4361
+ const getPrePrReviewRunningActions = () => [
4362
+ {
4363
+ type: "instruction",
4364
+ category: "pre_pr_review_run",
4365
+ requiresUserCheck: false,
4366
+ message: tr(lang, "messages", "prePrReviewRunning")
4367
+ }
4368
+ ];
4339
4369
  const getPrePrReviewRecordActions = (f) => {
4340
4370
  const evidencePath = resolvePrePrReviewEvidencePath(f);
4341
4371
  return [
@@ -4346,7 +4376,9 @@ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
4346
4376
  requiresUserCheck: true,
4347
4377
  scope: "docs",
4348
4378
  cwd: f.git.docsGitCwd,
4349
- cmd: buildSelfCliCommand(buildPrePrReviewCommandArgs(f, evidencePath))
4379
+ cmd: buildSelfCliCommand(
4380
+ buildPrePrReviewCommandArgs(f, evidencePath ?? void 0)
4381
+ )
4350
4382
  }
4351
4383
  ];
4352
4384
  };
@@ -4362,7 +4394,8 @@ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
4362
4394
  const isCodeReviewNeedEvidence = (f) => isCodeReviewCurrent(f) && f.pr.status === "Review" && workflowPolicy.requireReview && !isCodeReviewNeedEvidenceField(f) && !f.prReview.evidenceProvided;
4363
4395
  const isCodeReviewNeedDecisionField = (f) => isCodeReviewCurrent(f) && f.pr.status === "Review" && workflowPolicy.requireReview && !isCodeReviewNeedEvidenceField(f) && f.prReview.evidenceProvided && !f.docs.prReviewDecisionFieldExists;
4364
4396
  const isCodeReviewNeedDecision = (f) => isCodeReviewCurrent(f) && f.pr.status === "Review" && workflowPolicy.requireReview && !isCodeReviewNeedEvidenceField(f) && f.prReview.evidenceProvided && !isCodeReviewNeedDecisionField(f) && !f.prReview.decisionProvided;
4365
- const isCodeReviewRun = (f) => isCodeReviewCurrent(f) && f.pr.status === "Review" && workflowPolicy.requireReview && (f.git.projectBranchAhead || 0) === 0 && !isCodeReviewNeedEvidenceField(f) && !f.prReview.evidenceProvided && !f.prReview.decisionProvided;
4397
+ const isCodeReviewRun = (f) => isCodeReviewCurrent(f) && f.pr.status === "Review" && workflowPolicy.requireReview && (f.git.projectBranchAhead || 0) === 0 && f.prReview.status !== "Running" && !isCodeReviewNeedEvidenceField(f) && !f.prReview.evidenceProvided && !f.prReview.decisionProvided;
4398
+ const isCodeReviewRunning = (f) => isCodeReviewCurrent(f) && f.pr.status === "Review" && workflowPolicy.requireReview && (f.git.projectBranchAhead || 0) === 0 && f.prReview.status === "Running" && !isCodeReviewNeedEvidenceField(f) && !f.prReview.evidenceProvided && !f.prReview.decisionProvided;
4366
4399
  const isCodeReviewFinalize = (f) => isCodeReviewCurrent(f) && !isCodeReviewSyncApproved(f) && (!workflowPolicy.requireReview || f.pr.status === "Review" && !isCodeReviewNeedEvidenceField(f) && !isCodeReviewNeedEvidence(f) && !isCodeReviewNeedDecisionField(f) && !isCodeReviewNeedDecision(f) && f.prReview.evidenceProvided && f.prReview.decisionProvided);
4367
4400
  const isCodeReviewRequestReview = (f) => isCodeReviewCurrent(f) && !!f.pr.status && f.pr.status !== "Review";
4368
4401
  const getCodeReviewRunActions = (f) => [
@@ -4376,6 +4409,14 @@ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
4376
4409
  cmd: buildSelfCliCommand(buildCodeReviewRunCommandArgs(f))
4377
4410
  }
4378
4411
  ];
4412
+ const getCodeReviewRunningActions = () => [
4413
+ {
4414
+ type: "instruction",
4415
+ category: "code_review_run",
4416
+ requiresUserCheck: false,
4417
+ message: tr(lang, "messages", "prReviewRunning")
4418
+ }
4419
+ ];
4379
4420
  const getCodeReviewFinalizeActions = (f) => {
4380
4421
  const remoteBlockReasons = getPrReviewRemoteBlockReasons(f, lang);
4381
4422
  const remoteUnavailable = workflowPolicy.mode === "github" && !!f.pr.link && (!f.pr.remote || !f.pr.remote.available);
@@ -4599,7 +4640,7 @@ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
4599
4640
  done: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" && isTasksDocApproved(f) && f.git.docsEverCommitted
4600
4641
  },
4601
4642
  current: {
4602
- when: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" && isTasksDocApproved(f) && !f.activeTask && !f.git.docsEverCommitted && f.git.docsHasUncommittedChanges,
4643
+ when: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" && isTasksDocApproved(f) && !f.activeTask && !f.git.docsEverCommitted && f.git.docsHasCommitRequiredChanges,
4603
4644
  actions: (f) => {
4604
4645
  if (f.issueNumber) {
4605
4646
  return [
@@ -4747,9 +4788,9 @@ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
4747
4788
  actions: (f) => getTaskExecuteFinalizeActions(f)
4748
4789
  },
4749
4790
  {
4750
- id: "task_running",
4751
- phase: "running",
4752
- owner: "subagent",
4791
+ id: "task_complete",
4792
+ phase: "finalize",
4793
+ owner: "main",
4753
4794
  category: "task_execute",
4754
4795
  when: (f) => isTaskExecuteCurrent(f) && !!f.activeTask,
4755
4796
  actions: (f) => getTaskExecuteRunningActions(f)
@@ -4794,7 +4835,7 @@ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
4794
4835
  step: 11,
4795
4836
  name: tr(lang, "steps", "docsCommitSync"),
4796
4837
  checklist: {
4797
- done: (f) => !f.git.docsHasUncommittedChanges && !f.git.projectHasUncommittedChanges
4838
+ done: (f) => !f.git.docsHasCommitRequiredChanges && !f.git.projectHasUncommittedChanges
4798
4839
  },
4799
4840
  substates: [
4800
4841
  {
@@ -4854,6 +4895,14 @@ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
4854
4895
  when: (f) => isPrePrReviewRun(f),
4855
4896
  actions: (f) => getPrePrReviewRunActions(f)
4856
4897
  },
4898
+ {
4899
+ id: "pre_pr_review_running",
4900
+ phase: "running",
4901
+ owner: "subagent",
4902
+ category: "pre_pr_review_run",
4903
+ when: (f) => isPrePrReviewRunning(f),
4904
+ actions: () => getPrePrReviewRunningActions()
4905
+ },
4857
4906
  {
4858
4907
  id: "pre_pr_review_record",
4859
4908
  phase: "record",
@@ -5006,6 +5055,14 @@ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
5006
5055
  when: (f) => isCodeReviewRun(f),
5007
5056
  actions: (f) => getCodeReviewRunActions(f)
5008
5057
  },
5058
+ {
5059
+ id: "code_review_running",
5060
+ phase: "running",
5061
+ owner: "subagent",
5062
+ category: "code_review_run",
5063
+ when: (f) => isCodeReviewRunning(f),
5064
+ actions: () => getCodeReviewRunningActions()
5065
+ },
5009
5066
  {
5010
5067
  id: "code_review_need_evidence",
5011
5068
  phase: "blocked",
@@ -5206,7 +5263,7 @@ function applyApprovalPolicy(step, actions, approval, currentSubstatePhase) {
5206
5263
  function applyTaskExecutePhaseCheck(action, requiresUserCheck, policy, explicitlyRequired = false, currentSubstatePhase) {
5207
5264
  if (policy !== "start_only") return requiresUserCheck;
5208
5265
  if (action.category !== "task_execute") return requiresUserCheck;
5209
- const isCompletionPhase = currentSubstatePhase === "running" || !currentSubstatePhase && action.taskExecutePhase === "complete";
5266
+ const isCompletionPhase = currentSubstatePhase === "running" || currentSubstatePhase === "finalize" || !currentSubstatePhase && action.taskExecutePhase === "complete";
5210
5267
  if (!isCompletionPhase) return requiresUserCheck;
5211
5268
  if (explicitlyRequired) return requiresUserCheck;
5212
5269
  return false;
@@ -5681,14 +5738,78 @@ function parsePrReviewStatus(value) {
5681
5738
  if (normalized === "review") return "Review";
5682
5739
  return "Approved";
5683
5740
  }
5684
- function parsePrePrReviewStatus(value) {
5741
+ function parseReviewRunStatus(value) {
5685
5742
  if (!value) return void 0;
5686
5743
  const trimmed = value.trim();
5687
5744
  if (!trimmed || trimmed.includes("|")) return void 0;
5688
5745
  if (/^(done|complete|completed)$/i.test(trimmed)) return "Done";
5746
+ if (/^running$/i.test(trimmed)) return "Running";
5689
5747
  if (/^pending$/i.test(trimmed)) return "Pending";
5690
5748
  return void 0;
5691
5749
  }
5750
+ function parseGitStatusPaths(status) {
5751
+ return status.split("\n").map((line) => line.trimEnd()).filter(Boolean).map((line) => {
5752
+ const rawPath = line.slice(3).trim();
5753
+ const renamedPath = rawPath.includes(" -> ") ? rawPath.split(" -> ").pop() || rawPath : rawPath;
5754
+ return renamedPath.replace(/\\/g, "/");
5755
+ }).filter(Boolean);
5756
+ }
5757
+ function getGitDiffAgainstHead(ctx, cwd, relativePath) {
5758
+ try {
5759
+ return ctx.cmd.execFileSync("git", ["diff", "--unified=0", "HEAD", "--", relativePath], {
5760
+ cwd,
5761
+ encoding: "utf-8",
5762
+ stdio: ["ignore", "pipe", "pipe"]
5763
+ }).toString();
5764
+ } catch {
5765
+ return void 0;
5766
+ }
5767
+ }
5768
+ function getGitPathPrefix(ctx, cwd) {
5769
+ try {
5770
+ return ctx.cmd.execFileSync("git", ["rev-parse", "--show-prefix"], {
5771
+ cwd,
5772
+ encoding: "utf-8",
5773
+ stdio: ["ignore", "pipe", "pipe"]
5774
+ }).toString().trim();
5775
+ } catch {
5776
+ return "";
5777
+ }
5778
+ }
5779
+ function isAllowedReviewRunStatusRemoval(line) {
5780
+ return /^\s*-\s+\*\*(?:Pre-PR Review|PR 전 리뷰|PR Review|PR 리뷰)\*\*:\s*(?:Pending|-)\s*$/u.test(
5781
+ line
5782
+ );
5783
+ }
5784
+ function isAllowedReviewRunStatusAddition(line) {
5785
+ return /^\s*-\s+\*\*(?:Pre-PR Review|PR 전 리뷰|PR Review|PR 리뷰)\*\*:\s*Running\s*$/u.test(
5786
+ line
5787
+ );
5788
+ }
5789
+ function isReviewRunStateOnlyDocsChange(ctx, docsGitCwd, featurePathFromDocs, docsStatus) {
5790
+ if (!docsStatus) return false;
5791
+ const changedPaths = parseGitStatusPaths(docsStatus);
5792
+ const tasksPath = `${featurePathFromDocs.replace(/\\/g, "/")}/tasks.md`;
5793
+ const gitPrefix = getGitPathPrefix(ctx, docsGitCwd).replace(/\\/g, "/");
5794
+ const prefixedTasksPath = `${gitPrefix}${tasksPath}`.replace(/^\.\/+/, "");
5795
+ const acceptedTasksPaths = /* @__PURE__ */ new Set([tasksPath, prefixedTasksPath]);
5796
+ if (changedPaths.length === 0 || changedPaths.some((entry) => !acceptedTasksPaths.has(entry))) {
5797
+ return false;
5798
+ }
5799
+ const diff = getGitDiffAgainstHead(ctx, docsGitCwd, tasksPath);
5800
+ if (!diff) return false;
5801
+ const changedLines = diff.split("\n").filter(
5802
+ (line) => (line.startsWith("+") || line.startsWith("-")) && !line.startsWith("+++") && !line.startsWith("---")
5803
+ );
5804
+ if (changedLines.length === 0) return false;
5805
+ return changedLines.every((line) => {
5806
+ const payload = line.slice(1);
5807
+ if (line.startsWith("+")) {
5808
+ return isAllowedReviewRunStatusAddition(payload);
5809
+ }
5810
+ return isAllowedReviewRunStatusRemoval(payload);
5811
+ });
5812
+ }
5692
5813
  function isPlaceholderReviewEvidence(value) {
5693
5814
  if (!value) return true;
5694
5815
  const trimmed = value.trim();
@@ -6416,6 +6537,7 @@ async function parseFeature(ctx, featurePath, type, context, options) {
6416
6537
  let prePrDecision;
6417
6538
  let prePrDecisionOutcome;
6418
6539
  let prePrDecisionProvided = false;
6540
+ let prReviewRunStatus;
6419
6541
  let prReviewEvidence;
6420
6542
  let prReviewEvidenceProvided = false;
6421
6543
  let prReviewDecision;
@@ -6485,7 +6607,12 @@ async function parseFeature(ctx, featurePath, type, context, options) {
6485
6607
  "PR \uC804 \uB9AC\uBDF0",
6486
6608
  "Pre-PR Review"
6487
6609
  ]);
6488
- prePrReviewStatus = parsePrePrReviewStatus(prePrReviewValue);
6610
+ prePrReviewStatus = parseReviewRunStatus(prePrReviewValue);
6611
+ const prReviewStatusValue = extractFirstSpecValue(content, [
6612
+ "PR \uB9AC\uBDF0",
6613
+ "PR Review"
6614
+ ]);
6615
+ prReviewRunStatus = parseReviewRunStatus(prReviewStatusValue);
6489
6616
  const prePrEvidenceValue = extractFirstSpecValue(content, [
6490
6617
  "PR \uC804 \uB9AC\uBDF0 Evidence",
6491
6618
  "Pre-PR Evidence"
@@ -6626,19 +6753,34 @@ async function parseFeature(ctx, featurePath, type, context, options) {
6626
6753
  normalizedFeaturePathFromDocs
6627
6754
  );
6628
6755
  let docsHasUncommittedChanges = typeof context.docsHasUncommittedChanges === "boolean" ? context.docsHasUncommittedChanges : false;
6756
+ let docsRawStatus;
6629
6757
  let docsEverCommitted = typeof context.docsEverCommitted === "boolean" ? context.docsEverCommitted : false;
6630
6758
  let docsGitUnavailable = !!context.docsGitUnavailable;
6631
6759
  if (typeof context.docsHasUncommittedChanges !== "boolean") {
6632
- const docsStatus = getGitStatusPorcelain(ctx, context.docsGitCwd, [
6760
+ docsRawStatus = getGitStatusPorcelain(ctx, context.docsGitCwd, [
6633
6761
  normalizedFeaturePathFromDocs
6634
6762
  ]);
6635
- if (docsStatus === void 0) {
6763
+ if (docsRawStatus === void 0) {
6636
6764
  docsGitUnavailable = true;
6637
6765
  docsHasUncommittedChanges = true;
6638
6766
  } else {
6639
- docsHasUncommittedChanges = docsStatus.trim().length > 0;
6767
+ docsHasUncommittedChanges = docsRawStatus.trim().length > 0;
6640
6768
  }
6641
6769
  }
6770
+ if (docsHasUncommittedChanges && docsRawStatus === void 0 && !docsGitUnavailable) {
6771
+ docsRawStatus = getGitStatusPorcelain(ctx, context.docsGitCwd, [
6772
+ normalizedFeaturePathFromDocs
6773
+ ]);
6774
+ }
6775
+ let docsHasCommitRequiredChanges = docsHasUncommittedChanges;
6776
+ if (docsHasUncommittedChanges && !docsGitUnavailable && isReviewRunStateOnlyDocsChange(
6777
+ ctx,
6778
+ context.docsGitCwd,
6779
+ normalizedFeaturePathFromDocs,
6780
+ docsRawStatus
6781
+ )) {
6782
+ docsHasCommitRequiredChanges = false;
6783
+ }
6642
6784
  if (typeof context.docsEverCommitted !== "boolean") {
6643
6785
  const docsLastCommit = getLastCommitForPath(
6644
6786
  ctx,
@@ -6750,7 +6892,7 @@ async function parseFeature(ctx, featurePath, type, context, options) {
6750
6892
  })
6751
6893
  );
6752
6894
  }
6753
- if (docsEverCommitted && docsHasUncommittedChanges) {
6895
+ if (docsEverCommitted && docsHasCommitRequiredChanges) {
6754
6896
  warnings.push(tr(lang, "warnings", "docsUncommittedChanges"));
6755
6897
  }
6756
6898
  if (projectHasUncommittedChanges) {
@@ -6775,7 +6917,7 @@ async function parseFeature(ctx, featurePath, type, context, options) {
6775
6917
  }
6776
6918
  const tasksDocApproved = !tasksDocStatusFieldExists || tasksDocStatus === "Approved";
6777
6919
  const implementationDone = tasksExists && tasksSummary.total > 0 && tasksSummary.total === tasksSummary.done && isCompletionChecklistDone2({ completionChecklist }) && tasksDocApproved;
6778
- const workflowDone = implementationDone && !docsHasUncommittedChanges && !projectHasUncommittedChanges && specStatus === "Approved" && planStatus === "Approved" && (!workflowPolicy.requireIssue || !!issueNumber) && (!workflowPolicy.requirePr || isPrMetadataConfigured2({
6920
+ const workflowDone = implementationDone && !docsHasCommitRequiredChanges && !projectHasUncommittedChanges && specStatus === "Approved" && planStatus === "Approved" && (!workflowPolicy.requireIssue || !!issueNumber) && (!workflowPolicy.requirePr || isPrMetadataConfigured2({
6779
6921
  docs: { prFieldExists, prStatusFieldExists }
6780
6922
  }) && !!prLink) && (!workflowPolicy.requireMerge || prStatus === "Approved") && isPrePrReviewSatisfied2(
6781
6923
  {
@@ -6885,6 +7027,7 @@ async function parseFeature(ctx, featurePath, type, context, options) {
6885
7027
  decisionProvided: prePrDecisionProvided
6886
7028
  },
6887
7029
  prReview: {
7030
+ status: prReviewRunStatus,
6888
7031
  evidence: prReviewEvidence,
6889
7032
  evidenceProvided: prReviewEvidenceProvided,
6890
7033
  decision: prReviewDecision,
@@ -6902,6 +7045,7 @@ async function parseFeature(ctx, featurePath, type, context, options) {
6902
7045
  expectedWorktreePath,
6903
7046
  docsEverCommitted,
6904
7047
  docsHasUncommittedChanges,
7048
+ docsHasCommitRequiredChanges,
6905
7049
  projectHasUncommittedChanges,
6906
7050
  docsPathIgnored: docsPathIgnored === true,
6907
7051
  projectHasUpstream,
@@ -8271,7 +8415,7 @@ function toActionOptions(actions, lang) {
8271
8415
  const summary = getActionSummary(action, lang);
8272
8416
  const detail = buildActionDetail(action, lang);
8273
8417
  const requiresRequestText = action.category === "user_request_replan";
8274
- const replyExample = requiresRequestText ? `${label}, <your request>` : `${label} OK`;
8418
+ const replyExample = requiresRequestText ? `${label}, <your request>` : label;
8275
8419
  return {
8276
8420
  label,
8277
8421
  summary,
@@ -8454,6 +8598,23 @@ async function resolveContextSelection(ctx, featureName, options) {
8454
8598
  }
8455
8599
 
8456
8600
  // src/services/ContextPresenter.ts
8601
+ function getActionExecutionMetadata(action) {
8602
+ if (action.category === "code_review_run") {
8603
+ return {
8604
+ handoffOnly: true,
8605
+ advancesWorkflow: false,
8606
+ nextMainState: "code_review_running"
8607
+ };
8608
+ }
8609
+ if (action.category === "pre_pr_review_run") {
8610
+ return {
8611
+ handoffOnly: true,
8612
+ advancesWorkflow: false,
8613
+ nextMainState: "pre_pr_review_running"
8614
+ };
8615
+ }
8616
+ return null;
8617
+ }
8457
8618
  function isTaskExecuteProjectCommitCommand(option) {
8458
8619
  if (!option || option.action.type !== "command") return false;
8459
8620
  if (option.action.category !== "task_execute") return false;
@@ -8647,11 +8808,14 @@ function buildAutoRunCommand(state, featureName, selectedComponent, untilCategor
8647
8808
  function resolveAutoRunPlan(lang, state, featureName, selectedComponent, approval, approvalRequired) {
8648
8809
  const base = (reasonCode, untilCategories2 = [], unknownCategories2 = []) => ({
8649
8810
  available: false,
8811
+ policyEligible: false,
8812
+ executableNow: false,
8650
8813
  reasonCode,
8651
8814
  summary: tr(lang, "cli", "context.autoRunUnavailable"),
8652
8815
  command: "",
8653
8816
  untilCategories: untilCategories2,
8654
- unknownCategories: unknownCategories2
8817
+ unknownCategories: unknownCategories2,
8818
+ manualBoundary: null
8655
8819
  });
8656
8820
  if (state.status !== "single_matched") return base("NOT_SINGLE_MATCHED");
8657
8821
  if (state.actionOptions.length === 0) return base("NO_ACTION_OPTIONS");
@@ -8664,8 +8828,38 @@ function resolveAutoRunPlan(lang, state, featureName, selectedComponent, approva
8664
8828
  if (untilCategories.length === 0) {
8665
8829
  return base("NO_REQUIRE_CHECK_CATEGORIES", [], unknownCategories);
8666
8830
  }
8831
+ const executable = state.actionOptions.find(
8832
+ (option) => option.action.type === "command"
8833
+ );
8834
+ if (!executable) {
8835
+ const manualBoundary = state.actionOptions[0] ? {
8836
+ label: state.actionOptions[0].label,
8837
+ category: state.actionOptions[0].action.category,
8838
+ detail: state.actionOptions[0].detail
8839
+ } : null;
8840
+ return {
8841
+ available: false,
8842
+ policyEligible: true,
8843
+ executableNow: false,
8844
+ reasonCode: "MANUAL_BOUNDARY",
8845
+ summary: tr(lang, "cli", "context.autoRunManualBoundary", {
8846
+ detail: manualBoundary?.detail || ""
8847
+ }),
8848
+ command: buildAutoRunCommand(
8849
+ state,
8850
+ featureName,
8851
+ selectedComponent,
8852
+ untilCategories
8853
+ ),
8854
+ untilCategories,
8855
+ unknownCategories,
8856
+ manualBoundary
8857
+ };
8858
+ }
8667
8859
  return {
8668
8860
  available: true,
8861
+ policyEligible: true,
8862
+ executableNow: true,
8669
8863
  reasonCode: "AVAILABLE",
8670
8864
  summary: tr(lang, "cli", "context.autoRunSummary", {
8671
8865
  categories: untilCategories.join(", ")
@@ -8677,7 +8871,8 @@ function resolveAutoRunPlan(lang, state, featureName, selectedComponent, approva
8677
8871
  untilCategories
8678
8872
  ),
8679
8873
  untilCategories,
8680
- unknownCategories
8874
+ unknownCategories,
8875
+ manualBoundary: null
8681
8876
  };
8682
8877
  }
8683
8878
  function toSuggestionLabel(index) {
@@ -8786,7 +8981,7 @@ function buildRequiredDocHints(actionOptions) {
8786
8981
  }
8787
8982
  function getListLabel(f, stepsMap, lang, workflowPolicy, prePrReviewPolicy) {
8788
8983
  if (f.completion.implementationDone && !f.completion.workflowDone) {
8789
- if (f.git.docsHasUncommittedChanges) {
8984
+ if (f.git.docsHasCommitRequiredChanges) {
8790
8985
  return tr(lang, "cli", "context.list.docsCommitNeeded");
8791
8986
  }
8792
8987
  if (f.git.projectHasUncommittedChanges) {
@@ -8853,87 +9048,45 @@ function getFeatureRef(feature) {
8853
9048
  }
8854
9049
  function toCompactFeature(feature) {
8855
9050
  if (!feature) return null;
8856
- return {
9051
+ const compactFeature = {
8857
9052
  ref: getFeatureRef(feature),
8858
- id: feature.id ?? null,
8859
- slug: feature.slug,
8860
- folderName: feature.folderName,
8861
- type: feature.type,
8862
- path: feature.path,
8863
9053
  currentStep: feature.currentStep,
8864
9054
  currentSubstateId: feature.currentSubstateId,
8865
9055
  currentSubstateOwner: feature.currentSubstateOwner,
8866
9056
  currentSubstatePhase: feature.currentSubstatePhase,
8867
- nextAction: feature.nextAction,
8868
- completion: feature.completion,
9057
+ completion: {
9058
+ implementationDone: feature.completion.implementationDone,
9059
+ workflowDone: feature.completion.workflowDone
9060
+ },
8869
9061
  specStatus: feature.specStatus,
8870
9062
  planStatus: feature.planStatus,
8871
- tasks: feature.tasks,
8872
- prePrReview: {
8873
- status: feature.prePrReview.status,
8874
- evidenceProvided: feature.prePrReview.evidenceProvided,
8875
- decisionOutcome: feature.prePrReview.decisionOutcome,
8876
- decisionProvided: feature.prePrReview.decisionProvided
8877
- },
8878
- prReview: {
8879
- evidenceProvided: feature.prReview.evidenceProvided,
8880
- decisionProvided: feature.prReview.decisionProvided
8881
- },
8882
- pr: {
8883
- link: feature.pr.link,
8884
- status: feature.pr.status,
8885
- remote: feature.pr.remote
8886
- },
8887
- git: {
8888
- docsBranch: feature.git.docsBranch,
8889
- projectBranch: feature.git.projectBranch,
8890
- projectBranchAvailable: feature.git.projectBranchAvailable,
8891
- onExpectedBranch: feature.git.onExpectedBranch,
8892
- docsEverCommitted: feature.git.docsEverCommitted,
8893
- docsHasUncommittedChanges: feature.git.docsHasUncommittedChanges,
8894
- projectHasUncommittedChanges: feature.git.projectHasUncommittedChanges,
8895
- docsPathIgnored: feature.git.docsPathIgnored,
8896
- projectHasUpstream: feature.git.projectHasUpstream,
8897
- projectBranchAhead: feature.git.projectBranchAhead,
8898
- projectBranchBehind: feature.git.projectBranchBehind
8899
- },
8900
- docs: {
8901
- specExists: feature.docs.specExists,
8902
- planExists: feature.docs.planExists,
8903
- tasksExists: feature.docs.tasksExists,
8904
- issueDocIssueFieldExists: feature.docs.issueDocIssueFieldExists,
8905
- prDocPrFieldExists: feature.docs.prDocPrFieldExists,
8906
- prDocReviewStatusFieldExists: feature.docs.prDocReviewStatusFieldExists,
8907
- prFieldExists: feature.docs.prFieldExists,
8908
- prStatusFieldExists: feature.docs.prStatusFieldExists,
8909
- prePrReviewFieldExists: feature.docs.prePrReviewFieldExists,
8910
- prePrEvidenceFieldExists: feature.docs.prePrEvidenceFieldExists,
8911
- prePrDecisionFieldExists: feature.docs.prePrDecisionFieldExists,
8912
- prReviewEvidenceFieldExists: feature.docs.prReviewEvidenceFieldExists,
8913
- prReviewDecisionFieldExists: feature.docs.prReviewDecisionFieldExists
8914
- },
8915
- warnings: feature.warnings
9063
+ tasks: feature.tasks
8916
9064
  };
9065
+ if (feature.warnings.length > 0) {
9066
+ compactFeature.warnings = feature.warnings;
9067
+ }
9068
+ return compactFeature;
8917
9069
  }
8918
9070
  function toCompactActionOption(option) {
8919
9071
  const base = {
8920
9072
  label: option.label,
8921
- summary: option.summary,
8922
9073
  detail: option.detail,
8923
- approvalPrompt: option.approvalPrompt,
8924
- requiresRequestText: option.requiresRequestText,
8925
- replyExample: option.replyExample,
8926
9074
  actionType: option.action.type,
8927
9075
  category: option.action.category,
8928
9076
  operationType: option.action.operationType,
8929
9077
  requiresUserCheck: !!option.action.requiresUserCheck
8930
9078
  };
9079
+ const executionMetadata = getActionExecutionMetadata(option.action);
9080
+ if (executionMetadata) {
9081
+ base.handoffOnly = executionMetadata.handoffOnly;
9082
+ base.advancesWorkflow = executionMetadata.advancesWorkflow;
9083
+ if (executionMetadata.nextMainState) {
9084
+ base.nextMainState = executionMetadata.nextMainState;
9085
+ }
9086
+ }
8931
9087
  if (option.action.taskExecutePhase) {
8932
9088
  base.taskExecutePhase = option.action.taskExecutePhase;
8933
9089
  }
8934
- if (option.action.uiDetailParams) {
8935
- base.uiDetailParams = option.action.uiDetailParams;
8936
- }
8937
9090
  if (option.action.type === "command") {
8938
9091
  base.scope = option.action.scope;
8939
9092
  base.cwd = option.action.cwd;
@@ -9271,6 +9424,7 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
9271
9424
  );
9272
9425
  }
9273
9426
  const selectedAction = freshSelected.action;
9427
+ const executionMetadata = getActionExecutionMetadata(selectedAction);
9274
9428
  if (selectedAction.category === "user_request_replan") {
9275
9429
  const requestText = (parsedApproval?.requestText?.trim() || (parsedApproval ? "" : implicitReplanRequest)).trim();
9276
9430
  if (!requestText) {
@@ -9350,6 +9504,13 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
9350
9504
  executeCommand = executeCommand.replace(" [--ticket <TICKET>]", "");
9351
9505
  }
9352
9506
  console.log(chalk8.gray(` - Run with: ${executeCommand}`));
9507
+ if (executionMetadata?.handoffOnly && !executionMetadata.advancesWorkflow) {
9508
+ console.log(
9509
+ chalk8.gray(
9510
+ " - This command prepares a handoff only; complete the delegated work and update workflow evidence before re-running context."
9511
+ )
9512
+ );
9513
+ }
9353
9514
  } else {
9354
9515
  console.log(
9355
9516
  chalk8.gray(" - Instruction-only action (no command execution).")
@@ -9426,6 +9587,30 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
9426
9587
  { owner: `context-execute:${selectedAction.scope}` }
9427
9588
  );
9428
9589
  if (jsonMode) {
9590
+ if (executionMetadata?.handoffOnly && !executionMetadata.advancesWorkflow) {
9591
+ console.log(
9592
+ JSON.stringify(
9593
+ {
9594
+ status: "approved_handoff_prepared",
9595
+ reasonCode: "HANDOFF_PREPARED",
9596
+ feature: freshState.matchedFeature?.folderName ?? null,
9597
+ label: parsedLabel,
9598
+ action: selectedAction,
9599
+ userRequest,
9600
+ contextVersion: freshState.contextVersion,
9601
+ executed: true,
9602
+ handoffOnly: true,
9603
+ advancesWorkflow: false,
9604
+ nextMainState: executionMetadata.nextMainState,
9605
+ stdout: execResult.stdout?.trim() || void 0,
9606
+ stderr: execResult.stderr?.trim() || void 0
9607
+ },
9608
+ null,
9609
+ 2
9610
+ )
9611
+ );
9612
+ return;
9613
+ }
9429
9614
  console.log(
9430
9615
  JSON.stringify(
9431
9616
  {
@@ -9446,6 +9631,15 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
9446
9631
  );
9447
9632
  return;
9448
9633
  }
9634
+ if (executionMetadata?.handoffOnly && !executionMetadata.advancesWorkflow) {
9635
+ console.log(
9636
+ chalk8.yellow(
9637
+ "Prepared handoff only. Complete the delegated work and update workflow evidence before re-running context."
9638
+ )
9639
+ );
9640
+ console.log();
9641
+ return;
9642
+ }
9449
9643
  console.log(chalk8.green(`\u2705 Executed option ${parsedLabel}.`));
9450
9644
  console.log();
9451
9645
  } catch (error) {
@@ -9627,98 +9821,99 @@ async function runContext(featureName, options) {
9627
9821
  );
9628
9822
  if (options.jsonCompact) {
9629
9823
  const compactResult = {
9630
- schema: "context.v2.compact",
9824
+ schema: "context.v3.compact",
9631
9825
  status: state.status,
9632
9826
  reasonCode: toReasonCode(state.status),
9633
- selectionMode: state.selectionMode,
9634
- selectionFallback: state.selectionFallback,
9635
- branches: state.branches,
9636
- warnings: state.warnings,
9637
9827
  contextVersion: state.contextVersion,
9638
9828
  matchedFeature: toCompactFeature(state.matchedFeature),
9639
- candidateRefs: state.targetFeatures.length > 1 ? state.targetFeatures.map(
9640
- (feature) => getFeatureRef(feature)
9641
- ) : [],
9642
- completedCandidateRefs: state.selectionMode === "open" ? state.doneFeatures.map(
9643
- (feature) => getFeatureRef(feature)
9644
- ) : [],
9645
- openCandidateRefs: state.selectionMode === "open" ? state.openFeatures.map(
9646
- (feature) => getFeatureRef(feature)
9647
- ) : [],
9648
- inProgressCandidateRefs: state.selectionMode === "open" ? state.inProgressFeatures.map(
9649
- (feature) => getFeatureRef(feature)
9650
- ) : [],
9651
- readyToCloseCandidateRefs: state.selectionMode === "open" ? state.readyToCloseFeatures.map(
9652
- (feature) => getFeatureRef(feature)
9653
- ) : [],
9654
9829
  actionOptions: state.actionOptions.map(
9655
9830
  (option) => toCompactActionOption(option)
9656
9831
  ),
9657
- suggestionOptions: suggestionOptions.map(
9658
- (option) => toCompactSuggestionOption(option)
9659
- ),
9660
- primaryActionLabel: primaryAction?.label ?? null,
9661
- workflowPolicy,
9662
- taskCommitGatePolicy,
9663
- prePrReviewPolicy,
9664
9832
  checkPolicy: {
9665
- docPath: "builtin://agents/policy",
9666
9833
  token: "<LABEL>",
9667
- acceptedTokens: [
9668
- "<LABEL>",
9669
- "<LABEL> OK",
9670
- "<LABEL> ...",
9671
- "... <LABEL> ..."
9672
- ],
9673
- tokenPattern: "^.*\\b([A-Z]+)\\b.*$",
9674
9834
  validLabels: state.actionOptions.map((o) => o.label),
9675
- activeCategories,
9676
- knownCategories: ACTION_CATEGORIES,
9677
- uncategorizedLabels,
9678
9835
  checkRequiredLabels,
9679
9836
  checkRequiredCategories,
9680
9837
  approvalRequired,
9681
- categoryPolicyGuidance: 'For approval.mode="category", match against `actionOptions[].category`.',
9682
- oneApprovalPerAction: approvalRequired,
9683
- requireFreshContext: true,
9684
- contextVersion: state.contextVersion,
9685
- config: config.approval ?? { mode: "builtin" }
9838
+ contextVersion: state.contextVersion
9839
+ },
9840
+ agentOrchestration: {
9841
+ subAgentHandoff: agentOrchestration.subAgentHandoff
9686
9842
  },
9687
- agentOrchestration,
9688
9843
  autoRun: {
9689
9844
  available: autoRunPlan.available,
9845
+ policyEligible: autoRunPlan.policyEligible,
9846
+ executableNow: autoRunPlan.executableNow,
9690
9847
  reasonCode: autoRunPlan.reasonCode,
9691
- summary: autoRunPlan.summary,
9692
9848
  command: autoRunPlan.command,
9693
9849
  untilCategories: autoRunPlan.untilCategories,
9694
- unknownCategories: autoRunPlan.unknownCategories
9850
+ unknownCategories: autoRunPlan.unknownCategories,
9851
+ manualBoundary: autoRunPlan.manualBoundary
9695
9852
  },
9696
9853
  approvalRequest: {
9697
9854
  required: approvalRequired,
9698
9855
  finalPrompt: finalApprovalPrompt,
9699
- userFacingLines: approvalUserFacingLines,
9700
- labels: approvalRequired ? state.actionOptions.map((o) => o.label) : [],
9701
- approveCommand,
9702
- executeCommand,
9703
- executeRequiresTicket: !!state.actionOptions[0]?.action?.requiresUserCheck
9704
- },
9705
- suggestionRequest: {
9706
- finalPrompt: suggestionFinalPrompt,
9707
- userFacingLines: [
9708
- ...suggestionOptions.map((o) => `${o.label}: ${o.summary}`),
9709
- suggestionFinalPrompt
9710
- ].filter((line) => line.length > 0),
9711
- labels: suggestionOptions.map((o) => o.label)
9712
- },
9713
- prPolicy: {
9714
- screenshots: {
9715
- upload: config.pr?.screenshots?.upload ?? false
9716
- }
9717
- },
9718
- requiredDocs,
9719
- recommendation
9856
+ userFacingLines: approvalUserFacingLines
9857
+ }
9720
9858
  };
9721
- console.log(JSON.stringify(compactResult, null, 2));
9859
+ if (state.warnings.length > 0) {
9860
+ compactResult.warnings = state.warnings;
9861
+ }
9862
+ if (requiredDocs.length > 0) {
9863
+ compactResult.requiredDocs = requiredDocs;
9864
+ }
9865
+ if (state.status !== "single_matched") {
9866
+ const candidateRefs = state.targetFeatures.length > 1 ? state.targetFeatures.map(
9867
+ (feature) => getFeatureRef(feature)
9868
+ ) : [];
9869
+ const completedCandidateRefs = state.selectionMode === "open" ? state.doneFeatures.map(
9870
+ (feature) => getFeatureRef(feature)
9871
+ ) : [];
9872
+ const openCandidateRefs = state.selectionMode === "open" ? state.openFeatures.map(
9873
+ (feature) => getFeatureRef(feature)
9874
+ ) : [];
9875
+ const inProgressCandidateRefs = state.selectionMode === "open" ? state.inProgressFeatures.map(
9876
+ (feature) => getFeatureRef(feature)
9877
+ ) : [];
9878
+ const readyToCloseCandidateRefs = state.selectionMode === "open" ? state.readyToCloseFeatures.map(
9879
+ (feature) => getFeatureRef(feature)
9880
+ ) : [];
9881
+ const compactSuggestionOptions = suggestionOptions.map(
9882
+ (option) => toCompactSuggestionOption(option)
9883
+ );
9884
+ compactResult.selectionMode = state.selectionMode;
9885
+ compactResult.selectionFallback = state.selectionFallback;
9886
+ if (candidateRefs.length > 0) {
9887
+ compactResult.candidateRefs = candidateRefs;
9888
+ }
9889
+ if (completedCandidateRefs.length > 0) {
9890
+ compactResult.completedCandidateRefs = completedCandidateRefs;
9891
+ }
9892
+ if (openCandidateRefs.length > 0) {
9893
+ compactResult.openCandidateRefs = openCandidateRefs;
9894
+ }
9895
+ if (inProgressCandidateRefs.length > 0) {
9896
+ compactResult.inProgressCandidateRefs = inProgressCandidateRefs;
9897
+ }
9898
+ if (readyToCloseCandidateRefs.length > 0) {
9899
+ compactResult.readyToCloseCandidateRefs = readyToCloseCandidateRefs;
9900
+ }
9901
+ if (compactSuggestionOptions.length > 0) {
9902
+ compactResult.suggestionOptions = compactSuggestionOptions;
9903
+ compactResult.suggestionRequest = {
9904
+ finalPrompt: suggestionFinalPrompt,
9905
+ userFacingLines: [
9906
+ ...suggestionOptions.map((o) => `${o.label}: ${o.summary}`),
9907
+ suggestionFinalPrompt
9908
+ ].filter((line) => line.length > 0),
9909
+ labels: suggestionOptions.map((o) => o.label)
9910
+ };
9911
+ }
9912
+ if (recommendation) {
9913
+ compactResult.recommendation = recommendation;
9914
+ }
9915
+ }
9916
+ console.log(JSON.stringify(compactResult));
9722
9917
  return;
9723
9918
  }
9724
9919
  const result = {
@@ -9736,7 +9931,16 @@ async function runContext(featureName, options) {
9736
9931
  inProgressCandidates: state.selectionMode === "open" ? state.inProgressFeatures : [],
9737
9932
  readyToCloseCandidates: state.selectionMode === "open" ? state.readyToCloseFeatures : [],
9738
9933
  actions: state.actions,
9739
- actionOptions: state.actionOptions,
9934
+ actionOptions: state.actionOptions.map((option) => {
9935
+ const executionMetadata = getActionExecutionMetadata(
9936
+ option.action
9937
+ );
9938
+ if (!executionMetadata) return option;
9939
+ return {
9940
+ ...option,
9941
+ ...executionMetadata
9942
+ };
9943
+ }),
9740
9944
  suggestionOptions,
9741
9945
  primaryActionLabel: primaryAction?.label ?? null,
9742
9946
  primaryActionType: primaryAction?.action.type ?? null,
@@ -9780,12 +9984,15 @@ async function runContext(featureName, options) {
9780
9984
  agentOrchestration,
9781
9985
  autoRun: {
9782
9986
  available: autoRunPlan.available,
9987
+ policyEligible: autoRunPlan.policyEligible,
9988
+ executableNow: autoRunPlan.executableNow,
9783
9989
  reasonCode: autoRunPlan.reasonCode,
9784
9990
  summary: autoRunPlan.summary,
9785
9991
  command: autoRunPlan.command,
9786
9992
  untilCategories: autoRunPlan.untilCategories,
9787
9993
  unknownCategories: autoRunPlan.unknownCategories,
9788
- guidance: 'Use auto-run only when `autoRun.available=true`. Do not treat `autoRun.available` alone as a delegation trigger; use `agentOrchestration.subAgentHandoff.required` + `mode="auto_run"` for actual delegation. Stop and request approval when `approvalRequest.required=true` or when auto mode reaches configured gate categories.'
9994
+ manualBoundary: autoRunPlan.manualBoundary,
9995
+ guidance: 'Use auto-run only when `autoRun.available=true`. If `autoRun.policyEligible=true` but `autoRun.executableNow=false`, resolve `autoRun.manualBoundary` first. Do not treat `autoRun.available` alone as a delegation trigger; use `agentOrchestration.subAgentHandoff.required` + `mode="auto_run"` for actual delegation. Stop and request approval when `approvalRequest.required=true` or when auto mode reaches configured gate categories.'
9789
9996
  },
9790
9997
  approvalRequest: {
9791
9998
  guidance: 'User-facing output must include only approval prompts (`A: ...`) and `finalPrompt`. Do not expose `requiredDocs`, `checkPolicy`, or raw `cmd` unless explicitly requested. For approved command actions, prefer one-shot `flow --approve <LABEL> --execute`. Keep short steps in main agent. Prefer `matchedFeature.currentSubstateOwner` + `agentOrchestration.subAgentHandoff` as the delegation SSOT; `currentActionShouldDelegate` is a compatibility mirror. Delegate auto-run only when `agentOrchestration.subAgentHandoff.required=true` with `mode="auto_run"`.',
@@ -10497,8 +10704,9 @@ async function checkDocsStructure(config, cwd) {
10497
10704
  }
10498
10705
  async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10499
10706
  const issues = [];
10707
+ const fsAdapter = new DefaultFileSystemAdapter();
10500
10708
  const { definitions: prdDefinitions } = await scanPrdRequirements(
10501
- fs,
10709
+ fsAdapter,
10502
10710
  config.docsDir
10503
10711
  );
10504
10712
  if (features.length === 0) {
@@ -11775,6 +11983,26 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
11775
11983
  executeStatus: executeResult?.status ?? "unknown",
11776
11984
  executeReasonCode: executeResult?.reasonCode
11777
11985
  });
11986
+ if (executeResult?.status === "approved_handoff_prepared") {
11987
+ return {
11988
+ enabled: true,
11989
+ untilCategories,
11990
+ request: requestText,
11991
+ preset: metadata?.preset ?? null,
11992
+ source: metadata?.source ?? null,
11993
+ resume,
11994
+ status: "manual_required",
11995
+ reasonCode: toAutoReasonCode("manual_required"),
11996
+ iterations,
11997
+ executions,
11998
+ gate: null,
11999
+ manual: {
12000
+ label: executable.label,
12001
+ category: executable.action.category,
12002
+ detail: typeof executeResult?.nextMainState === "string" ? `Complete the delegated handoff work, then continue from ${executeResult.nextMainState}.` : executable.detail
12003
+ }
12004
+ };
12005
+ }
11778
12006
  if (executeResult?.status !== "approved_executed") {
11779
12007
  return {
11780
12008
  enabled: true,
@@ -15878,6 +16106,12 @@ async function resolvePrePrFeatureContext(featureName, component) {
15878
16106
  component: resolveComponentOption(component)
15879
16107
  };
15880
16108
  const ctx = await createCliContext({ cwd: process.cwd() });
16109
+ if (!ctx) {
16110
+ throw createCliError(
16111
+ "CONFIG_NOT_FOUND",
16112
+ "No lee-spec-kit config found in this workspace."
16113
+ );
16114
+ }
15881
16115
  const state = await resolveContextSelection(ctx, featureName, selectionOptions);
15882
16116
  if (state.status !== "single_matched" || !state.matchedFeature) {
15883
16117
  throw createCliError(
@@ -15898,6 +16132,23 @@ async function runPrePrReviewRun(featureName, options) {
15898
16132
  options.component
15899
16133
  );
15900
16134
  const policy = resolvePrePrReviewPolicy(config.workflow);
16135
+ const preferred = getPreferredKeys(config.lang);
16136
+ const tasksPath = path12.join(feature.path, "tasks.md");
16137
+ let tasksUpdated = false;
16138
+ if (await fs.pathExists(tasksPath)) {
16139
+ const tasksContent = await fs.readFile(tasksPath, "utf-8");
16140
+ const nextTasks = upsertSpecLine(
16141
+ tasksContent,
16142
+ ["PR \uC804 \uB9AC\uBDF0", "Pre-PR Review"],
16143
+ preferred.review,
16144
+ "Running",
16145
+ ["PR \uC0C1\uD0DC", "PR Status"]
16146
+ );
16147
+ tasksUpdated = nextTasks !== tasksContent;
16148
+ if (tasksUpdated) {
16149
+ await fs.writeFile(tasksPath, nextTasks, "utf-8");
16150
+ }
16151
+ }
15901
16152
  const prompt = getPrePrReviewPrompt(
15902
16153
  config.lang,
15903
16154
  policy.skills,
@@ -15928,8 +16179,14 @@ async function runPrePrReviewRun(featureName, options) {
15928
16179
  fallback: policy.fallback,
15929
16180
  handoffOnly: true,
15930
16181
  advancesWorkflow: false,
16182
+ reuseKey: `pre-pr:${featureRef}`,
16183
+ suggestedParallelism: 1,
16184
+ fallbackToMainAgentWhenQuotaExceeded: true,
15931
16185
  nextStepRequirement: "generate_review_trace_then_record",
16186
+ nextMainState: "pre_pr_review_running",
15932
16187
  evidenceFile: "review-trace.json",
16188
+ tasksUpdated,
16189
+ tasksPath,
15933
16190
  prompt,
15934
16191
  recordCommands: {
15935
16192
  changesRequested: changesRequestedCommand,
@@ -15949,7 +16206,26 @@ async function runPrePrReviewRun(featureName, options) {
15949
16206
  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
16207
  )
15951
16208
  );
16209
+ console.log(
16210
+ chalk8.gray(
16211
+ 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."
16212
+ )
16213
+ );
16214
+ console.log(
16215
+ chalk8.gray(
16216
+ 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."
16217
+ )
16218
+ );
16219
+ console.log(`Reuse key: pre-pr:${featureRef}`);
16220
+ console.log(`Suggested parallelism: 1`);
16221
+ console.log(`Next main state: pre_pr_review_running`);
15952
16222
  console.log(`Evidence file: review-trace.json`);
16223
+ if (tasksUpdated) {
16224
+ console.log(`tasks.md updated: ${tasksPath}`);
16225
+ console.log(
16226
+ config.lang === "ko" ? "PR \uC804 \uB9AC\uBDF0 \uC0C1\uD0DC: Running" : "Pre-PR Review status: Running"
16227
+ );
16228
+ }
15953
16229
  console.log(`Record changes requested: ${changesRequestedCommand}`);
15954
16230
  console.log(`Record approval: ${approveCommand}`);
15955
16231
  }
@@ -16129,6 +16405,53 @@ async function runPrePrReview(featureName, options) {
16129
16405
  }
16130
16406
  console.log();
16131
16407
  }
16408
+ function escapeRegExp5(value) {
16409
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16410
+ }
16411
+ function findSpecLineIndex2(lines, keys) {
16412
+ const escaped = keys.map((key) => escapeRegExp5(key));
16413
+ const re = new RegExp(
16414
+ `^\\s*-\\s*\\*\\*(?:${escaped.join("|")})\\*\\*\\s*:\\s*`
16415
+ );
16416
+ return lines.findIndex((line) => re.test(line));
16417
+ }
16418
+ function replaceSpecLine2(line, keys, preferredKey, value) {
16419
+ const escaped = keys.map((key) => escapeRegExp5(key));
16420
+ const re = new RegExp(
16421
+ `^(\\s*-\\s*\\*\\*)(?:${escaped.join("|")})(\\*\\*\\s*:\\s*)(.*)$`
16422
+ );
16423
+ if (!re.test(line)) return line;
16424
+ return line.replace(re, `$1${preferredKey}$2${value}`);
16425
+ }
16426
+ function computeInsertIndex2(lines, anchorKeys) {
16427
+ const anchorIndex = findSpecLineIndex2(lines, anchorKeys);
16428
+ if (anchorIndex !== -1) {
16429
+ let cursor = anchorIndex + 1;
16430
+ while (cursor < lines.length && /^\s{2,}-\s+/.test(lines[cursor])) {
16431
+ cursor += 1;
16432
+ }
16433
+ return cursor;
16434
+ }
16435
+ const sectionIndex = lines.findIndex(
16436
+ (line) => /^\s*##\s+(Task List|태스크 목록)\s*$/.test(line)
16437
+ );
16438
+ if (sectionIndex !== -1) return sectionIndex;
16439
+ return lines.length;
16440
+ }
16441
+ function upsertSpecLine2(content, keys, preferredKey, value, anchorKeys) {
16442
+ const lines = content.split("\n");
16443
+ const index = findSpecLineIndex2(lines, keys);
16444
+ if (index !== -1) {
16445
+ lines[index] = replaceSpecLine2(lines[index], keys, preferredKey, value);
16446
+ return lines.join("\n");
16447
+ }
16448
+ const insertAt = computeInsertIndex2(lines, anchorKeys);
16449
+ lines.splice(insertAt, 0, `- **${preferredKey}**: ${value}`);
16450
+ return lines.join("\n");
16451
+ }
16452
+ function getPreferredReviewKey(lang) {
16453
+ return lang === "ko" ? "PR \uB9AC\uBDF0" : "PR Review";
16454
+ }
16132
16455
  function buildCodeReviewRunPrompt(input) {
16133
16456
  if (input.lang === "ko") {
16134
16457
  return [
@@ -16168,6 +16491,22 @@ async function runCodeReviewRun(featureName, options) {
16168
16491
  );
16169
16492
  }
16170
16493
  const feature = state.matchedFeature;
16494
+ const tasksPath = path12.join(feature.path, "tasks.md");
16495
+ let tasksUpdated = false;
16496
+ if (await fs.pathExists(tasksPath)) {
16497
+ const tasksContent = await fs.readFile(tasksPath, "utf-8");
16498
+ const nextTasks = upsertSpecLine2(
16499
+ tasksContent,
16500
+ ["PR \uB9AC\uBDF0", "PR Review"],
16501
+ getPreferredReviewKey(config.lang),
16502
+ "Running",
16503
+ ["PR \uC804 \uB9AC\uBDF0 Decision", "Pre-PR Decision", "PR \uC0C1\uD0DC", "PR Status"]
16504
+ );
16505
+ tasksUpdated = nextTasks !== tasksContent;
16506
+ if (tasksUpdated) {
16507
+ await fs.writeFile(tasksPath, nextTasks, "utf-8");
16508
+ }
16509
+ }
16171
16510
  const prompt = buildCodeReviewRunPrompt({
16172
16511
  featureRef: feature.folderName,
16173
16512
  basePrompt: getCodeReviewPrompt(config.lang),
@@ -16181,8 +16520,12 @@ async function runCodeReviewRun(featureName, options) {
16181
16520
  owner: "subagent",
16182
16521
  handoffOnly: true,
16183
16522
  advancesWorkflow: false,
16184
- nextMainState: "code_review_finalize",
16185
- tasksPath: path12.join(feature.path, "tasks.md"),
16523
+ reuseKey: `code-review:${feature.folderName}`,
16524
+ suggestedParallelism: 1,
16525
+ fallbackToMainAgentWhenQuotaExceeded: true,
16526
+ nextMainState: "code_review_running",
16527
+ tasksUpdated,
16528
+ tasksPath,
16186
16529
  decisionsPath: path12.join(feature.path, "decisions.md"),
16187
16530
  prompt,
16188
16531
  recordedAt: getLocalDateString()
@@ -16200,7 +16543,22 @@ async function runCodeReviewRun(featureName, options) {
16200
16543
  );
16201
16544
  console.log(chalk8.gray(`- substate: ${payload.substateId}`));
16202
16545
  console.log(chalk8.gray(`- owner: ${payload.owner}`));
16546
+ console.log(chalk8.gray(`- reuse key: ${payload.reuseKey}`));
16547
+ console.log(chalk8.gray(`- suggested parallelism: ${payload.suggestedParallelism}`));
16548
+ console.log(
16549
+ chalk8.gray(
16550
+ `- quota fallback: ${payload.fallbackToMainAgentWhenQuotaExceeded ? "continue in main agent" : "none"}`
16551
+ )
16552
+ );
16203
16553
  console.log(chalk8.gray(`- next main state: ${payload.nextMainState}`));
16554
+ if (tasksUpdated) {
16555
+ console.log(chalk8.gray(`- tasks.md updated: ${payload.tasksPath}`));
16556
+ console.log(
16557
+ chalk8.gray(
16558
+ config.lang === "ko" ? "- PR \uB9AC\uBDF0 \uC0C1\uD0DC: Running" : "- PR Review status: Running"
16559
+ )
16560
+ );
16561
+ }
16204
16562
  console.log(chalk8.gray(`- tasks.md: ${payload.tasksPath}`));
16205
16563
  console.log(chalk8.gray(`- decisions.md: ${payload.decisionsPath}`));
16206
16564
  }
@@ -16451,9 +16809,10 @@ function parseTaskLine(line) {
16451
16809
  function buildTaskRunPrompt(input) {
16452
16810
  const shared = [
16453
16811
  "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.",
16812
+ "Reuse the existing helper/sub-agent for this task if one already exists. Default to a single helper agent.",
16813
+ "Use additional helper agents only when parallel analysis is clearly worth the extra slot cost.",
16456
16814
  "Keep one writer for overlapping files; do not let multiple sub-agents edit the same files concurrently.",
16815
+ "If helper-agent quota is exhausted, continue the task in the main agent instead of blocking progress.",
16457
16816
  "Update the assigned task status and verification notes in `tasks.md` before leaving this task.",
16458
16817
  "Mark the task `DONE` only after code changes and verification are complete."
16459
16818
  ];
@@ -16464,9 +16823,10 @@ function buildTaskRunPrompt(input) {
16464
16823
  `- Task: ${input.taskId} ${input.title}`,
16465
16824
  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
16825
  "- \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.",
16826
+ "- \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.",
16827
+ "- \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
16828
  "- \uAC19\uC740 \uD30C\uC77C\uAD70\uC744 \uC218\uC815\uD558\uB294 \uC791\uC131\uC790\uB294 \uD55C \uBA85\uB9CC \uB450\uC138\uC694.",
16829
+ "- \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
16830
  "- \uC774 task\uB97C \uB9C8\uCE58\uAE30 \uC804 `tasks.md`\uC5D0 \uC0C1\uD0DC\uC640 \uAC80\uC99D \uBA54\uBAA8\uB97C \uBC18\uC601\uD558\uC138\uC694.",
16471
16831
  "- \uCF54\uB4DC \uBCC0\uACBD\uACFC \uAC80\uC99D\uC774 \uB05D\uB0AC\uC744 \uB54C\uB9CC task\uB97C `DONE`\uC73C\uB85C \uD45C\uC2DC\uD558\uC138\uC694."
16472
16832
  ].join("\n");
@@ -16565,7 +16925,11 @@ async function runTaskRun(featureName, options) {
16565
16925
  mode,
16566
16926
  substateId: mode === "start" ? "task_run" : "task_running",
16567
16927
  owner: "subagent",
16568
- nextMainState: "task_finalize",
16928
+ handoffOnly: true,
16929
+ reuseKey: `task:${feature.folderName}:${resolvedTask.taskId}`,
16930
+ suggestedParallelism: 1,
16931
+ fallbackToMainAgentWhenQuotaExceeded: true,
16932
+ nextMainState: "task_complete",
16569
16933
  tasksUpdated,
16570
16934
  tasksPath,
16571
16935
  prompt,
@@ -16579,6 +16943,13 @@ async function runTaskRun(featureName, options) {
16579
16943
  console.log();
16580
16944
  console.log(chalk8.gray(`- substate: ${payload.substateId}`));
16581
16945
  console.log(chalk8.gray(`- owner: ${payload.owner}`));
16946
+ console.log(chalk8.gray(`- reuse key: ${payload.reuseKey}`));
16947
+ console.log(chalk8.gray(`- suggested parallelism: ${payload.suggestedParallelism}`));
16948
+ console.log(
16949
+ chalk8.gray(
16950
+ `- quota fallback: ${payload.fallbackToMainAgentWhenQuotaExceeded ? "continue in main agent" : "none"}`
16951
+ )
16952
+ );
16582
16953
  console.log(chalk8.gray(`- next main state: ${payload.nextMainState}`));
16583
16954
  if (tasksUpdated) {
16584
16955
  console.log();
@@ -16614,6 +16985,146 @@ function taskRunCommand(program2) {
16614
16985
  }
16615
16986
  });
16616
16987
  }
16988
+ function parseTaskLine2(line) {
16989
+ const match = line.match(
16990
+ /^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]\[[^\]]+\](?:\[[^\]]+\])*\s+(T-[A-Za-z0-9-]+)\s+(.+?)\s*$/
16991
+ );
16992
+ if (!match) return null;
16993
+ return {
16994
+ index: -1,
16995
+ raw: line,
16996
+ status: match[1],
16997
+ taskId: match[2],
16998
+ title: match[3]
16999
+ };
17000
+ }
17001
+ function setTaskStatus2(line, nextStatus) {
17002
+ return line.raw.replace(
17003
+ /^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]/,
17004
+ `- [${nextStatus}]`
17005
+ );
17006
+ }
17007
+ async function resolveTaskCompleteContext(featureName, options) {
17008
+ const config = await getConfig(process.cwd());
17009
+ if (!config) {
17010
+ throw createCliError(
17011
+ "CONFIG_NOT_FOUND",
17012
+ "No lee-spec-kit config found in this workspace."
17013
+ );
17014
+ }
17015
+ const ctx = await createCliContext({ cwd: process.cwd() });
17016
+ const state = await resolveContextSelection(ctx, featureName, {
17017
+ component: resolveComponentOption(options.component)
17018
+ });
17019
+ if (state.status !== "single_matched" || !state.matchedFeature) {
17020
+ throw createCliError(
17021
+ "CONTEXT_SELECTION_REQUIRED",
17022
+ "task-complete requires a single matched feature. Pass <feature-name> explicitly."
17023
+ );
17024
+ }
17025
+ return {
17026
+ config,
17027
+ feature: state.matchedFeature
17028
+ };
17029
+ }
17030
+ async function runTaskComplete(featureName, options) {
17031
+ const { feature } = await resolveTaskCompleteContext(featureName, options);
17032
+ const tasksPath = path12.join(feature.path, "tasks.md");
17033
+ if (!await fs.pathExists(tasksPath)) {
17034
+ throw createCliError(
17035
+ "PRECONDITION_FAILED",
17036
+ `tasks.md not found for feature: ${feature.folderName}`
17037
+ );
17038
+ }
17039
+ const content = await fs.readFile(tasksPath, "utf-8");
17040
+ const lines = content.split("\n");
17041
+ const requestedTaskId = options.task?.trim() || feature.activeTask?.id || feature.nextTodoTask?.id;
17042
+ if (!requestedTaskId) {
17043
+ throw createCliError(
17044
+ "PRECONDITION_FAILED",
17045
+ "No active task is available for task-complete."
17046
+ );
17047
+ }
17048
+ const resolvedTask = lines.map((line, index) => {
17049
+ const parsed = parseTaskLine2(line);
17050
+ return parsed ? { ...parsed, index } : null;
17051
+ }).find((entry) => entry?.taskId === requestedTaskId);
17052
+ if (!resolvedTask) {
17053
+ throw createCliError(
17054
+ "INVALID_ARGUMENT",
17055
+ `Task "${requestedTaskId}" was not found in tasks.md.`
17056
+ );
17057
+ }
17058
+ if (resolvedTask.status === "DONE") {
17059
+ throw createCliError(
17060
+ "PRECONDITION_FAILED",
17061
+ `Task "${requestedTaskId}" is already DONE.`
17062
+ );
17063
+ }
17064
+ if (resolvedTask.status !== "DOING" && resolvedTask.status !== "REVIEW") {
17065
+ throw createCliError(
17066
+ "PRECONDITION_FAILED",
17067
+ `Task "${requestedTaskId}" must be DOING/REVIEW before marking it DONE.`
17068
+ );
17069
+ }
17070
+ lines[resolvedTask.index] = setTaskStatus2(resolvedTask, "DONE");
17071
+ await fs.writeFile(tasksPath, lines.join("\n"), "utf-8");
17072
+ const payload = {
17073
+ status: "ok",
17074
+ reasonCode: "TASK_COMPLETED",
17075
+ feature: feature.folderName,
17076
+ taskId: resolvedTask.taskId,
17077
+ title: resolvedTask.title,
17078
+ previousStatus: resolvedTask.status,
17079
+ nextStatus: "DONE",
17080
+ substateId: "task_complete",
17081
+ owner: "main",
17082
+ nextMainState: "task_finalize",
17083
+ tasksUpdated: true,
17084
+ tasksPath,
17085
+ recordedAt: getLocalDateString()
17086
+ };
17087
+ if (options.json) {
17088
+ console.log(JSON.stringify(payload, null, 2));
17089
+ return;
17090
+ }
17091
+ console.log(
17092
+ chalk8.green(
17093
+ `Marked task ${resolvedTask.taskId} as DONE for ${feature.folderName}.`
17094
+ )
17095
+ );
17096
+ console.log(chalk8.gray(`- tasks.md updated: ${tasksPath}`));
17097
+ console.log(chalk8.gray(`- status: ${resolvedTask.status} -> DONE`));
17098
+ console.log(chalk8.gray(`- next main state: ${payload.nextMainState}`));
17099
+ }
17100
+ function taskCompleteCommand(program2) {
17101
+ 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(
17102
+ async (featureName, options) => {
17103
+ try {
17104
+ await runTaskComplete(featureName, options);
17105
+ } catch (error) {
17106
+ const config = await getConfig(process.cwd());
17107
+ const lang = config?.lang ?? DEFAULT_LANG;
17108
+ const cliError = toCliError(error);
17109
+ const suggestions = getCliErrorSuggestions(cliError.code, lang);
17110
+ if (options.json) {
17111
+ console.log(
17112
+ JSON.stringify({
17113
+ status: "error",
17114
+ reasonCode: cliError.code,
17115
+ error: cliError.message,
17116
+ suggestions
17117
+ })
17118
+ );
17119
+ } else {
17120
+ console.error(chalk8.red(`[${cliError.code}] ${cliError.message}`));
17121
+ printCliErrorSuggestions(suggestions, lang);
17122
+ }
17123
+ process.exitCode = 1;
17124
+ }
17125
+ }
17126
+ );
17127
+ }
16617
17128
  function isBannerDisabled() {
16618
17129
  const v = (process.env.LEE_SPEC_KIT_NO_BANNER || "").trim();
16619
17130
  return v === "1";
@@ -16797,6 +17308,7 @@ onboardCommand(program);
16797
17308
  prePrReviewCommand(program);
16798
17309
  codeReviewRunCommand(program);
16799
17310
  taskRunCommand(program);
17311
+ taskCompleteCommand(program);
16800
17312
  requirementsCommand(program);
16801
17313
  await program.parseAsync();
16802
17314
  //# sourceMappingURL=index.js.map