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/README.en.md +21 -27
- package/README.md +21 -27
- package/dist/index.js +710 -198
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/en/common/agents/agents.md +4 -4
- package/templates/en/common/agents/skills/execute-task.md +6 -3
- package/templates/en/common/features/feature-base/pr.md +2 -0
- package/templates/en/common/features/feature-base/tasks.md +5 -2
- package/templates/ko/common/agents/agents.md +4 -4
- package/templates/ko/common/agents/skills/execute-task.md +6 -3
- package/templates/ko/common/features/feature-base/pr.md +2 -0
- package/templates/ko/common/features/feature-base/tasks.md +5 -2
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)
|
|
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}) \
|
|
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 \
|
|
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)
|
|
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": "\
|
|
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": "
|
|
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})
|
|
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})
|
|
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
|
|
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": "
|
|
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": "
|
|
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\
|
|
3565
|
-
8. \
|
|
3566
|
-
9. \
|
|
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
|
|
3578
|
-
8. If
|
|
3579
|
-
9.
|
|
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.
|
|
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
|
|
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 =
|
|
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 =
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
4217
|
-
const isPostTaskSyncDocs = (f) => isPostTaskSyncCurrent(f) && f.git.
|
|
4218
|
-
const isPostTaskSyncReviewFix = (f) => isPostTaskSyncCurrent(f) && !f.git.
|
|
4219
|
-
const isPostTaskSyncProject = (f) => isPostTaskSyncCurrent(f) && !f.git.
|
|
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.
|
|
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(
|
|
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.
|
|
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: "
|
|
4751
|
-
phase: "
|
|
4752
|
-
owner: "
|
|
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.
|
|
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
|
|
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 =
|
|
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
|
-
|
|
6760
|
+
docsRawStatus = getGitStatusPorcelain(ctx, context.docsGitCwd, [
|
|
6633
6761
|
normalizedFeaturePathFromDocs
|
|
6634
6762
|
]);
|
|
6635
|
-
if (
|
|
6763
|
+
if (docsRawStatus === void 0) {
|
|
6636
6764
|
docsGitUnavailable = true;
|
|
6637
6765
|
docsHasUncommittedChanges = true;
|
|
6638
6766
|
} else {
|
|
6639
|
-
docsHasUncommittedChanges =
|
|
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 &&
|
|
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 && !
|
|
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>` :
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
8868
|
-
|
|
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.
|
|
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
|
-
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16185
|
-
|
|
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
|
-
"
|
|
16455
|
-
"
|
|
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
|
-
"- \
|
|
16468
|
-
"- \uC601\uD5A5 \uBC94\uC704 \uBD84\uC11D, \uD14C\uC2A4\uD2B8 \uC704\uCE58 \uD0D0\uC0C9, \uAE30\uC874 \uD328\uD134 \uC870\uC0AC\
|
|
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
|
-
|
|
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
|