lee-spec-kit 0.6.28 → 0.6.30

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
@@ -186,7 +186,7 @@ var ko = {
186
186
  "context.actionDetail.tasksWriteImprove": "tasks.md\uB97C \uBCF4\uC644\uD558\uACE0 \uBB38\uC11C \uC0C1\uD0DC\uB97C \uC815\uB82C\uD558\uC138\uC694",
187
187
  "context.actionDetail.tasksApprove": "tasks.md\uB97C \uC2B9\uC778\uD569\uB2C8\uB2E4",
188
188
  "context.actionDetail.issueCreate": "\uC774\uC288\uB97C \uC0DD\uC131\uD558\uACE0 tasks.md\uC758 \uC774\uC288 \uC815\uBCF4\uB97C \uB9DE\uCD94\uC138\uC694",
189
- "context.actionDetail.issueCreateAndWrite": "\uC774\uC288 \uCD08\uC548\uC744 \uBCF4\uC644\uD558\uACE0 \uC2B9\uC778(OK) \uD6C4 \uC774\uC288\uB97C \uC0DD\uC131\uD574 \uBC88\uD638\uB97C \uB3D9\uAE30\uD654\uD558\uC138\uC694",
189
+ "context.actionDetail.issueCreateAndWrite": "\uC774\uC288 \uCD08\uC548\uC744 \uBCF4\uC644\uD558\uACE0 \uB77C\uBCA8 \uC2B9\uC778(`A` \uB610\uB294 `A OK`) \uD6C4 \uC774\uC288\uB97C \uC0DD\uC131\uD574 \uBC88\uD638\uB97C \uB3D9\uAE30\uD654\uD558\uC138\uC694",
190
190
  "context.actionDetail.issueCreatePrepareFromDoc": "issue.md \uCD08\uC548\uC744 \uBCF4\uC644\uD558\uACE0 \uC0C1\uD0DC\uB97C Ready\uB85C \uC124\uC815\uD558\uC138\uC694",
191
191
  "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",
192
192
  "context.actionDetail.taskExecute": "\uD604\uC7AC \uD0DC\uC2A4\uD06C\uB97C \uC9C4\uD589\uD558\uC138\uC694",
@@ -504,8 +504,8 @@ var ko = {
504
504
  tasksImprove: "tasks.md\uB97C \uBCF4\uC644\uD558\uACE0 \uBB38\uC11C \uC0C1\uD0DC\uB97C Review\uB85C \uBCC0\uACBD\uD558\uC138\uC694.",
505
505
  tasksApproval: "tasks.md \uB0B4\uC6A9\uC744 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uACF5\uC720\uD558\uACE0 \uC9C4\uD589 \uC2B9\uC778(`A` \uB610\uB294 `A OK` \uD615\uC2DD)\uC744 \uBC1B\uC73C\uC138\uC694. (\uC2B9\uC778 \uD6C4 \uBB38\uC11C \uC0C1\uD0DC\uB97C Approved\uB85C \uBCC0\uACBD)",
506
506
  docsCommitPlanning: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(planning): {folderName} \uAE30\uD68D \uBB38\uC11C"',
507
- issueCreateAndWrite: "\uC774\uC288 \uBCF8\uBB38 \uD15C\uD50C\uB9BF\uC744 \uC0DD\uC131\uD574 \uBAA9\uD45C/\uC644\uB8CC \uAE30\uC900\uC744 \uAC80\uD1A0\xB7\uBCF4\uC644\uD558\uACE0, \uC0AC\uC6A9\uC790 \uC2B9\uC778(OK) \uD6C4 \uC774\uC288\uB97C \uC0DD\uC131\uD558\uC138\uC694. \uC774\uD6C4 tasks.md\uC758 \uC774\uC288 \uBC88\uD638\uB97C \uCC44\uC6B0\uACE0 \uBB38\uC11C \uCEE4\uBC0B\uC744 \uC900\uBE44\uD558\uC138\uC694.",
508
- issuePrepareFromDoc: "`issue.md`\uB97C \uAE30\uC900\uC73C\uB85C \uC774\uC288 \uC81C\uBAA9/\uBCF8\uBB38/\uB77C\uBCA8 \uCD08\uC548\uC744 \uBCF4\uC644\uD558\uACE0 \uC0AC\uC6A9\uC790 \uC2B9\uC778(OK)\uC744 \uBC1B\uC544 \uC0C1\uD0DC\uB97C `Ready`\uB85C \uBCC0\uACBD\uD558\uC138\uC694.",
507
+ issueCreateAndWrite: "\uC774\uC288 \uBCF8\uBB38 \uD15C\uD50C\uB9BF\uC744 \uC0DD\uC131\uD574 \uBAA9\uD45C/\uC644\uB8CC \uAE30\uC900\uC744 \uAC80\uD1A0\xB7\uBCF4\uC644\uD558\uACE0, \uB77C\uBCA8 \uC2B9\uC778(`A` \uB610\uB294 `A OK`) \uD6C4 \uC774\uC288\uB97C \uC0DD\uC131\uD558\uC138\uC694. \uC774\uD6C4 tasks.md\uC758 \uC774\uC288 \uBC88\uD638\uB97C \uCC44\uC6B0\uACE0 \uBB38\uC11C \uCEE4\uBC0B\uC744 \uC900\uBE44\uD558\uC138\uC694.",
508
+ issuePrepareFromDoc: "`issue.md`\uB97C \uAE30\uC900\uC73C\uB85C \uC774\uC288 \uC81C\uBAA9/\uBCF8\uBB38/\uB77C\uBCA8 \uCD08\uC548\uC744 \uBCF4\uC644\uD558\uACE0 \uB77C\uBCA8 \uC2B9\uC778(`A` \uB610\uB294 `A OK`)\uC744 \uBC1B\uC544 \uC0C1\uD0DC\uB97C `Ready`\uB85C \uBCC0\uACBD\uD558\uC138\uC694.",
509
509
  issueCreateFromDoc: "`issue.md` \uC0C1\uD0DC\uAC00 `Ready`\uC774\uBA74 GitHub Issue\uB97C \uC0DD\uC131\uD558\uACE0, \uC0DD\uC131\uB41C \uC774\uC288 \uBC88\uD638\uB97C `tasks.md`\uC5D0 \uBC18\uC601\uD558\uC138\uC694.",
510
510
  docsCommitIssueUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(#{issueNumber}): {folderName} \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8"',
511
511
  docsCommitUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs: {folderName} \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8"',
@@ -529,7 +529,7 @@ var ko = {
529
529
  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",
530
530
  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)",
531
531
  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)",
532
- 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`)\uC758 `Summary`/`Decision`\uC744 \uAE30\uB85D\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
532
+ 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`/`Decision`/`Findings`(\uB610\uB294 \uBA85\uC2DC\uC801 `0 findings`)/`Residual Risks`/`Tests Run`\uC744 \uAE30\uB85D\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
533
533
  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)",
534
534
  prePrReviewRun: "PR \uC0DD\uC131 \uC804 \uC0AC\uC804 \uCF54\uB4DC\uB9AC\uBDF0\uB97C \uC9C4\uD589\uD558\uC138\uC694. \uAE30\uBCF8 \uBCA0\uC774\uC2A4\uB77C\uC778\uC740 `{fallback}`\uC774\uBA70, `create-pr` \uBB38\uC11C\uC758 `Pre-PR \uAE30\uBCF8 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8` \uC139\uC158\uC744 \uD56D\uC0C1 \uC218\uD589\uD558\uC138\uC694. \uC6B0\uC120\uC21C\uC704 \uC2A4\uD0AC: {skills} (\uC124\uCE58\uB41C \uB354 \uC801\uD569\uD55C \uC2A4\uD0AC\uC774 \uC788\uB2E4\uBA74 \uBA3C\uC800 \uC81C\uC548 \uD6C4 \uC0AC\uC6A9)\uB85C \uCD94\uAC00 \uC2EC\uD654 \uAC80\uD1A0\uB97C \uC9C4\uD589\uD558\uC138\uC694. \uC644\uB8CC \uD6C4 `PR \uC804 \uB9AC\uBDF0`\uB97C Done\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.",
535
535
  prReviewEvidenceFieldMissing: "tasks.md\uC5D0 `PR \uB9AC\uBDF0 Evidence` \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. `- **PR \uB9AC\uBDF0 Evidence**: -` \uD56D\uBAA9\uC744 \uCD94\uAC00\uD558\uACE0 \uB2E4\uC2DC \uC9C4\uD589\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
@@ -588,7 +588,7 @@ var ko = {
588
588
  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.",
589
589
  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.)",
590
590
  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.)",
591
- workflowPrePrEvidenceMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Evidence`\uAC00 \uBE44\uC5B4\uC788\uAC70\uB098 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. (`Pre-PR Review Log`/`PR \uC804 \uB9AC\uBDF0 \uB85C\uADF8`\uC758 `Summary`/`Decision`\uC774 \uC788\uB294 \uC2E4\uC81C \uACBD\uB85C\uB97C \uAE30\uB85D\uD558\uC138\uC694.)",
591
+ workflowPrePrEvidenceMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Evidence`\uAC00 \uBE44\uC5B4\uC788\uAC70\uB098 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. (`Pre-PR Review Log`/`PR \uC804 \uB9AC\uBDF0 \uB85C\uADF8`\uC5D0 placeholder\uAC00 \uC544\uB2CC `Summary`/`Decision`/`Findings`(\uB610\uB294 \uBA85\uC2DC\uC801 `0 findings`)/`Residual Risks`/`Tests Run`\uC774 \uC788\uB294 \uC2E4\uC81C \uACBD\uB85C\uB97C \uAE30\uB85D\uD558\uC138\uC694.)",
592
592
  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)",
593
593
  workflowPrePrDecisionNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Decision`\uC774 `{outcome}`\uC785\uB2C8\uB2E4. \uB9AC\uBDF0 \uB9AC\uC2A4\uD06C\uB97C \uD574\uC18C\uD55C \uB4A4 pre-pr-review\uB97C \uC7AC\uC2E4\uD589\uD574 `approve`\uB85C \uB9DE\uCD94\uC138\uC694."
594
594
  }
@@ -1015,8 +1015,8 @@ var en = {
1015
1015
  tasksImprove: "Improve tasks.md and change Doc Status to Review.",
1016
1016
  tasksApproval: "Share tasks.md with the user and get progress approval (`A` or `A OK` format). (Then set Doc Status to Approved)",
1017
1017
  docsCommitPlanning: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(planning): {folderName} planning docs"',
1018
- issueCreateAndWrite: "Generate the issue body template, refine goals/completion criteria, get explicit user OK, create the issue, then update issue number in tasks.md and prepare a docs commit.",
1019
- issuePrepareFromDoc: "Use `issue.md` to refine issue title/body/labels draft, get explicit user OK, then set status to `Ready`.",
1018
+ issueCreateAndWrite: "Generate the issue body template, refine goals/completion criteria, get label approval (`A` or `A OK`), create the issue, then update issue number in tasks.md and prepare a docs commit.",
1019
+ issuePrepareFromDoc: "Use `issue.md` to refine issue title/body/labels draft, get label approval (`A` or `A OK`), then set status to `Ready`.",
1020
1020
  issueCreateFromDoc: "When `issue.md` status is `Ready`, create the GitHub Issue and sync the created issue number into `tasks.md`.",
1021
1021
  docsCommitIssueUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(#{issueNumber}): {folderName} docs update"',
1022
1022
  docsCommitUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs: {folderName} docs update"',
@@ -1040,7 +1040,7 @@ var en = {
1040
1040
  taskCommitGateReasonMismatchLastDone: "The latest project code commit does not match the last completed task",
1041
1041
  prLegacyAsk: "tasks.md is missing PR/PR Status fields. Update to the latest template format? (CHECK required)",
1042
1042
  prePrReviewFieldMissing: "tasks.md is missing the `Pre-PR Review` field. Add `- **Pre-PR Review**: Pending | Done` and run context again. (CHECK required)",
1043
- 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` and `Decision`. (CHECK required)",
1043
+ 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`, `Decision`, `Findings` (or explicit `0 findings`), `Residual Risks`, and `Tests Run`. (CHECK required)",
1044
1044
  prePrReviewDecisionMissing: "tasks.md `Pre-PR Decision` is empty/placeholder or missing decision format. Record it as `decision: ...` (or `\uACB0\uC815: ...`). (CHECK required)",
1045
1045
  prePrReviewRun: "Run a pre-PR code review before creating the PR. Always execute the `{fallback}` baseline by following the `Pre-PR Baseline Checklist` section in the `create-pr` doc. Then use preferred skills ({skills}) for deeper review (if a better installed skill fits this change, propose it first). After completing it, set `Pre-PR Review` to Done in tasks.md.",
1046
1046
  prReviewEvidenceFieldMissing: "tasks.md is missing the `PR Review Evidence` field. Add `- **PR Review Evidence**: -` and continue. (CHECK required)",
@@ -1099,7 +1099,7 @@ var en = {
1099
1099
  workflowPrRemoteChecksPending: "Remote PR has {count} pending check(s). Wait for checks to complete, then re-check.",
1100
1100
  workflowPrePrReviewMissing: "Implementation is done but `Pre-PR Review` is missing. (Add `- **Pre-PR Review**: Pending | Done` in tasks.md.)",
1101
1101
  workflowPrePrReviewNotDone: "Implementation is done but `Pre-PR Review` is not Done. (Run pre-PR review, then update it to Done.)",
1102
- workflowPrePrEvidenceMissing: "Implementation is done but `Pre-PR Evidence` is empty/invalid. (Point to an existing file and include `Pre-PR Review Log` with non-placeholder `Summary` and `Decision`.)",
1102
+ workflowPrePrEvidenceMissing: "Implementation is done but `Pre-PR Evidence` is empty/invalid. (Point to an existing file and include `Pre-PR Review Log` with non-placeholder `Summary`, `Decision`, `Findings` (or explicit `0 findings`), `Residual Risks`, and `Tests Run`.)",
1103
1103
  workflowPrePrDecisionMissing: "Implementation is done but `Pre-PR Decision` is empty/invalid. (Use `decision: approve|changes_requested|blocked ...`.)",
1104
1104
  workflowPrePrDecisionNotApproved: "Implementation is done but `Pre-PR Decision` is `{outcome}`. Resolve review risks and re-run pre-PR review until decision becomes `approve`."
1105
1105
  }
@@ -4699,17 +4699,62 @@ function hasReviewLogQuality(content, headerRegex, summaryKeys, decisionKeys) {
4699
4699
  }
4700
4700
  return false;
4701
4701
  }
4702
+ function isExplicitZeroFindingsEntry(value) {
4703
+ const trimmed = value.trim().toLowerCase();
4704
+ if (!trimmed) return false;
4705
+ return /^0+\s*findings?\b/.test(trimmed) || /^no\s+findings?\b/.test(trimmed) || /^findings?\s*[::]\s*0+\b/.test(trimmed) || /^지적\s*사항?\s*[::]?\s*0+\b/.test(trimmed) || /^지적\s*없음\b/.test(trimmed);
4706
+ }
4707
+ function hasPrePrReviewLogQuality(content) {
4708
+ const sections = splitReviewLogSections(content, PRE_PR_REVIEW_LOG_HEADER);
4709
+ for (const section of sections) {
4710
+ const summaryEntries = collectStructuredReviewEntries(section, [
4711
+ "Summary",
4712
+ "\uC694\uC57D",
4713
+ "Note",
4714
+ "\uB178\uD2B8"
4715
+ ]);
4716
+ if (!hasValidReviewLogEntries(summaryEntries)) continue;
4717
+ const decisionEntries = collectStructuredReviewEntries(section, [
4718
+ "Decision",
4719
+ "\uACB0\uC815"
4720
+ ]);
4721
+ if (!hasValidReviewLogEntries(decisionEntries)) continue;
4722
+ const findingsEntries = collectStructuredReviewEntries(section, [
4723
+ "Findings",
4724
+ "\uC9C0\uC801\uC0AC\uD56D",
4725
+ "\uC9C0\uC801 \uC0AC\uD56D"
4726
+ ]);
4727
+ const hasActionableFindings = findingsEntries.map((entry) => entry.trim()).some(
4728
+ (entry) => entry.length > 0 && !isReviewDraftPlaceholder(entry) && !isPlaceholderReviewEvidence(entry) && /\S+:\d+/.test(entry)
4729
+ );
4730
+ const hasExplicitZeroFindings = findingsEntries.some(
4731
+ (entry) => isExplicitZeroFindingsEntry(entry)
4732
+ );
4733
+ if (!hasActionableFindings && !hasExplicitZeroFindings) continue;
4734
+ const residualRiskEntries = collectStructuredReviewEntries(section, [
4735
+ "Residual Risks",
4736
+ "Residual Risk",
4737
+ "\uC794\uC5EC \uB9AC\uC2A4\uD06C",
4738
+ "\uC794\uC5EC \uC704\uD5D8"
4739
+ ]);
4740
+ if (!hasValidReviewLogEntries(residualRiskEntries)) continue;
4741
+ const testsRunEntries = collectStructuredReviewEntries(section, [
4742
+ "Tests Run",
4743
+ "Test Run",
4744
+ "\uC2E4\uD589 \uD14C\uC2A4\uD2B8",
4745
+ "\uD14C\uC2A4\uD2B8 \uC2E4\uD589"
4746
+ ]);
4747
+ if (!hasValidReviewLogEntries(testsRunEntries)) continue;
4748
+ return true;
4749
+ }
4750
+ return false;
4751
+ }
4702
4752
  var PRE_PR_REVIEW_LOG_HEADER = /^##\s+(?:Pre-PR Review Log|PR 전 리뷰 로그)\b.*$/gim;
4703
4753
  var PR_REVIEW_LOG_HEADER = /^##\s+(?:PR Review Log|PR 리뷰 로그)\b.*$/gim;
4704
4754
  async function hasPrePrReviewLogEvidence(candidatePath) {
4705
4755
  try {
4706
4756
  const content = await fs17.readFile(candidatePath, "utf-8");
4707
- return hasReviewLogQuality(
4708
- content,
4709
- PRE_PR_REVIEW_LOG_HEADER,
4710
- ["Summary", "\uC694\uC57D", "Note", "\uB178\uD2B8"],
4711
- ["Decision", "\uACB0\uC815"]
4712
- );
4757
+ return hasPrePrReviewLogQuality(content);
4713
4758
  } catch {
4714
4759
  return false;
4715
4760
  }
@@ -6862,7 +6907,7 @@ function getApprovalSessionId() {
6862
6907
  if (explicit) return explicit;
6863
6908
  const terminalSession = (process.env.TERM_SESSION_ID || process.env.WT_SESSION || process.env.TMUX_PANE || "").trim();
6864
6909
  if (terminalSession) return terminalSession;
6865
- return `ppid:${process.ppid}`;
6910
+ return "";
6866
6911
  }
6867
6912
  function getApprovalTicketPaths(config) {
6868
6913
  return {
@@ -7004,7 +7049,7 @@ async function consumeApprovalTicket(config, token, expected) {
7004
7049
  "Approval ticket expired. Run `context` again and re-approve."
7005
7050
  );
7006
7051
  }
7007
- if (record.sessionId !== sessionId) {
7052
+ if (record.sessionId && record.sessionId !== sessionId) {
7008
7053
  throw createCliError(
7009
7054
  "INVALID_APPROVAL",
7010
7055
  "Approval ticket session mismatch. Re-run `context` in the current session and approve again."
@@ -7198,17 +7243,38 @@ var LONG_RUNNING_DELEGATION_CATEGORIES = [
7198
7243
  "review_fix_commit",
7199
7244
  "pre_pr_review"
7200
7245
  ];
7201
- function shouldDelegateCurrentAction(actionOptions, autoRunAvailable) {
7202
- const primaryCategory = actionOptions[0]?.action?.category || null;
7246
+ function isTaskExecuteProjectCommitCommand(option) {
7247
+ if (!option || option.action.type !== "command") return false;
7248
+ if (option.action.category !== "task_execute") return false;
7249
+ if (option.action.scope !== "project") return false;
7250
+ return /\bgit\s+commit\b/i.test(option.action.cmd);
7251
+ }
7252
+ function shouldDelegateCurrentAction(actionOptions) {
7253
+ const primaryOption = actionOptions[0];
7254
+ const primaryCategory = primaryOption?.action?.category || null;
7203
7255
  const longRunningSet = new Set(LONG_RUNNING_DELEGATION_CATEGORIES);
7204
- const shouldDelegate = autoRunAvailable || !!primaryCategory && longRunningSet.has(primaryCategory);
7256
+ const shouldDelegate = !!primaryCategory && longRunningSet.has(primaryCategory) && !isTaskExecuteProjectCommitCommand(primaryOption);
7205
7257
  return {
7206
7258
  shouldDelegate,
7207
7259
  category: primaryCategory
7208
7260
  };
7209
7261
  }
7210
- function buildAgentOrchestrationPolicy(actionOptions, autoRunAvailable) {
7211
- const delegation = shouldDelegateCurrentAction(actionOptions, autoRunAvailable);
7262
+ function buildAgentOrchestrationPolicy(actionOptions, autoRunAvailable, autoRunCommand, featureRef) {
7263
+ const delegation = shouldDelegateCurrentAction(actionOptions);
7264
+ const primaryOption = actionOptions[0];
7265
+ const delegatedCommandOption = primaryOption && primaryOption.action.type === "command" && delegation.shouldDelegate ? primaryOption : null;
7266
+ const handoffMode = delegatedCommandOption ? "command" : autoRunAvailable ? "auto_run" : null;
7267
+ const handoffCwd = delegatedCommandOption?.action.cwd || (autoRunAvailable ? process.cwd() : null);
7268
+ const handoffCmd = delegatedCommandOption?.action.cmd || (autoRunAvailable ? autoRunCommand : null);
7269
+ const handoffRequired = !!handoffMode && !!handoffCwd && !!handoffCmd;
7270
+ const verifyCacheKey = handoffRequired ? createHash("sha1").update(
7271
+ [
7272
+ handoffMode,
7273
+ featureRef || "",
7274
+ handoffCwd || "",
7275
+ handoffCmd || ""
7276
+ ].join("|")
7277
+ ).digest("hex").slice(0, 12) : "";
7212
7278
  return {
7213
7279
  mode: "main_orchestrates_subagent_execution",
7214
7280
  delegationPolicy: "prefer_main_delegate_long_running_fallback_main",
@@ -7217,6 +7283,7 @@ function buildAgentOrchestrationPolicy(actionOptions, autoRunAvailable) {
7217
7283
  fallbackToMainAgentWhenSubAgentUnavailable: true,
7218
7284
  longRunningCategories: [...LONG_RUNNING_DELEGATION_CATEGORIES],
7219
7285
  currentActionShouldDelegate: delegation.shouldDelegate,
7286
+ autoRunShouldDelegate: autoRunAvailable,
7220
7287
  currentActionCategory: delegation.category,
7221
7288
  mainAgentResponsibilities: [
7222
7289
  "Keep user conversation state and approval boundaries",
@@ -7239,7 +7306,23 @@ function buildAgentOrchestrationPolicy(actionOptions, autoRunAvailable) {
7239
7306
  "flow --resume <RUN_ID>",
7240
7307
  "autoRun.resume.flowCommand",
7241
7308
  "context --json-compact"
7242
- ]
7309
+ ],
7310
+ subAgentHandoff: {
7311
+ required: handoffRequired,
7312
+ mode: handoffMode,
7313
+ featureRef,
7314
+ category: delegation.category,
7315
+ cwd: handoffCwd,
7316
+ cmd: handoffCmd,
7317
+ verify: handoffRequired ? {
7318
+ runOncePerSession: true,
7319
+ cacheKey: verifyCacheKey,
7320
+ expectedCwd: handoffCwd,
7321
+ commands: ["pwd", "git rev-parse --show-toplevel"],
7322
+ onMismatch: "stop_and_report",
7323
+ collectDetailedLogsOnMismatchOnly: true
7324
+ } : null
7325
+ }
7243
7326
  };
7244
7327
  }
7245
7328
  async function resolveContextState(config, featureName, options) {
@@ -7791,7 +7874,9 @@ async function runContext(featureName, options) {
7791
7874
  );
7792
7875
  const agentOrchestration = buildAgentOrchestrationPolicy(
7793
7876
  state.actionOptions,
7794
- autoRunPlan.available
7877
+ autoRunPlan.available,
7878
+ autoRunPlan.command,
7879
+ state.matchedFeature?.folderName || null
7795
7880
  );
7796
7881
  if (options.approve || options.execute) {
7797
7882
  await runApprovedOption(
@@ -8224,10 +8309,7 @@ async function runContext(featureName, options) {
8224
8309
  }
8225
8310
  const actionOptions = state.actionOptions;
8226
8311
  const hasCommandOption = actionOptions.some((option) => option.action.type === "command");
8227
- const longRunningDelegation = shouldDelegateCurrentAction(
8228
- actionOptions,
8229
- autoRunPlan.available
8230
- );
8312
+ const longRunningDelegation = shouldDelegateCurrentAction(actionOptions);
8231
8313
  console.log(chalk6.green(chalk6.bold("\u{1F449} Next Options (Atomic):")));
8232
8314
  let hasDocsCommand = false;
8233
8315
  actionOptions.forEach((option) => {
@@ -9614,8 +9696,12 @@ function toFlowRunStatus(status) {
9614
9696
  return "failed";
9615
9697
  }
9616
9698
  }
9617
- function buildAgentOrchestrationPolicy2(autoRun) {
9699
+ function buildAgentOrchestrationPolicy2(autoRun, featureRef) {
9618
9700
  const preferredResumeCommand = autoRun?.run?.resumeCommand || autoRun?.resume?.flowCommand || null;
9701
+ const handoffRequired = !!autoRun && !!preferredResumeCommand;
9702
+ const verifyCacheKey = handoffRequired ? `${(featureRef || "unknown").toLowerCase()}|${Buffer.from(
9703
+ preferredResumeCommand
9704
+ ).toString("base64").slice(0, 12)}` : "";
9619
9705
  return {
9620
9706
  mode: "main_orchestrates_subagent_execution",
9621
9707
  delegationPolicy: "prefer_main_delegate_long_running_fallback_main",
@@ -9640,7 +9726,23 @@ function buildAgentOrchestrationPolicy2(autoRun) {
9640
9726
  "AUTO_MANUAL_REQUIRED",
9641
9727
  "command execution error"
9642
9728
  ],
9643
- preferredResumeCommand
9729
+ preferredResumeCommand,
9730
+ subAgentHandoff: {
9731
+ required: handoffRequired,
9732
+ mode: handoffRequired ? "auto_run" : null,
9733
+ featureRef,
9734
+ category: null,
9735
+ cwd: handoffRequired ? process.cwd() : null,
9736
+ cmd: handoffRequired ? preferredResumeCommand : null,
9737
+ verify: handoffRequired ? {
9738
+ runOncePerSession: true,
9739
+ cacheKey: verifyCacheKey,
9740
+ expectedCwd: process.cwd(),
9741
+ commands: ["pwd", "git rev-parse --show-toplevel"],
9742
+ onMismatch: "stop_and_report",
9743
+ collectDetailedLogsOnMismatchOnly: true
9744
+ } : null
9745
+ }
9644
9746
  };
9645
9747
  }
9646
9748
  function getFeatureRef2(feature) {
@@ -10291,7 +10393,10 @@ async function runFlow(featureName, options) {
10291
10393
  const jsonMode = !!options.json || !!options.jsonCompact;
10292
10394
  if (jsonMode) {
10293
10395
  const autoRunFailed = !!(autoRun && isAutoRunFailureStatus(autoRun.status));
10294
- const agentOrchestration2 = buildAgentOrchestrationPolicy2(autoRun);
10396
+ const agentOrchestration2 = buildAgentOrchestrationPolicy2(
10397
+ autoRun,
10398
+ resolvedFeatureName || null
10399
+ );
10295
10400
  const status = autoRunFailed ? "error" : "ok";
10296
10401
  const reasonCode = autoRunFailed ? autoRun?.reasonCode || "AUTO_EXECUTION_FAILED" : "FLOW_SUMMARY";
10297
10402
  if (options.jsonCompact) {
@@ -10386,7 +10491,10 @@ async function runFlow(featureName, options) {
10386
10491
  console.log(chalk6.gray(`- Resume with: ${autoRun.run.resumeCommand}`));
10387
10492
  }
10388
10493
  }
10389
- const agentOrchestration = buildAgentOrchestrationPolicy2(autoRun);
10494
+ const agentOrchestration = buildAgentOrchestrationPolicy2(
10495
+ autoRun,
10496
+ resolvedFeatureName || null
10497
+ );
10390
10498
  console.log(
10391
10499
  chalk6.gray(
10392
10500
  `- Orchestration: ${agentOrchestration.mode}, delegate long-running loops to sub-agent`
@@ -11485,6 +11593,54 @@ ${mermaidSection}
11485
11593
  - **Spec**: \`${bodyPaths.specPath}\`
11486
11594
  - **Tasks**: \`${bodyPaths.tasksPath}\`${closes}`;
11487
11595
  }
11596
+ function stripMarkdownCodeContexts(body) {
11597
+ const lines = body.split("\n");
11598
+ const out = [];
11599
+ let inFence = false;
11600
+ let fenceChar = "";
11601
+ let fenceLength = 0;
11602
+ for (const line of lines) {
11603
+ const fenceStartMatch = line.match(/^\s*(`{3,}|~{3,})/);
11604
+ if (fenceStartMatch) {
11605
+ const marker = fenceStartMatch[1];
11606
+ const markerChar = marker[0];
11607
+ const markerLength = marker.length;
11608
+ if (!inFence) {
11609
+ inFence = true;
11610
+ fenceChar = markerChar;
11611
+ fenceLength = markerLength;
11612
+ continue;
11613
+ }
11614
+ if (markerChar === fenceChar && markerLength >= fenceLength) {
11615
+ inFence = false;
11616
+ fenceChar = "";
11617
+ fenceLength = 0;
11618
+ }
11619
+ continue;
11620
+ }
11621
+ if (inFence) continue;
11622
+ out.push(line.replace(/`[^`\n]*`/g, ""));
11623
+ }
11624
+ return out.join("\n");
11625
+ }
11626
+ function hasIssueClosingKeyword(body, issueNumber) {
11627
+ if (!issueNumber) return false;
11628
+ const cleaned = stripMarkdownCodeContexts(body);
11629
+ const issue = escapeRegExp3(issueNumber);
11630
+ const closeKeywordRegex = new RegExp(
11631
+ `\\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\\b\\s*(?:[a-zA-Z0-9_.-]+\\/)?#\\s*${issue}\\b`,
11632
+ "i"
11633
+ );
11634
+ return closeKeywordRegex.test(cleaned);
11635
+ }
11636
+ function ensureIssueClosingLine(body, issueNumber) {
11637
+ if (!issueNumber) return body;
11638
+ if (hasIssueClosingKeyword(body, issueNumber)) return body;
11639
+ const trimmed = body.trimEnd();
11640
+ const separator = trimmed.length > 0 ? "\n\n" : "";
11641
+ return `${trimmed}${separator}Closes #${issueNumber}
11642
+ `;
11643
+ }
11488
11644
  function getRequiredIssueSections(lang) {
11489
11645
  return getGithubDraftRequiredSections("issue", lang);
11490
11646
  }
@@ -11938,8 +12094,8 @@ function githubCommand(program2) {
11938
12094
  kindLabel: tg(config.lang, "kindPr"),
11939
12095
  lang: config.lang
11940
12096
  });
11941
- const body = preparedBody.body;
11942
- const bodyFile = preparedBody.bodyFile;
12097
+ let body = preparedBody.body;
12098
+ let bodyFile = preparedBody.bodyFile;
11943
12099
  const title = options.title?.trim() || (preparedBody.source === "workflow-ready" ? preparedBody.draftMetadata?.title : void 0) || defaultTitle;
11944
12100
  const labels = parseLabels(
11945
12101
  optionLabels || (preparedBody.source === "workflow-ready" ? preparedBody.draftMetadata?.labels : void 0),
@@ -11954,6 +12110,18 @@ function githubCommand(program2) {
11954
12110
  let syncChanged = false;
11955
12111
  const pushDocsSync = shouldPushDocsSync(config);
11956
12112
  if (options.create) {
12113
+ const normalizedBody = ensureIssueClosingLine(body, feature.issueNumber);
12114
+ if (normalizedBody !== body) {
12115
+ body = normalizedBody;
12116
+ const fallbackBodyFile = defaultBodyFile;
12117
+ if (preparedBody.source === "generated") {
12118
+ await fs17.writeFile(bodyFile, body, "utf-8");
12119
+ } else {
12120
+ await fs17.ensureDir(path23.dirname(fallbackBodyFile));
12121
+ await fs17.writeFile(fallbackBodyFile, body, "utf-8");
12122
+ bodyFile = fallbackBodyFile;
12123
+ }
12124
+ }
11957
12125
  const projectGitCwd = resolveGithubProjectCwd(config, feature);
11958
12126
  ensureNoTodoPlaceholders(body, tg(config.lang, "kindPr"), config.lang);
11959
12127
  ensurePrArtifacts(body, artifactPolicy, config.lang);
@@ -12983,6 +13151,12 @@ function buildReportContent(input) {
12983
13151
  - **Skills**: ${skills}
12984
13152
  - **Decision**: ${input.decision}
12985
13153
  - **Summary**: ${input.note}
13154
+ - **Findings**:
13155
+ - TODO: <file:line | severity: low|medium|high | fix: required|optional | note: ...> OR 0 findings
13156
+ - **Residual Risks**:
13157
+ - TODO: residual risk assessment after review
13158
+ - **Tests Run**:
13159
+ - TODO: commands/results verified during pre-PR review
12986
13160
  - **Evidence**:
12987
13161
  - TODO: review comments/paths/tests verified during pre-PR review
12988
13162
  - **Trace**: pre-pr-review command executed and synced with tasks.md