lee-spec-kit 0.6.35 → 0.6.37

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
@@ -597,6 +597,8 @@ var koMessages = {
597
597
  projectCommitUpdate: 'cd "{projectGitCwd}" && (git diff --cached --quiet && echo "\uC2A4\uD14C\uC774\uC9D5\uB41C \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC774\uBC88 \uD0DC\uC2A4\uD06C\uC5D0\uC11C \uC218\uC815\uD55C \uD30C\uC77C\uB9CC \uC120\uD0DD\uD574 git add [files] \uD6C4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694." && exit 1 || git commit -m "feat({folderName}): {commitTopic}")',
598
598
  reviewFixCommitIssueGuidance: "PR \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B\uC744 \uC9C4\uD589\uD558\uC138\uC694. \uB9AC\uBDF0 \uBC18\uC601 \uD30C\uC77C\uB9CC \uC2A4\uD14C\uC774\uC9D5\uD55C \uB4A4 `fix(#{issueNumber}): <review-fix-summary>` \uD615\uC2DD\uC73C\uB85C \uCEE4\uBC0B\uD558\uC138\uC694. `<review-fix-summary>`\uC5D0\uB294 \uC774\uBC88 \uCEE4\uBC0B\uC5D0\uC11C \uC2E4\uC81C\uB85C \uD574\uACB0\uD55C \uB9AC\uBDF0 \uD56D\uBAA9 \uC694\uC57D\uC744 \uC791\uC131\uD558\uC138\uC694. (\uD0DC\uC2A4\uD06C \uC81C\uBAA9 \uC7AC\uC0AC\uC6A9 \uAE08\uC9C0)",
599
599
  reviewFixCommitGuidance: "PR \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B\uC744 \uC9C4\uD589\uD558\uC138\uC694. \uB9AC\uBDF0 \uBC18\uC601 \uD30C\uC77C\uB9CC \uC2A4\uD14C\uC774\uC9D5\uD55C \uB4A4 `fix(review): <review-fix-summary>` \uD615\uC2DD\uC73C\uB85C \uCEE4\uBC0B\uD558\uC138\uC694. `<review-fix-summary>`\uC5D0\uB294 \uC774\uBC88 \uCEE4\uBC0B\uC5D0\uC11C \uC2E4\uC81C\uB85C \uD574\uACB0\uD55C \uB9AC\uBDF0 \uD56D\uBAA9 \uC694\uC57D\uC744 \uC791\uC131\uD558\uC138\uC694. (\uD0DC\uC2A4\uD06C \uC81C\uBAA9 \uC7AC\uC0AC\uC6A9 \uAE08\uC9C0)",
600
+ prePrFixCommitIssueGuidance: "pre-PR \uB9AC\uBDF0 \uC9C0\uC801\uC0AC\uD56D \uC218\uC815 \uCEE4\uBC0B\uC744 \uC9C4\uD589\uD558\uC138\uC694. pre-PR \uC218\uC815 \uD30C\uC77C\uB9CC \uC2A4\uD14C\uC774\uC9D5\uD55C \uB4A4 `fix(#{issueNumber}): <pre-pr-fix-summary>` \uD615\uC2DD\uC73C\uB85C \uCEE4\uBC0B\uD558\uC138\uC694. `<pre-pr-fix-summary>`\uC5D0\uB294 pre-PR\uC5D0\uC11C \uD574\uACB0\uD55C \uC9C0\uC801\uC0AC\uD56D\uC744 \uC791\uC131\uD558\uC138\uC694.",
601
+ prePrFixCommitGuidance: "pre-PR \uB9AC\uBDF0 \uC9C0\uC801\uC0AC\uD56D \uC218\uC815 \uCEE4\uBC0B\uC744 \uC9C4\uD589\uD558\uC138\uC694. pre-PR \uC218\uC815 \uD30C\uC77C\uB9CC \uC2A4\uD14C\uC774\uC9D5\uD55C \uB4A4 `fix(pre-pr): <pre-pr-fix-summary>` \uD615\uC2DD\uC73C\uB85C \uCEE4\uBC0B\uD558\uC138\uC694. `<pre-pr-fix-summary>`\uC5D0\uB294 pre-PR\uC5D0\uC11C \uD574\uACB0\uD55C \uC9C0\uC801\uC0AC\uD56D\uC744 \uC791\uC131\uD558\uC138\uC694.",
600
602
  standaloneNeedsProjectRoot: "standalone \uBAA8\uB4DC\uC5D0\uC11C\uB294 projectRoot \uC124\uC815\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. (npx lee-spec-kit config --project-root ...)",
601
603
  createBranch: 'cd "{projectGitCwd}" && mkdir -p .worktrees && (git worktree add ".worktrees/feat-{issueNumber}-{slug}" "feat/{issueNumber}-{slug}" || git worktree add -b "feat/{issueNumber}-{slug}" ".worktrees/feat-{issueNumber}-{slug}") && WT="{projectGitCwd}/.worktrees/feat-{issueNumber}-{slug}" && for f in .env .env.local .env.development .env.development.local .env.test .env.test.local .env.production .env.production.local; do [ -f "{projectGitCwd}/$f" ] && [ ! -e "$WT/$f" ] && cp "{projectGitCwd}/$f" "$WT/$f" || true; done && echo "worktree: {projectGitCwd}/.worktrees/feat-{issueNumber}-{slug}"',
602
604
  moveToExistingWorktree: '\uD574\uB2F9 feature\uC6A9 worktree\uAC00 \uC774\uBBF8 \uC788\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 `cd "{worktreePath}"`\uB85C \uC774\uB3D9\uD55C \uB4A4 context\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.',
@@ -618,6 +620,7 @@ var koMessages = {
618
620
  prePrReviewRun: "\uCF54\uB4DC \uB9AC\uBDF0 \uC5D0\uC774\uC804\uD2B8\uB97C \uC2E4\uD589\uD574 \uBE44\uC5B4\uC788\uC9C0 \uC54A\uC740 `commandsExecuted`\uB97C \uD3EC\uD568\uD55C `review-trace.json`\uC744 \uC0DD\uC131\uD55C \uB4A4, `pre-pr-review --evidence review-trace.json`\uC73C\uB85C \uB9AC\uBDF0 \uACB0\uACFC\uB97C \uAE30\uB85D\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
619
621
  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`/\uC2E4\uD589 \uBA85\uB839(`commandsExecuted`)\uC744 \uAE30\uB85D\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
620
622
  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)",
623
+ 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)",
621
624
  prePrReviewDecisionReconfirm: "\uD604\uC7AC `PR \uC804 \uB9AC\uBDF0 Decision`\uC774 `{decision}`\uC785\uB2C8\uB2E4. \uC9C0\uC801\uC0AC\uD56D\uC744 \uBC18\uC601\uD55C \uB4A4 \uC774\uC804 \uC0C1\uD0DC \uC7AC\uC0AC\uC6A9\uC744 \uB9C9\uAE30 \uC704\uD574 \uBA85\uC2DC\uC801\uC73C\uB85C Decision\uC744 \uC9C0\uC815\uD574 \uC7AC\uC2E4\uD589\uD558\uC138\uC694: `{command}` (\uD655\uC778 \uD544\uC694)",
622
625
  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)",
623
626
  prReviewEvidenceMissing: "tasks.md\uC758 `PR \uB9AC\uBDF0 Evidence`\uAC00 \uBE44\uC5B4\uC788\uAC70\uB098 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. `\uC694\uC57D: ...`(\uB610\uB294 `summary: ...`) \uD615\uC2DD\uC73C\uB85C \uAE30\uB85D\uD558\uAC70\uB098 `PR Review Log`(\uB610\uB294 `PR \uB9AC\uBDF0 \uB85C\uADF8`)\uC758 `Summary`/`Decision`\uC774 \uC788\uB294 \uD30C\uC77C \uACBD\uB85C\uB97C \uC9C0\uC815\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
@@ -1139,6 +1142,8 @@ var enMessages = {
1139
1142
  projectCommitUpdate: 'cd "{projectGitCwd}" && (git diff --cached --quiet && echo "No staged files. Stage only files changed in this task with git add [files], then run again." && exit 1 || git commit -m "feat({folderName}): {commitTopic}")',
1140
1143
  reviewFixCommitIssueGuidance: "Commit PR review fixes. Stage only review-fix files, then commit with `fix(#{issueNumber}): <review-fix-summary>`. `<review-fix-summary>` must describe review comments resolved in this commit (do not reuse task titles).",
1141
1144
  reviewFixCommitGuidance: "Commit PR review fixes. Stage only review-fix files, then commit with `fix(review): <review-fix-summary>`. `<review-fix-summary>` must describe review comments resolved in this commit (do not reuse task titles).",
1145
+ prePrFixCommitIssueGuidance: "Commit fixes requested by pre-PR review. Stage only pre-PR fix files, then commit with `fix(#{issueNumber}): <pre-pr-fix-summary>`. `<pre-pr-fix-summary>` must describe findings resolved from pre-PR review.",
1146
+ prePrFixCommitGuidance: "Commit fixes requested by pre-PR review. Stage only pre-PR fix files, then commit with `fix(pre-pr): <pre-pr-fix-summary>`. `<pre-pr-fix-summary>` must describe findings resolved from pre-PR review.",
1142
1147
  standaloneNeedsProjectRoot: "Standalone mode requires projectRoot. (npx lee-spec-kit config --project-root ...)",
1143
1148
  createBranch: 'cd "{projectGitCwd}" && mkdir -p .worktrees && (git worktree add ".worktrees/feat-{issueNumber}-{slug}" "feat/{issueNumber}-{slug}" || git worktree add -b "feat/{issueNumber}-{slug}" ".worktrees/feat-{issueNumber}-{slug}") && WT="{projectGitCwd}/.worktrees/feat-{issueNumber}-{slug}" && for f in .env .env.local .env.development .env.development.local .env.test .env.test.local .env.production .env.production.local; do [ -f "{projectGitCwd}/$f" ] && [ ! -e "$WT/$f" ] && cp "{projectGitCwd}/$f" "$WT/$f" || true; done && echo "worktree: {projectGitCwd}/.worktrees/feat-{issueNumber}-{slug}"',
1144
1149
  moveToExistingWorktree: 'A matching feature worktree already exists. Move first: `cd "{worktreePath}"`, then re-run context.',
@@ -1160,6 +1165,7 @@ var enMessages = {
1160
1165
  prePrReviewRun: "Run the code review agent and generate `review-trace.json` with non-empty `commandsExecuted`, then execute `pre-pr-review --evidence review-trace.json` to record findings. (CHECK required)",
1161
1166
  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`, `Tests Run`, and real executed commands (`commandsExecuted`). (CHECK required)",
1162
1167
  prePrReviewDecisionMissing: "tasks.md `Pre-PR Decision` is empty/placeholder or missing decision format. Record it as `decision: ...` (or `\uACB0\uC815: ...`). (CHECK required)",
1168
+ prePrReviewFixRequired: "Current `Pre-PR Decision` is `{decision}`. Apply the requested fixes from pre-PR findings before moving to PR creation. (CHECK required)",
1163
1169
  prePrReviewDecisionReconfirm: "Current `Pre-PR Decision` is `{decision}`. After fixing findings, rerun pre-PR review with an explicit decision to avoid replaying the prior state: `{command}` (CHECK required)",
1164
1170
  prReviewEvidenceFieldMissing: "tasks.md is missing the `PR Review Evidence` field. Add `- **PR Review Evidence**: -` and continue. (CHECK required)",
1165
1171
  prReviewEvidenceMissing: "tasks.md `PR Review Evidence` is empty/invalid. Use `summary: ...` (or `\uC694\uC57D: ...`), or point to a file containing `PR Review Log` with non-placeholder `Summary` and `Decision`. (CHECK required)",
@@ -3349,7 +3355,8 @@ function getPrePrReviewPrompt(lang, skills, fallbackText) {
3349
3355
  3. \uD655\uC778\uB41C \uAC01 \uD30C\uC77C\uC5D0 \uB300\uD574 risk, security, perf, maintainability \uD3C9\uAC00\uC640 \uAD6C\uCCB4\uC801\uC778 fileLine \uC704\uCE58\uAC00 \uD3EC\uD568\uB41C 'review-trace.json' \uC99D\uAC70 \uD30C\uC77C\uC744 \uC0DD\uC131\uD558\uC138\uC694. \uC794\uC5EC \uC704\uD5D8(residualRisks)\uACFC \uC2E4\uD589\uD55C \uBA85\uB839\uC5B4(commandsExecuted)\uB3C4 \uD3EC\uD568\uD558\uC138\uC694.
3350
3356
  4. \uAE30\uBCF8 \uBCA0\uC774\uC2A4\uB77C\uC778\uC740 '${fallbackText}'\uC774\uBA70, 'create-pr' \uBB38\uC11C\uC758 'Pre-PR \uAE30\uBCF8 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8' \uC139\uC158\uC744 \uC218\uD589\uD558\uC138\uC694.
3351
3357
  5. \uC6B0\uC120\uC21C\uC704 \uC2A4\uD0AC: ${skills.length > 0 ? skills.join(", ") : "\uC5C6\uC74C"} \uB85C \uC2EC\uD654 \uAC80\uD1A0\uB97C \uC9C4\uD589\uD558\uC138\uC694.
3352
- \uC644\uB8CC \uD6C4 'pnpm lee pre-pr-review <feature> --evidence review-trace.json --decision approve' \uB97C \uC2E4\uD589\uD558\uC138\uC694.`;
3358
+ 6. \uC9C0\uC801\uC0AC\uD56D\uC774 \uB0A8\uC544 \uC788\uC73C\uBA74 \uBA3C\uC800 'npx lee-spec-kit pre-pr-review <feature> --evidence review-trace.json --decision changes_requested' \uB85C \uAE30\uB85D\uD558\uACE0 \uCF54\uB4DC\uB97C \uC218\uC815\uD558\uC138\uC694.
3359
+ 7. \uC218\uC815/\uC7AC\uAC80\uC99D \uD6C4 \uCD5C\uC885 \uC2B9\uC778 \uC2DC\uC810\uC5D0 'npx lee-spec-kit pre-pr-review <feature> --evidence review-trace.json --decision approve' \uB97C \uC2E4\uD589\uD558\uC138\uC694.`;
3353
3360
  }
3354
3361
  return `Conduct a pre-PR code review.
3355
3362
  1. Run automated analyzers (e.g., vitest, biome check, pnpm audit).
@@ -3359,7 +3366,8 @@ function getPrePrReviewPrompt(lang, skills, fallbackText) {
3359
3366
  3. Generate a 'review-trace.json' file for all changed files, including evaluations for risk, security, perf, maintainability, and specific fileLine locators. Also include residualRisks and commandsExecuted array.
3360
3367
  4. The baseline is '${fallbackText}'. Always perform the 'Pre-PR Core Checklist' section of the 'create-pr' document.
3361
3368
  5. Priority skills: ${skills.length > 0 ? skills.join(", ") : "None"} for deeper technical review.
3362
- After completion, execute 'pnpm lee pre-pr-review <feature> --evidence review-trace.json --decision approve'.`;
3369
+ 6. If unresolved findings remain, first record them with 'npx lee-spec-kit pre-pr-review <feature> --evidence review-trace.json --decision changes_requested' and apply code fixes.
3370
+ 7. After fixes and re-validation, run 'npx lee-spec-kit pre-pr-review <feature> --evidence review-trace.json --decision approve' for final pre-PR approval.`;
3363
3371
  }
3364
3372
  function getCodeReviewPrompt(lang) {
3365
3373
  if (lang === "ko") {
@@ -3384,6 +3392,9 @@ function isPrMetadataConfigured(feature) {
3384
3392
  function isReviewIterationPhase(feature, workflowPolicy) {
3385
3393
  return workflowPolicy.requirePr && workflowPolicy.requireReview && isPrMetadataConfigured(feature) && !!feature.pr.link && feature.pr.status === "Review";
3386
3394
  }
3395
+ function isPrePrFixIterationPhase(feature, workflowPolicy, prePrReviewPolicy) {
3396
+ return prePrReviewPolicy.enabled && workflowPolicy.requirePr && feature.prePrReview.status === "Done" && !!feature.prePrReview.decisionOutcome && feature.prePrReview.decisionOutcome !== "approve" && (!isPrMetadataConfigured(feature) || !feature.pr.link);
3397
+ }
3387
3398
  function isPrePrReviewSatisfied(feature, prePrReviewPolicy) {
3388
3399
  if (!prePrReviewPolicy.enabled) return true;
3389
3400
  if (!feature.docs.prePrReviewFieldExists || feature.prePrReview.status !== "Done") {
@@ -3460,6 +3471,7 @@ function buildPrePrReviewCommandArgs(feature, evidencePath, decision) {
3460
3471
  }
3461
3472
  function resolvePrePrReviewEvidencePath(feature) {
3462
3473
  const docsRoot = feature.git.docsGitCwd;
3474
+ const docsParent = path12.dirname(docsRoot);
3463
3475
  const candidates = [];
3464
3476
  const explicit = (feature.prePrReview.evidence || "").trim();
3465
3477
  if (explicit && explicit !== "-") {
@@ -3467,8 +3479,16 @@ function resolvePrePrReviewEvidencePath(feature) {
3467
3479
  candidates.push(explicit);
3468
3480
  } else {
3469
3481
  candidates.push(path12.resolve(docsRoot, explicit));
3482
+ candidates.push(path12.resolve(docsParent, explicit));
3470
3483
  candidates.push(path12.resolve(process.cwd(), explicit));
3471
3484
  candidates.push(path12.resolve(feature.path, explicit));
3485
+ const normalizedExplicit = explicit.replace(/\\/g, "/");
3486
+ if (normalizedExplicit.startsWith("docs/")) {
3487
+ const withoutDocsPrefix = normalizedExplicit.slice("docs/".length);
3488
+ if (withoutDocsPrefix) {
3489
+ candidates.push(path12.resolve(docsRoot, withoutDocsPrefix));
3490
+ }
3491
+ }
3472
3492
  }
3473
3493
  }
3474
3494
  candidates.push(path12.resolve(process.cwd(), "review-trace.json"));
@@ -3501,6 +3521,17 @@ function resolveProjectCommitTopic(feature) {
3501
3521
  const topic = withoutTaskId || normalizeCommitTopicText(feature.folderName);
3502
3522
  return toShellSafeCommitTopic(topic);
3503
3523
  }
3524
+ function getReviewFixCommitGuidance(feature, lang, options) {
3525
+ const prePr = !!options?.prePr;
3526
+ if (prePr) {
3527
+ return feature.issueNumber ? tr(lang, "messages", "prePrFixCommitIssueGuidance", {
3528
+ issueNumber: feature.issueNumber
3529
+ }) : tr(lang, "messages", "prePrFixCommitGuidance");
3530
+ }
3531
+ return feature.issueNumber ? tr(lang, "messages", "reviewFixCommitIssueGuidance", {
3532
+ issueNumber: feature.issueNumber
3533
+ }) : tr(lang, "messages", "reviewFixCommitGuidance");
3534
+ }
3504
3535
  function resolveManagedWorktreeCleanupPaths(projectGitCwd) {
3505
3536
  if (!projectGitCwd) return null;
3506
3537
  const normalized = path12.resolve(projectGitCwd);
@@ -4013,7 +4044,16 @@ function getStepDefinitions(ctx) {
4013
4044
  ];
4014
4045
  }
4015
4046
  if (f.git.projectHasUncommittedChanges) {
4016
- if (isReviewIterationPhase(f, workflowPolicy)) {
4047
+ const reviewIterationPhase = isReviewIterationPhase(
4048
+ f,
4049
+ workflowPolicy
4050
+ );
4051
+ const prePrFixIterationPhase = isPrePrFixIterationPhase(
4052
+ f,
4053
+ workflowPolicy,
4054
+ prePrReviewPolicy
4055
+ );
4056
+ if (reviewIterationPhase || prePrFixIterationPhase) {
4017
4057
  if (!f.git.projectGitCwd) {
4018
4058
  return [
4019
4059
  {
@@ -4032,11 +4072,8 @@ function getStepDefinitions(ctx) {
4032
4072
  type: "instruction",
4033
4073
  category: "review_fix_commit",
4034
4074
  requiresUserCheck: true,
4035
- message: f.issueNumber ? tr(lang, "messages", "reviewFixCommitIssueGuidance", {
4036
- projectGitCwd: f.git.projectGitCwd,
4037
- issueNumber: f.issueNumber
4038
- }) : tr(lang, "messages", "reviewFixCommitGuidance", {
4039
- projectGitCwd: f.git.projectGitCwd
4075
+ message: getReviewFixCommitGuidance(f, lang, {
4076
+ prePr: prePrFixIterationPhase
4040
4077
  })
4041
4078
  }
4042
4079
  ];
@@ -4262,7 +4299,16 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
4262
4299
  }
4263
4300
  ];
4264
4301
  }
4265
- if (isReviewIterationPhase(f, workflowPolicy)) {
4302
+ const reviewIterationPhase = isReviewIterationPhase(
4303
+ f,
4304
+ workflowPolicy
4305
+ );
4306
+ const prePrFixIterationPhase = isPrePrFixIterationPhase(
4307
+ f,
4308
+ workflowPolicy,
4309
+ prePrReviewPolicy
4310
+ );
4311
+ if (reviewIterationPhase || prePrFixIterationPhase) {
4266
4312
  if (!f.git.projectGitCwd) {
4267
4313
  return [
4268
4314
  {
@@ -4277,11 +4323,8 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
4277
4323
  type: "instruction",
4278
4324
  category: "review_fix_commit",
4279
4325
  requiresUserCheck: true,
4280
- message: f.issueNumber ? tr(lang, "messages", "reviewFixCommitIssueGuidance", {
4281
- projectGitCwd: f.git.projectGitCwd,
4282
- issueNumber: f.issueNumber
4283
- }) : tr(lang, "messages", "reviewFixCommitGuidance", {
4284
- projectGitCwd: f.git.projectGitCwd
4326
+ message: getReviewFixCommitGuidance(f, lang, {
4327
+ prePr: prePrFixIterationPhase
4285
4328
  })
4286
4329
  }
4287
4330
  ];
@@ -4338,33 +4381,41 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
4338
4381
  }
4339
4382
  ];
4340
4383
  }
4341
- const evidencePath = resolvePrePrReviewEvidencePath(f);
4342
- if (!evidencePath) {
4384
+ if (f.prePrReview.decisionOutcome && f.prePrReview.decisionOutcome !== "approve") {
4385
+ const rerunEvidencePath = resolvePrePrReviewEvidencePath(f) || "review-trace.json";
4386
+ const rerunCommand = buildSelfCliCommand(
4387
+ buildPrePrReviewCommandArgs(f, rerunEvidencePath, "approve")
4388
+ );
4343
4389
  return [
4344
4390
  {
4345
4391
  type: "instruction",
4346
- category: "pre_pr_review",
4392
+ category: "review_fix_commit",
4347
4393
  requiresUserCheck: true,
4348
- message: getPrePrReviewPrompt(
4349
- lang,
4350
- prePrReviewPolicy.skills,
4351
- prePrReviewPolicy.fallback
4352
- )
4394
+ message: `${tr(lang, "messages", "prePrReviewFixRequired", {
4395
+ decision: f.prePrReview.decisionOutcome
4396
+ })}
4397
+ ${getReviewFixCommitGuidance(f, lang, {
4398
+ prePr: true
4399
+ })}
4400
+ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
4401
+ decision: f.prePrReview.decisionOutcome,
4402
+ command: rerunCommand
4403
+ })}`
4353
4404
  }
4354
4405
  ];
4355
4406
  }
4356
- if (f.prePrReview.decisionOutcome && f.prePrReview.decisionOutcome !== "approve") {
4407
+ const evidencePath = resolvePrePrReviewEvidencePath(f);
4408
+ if (!evidencePath) {
4357
4409
  return [
4358
4410
  {
4359
4411
  type: "instruction",
4360
4412
  category: "pre_pr_review",
4361
4413
  requiresUserCheck: true,
4362
- message: tr(lang, "messages", "prePrReviewDecisionReconfirm", {
4363
- decision: f.prePrReview.decisionOutcome,
4364
- command: buildSelfCliCommand(
4365
- buildPrePrReviewCommandArgs(f, evidencePath, "approve")
4366
- )
4367
- })
4414
+ message: getPrePrReviewPrompt(
4415
+ lang,
4416
+ prePrReviewPolicy.skills,
4417
+ prePrReviewPolicy.fallback
4418
+ )
4368
4419
  }
4369
4420
  ];
4370
4421
  }
@@ -5317,11 +5368,14 @@ function hasPrePrReviewLogQuality(content, options) {
5317
5368
  if (!hasValidReviewLogEntries(decisionEntries)) continue;
5318
5369
  const findingsEntries = collectStructuredReviewEntries(section, [
5319
5370
  "Findings",
5371
+ "Findings (Changed Files)",
5320
5372
  "\uC9C0\uC801\uC0AC\uD56D",
5321
- "\uC9C0\uC801 \uC0AC\uD56D"
5373
+ "\uC9C0\uC801 \uC0AC\uD56D",
5374
+ "\uC9C0\uC801\uC0AC\uD56D (\uBCC0\uACBD \uD30C\uC77C)",
5375
+ "\uC9C0\uC801 \uC0AC\uD56D (\uBCC0\uACBD \uD30C\uC77C)"
5322
5376
  ]);
5323
5377
  const hasActionableFindings = findingsEntries.map((entry) => entry.trim()).some(
5324
- (entry) => entry.length > 0 && !isReviewDraftPlaceholder(entry) && !isPlaceholderReviewEvidence(entry) && /\S+:\d+/.test(entry)
5378
+ (entry) => entry.length > 0 && !isReviewDraftPlaceholder(entry) && !isPlaceholderReviewEvidence(entry) && (/\S+:\d+/.test(entry) || /\b[Ll]ines?\s+\d+/.test(entry))
5325
5379
  );
5326
5380
  const hasExplicitZeroFindings = findingsEntries.some(
5327
5381
  (entry) => isExplicitZeroFindingsEntry(entry)
@@ -5340,14 +5394,15 @@ function hasPrePrReviewLogQuality(content, options) {
5340
5394
  "\uC2E4\uD589 \uD14C\uC2A4\uD2B8",
5341
5395
  "\uD14C\uC2A4\uD2B8 \uC2E4\uD589"
5342
5396
  ]);
5343
- if (!hasValidReviewLogEntries(testsRunEntries)) continue;
5397
+ const commandsEntries = collectStructuredReviewEntries(section, [
5398
+ "Commands Executed",
5399
+ "Executed Commands",
5400
+ "\uC2E4\uD589 \uBA85\uB839\uC5B4",
5401
+ "\uC2E4\uD589 \uBA85\uB839"
5402
+ ]);
5403
+ const hasTestsRunEntries = hasValidReviewLogEntries(testsRunEntries) || hasValidReviewLogEntries(commandsEntries);
5404
+ if (!hasTestsRunEntries) continue;
5344
5405
  if (requireCommandsExecuted) {
5345
- const commandsEntries = collectStructuredReviewEntries(section, [
5346
- "Commands Executed",
5347
- "Executed Commands",
5348
- "\uC2E4\uD589 \uBA85\uB839\uC5B4",
5349
- "\uC2E4\uD589 \uBA85\uB839"
5350
- ]);
5351
5406
  const hasCommandsExecuted = commandsEntries.map((entry) => entry.trim()).some(
5352
5407
  (entry) => entry.length > 0 && !isReviewDraftPlaceholder(entry) && !isPlaceholderReviewEvidence(entry) && !isNoCommandsPlaceholder(entry)
5353
5408
  );
@@ -5392,6 +5447,13 @@ function resolveLocalEvidencePathCandidates(rawValue, context) {
5392
5447
  candidates.add(path12.resolve(context.featurePath, evidencePath));
5393
5448
  candidates.add(path12.resolve(context.docsDir, evidencePath));
5394
5449
  candidates.add(path12.resolve(path12.dirname(context.docsDir), evidencePath));
5450
+ const normalizedEvidencePath = evidencePath.replace(/\\/g, "/");
5451
+ if (normalizedEvidencePath.startsWith("docs/")) {
5452
+ const withoutDocsPrefix = normalizedEvidencePath.slice("docs/".length);
5453
+ if (withoutDocsPrefix) {
5454
+ candidates.add(path12.resolve(context.docsDir, withoutDocsPrefix));
5455
+ }
5456
+ }
5395
5457
  }
5396
5458
  return [...candidates];
5397
5459
  }
@@ -11976,9 +12038,49 @@ function extractMarkdownByHeadings(content, headings, levels) {
11976
12038
  }
11977
12039
  return lines.slice(start, end).join("\n");
11978
12040
  }
12041
+ function removeMarkdownByHeadings(content, headings, levels) {
12042
+ const targets = new Set(headings.map((heading) => normalizeHeading(heading)));
12043
+ const lines = content.split("\n");
12044
+ const levelSet = new Set(levels);
12045
+ let start = -1;
12046
+ let startLevel = 0;
12047
+ for (let i = 0; i < lines.length; i++) {
12048
+ const match = lines[i].match(/^\s*(#{2,6})\s+(.+?)\s*$/);
12049
+ if (!match) continue;
12050
+ const level = match[1].length;
12051
+ if (!levelSet.has(level)) continue;
12052
+ if (!targets.has(normalizeHeading(match[2]))) continue;
12053
+ start = i;
12054
+ startLevel = level;
12055
+ break;
12056
+ }
12057
+ if (start < 0) return content;
12058
+ let end = lines.length;
12059
+ for (let i = start + 1; i < lines.length; i++) {
12060
+ const heading = lines[i].match(/^\s*(#{2,6})\s+(.+?)\s*$/);
12061
+ if (!heading) continue;
12062
+ const level = heading[1].length;
12063
+ if (level <= startLevel) {
12064
+ end = i;
12065
+ break;
12066
+ }
12067
+ }
12068
+ const next = [...lines.slice(0, start), ...lines.slice(end)].join("\n");
12069
+ const hasTrailingNewline = /\n$/.test(content);
12070
+ const normalized = next.replace(/\n{3,}/g, "\n\n").trimEnd();
12071
+ if (!normalized) return "";
12072
+ return hasTrailingNewline ? `${normalized}
12073
+ ` : normalized;
12074
+ }
11979
12075
  function extractMarkdownSection(content, headings) {
11980
12076
  return extractMarkdownByHeadings(content, headings, [2]);
11981
12077
  }
12078
+ function stripIssueDraftMetadataSection(content) {
12079
+ return stripWorkflowDraftMetadataSection(content);
12080
+ }
12081
+ function stripWorkflowDraftMetadataSection(content) {
12082
+ return removeMarkdownByHeadings(content, ["Metadata", "\uBA54\uD0C0\uB370\uC774\uD130"], [2]);
12083
+ }
11982
12084
  function isTemplateLine(line) {
11983
12085
  const trimmed = line.trim();
11984
12086
  if (!trimmed) return true;
@@ -12985,8 +13087,20 @@ function githubCommand(program2) {
12985
13087
  kindLabel: tg(config.lang, "kindIssue"),
12986
13088
  lang: config.lang
12987
13089
  });
12988
- const body = preparedBody.body;
12989
- const bodyFile = preparedBody.bodyFile;
13090
+ const body = stripIssueDraftMetadataSection(preparedBody.body);
13091
+ let bodyFile = preparedBody.bodyFile;
13092
+ if (options.create && body !== preparedBody.body) {
13093
+ const sanitizedBodyFile = toBodyFilePath(
13094
+ void 0,
13095
+ "issue",
13096
+ config.docsDir,
13097
+ `${feature.type}-issue-sanitized`,
13098
+ config.lang
13099
+ );
13100
+ await fs.ensureDir(path12.dirname(sanitizedBodyFile));
13101
+ await fs.writeFile(sanitizedBodyFile, body, "utf-8");
13102
+ bodyFile = sanitizedBodyFile;
13103
+ }
12990
13104
  const title = options.title?.trim() || (preparedBody.source === "workflow-ready" ? preparedBody.draftMetadata?.title : void 0) || defaultTitle;
12991
13105
  const labels = parseLabels(
12992
13106
  optionLabels || (preparedBody.source === "workflow-ready" ? preparedBody.draftMetadata?.labels : void 0),
@@ -13193,8 +13307,22 @@ function githubCommand(program2) {
13193
13307
  kindLabel: tg(config.lang, "kindPr"),
13194
13308
  lang: config.lang
13195
13309
  });
13196
- let body = preparedBody.body;
13310
+ let body = stripWorkflowDraftMetadataSection(
13311
+ preparedBody.body
13312
+ );
13197
13313
  let bodyFile = preparedBody.bodyFile;
13314
+ if (options.create && body !== preparedBody.body) {
13315
+ const sanitizedBodyFile = toBodyFilePath(
13316
+ void 0,
13317
+ "pr",
13318
+ config.docsDir,
13319
+ `${feature.type}-pr-sanitized`,
13320
+ config.lang
13321
+ );
13322
+ await fs.ensureDir(path12.dirname(sanitizedBodyFile));
13323
+ await fs.writeFile(sanitizedBodyFile, body, "utf-8");
13324
+ bodyFile = sanitizedBodyFile;
13325
+ }
13198
13326
  const title = options.title?.trim() || (preparedBody.source === "workflow-ready" ? preparedBody.draftMetadata?.title : void 0) || defaultTitle;
13199
13327
  const labels = parseLabels(
13200
13328
  optionLabels || (preparedBody.source === "workflow-ready" ? preparedBody.draftMetadata?.labels : void 0),
@@ -14606,7 +14734,24 @@ function getPreferredKeys(lang) {
14606
14734
  }
14607
14735
  function buildReportContent(input) {
14608
14736
  const skills = input.skills.length > 0 ? input.skills.join(", ") : "code-review-excellence";
14609
- const commandsRun = input.evidence.commandsExecuted.length > 0 ? input.evidence.commandsExecuted.map((c) => ` - \`${c}\``).join("\n") : " - None specified";
14737
+ const normalizedCommands = input.evidence.commandsExecuted.map((entry) => normalizeShellLikeCommand(entry)).filter(Boolean);
14738
+ const commandsRun = normalizedCommands.length > 0 ? normalizedCommands.map((c) => ` - \`${c}\``).join("\n") : " - None specified";
14739
+ const testCommandTokens = [
14740
+ "test",
14741
+ "vitest",
14742
+ "jest",
14743
+ "playwright",
14744
+ "cypress",
14745
+ "tsc",
14746
+ "biome check",
14747
+ "eslint",
14748
+ "audit"
14749
+ ];
14750
+ const testsRunCommands = normalizedCommands.filter((entry) => {
14751
+ const lowered = entry.toLowerCase();
14752
+ return testCommandTokens.some((token) => lowered.includes(token));
14753
+ });
14754
+ const testsRun = (testsRunCommands.length > 0 ? testsRunCommands : normalizedCommands).map((entry) => ` - \`${entry}\` -> executed`).join("\n");
14610
14755
  let filesSection = "";
14611
14756
  if (input.evidence.files.length === 0) {
14612
14757
  filesSection = " - 0 findings";
@@ -14631,6 +14776,9 @@ function buildReportContent(input) {
14631
14776
  - **Commands Executed**:
14632
14777
  ${commandsRun}
14633
14778
 
14779
+ - **Tests Run**:
14780
+ ${testsRun || " - Not recorded"}
14781
+
14634
14782
  - **Residual Risks**:
14635
14783
  - ${input.evidence.residualRisks}
14636
14784
 
@@ -14643,7 +14791,7 @@ ${mainScopeFiles}
14643
14791
  - **Worktree Changed Files**:
14644
14792
  ${worktreeScopeFiles}
14645
14793
 
14646
- - **Findings (Changed Files)**:
14794
+ - **Findings**:
14647
14795
  ${filesSection}
14648
14796
 
14649
14797
  - **Trace**: pre-pr-review command executed and synced with tasks.md