lee-spec-kit 0.6.23 → 0.6.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -163,6 +163,9 @@ var ko = {
163
163
  "context.suggestionHeader": "\uCD94\uCC9C \uB2E4\uC74C \uC120\uD0DD\uC9C0",
164
164
  "context.suggestionCommandHint": "\uB77C\uBCA8 \uCC38\uACE0 \uBA85\uB839: {command}",
165
165
  "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`)",
166
+ "context.autoRunUnavailable": "\uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8\uC5D0\uC11C\uB294 \uC790\uB3D9 \uC2E4\uD589\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
167
+ "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}",
168
+ "context.autoRunCommandHint": "\uC790\uB3D9 \uC2E4\uD589 \uBA85\uB839(config \uAC8C\uC774\uD2B8): {command}",
166
169
  "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",
167
170
  "context.commandDetail.branchCreateWithBranch": "({scope}) \uBE0C\uB79C\uCE58 {branch}\uC6A9 worktree\uB97C \uC0DD\uC131\uD558\uAC70\uB098 \uC7AC\uC0AC\uC6A9\uD558\uC138\uC694",
168
171
  "context.commandDetail.branchCreateGeneric": "({scope}) feature \uBE0C\uB79C\uCE58\uC6A9 worktree\uB97C \uC0DD\uC131\uD558\uAC70\uB098 \uC7AC\uC0AC\uC6A9\uD558\uC138\uC694",
@@ -206,8 +209,10 @@ var ko = {
206
209
  "context.list.recordPrLink": "PR \uB9C1\uD06C \uAE30\uB85D",
207
210
  "context.list.addPrePrReviewField": "Pre-PR Review \uD544\uB4DC \uCD94\uAC00",
208
211
  "context.list.completePrePrReview": "Pre-PR \uB9AC\uBDF0 \uC644\uB8CC \uCC98\uB9AC",
212
+ "context.list.addPrePrFindings": "Pre-PR Findings \uAE30\uB85D",
209
213
  "context.list.addPrePrEvidence": "Pre-PR Evidence \uADFC\uAC70 \uCD94\uAC00",
210
214
  "context.list.addPrePrDecision": "Pre-PR Decision \uAE30\uB85D",
215
+ "context.list.resolvePrePrDecision": "Pre-PR Decision\uC744 approve\uB85C \uC815\uB9AC",
211
216
  "context.list.addPrReviewEvidence": "PR \uB9AC\uBDF0 Evidence \uC694\uC57D \uCD94\uAC00",
212
217
  "context.list.addPrReviewDecision": "PR \uB9AC\uBDF0 Decision \uAE30\uB85D",
213
218
  "context.list.setPrStatus": "PR \uC0C1\uD0DC \uC124\uC815",
@@ -490,8 +495,8 @@ var ko = {
490
495
  worktreeCleanupCommand: 'cd "{projectGitCwd}" && git worktree remove "{worktreePath}" && git worktree prune && CURRENT_BRANCH=$(git branch --show-current) && DEFAULT_BRANCH=$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | cut -d/ -f2-) && TARGET_BRANCH="${DEFAULT_BRANCH:-$CURRENT_BRANCH}" && if [ -n "$TARGET_BRANCH" ]; then git checkout "$TARGET_BRANCH" >/dev/null 2>&1 || true; fi && if git rev-parse --abbrev-ref --symbolic-full-name "@{u}" >/dev/null 2>&1 && [ -z "$(git status --porcelain)" ]; then git pull --ff-only || true; fi',
491
496
  tasksAllDoneButNoChecklist: '\uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\uB97C \uC791\uC131\uD558\uC138\uC694. tasks.md\uC758 "\uC644\uB8CC \uC870\uAC74" \uC139\uC158\uC5D0 \uAC80\uC99D \uD56D\uBAA9\uC744 \uCD94\uAC00\uD558\uACE0, \uC0AC\uC6A9\uC790\uC640 \uD655\uC778 \uD6C4 \uCDA9\uC871 \uD56D\uBAA9\uC744 [x]\uB85C \uCCB4\uD06C\uD558\uC138\uC694. \uCD5C\uC885 \uC2B9\uC778(OK)\uB3C4 \uBC18\uC601\uD558\uC138\uC694.',
492
497
  tasksAllDoneButChecklist: "\uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\uC758 \uB0A8\uC740 \uD56D\uBAA9\uC744 \uC9C4\uD589\uD558\uC138\uC694. \uD604\uC7AC \uC9C4\uD589: ({checked}/{total}) \uC0AC\uC6A9\uC790\uC640 \uD655\uC778 \uD6C4 \uCDA9\uC871 \uD56D\uBAA9\uC744 [x]\uB85C \uCCB4\uD06C\uD558\uACE0 \uCD5C\uC885 \uC2B9\uC778(OK)\uC744 \uBC18\uC601\uD558\uC138\uC694.",
493
- finishDoingTask: '\uD604\uC7AC DOING/REVIEW \uD0DC\uC2A4\uD06C\uB97C \uC218\uD589\uD558\uC138\uC694: "{title}" ({done}/{total}) \uC644\uB8CC \uC2DC \uACB0\uACFC/\uAC80\uC99D\uC744 \uACF5\uC720\uD558\uACE0 \uC2B9\uC778(`A` \uB610\uB294 `A OK` \uD615\uC2DD)\uC744 \uBC1B\uC740 \uB4A4 DONE \uCC98\uB9AC',
494
- startNextTodoTask: '\uB2E4\uC74C TODO \uD0DC\uC2A4\uD06C\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4: "{title}" ({done}/{total}) \uC2B9\uC778(`A` \uB610\uB294 `A OK` \uD615\uC2DD) \uD6C4 DOING \uCC98\uB9AC',
498
+ finishDoingTask: '\uD604\uC7AC DOING/REVIEW \uD0DC\uC2A4\uD06C\uB97C \uC218\uD589\uD558\uC138\uC694: "{title}" ({done}/{total}) \uC644\uB8CC \uC2DC \uACB0\uACFC/\uAC80\uC99D\uC744 \uACF5\uC720\uD558\uACE0 DONE \uCC98\uB9AC',
499
+ startNextTodoTask: '\uB2E4\uC74C TODO \uD0DC\uC2A4\uD06C\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4: "{title}" ({done}/{total}) \uC791\uC5C5\uC744 \uC2DC\uC791\uD558\uBA74 DOING \uCC98\uB9AC',
495
500
  checkTaskStatuses: "\uD0DC\uC2A4\uD06C \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694. ({done}/{total})",
496
501
  taskCommitGateStrictBlock: "\uB2E4\uC74C TODO \uD0DC\uC2A4\uD06C\uB85C \uB118\uC5B4\uAC00\uAE30 \uC804\uC5D0 `1 \uD0DC\uC2A4\uD06C = 1 \uCEE4\uBC0B` \uADDC\uCE59\uC744 \uCDA9\uC871\uD574\uC57C \uD569\uB2C8\uB2E4. \uC810\uAC80 \uACB0\uACFC: {reason}. \uD0DC\uC2A4\uD06C \uCEE4\uBC0B \uB2E8\uC704\uB97C \uC815\uB9AC\uD55C \uB4A4 \uB2E4\uC2DC \uC9C4\uD589\uD558\uC138\uC694.",
497
502
  taskCommitGateWarnProceed: "\u26A0\uFE0F \uD0DC\uC2A4\uD06C \uCEE4\uBC0B \uB2E8\uC704 \uC810\uAC80 \uACBD\uACE0: {reason}. \uD604\uC7AC\uB294 \uC9C4\uD589 \uAC00\uB2A5\uD558\uC9C0\uB9CC `1 \uD0DC\uC2A4\uD06C = 1 \uCEE4\uBC0B`\uC744 \uAD8C\uC7A5\uD569\uB2C8\uB2E4.",
@@ -542,6 +547,7 @@ var ko = {
542
547
  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.",
543
548
  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.",
544
549
  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`)",
550
+ legacyTasksPrePrFindingsField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR \uC804 \uB9AC\uBDF0 Findings` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694. (`- **PR \uC804 \uB9AC\uBDF0 Findings**: major=0, minor=0`)",
545
551
  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.",
546
552
  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: ...`)",
547
553
  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.",
@@ -560,8 +566,10 @@ var ko = {
560
566
  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.",
561
567
  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.)",
562
568
  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.)",
563
- workflowPrePrEvidenceMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Evidence`\uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4. (\uB9AC\uBDF0 \uADFC\uAC70\uB97C \uAE30\uB85D\uD558\uC138\uC694.)",
564
- 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. (`\uACB0\uC815: ...` \uB610\uB294 `decision: ...` \uD615\uC2DD)"
569
+ workflowPrePrFindingsMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Findings`\uAC00 \uC5C6\uAC70\uB098 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. (`major=<n>, minor=<n>` \uD615\uC2DD\uC73C\uB85C \uAE30\uB85D)",
570
+ 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. (path_required \uC815\uCC45\uC774\uBA74 \uC2E4\uC81C \uC874\uC7AC\uD558\uB294 \uACBD\uB85C\uB97C \uAE30\uB85D\uD558\uC138\uC694.)",
571
+ 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)",
572
+ workflowPrePrDecisionNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Decision`\uC774 `{outcome}`\uC785\uB2C8\uB2E4. Findings\uB97C \uD574\uACB0\uD558\uACE0 pre-pr-review\uB97C \uC7AC\uC2E4\uD589\uD574 `approve`\uB85C \uB9DE\uCD94\uC138\uC694."
565
573
  }
566
574
  };
567
575
  var ko_default = ko;
@@ -645,6 +653,9 @@ var en = {
645
653
  "context.suggestionHeader": "Suggested Next Options",
646
654
  "context.suggestionCommandHint": "Reference command: {command}",
647
655
  "context.suggestionFinalPrompt": "Recommended labels now: {labels}. Please reply with a format that includes a label token. (e.g. {example}, `A proceed`)",
656
+ "context.autoRunUnavailable": "Auto-run is not available in the current context.",
657
+ "context.autoRunSummary": "Run continuously by config until approval-required categories appear: {categories}",
658
+ "context.autoRunCommandHint": "Auto-run command (config-based gate): {command}",
648
659
  "context.commandDetail.branchCreateWithWorktree": "({scope}) create or reuse worktree {worktree} for branch {branch}",
649
660
  "context.commandDetail.branchCreateWithBranch": "({scope}) create or reuse worktree for branch {branch}",
650
661
  "context.commandDetail.branchCreateGeneric": "({scope}) create or reuse feature branch worktree",
@@ -688,8 +699,10 @@ var en = {
688
699
  "context.list.recordPrLink": "Record PR link",
689
700
  "context.list.addPrePrReviewField": "Add Pre-PR Review field",
690
701
  "context.list.completePrePrReview": "Complete Pre-PR review",
702
+ "context.list.addPrePrFindings": "Record Pre-PR Findings",
691
703
  "context.list.addPrePrEvidence": "Add Pre-PR Evidence",
692
704
  "context.list.addPrePrDecision": "Add Pre-PR Decision",
705
+ "context.list.resolvePrePrDecision": "Resolve Pre-PR decision to approve",
693
706
  "context.list.addPrReviewEvidence": "Add PR Review Evidence summary",
694
707
  "context.list.addPrReviewDecision": "Add PR Review Decision",
695
708
  "context.list.setPrStatus": "Set PR Status",
@@ -972,8 +985,8 @@ var en = {
972
985
  worktreeCleanupCommand: 'cd "{projectGitCwd}" && git worktree remove "{worktreePath}" && git worktree prune && CURRENT_BRANCH=$(git branch --show-current) && DEFAULT_BRANCH=$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | cut -d/ -f2-) && TARGET_BRANCH="${DEFAULT_BRANCH:-$CURRENT_BRANCH}" && if [ -n "$TARGET_BRANCH" ]; then git checkout "$TARGET_BRANCH" >/dev/null 2>&1 || true; fi && if git rev-parse --abbrev-ref --symbolic-full-name "@{u}" >/dev/null 2>&1 && [ -z "$(git status --porcelain)" ]; then git pull --ff-only || true; fi',
973
986
  tasksAllDoneButNoChecklist: 'Create the completion checklist. Add verification items to the tasks.md "Completion Criteria" section, then mark satisfied items as [x] after user confirmation. Record final approval (OK) as well.',
974
987
  tasksAllDoneButChecklist: "Proceed with remaining completion checklist items. Current progress: ({checked}/{total}) Mark items as [x] only after user confirmation and real verification. Record final approval (OK) as well.",
975
- finishDoingTask: 'Continue working on the current DOING/REVIEW task: "{title}" ({done}/{total}) After it is complete, share outcome/verification and get approval before marking DONE',
976
- startNextTodoTask: 'Start the next TODO task: "{title}" ({done}/{total}) Get approval (`A` or `A OK`) before marking DOING',
988
+ finishDoingTask: 'Continue working on the current DOING/REVIEW task: "{title}" ({done}/{total}) After it is complete, share outcome/verification and mark it DONE',
989
+ startNextTodoTask: 'Start the next TODO task: "{title}" ({done}/{total}) Mark it DOING when you begin work',
977
990
  checkTaskStatuses: "Check task statuses. ({done}/{total})",
978
991
  taskCommitGateStrictBlock: "Before moving to the next TODO task, you must satisfy the `1 task = 1 commit` rule. Check result: {reason}. Re-align task commit boundaries, then continue.",
979
992
  taskCommitGateWarnProceed: "\u26A0\uFE0F Task commit boundary warning: {reason}. You may continue, but `1 task = 1 commit` is recommended.",
@@ -1024,6 +1037,7 @@ var en = {
1024
1037
  legacyTasksDocStatusField: "Legacy tasks.md format detected. Add a `Doc Status` field (Draft/Review/Approved) to enable tasks approval.",
1025
1038
  legacyTasksPrFields: "Legacy tasks.md format detected. Add `PR` and `PR Status` fields before PR steps.",
1026
1039
  legacyTasksPrePrReviewField: "Legacy tasks.md format detected. Add `Pre-PR Review` before PR steps. (`- **Pre-PR Review**: Pending | Done`)",
1040
+ legacyTasksPrePrFindingsField: "Legacy tasks.md format detected. Add `Pre-PR Findings` before PR steps. (`- **Pre-PR Findings**: major=0, minor=0`)",
1027
1041
  legacyTasksPrePrEvidenceField: "Legacy tasks.md format detected. Add `Pre-PR Evidence` before PR steps.",
1028
1042
  legacyTasksPrePrDecisionField: "Legacy tasks.md format detected. Add `Pre-PR Decision` before PR steps. (`- **Pre-PR Decision**: decision: ...`)",
1029
1043
  legacyTasksPrReviewEvidenceField: "Legacy tasks.md format detected. Add `PR Review Evidence` before review iteration.",
@@ -1042,8 +1056,10 @@ var en = {
1042
1056
  workflowPrRemoteChecksPending: "Remote PR has {count} pending check(s). Wait for checks to complete, then re-check.",
1043
1057
  workflowPrePrReviewMissing: "Implementation is done but `Pre-PR Review` is missing. (Add `- **Pre-PR Review**: Pending | Done` in tasks.md.)",
1044
1058
  workflowPrePrReviewNotDone: "Implementation is done but `Pre-PR Review` is not Done. (Run pre-PR review, then update it to Done.)",
1045
- workflowPrePrEvidenceMissing: "Implementation is done but `Pre-PR Evidence` is empty. (Record review evidence.)",
1046
- workflowPrePrDecisionMissing: "Implementation is done but `Pre-PR Decision` is empty/invalid. (Use `decision: ...` or `\uACB0\uC815: ...`.)"
1059
+ workflowPrePrFindingsMissing: "Implementation is done but `Pre-PR Findings` is missing/invalid. (Use `major=<n>, minor=<n>`.)",
1060
+ workflowPrePrEvidenceMissing: "Implementation is done but `Pre-PR Evidence` is empty/invalid. (Record a real existing path when path_required policy is enabled.)",
1061
+ workflowPrePrDecisionMissing: "Implementation is done but `Pre-PR Decision` is empty/invalid. (Use `decision: approve|changes_requested|blocked ...`.)",
1062
+ workflowPrePrDecisionNotApproved: "Implementation is done but `Pre-PR Decision` is `{outcome}`. Resolve findings and re-run pre-PR review until decision becomes `approve`."
1047
1063
  }
1048
1064
  };
1049
1065
  var en_default = en;
@@ -1066,7 +1082,10 @@ var I18N = {
1066
1082
  };
1067
1083
  function tr(lang, category, key, vars = {}) {
1068
1084
  const safeLang = normalizeLang(lang);
1069
- const template = I18N[safeLang]?.[category]?.[key] ?? I18N[DEFAULT_LANG]?.[category]?.[key] ?? I18N.ko?.[category]?.[key] ?? `${category}.${key}`;
1085
+ const safeCategory = I18N[safeLang]?.[category];
1086
+ const defaultCategory = I18N[DEFAULT_LANG]?.[category];
1087
+ const koCategory = I18N.ko?.[category];
1088
+ const template = safeCategory?.[key] ?? defaultCategory?.[key] ?? koCategory?.[key] ?? `${category}.${key}`;
1070
1089
  return formatTemplate(template, vars);
1071
1090
  }
1072
1091
 
@@ -2259,7 +2278,10 @@ async function runInit(options) {
2259
2278
  },
2260
2279
  prePrReview: {
2261
2280
  skills: ["code-review-excellence"],
2262
- minorPolicy: "warn"
2281
+ fallback: "builtin-checklist",
2282
+ evidenceMode: "path_required",
2283
+ findings: "required",
2284
+ decisionEnum: ["approve", "changes_requested", "blocked"]
2263
2285
  }
2264
2286
  },
2265
2287
  pr: {
@@ -2878,6 +2900,11 @@ var ACTION_CATEGORIES = [
2878
2900
 
2879
2901
  // src/utils/workflow.ts
2880
2902
  var DEFAULT_PRE_PR_REVIEW_SKILLS = ["code-review-excellence"];
2903
+ var DEFAULT_PRE_PR_DECISION_ENUM = [
2904
+ "approve",
2905
+ "changes_requested",
2906
+ "blocked"
2907
+ ];
2881
2908
  function resolveWorkflowPolicy(workflow) {
2882
2909
  const mode = workflow?.mode === "local" ? "local" : "github";
2883
2910
  const policy = mode === "local" ? {
@@ -2939,17 +2966,42 @@ function normalizeSkillList(input) {
2939
2966
  }
2940
2967
  return [...deduped];
2941
2968
  }
2969
+ function normalizeDecisionEnumList(input) {
2970
+ if (!Array.isArray(input)) return [];
2971
+ const deduped = /* @__PURE__ */ new Set();
2972
+ for (const raw of input) {
2973
+ const value = String(raw || "").trim().toLowerCase();
2974
+ if (!value) continue;
2975
+ if (value === "approve") {
2976
+ deduped.add("approve");
2977
+ continue;
2978
+ }
2979
+ if (value === "changes_requested") {
2980
+ deduped.add("changes_requested");
2981
+ continue;
2982
+ }
2983
+ if (value === "blocked") {
2984
+ deduped.add("blocked");
2985
+ continue;
2986
+ }
2987
+ }
2988
+ return [...deduped];
2989
+ }
2942
2990
  function resolvePrePrReviewPolicy(workflow) {
2943
2991
  const workflowPolicy = resolveWorkflowPolicy(workflow);
2944
2992
  const configured = workflow?.prePrReview;
2945
2993
  const configuredSkills = normalizeSkillList(configured?.skills);
2994
+ const configuredDecisionEnum = normalizeDecisionEnumList(
2995
+ configured?.decisionEnum
2996
+ );
2946
2997
  const configuredEnabled = typeof configured?.enabled === "boolean" ? configured.enabled : workflowPolicy.requirePr;
2947
2998
  return {
2948
2999
  enabled: workflowPolicy.requirePr ? configuredEnabled : false,
2949
3000
  skills: configuredSkills.length > 0 ? configuredSkills : DEFAULT_PRE_PR_REVIEW_SKILLS,
2950
3001
  fallback: configured?.fallback === "builtin-checklist" ? configured.fallback : "builtin-checklist",
2951
- blockOnFindings: typeof configured?.blockOnFindings === "boolean" ? configured.blockOnFindings : true,
2952
- minorPolicy: configured?.minorPolicy === "block" ? "block" : "warn"
3002
+ evidenceMode: configured?.evidenceMode === "any" ? "any" : "path_required",
3003
+ findings: configured?.findings === "optional" ? "optional" : "required",
3004
+ decisionEnum: configuredDecisionEnum.length > 0 ? configuredDecisionEnum : DEFAULT_PRE_PR_DECISION_ENUM
2953
3005
  };
2954
3006
  }
2955
3007
 
@@ -2977,17 +3029,20 @@ function isPrePrReviewSatisfied(feature, prePrReviewPolicy) {
2977
3029
  if (!feature.docs.prePrEvidenceFieldExists || !feature.prePrReview.evidenceProvided) {
2978
3030
  return false;
2979
3031
  }
3032
+ if (prePrReviewPolicy.findings === "required" && (!feature.docs.prePrFindingsFieldExists || !feature.prePrReview.findingsProvided)) {
3033
+ return false;
3034
+ }
2980
3035
  if (!feature.docs.prePrDecisionFieldExists || !feature.prePrReview.decisionProvided) {
2981
3036
  return false;
2982
3037
  }
3038
+ if (feature.prePrReview.decisionOutcome !== "approve") {
3039
+ return false;
3040
+ }
2983
3041
  return true;
2984
3042
  }
2985
3043
  function isFeatureDone(feature, workflowPolicy, prePrReviewPolicy) {
2986
3044
  return feature.specStatus === "Approved" && feature.planStatus === "Approved" && !feature.git.docsHasUncommittedChanges && !feature.git.projectHasUncommittedChanges && feature.docs.tasksExists && feature.tasks.total > 0 && feature.tasks.total === feature.tasks.done && isCompletionChecklistDone(feature) && isTasksDocApproved(feature) && (!workflowPolicy.requireIssue || !!feature.issueNumber) && (!workflowPolicy.requirePr || isPrMetadataConfigured(feature) && !!feature.pr.link) && (!workflowPolicy.requireReview || feature.pr.status === "Approved") && isPrePrReviewSatisfied(feature, prePrReviewPolicy);
2987
3045
  }
2988
- function formatSkillList(skills) {
2989
- return skills.join(", ");
2990
- }
2991
3046
  function getPrReviewRemoteBlockReasons(feature, lang) {
2992
3047
  const remote = feature.pr.remote;
2993
3048
  if (!remote || !remote.available) return [];
@@ -3024,6 +3079,14 @@ function getPrReviewRemoteBlockReasons(feature, lang) {
3024
3079
  function normalizeCommitTopicText(value) {
3025
3080
  return value.replace(/\s+/g, " ").trim();
3026
3081
  }
3082
+ function toShellArg(value) {
3083
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\$/g, "\\$").replace(/`/g, "\\`")}"`;
3084
+ }
3085
+ function buildSelfCliCommand(args) {
3086
+ const entry = process.argv[1] || "dist/index.js";
3087
+ const base = [process.execPath, entry, "--no-banner", ...args];
3088
+ return base.map((arg) => toShellArg(arg)).join(" ");
3089
+ }
3027
3090
  function toShellSafeCommitTopic(value) {
3028
3091
  return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\$/g, "\\$").replace(/`/g, "\\`");
3029
3092
  }
@@ -3792,74 +3855,19 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
3792
3855
  }
3793
3856
  ];
3794
3857
  }
3795
- if (f.prePrReview.status !== "Done") {
3796
- if (!prePrReviewPolicy.skills.length) {
3797
- return [
3798
- {
3799
- type: "instruction",
3800
- category: "pre_pr_review",
3801
- requiresUserCheck: true,
3802
- message: tr(lang, "messages", "prePrReviewRun", {
3803
- skills: "code-review-excellence",
3804
- fallback: prePrReviewPolicy.fallback
3805
- })
3806
- }
3807
- ];
3808
- }
3809
- return [
3810
- {
3811
- type: "instruction",
3812
- category: "pre_pr_review",
3813
- requiresUserCheck: true,
3814
- message: tr(lang, "messages", "prePrReviewRun", {
3815
- skills: formatSkillList(prePrReviewPolicy.skills),
3816
- fallback: prePrReviewPolicy.fallback
3817
- })
3818
- }
3819
- ];
3820
- }
3821
- if (!f.docs.prePrEvidenceFieldExists || !f.prePrReview.evidenceProvided) {
3822
- return [
3823
- {
3824
- type: "instruction",
3825
- category: "pre_pr_review",
3826
- requiresUserCheck: true,
3827
- message: tr(lang, "messages", "prePrReviewEvidenceMissing")
3828
- }
3829
- ];
3830
- }
3831
- if (!f.docs.prePrDecisionFieldExists || !f.prePrReview.decisionProvided) {
3832
- return [
3833
- {
3834
- type: "instruction",
3835
- category: "pre_pr_review",
3836
- requiresUserCheck: true,
3837
- message: tr(lang, "messages", "prePrReviewDecisionMissing")
3838
- }
3839
- ];
3840
- }
3841
- if (!prePrReviewPolicy.skills.length) {
3842
- return [
3843
- {
3844
- type: "instruction",
3845
- category: "pre_pr_review",
3846
- requiresUserCheck: true,
3847
- message: tr(lang, "messages", "prePrReviewRun", {
3848
- skills: "code-review-excellence",
3849
- fallback: prePrReviewPolicy.fallback
3850
- })
3851
- }
3852
- ];
3858
+ const commandArgs = ["pre-pr-review", f.folderName];
3859
+ if (f.type && f.type !== "single") {
3860
+ commandArgs.push("--component", f.type);
3853
3861
  }
3854
3862
  return [
3855
3863
  {
3856
- type: "instruction",
3864
+ type: "command",
3857
3865
  category: "pre_pr_review",
3866
+ operationType: "local",
3858
3867
  requiresUserCheck: true,
3859
- message: tr(lang, "messages", "prePrReviewRun", {
3860
- skills: formatSkillList(prePrReviewPolicy.skills),
3861
- fallback: prePrReviewPolicy.fallback
3862
- })
3868
+ scope: "docs",
3869
+ cwd: f.git.docsGitCwd,
3870
+ cmd: buildSelfCliCommand(commandArgs)
3863
3871
  }
3864
3872
  ];
3865
3873
  }
@@ -4556,6 +4564,52 @@ function hasStructuredReviewDecision(value) {
4556
4564
  if (!trimmed) return false;
4557
4565
  return /^(?:decision|결정)\s*[::]\s*\S.+$/i.test(trimmed);
4558
4566
  }
4567
+ function parsePrePrDecisionOutcome(value) {
4568
+ if (!value) return void 0;
4569
+ const trimmed = value.trim();
4570
+ if (!trimmed) return void 0;
4571
+ const structured = trimmed.match(/^(?:decision|결정)\s*[::]\s*(.+)$/i);
4572
+ if (!structured) return void 0;
4573
+ const payload = structured[1].trim().toLowerCase();
4574
+ const normalized = payload.split(/[,\s-]+/)[0]?.replace(/[^a-z_]/g, "");
4575
+ if (!normalized) return void 0;
4576
+ if (normalized === "approve" || normalized === "approved") {
4577
+ return "approve";
4578
+ }
4579
+ if (normalized === "changes_requested" || normalized === "changes" || normalized === "change") {
4580
+ return "changes_requested";
4581
+ }
4582
+ if (normalized === "blocked" || normalized === "block") {
4583
+ return "blocked";
4584
+ }
4585
+ return void 0;
4586
+ }
4587
+ function resolveEvidencePathValue(value) {
4588
+ const trimmed = value.trim();
4589
+ const mdLink = trimmed.match(/\(([^)]+)\)/);
4590
+ if (mdLink && mdLink[1]) return mdLink[1].trim();
4591
+ return trimmed.split(/\s+/)[0] || "";
4592
+ }
4593
+ async function isPrePrEvidenceProvided(rawValue, policy, context) {
4594
+ if (isPlaceholderReviewEvidence(rawValue)) return false;
4595
+ if (policy.evidenceMode !== "path_required") return true;
4596
+ if (!rawValue) return false;
4597
+ const evidencePath = resolveEvidencePathValue(rawValue);
4598
+ if (!evidencePath) return false;
4599
+ if (/^https?:\/\//i.test(evidencePath)) return false;
4600
+ const candidates = /* @__PURE__ */ new Set();
4601
+ if (path22.isAbsolute(evidencePath)) {
4602
+ candidates.add(path22.resolve(evidencePath));
4603
+ } else {
4604
+ candidates.add(path22.resolve(context.featurePath, evidencePath));
4605
+ candidates.add(path22.resolve(context.docsDir, evidencePath));
4606
+ candidates.add(path22.resolve(path22.dirname(context.docsDir), evidencePath));
4607
+ }
4608
+ for (const candidate of candidates) {
4609
+ if (await fs16.pathExists(candidate)) return true;
4610
+ }
4611
+ return false;
4612
+ }
4559
4613
  function parseIssueNumber(value) {
4560
4614
  if (!value) return void 0;
4561
4615
  const match = value.match(/#?(\d+)/);
@@ -4848,9 +4902,15 @@ function isPrePrReviewSatisfied2(feature, policy) {
4848
4902
  if (!feature.docs.prePrEvidenceFieldExists || !feature.prePrReview.evidenceProvided) {
4849
4903
  return false;
4850
4904
  }
4905
+ if (policy.findings === "required" && (!feature.docs.prePrFindingsFieldExists || !feature.prePrReview.findingsProvided)) {
4906
+ return false;
4907
+ }
4851
4908
  if (!feature.docs.prePrDecisionFieldExists || !feature.prePrReview.decisionProvided) {
4852
4909
  return false;
4853
4910
  }
4911
+ if (feature.prePrReview.decisionOutcome !== "approve") {
4912
+ return false;
4913
+ }
4854
4914
  return true;
4855
4915
  }
4856
4916
  async function parseFeature(featurePath, type, context, options) {
@@ -4917,9 +4977,11 @@ async function parseFeature(featurePath, type, context, options) {
4917
4977
  let completionChecklist;
4918
4978
  let prePrReviewStatus;
4919
4979
  let prePrFindings;
4980
+ let prePrFindingsProvided = false;
4920
4981
  let prePrEvidence;
4921
4982
  let prePrEvidenceProvided = false;
4922
4983
  let prePrDecision;
4984
+ let prePrDecisionOutcome;
4923
4985
  let prePrDecisionProvided = false;
4924
4986
  let prReviewFindings;
4925
4987
  let prReviewEvidence;
@@ -4994,6 +5056,7 @@ async function parseFeature(featurePath, type, context, options) {
4994
5056
  "Pre-PR Findings"
4995
5057
  ]);
4996
5058
  prePrFindings = parseReviewFindings(prePrFindingsValue);
5059
+ prePrFindingsProvided = !!prePrFindings;
4997
5060
  const prePrEvidenceValue = extractFirstSpecValue(content, [
4998
5061
  "PR \uC804 \uB9AC\uBDF0 Evidence",
4999
5062
  "Pre-PR Evidence"
@@ -5003,7 +5066,11 @@ async function parseFeature(featurePath, type, context, options) {
5003
5066
  "Pre-PR Evidence"
5004
5067
  ]);
5005
5068
  prePrEvidence = prePrEvidenceValue?.trim();
5006
- prePrEvidenceProvided = !isPlaceholderReviewEvidence(prePrEvidenceValue);
5069
+ prePrEvidenceProvided = await isPrePrEvidenceProvided(
5070
+ prePrEvidenceValue,
5071
+ prePrReviewPolicy,
5072
+ { featurePath, docsDir: context.docsDir }
5073
+ );
5007
5074
  const prePrDecisionValue = extractFirstSpecValue(content, [
5008
5075
  "PR \uC804 \uB9AC\uBDF0 Decision",
5009
5076
  "Pre-PR Decision"
@@ -5013,7 +5080,8 @@ async function parseFeature(featurePath, type, context, options) {
5013
5080
  "Pre-PR Decision"
5014
5081
  ]);
5015
5082
  prePrDecision = prePrDecisionValue?.trim();
5016
- prePrDecisionProvided = !isPlaceholderReviewEvidence(prePrDecisionValue) && hasStructuredReviewDecision(prePrDecisionValue);
5083
+ prePrDecisionOutcome = parsePrePrDecisionOutcome(prePrDecisionValue);
5084
+ prePrDecisionProvided = !isPlaceholderReviewEvidence(prePrDecisionValue) && hasStructuredReviewDecision(prePrDecisionValue) && !!prePrDecisionOutcome && prePrReviewPolicy.decisionEnum.includes(prePrDecisionOutcome);
5017
5085
  const prReviewFindingsValue = extractFirstSpecValue(content, [
5018
5086
  "PR \uB9AC\uBDF0 Findings",
5019
5087
  "PR Review Findings"
@@ -5188,6 +5256,9 @@ async function parseFeature(featurePath, type, context, options) {
5188
5256
  if (tasksExists && prePrReviewPolicy.enabled && !prePrReviewFieldExists) {
5189
5257
  warnings.push(tr(lang, "warnings", "legacyTasksPrePrReviewField"));
5190
5258
  }
5259
+ if (tasksExists && prePrReviewPolicy.enabled && prePrReviewPolicy.findings === "required" && !prePrFindingsFieldExists) {
5260
+ warnings.push(tr(lang, "warnings", "legacyTasksPrePrFindingsField"));
5261
+ }
5191
5262
  if (tasksExists && prePrReviewPolicy.enabled && !prePrEvidenceFieldExists) {
5192
5263
  warnings.push(tr(lang, "warnings", "legacyTasksPrePrEvidenceField"));
5193
5264
  }
@@ -5232,12 +5303,15 @@ async function parseFeature(featurePath, type, context, options) {
5232
5303
  {
5233
5304
  docs: {
5234
5305
  prePrReviewFieldExists,
5306
+ prePrFindingsFieldExists,
5235
5307
  prePrEvidenceFieldExists,
5236
5308
  prePrDecisionFieldExists
5237
5309
  },
5238
5310
  prePrReview: {
5239
5311
  status: prePrReviewStatus,
5312
+ findingsProvided: prePrFindingsProvided,
5240
5313
  evidenceProvided: prePrEvidenceProvided,
5314
+ decisionOutcome: prePrDecisionOutcome,
5241
5315
  decisionProvided: prePrDecisionProvided
5242
5316
  }
5243
5317
  },
@@ -5278,10 +5352,18 @@ async function parseFeature(featurePath, type, context, options) {
5278
5352
  warnings.push(tr(lang, "warnings", "workflowPrePrReviewMissing"));
5279
5353
  } else if (prePrReviewStatus !== "Done") {
5280
5354
  warnings.push(tr(lang, "warnings", "workflowPrePrReviewNotDone"));
5355
+ } else if (prePrReviewPolicy.findings === "required" && (!prePrFindingsFieldExists || !prePrFindingsProvided)) {
5356
+ warnings.push(tr(lang, "warnings", "workflowPrePrFindingsMissing"));
5281
5357
  } else if (!prePrEvidenceFieldExists || !prePrEvidenceProvided) {
5282
5358
  warnings.push(tr(lang, "warnings", "workflowPrePrEvidenceMissing"));
5283
5359
  } else if (!prePrDecisionFieldExists || !prePrDecisionProvided) {
5284
5360
  warnings.push(tr(lang, "warnings", "workflowPrePrDecisionMissing"));
5361
+ } else if (prePrDecisionOutcome !== "approve") {
5362
+ warnings.push(
5363
+ tr(lang, "warnings", "workflowPrePrDecisionNotApproved", {
5364
+ outcome: prePrDecisionOutcome || "-"
5365
+ })
5366
+ );
5285
5367
  }
5286
5368
  }
5287
5369
  }
@@ -5307,9 +5389,11 @@ async function parseFeature(featurePath, type, context, options) {
5307
5389
  prePrReview: {
5308
5390
  status: prePrReviewStatus,
5309
5391
  findings: prePrFindings,
5392
+ findingsProvided: prePrFindingsProvided,
5310
5393
  evidence: prePrEvidence,
5311
5394
  evidenceProvided: prePrEvidenceProvided,
5312
5395
  decision: prePrDecision,
5396
+ decisionOutcome: prePrDecisionOutcome,
5313
5397
  decisionProvided: prePrDecisionProvided
5314
5398
  },
5315
5399
  prReview: {
@@ -5867,6 +5951,18 @@ function normalizeSkillList2(raw) {
5867
5951
  }
5868
5952
  return [...deduped];
5869
5953
  }
5954
+ function normalizeDecisionEnumList2(raw) {
5955
+ if (!Array.isArray(raw)) return [];
5956
+ const deduped = /* @__PURE__ */ new Set();
5957
+ for (const item of raw) {
5958
+ const value = String(item || "").trim().toLowerCase();
5959
+ if (!value) continue;
5960
+ if (value === "approve" || value === "changes_requested" || value === "blocked") {
5961
+ deduped.add(value);
5962
+ }
5963
+ }
5964
+ return [...deduped];
5965
+ }
5870
5966
  async function backfillMissingConfigDefaults(docsDir) {
5871
5967
  const configPath = path22.join(docsDir, ".lee-spec-kit.json");
5872
5968
  if (!await fs16.pathExists(configPath)) {
@@ -5915,8 +6011,39 @@ async function backfillMissingConfigDefaults(docsDir) {
5915
6011
  }
5916
6012
  }
5917
6013
  setIfMissing(prePrReview, "fallback", "builtin-checklist", "workflow.prePrReview.fallback");
5918
- setIfMissing(prePrReview, "blockOnFindings", true, "workflow.prePrReview.blockOnFindings");
5919
- setIfMissing(prePrReview, "minorPolicy", "warn", "workflow.prePrReview.minorPolicy");
6014
+ setIfMissing(
6015
+ prePrReview,
6016
+ "evidenceMode",
6017
+ "path_required",
6018
+ "workflow.prePrReview.evidenceMode"
6019
+ );
6020
+ if (prePrReview.evidenceMode !== void 0 && prePrReview.evidenceMode !== "path_required" && prePrReview.evidenceMode !== "any") {
6021
+ prePrReview.evidenceMode = "path_required";
6022
+ changedPaths.push("workflow.prePrReview.evidenceMode");
6023
+ }
6024
+ setIfMissing(
6025
+ prePrReview,
6026
+ "findings",
6027
+ "required",
6028
+ "workflow.prePrReview.findings"
6029
+ );
6030
+ if (prePrReview.findings !== void 0 && prePrReview.findings !== "required" && prePrReview.findings !== "optional") {
6031
+ prePrReview.findings = "required";
6032
+ changedPaths.push("workflow.prePrReview.findings");
6033
+ }
6034
+ if (prePrReview.decisionEnum === void 0) {
6035
+ prePrReview.decisionEnum = ["approve", "changes_requested", "blocked"];
6036
+ changedPaths.push("workflow.prePrReview.decisionEnum");
6037
+ } else {
6038
+ const normalizedDecisionEnum = normalizeDecisionEnumList2(prePrReview.decisionEnum);
6039
+ if (normalizedDecisionEnum.length === 0) {
6040
+ prePrReview.decisionEnum = ["approve", "changes_requested", "blocked"];
6041
+ changedPaths.push("workflow.prePrReview.decisionEnum");
6042
+ } else if (JSON.stringify(normalizedDecisionEnum) !== JSON.stringify(prePrReview.decisionEnum)) {
6043
+ prePrReview.decisionEnum = normalizedDecisionEnum;
6044
+ changedPaths.push("workflow.prePrReview.decisionEnum");
6045
+ }
6046
+ }
5920
6047
  if (!isPlainObject(raw.pr)) {
5921
6048
  raw.pr = {};
5922
6049
  changedPaths.push("pr");
@@ -6312,21 +6439,27 @@ function buildActionDetail(action, lang) {
6312
6439
  const branchMatch = command.match(/\bfeat\/([A-Za-z0-9._-]+)/);
6313
6440
  const worktree = worktreeMatch ? `.worktrees/${worktreeMatch[1]}` : null;
6314
6441
  const branch = branchMatch ? `feat/${branchMatch[1]}` : null;
6442
+ if (action.type !== "command") {
6443
+ return tr(lang, "cli", "context.commandDetail.branchCreateGeneric", {
6444
+ scope: "project"
6445
+ });
6446
+ }
6447
+ const scope = action.scope;
6315
6448
  if (worktree && branch) {
6316
6449
  return tr(lang, "cli", "context.commandDetail.branchCreateWithWorktree", {
6317
- scope: action.scope,
6450
+ scope,
6318
6451
  worktree,
6319
6452
  branch
6320
6453
  });
6321
6454
  }
6322
6455
  if (branch) {
6323
6456
  return tr(lang, "cli", "context.commandDetail.branchCreateWithBranch", {
6324
- scope: action.scope,
6457
+ scope,
6325
6458
  branch
6326
6459
  });
6327
6460
  }
6328
6461
  return tr(lang, "cli", "context.commandDetail.branchCreateGeneric", {
6329
- scope: action.scope
6462
+ scope
6330
6463
  });
6331
6464
  };
6332
6465
  const extractCommitMessage = (command) => {
@@ -6989,6 +7122,72 @@ function buildSuggestionFinalPrompt(lang, suggestionOptions) {
6989
7122
  example
6990
7123
  });
6991
7124
  }
7125
+ function normalizeCategoryToken(value) {
7126
+ if (typeof value !== "string") return null;
7127
+ const normalized = value.trim().toLowerCase();
7128
+ if (!normalized) return null;
7129
+ return normalized;
7130
+ }
7131
+ function resolveAutoRunCategories(approval) {
7132
+ const known = new Set(ACTION_CATEGORIES);
7133
+ const unique2 = /* @__PURE__ */ new Set();
7134
+ const unknown = /* @__PURE__ */ new Set();
7135
+ for (const raw of approval?.requireCheckCategories ?? approval?.requireOkCategories ?? []) {
7136
+ const normalized = normalizeCategoryToken(raw);
7137
+ if (!normalized) continue;
7138
+ if (known.has(normalized)) {
7139
+ unique2.add(normalized);
7140
+ } else {
7141
+ unknown.add(normalized);
7142
+ }
7143
+ }
7144
+ return {
7145
+ untilCategories: Array.from(unique2),
7146
+ unknownCategories: Array.from(unknown)
7147
+ };
7148
+ }
7149
+ function buildAutoRunCommand(state, featureName, selectedComponent, untilCategories) {
7150
+ if (untilCategories.length === 0) return "";
7151
+ const featureRef = resolveFeatureRefForApproval(state, featureName);
7152
+ const componentArg = selectedComponent ? ` --component ${selectedComponent}` : "";
7153
+ return `npx lee-spec-kit flow ${featureRef}${componentArg} --auto-until-category ${untilCategories.join(",")}`;
7154
+ }
7155
+ function resolveAutoRunPlan(lang, state, featureName, selectedComponent, approval, approvalRequired) {
7156
+ const base = (reasonCode, untilCategories2 = [], unknownCategories2 = []) => ({
7157
+ available: false,
7158
+ reasonCode,
7159
+ summary: tr(lang, "cli", "context.autoRunUnavailable"),
7160
+ command: "",
7161
+ untilCategories: untilCategories2,
7162
+ unknownCategories: unknownCategories2
7163
+ });
7164
+ if (state.status !== "single_matched") return base("NOT_SINGLE_MATCHED");
7165
+ if (state.actionOptions.length === 0) return base("NO_ACTION_OPTIONS");
7166
+ if (approvalRequired) return base("APPROVAL_REQUIRED");
7167
+ const mode = approval?.mode ?? "builtin";
7168
+ if (mode !== "category") return base("APPROVAL_MODE_NOT_CATEGORY");
7169
+ const defaultPolicy = approval?.default ?? "keep";
7170
+ if (defaultPolicy !== "skip") return base("DEFAULT_NOT_SKIP");
7171
+ const { untilCategories, unknownCategories } = resolveAutoRunCategories(approval);
7172
+ if (untilCategories.length === 0) {
7173
+ return base("NO_REQUIRE_CHECK_CATEGORIES", [], unknownCategories);
7174
+ }
7175
+ return {
7176
+ available: true,
7177
+ reasonCode: "AVAILABLE",
7178
+ summary: tr(lang, "cli", "context.autoRunSummary", {
7179
+ categories: untilCategories.join(", ")
7180
+ }),
7181
+ command: buildAutoRunCommand(
7182
+ state,
7183
+ featureName,
7184
+ selectedComponent,
7185
+ untilCategories
7186
+ ),
7187
+ untilCategories,
7188
+ unknownCategories
7189
+ };
7190
+ }
6992
7191
  function toSuggestionLabel(index) {
6993
7192
  let n = index + 1;
6994
7193
  let label = "";
@@ -7182,12 +7381,18 @@ function getListLabel(f, stepsMap, lang, workflowPolicy, prePrReviewPolicy) {
7182
7381
  if (prePrReviewPolicy.enabled && f.prePrReview.status !== "Done") {
7183
7382
  return tr(lang, "cli", "context.list.completePrePrReview");
7184
7383
  }
7384
+ if (prePrReviewPolicy.enabled && prePrReviewPolicy.findings === "required" && (!f.docs.prePrFindingsFieldExists || !f.prePrReview.findingsProvided)) {
7385
+ return tr(lang, "cli", "context.list.addPrePrFindings");
7386
+ }
7185
7387
  if (prePrReviewPolicy.enabled && (!f.docs.prePrEvidenceFieldExists || !f.prePrReview.evidenceProvided)) {
7186
7388
  return tr(lang, "cli", "context.list.addPrePrEvidence");
7187
7389
  }
7188
7390
  if (prePrReviewPolicy.enabled && (!f.docs.prePrDecisionFieldExists || !f.prePrReview.decisionProvided)) {
7189
7391
  return tr(lang, "cli", "context.list.addPrePrDecision");
7190
7392
  }
7393
+ if (prePrReviewPolicy.enabled && f.prePrReview.decisionOutcome !== "approve") {
7394
+ return tr(lang, "cli", "context.list.resolvePrePrDecision");
7395
+ }
7191
7396
  if (workflowPolicy.requirePr && !f.pr.link) {
7192
7397
  return tr(lang, "cli", "context.list.recordPrLink");
7193
7398
  }
@@ -7244,7 +7449,9 @@ function toCompactFeature(feature) {
7244
7449
  prePrReview: {
7245
7450
  status: feature.prePrReview.status,
7246
7451
  findings: feature.prePrReview.findings,
7452
+ findingsProvided: feature.prePrReview.findingsProvided,
7247
7453
  evidenceProvided: feature.prePrReview.evidenceProvided,
7454
+ decisionOutcome: feature.prePrReview.decisionOutcome,
7248
7455
  decisionProvided: feature.prePrReview.decisionProvided
7249
7456
  },
7250
7457
  prReview: {
@@ -7385,6 +7592,26 @@ async function runContext(featureName, options) {
7385
7592
  selectedComponent
7386
7593
  );
7387
7594
  const suggestionFinalPrompt = buildSuggestionFinalPrompt(lang, suggestionOptions);
7595
+ const checkRequiredLabels = state.actionOptions.filter((option) => !!option.action.requiresUserCheck).map((option) => option.label);
7596
+ const checkRequiredCategories = [
7597
+ ...new Set(
7598
+ state.actionOptions.filter((option) => !!option.action.requiresUserCheck).map((option) => option.action.category || "uncategorized")
7599
+ )
7600
+ ];
7601
+ const approvalRequired = checkRequiredLabels.length > 0;
7602
+ const finalApprovalPrompt = approvalRequired ? buildFinalApprovalPrompt(lang, state.actionOptions) : "";
7603
+ const approvalUserFacingLines = approvalRequired ? [
7604
+ ...state.actionOptions.map((o) => o.approvalPrompt),
7605
+ finalApprovalPrompt
7606
+ ].filter((line) => line.length > 0) : [];
7607
+ const autoRunPlan = resolveAutoRunPlan(
7608
+ lang,
7609
+ state,
7610
+ featureName,
7611
+ selectedComponent,
7612
+ config.approval,
7613
+ approvalRequired
7614
+ );
7388
7615
  if (options.approve || options.execute) {
7389
7616
  await runApprovedOption(
7390
7617
  state,
@@ -7399,7 +7626,6 @@ async function runContext(featureName, options) {
7399
7626
  const jsonMode = !!options.json || !!options.jsonCompact;
7400
7627
  if (jsonMode) {
7401
7628
  const primaryAction = state.actionOptions[0] ?? null;
7402
- const finalApprovalPrompt = buildFinalApprovalPrompt(lang, state.actionOptions);
7403
7629
  const approveCommand = buildApprovalCommand(
7404
7630
  state,
7405
7631
  featureName,
@@ -7452,19 +7678,28 @@ async function runContext(featureName, options) {
7452
7678
  activeCategories,
7453
7679
  knownCategories: ACTION_CATEGORIES,
7454
7680
  uncategorizedLabels,
7681
+ checkRequiredLabels,
7682
+ checkRequiredCategories,
7683
+ approvalRequired,
7455
7684
  categoryPolicyGuidance: 'For approval.mode="category", match against `actionOptions[].category`.',
7456
- oneApprovalPerAction: true,
7685
+ oneApprovalPerAction: approvalRequired,
7457
7686
  requireFreshContext: true,
7458
7687
  contextVersion: state.contextVersion,
7459
7688
  config: config.approval ?? { mode: "builtin" }
7460
7689
  },
7690
+ autoRun: {
7691
+ available: autoRunPlan.available,
7692
+ reasonCode: autoRunPlan.reasonCode,
7693
+ summary: autoRunPlan.summary,
7694
+ command: autoRunPlan.command,
7695
+ untilCategories: autoRunPlan.untilCategories,
7696
+ unknownCategories: autoRunPlan.unknownCategories
7697
+ },
7461
7698
  approvalRequest: {
7699
+ required: approvalRequired,
7462
7700
  finalPrompt: finalApprovalPrompt,
7463
- userFacingLines: [
7464
- ...state.actionOptions.map((o) => o.approvalPrompt),
7465
- finalApprovalPrompt
7466
- ].filter((line) => line.length > 0),
7467
- labels: state.actionOptions.map((o) => o.label),
7701
+ userFacingLines: approvalUserFacingLines,
7702
+ labels: approvalRequired ? state.actionOptions.map((o) => o.label) : [],
7468
7703
  approveCommand,
7469
7704
  executeCommand,
7470
7705
  executeRequiresTicket: !!state.actionOptions[0]?.action?.requiresUserCheck
@@ -7523,27 +7758,37 @@ async function runContext(featureName, options) {
7523
7758
  activeCategories,
7524
7759
  knownCategories: ACTION_CATEGORIES,
7525
7760
  uncategorizedLabels,
7761
+ checkRequiredLabels,
7762
+ checkRequiredCategories,
7763
+ approvalRequired,
7526
7764
  categoryPolicyGuidance: 'For approval.mode="category", match against `actionOptions[].category`.',
7527
- requireExplanationBeforeApproval: true,
7528
- requiredExplanationFields: [
7765
+ requireExplanationBeforeApproval: approvalRequired,
7766
+ requiredExplanationFields: approvalRequired ? [
7529
7767
  "actionOptions[].label",
7530
7768
  "actionOptions[].detail",
7531
7769
  "actionOptions[].approvalPrompt"
7532
- ],
7770
+ ] : [],
7533
7771
  recommendation: "Before asking for approval, show only `actionOptions[].approvalPrompt` lines and `approvalRequest.finalPrompt` to the user. Keep `requiredDocs`, `checkPolicy`, and raw execution commands as internal guidance. For commit actions, include scope (`docs`/`project`) and commit message in the visible prompt. User replies should include the label token (e.g. `A`, `A OK`, `A proceed`, `A \uC9C4\uD589\uD574`). For command execution, prefer one-shot `npx lee-spec-kit flow <featureRef> --approve <LABEL> --execute` to avoid session mismatch after context compression/reset. Use ticket-based `context --execute --ticket` only when explicitly needed.",
7534
- oneApprovalPerAction: true,
7772
+ oneApprovalPerAction: approvalRequired,
7535
7773
  requireFreshContext: true,
7536
7774
  contextVersion: state.contextVersion,
7537
7775
  config: config.approval ?? { mode: "builtin" }
7538
7776
  },
7777
+ autoRun: {
7778
+ available: autoRunPlan.available,
7779
+ reasonCode: autoRunPlan.reasonCode,
7780
+ summary: autoRunPlan.summary,
7781
+ command: autoRunPlan.command,
7782
+ untilCategories: autoRunPlan.untilCategories,
7783
+ unknownCategories: autoRunPlan.unknownCategories,
7784
+ guidance: "Use auto-run only when `autoRun.available=true`. Stop and request approval when `approvalRequest.required=true` or when auto mode reaches configured gate categories."
7785
+ },
7539
7786
  approvalRequest: {
7540
7787
  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`.",
7788
+ required: approvalRequired,
7541
7789
  finalPrompt: finalApprovalPrompt,
7542
- userFacingLines: [
7543
- ...state.actionOptions.map((o) => o.approvalPrompt),
7544
- finalApprovalPrompt
7545
- ].filter((line) => line.length > 0),
7546
- labels: state.actionOptions.map((o) => o.label),
7790
+ userFacingLines: approvalUserFacingLines,
7791
+ labels: approvalRequired ? state.actionOptions.map((o) => o.label) : [],
7547
7792
  approveCommand,
7548
7793
  executeCommand,
7549
7794
  executeRequiresTicket: !!state.actionOptions[0]?.action?.requiresUserCheck,
@@ -7751,7 +7996,7 @@ async function runContext(featureName, options) {
7751
7996
  const f = state.targetFeatures[0];
7752
7997
  const stepName = stepsMap[f.currentStep] || "Unknown";
7753
7998
  const checkTag = (requiresUserCheck) => requiresUserCheck ? chalk6.yellow(tr(lang, "cli", "context.checkRequired")) : "";
7754
- const hasCheckAction = (f.actions || []).some((a) => !!a.requiresUserCheck);
7999
+ const hasCheckAction = approvalRequired;
7755
8000
  console.log(
7756
8001
  `\u{1F539} Feature: ${chalk6.bold(f.folderName)} ${config.projectType === "multi" ? chalk6.cyan(`(${f.type})`) : ""}`
7757
8002
  );
@@ -7823,8 +8068,17 @@ async function runContext(featureName, options) {
7823
8068
  );
7824
8069
  }
7825
8070
  }
7826
- if (actionOptions.length > 0) {
7827
- const finalApprovalPrompt = buildFinalApprovalPrompt(lang, actionOptions);
8071
+ if (autoRunPlan.available) {
8072
+ console.log(chalk6.gray(` \u21B3 ${autoRunPlan.summary}`));
8073
+ console.log(
8074
+ chalk6.gray(
8075
+ ` \u21B3 ${tr(lang, "cli", "context.autoRunCommandHint", {
8076
+ command: autoRunPlan.command
8077
+ })}`
8078
+ )
8079
+ );
8080
+ }
8081
+ if (actionOptions.length > 0 && hasCheckAction) {
7828
8082
  const approveCommand = buildApprovalCommand(
7829
8083
  state,
7830
8084
  featureName,
@@ -7950,7 +8204,7 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
7950
8204
  contextVersion: freshState.contextVersion,
7951
8205
  executable: selectedAction.type === "command",
7952
8206
  executeRequiresTicket,
7953
- oneApprovalPerAction: true,
8207
+ oneApprovalPerAction: executeRequiresTicket,
7954
8208
  approvalTicket: ticket ? {
7955
8209
  token: ticket.token,
7956
8210
  sessionId: ticket.sessionId,
@@ -9014,6 +9268,9 @@ function toAutoReasonCode(status) {
9014
9268
  return "AUTO_EXECUTION_FAILED";
9015
9269
  }
9016
9270
  }
9271
+ function isAutoRunFailureStatus(status) {
9272
+ return status === "manual_required" || status === "selection_required" || status === "no_progress" || status === "request_label_missing" || status === "request_failed" || status === "execution_failed";
9273
+ }
9017
9274
  async function runAutoUntilCategory(config, featureName, selectionOptions, untilCategories, requestText, metadata) {
9018
9275
  const contextArgs = ["context", ...buildSelectionArgs(featureName, selectionOptions)];
9019
9276
  const gateSet = new Set(untilCategories);
@@ -9422,9 +9679,10 @@ async function runFlow(featureName, options) {
9422
9679
  }
9423
9680
  }
9424
9681
  if (options.json) {
9682
+ const autoRunFailed = !!(autoRun && isAutoRunFailureStatus(autoRun.status));
9425
9683
  const payload = {
9426
- status: "ok",
9427
- reasonCode: "FLOW_SUMMARY",
9684
+ status: autoRunFailed ? "error" : "ok",
9685
+ reasonCode: autoRunFailed ? autoRun?.reasonCode || "AUTO_EXECUTION_FAILED" : "FLOW_SUMMARY",
9428
9686
  context: {
9429
9687
  before: {
9430
9688
  status: before.status,
@@ -9453,6 +9711,9 @@ async function runFlow(featureName, options) {
9453
9711
  suggestion: after.matchedFeature ? `npx lee-spec-kit context ${after.matchedFeature.folderName}${componentHint}` : `npx lee-spec-kit context${componentHint}`
9454
9712
  };
9455
9713
  console.log(JSON.stringify(payload, null, 2));
9714
+ if (autoRunFailed) {
9715
+ process.exitCode = 1;
9716
+ }
9456
9717
  return;
9457
9718
  }
9458
9719
  console.log();
@@ -9498,6 +9759,9 @@ async function runFlow(featureName, options) {
9498
9759
  console.log(chalk6.gray("Auto gate reached. Reply with one of the labels shown above (example: A OK)."));
9499
9760
  console.log();
9500
9761
  }
9762
+ if (autoRun && isAutoRunFailureStatus(autoRun.status)) {
9763
+ process.exitCode = 1;
9764
+ }
9501
9765
  if (after.matchedFeature) {
9502
9766
  console.log(
9503
9767
  chalk6.blue(
@@ -11986,6 +12250,263 @@ async function runOnboard(options) {
11986
12250
  process.exitCode = 1;
11987
12251
  }
11988
12252
  }
12253
+ function escapeRegExp4(value) {
12254
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12255
+ }
12256
+ function normalizeDecision(raw) {
12257
+ if (!raw) return null;
12258
+ const value = raw.trim().toLowerCase().replace(/[\s-]+/g, "_");
12259
+ if (value === "approve" || value === "approved") return "approve";
12260
+ if (value === "changes_requested" || value === "change_requested" || value === "changes") {
12261
+ return "changes_requested";
12262
+ }
12263
+ if (value === "blocked" || value === "block") return "blocked";
12264
+ return null;
12265
+ }
12266
+ function parseNonNegativeInt(raw, label) {
12267
+ if (raw === void 0) return null;
12268
+ const value = Number(raw);
12269
+ if (!Number.isInteger(value) || value < 0) {
12270
+ throw createCliError(
12271
+ "INVALID_ARGUMENT",
12272
+ `\`${label}\` must be a non-negative integer.`
12273
+ );
12274
+ }
12275
+ return value;
12276
+ }
12277
+ function findSpecLineIndex(lines, keys) {
12278
+ const escaped = keys.map((key) => escapeRegExp4(key));
12279
+ const re = new RegExp(`^\\s*-\\s*\\*\\*(?:${escaped.join("|")})\\*\\*\\s*:\\s*`);
12280
+ return lines.findIndex((line) => re.test(line));
12281
+ }
12282
+ function replaceSpecLine(line, keys, preferredKey, value) {
12283
+ const escaped = keys.map((key) => escapeRegExp4(key));
12284
+ const re = new RegExp(
12285
+ `^(\\s*-\\s*\\*\\*)(?:${escaped.join("|")})(\\*\\*\\s*:\\s*)(.*)$`
12286
+ );
12287
+ if (!re.test(line)) return line;
12288
+ return line.replace(re, `$1${preferredKey}$2${value}`);
12289
+ }
12290
+ function computeInsertIndex(lines, anchorKeys) {
12291
+ const anchorIndex = findSpecLineIndex(lines, anchorKeys);
12292
+ if (anchorIndex !== -1) {
12293
+ let cursor = anchorIndex + 1;
12294
+ while (cursor < lines.length && /^\s{2,}-\s+/.test(lines[cursor])) {
12295
+ cursor += 1;
12296
+ }
12297
+ return cursor;
12298
+ }
12299
+ const sectionIndex = lines.findIndex(
12300
+ (line) => /^\s*##\s+(Task List|태스크 목록)\s*$/.test(line)
12301
+ );
12302
+ if (sectionIndex !== -1) return sectionIndex;
12303
+ return lines.length;
12304
+ }
12305
+ function upsertSpecLine(content, keys, preferredKey, value, anchorKeys) {
12306
+ const lines = content.split("\n");
12307
+ const index = findSpecLineIndex(lines, keys);
12308
+ if (index !== -1) {
12309
+ lines[index] = replaceSpecLine(lines[index], keys, preferredKey, value);
12310
+ return lines.join("\n");
12311
+ }
12312
+ const insertAt = computeInsertIndex(lines, anchorKeys);
12313
+ lines.splice(insertAt, 0, `- **${preferredKey}**: ${value}`);
12314
+ return lines.join("\n");
12315
+ }
12316
+ function normalizePathForDoc(value) {
12317
+ return value.replace(/\\/g, "/");
12318
+ }
12319
+ function getPreferredKeys(lang) {
12320
+ if (lang === "ko") {
12321
+ return {
12322
+ review: "PR \uC804 \uB9AC\uBDF0",
12323
+ findings: "PR \uC804 \uB9AC\uBDF0 Findings",
12324
+ evidence: "PR \uC804 \uB9AC\uBDF0 Evidence",
12325
+ decision: "PR \uC804 \uB9AC\uBDF0 Decision",
12326
+ prStatus: "PR \uC0C1\uD0DC"
12327
+ };
12328
+ }
12329
+ return {
12330
+ review: "Pre-PR Review",
12331
+ findings: "Pre-PR Findings",
12332
+ evidence: "Pre-PR Evidence",
12333
+ decision: "Pre-PR Decision",
12334
+ prStatus: "PR Status"
12335
+ };
12336
+ }
12337
+ function buildReportContent(input) {
12338
+ const skills = input.skills.length > 0 ? input.skills.join(", ") : "code-review-excellence";
12339
+ return `# Pre-PR Review Report: ${input.folderName}
12340
+
12341
+ - Date: ${input.date}
12342
+ - Baseline: ${input.fallback}
12343
+ - Skills: ${skills}
12344
+ - Findings: major=${input.major}, minor=${input.minor}
12345
+ - Decision: ${input.decision}
12346
+ - Note: ${input.note}
12347
+
12348
+ ## Baseline Checklist
12349
+
12350
+ - [x] Checked alignment with spec/plan/tasks.
12351
+ - [x] Reviewed risk areas (regression, security, side effects, release readiness).
12352
+ - [x] Reviewed maintainability (structure, reuse, obsolete code cleanup).
12353
+ - [x] Reviewed test/verification coverage (or recorded reason if not run).
12354
+ `;
12355
+ }
12356
+ function prePrReviewCommand(program2) {
12357
+ program2.command("pre-pr-review [feature-name]").description("Run and record pre-PR review evidence for a feature").option("--component <component>", "Component name for multi projects").option(
12358
+ "--decision <outcome>",
12359
+ "Decision outcome: approve | changes_requested | blocked"
12360
+ ).option("--major <count>", "Pre-PR major findings count").option("--minor <count>", "Pre-PR minor findings count").option("--note <text>", "Decision note text").option("--json", "Output JSON").action(async (featureName, options) => {
12361
+ try {
12362
+ await runPrePrReview(featureName, options);
12363
+ } catch (error) {
12364
+ const config = await getConfig(process.cwd());
12365
+ const lang = config?.lang ?? DEFAULT_LANG;
12366
+ const cliError = toCliError(error);
12367
+ const suggestions = getCliErrorSuggestions(cliError.code, lang);
12368
+ if (options.json) {
12369
+ console.log(
12370
+ JSON.stringify({
12371
+ status: "error",
12372
+ reasonCode: cliError.code,
12373
+ error: cliError.message,
12374
+ suggestions
12375
+ })
12376
+ );
12377
+ } else {
12378
+ console.error(chalk6.red(`[${cliError.code}] ${cliError.message}`));
12379
+ printCliErrorSuggestions(suggestions, lang);
12380
+ }
12381
+ process.exitCode = 1;
12382
+ }
12383
+ });
12384
+ }
12385
+ async function runPrePrReview(featureName, options) {
12386
+ const config = await getConfig(process.cwd());
12387
+ if (!config) {
12388
+ throw createCliError(
12389
+ "CONFIG_NOT_FOUND",
12390
+ "No lee-spec-kit config found in this workspace."
12391
+ );
12392
+ }
12393
+ const selectionOptions = {
12394
+ component: resolveComponentOption(options.component)
12395
+ };
12396
+ const state = await resolveContextSelection(config, featureName, selectionOptions);
12397
+ if (state.status !== "single_matched" || !state.matchedFeature) {
12398
+ throw createCliError(
12399
+ "CONTEXT_SELECTION_REQUIRED",
12400
+ "pre-pr-review requires a single matched feature. Pass <feature-name> explicitly."
12401
+ );
12402
+ }
12403
+ const feature = state.matchedFeature;
12404
+ if (!feature.docs.tasksExists) {
12405
+ throw createCliError(
12406
+ "PRECONDITION_FAILED",
12407
+ `tasks.md not found for feature: ${feature.folderName}`
12408
+ );
12409
+ }
12410
+ const tasksPath = path22.join(feature.path, "tasks.md");
12411
+ const tasksContent = await fs16.readFile(tasksPath, "utf-8");
12412
+ const policy = resolvePrePrReviewPolicy(config.workflow);
12413
+ const preferred = getPreferredKeys(config.lang);
12414
+ const date = getLocalDateString();
12415
+ const major = parseNonNegativeInt(options.major, "--major") ?? feature.prePrReview.findings?.major ?? 0;
12416
+ const minor = parseNonNegativeInt(options.minor, "--minor") ?? feature.prePrReview.findings?.minor ?? 0;
12417
+ const explicitDecision = normalizeDecision(options.decision);
12418
+ if (options.decision && !explicitDecision) {
12419
+ throw createCliError(
12420
+ "INVALID_ARGUMENT",
12421
+ "`--decision` must be one of: approve, changes_requested, blocked."
12422
+ );
12423
+ }
12424
+ const inferredDecision = major > 0 ? "changes_requested" : "approve";
12425
+ const decision = explicitDecision || inferredDecision;
12426
+ if (!policy.decisionEnum.includes(decision)) {
12427
+ throw createCliError(
12428
+ "INVALID_ARGUMENT",
12429
+ `Decision "${decision}" is not allowed by workflow.prePrReview.decisionEnum.`
12430
+ );
12431
+ }
12432
+ const note = options.note?.trim() || (decision === "approve" ? "baseline review completed without blocking findings" : decision === "changes_requested" ? "resolve findings before PR creation" : "blocked until prerequisite risk is resolved");
12433
+ const reportPath = path22.join(feature.path, "pre-pr-review.md");
12434
+ const reportContent = buildReportContent({
12435
+ folderName: feature.folderName,
12436
+ date,
12437
+ decision,
12438
+ major,
12439
+ minor,
12440
+ note,
12441
+ fallback: policy.fallback,
12442
+ skills: policy.skills
12443
+ });
12444
+ await fs16.writeFile(reportPath, reportContent, "utf-8");
12445
+ const reportPathFromDocs = normalizePathForDoc(
12446
+ path22.join(feature.docs.featurePathFromDocs, "pre-pr-review.md")
12447
+ );
12448
+ const evidencePath = path22.basename(config.docsDir) === "docs" ? normalizePathForDoc(path22.join("docs", reportPathFromDocs)) : reportPathFromDocs;
12449
+ let nextTasks = tasksContent;
12450
+ nextTasks = upsertSpecLine(
12451
+ nextTasks,
12452
+ ["PR \uC804 \uB9AC\uBDF0", "Pre-PR Review"],
12453
+ preferred.review,
12454
+ "Done",
12455
+ ["PR \uC0C1\uD0DC", "PR Status"]
12456
+ );
12457
+ nextTasks = upsertSpecLine(
12458
+ nextTasks,
12459
+ ["PR \uC804 \uB9AC\uBDF0 Findings", "Pre-PR Findings"],
12460
+ preferred.findings,
12461
+ `major=${major}, minor=${minor}`,
12462
+ ["PR \uC804 \uB9AC\uBDF0", "Pre-PR Review"]
12463
+ );
12464
+ nextTasks = upsertSpecLine(
12465
+ nextTasks,
12466
+ ["PR \uC804 \uB9AC\uBDF0 Evidence", "Pre-PR Evidence"],
12467
+ preferred.evidence,
12468
+ evidencePath,
12469
+ ["PR \uC804 \uB9AC\uBDF0 Findings", "Pre-PR Findings", "PR \uC804 \uB9AC\uBDF0", "Pre-PR Review"]
12470
+ );
12471
+ nextTasks = upsertSpecLine(
12472
+ nextTasks,
12473
+ ["PR \uC804 \uB9AC\uBDF0 Decision", "Pre-PR Decision"],
12474
+ preferred.decision,
12475
+ `decision: ${decision} - ${note}`,
12476
+ ["PR \uC804 \uB9AC\uBDF0 Evidence", "Pre-PR Evidence"]
12477
+ );
12478
+ if (nextTasks !== tasksContent) {
12479
+ await fs16.writeFile(tasksPath, nextTasks, "utf-8");
12480
+ }
12481
+ if (options.json) {
12482
+ console.log(
12483
+ JSON.stringify(
12484
+ {
12485
+ status: "ok",
12486
+ reasonCode: "PRE_PR_REVIEW_RECORDED",
12487
+ feature: feature.folderName,
12488
+ reportPath: normalizePathForDoc(reportPath),
12489
+ evidencePath,
12490
+ decision,
12491
+ findings: { major, minor },
12492
+ tasksUpdated: nextTasks !== tasksContent
12493
+ },
12494
+ null,
12495
+ 2
12496
+ )
12497
+ );
12498
+ return;
12499
+ }
12500
+ console.log();
12501
+ console.log(chalk6.green(`\u2705 pre-pr-review completed: ${feature.folderName}`));
12502
+ console.log(chalk6.gray(`- Decision: ${decision}`));
12503
+ console.log(chalk6.gray(`- Findings: major=${major}, minor=${minor}`));
12504
+ console.log(chalk6.gray(`- Report: ${reportPath}`));
12505
+ if (nextTasks !== tasksContent) {
12506
+ console.log(chalk6.gray(`- tasks.md updated: ${tasksPath}`));
12507
+ }
12508
+ console.log();
12509
+ }
11989
12510
  function isBannerDisabled() {
11990
12511
  const v = (process.env.LEE_SPEC_KIT_NO_BANNER || "").trim();
11991
12512
  return v === "1";
@@ -12166,6 +12687,7 @@ githubCommand(program);
12166
12687
  docsCommand(program);
12167
12688
  detectCommand(program);
12168
12689
  onboardCommand(program);
12690
+ prePrReviewCommand(program);
12169
12691
  await program.parseAsync();
12170
12692
  //# sourceMappingURL=index.js.map
12171
12693
  //# sourceMappingURL=index.js.map