lee-spec-kit 0.6.14 → 0.6.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +2 -1
- package/README.md +2 -1
- package/dist/index.js +510 -288
- package/package.json +1 -1
- package/templates/en/common/agents/skills/create-issue.md +2 -3
- package/templates/en/common/agents/skills/create-pr.md +16 -2
- package/templates/en/common/features/README.md +11 -1
- package/templates/en/common/features/feature-base/issue.md +0 -1
- package/templates/en/common/features/feature-base/pr.md +0 -3
- package/templates/en/common/features/feature-base/spec.md +0 -1
- package/templates/en/common/features/feature-base/tasks.md +5 -0
- package/templates/ko/common/agents/skills/create-issue.md +2 -3
- package/templates/ko/common/agents/skills/create-pr.md +16 -2
- package/templates/ko/common/features/README.md +11 -1
- package/templates/ko/common/features/feature-base/issue.md +0 -1
- package/templates/ko/common/features/feature-base/pr.md +0 -3
- package/templates/ko/common/features/feature-base/spec.md +0 -1
- package/templates/ko/common/features/feature-base/tasks.md +5 -0
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import path19 from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { program } from 'commander';
|
|
5
5
|
import fs15 from 'fs-extra';
|
|
@@ -11,7 +11,7 @@ import os from 'os';
|
|
|
11
11
|
import { createHash, randomUUID } from 'crypto';
|
|
12
12
|
|
|
13
13
|
var getFilename = () => fileURLToPath(import.meta.url);
|
|
14
|
-
var getDirname = () =>
|
|
14
|
+
var getDirname = () => path19.dirname(getFilename());
|
|
15
15
|
var __dirname$1 = /* @__PURE__ */ getDirname();
|
|
16
16
|
async function copyTemplates(src, dest) {
|
|
17
17
|
await fs15.copy(src, dest, {
|
|
@@ -42,10 +42,10 @@ async function replaceInFiles(dir, replacements) {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
45
|
-
var __dirname2 =
|
|
45
|
+
var __dirname2 = path19.dirname(__filename2);
|
|
46
46
|
function getTemplatesDir() {
|
|
47
|
-
const rootDir =
|
|
48
|
-
return
|
|
47
|
+
const rootDir = path19.resolve(__dirname2, "..");
|
|
48
|
+
return path19.join(rootDir, "templates");
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// src/utils/i18n.ts
|
|
@@ -131,13 +131,13 @@ var I18N = {
|
|
|
131
131
|
"context.tipShowAll": "\uC804\uCCB4 \uBCF4\uAE30",
|
|
132
132
|
"context.tipShowDone": "\uC644\uB8CC\uB9CC \uBCF4\uAE30",
|
|
133
133
|
"context.checkRequired": "[\uD655\uC778 \uD544\uC694] ",
|
|
134
|
-
"context.checkPolicyHint": "\u2139\uFE0F \uC0AC\uC6A9\uC790 \uD655\uC778 \uC815\uCC45\uC740 \uC138\uC158 \uC2DC\uC791(\uB610\uB294 context \uC555\uCD95/\uB9AC\uC14B \uC9C1\uD6C4)\uC5D0 1\uD68C \uD655\uC778\uD558\uACE0, \uC774\uD6C4\uC5D0\uB294 \uC815\uCC45/\uC124\uC815 \uBCC0\uACBD \uB610\uB294 \uC0AC\uC6A9\uC790 \uC0C8\uB85C\uACE0\uCE68 \uC694\uCCAD \uC2DC\uC5D0\uB9CC \uC7AC\uD655\uC778\uD558\uC138\uC694. (git push/merge/merge commit \uD3EC\uD568) [\uD655\uC778 \uD544\uC694]\uAC00 \uC788\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C
|
|
134
|
+
"context.checkPolicyHint": "\u2139\uFE0F \uC0AC\uC6A9\uC790 \uD655\uC778 \uC815\uCC45\uC740 \uC138\uC158 \uC2DC\uC791(\uB610\uB294 context \uC555\uCD95/\uB9AC\uC14B \uC9C1\uD6C4)\uC5D0 1\uD68C \uD655\uC778\uD558\uACE0, \uC774\uD6C4\uC5D0\uB294 \uC815\uCC45/\uC124\uC815 \uBCC0\uACBD \uB610\uB294 \uC0AC\uC6A9\uC790 \uC0C8\uB85C\uACE0\uCE68 \uC694\uCCAD \uC2DC\uC5D0\uB9CC \uC7AC\uD655\uC778\uD558\uC138\uC694. (git push/merge/merge commit \uD3EC\uD568) [\uD655\uC778 \uD544\uC694]\uAC00 \uC788\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uB77C\uBCA8 \uD1A0\uD070\uC774 \uD3EC\uD568\uB41C \uC751\uB2F5(\uC608: `A`, `A OK`, `A \uC9C4\uD589\uD574`)\uC744 \uBC1B\uC740 \uB4A4 \uC9C4\uD589 (config: approval\uB85C \uC870\uC815 \uAC00\uB2A5)",
|
|
135
135
|
"context.actionOptionHint": "\uC2B9\uC778 \uC751\uB2F5 \uD615\uC2DD: \uB77C\uBCA8 \uD1A0\uD070 \uD3EC\uD568 (\uC608: `A`, `A OK`, `A \uC9C4\uD589\uD574`)",
|
|
136
136
|
"context.actionExplainHint": "\uC2B9\uC778 \uC694\uCCAD \uC804, \uAC01 \uB77C\uBCA8\uC774 \uBB34\uC5C7\uC744 \uC2E4\uD589/\uBCC0\uACBD\uD558\uB294\uC9C0 \uD55C \uC904 \uC694\uC57D\uACFC \uD568\uAED8 \uC124\uBA85\uD558\uC138\uC694.",
|
|
137
|
-
"context.finalLabelPrompt": "\uD604\uC7AC \uC120\uD0DD \uAC00\uB2A5\uD55C \uB77C\uBCA8: {labels}. \uC751\uB2F5\uC740
|
|
137
|
+
"context.finalLabelPrompt": "\uD604\uC7AC \uC120\uD0DD \uAC00\uB2A5\uD55C \uB77C\uBCA8: {labels}. \uC751\uB2F5\uC740 \uB77C\uBCA8 \uD1A0\uD070 \uD3EC\uD568 \uD615\uC2DD\uC73C\uB85C \uD574\uC8FC\uC138\uC694. (\uC608: {example}, `A \uC9C4\uD589\uD574`)",
|
|
138
138
|
"context.suggestionHeader": "\uCD94\uCC9C \uB2E4\uC74C \uC120\uD0DD\uC9C0",
|
|
139
139
|
"context.suggestionCommandHint": "\uB77C\uBCA8 \uCC38\uACE0 \uBA85\uB839: {command}",
|
|
140
|
-
"context.suggestionFinalPrompt": "\uD604\uC7AC \uCD94\uCC9C \uB77C\uBCA8: {labels}. \uC751\uB2F5\uC740
|
|
140
|
+
"context.suggestionFinalPrompt": "\uD604\uC7AC \uCD94\uCC9C \uB77C\uBCA8: {labels}. \uC751\uB2F5\uC740 \uB77C\uBCA8 \uD1A0\uD070 \uD3EC\uD568 \uD615\uC2DD\uC73C\uB85C \uD574\uC8FC\uC138\uC694. (\uC608: {example}, `A \uC9C4\uD589\uD574`)",
|
|
141
141
|
"context.suggestion.createFeature": "\uC0C8 Feature\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4",
|
|
142
142
|
"context.suggestion.showDone": "\uC644\uB8CC\uB41C Feature \uBAA9\uB85D\uC744 \uD655\uC778\uD569\uB2C8\uB2E4",
|
|
143
143
|
"context.suggestion.showAll": "\uC804\uCCB4 Feature \uBAA9\uB85D\uC744 \uD655\uC778\uD569\uB2C8\uB2E4",
|
|
@@ -156,6 +156,8 @@ var I18N = {
|
|
|
156
156
|
"context.list.completePrePrReview": "Pre-PR \uB9AC\uBDF0 \uC644\uB8CC \uCC98\uB9AC",
|
|
157
157
|
"context.list.addPrePrFindings": "Pre-PR Findings \uD544\uB4DC/\uAC12 \uBCF4\uC644",
|
|
158
158
|
"context.list.addPrePrEvidence": "Pre-PR Evidence \uADFC\uAC70 \uCD94\uAC00",
|
|
159
|
+
"context.list.addPrReviewFindings": "PR \uB9AC\uBDF0 Findings \uD544\uB4DC/\uAC12 \uBCF4\uC644",
|
|
160
|
+
"context.list.addPrReviewEvidence": "PR \uB9AC\uBDF0 Evidence \uADFC\uAC70 \uCD94\uAC00",
|
|
159
161
|
"context.list.resolvePrePrMajorFindings": "Pre-PR \uC8FC\uC694 Findings \uD574\uC18C \uD544\uC694 ({count}\uAC74)",
|
|
160
162
|
"context.list.resolvePrePrMinorFindings": "Pre-PR minor Findings \uD574\uC18C \uD544\uC694 ({count}\uAC74)",
|
|
161
163
|
"context.list.setPrStatus": "PR \uC0C1\uD0DC \uC124\uC815",
|
|
@@ -419,9 +421,9 @@ var I18N = {
|
|
|
419
421
|
tasksImprove: "tasks.md\uB97C \uBCF4\uC644\uD558\uACE0 \uBB38\uC11C \uC0C1\uD0DC\uB97C Review\uB85C \uBCC0\uACBD\uD558\uC138\uC694.",
|
|
420
422
|
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)",
|
|
421
423
|
docsCommitPlanning: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(planning): {folderName} \uAE30\uD68D \uBB38\uC11C"',
|
|
422
|
-
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
|
|
424
|
+
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.",
|
|
423
425
|
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.",
|
|
424
|
-
issueCreateFromDoc: "`issue.md` \uC0C1\uD0DC\uAC00 `Ready`\uC774\uBA74 GitHub Issue\uB97C \uC0DD\uC131\uD558\uACE0, \uC0DD\uC131\uB41C \uC774\uC288 \uBC88\uD638\uB97C `
|
|
426
|
+
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.",
|
|
425
427
|
docsCommitIssueUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(#{issueNumber}): {folderName} \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8"',
|
|
426
428
|
docsCommitUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs: {folderName} \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8"',
|
|
427
429
|
projectCommitIssueUpdate: '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(#{issueNumber}): {commitTopic}")',
|
|
@@ -437,30 +439,40 @@ var I18N = {
|
|
|
437
439
|
checkTaskStatuses: "\uD0DC\uC2A4\uD06C \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694. ({done}/{total})",
|
|
438
440
|
taskCommitGateStrictBlock: "\uB2E4\uC74C TODO \uD0DC\uC2A4\uD06C\uB85C \uB118\uC5B4\uAC00\uAE30 \uC804\uC5D0 `1 \uD0DC\uC2A4\uD06C = 1 \uCEE4\uBC0B` \uADDC\uCE59\uC744 \uCDA9\uC871\uD574\uC57C \uD569\uB2C8\uB2E4. \uC810\uAC80 \uACB0\uACFC: {reason}. \uD0DC\uC2A4\uD06C \uCEE4\uBC0B \uB2E8\uC704\uB97C \uC815\uB9AC\uD55C \uB4A4 \uB2E4\uC2DC \uC9C4\uD589\uD558\uC138\uC694.",
|
|
439
441
|
taskCommitGateWarnProceed: "\u26A0\uFE0F \uD0DC\uC2A4\uD06C \uCEE4\uBC0B \uB2E8\uC704 \uC810\uAC80 \uACBD\uACE0: {reason}. \uD604\uC7AC\uB294 \uC9C4\uD589 \uAC00\uB2A5\uD558\uC9C0\uB9CC `1 \uD0DC\uC2A4\uD06C = 1 \uCEE4\uBC0B`\uC744 \uAD8C\uC7A5\uD569\uB2C8\uB2E4.",
|
|
440
|
-
taskCommitGateReasonNoTasksCommit: "\uCD5C\uADFC
|
|
441
|
-
taskCommitGateReasonTasksFileUnavailable: "\uCD5C\uADFC \
|
|
442
|
-
taskCommitGateReasonDoneCount: "\uCD5C\uADFC
|
|
443
|
-
taskCommitGateReasonMismatchLastDone: "\uCD5C\uADFC
|
|
442
|
+
taskCommitGateReasonNoTasksCommit: "\uCD5C\uADFC \uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uCEE4\uBC0B\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
|
|
443
|
+
taskCommitGateReasonTasksFileUnavailable: "\uCD5C\uADFC \uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uCEE4\uBC0B \uC774\uB825\uC744 \uD310\uB3C5\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
|
|
444
|
+
taskCommitGateReasonDoneCount: "\uCD5C\uADFC \uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uCEE4\uBC0B \uC810\uAC80 \uACB0\uACFC\uAC00 \uC608\uC0C1\uACFC \uB2E4\uB985\uB2C8\uB2E4 ({count})",
|
|
445
|
+
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",
|
|
444
446
|
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)",
|
|
445
447
|
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)",
|
|
446
448
|
prePrReviewFindingsMissing: "tasks.md\uC5D0 `PR \uC804 \uB9AC\uBDF0 Findings` \uD544\uB4DC\uAC00 \uC5C6\uAC70\uB098 \uAC12 \uD615\uC2DD\uC774 \uC798\uBABB\uB418\uC5C8\uC2B5\uB2C8\uB2E4. `- **PR \uC804 \uB9AC\uBDF0 Findings**: major=0, minor=0` \uD615\uC2DD\uC73C\uB85C \uAE30\uB85D\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
|
|
447
449
|
prePrReviewEvidenceMissing: "tasks.md\uC758 `PR \uC804 \uB9AC\uBDF0 Evidence`\uAC00 \uBE44\uC5B4\uC788\uAC70\uB098 placeholder\uC785\uB2C8\uB2E4. \uB9AC\uBDF0 \uADFC\uAC70(\uBB38\uC11C \uACBD\uB85C/\uB9C1\uD06C/\uB85C\uADF8)\uB97C \uCC44\uC6B0\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
|
|
448
450
|
prePrReviewMajorBlocked: "Pre-PR \uC8FC\uC694 Findings\uAC00 {count}\uAC74\uC73C\uB85C \uAE30\uB85D\uB418\uC5C8\uC2B5\uB2C8\uB2E4. `blockOnFindings=true` \uC815\uCC45\uC5D0\uC11C\uB294 \uC8FC\uC694 \uC774\uC288 \uD574\uACB0/\uD569\uC758 \uC804 PR \uC0DD\uC131\uC73C\uB85C \uC9C4\uD589\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
449
451
|
prePrReviewMinorBlocked: "Pre-PR minor Findings\uAC00 {count}\uAC74\uC73C\uB85C \uAE30\uB85D\uB418\uC5C8\uC2B5\uB2C8\uB2E4. `minorPolicy=block` \uC815\uCC45\uC5D0\uC11C\uB294 minor \uC774\uC288 \uC815\uB9AC/\uD569\uC758 \uC804 PR \uC0DD\uC131\uC73C\uB85C \uC9C4\uD589\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
450
|
-
prePrReviewRun: "PR \uC0DD\uC131 \uC804 \uC0AC\uC804 \uCF54\uB4DC\uB9AC\uBDF0\uB97C \uC9C4\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)
|
|
452
|
+
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. Major \uC815\uCC45: {findingsPolicy}. Minor \uC815\uCC45: {minorFindingsPolicy}",
|
|
451
453
|
prePrReviewFindingsBlock: "\uC911\uC694 \uC774\uC288\uB294 \uC218\uC815/\uD569\uC758 \uD6C4\uC5D0\uB9CC PR \uC0DD\uC131",
|
|
452
454
|
prePrReviewFindingsWarn: "\uB9AC\uC2A4\uD06C\uB97C \uACF5\uC720\uD558\uBA74 PR \uC0DD\uC131 \uC9C4\uD589 \uAC00\uB2A5",
|
|
453
455
|
prePrReviewMinorFindingsBlock: "minor \uC774\uC288\uB3C4 \uC815\uB9AC/\uD569\uC758 \uD6C4\uC5D0\uB9CC PR \uC0DD\uC131",
|
|
454
456
|
prePrReviewMinorFindingsWarn: "minor \uC774\uC288\uB294 \uAE30\uB85D/\uACF5\uC720 \uD6C4 PR \uC0DD\uC131 \uC9C4\uD589 \uAC00\uB2A5",
|
|
457
|
+
prReviewFindingsFieldMissing: "tasks.md\uC5D0 `PR \uB9AC\uBDF0 Findings` \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. `- **PR \uB9AC\uBDF0 Findings**: major=0, minor=0` \uD56D\uBAA9\uC744 \uCD94\uAC00\uD558\uACE0 \uB2E4\uC2DC \uC9C4\uD589\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
|
|
458
|
+
prReviewFindingsMissing: "tasks.md\uC758 `PR \uB9AC\uBDF0 Findings` \uAC12\uC774 \uC5C6\uAC70\uB098 \uD615\uC2DD\uC774 \uC798\uBABB\uB418\uC5C8\uC2B5\uB2C8\uB2E4. `- **PR \uB9AC\uBDF0 Findings**: major=0, minor=0` \uD615\uC2DD\uC73C\uB85C \uAE30\uB85D\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
|
|
459
|
+
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)",
|
|
460
|
+
prReviewEvidenceMissing: "tasks.md\uC758 `PR \uB9AC\uBDF0 Evidence`\uAC00 \uBE44\uC5B4\uC788\uAC70\uB098 placeholder\uC785\uB2C8\uB2E4. \uC2E4\uC81C\uB85C \uD574\uACB0/\uD569\uC758\uD55C \uB9AC\uBDF0 \uCF54\uBA58\uD2B8 \uADFC\uAC70(\uB9C1\uD06C/\uB85C\uADF8/\uBB38\uC11C)\uB97C \uAE30\uB85D\uD558\uC138\uC694. (\uD655\uC778 \uD544\uC694)",
|
|
455
461
|
prCreate: "PR \uBCF8\uBB38 \uD15C\uD50C\uB9BF\uC744 \uC0DD\uC131\uD574 \uBCC0\uACBD \uC0AC\uD56D/\uD14C\uC2A4\uD2B8 \uC139\uC158\uC744 \uAC80\uD1A0\xB7\uBCF4\uC644\uD558\uACE0, \uC0AC\uC6A9\uC790 \uC2B9\uC778(OK) \uD6C4 PR\uC744 \uC0DD\uC131\uD558\uC138\uC694. \uC774\uD6C4 tasks.md\uC5D0 PR \uB9C1\uD06C\uB97C \uAE30\uB85D\uD558\uC138\uC694.",
|
|
456
462
|
prCreatePrepareFromDoc: "`pr.md`\uB97C \uAE30\uC900\uC73C\uB85C PR \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.",
|
|
457
|
-
prCreateExecuteFromDoc: "`pr.md` \uC0C1\uD0DC\uAC00 `Ready`\uC774\uBA74 PR\uC744 \uC0DD\uC131\uD558\uACE0, \uC0DD\uC131\uB41C PR \uB9C1\uD06C/PR \uC0C1\uD0DC\uB97C `
|
|
463
|
+
prCreateExecuteFromDoc: "`pr.md` \uC0C1\uD0DC\uAC00 `Ready`\uC774\uBA74 PR\uC744 \uC0DD\uC131\uD558\uACE0, \uC0DD\uC131\uB41C PR \uB9C1\uD06C/PR \uC0C1\uD0DC\uB97C `tasks.md`\uC5D0 \uAE30\uB85D\uD558\uC138\uC694. (`pr.md`\uB294 \uC0C1\uD0DC `Ready`\uB9CC \uC720\uC9C0)",
|
|
458
464
|
prCreatePrepare: "PR \uBCF8\uBB38 \uD15C\uD50C\uB9BF\uC744 \uC0DD\uC131\uD574 \uBCC0\uACBD \uC0AC\uD56D/\uD14C\uC2A4\uD2B8 \uC139\uC158\uC744 \uAC80\uD1A0\xB7\uBCF4\uC644\uD558\uACE0, PR \uC0DD\uC131 \uC804 \uC0AC\uC6A9\uC790 \uC2B9\uC778(OK)\uC744 \uBC1B\uC73C\uC138\uC694.",
|
|
459
465
|
prCreateExecute: "\uD655\uC815\uB41C PR \uBCF8\uBB38\uC73C\uB85C PR\uC744 \uC0DD\uC131\uD558\uACE0, \uC0DD\uC131\uB41C PR \uB9C1\uD06C\uB97C tasks.md\uC758 PR \uD544\uB4DC\uC5D0 \uAE30\uB85D\uD558\uC138\uC694.",
|
|
460
466
|
prCreateRequiredSequence: "PR \uC0DD\uC131\uC740 \uD544\uC218 2\uB2E8\uACC4\uC785\uB2C8\uB2E4: (1) PR \uBCF8\uBB38 \uD15C\uD50C\uB9BF \uC0DD\uC131/\uBCF4\uC644 + \uC0AC\uC6A9\uC790 \uC2B9\uC778(OK), (2) PR \uC0DD\uC131 + tasks.md PR \uB9C1\uD06C \uAE30\uB85D. \uC704 \uC21C\uC11C\uB97C \uBAA8\uB450 \uC644\uB8CC\uD558\uC138\uC694.",
|
|
461
467
|
prFillStatus: "tasks.md\uC758 PR \uC0C1\uD0DC\uB97C Review\uB85C \uC124\uC815\uD558\uC138\uC694. (PR \uC0DD\uC131/\uB9AC\uBDF0 \uB2E8\uACC4\uC5D0\uC11C\uB294 Review\uB97C \uC720\uC9C0\uD569\uB2C8\uB2E4.)",
|
|
462
468
|
prResolveReview: "\uB9AC\uBDF0 \uCF54\uBA58\uD2B8\uB97C \uD574\uACB0\uD558\uB294 \uB3D9\uC548 PR \uC0C1\uD0DC\uB294 Review\uB85C \uC720\uC9C0\uD558\uC138\uC694. \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B \uBA54\uC2DC\uC9C0\uB294 \uD0DC\uC2A4\uD06C\uBA85\uC774 \uC544\uB2C8\uB77C \uC2E4\uC81C\uB85C \uD574\uACB0\uD55C \uB9AC\uBDF0 \uC9C0\uC801\uC0AC\uD56D \uC694\uC57D\uC73C\uB85C \uC791\uC131\uD558\uC138\uC694. \uBA38\uC9C0 \uC900\uBE44\uAC00 \uB418\uBA74 \uC0AC\uC6A9\uC790 \uC2B9\uC778(OK) \uD6C4 `npx lee-spec-kit github pr {featureRef} --merge --confirm OK`\uB97C \uC2E4\uD589\uD558\uC138\uC694. (\uC131\uACF5 \uC2DC PR \uC0C1\uD0DC\uAC00 Approved\uB85C \uB3D9\uAE30\uD654\uB429\uB2C8\uB2E4.)",
|
|
463
|
-
prReviewResolve: "\uB9AC\uBDF0 \uCF54\uBA58\uD2B8\uB97C \
|
|
469
|
+
prReviewResolve: "\uB9AC\uBDF0 \uCF54\uBA58\uD2B8\uB97C \uBA3C\uC800 \uD655\uC778/\uBD84\uC11D\uD55C \uB4A4 \uD544\uC694\uD55C \uC218\uC815 \uC791\uC5C5\uC744 \uC9C4\uD589\uD558\uC138\uC694. \uC9C4\uD589 \uC911\uC5D0\uB294 PR \uC0C1\uD0DC\uB97C Review\uB85C \uC720\uC9C0\uD558\uC138\uC694. \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B \uBA54\uC2DC\uC9C0\uB294 \uD0DC\uC2A4\uD06C\uBA85\uC774 \uC544\uB2C8\uB77C \uC2E4\uC81C\uB85C \uD574\uACB0\uD55C \uB9AC\uBDF0 \uC9C0\uC801\uC0AC\uD56D \uC694\uC57D\uC73C\uB85C \uC791\uC131\uD558\uC138\uC694. `PR \uB9AC\uBDF0 Findings/Evidence`\uB97C \uCD5C\uC2E0\uC73C\uB85C \uAE30\uB85D\uD558\uACE0, \uCEE4\uBC0B \uD6C4 \uC6D0\uACA9 \uBC18\uC601(push)\uB3C4 \uC0AC\uC6A9\uC790 \uC2B9\uC778(OK)\uC744 \uBC1B\uC740 \uB4A4 \uC9C4\uD589\uD558\uC138\uC694.",
|
|
470
|
+
prReviewPush: '\uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B\uC744 \uC6D0\uACA9 PR \uBE0C\uB79C\uCE58\uC5D0 \uBC18\uC601\uD558\uB824\uBA74 \uC0AC\uC6A9\uC790 \uC2B9\uC778(OK) \uD6C4 `cd "{projectGitCwd}" && git push`\uB97C \uC2E4\uD589\uD558\uC138\uC694.',
|
|
471
|
+
prReviewRemoteBlocked: "\uC6D0\uACA9 PR \uC0C1\uD0DC\uB97C \uD655\uC778\uD55C \uACB0\uACFC \uC544\uC9C1 \uBA38\uC9C0 \uC900\uBE44\uAC00 \uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4: {reasons}. \uB9AC\uBDF0 \uCF54\uBA58\uD2B8/\uCCB4\uD06C \uC0C1\uD0DC\uB97C \uC815\uB9AC\uD55C \uB4A4 \uB2E4\uC2DC \uD655\uC778\uD558\uC138\uC694.",
|
|
472
|
+
prReviewRemoteReasonChangesRequested: "\uB9AC\uBDF0 \uC2B9\uC778 \uC0C1\uD0DC\uAC00 \uBCC0\uACBD \uC694\uCCAD \uB610\uB294 \uCD94\uAC00 \uB9AC\uBDF0 \uD544\uC694 \uC0C1\uD0DC\uC785\uB2C8\uB2E4",
|
|
473
|
+
prReviewRemoteReasonChecksFailing: "\uC2E4\uD328\uD55C \uCCB4\uD06C\uAC00 {count}\uAC74 \uC788\uC2B5\uB2C8\uB2E4",
|
|
474
|
+
prReviewRemoteReasonChecksPending: "\uB300\uAE30 \uC911\uC778 \uCCB4\uD06C\uAC00 {count}\uAC74 \uC788\uC2B5\uB2C8\uB2E4",
|
|
475
|
+
prReviewRemoteReasonMergeBlocked: "\uBA38\uC9C0 \uC0C1\uD0DC\uAC00 `{status}`\uB85C \uCC28\uB2E8\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4",
|
|
464
476
|
prReviewMerge: "\uBA38\uC9C0 \uC900\uBE44\uAC00 \uB418\uBA74 \uC0AC\uC6A9\uC790 \uC2B9\uC778(OK) \uD6C4 `npx lee-spec-kit github pr {featureRef} --merge --confirm OK`\uB97C \uC2E4\uD589\uD558\uC138\uC694. (\uC131\uACF5 \uC2DC PR \uC0C1\uD0DC\uAC00 Approved\uB85C \uB3D9\uAE30\uD654\uB429\uB2C8\uB2E4.)",
|
|
465
477
|
prRequestReview: "\uB9AC\uBDF0\uC5B4\uC5D0\uAC8C \uB9AC\uBDF0\uB97C \uC694\uCCAD\uD558\uACE0 PR \uC0C1\uD0DC\uB97C Review\uB85C \uC124\uC815/\uC720\uC9C0\uD558\uC138\uC694.",
|
|
466
478
|
featureDone: "\uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC694\uAD6C\uC0AC\uD56D\uACFC \uBAA8\uB4E0 \uD0DC\uC2A4\uD06C/\uC644\uB8CC \uC870\uAC74\uC774 \uCDA9\uC871\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC774 Feature\uB294 \uC644\uB8CC \uC0C1\uD0DC\uC785\uB2C8\uB2E4.",
|
|
@@ -477,13 +489,20 @@ var I18N = {
|
|
|
477
489
|
legacyTasksPrePrReviewField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR \uC804 \uB9AC\uBDF0` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694. (`- **PR \uC804 \uB9AC\uBDF0**: Pending | Done`)",
|
|
478
490
|
legacyTasksPrePrFindingsField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR \uC804 \uB9AC\uBDF0 Findings` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694. (`- **PR \uC804 \uB9AC\uBDF0 Findings**: major=0, minor=0`)",
|
|
479
491
|
legacyTasksPrePrEvidenceField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR \uC804 \uB9AC\uBDF0 Evidence` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694.",
|
|
492
|
+
legacyTasksPrReviewFindingsField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. \uB9AC\uBDF0 \uB2E8\uACC4 \uC804\uC5D0 `PR \uB9AC\uBDF0 Findings` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694. (`- **PR \uB9AC\uBDF0 Findings**: major=0, minor=0`)",
|
|
493
|
+
legacyTasksPrReviewEvidenceField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. \uB9AC\uBDF0 \uB2E8\uACC4 \uC804\uC5D0 `PR \uB9AC\uBDF0 Evidence` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694.",
|
|
480
494
|
workflowSpecNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC spec.md \uC0C1\uD0DC\uAC00 Approved\uAC00 \uC544\uB2D9\uB2C8\uB2E4. (spec.md\uC758 \uC0C1\uD0DC\uB97C Approved\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.)",
|
|
481
495
|
workflowPlanNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC plan.md \uC0C1\uD0DC\uAC00 Approved\uAC00 \uC544\uB2D9\uB2C8\uB2E4. (plan.md\uC758 \uC0C1\uD0DC\uB97C Approved\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.)",
|
|
482
|
-
workflowIssueMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC \uC774\uC288 \uBC88\uD638\uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4. (
|
|
496
|
+
workflowIssueMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC \uC774\uC288 \uBC88\uD638\uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4. (tasks.md\uC758 \uC774\uC288 \uBC88\uD638\uB97C \uCC44\uC6B0\uC138\uC694.)",
|
|
483
497
|
workflowProjectUncommittedChanges: "\uC644\uB8CC \uC870\uAC74 \uC774\uC804\uC5D0 \uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uBCC0\uACBD\uC0AC\uD56D\uC744 \uCEE4\uBC0B\uD574\uC57C \uD569\uB2C8\uB2E4. (\uD504\uB85C\uC81D\uD2B8 \uC6CC\uD06C\uD2B8\uB9AC \uBBF8\uCEE4\uBC0B \uBCC0\uACBD \uC874\uC7AC)",
|
|
484
498
|
workflowPrLinkMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC PR \uB9C1\uD06C\uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4. (tasks.md\uC758 PR \uD544\uB4DC\uB97C \uCC44\uC6B0\uC138\uC694.)",
|
|
485
499
|
workflowPrStatusMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC PR \uC0C1\uD0DC\uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4. (PR \uC0DD\uC131/\uB9AC\uBDF0 \uB2E8\uACC4\uC5D0\uC11C\uB294 PR \uC0C1\uD0DC\uB97C Review\uB85C \uC124\uC815\uD558\uC138\uC694.)",
|
|
486
500
|
workflowPrStatusNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC PR \uC0C1\uD0DC\uAC00 Approved\uAC00 \uC544\uB2D9\uB2C8\uB2E4. (PR \uC0DD\uC131/\uB9AC\uBDF0 \uB2E8\uACC4\uB294 Review\uB97C \uC720\uC9C0\uD558\uACE0, merge \uC131\uACF5 \uC2DC\uC5D0\uB9CC Approved\uB85C \uB3D9\uAE30\uD654\uD558\uC138\uC694.)",
|
|
501
|
+
workflowPrReviewFindingsMissing: "\uB9AC\uBDF0 \uB2E8\uACC4\uC5D0\uC11C `PR \uB9AC\uBDF0 Findings`\uAC00 \uC5C6\uAC70\uB098 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. (`major=0, minor=0` \uD615\uC2DD)",
|
|
502
|
+
workflowPrReviewEvidenceMissing: "\uB9AC\uBDF0 \uB2E8\uACC4\uC5D0\uC11C `PR \uB9AC\uBDF0 Evidence`\uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4. (\uB9AC\uBDF0 \uCF54\uBA58\uD2B8 \uCC98\uB9AC \uADFC\uAC70\uB97C \uAE30\uB85D\uD558\uC138\uC694.)",
|
|
503
|
+
workflowPrRemoteChangesRequested: "\uC6D0\uACA9 PR\uC5D0\uC11C \uBCC0\uACBD \uC694\uCCAD \uB610\uB294 \uCD94\uAC00 \uB9AC\uBDF0\uAC00 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uCF54\uBA58\uD2B8 \uBC18\uC601 \uD6C4 push\uD558\uACE0 \uB2E4\uC2DC \uD655\uC778\uD558\uC138\uC694.",
|
|
504
|
+
workflowPrRemoteChecksFailing: "\uC6D0\uACA9 PR \uCCB4\uD06C \uC2E4\uD328\uAC00 {count}\uAC74 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC2E4\uD328 \uC6D0\uC778\uC744 \uD574\uACB0 \uD6C4 \uB2E4\uC2DC \uD655\uC778\uD558\uC138\uC694.",
|
|
505
|
+
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.",
|
|
487
506
|
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.)",
|
|
488
507
|
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.)",
|
|
489
508
|
workflowPrePrFindingsMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Findings`\uAC00 \uC5C6\uAC70\uB098 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. (`major=0, minor=0` \uD615\uC2DD)",
|
|
@@ -562,13 +581,13 @@ var I18N = {
|
|
|
562
581
|
"context.tipShowAll": "Show all",
|
|
563
582
|
"context.tipShowDone": "Show done only",
|
|
564
583
|
"context.checkRequired": "[CHECK required] ",
|
|
565
|
-
"context.checkPolicyHint": "\u2139\uFE0F Check user-approval policy once at session start (or right after context compression/reset); re-check only when policy/config changes or the user explicitly requests refresh. (includes git push/merge and merge commits). If you see [CHECK required], wait for
|
|
584
|
+
"context.checkPolicyHint": "\u2139\uFE0F Check user-approval policy once at session start (or right after context compression/reset); re-check only when policy/config changes or the user explicitly requests refresh. (includes git push/merge and merge commits). If you see [CHECK required], wait for a reply that includes a label token (e.g. `A`, `A OK`, `A proceed`) before proceeding (config: approval can override)",
|
|
566
585
|
"context.actionOptionHint": "Approval reply format: include a label token (e.g. `A`, `A OK`, `A proceed`)",
|
|
567
586
|
"context.actionExplainHint": "Before requesting approval, explain what each label will run/change with a one-line summary.",
|
|
568
|
-
"context.finalLabelPrompt": "Available labels now: {labels}. Please reply
|
|
587
|
+
"context.finalLabelPrompt": "Available labels now: {labels}. Please reply with a format that includes a label token. (e.g. {example}, `A proceed`)",
|
|
569
588
|
"context.suggestionHeader": "Suggested Next Options",
|
|
570
589
|
"context.suggestionCommandHint": "Reference command: {command}",
|
|
571
|
-
"context.suggestionFinalPrompt": "Recommended labels now: {labels}. Please reply
|
|
590
|
+
"context.suggestionFinalPrompt": "Recommended labels now: {labels}. Please reply with a format that includes a label token. (e.g. {example}, `A proceed`)",
|
|
572
591
|
"context.suggestion.createFeature": "Create a new feature",
|
|
573
592
|
"context.suggestion.showDone": "Show completed features",
|
|
574
593
|
"context.suggestion.showAll": "Show all features",
|
|
@@ -587,6 +606,8 @@ var I18N = {
|
|
|
587
606
|
"context.list.completePrePrReview": "Complete Pre-PR review",
|
|
588
607
|
"context.list.addPrePrFindings": "Add/fill Pre-PR Findings",
|
|
589
608
|
"context.list.addPrePrEvidence": "Add Pre-PR Evidence",
|
|
609
|
+
"context.list.addPrReviewFindings": "Add/fill PR Review Findings",
|
|
610
|
+
"context.list.addPrReviewEvidence": "Add PR Review Evidence",
|
|
590
611
|
"context.list.resolvePrePrMajorFindings": "Resolve major pre-PR findings ({count})",
|
|
591
612
|
"context.list.resolvePrePrMinorFindings": "Resolve minor pre-PR findings ({count})",
|
|
592
613
|
"context.list.setPrStatus": "Set PR Status",
|
|
@@ -850,9 +871,9 @@ var I18N = {
|
|
|
850
871
|
tasksImprove: "Improve tasks.md and change Doc Status to Review.",
|
|
851
872
|
tasksApproval: "Share tasks.md with the user and get progress approval (`A` or `A OK` format). (Then set Doc Status to Approved)",
|
|
852
873
|
docsCommitPlanning: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(planning): {folderName} planning docs"',
|
|
853
|
-
issueCreateAndWrite: "Generate the issue body template, refine goals/completion criteria, get explicit user OK, create the issue, then update issue number in
|
|
874
|
+
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.",
|
|
854
875
|
issuePrepareFromDoc: "Use `issue.md` to refine issue title/body/labels draft, get explicit user OK, then set status to `Ready`.",
|
|
855
|
-
issueCreateFromDoc: "When `issue.md` status is `Ready`, create the GitHub Issue and sync the created issue number into `
|
|
876
|
+
issueCreateFromDoc: "When `issue.md` status is `Ready`, create the GitHub Issue and sync the created issue number into `tasks.md`.",
|
|
856
877
|
docsCommitIssueUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(#{issueNumber}): {folderName} docs update"',
|
|
857
878
|
docsCommitUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs: {folderName} docs update"',
|
|
858
879
|
projectCommitIssueUpdate: '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(#{issueNumber}): {commitTopic}")',
|
|
@@ -868,30 +889,40 @@ var I18N = {
|
|
|
868
889
|
checkTaskStatuses: "Check task statuses. ({done}/{total})",
|
|
869
890
|
taskCommitGateStrictBlock: "Before moving to the next TODO task, you must satisfy the `1 task = 1 commit` rule. Check result: {reason}. Re-align task commit boundaries, then continue.",
|
|
870
891
|
taskCommitGateWarnProceed: "\u26A0\uFE0F Task commit boundary warning: {reason}. You may continue, but `1 task = 1 commit` is recommended.",
|
|
871
|
-
taskCommitGateReasonNoTasksCommit: "No recent
|
|
872
|
-
taskCommitGateReasonTasksFileUnavailable: "Cannot read
|
|
873
|
-
taskCommitGateReasonDoneCount: "
|
|
874
|
-
taskCommitGateReasonMismatchLastDone: "The latest
|
|
892
|
+
taskCommitGateReasonNoTasksCommit: "No recent project code commit was found",
|
|
893
|
+
taskCommitGateReasonTasksFileUnavailable: "Cannot read recent project code commit history",
|
|
894
|
+
taskCommitGateReasonDoneCount: "Project commit gate check result is unexpected ({count})",
|
|
895
|
+
taskCommitGateReasonMismatchLastDone: "The latest project code commit does not match the last completed task",
|
|
875
896
|
prLegacyAsk: "tasks.md is missing PR/PR Status fields. Update to the latest template format? (CHECK required)",
|
|
876
897
|
prePrReviewFieldMissing: "tasks.md is missing the `Pre-PR Review` field. Add `- **Pre-PR Review**: Pending | Done` and run context again. (CHECK required)",
|
|
877
898
|
prePrReviewFindingsMissing: "tasks.md is missing `Pre-PR Findings` or uses an invalid format. Record it as `- **Pre-PR Findings**: major=0, minor=0`. (CHECK required)",
|
|
878
899
|
prePrReviewEvidenceMissing: "tasks.md `Pre-PR Evidence` is empty or placeholder. Add concrete review evidence (doc path/link/log). (CHECK required)",
|
|
879
900
|
prePrReviewMajorBlocked: "Pre-PR major findings are recorded ({count}). With `blockOnFindings=true`, PR creation is blocked until major findings are resolved/aligned.",
|
|
880
901
|
prePrReviewMinorBlocked: "Pre-PR minor findings are recorded ({count}). With `minorPolicy=block`, PR creation is blocked until minor findings are resolved/aligned.",
|
|
881
|
-
prePrReviewRun: "Run a pre-PR code review before creating the PR.
|
|
902
|
+
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. Major policy: {findingsPolicy}. Minor policy: {minorFindingsPolicy}",
|
|
882
903
|
prePrReviewFindingsBlock: "major findings must be fixed/aligned before PR creation",
|
|
883
904
|
prePrReviewFindingsWarn: "you may proceed after sharing the risks",
|
|
884
905
|
prePrReviewMinorFindingsBlock: "minor findings must also be fixed/aligned before PR creation",
|
|
885
906
|
prePrReviewMinorFindingsWarn: "you may proceed after documenting/sharing minor risks",
|
|
907
|
+
prReviewFindingsFieldMissing: "tasks.md is missing the `PR Review Findings` field. Add `- **PR Review Findings**: major=0, minor=0` and continue. (CHECK required)",
|
|
908
|
+
prReviewFindingsMissing: "tasks.md `PR Review Findings` is missing or invalid. Record it as `- **PR Review Findings**: major=0, minor=0`. (CHECK required)",
|
|
909
|
+
prReviewEvidenceFieldMissing: "tasks.md is missing the `PR Review Evidence` field. Add `- **PR Review Evidence**: -` and continue. (CHECK required)",
|
|
910
|
+
prReviewEvidenceMissing: "tasks.md `PR Review Evidence` is empty or placeholder. Record concrete evidence for resolved/aligned review comments. (CHECK required)",
|
|
886
911
|
prCreate: "Generate the PR body template, refine changes/tests sections, get explicit user OK, create the PR, then record the PR link in tasks.md.",
|
|
887
912
|
prCreatePrepareFromDoc: "Use `pr.md` to refine PR title/body/labels draft, get explicit user OK, then set status to `Ready`.",
|
|
888
|
-
prCreateExecuteFromDoc: "When `pr.md` status is `Ready`, create the PR and record the PR link/status in `pr.md`
|
|
913
|
+
prCreateExecuteFromDoc: "When `pr.md` status is `Ready`, create the PR and record the PR link/status in `tasks.md`. (Keep only `pr.md` status as `Ready`.)",
|
|
889
914
|
prCreatePrepare: "Generate the PR body template, refine changes/tests sections, and get explicit user OK before PR creation.",
|
|
890
915
|
prCreateExecute: "Create the PR with the finalized body, then record the created PR link in tasks.md.",
|
|
891
916
|
prCreateRequiredSequence: "PR creation is a required 2-step sequence: (1) generate/refine PR body template + explicit user OK, (2) create PR + record PR link in tasks.md. Complete both in order.",
|
|
892
917
|
prFillStatus: "Set PR Status in tasks.md to Review. (Keep Review during PR creation/review stages.)",
|
|
893
918
|
prResolveReview: "Keep PR Status as Review while addressing comments. For review-fix commits, use commit messages that summarize resolved review feedback (not task titles). Once ready to merge, get explicit user OK and run `npx lee-spec-kit github pr {featureRef} --merge --confirm OK`. (On success, PR Status is synced to Approved.)",
|
|
894
|
-
prReviewResolve: "Keep PR Status as Review while addressing comments. For review-fix commits, use commit messages that summarize resolved review feedback (not task titles).",
|
|
919
|
+
prReviewResolve: "First review/analyze the PR comments, then apply the required fixes. Keep PR Status as Review while addressing comments. For review-fix commits, use commit messages that summarize resolved review feedback (not task titles). Keep `PR Review Findings/Evidence` updated, then run push only after explicit user OK.",
|
|
920
|
+
prReviewPush: 'To reflect review-fix commits on the PR head branch, get explicit user OK and run `cd "{projectGitCwd}" && git push`.',
|
|
921
|
+
prReviewRemoteBlocked: "Remote PR checks indicate this PR is not ready to merge yet: {reasons}. Resolve review comments/check statuses, then re-check.",
|
|
922
|
+
prReviewRemoteReasonChangesRequested: "review decision is changes requested or additional review required",
|
|
923
|
+
prReviewRemoteReasonChecksFailing: "{count} failing check(s) detected",
|
|
924
|
+
prReviewRemoteReasonChecksPending: "{count} pending check(s) detected",
|
|
925
|
+
prReviewRemoteReasonMergeBlocked: "merge state is blocked (`{status}`)",
|
|
895
926
|
prReviewMerge: "When ready to merge, get explicit user OK and run `npx lee-spec-kit github pr {featureRef} --merge --confirm OK`. (On success, PR Status is synced to Approved.)",
|
|
896
927
|
prRequestReview: "Request review and set/keep PR Status as Review.",
|
|
897
928
|
featureDone: "Workflow requirements and all tasks/completion criteria are satisfied. This feature is done.",
|
|
@@ -908,13 +939,20 @@ var I18N = {
|
|
|
908
939
|
legacyTasksPrePrReviewField: "Legacy tasks.md format detected. Add `Pre-PR Review` before PR steps. (`- **Pre-PR Review**: Pending | Done`)",
|
|
909
940
|
legacyTasksPrePrFindingsField: "Legacy tasks.md format detected. Add `Pre-PR Findings` before PR steps. (`- **Pre-PR Findings**: major=0, minor=0`)",
|
|
910
941
|
legacyTasksPrePrEvidenceField: "Legacy tasks.md format detected. Add `Pre-PR Evidence` before PR steps.",
|
|
942
|
+
legacyTasksPrReviewFindingsField: "Legacy tasks.md format detected. Add `PR Review Findings` before review iteration. (`- **PR Review Findings**: major=0, minor=0`)",
|
|
943
|
+
legacyTasksPrReviewEvidenceField: "Legacy tasks.md format detected. Add `PR Review Evidence` before review iteration.",
|
|
911
944
|
workflowSpecNotApproved: "Implementation is done but spec.md Status is not Approved. (Update spec.md Status to Approved.)",
|
|
912
945
|
workflowPlanNotApproved: "Implementation is done but plan.md Status is not Approved. (Update plan.md Status to Approved.)",
|
|
913
|
-
workflowIssueMissing: "Implementation is done but Issue Number is missing. (Fill Issue Number in
|
|
946
|
+
workflowIssueMissing: "Implementation is done but Issue Number is missing. (Fill Issue Number in tasks.md.)",
|
|
914
947
|
workflowProjectUncommittedChanges: "Commit project code changes before completing workflow. (Project worktree has uncommitted changes.)",
|
|
915
948
|
workflowPrLinkMissing: "Implementation is done but PR link is missing. (Fill the PR field in tasks.md.)",
|
|
916
949
|
workflowPrStatusMissing: "Implementation is done but PR Status is missing. (Set PR Status to Review during PR creation/review stages.)",
|
|
917
950
|
workflowPrStatusNotApproved: "Implementation is done but PR Status is not Approved. (Keep PR Status as Review before merge and sync to Approved only after successful merge.)",
|
|
951
|
+
workflowPrReviewFindingsMissing: "In review stage, `PR Review Findings` is missing or invalid. (Use `major=0, minor=0` format.)",
|
|
952
|
+
workflowPrReviewEvidenceMissing: "In review stage, `PR Review Evidence` is empty. (Record evidence for review comment handling.)",
|
|
953
|
+
workflowPrRemoteChangesRequested: "Remote PR shows changes requested or additional review required. Address comments, push, then re-check.",
|
|
954
|
+
workflowPrRemoteChecksFailing: "Remote PR has {count} failing check(s). Fix failures, then re-check.",
|
|
955
|
+
workflowPrRemoteChecksPending: "Remote PR has {count} pending check(s). Wait for checks to complete, then re-check.",
|
|
918
956
|
workflowPrePrReviewMissing: "Implementation is done but `Pre-PR Review` is missing. (Add `- **Pre-PR Review**: Pending | Done` in tasks.md.)",
|
|
919
957
|
workflowPrePrReviewNotDone: "Implementation is done but `Pre-PR Review` is not Done. (Run pre-PR review, then update it to Done.)",
|
|
920
958
|
workflowPrePrFindingsMissing: "Implementation is done but `Pre-PR Findings` is missing or invalid. (Use `major=0, minor=0` format.)",
|
|
@@ -1307,10 +1345,10 @@ function sleep(ms) {
|
|
|
1307
1345
|
return new Promise((resolve) => globalThis.setTimeout(resolve, ms));
|
|
1308
1346
|
}
|
|
1309
1347
|
function toScopeKey(value) {
|
|
1310
|
-
return createHash("sha1").update(
|
|
1348
|
+
return createHash("sha1").update(path19.resolve(value)).digest("hex").slice(0, 16);
|
|
1311
1349
|
}
|
|
1312
1350
|
function getTempRuntimeDir(scopePath) {
|
|
1313
|
-
return
|
|
1351
|
+
return path19.join(os.tmpdir(), RUNTIME_TEMP_DIRNAME, toScopeKey(scopePath));
|
|
1314
1352
|
}
|
|
1315
1353
|
function resolveGitRuntimeDir(cwd) {
|
|
1316
1354
|
try {
|
|
@@ -1324,38 +1362,38 @@ function resolveGitRuntimeDir(cwd) {
|
|
|
1324
1362
|
}
|
|
1325
1363
|
).trim();
|
|
1326
1364
|
if (!out) return null;
|
|
1327
|
-
return
|
|
1365
|
+
return path19.isAbsolute(out) ? out : path19.resolve(cwd, out);
|
|
1328
1366
|
} catch {
|
|
1329
1367
|
return null;
|
|
1330
1368
|
}
|
|
1331
1369
|
}
|
|
1332
1370
|
function getRuntimeStateDir(cwd) {
|
|
1333
|
-
const resolved =
|
|
1371
|
+
const resolved = path19.resolve(cwd);
|
|
1334
1372
|
return resolveGitRuntimeDir(resolved) ?? getTempRuntimeDir(resolved);
|
|
1335
1373
|
}
|
|
1336
1374
|
function getDocsLockPath(docsDir) {
|
|
1337
|
-
return
|
|
1375
|
+
return path19.join(
|
|
1338
1376
|
getRuntimeStateDir(docsDir),
|
|
1339
1377
|
"locks",
|
|
1340
1378
|
`docs-${toScopeKey(docsDir)}.lock`
|
|
1341
1379
|
);
|
|
1342
1380
|
}
|
|
1343
1381
|
function getInitLockPath(targetDir) {
|
|
1344
|
-
return
|
|
1345
|
-
getRuntimeStateDir(
|
|
1382
|
+
return path19.join(
|
|
1383
|
+
getRuntimeStateDir(path19.dirname(path19.resolve(targetDir))),
|
|
1346
1384
|
"locks",
|
|
1347
1385
|
`init-${toScopeKey(targetDir)}.lock`
|
|
1348
1386
|
);
|
|
1349
1387
|
}
|
|
1350
1388
|
function getApprovalTicketStorePath(docsDir) {
|
|
1351
|
-
return
|
|
1389
|
+
return path19.join(
|
|
1352
1390
|
getRuntimeStateDir(docsDir),
|
|
1353
1391
|
"tickets",
|
|
1354
1392
|
`approval-${toScopeKey(docsDir)}.json`
|
|
1355
1393
|
);
|
|
1356
1394
|
}
|
|
1357
1395
|
function getProjectExecutionLockPath(cwd) {
|
|
1358
|
-
return
|
|
1396
|
+
return path19.join(getRuntimeStateDir(cwd), "locks", "project.lock");
|
|
1359
1397
|
}
|
|
1360
1398
|
async function isStaleLock(lockPath, staleMs) {
|
|
1361
1399
|
try {
|
|
@@ -1396,7 +1434,7 @@ function isProcessAlive(pid) {
|
|
|
1396
1434
|
}
|
|
1397
1435
|
}
|
|
1398
1436
|
async function tryAcquire(lockPath, owner) {
|
|
1399
|
-
await fs15.ensureDir(
|
|
1437
|
+
await fs15.ensureDir(path19.dirname(lockPath));
|
|
1400
1438
|
try {
|
|
1401
1439
|
const fd = await fs15.open(lockPath, "wx");
|
|
1402
1440
|
const payload = JSON.stringify(
|
|
@@ -1473,30 +1511,30 @@ var ENGINE_MANAGED_AGENT_FILES = [
|
|
|
1473
1511
|
"pr-template.md"
|
|
1474
1512
|
];
|
|
1475
1513
|
var ENGINE_MANAGED_AGENT_DIRS = ["skills"];
|
|
1476
|
-
var ENGINE_MANAGED_FEATURE_PATH =
|
|
1514
|
+
var ENGINE_MANAGED_FEATURE_PATH = path19.join(
|
|
1477
1515
|
"features",
|
|
1478
1516
|
"feature-base"
|
|
1479
1517
|
);
|
|
1480
1518
|
async function pruneEngineManagedDocs(docsDir) {
|
|
1481
1519
|
const removed = [];
|
|
1482
1520
|
for (const file of ENGINE_MANAGED_AGENT_FILES) {
|
|
1483
|
-
const target =
|
|
1521
|
+
const target = path19.join(docsDir, "agents", file);
|
|
1484
1522
|
if (await fs15.pathExists(target)) {
|
|
1485
1523
|
await fs15.remove(target);
|
|
1486
|
-
removed.push(
|
|
1524
|
+
removed.push(path19.relative(docsDir, target));
|
|
1487
1525
|
}
|
|
1488
1526
|
}
|
|
1489
1527
|
for (const dir of ENGINE_MANAGED_AGENT_DIRS) {
|
|
1490
|
-
const target =
|
|
1528
|
+
const target = path19.join(docsDir, "agents", dir);
|
|
1491
1529
|
if (await fs15.pathExists(target)) {
|
|
1492
1530
|
await fs15.remove(target);
|
|
1493
|
-
removed.push(
|
|
1531
|
+
removed.push(path19.relative(docsDir, target));
|
|
1494
1532
|
}
|
|
1495
1533
|
}
|
|
1496
|
-
const featureBasePath =
|
|
1534
|
+
const featureBasePath = path19.join(docsDir, ENGINE_MANAGED_FEATURE_PATH);
|
|
1497
1535
|
if (await fs15.pathExists(featureBasePath)) {
|
|
1498
1536
|
await fs15.remove(featureBasePath);
|
|
1499
|
-
removed.push(
|
|
1537
|
+
removed.push(path19.relative(docsDir, featureBasePath));
|
|
1500
1538
|
}
|
|
1501
1539
|
return removed;
|
|
1502
1540
|
}
|
|
@@ -1637,7 +1675,7 @@ ${tr(lang2, "cli", "common.canceled")}`)
|
|
|
1637
1675
|
}
|
|
1638
1676
|
async function runInit(options) {
|
|
1639
1677
|
const cwd = process.cwd();
|
|
1640
|
-
const defaultName =
|
|
1678
|
+
const defaultName = path19.basename(cwd);
|
|
1641
1679
|
let projectName = options.name || defaultName;
|
|
1642
1680
|
let projectType = options.type;
|
|
1643
1681
|
let components = parseComponentsOption(options.components);
|
|
@@ -1650,7 +1688,7 @@ async function runInit(options) {
|
|
|
1650
1688
|
const componentProjectRoots = parseComponentProjectRootsOption(
|
|
1651
1689
|
options.componentProjectRoots
|
|
1652
1690
|
);
|
|
1653
|
-
const targetDir =
|
|
1691
|
+
const targetDir = path19.resolve(cwd, options.dir || "./docs");
|
|
1654
1692
|
const skipPrompts = !!options.yes || !!options.nonInteractive;
|
|
1655
1693
|
if (options.docsRepo && !["embedded", "standalone"].includes(options.docsRepo)) {
|
|
1656
1694
|
throw createCliError(
|
|
@@ -2039,7 +2077,7 @@ async function runInit(options) {
|
|
|
2039
2077
|
);
|
|
2040
2078
|
console.log();
|
|
2041
2079
|
const templatesDir = getTemplatesDir();
|
|
2042
|
-
const commonPath =
|
|
2080
|
+
const commonPath = path19.join(templatesDir, lang, "common");
|
|
2043
2081
|
if (!await fs15.pathExists(commonPath)) {
|
|
2044
2082
|
throw new Error(
|
|
2045
2083
|
tr(lang, "cli", "init.error.templateNotFound", { path: commonPath })
|
|
@@ -2047,11 +2085,11 @@ async function runInit(options) {
|
|
|
2047
2085
|
}
|
|
2048
2086
|
await copyTemplates(commonPath, targetDir);
|
|
2049
2087
|
if (projectType === "multi") {
|
|
2050
|
-
const featuresRoot =
|
|
2088
|
+
const featuresRoot = path19.join(targetDir, "features");
|
|
2051
2089
|
for (const component of components) {
|
|
2052
|
-
const componentDir =
|
|
2090
|
+
const componentDir = path19.join(featuresRoot, component);
|
|
2053
2091
|
await fs15.ensureDir(componentDir);
|
|
2054
|
-
const readmePath =
|
|
2092
|
+
const readmePath = path19.join(componentDir, "README.md");
|
|
2055
2093
|
if (!await fs15.pathExists(readmePath)) {
|
|
2056
2094
|
await fs15.writeFile(
|
|
2057
2095
|
readmePath,
|
|
@@ -2104,7 +2142,7 @@ async function runInit(options) {
|
|
|
2104
2142
|
config.projectRoot = projectRoot;
|
|
2105
2143
|
}
|
|
2106
2144
|
}
|
|
2107
|
-
const configPath =
|
|
2145
|
+
const configPath = path19.join(targetDir, ".lee-spec-kit.json");
|
|
2108
2146
|
await fs15.writeJson(configPath, config, { spaces: 2 });
|
|
2109
2147
|
console.log(chalk6.green(tr(lang, "cli", "init.log.docsCreated")));
|
|
2110
2148
|
console.log();
|
|
@@ -2151,7 +2189,7 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
|
|
|
2151
2189
|
}
|
|
2152
2190
|
};
|
|
2153
2191
|
const toGitPath = (input) => input.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
2154
|
-
const
|
|
2192
|
+
const toRepoRelativePath = (workdir, relativePath2) => {
|
|
2155
2193
|
if (relativePath2 === ".") return ".";
|
|
2156
2194
|
try {
|
|
2157
2195
|
const prefix = execFileSync("git", ["rev-parse", "--show-prefix"], {
|
|
@@ -2173,7 +2211,7 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
|
|
|
2173
2211
|
console.log(chalk6.blue(tr(lang, "cli", "init.log.gitInit")));
|
|
2174
2212
|
runGit(["init"], cwd);
|
|
2175
2213
|
}
|
|
2176
|
-
const relativePath =
|
|
2214
|
+
const relativePath = path19.relative(cwd, targetDir);
|
|
2177
2215
|
const stagedBeforeAdd = getCachedStagedFiles(cwd);
|
|
2178
2216
|
if (relativePath === "." && stagedBeforeAdd && stagedBeforeAdd.length > 0) {
|
|
2179
2217
|
console.log(
|
|
@@ -2186,7 +2224,7 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
|
|
|
2186
2224
|
return;
|
|
2187
2225
|
}
|
|
2188
2226
|
if (relativePath !== "." && isPathIgnored(cwd, relativePath)) {
|
|
2189
|
-
const repoRelativePath =
|
|
2227
|
+
const repoRelativePath = toRepoRelativePath(cwd, relativePath);
|
|
2190
2228
|
console.log(
|
|
2191
2229
|
chalk6.yellow(
|
|
2192
2230
|
tr(lang, "cli", "init.warn.docsPathIgnoredSkipCommit", {
|
|
@@ -2230,17 +2268,17 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
|
|
|
2230
2268
|
}
|
|
2231
2269
|
function getAncestorDirs(startDir) {
|
|
2232
2270
|
const dirs = [];
|
|
2233
|
-
let current =
|
|
2271
|
+
let current = path19.resolve(startDir);
|
|
2234
2272
|
while (true) {
|
|
2235
2273
|
dirs.push(current);
|
|
2236
|
-
const parent =
|
|
2274
|
+
const parent = path19.dirname(current);
|
|
2237
2275
|
if (parent === current) break;
|
|
2238
2276
|
current = parent;
|
|
2239
2277
|
}
|
|
2240
2278
|
return dirs;
|
|
2241
2279
|
}
|
|
2242
2280
|
function hasWorkspaceBoundary(dir) {
|
|
2243
|
-
return fs15.existsSync(
|
|
2281
|
+
return fs15.existsSync(path19.join(dir, "package.json")) || fs15.existsSync(path19.join(dir, ".git"));
|
|
2244
2282
|
}
|
|
2245
2283
|
function getSearchBaseDirs(cwd) {
|
|
2246
2284
|
const ancestors = getAncestorDirs(cwd);
|
|
@@ -2256,7 +2294,7 @@ function normalizeComponentKeys(value) {
|
|
|
2256
2294
|
return Object.keys(value).map((key) => key.trim().toLowerCase()).filter(Boolean);
|
|
2257
2295
|
}
|
|
2258
2296
|
async function inferComponentsFromFeaturesDir(docsDir) {
|
|
2259
|
-
const featuresPath =
|
|
2297
|
+
const featuresPath = path19.join(docsDir, "features");
|
|
2260
2298
|
if (!await fs15.pathExists(featuresPath)) return [];
|
|
2261
2299
|
const entries = await fs15.readdir(featuresPath, { withFileTypes: true });
|
|
2262
2300
|
const inferred = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name.trim().toLowerCase()).filter(
|
|
@@ -2267,21 +2305,21 @@ async function inferComponentsFromFeaturesDir(docsDir) {
|
|
|
2267
2305
|
async function getConfig(cwd) {
|
|
2268
2306
|
const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
|
|
2269
2307
|
const baseDirs = [
|
|
2270
|
-
...explicitDocsDir ? [
|
|
2308
|
+
...explicitDocsDir ? [path19.resolve(explicitDocsDir)] : [],
|
|
2271
2309
|
...getSearchBaseDirs(cwd)
|
|
2272
2310
|
];
|
|
2273
2311
|
const visitedBaseDirs = /* @__PURE__ */ new Set();
|
|
2274
2312
|
const visitedDocsDirs = /* @__PURE__ */ new Set();
|
|
2275
2313
|
for (const baseDir of baseDirs) {
|
|
2276
|
-
const resolvedBaseDir =
|
|
2314
|
+
const resolvedBaseDir = path19.resolve(baseDir);
|
|
2277
2315
|
if (visitedBaseDirs.has(resolvedBaseDir)) continue;
|
|
2278
2316
|
visitedBaseDirs.add(resolvedBaseDir);
|
|
2279
|
-
const possibleDocsDirs = [
|
|
2317
|
+
const possibleDocsDirs = [path19.join(resolvedBaseDir, "docs"), resolvedBaseDir];
|
|
2280
2318
|
for (const docsDir of possibleDocsDirs) {
|
|
2281
|
-
const resolvedDocsDir =
|
|
2319
|
+
const resolvedDocsDir = path19.resolve(docsDir);
|
|
2282
2320
|
if (visitedDocsDirs.has(resolvedDocsDir)) continue;
|
|
2283
2321
|
visitedDocsDirs.add(resolvedDocsDir);
|
|
2284
|
-
const configPath =
|
|
2322
|
+
const configPath = path19.join(resolvedDocsDir, ".lee-spec-kit.json");
|
|
2285
2323
|
if (await fs15.pathExists(configPath)) {
|
|
2286
2324
|
try {
|
|
2287
2325
|
const configFile = await fs15.readJson(configPath);
|
|
@@ -2311,16 +2349,16 @@ async function getConfig(cwd) {
|
|
|
2311
2349
|
} catch {
|
|
2312
2350
|
}
|
|
2313
2351
|
}
|
|
2314
|
-
const agentsPath =
|
|
2315
|
-
const featuresPath =
|
|
2352
|
+
const agentsPath = path19.join(resolvedDocsDir, "agents");
|
|
2353
|
+
const featuresPath = path19.join(resolvedDocsDir, "features");
|
|
2316
2354
|
if (await fs15.pathExists(agentsPath) && await fs15.pathExists(featuresPath)) {
|
|
2317
2355
|
const inferredComponents = await inferComponentsFromFeaturesDir(resolvedDocsDir);
|
|
2318
2356
|
const projectType = inferredComponents.length > 0 ? "multi" : "single";
|
|
2319
2357
|
const components = projectType === "multi" ? resolveProjectComponents("multi", inferredComponents) : void 0;
|
|
2320
2358
|
const langProbeCandidates = [
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2359
|
+
path19.join(agentsPath, "custom.md"),
|
|
2360
|
+
path19.join(agentsPath, "constitution.md"),
|
|
2361
|
+
path19.join(agentsPath, "agents.md")
|
|
2324
2362
|
];
|
|
2325
2363
|
let lang = "en";
|
|
2326
2364
|
for (const candidate of langProbeCandidates) {
|
|
@@ -2386,13 +2424,13 @@ async function patchMarkdownIfExists(filePath, transform) {
|
|
|
2386
2424
|
await fs15.writeFile(filePath, transform(content), "utf-8");
|
|
2387
2425
|
}
|
|
2388
2426
|
async function applyLocalWorkflowTemplateToFeatureDir(featureDir, lang) {
|
|
2389
|
-
await patchMarkdownIfExists(
|
|
2427
|
+
await patchMarkdownIfExists(path19.join(featureDir, "spec.md"), sanitizeSpecForLocal);
|
|
2390
2428
|
await patchMarkdownIfExists(
|
|
2391
|
-
|
|
2429
|
+
path19.join(featureDir, "tasks.md"),
|
|
2392
2430
|
(content) => sanitizeTasksForLocal(content, lang)
|
|
2393
2431
|
);
|
|
2394
|
-
await fs15.remove(
|
|
2395
|
-
await fs15.remove(
|
|
2432
|
+
await fs15.remove(path19.join(featureDir, "issue.md"));
|
|
2433
|
+
await fs15.remove(path19.join(featureDir, "pr.md"));
|
|
2396
2434
|
}
|
|
2397
2435
|
|
|
2398
2436
|
// src/commands/feature.ts
|
|
@@ -2537,19 +2575,19 @@ async function runFeature(name, options) {
|
|
|
2537
2575
|
}
|
|
2538
2576
|
let featuresDir;
|
|
2539
2577
|
if (projectType === "multi") {
|
|
2540
|
-
featuresDir =
|
|
2578
|
+
featuresDir = path19.join(docsDir, "features", component);
|
|
2541
2579
|
} else {
|
|
2542
|
-
featuresDir =
|
|
2580
|
+
featuresDir = path19.join(docsDir, "features");
|
|
2543
2581
|
}
|
|
2544
2582
|
const featureFolderName = `${featureId}-${name}`;
|
|
2545
|
-
const featureDir =
|
|
2583
|
+
const featureDir = path19.join(featuresDir, featureFolderName);
|
|
2546
2584
|
if (await fs15.pathExists(featureDir)) {
|
|
2547
2585
|
throw createCliError(
|
|
2548
2586
|
"INVALID_ARGUMENT",
|
|
2549
2587
|
tr(lang, "cli", "feature.folderExists", { path: featureDir })
|
|
2550
2588
|
);
|
|
2551
2589
|
}
|
|
2552
|
-
const featureBasePath =
|
|
2590
|
+
const featureBasePath = path19.join(
|
|
2553
2591
|
getTemplatesDir(),
|
|
2554
2592
|
lang,
|
|
2555
2593
|
"common",
|
|
@@ -2608,7 +2646,7 @@ async function runFeature(name, options) {
|
|
|
2608
2646
|
featureName: name,
|
|
2609
2647
|
component: projectType === "multi" ? component : void 0,
|
|
2610
2648
|
featurePath: featureDir,
|
|
2611
|
-
featurePathFromDocs:
|
|
2649
|
+
featurePathFromDocs: path19.relative(docsDir, featureDir)
|
|
2612
2650
|
};
|
|
2613
2651
|
},
|
|
2614
2652
|
{ owner: "feature" }
|
|
@@ -2620,9 +2658,9 @@ function sleep2(ms) {
|
|
|
2620
2658
|
async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
|
|
2621
2659
|
const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
|
|
2622
2660
|
const candidates = [
|
|
2623
|
-
...explicitDocsDir ? [
|
|
2624
|
-
|
|
2625
|
-
|
|
2661
|
+
...explicitDocsDir ? [path19.resolve(explicitDocsDir)] : [],
|
|
2662
|
+
path19.resolve(cwd, "docs"),
|
|
2663
|
+
path19.resolve(cwd)
|
|
2626
2664
|
];
|
|
2627
2665
|
const endAt = Date.now() + timeoutMs;
|
|
2628
2666
|
while (Date.now() < endAt) {
|
|
@@ -2649,11 +2687,11 @@ async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
|
|
|
2649
2687
|
return getConfig(cwd);
|
|
2650
2688
|
}
|
|
2651
2689
|
async function getNextFeatureId(docsDir, projectType, components) {
|
|
2652
|
-
const featuresDir =
|
|
2690
|
+
const featuresDir = path19.join(docsDir, "features");
|
|
2653
2691
|
let max = 0;
|
|
2654
2692
|
const scanDirs = [];
|
|
2655
2693
|
if (projectType === "multi") {
|
|
2656
|
-
scanDirs.push(...components.map((component) =>
|
|
2694
|
+
scanDirs.push(...components.map((component) => path19.join(featuresDir, component)));
|
|
2657
2695
|
} else {
|
|
2658
2696
|
scanDirs.push(featuresDir);
|
|
2659
2697
|
}
|
|
@@ -2798,6 +2836,36 @@ function getFindingsPolicyText(lang, blockOnFindings) {
|
|
|
2798
2836
|
function getMinorFindingsPolicyText(lang, minorPolicy) {
|
|
2799
2837
|
return minorPolicy === "block" ? tr(lang, "messages", "prePrReviewMinorFindingsBlock") : tr(lang, "messages", "prePrReviewMinorFindingsWarn");
|
|
2800
2838
|
}
|
|
2839
|
+
function getPrReviewRemoteBlockReasons(feature, lang) {
|
|
2840
|
+
const remote = feature.pr.remote;
|
|
2841
|
+
if (!remote || !remote.available) return [];
|
|
2842
|
+
const reasons = [];
|
|
2843
|
+
if (remote.hasBlockingReview) {
|
|
2844
|
+
reasons.push(tr(lang, "messages", "prReviewRemoteReasonChangesRequested"));
|
|
2845
|
+
}
|
|
2846
|
+
if (remote.failingChecks > 0) {
|
|
2847
|
+
reasons.push(
|
|
2848
|
+
tr(lang, "messages", "prReviewRemoteReasonChecksFailing", {
|
|
2849
|
+
count: remote.failingChecks
|
|
2850
|
+
})
|
|
2851
|
+
);
|
|
2852
|
+
}
|
|
2853
|
+
if (remote.pendingChecks > 0) {
|
|
2854
|
+
reasons.push(
|
|
2855
|
+
tr(lang, "messages", "prReviewRemoteReasonChecksPending", {
|
|
2856
|
+
count: remote.pendingChecks
|
|
2857
|
+
})
|
|
2858
|
+
);
|
|
2859
|
+
}
|
|
2860
|
+
if (remote.mergeBlocked) {
|
|
2861
|
+
reasons.push(
|
|
2862
|
+
tr(lang, "messages", "prReviewRemoteReasonMergeBlocked", {
|
|
2863
|
+
status: remote.mergeStateStatus || "UNKNOWN"
|
|
2864
|
+
})
|
|
2865
|
+
);
|
|
2866
|
+
}
|
|
2867
|
+
return reasons;
|
|
2868
|
+
}
|
|
2801
2869
|
function normalizeCommitTopicText(value) {
|
|
2802
2870
|
return value.replace(/\s+/g, " ").trim();
|
|
2803
2871
|
}
|
|
@@ -2815,19 +2883,11 @@ function resolveProjectCommitTopic(feature) {
|
|
|
2815
2883
|
}
|
|
2816
2884
|
function shouldBlockTaskCommitGate(policy, check) {
|
|
2817
2885
|
if (policy !== "strict") return false;
|
|
2818
|
-
|
|
2819
|
-
return (check.newDoneCount ?? 0) > 1;
|
|
2820
|
-
}
|
|
2821
|
-
return false;
|
|
2886
|
+
return !check.pass;
|
|
2822
2887
|
}
|
|
2823
2888
|
function normalizeGitRelativePath(value) {
|
|
2824
2889
|
return value.replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
2825
2890
|
}
|
|
2826
|
-
function toRepoRelativePath(cwd, relativePathFromCwd) {
|
|
2827
|
-
const prefix = (readGitText(cwd, ["rev-parse", "--show-prefix"]) || "").trim().replace(/\/+$/, "");
|
|
2828
|
-
if (!prefix) return normalizeGitRelativePath(relativePathFromCwd);
|
|
2829
|
-
return normalizeGitRelativePath(`${prefix}/${relativePathFromCwd}`);
|
|
2830
|
-
}
|
|
2831
2891
|
function readGitText(cwd, args) {
|
|
2832
2892
|
try {
|
|
2833
2893
|
return execFileSync("git", args, {
|
|
@@ -2842,79 +2902,45 @@ function readGitText(cwd, args) {
|
|
|
2842
2902
|
function normalizeTaskTopic(value) {
|
|
2843
2903
|
return normalizeCommitTopicText(value).replace(/^T-[A-Za-z0-9-]+\s+/, "");
|
|
2844
2904
|
}
|
|
2845
|
-
function
|
|
2846
|
-
|
|
2847
|
-
const lines = content.split("\n");
|
|
2848
|
-
let inCodeBlock = false;
|
|
2849
|
-
for (const line of lines) {
|
|
2850
|
-
if (/^\s*(```|~~~)/.test(line)) {
|
|
2851
|
-
inCodeBlock = !inCodeBlock;
|
|
2852
|
-
continue;
|
|
2853
|
-
}
|
|
2854
|
-
if (inCodeBlock) continue;
|
|
2855
|
-
const match = line.match(/^\s*-\s*\[([A-Z]+)\]((?:\[[^\]]+\])*)\s*(.+?)\s*$/);
|
|
2856
|
-
if (!match) continue;
|
|
2857
|
-
if (match[1].toUpperCase() !== "DONE") continue;
|
|
2858
|
-
const topic = normalizeTaskTopic(match[3] || "");
|
|
2859
|
-
if (!topic) continue;
|
|
2860
|
-
counts.set(topic, (counts.get(topic) || 0) + 1);
|
|
2861
|
-
}
|
|
2862
|
-
return counts;
|
|
2905
|
+
function normalizeCommitSubjectForGate(value) {
|
|
2906
|
+
return normalizeCommitTopicText(value).replace(/^[a-z]+(?:\([^)]*\))?!?:\s*/i, "").toLowerCase();
|
|
2863
2907
|
}
|
|
2864
2908
|
function checkTaskCommitGate(feature) {
|
|
2865
|
-
const
|
|
2866
|
-
const
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
if (!latestTasksCommit) {
|
|
2870
|
-
return { pass: false, reason: "NO_TASKS_COMMIT" };
|
|
2871
|
-
}
|
|
2872
|
-
const currentContent = readGitText(docsGitCwd, ["show", `${latestTasksCommit}:${repoTasksPath}`]);
|
|
2873
|
-
if (currentContent === void 0) {
|
|
2874
|
-
return { pass: false, reason: "TASKS_FILE_UNAVAILABLE" };
|
|
2875
|
-
}
|
|
2876
|
-
const previousContent = readGitText(docsGitCwd, ["show", `${latestTasksCommit}^:${repoTasksPath}`]) || "";
|
|
2877
|
-
const currentDone = parseDoneTaskTopicCounts(currentContent);
|
|
2878
|
-
const previousDone = parseDoneTaskTopicCounts(previousContent);
|
|
2879
|
-
let newDoneCount = 0;
|
|
2880
|
-
for (const [topic, currentCount] of currentDone.entries()) {
|
|
2881
|
-
const previousCount = previousDone.get(topic) || 0;
|
|
2882
|
-
if (currentCount > previousCount) {
|
|
2883
|
-
newDoneCount += currentCount - previousCount;
|
|
2884
|
-
}
|
|
2885
|
-
}
|
|
2886
|
-
if (newDoneCount > 1) {
|
|
2887
|
-
return {
|
|
2888
|
-
pass: false,
|
|
2889
|
-
reason: "MULTIPLE_DONE_TRANSITIONS",
|
|
2890
|
-
newDoneCount
|
|
2891
|
-
};
|
|
2909
|
+
const projectGitCwd = feature.git.projectGitCwd;
|
|
2910
|
+
const lastDoneTopic = normalizeTaskTopic(feature.lastDoneTask?.title || "");
|
|
2911
|
+
if (!projectGitCwd || !lastDoneTopic) {
|
|
2912
|
+
return { pass: true };
|
|
2892
2913
|
}
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
}
|
|
2914
|
+
const args = ["log", "-n", "1", "--pretty=%s", "--", "."];
|
|
2915
|
+
const relativeDocsDir = path19.relative(projectGitCwd, feature.git.docsGitCwd);
|
|
2916
|
+
const normalizedDocsDir = normalizeGitRelativePath(relativeDocsDir);
|
|
2917
|
+
if (normalizedDocsDir && normalizedDocsDir !== "." && normalizedDocsDir !== ".." && !normalizedDocsDir.startsWith("../")) {
|
|
2918
|
+
args.push(`:(exclude)${normalizedDocsDir}/**`);
|
|
2919
|
+
}
|
|
2920
|
+
const latestProjectSubject = readGitText(projectGitCwd, args);
|
|
2921
|
+
if (latestProjectSubject === void 0) {
|
|
2922
|
+
return { pass: false, reason: "PROJECT_LOG_UNAVAILABLE" };
|
|
2923
|
+
}
|
|
2924
|
+
const normalizedSubject = normalizeCommitSubjectForGate(latestProjectSubject);
|
|
2925
|
+
if (!normalizedSubject) {
|
|
2926
|
+
return { pass: false, reason: "NO_PROJECT_COMMIT" };
|
|
2902
2927
|
}
|
|
2903
|
-
|
|
2928
|
+
const normalizedLastDone = normalizeTaskTopic(lastDoneTopic).toLowerCase();
|
|
2929
|
+
if (!normalizedSubject.includes(normalizedLastDone)) {
|
|
2930
|
+
return { pass: false, reason: "MISMATCH_LAST_DONE" };
|
|
2931
|
+
}
|
|
2932
|
+
return { pass: true };
|
|
2904
2933
|
}
|
|
2905
2934
|
function getTaskCommitGateReasonText(lang, check) {
|
|
2906
2935
|
switch (check.reason) {
|
|
2907
|
-
case "
|
|
2936
|
+
case "NO_PROJECT_COMMIT":
|
|
2908
2937
|
return tr(lang, "messages", "taskCommitGateReasonNoTasksCommit");
|
|
2909
|
-
case "
|
|
2938
|
+
case "PROJECT_LOG_UNAVAILABLE":
|
|
2910
2939
|
return tr(lang, "messages", "taskCommitGateReasonTasksFileUnavailable");
|
|
2911
2940
|
case "MISMATCH_LAST_DONE":
|
|
2912
2941
|
return tr(lang, "messages", "taskCommitGateReasonMismatchLastDone");
|
|
2913
|
-
case "MULTIPLE_DONE_TRANSITIONS":
|
|
2914
2942
|
default:
|
|
2915
|
-
return tr(lang, "messages", "
|
|
2916
|
-
count: check.newDoneCount ?? 0
|
|
2917
|
-
});
|
|
2943
|
+
return tr(lang, "messages", "taskCommitGateReasonMismatchLastDone");
|
|
2918
2944
|
}
|
|
2919
2945
|
}
|
|
2920
2946
|
function getStepDefinitions(lang, workflow) {
|
|
@@ -3720,22 +3746,85 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
3720
3746
|
];
|
|
3721
3747
|
}
|
|
3722
3748
|
if (f.pr.status === "Review") {
|
|
3723
|
-
|
|
3749
|
+
if (!f.docs.prReviewFindingsFieldExists) {
|
|
3750
|
+
return [
|
|
3751
|
+
{
|
|
3752
|
+
type: "instruction",
|
|
3753
|
+
category: "code_review",
|
|
3754
|
+
requiresUserCheck: true,
|
|
3755
|
+
message: tr(lang, "messages", "prReviewFindingsFieldMissing")
|
|
3756
|
+
}
|
|
3757
|
+
];
|
|
3758
|
+
}
|
|
3759
|
+
if (!f.prReview.findings) {
|
|
3760
|
+
return [
|
|
3761
|
+
{
|
|
3762
|
+
type: "instruction",
|
|
3763
|
+
category: "code_review",
|
|
3764
|
+
requiresUserCheck: true,
|
|
3765
|
+
message: tr(lang, "messages", "prReviewFindingsMissing")
|
|
3766
|
+
}
|
|
3767
|
+
];
|
|
3768
|
+
}
|
|
3769
|
+
if (!f.docs.prReviewEvidenceFieldExists) {
|
|
3770
|
+
return [
|
|
3771
|
+
{
|
|
3772
|
+
type: "instruction",
|
|
3773
|
+
category: "code_review",
|
|
3774
|
+
requiresUserCheck: true,
|
|
3775
|
+
message: tr(lang, "messages", "prReviewEvidenceFieldMissing")
|
|
3776
|
+
}
|
|
3777
|
+
];
|
|
3778
|
+
}
|
|
3779
|
+
if ((f.prReview.findings.major > 0 || f.prReview.findings.minor > 0) && !f.prReview.evidenceProvided) {
|
|
3780
|
+
return [
|
|
3781
|
+
{
|
|
3782
|
+
type: "instruction",
|
|
3783
|
+
category: "code_review",
|
|
3784
|
+
requiresUserCheck: true,
|
|
3785
|
+
message: tr(lang, "messages", "prReviewEvidenceMissing")
|
|
3786
|
+
}
|
|
3787
|
+
];
|
|
3788
|
+
}
|
|
3789
|
+
const remoteBlockReasons = getPrReviewRemoteBlockReasons(f, lang);
|
|
3790
|
+
const actions = [
|
|
3724
3791
|
{
|
|
3725
3792
|
type: "instruction",
|
|
3726
3793
|
category: "code_review",
|
|
3727
3794
|
requiresUserCheck: true,
|
|
3728
3795
|
message: tr(lang, "messages", "prReviewResolve")
|
|
3729
|
-
}
|
|
3730
|
-
|
|
3796
|
+
}
|
|
3797
|
+
];
|
|
3798
|
+
if (!f.git.projectGitCwd) {
|
|
3799
|
+
actions.push({
|
|
3731
3800
|
type: "instruction",
|
|
3732
3801
|
category: "code_review",
|
|
3733
3802
|
requiresUserCheck: true,
|
|
3734
|
-
message: tr(lang, "messages", "
|
|
3735
|
-
|
|
3803
|
+
message: tr(lang, "messages", "standaloneNeedsProjectRoot")
|
|
3804
|
+
});
|
|
3805
|
+
} else {
|
|
3806
|
+
actions.push({
|
|
3807
|
+
type: "command",
|
|
3808
|
+
category: "code_review",
|
|
3809
|
+
requiresUserCheck: true,
|
|
3810
|
+
scope: "project",
|
|
3811
|
+
cwd: f.git.projectGitCwd,
|
|
3812
|
+
cmd: tr(lang, "messages", "prReviewPush", {
|
|
3813
|
+
projectGitCwd: f.git.projectGitCwd
|
|
3736
3814
|
})
|
|
3737
|
-
}
|
|
3738
|
-
|
|
3815
|
+
});
|
|
3816
|
+
}
|
|
3817
|
+
actions.push({
|
|
3818
|
+
type: "instruction",
|
|
3819
|
+
category: "code_review",
|
|
3820
|
+
requiresUserCheck: true,
|
|
3821
|
+
message: remoteBlockReasons.length > 0 ? tr(lang, "messages", "prReviewRemoteBlocked", {
|
|
3822
|
+
reasons: remoteBlockReasons.join("; ")
|
|
3823
|
+
}) : tr(lang, "messages", "prReviewMerge", {
|
|
3824
|
+
featureRef: f.id || f.folderName
|
|
3825
|
+
})
|
|
3826
|
+
});
|
|
3827
|
+
return actions;
|
|
3739
3828
|
}
|
|
3740
3829
|
return [
|
|
3741
3830
|
{
|
|
@@ -4071,7 +4160,7 @@ function parsePrePrReviewStatus(value) {
|
|
|
4071
4160
|
if (/^pending$/i.test(trimmed)) return "Pending";
|
|
4072
4161
|
return void 0;
|
|
4073
4162
|
}
|
|
4074
|
-
function
|
|
4163
|
+
function parseReviewFindings(value) {
|
|
4075
4164
|
if (!value) return void 0;
|
|
4076
4165
|
const trimmed = value.trim();
|
|
4077
4166
|
if (!trimmed || trimmed.includes("|")) return void 0;
|
|
@@ -4109,13 +4198,13 @@ function parsePrLink(value) {
|
|
|
4109
4198
|
return trimmed;
|
|
4110
4199
|
}
|
|
4111
4200
|
function normalizeGitPath(value) {
|
|
4112
|
-
return value.split(
|
|
4201
|
+
return value.split(path19.sep).join("/");
|
|
4113
4202
|
}
|
|
4114
4203
|
function resolveProjectStatusPaths(projectGitCwd, docsDir) {
|
|
4115
|
-
const relativeDocsDir =
|
|
4204
|
+
const relativeDocsDir = path19.relative(projectGitCwd, docsDir);
|
|
4116
4205
|
if (!relativeDocsDir) return [];
|
|
4117
|
-
if (
|
|
4118
|
-
if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${
|
|
4206
|
+
if (path19.isAbsolute(relativeDocsDir)) return [];
|
|
4207
|
+
if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${path19.sep}`)) {
|
|
4119
4208
|
return [];
|
|
4120
4209
|
}
|
|
4121
4210
|
const normalizedDocsDir = normalizeGitPath(relativeDocsDir).replace(/\/+$/, "");
|
|
@@ -4136,6 +4225,93 @@ function uniqueNormalizedPaths(values) {
|
|
|
4136
4225
|
}
|
|
4137
4226
|
var PROJECT_DIRTY_STATUS_CACHE = /* @__PURE__ */ new Map();
|
|
4138
4227
|
var COMPONENT_STATUS_PATH_CACHE = /* @__PURE__ */ new Map();
|
|
4228
|
+
var PR_REMOTE_STATUS_CACHE = /* @__PURE__ */ new Map();
|
|
4229
|
+
function toUpperToken(value) {
|
|
4230
|
+
if (typeof value !== "string") return void 0;
|
|
4231
|
+
const normalized = value.trim().toUpperCase();
|
|
4232
|
+
return normalized || void 0;
|
|
4233
|
+
}
|
|
4234
|
+
function parseCheckSignal(check) {
|
|
4235
|
+
if (!check || typeof check !== "object") return { failing: false, pending: false };
|
|
4236
|
+
const row = check;
|
|
4237
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
4238
|
+
for (const key of ["conclusion", "status", "state"]) {
|
|
4239
|
+
const token = toUpperToken(row[key]);
|
|
4240
|
+
if (token) tokens.add(token);
|
|
4241
|
+
}
|
|
4242
|
+
for (const token of tokens) {
|
|
4243
|
+
if (token === "FAILURE" || token === "FAILED" || token === "ERROR" || token === "TIMED_OUT" || token === "CANCELLED" || token === "ACTION_REQUIRED" || token === "STARTUP_FAILURE") {
|
|
4244
|
+
return { failing: true, pending: false };
|
|
4245
|
+
}
|
|
4246
|
+
}
|
|
4247
|
+
for (const token of tokens) {
|
|
4248
|
+
if (token === "PENDING" || token === "IN_PROGRESS" || token === "QUEUED" || token === "EXPECTED" || token === "WAITING" || token === "REQUESTED") {
|
|
4249
|
+
return { failing: false, pending: true };
|
|
4250
|
+
}
|
|
4251
|
+
}
|
|
4252
|
+
return { failing: false, pending: false };
|
|
4253
|
+
}
|
|
4254
|
+
function isMergeBlockedState(value) {
|
|
4255
|
+
if (!value) return false;
|
|
4256
|
+
return value === "BLOCKED" || value === "DIRTY" || value === "BEHIND" || value === "DRAFT" || value === "HAS_HOOKS" || value === "UNKNOWN" || value === "UNSTABLE";
|
|
4257
|
+
}
|
|
4258
|
+
function resolvePrRemoteStatus(prRef, projectGitCwd) {
|
|
4259
|
+
const cacheKey = `${projectGitCwd}::${prRef}`;
|
|
4260
|
+
if (PR_REMOTE_STATUS_CACHE.has(cacheKey)) {
|
|
4261
|
+
return PR_REMOTE_STATUS_CACHE.get(cacheKey) || null;
|
|
4262
|
+
}
|
|
4263
|
+
try {
|
|
4264
|
+
const raw = execFileSync(
|
|
4265
|
+
"gh",
|
|
4266
|
+
[
|
|
4267
|
+
"pr",
|
|
4268
|
+
"view",
|
|
4269
|
+
prRef,
|
|
4270
|
+
"--json",
|
|
4271
|
+
"reviewDecision,mergeStateStatus,isDraft,statusCheckRollup"
|
|
4272
|
+
],
|
|
4273
|
+
{
|
|
4274
|
+
cwd: projectGitCwd,
|
|
4275
|
+
encoding: "utf-8",
|
|
4276
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
4277
|
+
timeout: 5e3,
|
|
4278
|
+
maxBuffer: 1024 * 1024
|
|
4279
|
+
}
|
|
4280
|
+
).trim();
|
|
4281
|
+
if (!raw) {
|
|
4282
|
+
PR_REMOTE_STATUS_CACHE.set(cacheKey, null);
|
|
4283
|
+
return null;
|
|
4284
|
+
}
|
|
4285
|
+
const parsed = JSON.parse(raw);
|
|
4286
|
+
const reviewDecision = toUpperToken(parsed.reviewDecision);
|
|
4287
|
+
const mergeStateStatus = toUpperToken(parsed.mergeStateStatus);
|
|
4288
|
+
const isDraft = parsed.isDraft === true;
|
|
4289
|
+
let failingChecks = 0;
|
|
4290
|
+
let pendingChecks = 0;
|
|
4291
|
+
const rollup = Array.isArray(parsed.statusCheckRollup) ? parsed.statusCheckRollup : [];
|
|
4292
|
+
for (const check of rollup) {
|
|
4293
|
+
const signal = parseCheckSignal(check);
|
|
4294
|
+
if (signal.failing) failingChecks++;
|
|
4295
|
+
else if (signal.pending) pendingChecks++;
|
|
4296
|
+
}
|
|
4297
|
+
const remote = {
|
|
4298
|
+
source: "gh",
|
|
4299
|
+
available: true,
|
|
4300
|
+
reviewDecision,
|
|
4301
|
+
mergeStateStatus,
|
|
4302
|
+
isDraft,
|
|
4303
|
+
hasBlockingReview: reviewDecision === "CHANGES_REQUESTED" || reviewDecision === "REVIEW_REQUIRED",
|
|
4304
|
+
mergeBlocked: isDraft || isMergeBlockedState(mergeStateStatus),
|
|
4305
|
+
failingChecks,
|
|
4306
|
+
pendingChecks
|
|
4307
|
+
};
|
|
4308
|
+
PR_REMOTE_STATUS_CACHE.set(cacheKey, remote);
|
|
4309
|
+
return remote;
|
|
4310
|
+
} catch {
|
|
4311
|
+
PR_REMOTE_STATUS_CACHE.set(cacheKey, null);
|
|
4312
|
+
return null;
|
|
4313
|
+
}
|
|
4314
|
+
}
|
|
4139
4315
|
async function resolveComponentStatusPaths(projectGitCwd, component, workflow) {
|
|
4140
4316
|
const configured = workflow?.componentPaths?.[component];
|
|
4141
4317
|
const configuredCandidates = Array.isArray(configured) ? configured.map((value) => String(value).trim()).filter(Boolean) : [];
|
|
@@ -4149,10 +4325,10 @@ async function resolveComponentStatusPaths(projectGitCwd, component, workflow) {
|
|
|
4149
4325
|
const normalizedCandidates = uniqueNormalizedPaths(
|
|
4150
4326
|
candidates.map((candidate) => {
|
|
4151
4327
|
if (!candidate) return "";
|
|
4152
|
-
if (!
|
|
4153
|
-
const relative =
|
|
4328
|
+
if (!path19.isAbsolute(candidate)) return candidate;
|
|
4329
|
+
const relative = path19.relative(projectGitCwd, candidate);
|
|
4154
4330
|
if (!relative) return "";
|
|
4155
|
-
if (relative === ".." || relative.startsWith(`..${
|
|
4331
|
+
if (relative === ".." || relative.startsWith(`..${path19.sep}`)) return "";
|
|
4156
4332
|
return relative;
|
|
4157
4333
|
}).filter(Boolean)
|
|
4158
4334
|
);
|
|
@@ -4165,7 +4341,7 @@ async function resolveComponentStatusPaths(projectGitCwd, component, workflow) {
|
|
|
4165
4341
|
if (cached) return [...cached];
|
|
4166
4342
|
const existing = [];
|
|
4167
4343
|
for (const candidate of normalizedCandidates) {
|
|
4168
|
-
if (await fs15.pathExists(
|
|
4344
|
+
if (await fs15.pathExists(path19.join(projectGitCwd, candidate))) {
|
|
4169
4345
|
existing.push(candidate);
|
|
4170
4346
|
}
|
|
4171
4347
|
}
|
|
@@ -4254,15 +4430,15 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4254
4430
|
const lang = options.lang;
|
|
4255
4431
|
const workflowPolicy = resolveWorkflowPolicy(options.workflow);
|
|
4256
4432
|
const prePrReviewPolicy = resolvePrePrReviewPolicy(options.workflow);
|
|
4257
|
-
const folderName =
|
|
4433
|
+
const folderName = path19.basename(featurePath);
|
|
4258
4434
|
const match = folderName.match(/^(F\d+)-(.+)$/);
|
|
4259
4435
|
const id = match?.[1];
|
|
4260
4436
|
const slug = match?.[2] || folderName;
|
|
4261
|
-
const specPath =
|
|
4262
|
-
const planPath =
|
|
4263
|
-
const tasksPath =
|
|
4264
|
-
const issueDocPath =
|
|
4265
|
-
const prDocPath =
|
|
4437
|
+
const specPath = path19.join(featurePath, "spec.md");
|
|
4438
|
+
const planPath = path19.join(featurePath, "plan.md");
|
|
4439
|
+
const tasksPath = path19.join(featurePath, "tasks.md");
|
|
4440
|
+
const issueDocPath = path19.join(featurePath, "issue.md");
|
|
4441
|
+
const prDocPath = path19.join(featurePath, "pr.md");
|
|
4266
4442
|
let specStatus;
|
|
4267
4443
|
let issueNumber;
|
|
4268
4444
|
const specExists = await fs15.pathExists(specPath);
|
|
@@ -4292,8 +4468,12 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4292
4468
|
let prePrFindings;
|
|
4293
4469
|
let prePrEvidence;
|
|
4294
4470
|
let prePrEvidenceProvided = false;
|
|
4471
|
+
let prReviewFindings;
|
|
4472
|
+
let prReviewEvidence;
|
|
4473
|
+
let prReviewEvidenceProvided = false;
|
|
4295
4474
|
let prLink;
|
|
4296
4475
|
let prStatus;
|
|
4476
|
+
let prRemote;
|
|
4297
4477
|
let prFieldExists = false;
|
|
4298
4478
|
let prStatusFieldExists = false;
|
|
4299
4479
|
let issueDocStatus;
|
|
@@ -4306,6 +4486,8 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4306
4486
|
let prePrReviewFieldExists = false;
|
|
4307
4487
|
let prePrFindingsFieldExists = false;
|
|
4308
4488
|
let prePrEvidenceFieldExists = false;
|
|
4489
|
+
let prReviewFindingsFieldExists = false;
|
|
4490
|
+
let prReviewEvidenceFieldExists = false;
|
|
4309
4491
|
if (tasksExists) {
|
|
4310
4492
|
const content = await fs15.readFile(tasksPath, "utf-8");
|
|
4311
4493
|
const {
|
|
@@ -4322,10 +4504,12 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4322
4504
|
lastDoneTask = lastDone;
|
|
4323
4505
|
nextTodoTask = nextTodo;
|
|
4324
4506
|
completionChecklist = parseCompletionChecklist(content);
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4507
|
+
const issueValue = extractFirstSpecValue(content, [
|
|
4508
|
+
"\uC774\uC288 \uBC88\uD638",
|
|
4509
|
+
"Issue Number",
|
|
4510
|
+
"Issue"
|
|
4511
|
+
]);
|
|
4512
|
+
issueNumber = parseIssueNumber(issueValue);
|
|
4329
4513
|
const tasksDocStatusValue = extractFirstSpecValue(content, ["\uBB38\uC11C \uC0C1\uD0DC", "Doc Status"]);
|
|
4330
4514
|
tasksDocStatusFieldExists = hasAnySpecKey(content, ["\uBB38\uC11C \uC0C1\uD0DC", "Doc Status"]);
|
|
4331
4515
|
tasksDocStatus = parseDocStatus(tasksDocStatusValue);
|
|
@@ -4352,7 +4536,7 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4352
4536
|
"PR \uC804 \uB9AC\uBDF0 Findings",
|
|
4353
4537
|
"Pre-PR Findings"
|
|
4354
4538
|
]);
|
|
4355
|
-
prePrFindings =
|
|
4539
|
+
prePrFindings = parseReviewFindings(prePrFindingsValue);
|
|
4356
4540
|
const prePrEvidenceValue = extractFirstSpecValue(content, [
|
|
4357
4541
|
"PR \uC804 \uB9AC\uBDF0 Evidence",
|
|
4358
4542
|
"Pre-PR Evidence"
|
|
@@ -4363,6 +4547,25 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4363
4547
|
]);
|
|
4364
4548
|
prePrEvidence = prePrEvidenceValue?.trim();
|
|
4365
4549
|
prePrEvidenceProvided = !isPlaceholderReviewEvidence(prePrEvidenceValue);
|
|
4550
|
+
const prReviewFindingsValue = extractFirstSpecValue(content, [
|
|
4551
|
+
"PR \uB9AC\uBDF0 Findings",
|
|
4552
|
+
"PR Review Findings"
|
|
4553
|
+
]);
|
|
4554
|
+
prReviewFindingsFieldExists = hasAnySpecKey(content, [
|
|
4555
|
+
"PR \uB9AC\uBDF0 Findings",
|
|
4556
|
+
"PR Review Findings"
|
|
4557
|
+
]);
|
|
4558
|
+
prReviewFindings = parseReviewFindings(prReviewFindingsValue);
|
|
4559
|
+
const prReviewEvidenceValue = extractFirstSpecValue(content, [
|
|
4560
|
+
"PR \uB9AC\uBDF0 Evidence",
|
|
4561
|
+
"PR Review Evidence"
|
|
4562
|
+
]);
|
|
4563
|
+
prReviewEvidenceFieldExists = hasAnySpecKey(content, [
|
|
4564
|
+
"PR \uB9AC\uBDF0 Evidence",
|
|
4565
|
+
"PR Review Evidence"
|
|
4566
|
+
]);
|
|
4567
|
+
prReviewEvidence = prReviewEvidenceValue?.trim();
|
|
4568
|
+
prReviewEvidenceProvided = !isPlaceholderReviewEvidence(prReviewEvidenceValue);
|
|
4366
4569
|
}
|
|
4367
4570
|
const issueDocExists = await fs15.pathExists(issueDocPath);
|
|
4368
4571
|
if (issueDocExists) {
|
|
@@ -4370,20 +4573,11 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4370
4573
|
const issueDocStatusValue = extractFirstSpecValue(content, ["\uC0C1\uD0DC", "Status"]);
|
|
4371
4574
|
issueDocStatusFieldExists = hasAnySpecKey(content, ["\uC0C1\uD0DC", "Status"]);
|
|
4372
4575
|
issueDocStatus = parseWorkflowDocStatus(issueDocStatusValue);
|
|
4373
|
-
const issueValue = extractFirstSpecValue(content, [
|
|
4374
|
-
"\uC774\uC288 \uBC88\uD638",
|
|
4375
|
-
"Issue Number",
|
|
4376
|
-
"Issue"
|
|
4377
|
-
]);
|
|
4378
4576
|
issueDocIssueFieldExists = hasAnySpecKey(content, [
|
|
4379
4577
|
"\uC774\uC288 \uBC88\uD638",
|
|
4380
4578
|
"Issue Number",
|
|
4381
4579
|
"Issue"
|
|
4382
4580
|
]);
|
|
4383
|
-
const parsedIssueFromDoc = parseIssueNumber(issueValue);
|
|
4384
|
-
if (parsedIssueFromDoc) {
|
|
4385
|
-
issueNumber = parsedIssueFromDoc;
|
|
4386
|
-
}
|
|
4387
4581
|
}
|
|
4388
4582
|
const prDocExists = await fs15.pathExists(prDocPath);
|
|
4389
4583
|
if (prDocExists) {
|
|
@@ -4391,27 +4585,11 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4391
4585
|
const prDocStatusValue = extractFirstSpecValue(content, ["\uC0C1\uD0DC", "Status"]);
|
|
4392
4586
|
prDocStatusFieldExists = hasAnySpecKey(content, ["\uC0C1\uD0DC", "Status"]);
|
|
4393
4587
|
prDocStatus = parseWorkflowDocStatus(prDocStatusValue);
|
|
4394
|
-
const prValue = extractFirstSpecValue(content, ["PR", "Pull Request"]);
|
|
4395
4588
|
prDocPrFieldExists = hasAnySpecKey(content, ["PR", "Pull Request"]);
|
|
4396
|
-
const parsedPrLink = parsePrLink(prValue);
|
|
4397
|
-
if (parsedPrLink) {
|
|
4398
|
-
prLink = parsedPrLink;
|
|
4399
|
-
}
|
|
4400
|
-
const prReviewStatusValue = extractFirstSpecValue(content, [
|
|
4401
|
-
"PR \uC0C1\uD0DC",
|
|
4402
|
-
"PR Status"
|
|
4403
|
-
]);
|
|
4404
4589
|
prDocReviewStatusFieldExists = hasAnySpecKey(content, ["PR \uC0C1\uD0DC", "PR Status"]);
|
|
4405
|
-
const parsedPrStatus = parsePrReviewStatus(prReviewStatusValue);
|
|
4406
|
-
if (parsedPrStatus) {
|
|
4407
|
-
prStatus = parsedPrStatus;
|
|
4408
|
-
}
|
|
4409
|
-
}
|
|
4410
|
-
if (prDocPrFieldExists) {
|
|
4411
|
-
prFieldExists = true;
|
|
4412
4590
|
}
|
|
4413
|
-
if (
|
|
4414
|
-
|
|
4591
|
+
if (workflowPolicy.requireReview && prStatus === "Review" && prLink && context.projectGitCwd) {
|
|
4592
|
+
prRemote = resolvePrRemoteStatus(prLink, context.projectGitCwd) || void 0;
|
|
4415
4593
|
}
|
|
4416
4594
|
const warnings = [];
|
|
4417
4595
|
if (context.projectBranchAvailable === false) {
|
|
@@ -4423,7 +4601,7 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4423
4601
|
slug,
|
|
4424
4602
|
folderName
|
|
4425
4603
|
);
|
|
4426
|
-
const relativeFeaturePathFromDocs =
|
|
4604
|
+
const relativeFeaturePathFromDocs = path19.relative(context.docsDir, featurePath);
|
|
4427
4605
|
const normalizedFeaturePathFromDocs = normalizeGitPath(relativeFeaturePathFromDocs);
|
|
4428
4606
|
const docsPathIgnored = typeof context.docsPathIgnored === "boolean" ? context.docsPathIgnored : isGitPathIgnored(context.docsGitCwd, normalizedFeaturePathFromDocs);
|
|
4429
4607
|
let docsHasUncommittedChanges = typeof context.docsHasUncommittedChanges === "boolean" ? context.docsHasUncommittedChanges : false;
|
|
@@ -4509,6 +4687,12 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4509
4687
|
if (tasksExists && prePrReviewPolicy.enabled && !prePrEvidenceFieldExists) {
|
|
4510
4688
|
warnings.push(tr(lang, "warnings", "legacyTasksPrePrEvidenceField"));
|
|
4511
4689
|
}
|
|
4690
|
+
if (tasksExists && workflowPolicy.requireReview && !prReviewFindingsFieldExists) {
|
|
4691
|
+
warnings.push(tr(lang, "warnings", "legacyTasksPrReviewFindingsField"));
|
|
4692
|
+
}
|
|
4693
|
+
if (tasksExists && workflowPolicy.requireReview && !prReviewEvidenceFieldExists) {
|
|
4694
|
+
warnings.push(tr(lang, "warnings", "legacyTasksPrReviewEvidenceField"));
|
|
4695
|
+
}
|
|
4512
4696
|
if (tasksExists && !tasksDocStatusFieldExists) {
|
|
4513
4697
|
warnings.push(tr(lang, "warnings", "legacyTasksDocStatusField"));
|
|
4514
4698
|
}
|
|
@@ -4518,6 +4702,23 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4518
4702
|
if (projectHasUncommittedChanges) {
|
|
4519
4703
|
warnings.push(tr(lang, "warnings", "projectUncommittedChanges"));
|
|
4520
4704
|
}
|
|
4705
|
+
if (prRemote?.hasBlockingReview) {
|
|
4706
|
+
warnings.push(tr(lang, "warnings", "workflowPrRemoteChangesRequested"));
|
|
4707
|
+
}
|
|
4708
|
+
if ((prRemote?.failingChecks || 0) > 0) {
|
|
4709
|
+
warnings.push(
|
|
4710
|
+
tr(lang, "warnings", "workflowPrRemoteChecksFailing", {
|
|
4711
|
+
count: prRemote?.failingChecks || 0
|
|
4712
|
+
})
|
|
4713
|
+
);
|
|
4714
|
+
}
|
|
4715
|
+
if ((prRemote?.pendingChecks || 0) > 0) {
|
|
4716
|
+
warnings.push(
|
|
4717
|
+
tr(lang, "warnings", "workflowPrRemoteChecksPending", {
|
|
4718
|
+
count: prRemote?.pendingChecks || 0
|
|
4719
|
+
})
|
|
4720
|
+
);
|
|
4721
|
+
}
|
|
4521
4722
|
const tasksDocApproved = !tasksDocStatusFieldExists || tasksDocStatus === "Approved";
|
|
4522
4723
|
const implementationDone = tasksExists && tasksSummary.total > 0 && tasksSummary.total === tasksSummary.done && isCompletionChecklistDone2({ completionChecklist }) && tasksDocApproved;
|
|
4523
4724
|
const workflowDone = implementationDone && !docsHasUncommittedChanges && !projectHasUncommittedChanges && specStatus === "Approved" && planStatus === "Approved" && (!workflowPolicy.requireIssue || !!issueNumber) && (!workflowPolicy.requirePr || isPrMetadataConfigured2({ docs: { prFieldExists, prStatusFieldExists } }) && !!prLink) && (!workflowPolicy.requireReview || prStatus === "Approved") && isPrePrReviewSatisfied2(
|
|
@@ -4554,6 +4755,14 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4554
4755
|
if (!prStatus) warnings.push(tr(lang, "warnings", "workflowPrStatusMissing"));
|
|
4555
4756
|
if (prStatus && prStatus !== "Approved") {
|
|
4556
4757
|
warnings.push(tr(lang, "warnings", "workflowPrStatusNotApproved"));
|
|
4758
|
+
if (prStatus === "Review") {
|
|
4759
|
+
if (!prReviewFindingsFieldExists || !prReviewFindings) {
|
|
4760
|
+
warnings.push(tr(lang, "warnings", "workflowPrReviewFindingsMissing"));
|
|
4761
|
+
}
|
|
4762
|
+
if ((!prReviewEvidenceFieldExists || !prReviewEvidenceProvided) && (prReviewFindings?.major || 0) + (prReviewFindings?.minor || 0) > 0) {
|
|
4763
|
+
warnings.push(tr(lang, "warnings", "workflowPrReviewEvidenceMissing"));
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4557
4766
|
}
|
|
4558
4767
|
}
|
|
4559
4768
|
}
|
|
@@ -4606,7 +4815,12 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4606
4815
|
evidence: prePrEvidence,
|
|
4607
4816
|
evidenceProvided: prePrEvidenceProvided
|
|
4608
4817
|
},
|
|
4609
|
-
|
|
4818
|
+
prReview: {
|
|
4819
|
+
findings: prReviewFindings,
|
|
4820
|
+
evidence: prReviewEvidence,
|
|
4821
|
+
evidenceProvided: prReviewEvidenceProvided
|
|
4822
|
+
},
|
|
4823
|
+
pr: { link: prLink, status: prStatus, remote: prRemote },
|
|
4610
4824
|
git: {
|
|
4611
4825
|
docsBranch: context.docsBranch,
|
|
4612
4826
|
projectBranch: context.projectBranch,
|
|
@@ -4638,7 +4852,9 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4638
4852
|
prStatusFieldExists,
|
|
4639
4853
|
prePrReviewFieldExists,
|
|
4640
4854
|
prePrFindingsFieldExists,
|
|
4641
|
-
prePrEvidenceFieldExists
|
|
4855
|
+
prePrEvidenceFieldExists,
|
|
4856
|
+
prReviewFindingsFieldExists,
|
|
4857
|
+
prReviewEvidenceFieldExists
|
|
4642
4858
|
}
|
|
4643
4859
|
};
|
|
4644
4860
|
const { currentStep, actions, nextAction } = resolveFeatureProgress(
|
|
@@ -4783,7 +4999,7 @@ async function scanFeatures(config) {
|
|
|
4783
4999
|
}
|
|
4784
5000
|
}
|
|
4785
5001
|
const relativeFeaturePaths = allFeatureDirs.map(
|
|
4786
|
-
(dir) => normalizeRelPath(
|
|
5002
|
+
(dir) => normalizeRelPath(path19.relative(config.docsDir, dir))
|
|
4787
5003
|
);
|
|
4788
5004
|
const docsGitMeta = buildDocsFeatureGitMeta(config.docsDir, relativeFeaturePaths);
|
|
4789
5005
|
const parseTargets = config.projectType === "single" ? [{ type: "single", dirs: componentFeatureDirs.get("single") || [] }] : resolveProjectComponents(config.projectType, config.components).map((component) => ({
|
|
@@ -4794,7 +5010,7 @@ async function scanFeatures(config) {
|
|
|
4794
5010
|
const parsed = await Promise.all(
|
|
4795
5011
|
target.dirs.map(async (dir) => {
|
|
4796
5012
|
const relativeFeaturePathFromDocs = normalizeRelPath(
|
|
4797
|
-
|
|
5013
|
+
path19.relative(config.docsDir, dir)
|
|
4798
5014
|
);
|
|
4799
5015
|
const docsMeta = docsGitMeta.get(relativeFeaturePathFromDocs);
|
|
4800
5016
|
return parseFeature(
|
|
@@ -4863,13 +5079,13 @@ async function runStatus(options) {
|
|
|
4863
5079
|
);
|
|
4864
5080
|
}
|
|
4865
5081
|
const { docsDir, projectType, projectName, lang } = config;
|
|
4866
|
-
const featuresDir =
|
|
5082
|
+
const featuresDir = path19.join(docsDir, "features");
|
|
4867
5083
|
const scan = await scanFeatures(config);
|
|
4868
5084
|
const features = [];
|
|
4869
5085
|
const idMap = /* @__PURE__ */ new Map();
|
|
4870
5086
|
for (const f of scan.features) {
|
|
4871
5087
|
const id = f.id || "UNKNOWN";
|
|
4872
|
-
const relPath =
|
|
5088
|
+
const relPath = path19.relative(docsDir, f.path);
|
|
4873
5089
|
if (!idMap.has(id)) idMap.set(id, []);
|
|
4874
5090
|
idMap.get(id).push(relPath);
|
|
4875
5091
|
if (!f.docs.specExists || !f.docs.tasksExists) continue;
|
|
@@ -4950,7 +5166,7 @@ async function runStatus(options) {
|
|
|
4950
5166
|
}
|
|
4951
5167
|
console.log();
|
|
4952
5168
|
if (options.write) {
|
|
4953
|
-
const outputPath =
|
|
5169
|
+
const outputPath = path19.join(featuresDir, "status.md");
|
|
4954
5170
|
const date = getLocalDateString();
|
|
4955
5171
|
const content = [
|
|
4956
5172
|
"# Feature Status",
|
|
@@ -4978,7 +5194,7 @@ function escapeRegExp2(value) {
|
|
|
4978
5194
|
}
|
|
4979
5195
|
async function getFeatureNameFromSpec(featureDir, fallbackSlug, fallbackFolderName) {
|
|
4980
5196
|
try {
|
|
4981
|
-
const specPath =
|
|
5197
|
+
const specPath = path19.join(featureDir, "spec.md");
|
|
4982
5198
|
if (!await fs15.pathExists(specPath)) return fallbackSlug;
|
|
4983
5199
|
const content = await fs15.readFile(specPath, "utf-8");
|
|
4984
5200
|
const keys = ["\uAE30\uB2A5\uBA85", "Feature Name"];
|
|
@@ -5060,10 +5276,10 @@ async function runUpdate(options) {
|
|
|
5060
5276
|
console.log(chalk6.blue(tr(lang, "cli", "update.updatingAgents")));
|
|
5061
5277
|
}
|
|
5062
5278
|
if (agentsMode === "all") {
|
|
5063
|
-
const commonAgentsBase =
|
|
5064
|
-
const targetAgentsBase =
|
|
5065
|
-
const commonAgents = agentsMode === "skills" ?
|
|
5066
|
-
const targetAgents = agentsMode === "skills" ?
|
|
5279
|
+
const commonAgentsBase = path19.join(templatesDir, lang, "common", "agents");
|
|
5280
|
+
const targetAgentsBase = path19.join(docsDir, "agents");
|
|
5281
|
+
const commonAgents = agentsMode === "skills" ? path19.join(commonAgentsBase, "skills") : commonAgentsBase;
|
|
5282
|
+
const targetAgents = agentsMode === "skills" ? path19.join(targetAgentsBase, "skills") : targetAgentsBase;
|
|
5067
5283
|
const featurePath = projectType === "multi" ? "docs/features/{component}" : "docs/features";
|
|
5068
5284
|
const projectName = config.projectName ?? "{{projectName}}";
|
|
5069
5285
|
const commonReplacements = {
|
|
@@ -5143,7 +5359,7 @@ function normalizeSkillList2(raw) {
|
|
|
5143
5359
|
return [...deduped];
|
|
5144
5360
|
}
|
|
5145
5361
|
async function backfillMissingConfigDefaults(docsDir) {
|
|
5146
|
-
const configPath =
|
|
5362
|
+
const configPath = path19.join(docsDir, ".lee-spec-kit.json");
|
|
5147
5363
|
if (!await fs15.pathExists(configPath)) {
|
|
5148
5364
|
return { changed: false, changedPaths: [] };
|
|
5149
5365
|
}
|
|
@@ -5216,8 +5432,8 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
|
|
|
5216
5432
|
const files = await fs15.readdir(sourceDir);
|
|
5217
5433
|
let updatedCount = 0;
|
|
5218
5434
|
for (const file of files) {
|
|
5219
|
-
const sourcePath =
|
|
5220
|
-
const targetPath =
|
|
5435
|
+
const sourcePath = path19.join(sourceDir, file);
|
|
5436
|
+
const targetPath = path19.join(targetDir, file);
|
|
5221
5437
|
const stat = await fs15.stat(sourcePath);
|
|
5222
5438
|
if (stat.isFile()) {
|
|
5223
5439
|
if (protectedFiles.has(file)) {
|
|
@@ -5300,7 +5516,7 @@ function extractPorcelainPaths(line) {
|
|
|
5300
5516
|
function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
|
|
5301
5517
|
const top = getGitTopLevel2(docsDir);
|
|
5302
5518
|
if (!top) return null;
|
|
5303
|
-
const rel =
|
|
5519
|
+
const rel = path19.relative(top, docsDir) || ".";
|
|
5304
5520
|
try {
|
|
5305
5521
|
const output = execFileSync("git", ["status", "--porcelain=v1", "--", rel], {
|
|
5306
5522
|
cwd: top,
|
|
@@ -5312,7 +5528,7 @@ function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
|
|
|
5312
5528
|
}
|
|
5313
5529
|
const ignoredRelPaths = new Set(
|
|
5314
5530
|
ignoredAbsPaths.map(
|
|
5315
|
-
(absPath) => normalizeGitPath2(
|
|
5531
|
+
(absPath) => normalizeGitPath2(path19.relative(top, absPath) || ".")
|
|
5316
5532
|
)
|
|
5317
5533
|
);
|
|
5318
5534
|
const filtered = output.split("\n").filter((line) => {
|
|
@@ -5369,7 +5585,7 @@ ${tr(lang2, "cli", "common.canceled")}`));
|
|
|
5369
5585
|
}
|
|
5370
5586
|
async function runConfig(options) {
|
|
5371
5587
|
const cwd = process.cwd();
|
|
5372
|
-
const targetCwd = options.dir ?
|
|
5588
|
+
const targetCwd = options.dir ? path19.resolve(cwd, options.dir) : cwd;
|
|
5373
5589
|
const config = await getConfig(targetCwd);
|
|
5374
5590
|
if (!config) {
|
|
5375
5591
|
throw createCliError(
|
|
@@ -5377,7 +5593,7 @@ async function runConfig(options) {
|
|
|
5377
5593
|
tr(DEFAULT_LANG, "cli", "common.configNotFound")
|
|
5378
5594
|
);
|
|
5379
5595
|
}
|
|
5380
|
-
const configPath =
|
|
5596
|
+
const configPath = path19.join(config.docsDir, ".lee-spec-kit.json");
|
|
5381
5597
|
if (!options.projectRoot) {
|
|
5382
5598
|
console.log();
|
|
5383
5599
|
console.log(chalk6.blue(tr(config.lang, "cli", "config.currentTitle")));
|
|
@@ -5743,42 +5959,42 @@ var BUILTIN_DOC_DEFINITIONS = [
|
|
|
5743
5959
|
{
|
|
5744
5960
|
id: "agents",
|
|
5745
5961
|
title: { ko: "\uC5D0\uC774\uC804\uD2B8 \uC6B4\uC601 \uADDC\uCE59", en: "Agent Operating Rules" },
|
|
5746
|
-
relativePath: (_, lang) =>
|
|
5962
|
+
relativePath: (_, lang) => path19.join(lang, "common", "agents", "agents.md")
|
|
5747
5963
|
},
|
|
5748
5964
|
{
|
|
5749
5965
|
id: "git-workflow",
|
|
5750
5966
|
title: { ko: "Git \uC6CC\uD06C\uD50C\uB85C\uC6B0", en: "Git Workflow" },
|
|
5751
|
-
relativePath: (_, lang) =>
|
|
5967
|
+
relativePath: (_, lang) => path19.join(lang, "common", "agents", "git-workflow.md")
|
|
5752
5968
|
},
|
|
5753
5969
|
{
|
|
5754
5970
|
id: "issue-doc",
|
|
5755
5971
|
title: { ko: "Issue \uBB38\uC11C \uD15C\uD50C\uB9BF", en: "Issue Document Template" },
|
|
5756
|
-
relativePath: (_, lang) =>
|
|
5972
|
+
relativePath: (_, lang) => path19.join(lang, "common", "features", "feature-base", "issue.md")
|
|
5757
5973
|
},
|
|
5758
5974
|
{
|
|
5759
5975
|
id: "pr-doc",
|
|
5760
5976
|
title: { ko: "PR \uBB38\uC11C \uD15C\uD50C\uB9BF", en: "PR Document Template" },
|
|
5761
|
-
relativePath: (_, lang) =>
|
|
5977
|
+
relativePath: (_, lang) => path19.join(lang, "common", "features", "feature-base", "pr.md")
|
|
5762
5978
|
},
|
|
5763
5979
|
{
|
|
5764
5980
|
id: "create-feature",
|
|
5765
5981
|
title: { ko: "create-feature \uC2A4\uD0AC", en: "create-feature skill" },
|
|
5766
|
-
relativePath: (_, lang) =>
|
|
5982
|
+
relativePath: (_, lang) => path19.join(lang, "common", "agents", "skills", "create-feature.md")
|
|
5767
5983
|
},
|
|
5768
5984
|
{
|
|
5769
5985
|
id: "execute-task",
|
|
5770
5986
|
title: { ko: "execute-task \uC2A4\uD0AC", en: "execute-task skill" },
|
|
5771
|
-
relativePath: (_, lang) =>
|
|
5987
|
+
relativePath: (_, lang) => path19.join(lang, "common", "agents", "skills", "execute-task.md")
|
|
5772
5988
|
},
|
|
5773
5989
|
{
|
|
5774
5990
|
id: "create-issue",
|
|
5775
5991
|
title: { ko: "create-issue \uC2A4\uD0AC", en: "create-issue skill" },
|
|
5776
|
-
relativePath: (_, lang) =>
|
|
5992
|
+
relativePath: (_, lang) => path19.join(lang, "common", "agents", "skills", "create-issue.md")
|
|
5777
5993
|
},
|
|
5778
5994
|
{
|
|
5779
5995
|
id: "create-pr",
|
|
5780
5996
|
title: { ko: "create-pr \uC2A4\uD0AC", en: "create-pr skill" },
|
|
5781
|
-
relativePath: (_, lang) =>
|
|
5997
|
+
relativePath: (_, lang) => path19.join(lang, "common", "agents", "skills", "create-pr.md")
|
|
5782
5998
|
}
|
|
5783
5999
|
];
|
|
5784
6000
|
var DOC_FOLLOWUPS = {
|
|
@@ -5865,7 +6081,7 @@ function listBuiltinDocs(projectType, lang) {
|
|
|
5865
6081
|
id: doc.id,
|
|
5866
6082
|
title: doc.title[lang],
|
|
5867
6083
|
relativePath,
|
|
5868
|
-
absolutePath:
|
|
6084
|
+
absolutePath: path19.join(templatesDir, relativePath)
|
|
5869
6085
|
};
|
|
5870
6086
|
});
|
|
5871
6087
|
}
|
|
@@ -5914,7 +6130,7 @@ function parseApprovalLabel(input, validLabels) {
|
|
|
5914
6130
|
function getApprovalTicketPaths(config) {
|
|
5915
6131
|
return {
|
|
5916
6132
|
runtimePath: getApprovalTicketStorePath(config.docsDir),
|
|
5917
|
-
legacyPath:
|
|
6133
|
+
legacyPath: path19.join(config.docsDir, LEGACY_APPROVAL_TICKET_FILENAME)
|
|
5918
6134
|
};
|
|
5919
6135
|
}
|
|
5920
6136
|
function getApprovalSessionId() {
|
|
@@ -5942,7 +6158,7 @@ async function loadApprovalTicketStore(storePath) {
|
|
|
5942
6158
|
}
|
|
5943
6159
|
}
|
|
5944
6160
|
async function saveApprovalTicketStore(storePath, payload) {
|
|
5945
|
-
await fs15.ensureDir(
|
|
6161
|
+
await fs15.ensureDir(path19.dirname(storePath));
|
|
5946
6162
|
await fs15.writeJson(storePath, payload, { spaces: 2 });
|
|
5947
6163
|
}
|
|
5948
6164
|
async function resolveApprovalTicketStoreAndPath(config, nowMs) {
|
|
@@ -6355,6 +6571,12 @@ function getListLabel(f, stepsMap, lang, workflowPolicy, prePrReviewPolicy) {
|
|
|
6355
6571
|
if (workflowPolicy.requireReview && !f.pr.status) {
|
|
6356
6572
|
return tr(lang, "cli", "context.list.setPrStatus");
|
|
6357
6573
|
}
|
|
6574
|
+
if (workflowPolicy.requireReview && f.pr.status === "Review" && (!f.docs.prReviewFindingsFieldExists || !f.prReview.findings)) {
|
|
6575
|
+
return tr(lang, "cli", "context.list.addPrReviewFindings");
|
|
6576
|
+
}
|
|
6577
|
+
if (workflowPolicy.requireReview && f.pr.status === "Review" && (!f.docs.prReviewEvidenceFieldExists || (f.prReview.findings?.major || 0) + (f.prReview.findings?.minor || 0) > 0 && !f.prReview.evidenceProvided)) {
|
|
6578
|
+
return tr(lang, "cli", "context.list.addPrReviewEvidence");
|
|
6579
|
+
}
|
|
6358
6580
|
if (workflowPolicy.requireReview && f.pr.status !== "Approved") {
|
|
6359
6581
|
return tr(lang, "cli", "context.list.prStatusToApproved", {
|
|
6360
6582
|
status: f.pr.status
|
|
@@ -6734,7 +6956,7 @@ async function runContext(featureName, options) {
|
|
|
6734
6956
|
if (f.issueNumber) {
|
|
6735
6957
|
console.log(` \u2022 Issue: #${f.issueNumber}`);
|
|
6736
6958
|
}
|
|
6737
|
-
console.log(` \u2022 Path: ${
|
|
6959
|
+
console.log(` \u2022 Path: ${path19.relative(cwd, f.path)}`);
|
|
6738
6960
|
if (f.git.projectBranch) {
|
|
6739
6961
|
console.log(` \u2022 Project Branch: ${f.git.projectBranch}`);
|
|
6740
6962
|
}
|
|
@@ -7063,7 +7285,7 @@ var FIXABLE_ISSUE_CODES = /* @__PURE__ */ new Set([
|
|
|
7063
7285
|
]);
|
|
7064
7286
|
function formatPath(cwd, p) {
|
|
7065
7287
|
if (!p) return "";
|
|
7066
|
-
return
|
|
7288
|
+
return path19.isAbsolute(p) ? path19.relative(cwd, p) : p;
|
|
7067
7289
|
}
|
|
7068
7290
|
function detectPlaceholders(content) {
|
|
7069
7291
|
const patterns = [
|
|
@@ -7212,7 +7434,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
|
|
|
7212
7434
|
const placeholderContext = {
|
|
7213
7435
|
projectName: config.projectName,
|
|
7214
7436
|
featureName: f.slug,
|
|
7215
|
-
featurePath: f.docs.featurePathFromDocs ||
|
|
7437
|
+
featurePath: f.docs.featurePathFromDocs || path19.relative(config.docsDir, f.path),
|
|
7216
7438
|
repoType: f.type,
|
|
7217
7439
|
featureNumber
|
|
7218
7440
|
};
|
|
@@ -7222,7 +7444,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
|
|
|
7222
7444
|
"tasks.md"
|
|
7223
7445
|
];
|
|
7224
7446
|
for (const file of files) {
|
|
7225
|
-
const fullPath =
|
|
7447
|
+
const fullPath = path19.join(f.path, file);
|
|
7226
7448
|
if (!await fs15.pathExists(fullPath)) continue;
|
|
7227
7449
|
const original = await fs15.readFile(fullPath, "utf-8");
|
|
7228
7450
|
let next = original;
|
|
@@ -7267,7 +7489,7 @@ async function checkDocsStructure(config, cwd) {
|
|
|
7267
7489
|
const issues = [];
|
|
7268
7490
|
const requiredDirs = ["agents", "features", "prd", "designs", "ideas"];
|
|
7269
7491
|
for (const dir of requiredDirs) {
|
|
7270
|
-
const p =
|
|
7492
|
+
const p = path19.join(config.docsDir, dir);
|
|
7271
7493
|
if (!await fs15.pathExists(p)) {
|
|
7272
7494
|
issues.push({
|
|
7273
7495
|
level: "error",
|
|
@@ -7277,7 +7499,7 @@ async function checkDocsStructure(config, cwd) {
|
|
|
7277
7499
|
});
|
|
7278
7500
|
}
|
|
7279
7501
|
}
|
|
7280
|
-
const configPath =
|
|
7502
|
+
const configPath = path19.join(config.docsDir, ".lee-spec-kit.json");
|
|
7281
7503
|
if (!await fs15.pathExists(configPath)) {
|
|
7282
7504
|
issues.push({
|
|
7283
7505
|
level: "warn",
|
|
@@ -7300,7 +7522,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
7300
7522
|
}
|
|
7301
7523
|
const idMap = /* @__PURE__ */ new Map();
|
|
7302
7524
|
for (const f of features) {
|
|
7303
|
-
const rel = f.docs.featurePathFromDocs ||
|
|
7525
|
+
const rel = f.docs.featurePathFromDocs || path19.relative(config.docsDir, f.path);
|
|
7304
7526
|
const id = f.id || "UNKNOWN";
|
|
7305
7527
|
if (!idMap.has(id)) idMap.set(id, []);
|
|
7306
7528
|
idMap.get(id).push(rel);
|
|
@@ -7308,7 +7530,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
7308
7530
|
if (!isInitialTemplateState) {
|
|
7309
7531
|
const featureDocs = ["spec.md", "plan.md", "tasks.md"];
|
|
7310
7532
|
for (const file of featureDocs) {
|
|
7311
|
-
const p =
|
|
7533
|
+
const p = path19.join(f.path, file);
|
|
7312
7534
|
if (!await fs15.pathExists(p)) continue;
|
|
7313
7535
|
const content = await fs15.readFile(p, "utf-8");
|
|
7314
7536
|
const placeholders = detectPlaceholders(content);
|
|
@@ -7323,7 +7545,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
7323
7545
|
});
|
|
7324
7546
|
}
|
|
7325
7547
|
if (decisionsPlaceholderMode !== "off") {
|
|
7326
|
-
const decisionsPath =
|
|
7548
|
+
const decisionsPath = path19.join(f.path, "decisions.md");
|
|
7327
7549
|
if (await fs15.pathExists(decisionsPath)) {
|
|
7328
7550
|
const content = await fs15.readFile(decisionsPath, "utf-8");
|
|
7329
7551
|
const placeholders = detectPlaceholders(content);
|
|
@@ -7352,7 +7574,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
7352
7574
|
level: "warn",
|
|
7353
7575
|
code: "spec_status_unset",
|
|
7354
7576
|
message: tr(config.lang, "cli", "doctor.issue.specStatusUnset"),
|
|
7355
|
-
path: formatPath(cwd,
|
|
7577
|
+
path: formatPath(cwd, path19.join(f.path, "spec.md"))
|
|
7356
7578
|
});
|
|
7357
7579
|
}
|
|
7358
7580
|
if (f.docs.planExists && !f.planStatus && !isInitialTemplateState) {
|
|
@@ -7360,7 +7582,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
7360
7582
|
level: "warn",
|
|
7361
7583
|
code: "plan_status_unset",
|
|
7362
7584
|
message: tr(config.lang, "cli", "doctor.issue.planStatusUnset"),
|
|
7363
|
-
path: formatPath(cwd,
|
|
7585
|
+
path: formatPath(cwd, path19.join(f.path, "plan.md"))
|
|
7364
7586
|
});
|
|
7365
7587
|
}
|
|
7366
7588
|
if (f.docs.tasksExists && f.tasks.total === 0 && !isInitialTemplateState) {
|
|
@@ -7368,7 +7590,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
7368
7590
|
level: "warn",
|
|
7369
7591
|
code: "tasks_empty",
|
|
7370
7592
|
message: tr(config.lang, "cli", "doctor.issue.tasksEmpty"),
|
|
7371
|
-
path: formatPath(cwd,
|
|
7593
|
+
path: formatPath(cwd, path19.join(f.path, "tasks.md"))
|
|
7372
7594
|
});
|
|
7373
7595
|
}
|
|
7374
7596
|
if (f.docs.tasksExists && !f.docs.tasksDocStatusFieldExists && !isInitialTemplateState) {
|
|
@@ -7376,7 +7598,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
7376
7598
|
level: "warn",
|
|
7377
7599
|
code: "tasks_doc_status_missing",
|
|
7378
7600
|
message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusMissing"),
|
|
7379
|
-
path: formatPath(cwd,
|
|
7601
|
+
path: formatPath(cwd, path19.join(f.path, "tasks.md"))
|
|
7380
7602
|
});
|
|
7381
7603
|
}
|
|
7382
7604
|
if (f.docs.tasksExists && f.docs.tasksDocStatusFieldExists && !f.tasksDocStatus && !isInitialTemplateState) {
|
|
@@ -7384,7 +7606,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
7384
7606
|
level: "warn",
|
|
7385
7607
|
code: "tasks_doc_status_unset",
|
|
7386
7608
|
message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusUnset"),
|
|
7387
|
-
path: formatPath(cwd,
|
|
7609
|
+
path: formatPath(cwd, path19.join(f.path, "tasks.md"))
|
|
7388
7610
|
});
|
|
7389
7611
|
}
|
|
7390
7612
|
}
|
|
@@ -7408,7 +7630,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
7408
7630
|
level: "warn",
|
|
7409
7631
|
code: "missing_feature_id",
|
|
7410
7632
|
message: tr(config.lang, "cli", "doctor.issue.missingFeatureId"),
|
|
7411
|
-
path: formatPath(cwd,
|
|
7633
|
+
path: formatPath(cwd, path19.join(config.docsDir, p))
|
|
7412
7634
|
});
|
|
7413
7635
|
}
|
|
7414
7636
|
return issues;
|
|
@@ -7530,7 +7752,7 @@ function doctorCommand(program2) {
|
|
|
7530
7752
|
}
|
|
7531
7753
|
console.log();
|
|
7532
7754
|
console.log(chalk6.bold(tr(lang, "cli", "doctor.title")));
|
|
7533
|
-
console.log(chalk6.gray(`- Docs: ${
|
|
7755
|
+
console.log(chalk6.gray(`- Docs: ${path19.relative(cwd, docsDir)}`));
|
|
7534
7756
|
console.log(chalk6.gray(`- Type: ${projectType}`));
|
|
7535
7757
|
console.log(chalk6.gray(`- Lang: ${lang}`));
|
|
7536
7758
|
console.log();
|
|
@@ -7703,7 +7925,7 @@ async function runView(featureName, options) {
|
|
|
7703
7925
|
}
|
|
7704
7926
|
console.log();
|
|
7705
7927
|
console.log(chalk6.bold("\u{1F4CA} Workflow View"));
|
|
7706
|
-
console.log(chalk6.gray(`- Docs: ${
|
|
7928
|
+
console.log(chalk6.gray(`- Docs: ${path19.relative(cwd, config.docsDir)}`));
|
|
7707
7929
|
console.log(
|
|
7708
7930
|
chalk6.gray(
|
|
7709
7931
|
`- Features: ${state.features.length} (open ${state.openFeatures.length} / done ${state.doneFeatures.length})`
|
|
@@ -8076,25 +8298,25 @@ function tg(lang, key, vars = {}) {
|
|
|
8076
8298
|
}
|
|
8077
8299
|
function detectGithubCliLangSync(cwd) {
|
|
8078
8300
|
const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
|
|
8079
|
-
const startDirs = [explicitDocsDir ?
|
|
8301
|
+
const startDirs = [explicitDocsDir ? path19.resolve(explicitDocsDir) : "", path19.resolve(cwd)].filter(Boolean);
|
|
8080
8302
|
const scanOrder = [];
|
|
8081
8303
|
const seen = /* @__PURE__ */ new Set();
|
|
8082
8304
|
for (const start of startDirs) {
|
|
8083
8305
|
let current = start;
|
|
8084
8306
|
while (true) {
|
|
8085
|
-
const abs =
|
|
8307
|
+
const abs = path19.resolve(current);
|
|
8086
8308
|
if (!seen.has(abs)) {
|
|
8087
8309
|
scanOrder.push(abs);
|
|
8088
8310
|
seen.add(abs);
|
|
8089
8311
|
}
|
|
8090
|
-
const parent =
|
|
8312
|
+
const parent = path19.dirname(abs);
|
|
8091
8313
|
if (parent === abs) break;
|
|
8092
8314
|
current = parent;
|
|
8093
8315
|
}
|
|
8094
8316
|
}
|
|
8095
8317
|
for (const base of scanOrder) {
|
|
8096
|
-
for (const docsDir of [
|
|
8097
|
-
const configPath =
|
|
8318
|
+
for (const docsDir of [path19.join(base, "docs"), base]) {
|
|
8319
|
+
const configPath = path19.join(docsDir, ".lee-spec-kit.json");
|
|
8098
8320
|
if (fs15.existsSync(configPath)) {
|
|
8099
8321
|
try {
|
|
8100
8322
|
const parsed = fs15.readJsonSync(configPath);
|
|
@@ -8102,11 +8324,11 @@ function detectGithubCliLangSync(cwd) {
|
|
|
8102
8324
|
} catch {
|
|
8103
8325
|
}
|
|
8104
8326
|
}
|
|
8105
|
-
const agentsPath =
|
|
8106
|
-
const featuresPath =
|
|
8327
|
+
const agentsPath = path19.join(docsDir, "agents");
|
|
8328
|
+
const featuresPath = path19.join(docsDir, "features");
|
|
8107
8329
|
if (!fs15.existsSync(agentsPath) || !fs15.existsSync(featuresPath)) continue;
|
|
8108
8330
|
for (const probe of ["custom.md", "constitution.md", "agents.md"]) {
|
|
8109
|
-
const file =
|
|
8331
|
+
const file = path19.join(agentsPath, probe);
|
|
8110
8332
|
if (!fs15.existsSync(file)) continue;
|
|
8111
8333
|
try {
|
|
8112
8334
|
const content = fs15.readFileSync(file, "utf-8");
|
|
@@ -8204,7 +8426,7 @@ function ensureSections(body, sections, kind, lang) {
|
|
|
8204
8426
|
}
|
|
8205
8427
|
function ensureDocsExist(docsDir, relativePaths, lang) {
|
|
8206
8428
|
const missing = relativePaths.filter(
|
|
8207
|
-
(relativePath) => !fs15.existsSync(
|
|
8429
|
+
(relativePath) => !fs15.existsSync(path19.join(docsDir, relativePath))
|
|
8208
8430
|
);
|
|
8209
8431
|
if (missing.length > 0) {
|
|
8210
8432
|
throw createCliError(
|
|
@@ -8214,13 +8436,13 @@ function ensureDocsExist(docsDir, relativePaths, lang) {
|
|
|
8214
8436
|
}
|
|
8215
8437
|
}
|
|
8216
8438
|
function buildDefaultBodyFileName(kind, docsDir, component) {
|
|
8217
|
-
const key = `${
|
|
8439
|
+
const key = `${path19.resolve(docsDir)}::${component.trim().toLowerCase()}`;
|
|
8218
8440
|
const digest = createHash("sha1").update(key).digest("hex").slice(0, 12);
|
|
8219
8441
|
return `lee-spec-kit.${digest}.${kind}.md`;
|
|
8220
8442
|
}
|
|
8221
8443
|
function toBodyFilePath(raw, kind, docsDir, component) {
|
|
8222
|
-
const selected = raw?.trim() ||
|
|
8223
|
-
return
|
|
8444
|
+
const selected = raw?.trim() || path19.join(os.tmpdir(), buildDefaultBodyFileName(kind, docsDir, component));
|
|
8445
|
+
return path19.resolve(selected);
|
|
8224
8446
|
}
|
|
8225
8447
|
function toProjectRootDocsPath(relativePathFromDocs) {
|
|
8226
8448
|
const normalized = relativePathFromDocs.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
|
|
@@ -9040,7 +9262,7 @@ function ensureCleanWorktree(cwd, lang) {
|
|
|
9040
9262
|
}
|
|
9041
9263
|
}
|
|
9042
9264
|
function commitAndPushPath(cwd, absPath, message, lang) {
|
|
9043
|
-
const relativePath =
|
|
9265
|
+
const relativePath = path19.relative(cwd, absPath) || absPath;
|
|
9044
9266
|
const status = runProcessOrThrow(
|
|
9045
9267
|
"git",
|
|
9046
9268
|
["status", "--porcelain=v1", "--", relativePath],
|
|
@@ -9172,9 +9394,9 @@ function githubCommand(program2) {
|
|
|
9172
9394
|
[paths.specPath, paths.planPath, paths.tasksPath],
|
|
9173
9395
|
config.lang
|
|
9174
9396
|
);
|
|
9175
|
-
const specContent = await fs15.readFile(
|
|
9176
|
-
const planContent = await fs15.readFile(
|
|
9177
|
-
const tasksContent = await fs15.readFile(
|
|
9397
|
+
const specContent = await fs15.readFile(path19.join(config.docsDir, paths.specPath), "utf-8");
|
|
9398
|
+
const planContent = await fs15.readFile(path19.join(config.docsDir, paths.planPath), "utf-8");
|
|
9399
|
+
const tasksContent = await fs15.readFile(path19.join(config.docsDir, paths.tasksPath), "utf-8");
|
|
9178
9400
|
const overview = resolveOverviewFromSpec(specContent, feature, config.lang);
|
|
9179
9401
|
const title = options.title?.trim() || tg(config.lang, "issueDefaultTitle", {
|
|
9180
9402
|
slug: feature.slug,
|
|
@@ -9212,7 +9434,7 @@ function githubCommand(program2) {
|
|
|
9212
9434
|
config.lang
|
|
9213
9435
|
);
|
|
9214
9436
|
} else {
|
|
9215
|
-
await fs15.ensureDir(
|
|
9437
|
+
await fs15.ensureDir(path19.dirname(bodyFile));
|
|
9216
9438
|
await fs15.writeFile(bodyFile, generatedBody, "utf-8");
|
|
9217
9439
|
}
|
|
9218
9440
|
let issueUrl;
|
|
@@ -9311,10 +9533,10 @@ function githubCommand(program2) {
|
|
|
9311
9533
|
const labels = parseLabels(options.labels, config.lang);
|
|
9312
9534
|
const paths = getFeatureDocPaths(feature);
|
|
9313
9535
|
ensureDocsExist(config.docsDir, [paths.specPath, paths.tasksPath], config.lang);
|
|
9314
|
-
const specContent = await fs15.readFile(
|
|
9315
|
-
const planPath =
|
|
9536
|
+
const specContent = await fs15.readFile(path19.join(config.docsDir, paths.specPath), "utf-8");
|
|
9537
|
+
const planPath = path19.join(config.docsDir, paths.planPath);
|
|
9316
9538
|
const planContent = await fs15.pathExists(planPath) ? await fs15.readFile(planPath, "utf-8") : "";
|
|
9317
|
-
const tasksContent = await fs15.readFile(
|
|
9539
|
+
const tasksContent = await fs15.readFile(path19.join(config.docsDir, paths.tasksPath), "utf-8");
|
|
9318
9540
|
const overview = resolveOverviewFromSpec(specContent, feature, config.lang);
|
|
9319
9541
|
const defaultTitle = feature.issueNumber ? tg(config.lang, "prDefaultTitleWithIssue", {
|
|
9320
9542
|
issue: feature.issueNumber,
|
|
@@ -9357,7 +9579,7 @@ function githubCommand(program2) {
|
|
|
9357
9579
|
config.lang
|
|
9358
9580
|
);
|
|
9359
9581
|
} else {
|
|
9360
|
-
await fs15.ensureDir(
|
|
9582
|
+
await fs15.ensureDir(path19.dirname(bodyFile));
|
|
9361
9583
|
await fs15.writeFile(bodyFile, generatedBody, "utf-8");
|
|
9362
9584
|
}
|
|
9363
9585
|
const retryCount = toRetryCount(options.retry, config.lang);
|
|
@@ -9411,7 +9633,7 @@ function githubCommand(program2) {
|
|
|
9411
9633
|
}
|
|
9412
9634
|
if (prUrl && options.syncTasks !== false) {
|
|
9413
9635
|
const synced = syncTasksPrMetadata(
|
|
9414
|
-
|
|
9636
|
+
path19.join(config.docsDir, paths.tasksPath),
|
|
9415
9637
|
prUrl,
|
|
9416
9638
|
"Review",
|
|
9417
9639
|
config.lang
|
|
@@ -9458,7 +9680,7 @@ function githubCommand(program2) {
|
|
|
9458
9680
|
);
|
|
9459
9681
|
if (prUrl && options.syncTasks !== false) {
|
|
9460
9682
|
const mergedSync = syncTasksPrMetadata(
|
|
9461
|
-
|
|
9683
|
+
path19.join(config.docsDir, paths.tasksPath),
|
|
9462
9684
|
prUrl,
|
|
9463
9685
|
"Approved",
|
|
9464
9686
|
config.lang
|
|
@@ -9663,7 +9885,7 @@ function docsCommand(program2) {
|
|
|
9663
9885
|
);
|
|
9664
9886
|
return;
|
|
9665
9887
|
}
|
|
9666
|
-
const relativeFromCwd =
|
|
9888
|
+
const relativeFromCwd = path19.relative(process.cwd(), loaded.entry.absolutePath);
|
|
9667
9889
|
console.log();
|
|
9668
9890
|
console.log(chalk6.bold(`\u{1F4C4} ${loaded.entry.id}: ${loaded.entry.title}`));
|
|
9669
9891
|
console.log(
|
|
@@ -9741,7 +9963,7 @@ function detectCommand(program2) {
|
|
|
9741
9963
|
}
|
|
9742
9964
|
async function runDetect(options) {
|
|
9743
9965
|
const cwd = process.cwd();
|
|
9744
|
-
const targetCwd = options.dir ?
|
|
9966
|
+
const targetCwd = options.dir ? path19.resolve(cwd, options.dir) : cwd;
|
|
9745
9967
|
const config = await getConfig(targetCwd);
|
|
9746
9968
|
const detected = !!config;
|
|
9747
9969
|
const reasonCode = detected ? "PROJECT_DETECTED" : "PROJECT_NOT_DETECTED";
|
|
@@ -9768,7 +9990,7 @@ async function runDetect(options) {
|
|
|
9768
9990
|
);
|
|
9769
9991
|
return;
|
|
9770
9992
|
}
|
|
9771
|
-
const configPath2 =
|
|
9993
|
+
const configPath2 = path19.join(config.docsDir, ".lee-spec-kit.json");
|
|
9772
9994
|
const configFilePresent2 = await fs15.pathExists(configPath2);
|
|
9773
9995
|
const detectionSource2 = configFilePresent2 ? "config" : "heuristic";
|
|
9774
9996
|
console.log(
|
|
@@ -9802,7 +10024,7 @@ async function runDetect(options) {
|
|
|
9802
10024
|
console.log();
|
|
9803
10025
|
return;
|
|
9804
10026
|
}
|
|
9805
|
-
const configPath =
|
|
10027
|
+
const configPath = path19.join(config.docsDir, ".lee-spec-kit.json");
|
|
9806
10028
|
const configFilePresent = await fs15.pathExists(configPath);
|
|
9807
10029
|
const detectionSource = configFilePresent ? "config" : "heuristic";
|
|
9808
10030
|
console.log(chalk6.green(`- ${tr(lang, "cli", "detect.resultDetected")}`));
|
|
@@ -9879,11 +10101,11 @@ ${version}
|
|
|
9879
10101
|
}
|
|
9880
10102
|
return `${ascii}${footer}`;
|
|
9881
10103
|
}
|
|
9882
|
-
var CACHE_FILE =
|
|
10104
|
+
var CACHE_FILE = path19.join(os.homedir(), ".lee-spec-kit-version-cache.json");
|
|
9883
10105
|
var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
|
|
9884
10106
|
function getCurrentVersion() {
|
|
9885
10107
|
try {
|
|
9886
|
-
const packageJsonPath =
|
|
10108
|
+
const packageJsonPath = path19.join(__dirname$1, "..", "package.json");
|
|
9887
10109
|
if (fs15.existsSync(packageJsonPath)) {
|
|
9888
10110
|
const pkg = fs15.readJsonSync(packageJsonPath);
|
|
9889
10111
|
return pkg.version;
|
|
@@ -9987,7 +10209,7 @@ function shouldCheckForUpdates() {
|
|
|
9987
10209
|
if (shouldCheckForUpdates()) checkForUpdates();
|
|
9988
10210
|
function getCliVersion() {
|
|
9989
10211
|
try {
|
|
9990
|
-
const packageJsonPath =
|
|
10212
|
+
const packageJsonPath = path19.join(__dirname$1, "..", "package.json");
|
|
9991
10213
|
if (fs15.existsSync(packageJsonPath)) {
|
|
9992
10214
|
const pkg = fs15.readJsonSync(packageJsonPath);
|
|
9993
10215
|
if (pkg?.version) return String(pkg.version);
|