lee-spec-kit 0.6.39 → 0.6.40

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
@@ -215,6 +215,7 @@ var koCli = {
215
215
  "doctor.issue.tasksEmpty": "tasks.md\uC5D0 \uD0DC\uC2A4\uD06C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
216
216
  "doctor.issue.tasksDocStatusUnset": "tasks.md\uC758 \uBB38\uC11C \uC0C1\uD0DC(Doc Status)\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (Draft/Review/Approved \uC911 \uD558\uB098\uB85C \uC124\uC815\uD558\uC138\uC694.)",
217
217
  "doctor.issue.tasksDocStatusMissing": "tasks.md\uC5D0 \uBB38\uC11C \uC0C1\uD0DC(Doc Status) \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. `- **\uBB38\uC11C \uC0C1\uD0DC**: -`\uC640 `\uAC12: Draft | Review | Approved`\uB97C \uCD94\uAC00\uD558\uC138\uC694.",
218
+ "doctor.issue.tasksPrdTagUnknown": "tasks.md\uC5D0 \uC815\uC758\uB418\uC9C0 \uC54A\uC740 PRD \uD0DC\uADF8\uAC00 \uC788\uC2B5\uB2C8\uB2E4: {ids}{extra}. tasks.md\uC5D0\uC11C PRD-FR-001 \uAC19\uC740 ID\uB97C \uC784\uC758\uB85C \uB9CC\uB4E4\uC9C0 \uB9D0\uACE0, \uBA3C\uC800 docs/prd \uB610\uB294 \uC0C1\uC704 \uC694\uAD6C\uC0AC\uD56D \uBB38\uC11C\uC5D0 ID\uB97C backfill\uD55C \uB4A4 spec.md `PRD Refs`\uC640 tasks \uD0DC\uADF8\uB97C \uD568\uAED8 \uB9DE\uCD94\uC138\uC694.",
218
219
  "doctor.issue.duplicateFeatureId": "\uC911\uBCF5 Feature ID \uAC10\uC9C0: {id} ({count}\uAC1C)",
219
220
  "doctor.issue.missingFeatureId": "Feature \uD3F4\uB354\uBA85\uC774 F001-... \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4. (ID\uB97C \uCD94\uCD9C\uD560 \uC218 \uC5C6\uC74C)",
220
221
  "init.selectLangPrompt": "\uBB38\uC11C \uC5B8\uC5B4\uB97C \uC120\uD0DD\uD558\uC138\uC694:",
@@ -475,9 +476,9 @@ var koContext = {
475
476
  "context.commandDetail.branchCreateGeneric": "({scope}) feature \uBE0C\uB79C\uCE58\uC6A9 worktree\uB97C \uC0DD\uC131\uD558\uAC70\uB098 \uC7AC\uC0AC\uC6A9\uD558\uC138\uC694",
476
477
  "context.commandDetail.codeReviewMergeAfterOk": "({scope}) \uBA85\uC2DC\uC801 \uC2B9\uC778 \uD6C4 PR\uC744 \uBA38\uC9C0\uD558\uC138\uC694",
477
478
  "context.commandDetail.codeReviewPushFix": "({scope}) \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B\uC744 push\uD558\uC138\uC694",
478
- "context.commandDetail.prePrReviewRun": "({scope}) PR \uC804 \uB9AC\uBDF0\uB97C \uC2DC\uC791\uD558\uC138\uC694. (\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent) \uC2E4\uD589 \uB2E8\uACC4)",
479
+ "context.commandDetail.prePrReviewRun": "({scope}) \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent)\uB85C PR \uC804 \uB9AC\uBDF0\uB97C \uC2E4\uD589\uD574 `review-trace.json`\uC744 \uC900\uBE44\uD558\uC138\uC694",
479
480
  "context.commandDetail.prePrReviewRecord": "({scope}) Pre-PR \uB9AC\uBDF0 evidence\uB97C decisions.md\uC640 tasks.md\uC5D0 \uAE30\uB85D\uD558\uC138\uC694",
480
- "context.commandDetail.codeReviewRun": "({scope}) PR \uB9AC\uBDF0\uB97C \uC2DC\uC791\uD558\uC138\uC694. (\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent) \uC2E4\uD589 \uB2E8\uACC4)",
481
+ "context.commandDetail.codeReviewRun": "({scope}) \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent)\uB85C PR \uB9AC\uBDF0\uB97C \uC2E4\uD589\uD574 evidence/\uC218\uC815 \uC694\uC57D\uC744 \uC900\uBE44\uD558\uC138\uC694",
481
482
  "context.actionSummary.runDocsCommand": "\uBB38\uC11C \uC791\uC5C5 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uC138\uC694",
482
483
  "context.actionSummary.runProjectCommand": "\uD504\uB85C\uC81D\uD2B8 \uC791\uC5C5 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uC138\uC694",
483
484
  "context.actionDetail.featureFolder": "Feature \uD3F4\uB354\uC640 \uAE30\uBCF8 \uBB38\uC11C \uACE8\uACA9\uC744 \uC900\uBE44\uD558\uC138\uC694",
@@ -495,12 +496,12 @@ var koContext = {
495
496
  "context.actionDetail.issueCreatePrepareFromDoc": "issue.md \uCD08\uC548\uC744 \uBCF4\uC644\uD558\uACE0 \uC0C1\uD0DC\uB97C Ready\uB85C \uC124\uC815\uD558\uC138\uC694",
496
497
  "context.actionDetail.issueCreateFromDoc": "Ready \uC0C1\uD0DC issue.md\uB85C \uC774\uC288\uB97C \uC0DD\uC131\uD558\uACE0 \uBC88\uD638\uB97C \uB3D9\uAE30\uD654\uD558\uC138\uC694",
497
498
  "context.actionDetail.taskExecute": "\uD604\uC7AC \uD0DC\uC2A4\uD06C\uB97C \uC9C4\uD589\uD558\uC138\uC694",
498
- "context.actionDetail.taskExecuteRun": "\uC791\uC5C5 \uAD6C\uD604\uC744 \uC2DC\uC791\uD558\uC138\uC694. (\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent) \uC2E4\uD589 \uB2E8\uACC4)",
499
- "context.actionDetail.taskExecuteContinue": "\uC9C4\uD589 \uC911\uC778 \uC791\uC5C5\uC744 \uACC4\uC18D \uC9C4\uD589\uD558\uC138\uC694. (\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent) \uC2E4\uD589 \uB2E8\uACC4)",
499
+ "context.actionDetail.taskExecuteRun": "\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent) \uC791\uC5C5 handoff\uB97C \uC900\uBE44\uD558\uACE0 \uD0DC\uC2A4\uD06C\uB97C \uC2DC\uC791\uD558\uC138\uC694. (TODO\uBA74 DOING\uC73C\uB85C \uBCC0\uACBD)",
500
+ "context.actionDetail.taskExecuteContinue": "\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent) \uC791\uC5C5 handoff\uB97C \uC900\uBE44\uD574 \uC9C4\uD589 \uC911\uC778 \uD0DC\uC2A4\uD06C\uB97C \uC774\uC5B4\uAC00\uC138\uC694",
500
501
  "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",
501
- "context.actionDetail.prePrReviewRun": "PR \uC804 \uB9AC\uBDF0\uB97C \uC218\uD589\uD558\uC138\uC694. (`review-trace.json` \uC0DD\uC131, \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent) \uC2E4\uD589 \uB2E8\uACC4)",
502
+ "context.actionDetail.prePrReviewRun": "\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent)\uB85C PR \uC804 \uB9AC\uBDF0\uB97C \uC2E4\uD589\uD574 `review-trace.json`\uC744 \uC900\uBE44\uD558\uC138\uC694",
502
503
  "context.actionDetail.prePrReviewRecord": "PR \uC804 \uB9AC\uBDF0 evidence\uB97C decisions.md\uC640 tasks.md\uC5D0 \uAE30\uB85D\uD558\uC138\uC694",
503
- "context.actionDetail.codeReviewRun": "PR \uB9AC\uBDF0\uB97C \uC9C4\uD589\uD558\uC138\uC694. (\uB9AC\uBDF0 evidence/\uC218\uC815 \uC694\uC57D \uC900\uBE44, \uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent) \uC2E4\uD589 \uB2E8\uACC4)",
504
+ "context.actionDetail.codeReviewRun": "\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8(sub-agent)\uB85C PR \uB9AC\uBDF0\uB97C \uC2E4\uD589\uD574 evidence/\uC218\uC815 \uC694\uC57D\uC744 \uC900\uBE44\uD558\uC138\uC694",
504
505
  "context.actionDetail.prCreate": "PR\uC744 \uC0DD\uC131\uD558\uACE0 tasks \uAE30.md\uC758 PR \uC815\uBCF4\uB97C \uB9DE\uCD94\uC138\uC694",
505
506
  "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",
506
507
  "context.actionDetail.prCreatePrepareFromDoc": "pr.md \uCD08\uC548\uC744 \uBCF4\uC644\uD558\uACE0 \uC0C1\uD0DC\uB97C Ready\uB85C \uC124\uC815\uD558\uC138\uC694",
@@ -623,7 +624,7 @@ var koMessages = {
623
624
  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",
624
625
  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)",
625
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)",
626
- 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. \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)",
627
+ 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)",
627
628
  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)",
628
629
  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)",
629
630
  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)",
@@ -766,6 +767,7 @@ var enCli = {
766
767
  "doctor.issue.tasksEmpty": "tasks.md has no tasks.",
767
768
  "doctor.issue.tasksDocStatusUnset": "tasks.md Doc Status is not set. (Set it to Draft, Review, or Approved.)",
768
769
  "doctor.issue.tasksDocStatusMissing": "tasks.md is missing the Doc Status field. Add `- **Doc Status**: -` and `Values: Draft | Review | Approved`.",
770
+ "doctor.issue.tasksPrdTagUnknown": "tasks.md uses PRD tags with no matching source definition: {ids}{extra}. Do not invent IDs like PRD-FR-001 in tasks.md. Backfill IDs in docs/prd or the upstream requirements doc first, then align spec.md `PRD Refs` and task tags.",
769
771
  "doctor.issue.duplicateFeatureId": "Duplicate Feature ID detected: {id} ({count})",
770
772
  "doctor.issue.missingFeatureId": "Feature folder name is not in F001-... format. (Cannot extract ID)",
771
773
  "init.selectLangPrompt": "Select docs language:",
@@ -1026,9 +1028,9 @@ var enContext = {
1026
1028
  "context.commandDetail.branchCreateGeneric": "({scope}) create or reuse feature branch worktree",
1027
1029
  "context.commandDetail.codeReviewMergeAfterOk": "({scope}) merge PR after explicit OK",
1028
1030
  "context.commandDetail.codeReviewPushFix": "({scope}) push review-fix commits",
1029
- "context.commandDetail.prePrReviewRun": "({scope}) start the pre-PR review. (helper agent/sub-agent execution stage)",
1031
+ "context.commandDetail.prePrReviewRun": "({scope}) run the pre-PR review via a helper agent/sub-agent and prepare `review-trace.json`",
1030
1032
  "context.commandDetail.prePrReviewRecord": "({scope}) record pre-PR review evidence into decisions.md + tasks.md",
1031
- "context.commandDetail.codeReviewRun": "({scope}) start the PR review. (helper agent/sub-agent execution stage)",
1033
+ "context.commandDetail.codeReviewRun": "({scope}) run the PR review via a helper agent/sub-agent and prepare evidence/fix summary",
1032
1034
  "context.actionSummary.runDocsCommand": "Run docs command",
1033
1035
  "context.actionSummary.runProjectCommand": "Run project command",
1034
1036
  "context.actionDetail.featureFolder": "Prepare feature folder and baseline docs",
@@ -1046,12 +1048,12 @@ var enContext = {
1046
1048
  "context.actionDetail.issueCreatePrepareFromDoc": "Refine issue.md draft and set Status to Ready",
1047
1049
  "context.actionDetail.issueCreateFromDoc": "Create GitHub Issue from ready issue.md and sync Issue",
1048
1050
  "context.actionDetail.taskExecute": "Proceed with the current task",
1049
- "context.actionDetail.taskExecuteRun": "Start implementing the task. (helper agent/sub-agent execution stage)",
1050
- "context.actionDetail.taskExecuteContinue": "Continue the in-progress task. (helper agent/sub-agent execution stage)",
1051
+ "context.actionDetail.taskExecuteRun": "Prepare helper agent/sub-agent task handoff and start the task. (TODO becomes DOING)",
1052
+ "context.actionDetail.taskExecuteContinue": "Prepare helper agent/sub-agent handoff and continue the in-progress task",
1051
1053
  "context.actionDetail.reviewFixCommit": "Create a review-fix commit with resolved feedback summary",
1052
- "context.actionDetail.prePrReviewRun": "Run the pre-PR review. (generate `review-trace.json`, helper agent/sub-agent execution stage)",
1054
+ "context.actionDetail.prePrReviewRun": "Run the pre-PR review via a helper agent/sub-agent and prepare `review-trace.json`",
1053
1055
  "context.actionDetail.prePrReviewRecord": "Record pre-PR review evidence into decisions.md and tasks.md",
1054
- "context.actionDetail.codeReviewRun": "Run the PR review. (prepare review evidence/fix summary, helper agent/sub-agent execution stage)",
1056
+ "context.actionDetail.codeReviewRun": "Run the PR review via a helper agent/sub-agent and prepare evidence/fix summary",
1055
1057
  "context.actionDetail.prCreate": "Create PR and sync PR fields in tasks.md",
1056
1058
  "context.actionDetail.prCreateRequiredSequence": "Complete PR 2-step flow: prepare draft + OK, then create and sync",
1057
1059
  "context.actionDetail.prCreatePrepareFromDoc": "Refine pr.md draft and set Status to Ready",
@@ -1174,7 +1176,7 @@ var enMessages = {
1174
1176
  taskCommitGateReasonMismatchLastDone: "The latest project code commit does not match the last completed task",
1175
1177
  prLegacyAsk: "tasks.md is missing PR/PR Status fields. Update to the latest template format? (CHECK required)",
1176
1178
  prePrReviewFieldMissing: "tasks.md is missing the `Pre-PR Review` field. Add `- **Pre-PR Review**: Pending | Done` and run context again. (CHECK required)",
1177
- 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`; use `--evidence review-trace.json` when the active evidence policy requires a path. (CHECK required)",
1179
+ 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)",
1178
1180
  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)",
1179
1181
  prePrReviewDecisionMissing: "tasks.md `Pre-PR Decision` is empty/placeholder or missing decision format. Record it as `decision: ...` (or `\uACB0\uC815: ...`). (CHECK required)",
1180
1182
  prePrReviewFixRequired: "Current `Pre-PR Decision` is `{decision}`. Apply the requested fixes from pre-PR findings before moving to PR creation. (CHECK required)",
@@ -10106,6 +10108,108 @@ function printChecklist(f, stepDefinitions) {
10106
10108
  console.log(` ${mark} ${definition.step}. ${label} ${detail}`);
10107
10109
  });
10108
10110
  }
10111
+ var PRD_REQUIREMENT_ID_RE = /\bPRD-(?:FR|US|NFR)-\d+\b/gi;
10112
+ function isPrdRequirementId(value) {
10113
+ return /^PRD-(?:FR|US|NFR)-\d+$/i.test(value.trim());
10114
+ }
10115
+ function isNonPrdTag(value) {
10116
+ const trimmed = value.trim();
10117
+ return /^NON[-_ ]?PRD$/i.test(trimmed);
10118
+ }
10119
+ function normalizeRelPath2(value) {
10120
+ return value.replace(/\\/g, "/").replace(/^\.\/+/, "");
10121
+ }
10122
+ function extractTitleAfterId(line, id) {
10123
+ const idx = line.indexOf(id);
10124
+ if (idx < 0) return void 0;
10125
+ const after = line.slice(idx + id.length);
10126
+ const cleaned = after.replace(/^[\s::\-–—)\]]+/, "").trim();
10127
+ return cleaned ? cleaned : void 0;
10128
+ }
10129
+ async function scanPrdRequirements(fsAdapter, docsDir) {
10130
+ const prdDir = path12.join(docsDir, "prd");
10131
+ const files = await walkFiles(fsAdapter, prdDir, {
10132
+ extensions: [".md"],
10133
+ ignoreDirs: [".git", "node_modules", "dist", "tmp"]
10134
+ });
10135
+ const definitions = /* @__PURE__ */ new Map();
10136
+ const duplicates = [];
10137
+ for (const filePath of files) {
10138
+ if (path12.basename(filePath).toLowerCase() === "readme.md") {
10139
+ continue;
10140
+ }
10141
+ let content = "";
10142
+ try {
10143
+ content = await fsAdapter.readFile(filePath, "utf-8");
10144
+ } catch {
10145
+ continue;
10146
+ }
10147
+ const relFile = normalizeRelPath2(path12.relative(docsDir, filePath));
10148
+ const lines = content.split(/\r?\n/);
10149
+ let inCodeBlock = false;
10150
+ for (let i = 0; i < lines.length; i += 1) {
10151
+ const line = lines[i] || "";
10152
+ if (/^\s*(```|~~~)/.test(line)) {
10153
+ inCodeBlock = !inCodeBlock;
10154
+ continue;
10155
+ }
10156
+ if (inCodeBlock) continue;
10157
+ const ids = [...line.matchAll(PRD_REQUIREMENT_ID_RE)].map(
10158
+ (match) => (match[0] || "").toUpperCase()
10159
+ );
10160
+ if (ids.length === 0) continue;
10161
+ for (const id of ids) {
10162
+ const def = {
10163
+ id,
10164
+ title: extractTitleAfterId(line, id),
10165
+ file: relFile,
10166
+ line: i + 1
10167
+ };
10168
+ const existing = definitions.get(id);
10169
+ if (existing) {
10170
+ duplicates.push(def);
10171
+ continue;
10172
+ }
10173
+ definitions.set(id, def);
10174
+ }
10175
+ }
10176
+ }
10177
+ return { definitions, duplicates, filesScanned: files.length };
10178
+ }
10179
+ function parseTaskLines(content) {
10180
+ const out = [];
10181
+ const lines = content.split(/\r?\n/);
10182
+ let inCodeBlock = false;
10183
+ for (let i = 0; i < lines.length; i += 1) {
10184
+ const line = lines[i] || "";
10185
+ if (/^\s*(```|~~~)/.test(line)) {
10186
+ inCodeBlock = !inCodeBlock;
10187
+ continue;
10188
+ }
10189
+ if (inCodeBlock) continue;
10190
+ const match = line.match(
10191
+ /^\s*-\s*\[([A-Z]+)\]((?:\[[^\]]+\])*)\s*(.+?)\s*$/
10192
+ );
10193
+ if (!match) continue;
10194
+ const status = (match[1] || "").trim().toUpperCase();
10195
+ const tagsPart = match[2] || "";
10196
+ const title = (match[3] || "").trim();
10197
+ if (!title) continue;
10198
+ if (status !== "TODO" && status !== "DOING" && status !== "DONE" && status !== "REVIEW") {
10199
+ continue;
10200
+ }
10201
+ const tags = [...tagsPart.matchAll(/\[([^\]]+)\]/g)].map((m) => (m[1] || "").trim()).filter(Boolean);
10202
+ out.push({
10203
+ status,
10204
+ tags,
10205
+ title,
10206
+ line: i + 1
10207
+ });
10208
+ }
10209
+ return out;
10210
+ }
10211
+
10212
+ // src/commands/doctor.ts
10109
10213
  var FIXABLE_ISSUE_CODES = /* @__PURE__ */ new Set([
10110
10214
  "placeholder_left",
10111
10215
  "spec_status_unset",
@@ -10362,6 +10466,10 @@ async function checkDocsStructure(config, cwd) {
10362
10466
  }
10363
10467
  async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10364
10468
  const issues = [];
10469
+ const { definitions: prdDefinitions } = await scanPrdRequirements(
10470
+ fs,
10471
+ config.docsDir
10472
+ );
10365
10473
  if (features.length === 0) {
10366
10474
  issues.push({
10367
10475
  level: "warn",
@@ -10443,6 +10551,24 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
10443
10551
  path: formatPath(cwd, path12.join(f.path, "tasks.md"))
10444
10552
  });
10445
10553
  }
10554
+ if (f.docs.tasksExists) {
10555
+ const tasksPath = path12.join(f.path, "tasks.md");
10556
+ const tasksContent = await fs.readFile(tasksPath, "utf-8");
10557
+ const unknownPrdTags = [...new Set(
10558
+ parseTaskLines(tasksContent).flatMap((task) => task.tags).filter((tag) => isPrdRequirementId(tag)).map((tag) => tag.trim().toUpperCase()).filter((tag) => !prdDefinitions.has(tag))
10559
+ )].sort();
10560
+ if (unknownPrdTags.length > 0) {
10561
+ issues.push({
10562
+ level: "warn",
10563
+ code: "tasks_prd_tag_unknown",
10564
+ message: tr(config.lang, "cli", "doctor.issue.tasksPrdTagUnknown", {
10565
+ ids: unknownPrdTags.slice(0, 5).join(", "),
10566
+ extra: unknownPrdTags.length > 5 ? ` (+${unknownPrdTags.length - 5} more)` : ""
10567
+ }),
10568
+ path: formatPath(cwd, tasksPath)
10569
+ });
10570
+ }
10571
+ }
10446
10572
  if (f.docs.tasksExists && !f.docs.tasksDocStatusFieldExists && !isInitialTemplateState) {
10447
10573
  issues.push({
10448
10574
  level: "warn",
@@ -15660,6 +15786,9 @@ async function runPrePrReviewRun(featureName, options) {
15660
15786
  feature: featureRef,
15661
15787
  skills: policy.skills,
15662
15788
  fallback: policy.fallback,
15789
+ handoffOnly: true,
15790
+ advancesWorkflow: false,
15791
+ nextStepRequirement: "generate_review_trace_then_record",
15663
15792
  evidenceFile: "review-trace.json",
15664
15793
  prompt,
15665
15794
  recordCommands: {
@@ -15675,6 +15804,11 @@ async function runPrePrReviewRun(featureName, options) {
15675
15804
  }
15676
15805
  console.log(prompt);
15677
15806
  console.log();
15807
+ console.log(
15808
+ chalk8.yellow(
15809
+ 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."
15810
+ )
15811
+ );
15678
15812
  console.log(`Evidence file: review-trace.json`);
15679
15813
  console.log(`Record changes requested: ${changesRequestedCommand}`);
15680
15814
  console.log(`Record approval: ${approveCommand}`);
@@ -15903,6 +16037,8 @@ async function runCodeReviewRun(featureName, options) {
15903
16037
  feature: feature.folderName,
15904
16038
  substateId: "code_review_run",
15905
16039
  owner: "subagent",
16040
+ handoffOnly: true,
16041
+ advancesWorkflow: false,
15906
16042
  nextMainState: "code_review_finalize",
15907
16043
  tasksPath: path12.join(feature.path, "tasks.md"),
15908
16044
  decisionsPath: path12.join(feature.path, "decisions.md"),
@@ -15915,6 +16051,11 @@ async function runCodeReviewRun(featureName, options) {
15915
16051
  }
15916
16052
  console.log(prompt);
15917
16053
  console.log();
16054
+ console.log(
16055
+ chalk8.yellow(
16056
+ config.lang === "ko" ? "\uC774 \uBA85\uB839\uC740 PR \uB9AC\uBDF0 handoff\uB9CC \uC900\uBE44\uD569\uB2C8\uB2E4. \uB9AC\uBDF0 evidence/decision\uC744 \uC9C1\uC811 \uAE30\uB85D\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 PR review handoff. It does not record review evidence/decision or advance workflow state by itself."
16057
+ )
16058
+ );
15918
16059
  console.log(chalk8.gray(`- substate: ${payload.substateId}`));
15919
16060
  console.log(chalk8.gray(`- owner: ${payload.owner}`));
15920
16061
  console.log(chalk8.gray(`- next main state: ${payload.nextMainState}`));
@@ -15949,105 +16090,6 @@ function codeReviewRunCommand(program2) {
15949
16090
  }
15950
16091
  );
15951
16092
  }
15952
- var PRD_REQUIREMENT_ID_RE = /\bPRD-(?:FR|US|NFR)-\d+\b/gi;
15953
- function isPrdRequirementId(value) {
15954
- return /^PRD-(?:FR|US|NFR)-\d+$/i.test(value.trim());
15955
- }
15956
- function isNonPrdTag(value) {
15957
- const trimmed = value.trim();
15958
- return /^NON[-_ ]?PRD$/i.test(trimmed);
15959
- }
15960
- function normalizeRelPath2(value) {
15961
- return value.replace(/\\/g, "/").replace(/^\.\/+/, "");
15962
- }
15963
- function extractTitleAfterId(line, id) {
15964
- const idx = line.indexOf(id);
15965
- if (idx < 0) return void 0;
15966
- const after = line.slice(idx + id.length);
15967
- const cleaned = after.replace(/^[\s::\-–—)\]]+/, "").trim();
15968
- return cleaned ? cleaned : void 0;
15969
- }
15970
- async function scanPrdRequirements(fsAdapter, docsDir) {
15971
- const prdDir = path12.join(docsDir, "prd");
15972
- const files = await walkFiles(fsAdapter, prdDir, {
15973
- extensions: [".md"],
15974
- ignoreDirs: [".git", "node_modules", "dist", "tmp"]
15975
- });
15976
- const definitions = /* @__PURE__ */ new Map();
15977
- const duplicates = [];
15978
- for (const filePath of files) {
15979
- let content = "";
15980
- try {
15981
- content = await fsAdapter.readFile(filePath, "utf-8");
15982
- } catch {
15983
- continue;
15984
- }
15985
- const relFile = normalizeRelPath2(path12.relative(docsDir, filePath));
15986
- const lines = content.split(/\r?\n/);
15987
- let inCodeBlock = false;
15988
- for (let i = 0; i < lines.length; i += 1) {
15989
- const line = lines[i] || "";
15990
- if (/^\s*(```|~~~)/.test(line)) {
15991
- inCodeBlock = !inCodeBlock;
15992
- continue;
15993
- }
15994
- if (inCodeBlock) continue;
15995
- const ids = [...line.matchAll(PRD_REQUIREMENT_ID_RE)].map(
15996
- (match) => (match[0] || "").toUpperCase()
15997
- );
15998
- if (ids.length === 0) continue;
15999
- for (const id of ids) {
16000
- const def = {
16001
- id,
16002
- title: extractTitleAfterId(line, id),
16003
- file: relFile,
16004
- line: i + 1
16005
- };
16006
- const existing = definitions.get(id);
16007
- if (existing) {
16008
- duplicates.push(def);
16009
- continue;
16010
- }
16011
- definitions.set(id, def);
16012
- }
16013
- }
16014
- }
16015
- return { definitions, duplicates, filesScanned: files.length };
16016
- }
16017
- function parseTaskLines(content) {
16018
- const out = [];
16019
- const lines = content.split(/\r?\n/);
16020
- let inCodeBlock = false;
16021
- for (let i = 0; i < lines.length; i += 1) {
16022
- const line = lines[i] || "";
16023
- if (/^\s*(```|~~~)/.test(line)) {
16024
- inCodeBlock = !inCodeBlock;
16025
- continue;
16026
- }
16027
- if (inCodeBlock) continue;
16028
- const match = line.match(
16029
- /^\s*-\s*\[([A-Z]+)\]((?:\[[^\]]+\])*)\s*(.+?)\s*$/
16030
- );
16031
- if (!match) continue;
16032
- const status = (match[1] || "").trim().toUpperCase();
16033
- const tagsPart = match[2] || "";
16034
- const title = (match[3] || "").trim();
16035
- if (!title) continue;
16036
- if (status !== "TODO" && status !== "DOING" && status !== "DONE" && status !== "REVIEW") {
16037
- continue;
16038
- }
16039
- const tags = [...tagsPart.matchAll(/\[([^\]]+)\]/g)].map((m) => (m[1] || "").trim()).filter(Boolean);
16040
- out.push({
16041
- status,
16042
- tags,
16043
- title,
16044
- line: i + 1
16045
- });
16046
- }
16047
- return out;
16048
- }
16049
-
16050
- // src/commands/requirements.ts
16051
16093
  function getRequirementState(tasks) {
16052
16094
  if (tasks.total === 0) return "UNTRACKED";
16053
16095
  if (tasks.done === tasks.total) return "DONE";
@@ -16252,7 +16294,7 @@ async function runRequirements(options) {
16252
16294
  }
16253
16295
  function parseTaskLine(line) {
16254
16296
  const match = line.match(
16255
- /^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]\[([^\]]+)\]\s+(T-[A-Za-z0-9-]+)\s+(.+?)\s*$/
16297
+ /^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]\[([^\]]+)\](?:\[[^\]]+\])*\s+(T-[A-Za-z0-9-]+)\s+(.+?)\s*$/
16256
16298
  );
16257
16299
  if (!match) return null;
16258
16300
  return {
@@ -16275,9 +16317,10 @@ function buildTaskRunPrompt(input) {
16275
16317
  ];
16276
16318
  if (input.lang === "ko") {
16277
16319
  return [
16278
- `${input.mode === "start" ? "\uC0C8 task \uC2E4\uD589\uC744 \uC2DC\uC791\uD558\uC138\uC694." : "\uC9C4\uD589 \uC911\uC778 task \uC2E4\uD589\uC744 \uC774\uC5B4\uAC00\uC138\uC694."}`,
16320
+ `${input.mode === "start" ? "\uC0C8 task \uC2E4\uD589\uC6A9 handoff\uB97C \uC900\uBE44\uD558\uC138\uC694." : "\uC9C4\uD589 \uC911\uC778 task handoff\uB97C \uC774\uC5B4\uAC00\uC138\uC694."}`,
16279
16321
  `- Feature: ${input.featureRef}`,
16280
16322
  `- Task: ${input.taskId} ${input.title}`,
16323
+ 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.",
16281
16324
  "- \uBA3C\uC800 `spec.md`, `plan.md`, `tasks.md`\uB97C \uC77D\uACE0 \uBC94\uC704\uC640 \uC644\uB8CC \uAE30\uC900\uC744 \uC815\uB9AC\uD558\uC138\uC694.",
16282
16325
  "- \uCF54\uB4DC \uBD84\uC11D\uACFC \uD0D0\uC0C9 \uC791\uC5C5\uC740 \uAE30\uBCF8\uC801\uC73C\uB85C \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uB97C \uC0AC\uC6A9\uD558\uC138\uC694.",
16283
16326
  "- \uC601\uD5A5 \uBC94\uC704 \uBD84\uC11D, \uD14C\uC2A4\uD2B8 \uC704\uCE58 \uD0D0\uC0C9, \uAE30\uC874 \uD328\uD134 \uC870\uC0AC\uCC98\uB7FC \uB3C5\uB9BD\uC801\uC778 \uBD84\uC11D\uC740 \uBCD1\uB82C\uB85C \uC218\uD589\uD558\uC138\uC694.",
@@ -16287,9 +16330,10 @@ function buildTaskRunPrompt(input) {
16287
16330
  ].join("\n");
16288
16331
  }
16289
16332
  return [
16290
- input.mode === "start" ? "Start this task execution." : "Continue this in-progress task execution.",
16333
+ input.mode === "start" ? "Prepare the handoff for this task execution." : "Prepare the handoff for this in-progress task.",
16291
16334
  `- Feature: ${input.featureRef}`,
16292
16335
  `- Task: ${input.taskId} ${input.title}`,
16336
+ `- ${input.mode === "start" ? "This command marks the current task as DOING in tasks.md, then prepares the implementation handoff prompt." : "This command prepares the implementation handoff prompt again for the in-progress task."}`,
16293
16337
  ...shared.map((line) => `- ${line}`)
16294
16338
  ].join("\n");
16295
16339
  }
@@ -16401,7 +16445,9 @@ async function runTaskRun(featureName, options) {
16401
16445
  }
16402
16446
  }
16403
16447
  function taskRunCommand(program2) {
16404
- program2.command("task-run [feature-name]").description("Prepare and start a task execution handoff for sub-agent work").option("--component <component>", "Component name for multi projects").option("--task <task-id>", "Explicit task id to execute").option("--json", "Output JSON").action(async (featureName, options) => {
16448
+ program2.command("task-run [feature-name]").description(
16449
+ "Prepare task execution handoff for sub-agent work (marks TODO tasks as DOING)"
16450
+ ).option("--component <component>", "Component name for multi projects").option("--task <task-id>", "Explicit task id to execute").option("--json", "Output JSON").action(async (featureName, options) => {
16405
16451
  try {
16406
16452
  await runTaskRun(featureName, options);
16407
16453
  } catch (error) {