lee-spec-kit 0.5.2 → 0.6.1
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 +25 -4
- package/README.md +25 -11
- package/dist/index.js +526 -253
- package/package.json +1 -1
- package/templates/en/common/agents/skills/execute-task.md +1 -0
- package/templates/en/fullstack/features/feature-base/tasks.md +9 -3
- package/templates/en/single/features/feature-base/tasks.md +9 -3
- package/templates/ko/common/agents/skills/execute-task.md +4 -3
- package/templates/ko/fullstack/features/feature-base/tasks.md +8 -2
- package/templates/ko/single/features/feature-base/tasks.md +8 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import path6 from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { program } from 'commander';
|
|
5
5
|
import fs2 from 'fs-extra';
|
|
@@ -11,7 +11,7 @@ import { createHash } from 'crypto';
|
|
|
11
11
|
import os from 'os';
|
|
12
12
|
|
|
13
13
|
var getFilename = () => fileURLToPath(import.meta.url);
|
|
14
|
-
var getDirname = () =>
|
|
14
|
+
var getDirname = () => path6.dirname(getFilename());
|
|
15
15
|
var __dirname$1 = /* @__PURE__ */ getDirname();
|
|
16
16
|
async function copyTemplates(src, dest) {
|
|
17
17
|
await fs2.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 = path6.dirname(__filename2);
|
|
46
46
|
function getTemplatesDir() {
|
|
47
|
-
const rootDir =
|
|
48
|
-
return
|
|
47
|
+
const rootDir = path6.resolve(__dirname2, "..");
|
|
48
|
+
return path6.join(rootDir, "templates");
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// src/utils/i18n.ts
|
|
@@ -73,7 +73,7 @@ var I18N = {
|
|
|
73
73
|
"status.wrote": "\u2705 {path} \uC0DD\uC131 \uC644\uB8CC",
|
|
74
74
|
"feature.selectRepo": "\uB808\uD3EC\uC9C0\uD1A0\uB9AC\uB97C \uC120\uD0DD\uD558\uC138\uC694:",
|
|
75
75
|
"feature.folderExists": "\uC774\uBBF8 \uC874\uC7AC\uD558\uB294 \uD3F4\uB354\uC785\uB2C8\uB2E4: {path}",
|
|
76
|
-
"feature.baseNotFound": "feature
|
|
76
|
+
"feature.baseNotFound": "CLI \uB0B4\uC7A5 feature \uD15C\uD50C\uB9BF\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
77
77
|
"feature.created": "\u2705 Feature \uD3F4\uB354 \uC0DD\uC131 \uC644\uB8CC: {path}",
|
|
78
78
|
"feature.nextStepsTitle": "\uB2E4\uC74C \uB2E8\uACC4:",
|
|
79
79
|
"feature.nextSteps1": " 1. {path}/spec.md \uC791\uC131",
|
|
@@ -94,6 +94,9 @@ var I18N = {
|
|
|
94
94
|
"update.agentsUpdated": "agents/ \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC",
|
|
95
95
|
"update.skillsUpdated": "agents/skills \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC",
|
|
96
96
|
"update.updatingFeatureBase": "\u{1F4C1} features/feature-base/ \uD3F4\uB354 \uC5C5\uB370\uC774\uD2B8 \uC911...",
|
|
97
|
+
"update.engineManagedSkillsBuiltin": "agents/skills\uB294 CLI \uB0B4\uC7A5 \uADDC\uCE59\uC73C\uB85C \uAD00\uB9AC\uB418\uC5B4 docs\uB85C \uB3D9\uAE30\uD654\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
98
|
+
"update.engineManagedFeatureBaseBuiltin": "features/feature-base\uB294 CLI \uB0B4\uC7A5 \uD15C\uD50C\uB9BF\uC73C\uB85C \uAD00\uB9AC\uB418\uC5B4 docs\uB85C \uB3D9\uAE30\uD654\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
99
|
+
"update.engineManagedPruned": "docs\uC5D0\uC11C CLI \uAD00\uB9AC \uBB38\uC11C {count}\uAC1C\uB97C \uC815\uB9AC\uD588\uC2B5\uB2C8\uB2E4.",
|
|
97
100
|
"update.filesUpdated": "{count}\uAC1C \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC",
|
|
98
101
|
"update.updatedTotal": "\uCD1D {count}\uAC1C \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC!",
|
|
99
102
|
"update.changeDetected": "\uBCC0\uACBD \uAC10\uC9C0 (--force\uB85C \uB36E\uC5B4\uC4F0\uAE30)",
|
|
@@ -128,15 +131,17 @@ var I18N = {
|
|
|
128
131
|
"context.tipShowAll": "\uC804\uCCB4 \uBCF4\uAE30",
|
|
129
132
|
"context.tipShowDone": "\uC644\uB8CC\uB9CC \uBCF4\uAE30",
|
|
130
133
|
"context.checkRequired": "[\uD655\uC778 \uD544\uC694] ",
|
|
131
|
-
"context.checkPolicyHint": "\u2139\uFE0F \uC0AC\uC6A9\uC790 \uD655\uC778 \uC815\uCC45 \uC548\uB0B4(\uD604\uC7AC Next Action \uC544\uB2D8):
|
|
134
|
+
"context.checkPolicyHint": "\u2139\uFE0F \uC0AC\uC6A9\uC790 \uD655\uC778 \uC815\uCC45 \uC548\uB0B4(\uD604\uC7AC Next Action \uC544\uB2D8): CLI \uB0B4\uC7A5 \uC5D0\uC774\uC804\uD2B8 \uC815\uCC45 \uCC38\uACE0 (git push/merge/merge commit \uD3EC\uD568). [\uD655\uC778 \uD544\uC694]\uAC00 \uC788\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C `<\uB77C\uBCA8>` \uB610\uB294 `<\uB77C\uBCA8> OK` (\uC608: `A`, `A OK`) \uC751\uB2F5\uC744 \uBC1B\uC740 \uB4A4 \uC9C4\uD589 (config: approval\uB85C \uC870\uC815 \uAC00\uB2A5)",
|
|
132
135
|
"context.actionOptionHint": "\uC2B9\uC778 \uC751\uB2F5 \uD615\uC2DD: `<\uB77C\uBCA8>` \uB610\uB294 `<\uB77C\uBCA8> OK` (\uC608: `A`, `A OK`)",
|
|
133
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.",
|
|
134
|
-
"context.tipDocsCommitRules": "\uCEE4\uBC0B \uBA54\uC2DC\uC9C0 \uADDC\uCE59:
|
|
137
|
+
"context.tipDocsCommitRules": "\uCEE4\uBC0B \uBA54\uC2DC\uC9C0 \uADDC\uCE59: CLI \uB0B4\uC7A5 git-workflow \uADDC\uCE59 \uCC38\uACE0",
|
|
135
138
|
"context.list.docsCommitNeeded": "\uBB38\uC11C \uCEE4\uBC0B \uD544\uC694",
|
|
136
139
|
"context.list.projectCommitNeeded": "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uCEE4\uBC0B \uD544\uC694",
|
|
137
140
|
"context.list.issueNumberNeeded": "\uC774\uC288 \uBC88\uD638 \uAE30\uB85D \uD544\uC694",
|
|
138
141
|
"context.list.addPrMetadata": "PR \uBA54\uD0C0\uB370\uC774\uD130(PR/PR \uC0C1\uD0DC) \uCD94\uAC00",
|
|
139
142
|
"context.list.recordPrLink": "PR \uB9C1\uD06C \uAE30\uB85D",
|
|
143
|
+
"context.list.addPrePrReviewField": "Pre-PR Review \uD544\uB4DC \uCD94\uAC00",
|
|
144
|
+
"context.list.completePrePrReview": "Pre-PR \uB9AC\uBDF0 \uC644\uB8CC \uCC98\uB9AC",
|
|
140
145
|
"context.list.setPrStatus": "PR \uC0C1\uD0DC \uC124\uC815",
|
|
141
146
|
"context.list.prStatusToApproved": "PR \uC0C1\uD0DC {status} \u2192 Approved",
|
|
142
147
|
"context.list.approveSpec": "spec \uC2B9\uC778 \uD544\uC694",
|
|
@@ -205,23 +210,24 @@ var I18N = {
|
|
|
205
210
|
branchCreate: "\uBE0C\uB79C\uCE58 \uC0DD\uC131",
|
|
206
211
|
tasksExecute: "\uD0DC\uC2A4\uD06C \uC2E4\uD589",
|
|
207
212
|
docsCommitSync: "\uBB38\uC11C \uCEE4\uBC0B(\uB3D9\uAE30\uD654)",
|
|
213
|
+
prePrReview: "Pre-PR \uB9AC\uBDF0",
|
|
208
214
|
prCreate: "PR \uC0DD\uC131",
|
|
209
215
|
codeReview: "\uCF54\uB4DC \uB9AC\uBDF0",
|
|
210
216
|
featureDone: "Feature \uC644\uB8CC"
|
|
211
217
|
},
|
|
212
218
|
messages: {
|
|
213
|
-
specCreate: "spec.md \uD15C\uD50C\uB9BF
|
|
219
|
+
specCreate: "spec.md\uB97C CLI \uB0B4\uC7A5 \uD15C\uD50C\uB9BF \uAE30\uC900\uC73C\uB85C \uC791\uC131\uD558\uC138\uC694.",
|
|
214
220
|
specImprove: "spec.md\uB97C \uBCF4\uC644\uD558\uACE0 \uC0C1\uD0DC\uB97C Review\uB85C \uBCC0\uACBD\uD558\uC138\uC694.",
|
|
215
221
|
specApproval: "spec.md \uB0B4\uC6A9\uC744 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uACF5\uC720\uD558\uACE0 \uC2B9\uC778(`A` \uB610\uB294 `A OK` \uD615\uC2DD)\uC744 \uBC1B\uC73C\uC138\uC694.",
|
|
216
|
-
planCreate: "plan.md \uD15C\uD50C\uB9BF
|
|
222
|
+
planCreate: "plan.md\uB97C CLI \uB0B4\uC7A5 \uD15C\uD50C\uB9BF \uAE30\uC900\uC73C\uB85C \uC791\uC131\uD558\uC138\uC694.",
|
|
217
223
|
planImprove: "plan.md\uB97C \uBCF4\uC644\uD558\uACE0 \uC0C1\uD0DC\uB97C Review\uB85C \uBCC0\uACBD\uD558\uC138\uC694.",
|
|
218
224
|
planApproval: "plan.md \uB0B4\uC6A9\uC744 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uACF5\uC720\uD558\uACE0 \uC2B9\uC778(`A` \uB610\uB294 `A OK` \uD615\uC2DD)\uC744 \uBC1B\uC73C\uC138\uC694.",
|
|
219
|
-
tasksCreate: "tasks.md \uD15C\uD50C\uB9BF
|
|
225
|
+
tasksCreate: "tasks.md\uB97C CLI \uB0B4\uC7A5 \uD15C\uD50C\uB9BF \uAE30\uC900\uC73C\uB85C \uC791\uC131\uD558\uC138\uC694.",
|
|
220
226
|
tasksNeedAtLeastOne: "tasks.md\uC5D0 \uCD5C\uC18C 1\uAC1C \uC774\uC0C1\uC758 \uD0DC\uC2A4\uD06C\uB97C \uC791\uC131\uD558\uC138\uC694.",
|
|
221
227
|
tasksImprove: "tasks.md\uB97C \uBCF4\uC644\uD558\uACE0 \uBB38\uC11C \uC0C1\uD0DC\uB97C Review\uB85C \uBCC0\uACBD\uD558\uC138\uC694.",
|
|
222
228
|
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)",
|
|
223
229
|
docsCommitPlanning: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(planning): {folderName} \uAE30\uD68D \uBB38\uC11C"',
|
|
224
|
-
issueCreateAndWrite: "GitHub Issue\uB97C \uC0DD\uC131\uD55C \uB4A4, spec.md/tasks.md\uC758 \uC774\uC288 \uBC88\uD638\uB97C \uCC44\uC6B0\uACE0 \uBB38\uC11C \uCEE4\uBC0B\uC744 \uC900\uBE44\uD558\uC138\uC694. (
|
|
230
|
+
issueCreateAndWrite: "GitHub Issue\uB97C \uC0DD\uC131\uD55C \uB4A4, spec.md/tasks.md\uC758 \uC774\uC288 \uBC88\uD638\uB97C \uCC44\uC6B0\uACE0 \uBB38\uC11C \uCEE4\uBC0B\uC744 \uC900\uBE44\uD558\uC138\uC694. (CLI \uB0B4\uC7A5 create-issue \uAC00\uC774\uB4DC)",
|
|
225
231
|
docsCommitIssueUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(#{issueNumber}): {folderName} \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8"',
|
|
226
232
|
docsCommitUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs: {folderName} \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8"',
|
|
227
233
|
projectCommitIssueUpdate: 'cd "{projectGitCwd}" && git add -A && git commit -m "feat(#{issueNumber}): {folderName} \uAD6C\uD604 \uC5C5\uB370\uC774\uD2B8"',
|
|
@@ -230,11 +236,15 @@ var I18N = {
|
|
|
230
236
|
createBranch: 'cd "{projectGitCwd}" && git checkout -b feat/{issueNumber}-{slug}',
|
|
231
237
|
tasksAllDoneButNoChecklist: '\uBAA8\uB4E0 \uD0DC\uC2A4\uD06C\uAC00 DONE\uC774\uC9C0\uB9CC \uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 \uC139\uC158\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. tasks.md\uC758 "\uC644\uB8CC \uC870\uAC74" \uC139\uC158\uC744 \uCD94\uAC00/\uD655\uC778\uD558\uC138\uC694.',
|
|
232
238
|
tasksAllDoneButChecklist: "\uBAA8\uB4E0 \uD0DC\uC2A4\uD06C\uAC00 DONE\uC774\uC9C0\uB9CC \uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\uAC00 \uC644\uC804\uD788 \uCCB4\uD06C\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. ({checked}/{total})",
|
|
233
|
-
finishDoingTask: '\uD604\uC7AC DOING/REVIEW \uC911\uC778 \uD0DC\uC2A4\uD06C\uB97C \uC644\uB8CC\uD558\uC138\uC694: "{title}" ({done}/{total}) (\uC644\uB8CC \uC804 \uACB0\uACFC/\uAC80\uC99D \uACF5\uC720 + \uC2B9\uC778(`A` \uB610\uB294 `A OK` \uD615\uC2DD) \uD6C4 DONE \uCC98\uB9AC) (
|
|
234
|
-
startNextTodoTask: '\uB2E4\uC74C TODO \uD0DC\uC2A4\uD06C\uB97C \uC2DC\uC791\uD558\uC138\uC694: "{title}" ({done}/{total}) (\uC2DC\uC791 \uC804 \uC81C\uBAA9 \uACF5\uC720 + \uC2B9\uC778(`A` \uB610\uB294 `A OK` \uD615\uC2DD) \uD6C4 DOING \uCC98\uB9AC) (
|
|
235
|
-
checkTaskStatuses: "\uD0DC\uC2A4\uD06C \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694. ({done}/{total}) (
|
|
239
|
+
finishDoingTask: '\uD604\uC7AC DOING/REVIEW \uC911\uC778 \uD0DC\uC2A4\uD06C\uB97C \uC644\uB8CC\uD558\uC138\uC694: "{title}" ({done}/{total}) (\uC644\uB8CC \uC804 \uACB0\uACFC/\uAC80\uC99D \uACF5\uC720 + \uC2B9\uC778(`A` \uB610\uB294 `A OK` \uD615\uC2DD) \uD6C4 DONE \uCC98\uB9AC) (CLI \uB0B4\uC7A5 execute-task \uAC00\uC774\uB4DC)',
|
|
240
|
+
startNextTodoTask: '\uB2E4\uC74C TODO \uD0DC\uC2A4\uD06C\uB97C \uC2DC\uC791\uD558\uC138\uC694: "{title}" ({done}/{total}) (\uC2DC\uC791 \uC804 \uC81C\uBAA9 \uACF5\uC720 + \uC2B9\uC778(`A` \uB610\uB294 `A OK` \uD615\uC2DD) \uD6C4 DOING \uCC98\uB9AC) (CLI \uB0B4\uC7A5 execute-task \uAC00\uC774\uB4DC)',
|
|
241
|
+
checkTaskStatuses: "\uD0DC\uC2A4\uD06C \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694. ({done}/{total}) (CLI \uB0B4\uC7A5 execute-task \uAC00\uC774\uB4DC)",
|
|
236
242
|
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)",
|
|
237
|
-
|
|
243
|
+
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)",
|
|
244
|
+
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). \uC2A4\uD0AC\uC744 \uC4F8 \uC218 \uC5C6\uC73C\uBA74 `{fallback}` \uC815\uCC45\uC73C\uB85C \uC9C4\uD589\uD558\uACE0 `PR \uC804 \uB9AC\uBDF0`\uB97C Done\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694. Findings \uC815\uCC45: {findingsPolicy}",
|
|
245
|
+
prePrReviewFindingsBlock: "\uC911\uC694 \uC774\uC288\uB294 \uC218\uC815/\uD569\uC758 \uD6C4\uC5D0\uB9CC PR \uC0DD\uC131",
|
|
246
|
+
prePrReviewFindingsWarn: "\uB9AC\uC2A4\uD06C\uB97C \uACF5\uC720\uD558\uBA74 PR \uC0DD\uC131 \uC9C4\uD589 \uAC00\uB2A5",
|
|
247
|
+
prCreate: "PR\uC744 \uC0DD\uC131\uD558\uACE0 tasks.md\uC5D0 PR \uB9C1\uD06C\uB97C \uAE30\uB85D\uD558\uC138\uC694. (CLI \uB0B4\uC7A5 create-pr \uAC00\uC774\uB4DC)",
|
|
238
248
|
prFillStatus: "tasks.md\uC758 PR \uC0C1\uD0DC\uB97C Review/Approved \uC911 \uD558\uB098\uB85C \uC124\uC815\uD558\uC138\uC694. (merge \uD6C4 Approved\uB85C \uC5C5\uB370\uC774\uD2B8)",
|
|
239
249
|
prResolveReview: "\uB9AC\uBDF0 \uCF54\uBA58\uD2B8\uB97C \uD574\uACB0\uD558\uACE0 PR \uC0C1\uD0DC\uB97C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694. (PR \uC0C1\uD0DC: Review \u2192 Approved)",
|
|
240
250
|
prRequestReview: "\uB9AC\uBDF0\uC5B4\uC5D0\uAC8C \uB9AC\uBDF0\uB97C \uC694\uCCAD\uD558\uACE0 PR \uC0C1\uD0DC\uB97C Review\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.",
|
|
@@ -245,17 +255,20 @@ var I18N = {
|
|
|
245
255
|
projectBranchUnavailable: "\uD504\uB85C\uC81D\uD2B8 \uBE0C\uB79C\uCE58\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. (standalone \uBAA8\uB4DC\uC5D0\uC11C\uB294 projectRoot\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.)",
|
|
246
256
|
docsGitUnavailable: "docs \uB808\uD3EC\uC758 git \uC0C1\uD0DC\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. (\uB808\uD3EC \uC704\uCE58 / git init \uD655\uC778)",
|
|
247
257
|
docsPathIgnored: "\uD604\uC7AC Feature \uBB38\uC11C \uACBD\uB85C\uAC00 git ignore \uB300\uC0C1\uC785\uB2C8\uB2E4: {path} (docs \uCEE4\uBC0B \uAC10\uC9C0\uAC00 \uC81C\uD55C\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4.)",
|
|
248
|
-
docsUncommittedChanges: "\uBB38\uC11C \uBCC0\uACBD\uC0AC\uD56D\uC774 \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (\uCD94\uAC00 \uBB38\uC11C \uCEE4\uBC0B \uD544\uC694) \uCEE4\uBC0B \uBA54\uC2DC\uC9C0 \uADDC\uCE59:
|
|
258
|
+
docsUncommittedChanges: "\uBB38\uC11C \uBCC0\uACBD\uC0AC\uD56D\uC774 \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (\uCD94\uAC00 \uBB38\uC11C \uCEE4\uBC0B \uD544\uC694) \uCEE4\uBC0B \uBA54\uC2DC\uC9C0 \uADDC\uCE59: CLI \uB0B4\uC7A5 git-workflow \uADDC\uCE59 \uCC38\uACE0",
|
|
249
259
|
projectUncommittedChanges: "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uBCC0\uACBD\uC0AC\uD56D\uC774 \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (\uCD94\uAC00 \uCF54\uB4DC \uCEE4\uBC0B \uD544\uC694)",
|
|
250
260
|
legacyTasksDocStatusField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. `\uBB38\uC11C \uC0C1\uD0DC` \uD544\uB4DC(Review/Approved)\uB97C \uCD94\uAC00\uD574 \uD0DC\uC2A4\uD06C \uC2B9\uC778 \uB2E8\uACC4\uB97C \uD65C\uC131\uD654\uD558\uC138\uC694.",
|
|
251
261
|
legacyTasksPrFields: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR` \uBC0F `PR \uC0C1\uD0DC` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694.",
|
|
262
|
+
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`)",
|
|
252
263
|
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.)",
|
|
253
264
|
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.)",
|
|
254
265
|
workflowIssueMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC \uC774\uC288 \uBC88\uD638\uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4. (spec.md/tasks.md\uC758 \uC774\uC288 \uBC88\uD638\uB97C \uCC44\uC6B0\uC138\uC694.)",
|
|
255
266
|
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)",
|
|
256
267
|
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.)",
|
|
257
268
|
workflowPrStatusMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC PR \uC0C1\uD0DC\uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4. (tasks.md\uC758 PR \uC0C1\uD0DC\uB97C Review/Approved \uC911 \uD558\uB098\uB85C \uC124\uC815\uD558\uC138\uC694.)",
|
|
258
|
-
workflowPrStatusNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC PR \uC0C1\uD0DC\uAC00 Approved\uAC00 \uC544\uB2D9\uB2C8\uB2E4. (merge \uD6C4 PR \uC0C1\uD0DC\uB97C Approved\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.)"
|
|
269
|
+
workflowPrStatusNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC PR \uC0C1\uD0DC\uAC00 Approved\uAC00 \uC544\uB2D9\uB2C8\uB2E4. (merge \uD6C4 PR \uC0C1\uD0DC\uB97C Approved\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.)",
|
|
270
|
+
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.)",
|
|
271
|
+
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.)"
|
|
259
272
|
}
|
|
260
273
|
},
|
|
261
274
|
en: {
|
|
@@ -270,7 +283,7 @@ var I18N = {
|
|
|
270
283
|
"status.wrote": "\u2705 Wrote {path}",
|
|
271
284
|
"feature.selectRepo": "Select a repository:",
|
|
272
285
|
"feature.folderExists": "Folder already exists: {path}",
|
|
273
|
-
"feature.baseNotFound": "
|
|
286
|
+
"feature.baseNotFound": "Built-in feature template not found.",
|
|
274
287
|
"feature.created": "\u2705 Feature folder created: {path}",
|
|
275
288
|
"feature.nextStepsTitle": "Next steps:",
|
|
276
289
|
"feature.nextSteps1": " 1. Write {path}/spec.md",
|
|
@@ -291,6 +304,9 @@ var I18N = {
|
|
|
291
304
|
"update.agentsUpdated": "agents/ updated",
|
|
292
305
|
"update.skillsUpdated": "agents/skills updated",
|
|
293
306
|
"update.updatingFeatureBase": "\u{1F4C1} Updating features/feature-base/ folder...",
|
|
307
|
+
"update.engineManagedSkillsBuiltin": "agents/skills is CLI-managed and is not synced into docs.",
|
|
308
|
+
"update.engineManagedFeatureBaseBuiltin": "features/feature-base is CLI-managed and is not synced into docs.",
|
|
309
|
+
"update.engineManagedPruned": "Removed {count} CLI-managed docs entries from this docs tree.",
|
|
294
310
|
"update.filesUpdated": "{count} files updated",
|
|
295
311
|
"update.updatedTotal": "Updated {count} files!",
|
|
296
312
|
"update.changeDetected": "changes detected (use --force to overwrite)",
|
|
@@ -325,15 +341,17 @@ var I18N = {
|
|
|
325
341
|
"context.tipShowAll": "Show all",
|
|
326
342
|
"context.tipShowDone": "Show done only",
|
|
327
343
|
"context.checkRequired": "[CHECK required] ",
|
|
328
|
-
"context.checkPolicyHint": "\u2139\uFE0F User check policy notice (not the current next action): see
|
|
344
|
+
"context.checkPolicyHint": "\u2139\uFE0F User check policy notice (not the current next action): see the CLI built-in agent policy (includes git push/merge and merge commits). If you see [CHECK required], wait for `<label>` or `<label> OK` (e.g. `A`, `A OK`) before proceeding (config: approval can override)",
|
|
329
345
|
"context.actionOptionHint": "Approval reply format: `<label>` or `<label> OK` (e.g. `A`, `A OK`)",
|
|
330
346
|
"context.actionExplainHint": "Before requesting approval, explain what each label will run/change with a one-line summary.",
|
|
331
|
-
"context.tipDocsCommitRules": "Commit message rules:
|
|
347
|
+
"context.tipDocsCommitRules": "Commit message rules: CLI built-in git-workflow policy",
|
|
332
348
|
"context.list.docsCommitNeeded": "Commit docs changes",
|
|
333
349
|
"context.list.projectCommitNeeded": "Commit project code changes",
|
|
334
350
|
"context.list.issueNumberNeeded": "Fill issue number in docs",
|
|
335
351
|
"context.list.addPrMetadata": "Add PR metadata (PR/PR Status)",
|
|
336
352
|
"context.list.recordPrLink": "Record PR link",
|
|
353
|
+
"context.list.addPrePrReviewField": "Add Pre-PR Review field",
|
|
354
|
+
"context.list.completePrePrReview": "Complete Pre-PR review",
|
|
337
355
|
"context.list.setPrStatus": "Set PR Status",
|
|
338
356
|
"context.list.prStatusToApproved": "PR Status {status} \u2192 Approved",
|
|
339
357
|
"context.list.approveSpec": "Approve spec",
|
|
@@ -402,23 +420,24 @@ var I18N = {
|
|
|
402
420
|
branchCreate: "Create branch",
|
|
403
421
|
tasksExecute: "Execute tasks",
|
|
404
422
|
docsCommitSync: "Commit docs (sync)",
|
|
423
|
+
prePrReview: "Pre-PR review",
|
|
405
424
|
prCreate: "Create PR",
|
|
406
425
|
codeReview: "Code review",
|
|
407
426
|
featureDone: "Feature done"
|
|
408
427
|
},
|
|
409
428
|
messages: {
|
|
410
|
-
specCreate: "Create spec.md
|
|
429
|
+
specCreate: "Create spec.md using the CLI built-in template policy.",
|
|
411
430
|
specImprove: "Improve spec.md and change Status to Review.",
|
|
412
431
|
specApproval: "Share spec.md with the user and get approval (`A` or `A OK` format).",
|
|
413
|
-
planCreate: "Create plan.md
|
|
432
|
+
planCreate: "Create plan.md using the CLI built-in template policy.",
|
|
414
433
|
planImprove: "Improve plan.md and change Status to Review.",
|
|
415
434
|
planApproval: "Share plan.md with the user and get approval (`A` or `A OK` format).",
|
|
416
|
-
tasksCreate: "Create tasks.md
|
|
435
|
+
tasksCreate: "Create tasks.md using the CLI built-in template policy.",
|
|
417
436
|
tasksNeedAtLeastOne: "Write at least 1 task in tasks.md.",
|
|
418
437
|
tasksImprove: "Improve tasks.md and change Doc Status to Review.",
|
|
419
438
|
tasksApproval: "Share tasks.md with the user and get progress approval (`A` or `A OK` format). (Then set Doc Status to Approved)",
|
|
420
439
|
docsCommitPlanning: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(planning): {folderName} planning docs"',
|
|
421
|
-
issueCreateAndWrite: "Create a GitHub Issue, fill the issue number in spec.md/tasks.md, then prepare a docs commit. (
|
|
440
|
+
issueCreateAndWrite: "Create a GitHub Issue, fill the issue number in spec.md/tasks.md, then prepare a docs commit. (CLI built-in create-issue guide)",
|
|
422
441
|
docsCommitIssueUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(#{issueNumber}): {folderName} docs update"',
|
|
423
442
|
docsCommitUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs: {folderName} docs update"',
|
|
424
443
|
projectCommitIssueUpdate: 'cd "{projectGitCwd}" && git add -A && git commit -m "feat(#{issueNumber}): {folderName} implementation update"',
|
|
@@ -427,11 +446,15 @@ var I18N = {
|
|
|
427
446
|
createBranch: 'cd "{projectGitCwd}" && git checkout -b feat/{issueNumber}-{slug}',
|
|
428
447
|
tasksAllDoneButNoChecklist: 'All tasks are DONE, but no completion checklist section was found. Add/verify the "Completion Criteria" section in tasks.md.',
|
|
429
448
|
tasksAllDoneButChecklist: "All tasks are DONE, but the completion checklist is not fully checked. ({checked}/{total})",
|
|
430
|
-
finishDoingTask: 'Finish the current DOING/REVIEW task: "{title}" ({done}/{total}) (Share outcome/verification + get OK before marking DONE) (
|
|
431
|
-
startNextTodoTask: 'Start the next TODO task: "{title}" ({done}/{total}) (Share title + get OK before marking DOING) (
|
|
432
|
-
checkTaskStatuses: "Check task statuses. ({done}/{total}) (
|
|
449
|
+
finishDoingTask: 'Finish the current DOING/REVIEW task: "{title}" ({done}/{total}) (Share outcome/verification + get OK before marking DONE) (CLI built-in execute-task guide)',
|
|
450
|
+
startNextTodoTask: 'Start the next TODO task: "{title}" ({done}/{total}) (Share title + get OK before marking DOING) (CLI built-in execute-task guide)',
|
|
451
|
+
checkTaskStatuses: "Check task statuses. ({done}/{total}) (CLI built-in execute-task guide)",
|
|
433
452
|
prLegacyAsk: "tasks.md is missing PR/PR Status fields. Update to the latest template format? (CHECK required)",
|
|
434
|
-
|
|
453
|
+
prePrReviewFieldMissing: "tasks.md is missing the `Pre-PR Review` field. Add `- **Pre-PR Review**: Pending | Done` and run context again. (CHECK required)",
|
|
454
|
+
prePrReviewRun: "Run a pre-PR code review before creating the PR. Preferred skills: {skills} (if a better installed skill fits this change, propose it first). If no skill can run, use `{fallback}` and set `Pre-PR Review` to Done in tasks.md. Findings policy: {findingsPolicy}",
|
|
455
|
+
prePrReviewFindingsBlock: "major findings must be fixed/aligned before PR creation",
|
|
456
|
+
prePrReviewFindingsWarn: "you may proceed after sharing the risks",
|
|
457
|
+
prCreate: "Create a PR and record the PR link in tasks.md. (CLI built-in create-pr guide)",
|
|
435
458
|
prFillStatus: "Set PR Status in tasks.md to Review/Approved. (After merge, update it to Approved.)",
|
|
436
459
|
prResolveReview: "Resolve review comments and update PR Status. (PR Status: Review \u2192 Approved)",
|
|
437
460
|
prRequestReview: "Request review and update PR Status to Review.",
|
|
@@ -442,17 +465,20 @@ var I18N = {
|
|
|
442
465
|
projectBranchUnavailable: "Cannot determine project branch. (In standalone mode, projectRoot is required.)",
|
|
443
466
|
docsGitUnavailable: "Cannot read git status for the docs repo. (Check repo location / git init.)",
|
|
444
467
|
docsPathIgnored: "Current feature docs path is ignored by git: {path} (docs commit detection may be limited).",
|
|
445
|
-
docsUncommittedChanges: "Docs changes are not committed. (Additional docs commit needed.) Commit message rules:
|
|
468
|
+
docsUncommittedChanges: "Docs changes are not committed. (Additional docs commit needed.) Commit message rules: CLI built-in git-workflow policy",
|
|
446
469
|
projectUncommittedChanges: "Project code changes are not committed. (Additional code commit needed.)",
|
|
447
470
|
legacyTasksDocStatusField: "Legacy tasks.md format detected. Add a `Doc Status` field (Review/Approved) to enable tasks approval.",
|
|
448
471
|
legacyTasksPrFields: "Legacy tasks.md format detected. Add `PR` and `PR Status` fields before PR steps.",
|
|
472
|
+
legacyTasksPrePrReviewField: "Legacy tasks.md format detected. Add `Pre-PR Review` before PR steps. (`- **Pre-PR Review**: Pending | Done`)",
|
|
449
473
|
workflowSpecNotApproved: "Implementation is done but spec.md Status is not Approved. (Update spec.md Status to Approved.)",
|
|
450
474
|
workflowPlanNotApproved: "Implementation is done but plan.md Status is not Approved. (Update plan.md Status to Approved.)",
|
|
451
475
|
workflowIssueMissing: "Implementation is done but Issue Number is missing. (Fill Issue Number in spec.md/tasks.md.)",
|
|
452
476
|
workflowProjectUncommittedChanges: "Commit project code changes before completing workflow. (Project worktree has uncommitted changes.)",
|
|
453
477
|
workflowPrLinkMissing: "Implementation is done but PR link is missing. (Fill the PR field in tasks.md.)",
|
|
454
478
|
workflowPrStatusMissing: "Implementation is done but PR Status is missing. (Set PR Status to Review/Approved in tasks.md.)",
|
|
455
|
-
workflowPrStatusNotApproved: "Implementation is done but PR Status is not Approved. (After merge, update PR Status to Approved in tasks.md.)"
|
|
479
|
+
workflowPrStatusNotApproved: "Implementation is done but PR Status is not Approved. (After merge, update PR Status to Approved in tasks.md.)",
|
|
480
|
+
workflowPrePrReviewMissing: "Implementation is done but `Pre-PR Review` is missing. (Add `- **Pre-PR Review**: Pending | Done` in tasks.md.)",
|
|
481
|
+
workflowPrePrReviewNotDone: "Implementation is done but `Pre-PR Review` is not Done. (Run pre-PR review, then update it to Done.)"
|
|
456
482
|
}
|
|
457
483
|
}
|
|
458
484
|
};
|
|
@@ -1033,12 +1059,12 @@ function sleep(ms) {
|
|
|
1033
1059
|
return new Promise((resolve) => globalThis.setTimeout(resolve, ms));
|
|
1034
1060
|
}
|
|
1035
1061
|
function getDocsLockPath(docsDir) {
|
|
1036
|
-
return
|
|
1062
|
+
return path6.join(docsDir, ".lee-spec-kit.lock");
|
|
1037
1063
|
}
|
|
1038
1064
|
function getInitLockPath(targetDir) {
|
|
1039
|
-
return
|
|
1040
|
-
|
|
1041
|
-
`.lee-spec-kit.${
|
|
1065
|
+
return path6.join(
|
|
1066
|
+
path6.dirname(targetDir),
|
|
1067
|
+
`.lee-spec-kit.${path6.basename(targetDir)}.lock`
|
|
1042
1068
|
);
|
|
1043
1069
|
}
|
|
1044
1070
|
async function isStaleLock(lockPath, staleMs) {
|
|
@@ -1080,7 +1106,7 @@ function isProcessAlive(pid) {
|
|
|
1080
1106
|
}
|
|
1081
1107
|
}
|
|
1082
1108
|
async function tryAcquire(lockPath, owner) {
|
|
1083
|
-
await fs2.ensureDir(
|
|
1109
|
+
await fs2.ensureDir(path6.dirname(lockPath));
|
|
1084
1110
|
try {
|
|
1085
1111
|
const fd = await fs2.open(lockPath, "wx");
|
|
1086
1112
|
const payload = JSON.stringify(
|
|
@@ -1150,48 +1176,39 @@ function getLocalDateString(date = /* @__PURE__ */ new Date()) {
|
|
|
1150
1176
|
const day = String(date.getDate()).padStart(2, "0");
|
|
1151
1177
|
return `${year}-${month}-${day}`;
|
|
1152
1178
|
}
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
if (/^\s*-\s*(Example|Values)\s*:/.test(line)) continue;
|
|
1173
|
-
if (/^\s*-\s*(예|값)\s*:/.test(line)) continue;
|
|
1174
|
-
filtered.push(line);
|
|
1179
|
+
var ENGINE_MANAGED_AGENT_FILES = [
|
|
1180
|
+
"agents.md",
|
|
1181
|
+
"git-workflow.md",
|
|
1182
|
+
"issue-template.md",
|
|
1183
|
+
"pr-template.md"
|
|
1184
|
+
];
|
|
1185
|
+
var ENGINE_MANAGED_AGENT_DIRS = ["skills"];
|
|
1186
|
+
var ENGINE_MANAGED_FEATURE_PATH = path6.join(
|
|
1187
|
+
"features",
|
|
1188
|
+
"feature-base"
|
|
1189
|
+
);
|
|
1190
|
+
async function pruneEngineManagedDocs(docsDir) {
|
|
1191
|
+
const removed = [];
|
|
1192
|
+
for (const file of ENGINE_MANAGED_AGENT_FILES) {
|
|
1193
|
+
const target = path6.join(docsDir, "agents", file);
|
|
1194
|
+
if (await fs2.pathExists(target)) {
|
|
1195
|
+
await fs2.remove(target);
|
|
1196
|
+
removed.push(path6.relative(docsDir, target));
|
|
1197
|
+
}
|
|
1175
1198
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
(content) => sanitizeTasksForLocal(content, lang)
|
|
1190
|
-
);
|
|
1191
|
-
}
|
|
1192
|
-
async function applyLocalWorkflowTemplateToFeatureBase(docsDir, lang) {
|
|
1193
|
-
const baseDir = path13.join(docsDir, "features", "feature-base");
|
|
1194
|
-
await applyLocalWorkflowTemplateToFeatureDir(baseDir, lang);
|
|
1199
|
+
for (const dir of ENGINE_MANAGED_AGENT_DIRS) {
|
|
1200
|
+
const target = path6.join(docsDir, "agents", dir);
|
|
1201
|
+
if (await fs2.pathExists(target)) {
|
|
1202
|
+
await fs2.remove(target);
|
|
1203
|
+
removed.push(path6.relative(docsDir, target));
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
const featureBasePath = path6.join(docsDir, ENGINE_MANAGED_FEATURE_PATH);
|
|
1207
|
+
if (await fs2.pathExists(featureBasePath)) {
|
|
1208
|
+
await fs2.remove(featureBasePath);
|
|
1209
|
+
removed.push(path6.relative(docsDir, featureBasePath));
|
|
1210
|
+
}
|
|
1211
|
+
return removed;
|
|
1195
1212
|
}
|
|
1196
1213
|
|
|
1197
1214
|
// src/commands/init.ts
|
|
@@ -1242,7 +1259,7 @@ ${tr(lang2, "cli", "common.canceled")}`)
|
|
|
1242
1259
|
}
|
|
1243
1260
|
async function runInit(options) {
|
|
1244
1261
|
const cwd = process.cwd();
|
|
1245
|
-
const defaultName =
|
|
1262
|
+
const defaultName = path6.basename(cwd);
|
|
1246
1263
|
let projectName = options.name || defaultName;
|
|
1247
1264
|
let projectType = options.type;
|
|
1248
1265
|
let components = parseComponentsOption(options.components);
|
|
@@ -1252,7 +1269,7 @@ async function runInit(options) {
|
|
|
1252
1269
|
let pushDocs = typeof options.pushDocs === "boolean" ? options.pushDocs : void 0;
|
|
1253
1270
|
let docsRemote = options.docsRemote;
|
|
1254
1271
|
let projectRoot;
|
|
1255
|
-
const targetDir =
|
|
1272
|
+
const targetDir = path6.resolve(cwd, options.dir || "./docs");
|
|
1256
1273
|
const skipPrompts = !!options.yes || !!options.nonInteractive;
|
|
1257
1274
|
if (options.docsRepo && !["embedded", "standalone"].includes(options.docsRepo)) {
|
|
1258
1275
|
throw createCliError(
|
|
@@ -1591,9 +1608,9 @@ async function runInit(options) {
|
|
|
1591
1608
|
);
|
|
1592
1609
|
console.log();
|
|
1593
1610
|
const templatesDir = getTemplatesDir();
|
|
1594
|
-
const commonPath =
|
|
1611
|
+
const commonPath = path6.join(templatesDir, lang, "common");
|
|
1595
1612
|
const templateProjectType = toTemplateProjectType(projectType);
|
|
1596
|
-
const typePath =
|
|
1613
|
+
const typePath = path6.join(templatesDir, lang, templateProjectType);
|
|
1597
1614
|
if (await fs2.pathExists(commonPath)) {
|
|
1598
1615
|
await copyTemplates(commonPath, targetDir);
|
|
1599
1616
|
}
|
|
@@ -1604,13 +1621,13 @@ async function runInit(options) {
|
|
|
1604
1621
|
}
|
|
1605
1622
|
await copyTemplates(typePath, targetDir);
|
|
1606
1623
|
if (projectType === "multi" && !isDefaultFullstackComponents(components)) {
|
|
1607
|
-
const featuresRoot =
|
|
1608
|
-
await fs2.remove(
|
|
1609
|
-
await fs2.remove(
|
|
1624
|
+
const featuresRoot = path6.join(targetDir, "features");
|
|
1625
|
+
await fs2.remove(path6.join(featuresRoot, "fe"));
|
|
1626
|
+
await fs2.remove(path6.join(featuresRoot, "be"));
|
|
1610
1627
|
for (const component of components) {
|
|
1611
|
-
const componentDir =
|
|
1628
|
+
const componentDir = path6.join(featuresRoot, component);
|
|
1612
1629
|
await fs2.ensureDir(componentDir);
|
|
1613
|
-
const readmePath =
|
|
1630
|
+
const readmePath = path6.join(componentDir, "README.md");
|
|
1614
1631
|
if (!await fs2.pathExists(readmePath)) {
|
|
1615
1632
|
await fs2.writeFile(
|
|
1616
1633
|
readmePath,
|
|
@@ -1630,9 +1647,7 @@ Store ${component} feature specs here.
|
|
|
1630
1647
|
"{{featurePath}}": featurePath
|
|
1631
1648
|
};
|
|
1632
1649
|
await replaceInFiles(targetDir, replacements);
|
|
1633
|
-
|
|
1634
|
-
await applyLocalWorkflowTemplateToFeatureBase(targetDir, lang);
|
|
1635
|
-
}
|
|
1650
|
+
await pruneEngineManagedDocs(targetDir);
|
|
1636
1651
|
const config = {
|
|
1637
1652
|
projectName,
|
|
1638
1653
|
projectType,
|
|
@@ -1640,7 +1655,11 @@ Store ${component} feature specs here.
|
|
|
1640
1655
|
lang,
|
|
1641
1656
|
createdAt: getLocalDateString(),
|
|
1642
1657
|
docsRepo,
|
|
1643
|
-
workflow: {
|
|
1658
|
+
workflow: {
|
|
1659
|
+
mode: workflowMode,
|
|
1660
|
+
codeDirtyScope: "auto",
|
|
1661
|
+
prePrReview: { skills: ["code-review-excellence"] }
|
|
1662
|
+
},
|
|
1644
1663
|
pr: {
|
|
1645
1664
|
screenshots: { upload: false }
|
|
1646
1665
|
},
|
|
@@ -1659,7 +1678,7 @@ Store ${component} feature specs here.
|
|
|
1659
1678
|
config.projectRoot = projectRoot;
|
|
1660
1679
|
}
|
|
1661
1680
|
}
|
|
1662
|
-
const configPath =
|
|
1681
|
+
const configPath = path6.join(targetDir, ".lee-spec-kit.json");
|
|
1663
1682
|
await fs2.writeJson(configPath, config, { spaces: 2 });
|
|
1664
1683
|
console.log(chalk6.green(tr(lang, "cli", "init.log.docsCreated")));
|
|
1665
1684
|
console.log();
|
|
@@ -1728,7 +1747,7 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
|
|
|
1728
1747
|
console.log(chalk6.blue(tr(lang, "cli", "init.log.gitInit")));
|
|
1729
1748
|
runGit(["init"], cwd);
|
|
1730
1749
|
}
|
|
1731
|
-
const relativePath =
|
|
1750
|
+
const relativePath = path6.relative(cwd, targetDir);
|
|
1732
1751
|
const stagedBeforeAdd = getCachedStagedFiles(cwd);
|
|
1733
1752
|
if (relativePath === "." && stagedBeforeAdd && stagedBeforeAdd.length > 0) {
|
|
1734
1753
|
console.log(
|
|
@@ -1785,17 +1804,17 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
|
|
|
1785
1804
|
}
|
|
1786
1805
|
function getAncestorDirs(startDir) {
|
|
1787
1806
|
const dirs = [];
|
|
1788
|
-
let current =
|
|
1807
|
+
let current = path6.resolve(startDir);
|
|
1789
1808
|
while (true) {
|
|
1790
1809
|
dirs.push(current);
|
|
1791
|
-
const parent =
|
|
1810
|
+
const parent = path6.dirname(current);
|
|
1792
1811
|
if (parent === current) break;
|
|
1793
1812
|
current = parent;
|
|
1794
1813
|
}
|
|
1795
1814
|
return dirs;
|
|
1796
1815
|
}
|
|
1797
1816
|
function hasWorkspaceBoundary(dir) {
|
|
1798
|
-
return fs2.existsSync(
|
|
1817
|
+
return fs2.existsSync(path6.join(dir, "package.json")) || fs2.existsSync(path6.join(dir, ".git"));
|
|
1799
1818
|
}
|
|
1800
1819
|
function getSearchBaseDirs(cwd) {
|
|
1801
1820
|
const ancestors = getAncestorDirs(cwd);
|
|
@@ -1808,21 +1827,21 @@ function getSearchBaseDirs(cwd) {
|
|
|
1808
1827
|
async function getConfig(cwd) {
|
|
1809
1828
|
const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
|
|
1810
1829
|
const baseDirs = [
|
|
1811
|
-
...explicitDocsDir ? [
|
|
1830
|
+
...explicitDocsDir ? [path6.resolve(explicitDocsDir)] : [],
|
|
1812
1831
|
...getSearchBaseDirs(cwd)
|
|
1813
1832
|
];
|
|
1814
1833
|
const visitedBaseDirs = /* @__PURE__ */ new Set();
|
|
1815
1834
|
const visitedDocsDirs = /* @__PURE__ */ new Set();
|
|
1816
1835
|
for (const baseDir of baseDirs) {
|
|
1817
|
-
const resolvedBaseDir =
|
|
1836
|
+
const resolvedBaseDir = path6.resolve(baseDir);
|
|
1818
1837
|
if (visitedBaseDirs.has(resolvedBaseDir)) continue;
|
|
1819
1838
|
visitedBaseDirs.add(resolvedBaseDir);
|
|
1820
|
-
const possibleDocsDirs = [
|
|
1839
|
+
const possibleDocsDirs = [path6.join(resolvedBaseDir, "docs"), resolvedBaseDir];
|
|
1821
1840
|
for (const docsDir of possibleDocsDirs) {
|
|
1822
|
-
const resolvedDocsDir =
|
|
1841
|
+
const resolvedDocsDir = path6.resolve(docsDir);
|
|
1823
1842
|
if (visitedDocsDirs.has(resolvedDocsDir)) continue;
|
|
1824
1843
|
visitedDocsDirs.add(resolvedDocsDir);
|
|
1825
|
-
const configPath =
|
|
1844
|
+
const configPath = path6.join(resolvedDocsDir, ".lee-spec-kit.json");
|
|
1826
1845
|
if (await fs2.pathExists(configPath)) {
|
|
1827
1846
|
try {
|
|
1828
1847
|
const configFile = await fs2.readJson(configPath);
|
|
@@ -1848,18 +1867,26 @@ async function getConfig(cwd) {
|
|
|
1848
1867
|
} catch {
|
|
1849
1868
|
}
|
|
1850
1869
|
}
|
|
1851
|
-
const agentsPath =
|
|
1852
|
-
const featuresPath =
|
|
1870
|
+
const agentsPath = path6.join(resolvedDocsDir, "agents");
|
|
1871
|
+
const featuresPath = path6.join(resolvedDocsDir, "features");
|
|
1853
1872
|
if (await fs2.pathExists(agentsPath) && await fs2.pathExists(featuresPath)) {
|
|
1854
|
-
const bePath =
|
|
1855
|
-
const fePath =
|
|
1873
|
+
const bePath = path6.join(featuresPath, "be");
|
|
1874
|
+
const fePath = path6.join(featuresPath, "fe");
|
|
1856
1875
|
const projectType = await fs2.pathExists(bePath) || await fs2.pathExists(fePath) ? "multi" : "single";
|
|
1857
1876
|
const components = projectType === "multi" ? resolveProjectComponents("multi", ["fe", "be"]) : void 0;
|
|
1858
|
-
const
|
|
1877
|
+
const langProbeCandidates = [
|
|
1878
|
+
path6.join(agentsPath, "custom.md"),
|
|
1879
|
+
path6.join(agentsPath, "constitution.md"),
|
|
1880
|
+
path6.join(agentsPath, "agents.md")
|
|
1881
|
+
];
|
|
1859
1882
|
let lang = "en";
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1883
|
+
for (const candidate of langProbeCandidates) {
|
|
1884
|
+
if (!await fs2.pathExists(candidate)) continue;
|
|
1885
|
+
const content = await fs2.readFile(candidate, "utf-8");
|
|
1886
|
+
if (/[가-힣]/.test(content)) {
|
|
1887
|
+
lang = "ko";
|
|
1888
|
+
break;
|
|
1889
|
+
}
|
|
1863
1890
|
}
|
|
1864
1891
|
return { docsDir: resolvedDocsDir, projectType, components, lang };
|
|
1865
1892
|
}
|
|
@@ -1867,6 +1894,51 @@ async function getConfig(cwd) {
|
|
|
1867
1894
|
}
|
|
1868
1895
|
return null;
|
|
1869
1896
|
}
|
|
1897
|
+
function normalizeTrailingBlankLines(content) {
|
|
1898
|
+
return content.replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
|
|
1899
|
+
}
|
|
1900
|
+
function sanitizeSpecForLocal(content) {
|
|
1901
|
+
const withoutIssueLine = content.split("\n").filter(
|
|
1902
|
+
(line) => !/^\s*-\s*\*\*(Issue Number|이슈 번호)\*\*\s*:/.test(line)
|
|
1903
|
+
).join("\n");
|
|
1904
|
+
return normalizeTrailingBlankLines(withoutIssueLine);
|
|
1905
|
+
}
|
|
1906
|
+
function sanitizeTasksForLocal(content, lang) {
|
|
1907
|
+
let next = content;
|
|
1908
|
+
next = next.replace(
|
|
1909
|
+
/^##\s+GitHub Issue\s*$/m,
|
|
1910
|
+
lang === "ko" ? "## \uB85C\uCEEC \uCD94\uC801 \uC815\uBCF4" : "## Local Tracking"
|
|
1911
|
+
);
|
|
1912
|
+
const lines = next.split("\n");
|
|
1913
|
+
const filtered = [];
|
|
1914
|
+
for (const line of lines) {
|
|
1915
|
+
if (/^\s*-\s*\*\*(Issue|PR|PR Status|PR 상태|Pre-PR Review|PR 전 리뷰)\*\*\s*:/.test(
|
|
1916
|
+
line
|
|
1917
|
+
)) {
|
|
1918
|
+
continue;
|
|
1919
|
+
}
|
|
1920
|
+
if (/^\s*-\s*(Example|Values)\s*:/.test(line)) continue;
|
|
1921
|
+
if (/^\s*-\s*(예|값)\s*:/.test(line)) continue;
|
|
1922
|
+
if (/^\s*-\s*Mark\s+`?Done`?/i.test(line)) continue;
|
|
1923
|
+
if (/^\s*-\s*사전 코드리뷰 완료 후/.test(line)) continue;
|
|
1924
|
+
filtered.push(line);
|
|
1925
|
+
}
|
|
1926
|
+
next = filtered.join("\n");
|
|
1927
|
+
next = next.replace(/feat\/\{issue-number\}-/g, "feat/").replace(/feat\/\{이슈번호\}-/g, "feat/").replace(/feat\/-/g, "feat/");
|
|
1928
|
+
return normalizeTrailingBlankLines(next);
|
|
1929
|
+
}
|
|
1930
|
+
async function patchMarkdownIfExists(filePath, transform) {
|
|
1931
|
+
if (!await fs2.pathExists(filePath)) return;
|
|
1932
|
+
const content = await fs2.readFile(filePath, "utf-8");
|
|
1933
|
+
await fs2.writeFile(filePath, transform(content), "utf-8");
|
|
1934
|
+
}
|
|
1935
|
+
async function applyLocalWorkflowTemplateToFeatureDir(featureDir, lang) {
|
|
1936
|
+
await patchMarkdownIfExists(path6.join(featureDir, "spec.md"), sanitizeSpecForLocal);
|
|
1937
|
+
await patchMarkdownIfExists(
|
|
1938
|
+
path6.join(featureDir, "tasks.md"),
|
|
1939
|
+
(content) => sanitizeTasksForLocal(content, lang)
|
|
1940
|
+
);
|
|
1941
|
+
}
|
|
1870
1942
|
|
|
1871
1943
|
// src/commands/feature.ts
|
|
1872
1944
|
function featureCommand(program2) {
|
|
@@ -2005,19 +2077,25 @@ async function runFeature(name, options) {
|
|
|
2005
2077
|
}
|
|
2006
2078
|
let featuresDir;
|
|
2007
2079
|
if (projectType === "multi") {
|
|
2008
|
-
featuresDir =
|
|
2080
|
+
featuresDir = path6.join(docsDir, "features", component);
|
|
2009
2081
|
} else {
|
|
2010
|
-
featuresDir =
|
|
2082
|
+
featuresDir = path6.join(docsDir, "features");
|
|
2011
2083
|
}
|
|
2012
2084
|
const featureFolderName = `${featureId}-${name}`;
|
|
2013
|
-
const featureDir =
|
|
2085
|
+
const featureDir = path6.join(featuresDir, featureFolderName);
|
|
2014
2086
|
if (await fs2.pathExists(featureDir)) {
|
|
2015
2087
|
throw createCliError(
|
|
2016
2088
|
"INVALID_ARGUMENT",
|
|
2017
2089
|
tr(lang, "cli", "feature.folderExists", { path: featureDir })
|
|
2018
2090
|
);
|
|
2019
2091
|
}
|
|
2020
|
-
const featureBasePath =
|
|
2092
|
+
const featureBasePath = path6.join(
|
|
2093
|
+
getTemplatesDir(),
|
|
2094
|
+
lang,
|
|
2095
|
+
toTemplateProjectType(projectType),
|
|
2096
|
+
"features",
|
|
2097
|
+
"feature-base"
|
|
2098
|
+
);
|
|
2021
2099
|
if (!await fs2.pathExists(featureBasePath)) {
|
|
2022
2100
|
throw createCliError("DOCS_NOT_FOUND", tr(lang, "cli", "feature.baseNotFound"));
|
|
2023
2101
|
}
|
|
@@ -2069,7 +2147,7 @@ async function runFeature(name, options) {
|
|
|
2069
2147
|
featureName: name,
|
|
2070
2148
|
component: projectType === "multi" ? component : void 0,
|
|
2071
2149
|
featurePath: featureDir,
|
|
2072
|
-
featurePathFromDocs:
|
|
2150
|
+
featurePathFromDocs: path6.relative(docsDir, featureDir)
|
|
2073
2151
|
};
|
|
2074
2152
|
},
|
|
2075
2153
|
{ owner: "feature" }
|
|
@@ -2081,9 +2159,9 @@ function sleep2(ms) {
|
|
|
2081
2159
|
async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
|
|
2082
2160
|
const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
|
|
2083
2161
|
const candidates = [
|
|
2084
|
-
...explicitDocsDir ? [
|
|
2085
|
-
|
|
2086
|
-
|
|
2162
|
+
...explicitDocsDir ? [path6.resolve(explicitDocsDir)] : [],
|
|
2163
|
+
path6.resolve(cwd, "docs"),
|
|
2164
|
+
path6.resolve(cwd)
|
|
2087
2165
|
];
|
|
2088
2166
|
const endAt = Date.now() + timeoutMs;
|
|
2089
2167
|
while (Date.now() < endAt) {
|
|
@@ -2110,11 +2188,11 @@ async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
|
|
|
2110
2188
|
return getConfig(cwd);
|
|
2111
2189
|
}
|
|
2112
2190
|
async function getNextFeatureId(docsDir, projectType, components) {
|
|
2113
|
-
const featuresDir =
|
|
2191
|
+
const featuresDir = path6.join(docsDir, "features");
|
|
2114
2192
|
let max = 0;
|
|
2115
2193
|
const scanDirs = [];
|
|
2116
2194
|
if (projectType === "multi") {
|
|
2117
|
-
scanDirs.push(...components.map((component) =>
|
|
2195
|
+
scanDirs.push(...components.map((component) => path6.join(featuresDir, component)));
|
|
2118
2196
|
} else {
|
|
2119
2197
|
scanDirs.push(featuresDir);
|
|
2120
2198
|
}
|
|
@@ -2136,6 +2214,7 @@ async function getNextFeatureId(docsDir, projectType, components) {
|
|
|
2136
2214
|
}
|
|
2137
2215
|
|
|
2138
2216
|
// src/utils/workflow.ts
|
|
2217
|
+
var DEFAULT_PRE_PR_REVIEW_SKILLS = ["code-review-excellence"];
|
|
2139
2218
|
function resolveWorkflowPolicy(workflow) {
|
|
2140
2219
|
const mode = workflow?.mode === "local" ? "local" : "github";
|
|
2141
2220
|
const policy = mode === "local" ? {
|
|
@@ -2182,6 +2261,28 @@ function resolveCodeDirtyScopePolicy(workflow, projectType) {
|
|
|
2182
2261
|
}
|
|
2183
2262
|
return projectType === "multi" ? "component" : "repo";
|
|
2184
2263
|
}
|
|
2264
|
+
function normalizeSkillList(input) {
|
|
2265
|
+
if (!Array.isArray(input)) return [];
|
|
2266
|
+
const deduped = /* @__PURE__ */ new Set();
|
|
2267
|
+
for (const raw of input) {
|
|
2268
|
+
const value = String(raw || "").trim();
|
|
2269
|
+
if (!value) continue;
|
|
2270
|
+
deduped.add(value);
|
|
2271
|
+
}
|
|
2272
|
+
return [...deduped];
|
|
2273
|
+
}
|
|
2274
|
+
function resolvePrePrReviewPolicy(workflow) {
|
|
2275
|
+
const workflowPolicy = resolveWorkflowPolicy(workflow);
|
|
2276
|
+
const configured = workflow?.prePrReview;
|
|
2277
|
+
const configuredSkills = normalizeSkillList(configured?.skills);
|
|
2278
|
+
const configuredEnabled = typeof configured?.enabled === "boolean" ? configured.enabled : workflowPolicy.requirePr;
|
|
2279
|
+
return {
|
|
2280
|
+
enabled: workflowPolicy.requirePr ? configuredEnabled : false,
|
|
2281
|
+
skills: configuredSkills.length > 0 ? configuredSkills : DEFAULT_PRE_PR_REVIEW_SKILLS,
|
|
2282
|
+
fallback: configured?.fallback === "builtin-checklist" ? configured.fallback : "builtin-checklist",
|
|
2283
|
+
blockOnFindings: typeof configured?.blockOnFindings === "boolean" ? configured.blockOnFindings : true
|
|
2284
|
+
};
|
|
2285
|
+
}
|
|
2185
2286
|
|
|
2186
2287
|
// src/utils/context/steps.ts
|
|
2187
2288
|
function isCompletionChecklistDone(feature) {
|
|
@@ -2196,11 +2297,18 @@ function isImplementationDone(feature) {
|
|
|
2196
2297
|
function isPrMetadataConfigured(feature) {
|
|
2197
2298
|
return feature.docs.prFieldExists && feature.docs.prStatusFieldExists;
|
|
2198
2299
|
}
|
|
2199
|
-
function isFeatureDone(feature, workflowPolicy) {
|
|
2200
|
-
return feature.specStatus === "Approved" && feature.planStatus === "Approved" && !feature.git.docsHasUncommittedChanges && !feature.git.projectHasUncommittedChanges && feature.docs.tasksExists && feature.tasks.total > 0 && feature.tasks.total === feature.tasks.done && isCompletionChecklistDone(feature) && isTasksDocApproved(feature) && (!workflowPolicy.requireIssue || !!feature.issueNumber) && (!workflowPolicy.requirePr || isPrMetadataConfigured(feature) && !!feature.pr.link) && (!workflowPolicy.requireReview || feature.pr.status === "Approved");
|
|
2300
|
+
function isFeatureDone(feature, workflowPolicy, prePrReviewPolicy) {
|
|
2301
|
+
return feature.specStatus === "Approved" && feature.planStatus === "Approved" && !feature.git.docsHasUncommittedChanges && !feature.git.projectHasUncommittedChanges && feature.docs.tasksExists && feature.tasks.total > 0 && feature.tasks.total === feature.tasks.done && isCompletionChecklistDone(feature) && isTasksDocApproved(feature) && (!workflowPolicy.requireIssue || !!feature.issueNumber) && (!workflowPolicy.requirePr || isPrMetadataConfigured(feature) && !!feature.pr.link) && (!workflowPolicy.requireReview || feature.pr.status === "Approved") && (!prePrReviewPolicy.enabled || feature.docs.prePrReviewFieldExists && feature.prePrReview.status === "Done");
|
|
2302
|
+
}
|
|
2303
|
+
function formatSkillList(skills) {
|
|
2304
|
+
return skills.join(", ");
|
|
2305
|
+
}
|
|
2306
|
+
function getFindingsPolicyText(lang, blockOnFindings) {
|
|
2307
|
+
return blockOnFindings ? tr(lang, "messages", "prePrReviewFindingsBlock") : tr(lang, "messages", "prePrReviewFindingsWarn");
|
|
2201
2308
|
}
|
|
2202
2309
|
function getStepDefinitions(lang, workflow) {
|
|
2203
2310
|
const workflowPolicy = resolveWorkflowPolicy(workflow);
|
|
2311
|
+
const prePrReviewPolicy = resolvePrePrReviewPolicy(workflow);
|
|
2204
2312
|
return [
|
|
2205
2313
|
{
|
|
2206
2314
|
step: 1,
|
|
@@ -2401,10 +2509,10 @@ function getStepDefinitions(lang, workflow) {
|
|
|
2401
2509
|
step: 9,
|
|
2402
2510
|
name: tr(lang, "steps", "branchCreate"),
|
|
2403
2511
|
checklist: {
|
|
2404
|
-
done: (f) => !workflowPolicy.requireBranch || f.git.onExpectedBranch || isImplementationDone(f) || isFeatureDone(f, workflowPolicy)
|
|
2512
|
+
done: (f) => !workflowPolicy.requireBranch || f.git.onExpectedBranch || isImplementationDone(f) || isFeatureDone(f, workflowPolicy, prePrReviewPolicy)
|
|
2405
2513
|
},
|
|
2406
2514
|
current: {
|
|
2407
|
-
when: (f) => workflowPolicy.requireBranch && !!f.issueNumber && f.tasks.total > 0 && f.tasks.done < f.tasks.total && !isFeatureDone(f, workflowPolicy) && (!f.git.projectBranchAvailable || !f.git.onExpectedBranch),
|
|
2515
|
+
when: (f) => workflowPolicy.requireBranch && !!f.issueNumber && f.tasks.total > 0 && f.tasks.done < f.tasks.total && !isFeatureDone(f, workflowPolicy, prePrReviewPolicy) && (!f.git.projectBranchAvailable || !f.git.onExpectedBranch),
|
|
2408
2516
|
actions: (f) => {
|
|
2409
2517
|
if (!f.git.projectBranchAvailable || !f.git.projectGitCwd) {
|
|
2410
2518
|
return [
|
|
@@ -2499,6 +2607,34 @@ function getStepDefinitions(lang, workflow) {
|
|
|
2499
2607
|
}
|
|
2500
2608
|
];
|
|
2501
2609
|
}
|
|
2610
|
+
if (f.git.projectHasUncommittedChanges) {
|
|
2611
|
+
if (!f.git.projectGitCwd) {
|
|
2612
|
+
return [
|
|
2613
|
+
{
|
|
2614
|
+
type: "instruction",
|
|
2615
|
+
category: "task_execute",
|
|
2616
|
+
message: tr(lang, "messages", "standaloneNeedsProjectRoot")
|
|
2617
|
+
}
|
|
2618
|
+
];
|
|
2619
|
+
}
|
|
2620
|
+
return [
|
|
2621
|
+
{
|
|
2622
|
+
type: "command",
|
|
2623
|
+
category: "task_execute",
|
|
2624
|
+
requiresUserCheck: true,
|
|
2625
|
+
scope: "project",
|
|
2626
|
+
cwd: f.git.projectGitCwd,
|
|
2627
|
+
cmd: f.issueNumber ? tr(lang, "messages", "projectCommitIssueUpdate", {
|
|
2628
|
+
projectGitCwd: f.git.projectGitCwd,
|
|
2629
|
+
issueNumber: f.issueNumber,
|
|
2630
|
+
folderName: f.folderName
|
|
2631
|
+
}) : tr(lang, "messages", "projectCommitUpdate", {
|
|
2632
|
+
projectGitCwd: f.git.projectGitCwd,
|
|
2633
|
+
folderName: f.folderName
|
|
2634
|
+
})
|
|
2635
|
+
}
|
|
2636
|
+
];
|
|
2637
|
+
}
|
|
2502
2638
|
return [
|
|
2503
2639
|
{
|
|
2504
2640
|
type: "instruction",
|
|
@@ -2587,6 +2723,61 @@ function getStepDefinitions(lang, workflow) {
|
|
|
2587
2723
|
},
|
|
2588
2724
|
{
|
|
2589
2725
|
step: 12,
|
|
2726
|
+
name: tr(lang, "steps", "prePrReview"),
|
|
2727
|
+
checklist: {
|
|
2728
|
+
done: (f) => !prePrReviewPolicy.enabled || f.docs.prePrReviewFieldExists && f.prePrReview.status === "Done"
|
|
2729
|
+
},
|
|
2730
|
+
current: {
|
|
2731
|
+
when: (f) => prePrReviewPolicy.enabled && workflowPolicy.requirePr && f.docs.tasksExists && f.tasks.total > 0 && f.tasks.total === f.tasks.done && isCompletionChecklistDone(f) && !f.git.docsHasUncommittedChanges && !f.git.projectHasUncommittedChanges && (!isPrMetadataConfigured(f) || !f.pr.link) && (!f.docs.prePrReviewFieldExists || f.prePrReview.status !== "Done"),
|
|
2732
|
+
actions: (f) => {
|
|
2733
|
+
if (!prePrReviewPolicy.enabled) return [];
|
|
2734
|
+
if (!f.docs.prePrReviewFieldExists) {
|
|
2735
|
+
return [
|
|
2736
|
+
{
|
|
2737
|
+
type: "instruction",
|
|
2738
|
+
category: "pr_metadata_migrate",
|
|
2739
|
+
requiresUserCheck: true,
|
|
2740
|
+
message: tr(lang, "messages", "prePrReviewFieldMissing")
|
|
2741
|
+
}
|
|
2742
|
+
];
|
|
2743
|
+
}
|
|
2744
|
+
if (!prePrReviewPolicy.skills.length) {
|
|
2745
|
+
return [
|
|
2746
|
+
{
|
|
2747
|
+
type: "instruction",
|
|
2748
|
+
category: "pre_pr_review",
|
|
2749
|
+
requiresUserCheck: true,
|
|
2750
|
+
message: tr(lang, "messages", "prePrReviewRun", {
|
|
2751
|
+
skills: "code-review-excellence",
|
|
2752
|
+
fallback: prePrReviewPolicy.fallback,
|
|
2753
|
+
findingsPolicy: getFindingsPolicyText(
|
|
2754
|
+
lang,
|
|
2755
|
+
prePrReviewPolicy.blockOnFindings
|
|
2756
|
+
)
|
|
2757
|
+
})
|
|
2758
|
+
}
|
|
2759
|
+
];
|
|
2760
|
+
}
|
|
2761
|
+
return [
|
|
2762
|
+
{
|
|
2763
|
+
type: "instruction",
|
|
2764
|
+
category: "pre_pr_review",
|
|
2765
|
+
requiresUserCheck: true,
|
|
2766
|
+
message: tr(lang, "messages", "prePrReviewRun", {
|
|
2767
|
+
skills: formatSkillList(prePrReviewPolicy.skills),
|
|
2768
|
+
fallback: prePrReviewPolicy.fallback,
|
|
2769
|
+
findingsPolicy: getFindingsPolicyText(
|
|
2770
|
+
lang,
|
|
2771
|
+
prePrReviewPolicy.blockOnFindings
|
|
2772
|
+
)
|
|
2773
|
+
})
|
|
2774
|
+
}
|
|
2775
|
+
];
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
},
|
|
2779
|
+
{
|
|
2780
|
+
step: 13,
|
|
2590
2781
|
name: tr(lang, "steps", "prCreate"),
|
|
2591
2782
|
checklist: {
|
|
2592
2783
|
done: (f) => !workflowPolicy.requirePr || isPrMetadataConfigured(f) && !!f.pr.link
|
|
@@ -2616,7 +2807,7 @@ function getStepDefinitions(lang, workflow) {
|
|
|
2616
2807
|
}
|
|
2617
2808
|
},
|
|
2618
2809
|
{
|
|
2619
|
-
step:
|
|
2810
|
+
step: 14,
|
|
2620
2811
|
name: tr(lang, "steps", "codeReview"),
|
|
2621
2812
|
checklist: {
|
|
2622
2813
|
done: (f) => !workflowPolicy.requireReview || isPrMetadataConfigured(f) && f.pr.status === "Approved"
|
|
@@ -2654,11 +2845,13 @@ function getStepDefinitions(lang, workflow) {
|
|
|
2654
2845
|
}
|
|
2655
2846
|
},
|
|
2656
2847
|
{
|
|
2657
|
-
step:
|
|
2848
|
+
step: 15,
|
|
2658
2849
|
name: tr(lang, "steps", "featureDone"),
|
|
2659
|
-
checklist: {
|
|
2850
|
+
checklist: {
|
|
2851
|
+
done: (f) => isFeatureDone(f, workflowPolicy, prePrReviewPolicy)
|
|
2852
|
+
},
|
|
2660
2853
|
current: {
|
|
2661
|
-
when: (f) => isFeatureDone(f, workflowPolicy),
|
|
2854
|
+
when: (f) => isFeatureDone(f, workflowPolicy, prePrReviewPolicy),
|
|
2662
2855
|
actions: () => [
|
|
2663
2856
|
{
|
|
2664
2857
|
type: "instruction",
|
|
@@ -2889,6 +3082,14 @@ function parseDocStatus(value) {
|
|
|
2889
3082
|
if (normalized === "review") return "Review";
|
|
2890
3083
|
return "Approved";
|
|
2891
3084
|
}
|
|
3085
|
+
function parsePrePrReviewStatus(value) {
|
|
3086
|
+
if (!value) return void 0;
|
|
3087
|
+
const trimmed = value.trim();
|
|
3088
|
+
if (!trimmed || trimmed.includes("|")) return void 0;
|
|
3089
|
+
if (/^done$/i.test(trimmed)) return "Done";
|
|
3090
|
+
if (/^pending$/i.test(trimmed)) return "Pending";
|
|
3091
|
+
return void 0;
|
|
3092
|
+
}
|
|
2892
3093
|
function parseIssueNumber(value) {
|
|
2893
3094
|
if (!value) return void 0;
|
|
2894
3095
|
const match = value.match(/#?(\d+)/);
|
|
@@ -2906,13 +3107,13 @@ function parsePrLink(value) {
|
|
|
2906
3107
|
return trimmed;
|
|
2907
3108
|
}
|
|
2908
3109
|
function normalizeGitPath(value) {
|
|
2909
|
-
return value.split(
|
|
3110
|
+
return value.split(path6.sep).join("/");
|
|
2910
3111
|
}
|
|
2911
3112
|
function resolveProjectStatusPaths(projectGitCwd, docsDir) {
|
|
2912
|
-
const relativeDocsDir =
|
|
3113
|
+
const relativeDocsDir = path6.relative(projectGitCwd, docsDir);
|
|
2913
3114
|
if (!relativeDocsDir) return [];
|
|
2914
|
-
if (
|
|
2915
|
-
if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${
|
|
3115
|
+
if (path6.isAbsolute(relativeDocsDir)) return [];
|
|
3116
|
+
if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${path6.sep}`)) {
|
|
2916
3117
|
return [];
|
|
2917
3118
|
}
|
|
2918
3119
|
const normalizedDocsDir = normalizeGitPath(relativeDocsDir).replace(/\/+$/, "");
|
|
@@ -2944,16 +3145,16 @@ async function resolveComponentStatusPaths(projectGitCwd, component, workflow) {
|
|
|
2944
3145
|
const normalizedCandidates = uniqueNormalizedPaths(
|
|
2945
3146
|
candidates.map((candidate) => {
|
|
2946
3147
|
if (!candidate) return "";
|
|
2947
|
-
if (!
|
|
2948
|
-
const relative =
|
|
3148
|
+
if (!path6.isAbsolute(candidate)) return candidate;
|
|
3149
|
+
const relative = path6.relative(projectGitCwd, candidate);
|
|
2949
3150
|
if (!relative) return "";
|
|
2950
|
-
if (relative === ".." || relative.startsWith(`..${
|
|
3151
|
+
if (relative === ".." || relative.startsWith(`..${path6.sep}`)) return "";
|
|
2951
3152
|
return relative;
|
|
2952
3153
|
}).filter(Boolean)
|
|
2953
3154
|
);
|
|
2954
3155
|
const existing = [];
|
|
2955
3156
|
for (const candidate of normalizedCandidates) {
|
|
2956
|
-
if (await fs2.pathExists(
|
|
3157
|
+
if (await fs2.pathExists(path6.join(projectGitCwd, candidate))) {
|
|
2957
3158
|
existing.push(candidate);
|
|
2958
3159
|
}
|
|
2959
3160
|
}
|
|
@@ -3015,13 +3216,14 @@ function isPrMetadataConfigured2(feature) {
|
|
|
3015
3216
|
async function parseFeature(featurePath, type, context, options) {
|
|
3016
3217
|
const lang = options.lang;
|
|
3017
3218
|
const workflowPolicy = resolveWorkflowPolicy(options.workflow);
|
|
3018
|
-
const
|
|
3219
|
+
const prePrReviewPolicy = resolvePrePrReviewPolicy(options.workflow);
|
|
3220
|
+
const folderName = path6.basename(featurePath);
|
|
3019
3221
|
const match = folderName.match(/^(F\d+)-(.+)$/);
|
|
3020
3222
|
const id = match?.[1];
|
|
3021
3223
|
const slug = match?.[2] || folderName;
|
|
3022
|
-
const specPath =
|
|
3023
|
-
const planPath =
|
|
3024
|
-
const tasksPath =
|
|
3224
|
+
const specPath = path6.join(featurePath, "spec.md");
|
|
3225
|
+
const planPath = path6.join(featurePath, "plan.md");
|
|
3226
|
+
const tasksPath = path6.join(featurePath, "tasks.md");
|
|
3025
3227
|
let specStatus;
|
|
3026
3228
|
let issueNumber;
|
|
3027
3229
|
const specExists = await fs2.pathExists(specPath);
|
|
@@ -3046,10 +3248,12 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
3046
3248
|
let tasksDocStatus;
|
|
3047
3249
|
let tasksDocStatusFieldExists = false;
|
|
3048
3250
|
let completionChecklist;
|
|
3251
|
+
let prePrReviewStatus;
|
|
3049
3252
|
let prLink;
|
|
3050
3253
|
let prStatus;
|
|
3051
3254
|
let prFieldExists = false;
|
|
3052
3255
|
let prStatusFieldExists = false;
|
|
3256
|
+
let prePrReviewFieldExists = false;
|
|
3053
3257
|
if (tasksExists) {
|
|
3054
3258
|
const content = await fs2.readFile(tasksPath, "utf-8");
|
|
3055
3259
|
const { summary, activeTask: active, nextTodoTask: nextTodo } = parseTasks(content);
|
|
@@ -3073,6 +3277,15 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
3073
3277
|
const prStatusValue = extractFirstSpecValue(content, ["PR \uC0C1\uD0DC", "PR Status"]);
|
|
3074
3278
|
prStatusFieldExists = hasAnySpecKey(content, ["PR \uC0C1\uD0DC", "PR Status"]);
|
|
3075
3279
|
prStatus = parseDocStatus(prStatusValue);
|
|
3280
|
+
const prePrReviewValue = extractFirstSpecValue(content, [
|
|
3281
|
+
"PR \uC804 \uB9AC\uBDF0",
|
|
3282
|
+
"Pre-PR Review"
|
|
3283
|
+
]);
|
|
3284
|
+
prePrReviewFieldExists = hasAnySpecKey(content, [
|
|
3285
|
+
"PR \uC804 \uB9AC\uBDF0",
|
|
3286
|
+
"Pre-PR Review"
|
|
3287
|
+
]);
|
|
3288
|
+
prePrReviewStatus = parsePrePrReviewStatus(prePrReviewValue);
|
|
3076
3289
|
}
|
|
3077
3290
|
const warnings = [];
|
|
3078
3291
|
if (context.projectBranchAvailable === false) {
|
|
@@ -3084,7 +3297,7 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
3084
3297
|
slug,
|
|
3085
3298
|
folderName
|
|
3086
3299
|
);
|
|
3087
|
-
const relativeFeaturePathFromDocs =
|
|
3300
|
+
const relativeFeaturePathFromDocs = path6.relative(context.docsDir, featurePath);
|
|
3088
3301
|
const docsPathIgnored = isGitPathIgnored(
|
|
3089
3302
|
context.docsGitCwd,
|
|
3090
3303
|
relativeFeaturePathFromDocs
|
|
@@ -3128,6 +3341,9 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
3128
3341
|
if (tasksExists && workflowPolicy.requirePr && (!prFieldExists || !prStatusFieldExists)) {
|
|
3129
3342
|
warnings.push(tr(lang, "warnings", "legacyTasksPrFields"));
|
|
3130
3343
|
}
|
|
3344
|
+
if (tasksExists && prePrReviewPolicy.enabled && !prePrReviewFieldExists) {
|
|
3345
|
+
warnings.push(tr(lang, "warnings", "legacyTasksPrePrReviewField"));
|
|
3346
|
+
}
|
|
3131
3347
|
if (tasksExists && !tasksDocStatusFieldExists) {
|
|
3132
3348
|
warnings.push(tr(lang, "warnings", "legacyTasksDocStatusField"));
|
|
3133
3349
|
}
|
|
@@ -3139,7 +3355,7 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
3139
3355
|
}
|
|
3140
3356
|
const tasksDocApproved = !tasksDocStatusFieldExists || tasksDocStatus === "Approved";
|
|
3141
3357
|
const implementationDone = tasksExists && tasksSummary.total > 0 && tasksSummary.total === tasksSummary.done && isCompletionChecklistDone2({ completionChecklist }) && tasksDocApproved;
|
|
3142
|
-
const workflowDone = implementationDone && !docsHasUncommittedChanges && !projectHasUncommittedChanges && specStatus === "Approved" && planStatus === "Approved" && (!workflowPolicy.requireIssue || !!issueNumber) && (!workflowPolicy.requirePr || isPrMetadataConfigured2({ docs: { prFieldExists, prStatusFieldExists } }) && !!prLink) && (!workflowPolicy.requireReview || prStatus === "Approved");
|
|
3358
|
+
const workflowDone = implementationDone && !docsHasUncommittedChanges && !projectHasUncommittedChanges && specStatus === "Approved" && planStatus === "Approved" && (!workflowPolicy.requireIssue || !!issueNumber) && (!workflowPolicy.requirePr || isPrMetadataConfigured2({ docs: { prFieldExists, prStatusFieldExists } }) && !!prLink) && (!workflowPolicy.requireReview || prStatus === "Approved") && (!prePrReviewPolicy.enabled || prePrReviewFieldExists && prePrReviewStatus === "Done");
|
|
3143
3359
|
if (implementationDone && !workflowDone) {
|
|
3144
3360
|
if (specStatus !== "Approved") {
|
|
3145
3361
|
warnings.push(tr(lang, "warnings", "workflowSpecNotApproved"));
|
|
@@ -3162,6 +3378,13 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
3162
3378
|
}
|
|
3163
3379
|
}
|
|
3164
3380
|
}
|
|
3381
|
+
if (prePrReviewPolicy.enabled) {
|
|
3382
|
+
if (!prePrReviewFieldExists) {
|
|
3383
|
+
warnings.push(tr(lang, "warnings", "workflowPrePrReviewMissing"));
|
|
3384
|
+
} else if (prePrReviewStatus !== "Done") {
|
|
3385
|
+
warnings.push(tr(lang, "warnings", "workflowPrePrReviewNotDone"));
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3165
3388
|
}
|
|
3166
3389
|
const featureState = {
|
|
3167
3390
|
id,
|
|
@@ -3181,6 +3404,9 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
3181
3404
|
activeTask,
|
|
3182
3405
|
nextTodoTask,
|
|
3183
3406
|
completionChecklist,
|
|
3407
|
+
prePrReview: {
|
|
3408
|
+
status: prePrReviewStatus
|
|
3409
|
+
},
|
|
3184
3410
|
pr: { link: prLink, status: prStatus },
|
|
3185
3411
|
git: {
|
|
3186
3412
|
docsBranch: context.docsBranch,
|
|
@@ -3201,7 +3427,8 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
3201
3427
|
tasksExists,
|
|
3202
3428
|
tasksDocStatusFieldExists,
|
|
3203
3429
|
prFieldExists,
|
|
3204
|
-
prStatusFieldExists
|
|
3430
|
+
prStatusFieldExists,
|
|
3431
|
+
prePrReviewFieldExists
|
|
3205
3432
|
}
|
|
3206
3433
|
};
|
|
3207
3434
|
const { currentStep, actions, nextAction } = resolveFeatureProgress(
|
|
@@ -3335,13 +3562,13 @@ async function runStatus(options) {
|
|
|
3335
3562
|
);
|
|
3336
3563
|
}
|
|
3337
3564
|
const { docsDir, projectType, projectName, lang } = config;
|
|
3338
|
-
const featuresDir =
|
|
3565
|
+
const featuresDir = path6.join(docsDir, "features");
|
|
3339
3566
|
const scan = await scanFeatures(config);
|
|
3340
3567
|
const features = [];
|
|
3341
3568
|
const idMap = /* @__PURE__ */ new Map();
|
|
3342
3569
|
for (const f of scan.features) {
|
|
3343
3570
|
const id = f.id || "UNKNOWN";
|
|
3344
|
-
const relPath =
|
|
3571
|
+
const relPath = path6.relative(docsDir, f.path);
|
|
3345
3572
|
if (!idMap.has(id)) idMap.set(id, []);
|
|
3346
3573
|
idMap.get(id).push(relPath);
|
|
3347
3574
|
if (!f.docs.specExists || !f.docs.tasksExists) continue;
|
|
@@ -3422,7 +3649,7 @@ async function runStatus(options) {
|
|
|
3422
3649
|
}
|
|
3423
3650
|
console.log();
|
|
3424
3651
|
if (options.write) {
|
|
3425
|
-
const outputPath =
|
|
3652
|
+
const outputPath = path6.join(featuresDir, "status.md");
|
|
3426
3653
|
const date = getLocalDateString();
|
|
3427
3654
|
const content = [
|
|
3428
3655
|
"# Feature Status",
|
|
@@ -3450,7 +3677,7 @@ function escapeRegExp2(value) {
|
|
|
3450
3677
|
}
|
|
3451
3678
|
async function getFeatureNameFromSpec(featureDir, fallbackSlug, fallbackFolderName) {
|
|
3452
3679
|
try {
|
|
3453
|
-
const specPath =
|
|
3680
|
+
const specPath = path6.join(featureDir, "spec.md");
|
|
3454
3681
|
if (!await fs2.pathExists(specPath)) return fallbackSlug;
|
|
3455
3682
|
const content = await fs2.readFile(specPath, "utf-8");
|
|
3456
3683
|
const keys = ["\uAE30\uB2A5\uBA85", "Feature Name"];
|
|
@@ -3468,7 +3695,7 @@ async function getFeatureNameFromSpec(featureDir, fallbackSlug, fallbackFolderNa
|
|
|
3468
3695
|
return fallbackSlug || fallbackFolderName;
|
|
3469
3696
|
}
|
|
3470
3697
|
function updateCommand(program2) {
|
|
3471
|
-
program2.command("update").description("Update docs templates to the latest version").option("--agents", "Update agents/ folder only").option("--skills", "
|
|
3698
|
+
program2.command("update").description("Update docs templates to the latest version").option("--agents", "Update agents/ folder only").option("--skills", "Cleanup legacy agents/skills copies (CLI-managed)").option("--templates", "Cleanup legacy feature-base copies (CLI-managed)").option(
|
|
3472
3699
|
"-f, --force",
|
|
3473
3700
|
"Force overwrite even if docs has uncommitted changes"
|
|
3474
3701
|
).action(async (options) => {
|
|
@@ -3507,7 +3734,6 @@ async function runUpdate(options) {
|
|
|
3507
3734
|
getDocsLockPath(docsDir),
|
|
3508
3735
|
async () => {
|
|
3509
3736
|
const templatesDir = getTemplatesDir();
|
|
3510
|
-
const sourceDir = path13.join(templatesDir, lang, toTemplateProjectType(projectType));
|
|
3511
3737
|
const docsLockPath = getDocsLockPath(docsDir);
|
|
3512
3738
|
const forceOverwrite = !!options.force || await isDocsWorktreeCleanOrThrow(docsDir, lang, [docsLockPath]);
|
|
3513
3739
|
const hasExplicitSelection = !!(options.agents || options.skills || options.templates);
|
|
@@ -3522,78 +3748,93 @@ async function runUpdate(options) {
|
|
|
3522
3748
|
console.log();
|
|
3523
3749
|
let updatedCount = 0;
|
|
3524
3750
|
if (updateAgents) {
|
|
3525
|
-
|
|
3526
|
-
chalk6.blue(
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
);
|
|
3530
|
-
const commonAgentsBase = path13.join(templatesDir, lang, "common", "agents");
|
|
3531
|
-
const typeAgentsBase = path13.join(
|
|
3532
|
-
templatesDir,
|
|
3533
|
-
lang,
|
|
3534
|
-
toTemplateProjectType(projectType),
|
|
3535
|
-
"agents"
|
|
3536
|
-
);
|
|
3537
|
-
const targetAgentsBase = path13.join(docsDir, "agents");
|
|
3538
|
-
const commonAgents = agentsMode === "skills" ? path13.join(commonAgentsBase, "skills") : commonAgentsBase;
|
|
3539
|
-
const typeAgents = agentsMode === "skills" ? path13.join(typeAgentsBase, "skills") : typeAgentsBase;
|
|
3540
|
-
const targetAgents = agentsMode === "skills" ? path13.join(targetAgentsBase, "skills") : targetAgentsBase;
|
|
3541
|
-
const featurePath = projectType === "multi" ? isDefaultFullstackComponents(config.components || []) ? "docs/features/{be|fe}" : "docs/features/{component}" : "docs/features";
|
|
3542
|
-
const projectName = config.projectName ?? "{{projectName}}";
|
|
3543
|
-
const commonReplacements = {
|
|
3544
|
-
"{{projectName}}": projectName,
|
|
3545
|
-
"{{featurePath}}": featurePath
|
|
3546
|
-
};
|
|
3547
|
-
const typeReplacements = {
|
|
3548
|
-
"{{projectName}}": projectName
|
|
3549
|
-
};
|
|
3550
|
-
if (await fs2.pathExists(commonAgents)) {
|
|
3551
|
-
const count = await updateFolder(
|
|
3552
|
-
commonAgents,
|
|
3553
|
-
targetAgents,
|
|
3554
|
-
forceOverwrite,
|
|
3555
|
-
commonReplacements,
|
|
3556
|
-
lang
|
|
3751
|
+
if (agentsMode === "skills") {
|
|
3752
|
+
console.log(chalk6.blue(tr(lang, "cli", "update.updatingSkills")));
|
|
3753
|
+
console.log(
|
|
3754
|
+
chalk6.gray(tr(lang, "cli", "update.engineManagedSkillsBuiltin"))
|
|
3557
3755
|
);
|
|
3558
|
-
|
|
3756
|
+
console.log(chalk6.green(` \u2705 ${tr(lang, "cli", "update.skillsUpdated")}`));
|
|
3757
|
+
} else {
|
|
3758
|
+
console.log(chalk6.blue(tr(lang, "cli", "update.updatingAgents")));
|
|
3559
3759
|
}
|
|
3560
|
-
if (
|
|
3561
|
-
const
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3760
|
+
if (agentsMode === "all") {
|
|
3761
|
+
const commonAgentsBase = path6.join(templatesDir, lang, "common", "agents");
|
|
3762
|
+
const typeAgentsBase = path6.join(
|
|
3763
|
+
templatesDir,
|
|
3764
|
+
lang,
|
|
3765
|
+
toTemplateProjectType(projectType),
|
|
3766
|
+
"agents"
|
|
3567
3767
|
);
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
console.log(chalk6.blue(tr(lang, "cli", "update.updatingFeatureBase")));
|
|
3578
|
-
const sourceFeatureBase = path13.join(sourceDir, "features", "feature-base");
|
|
3579
|
-
const targetFeatureBase = path13.join(docsDir, "features", "feature-base");
|
|
3580
|
-
if (await fs2.pathExists(sourceFeatureBase)) {
|
|
3581
|
-
const replacements = {
|
|
3582
|
-
"{{projectName}}": config.projectName ?? "{{projectName}}"
|
|
3768
|
+
const targetAgentsBase = path6.join(docsDir, "agents");
|
|
3769
|
+
const commonAgents = agentsMode === "skills" ? path6.join(commonAgentsBase, "skills") : commonAgentsBase;
|
|
3770
|
+
const typeAgents = agentsMode === "skills" ? path6.join(typeAgentsBase, "skills") : typeAgentsBase;
|
|
3771
|
+
const targetAgents = agentsMode === "skills" ? path6.join(targetAgentsBase, "skills") : targetAgentsBase;
|
|
3772
|
+
const featurePath = projectType === "multi" ? isDefaultFullstackComponents(config.components || []) ? "docs/features/{be|fe}" : "docs/features/{component}" : "docs/features";
|
|
3773
|
+
const projectName = config.projectName ?? "{{projectName}}";
|
|
3774
|
+
const commonReplacements = {
|
|
3775
|
+
"{{projectName}}": projectName,
|
|
3776
|
+
"{{featurePath}}": featurePath
|
|
3583
3777
|
};
|
|
3584
|
-
const
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3778
|
+
const typeReplacements = {
|
|
3779
|
+
"{{projectName}}": projectName
|
|
3780
|
+
};
|
|
3781
|
+
if (await fs2.pathExists(commonAgents)) {
|
|
3782
|
+
const count = await updateFolder(
|
|
3783
|
+
commonAgents,
|
|
3784
|
+
targetAgents,
|
|
3785
|
+
forceOverwrite,
|
|
3786
|
+
commonReplacements,
|
|
3787
|
+
lang,
|
|
3788
|
+
{
|
|
3789
|
+
protectedFiles: /* @__PURE__ */ new Set([
|
|
3790
|
+
"custom.md",
|
|
3791
|
+
"constitution.md",
|
|
3792
|
+
...ENGINE_MANAGED_AGENT_FILES
|
|
3793
|
+
]),
|
|
3794
|
+
skipDirectories: new Set(ENGINE_MANAGED_AGENT_DIRS)
|
|
3795
|
+
}
|
|
3796
|
+
);
|
|
3797
|
+
updatedCount += count;
|
|
3798
|
+
}
|
|
3799
|
+
if (await fs2.pathExists(typeAgents)) {
|
|
3800
|
+
const count = await updateFolder(
|
|
3801
|
+
typeAgents,
|
|
3802
|
+
targetAgents,
|
|
3803
|
+
forceOverwrite,
|
|
3804
|
+
typeReplacements,
|
|
3805
|
+
lang,
|
|
3806
|
+
{
|
|
3807
|
+
protectedFiles: /* @__PURE__ */ new Set([
|
|
3808
|
+
"custom.md",
|
|
3809
|
+
"constitution.md",
|
|
3810
|
+
...ENGINE_MANAGED_AGENT_FILES
|
|
3811
|
+
]),
|
|
3812
|
+
skipDirectories: new Set(ENGINE_MANAGED_AGENT_DIRS)
|
|
3813
|
+
}
|
|
3814
|
+
);
|
|
3815
|
+
updatedCount += count;
|
|
3816
|
+
}
|
|
3592
3817
|
console.log(
|
|
3593
|
-
chalk6.green(
|
|
3818
|
+
chalk6.green(
|
|
3819
|
+
` \u2705 ${agentsMode === "skills" ? tr(lang, "cli", "update.skillsUpdated") : tr(lang, "cli", "update.agentsUpdated")}`
|
|
3820
|
+
)
|
|
3594
3821
|
);
|
|
3595
3822
|
}
|
|
3596
3823
|
}
|
|
3824
|
+
if (updateTemplates) {
|
|
3825
|
+
console.log(chalk6.blue(tr(lang, "cli", "update.updatingFeatureBase")));
|
|
3826
|
+
console.log(chalk6.gray(tr(lang, "cli", "update.engineManagedFeatureBaseBuiltin")));
|
|
3827
|
+
}
|
|
3828
|
+
const pruned = await pruneEngineManagedDocs(docsDir);
|
|
3829
|
+
if (pruned.length > 0) {
|
|
3830
|
+
console.log(
|
|
3831
|
+
chalk6.gray(
|
|
3832
|
+
` - ${tr(lang, "cli", "update.engineManagedPruned", {
|
|
3833
|
+
count: pruned.length
|
|
3834
|
+
})}`
|
|
3835
|
+
)
|
|
3836
|
+
);
|
|
3837
|
+
}
|
|
3597
3838
|
console.log();
|
|
3598
3839
|
console.log(
|
|
3599
3840
|
chalk6.green(`\u2705 ${tr(lang, "cli", "update.updatedTotal", { count: updatedCount })}`)
|
|
@@ -3602,14 +3843,15 @@ async function runUpdate(options) {
|
|
|
3602
3843
|
{ owner: "update" }
|
|
3603
3844
|
);
|
|
3604
3845
|
}
|
|
3605
|
-
async function updateFolder(sourceDir, targetDir, force, replacements, lang = DEFAULT_LANG) {
|
|
3606
|
-
const protectedFiles = /* @__PURE__ */ new Set(["custom.md", "constitution.md"]);
|
|
3846
|
+
async function updateFolder(sourceDir, targetDir, force, replacements, lang = DEFAULT_LANG, options = {}) {
|
|
3847
|
+
const protectedFiles = options.protectedFiles ?? /* @__PURE__ */ new Set(["custom.md", "constitution.md"]);
|
|
3848
|
+
const skipDirectories = options.skipDirectories ?? /* @__PURE__ */ new Set();
|
|
3607
3849
|
await fs2.ensureDir(targetDir);
|
|
3608
3850
|
const files = await fs2.readdir(sourceDir);
|
|
3609
3851
|
let updatedCount = 0;
|
|
3610
3852
|
for (const file of files) {
|
|
3611
|
-
const sourcePath =
|
|
3612
|
-
const targetPath =
|
|
3853
|
+
const sourcePath = path6.join(sourceDir, file);
|
|
3854
|
+
const targetPath = path6.join(targetDir, file);
|
|
3613
3855
|
const stat = await fs2.stat(sourcePath);
|
|
3614
3856
|
if (stat.isFile()) {
|
|
3615
3857
|
if (protectedFiles.has(file)) {
|
|
@@ -3642,12 +3884,16 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
|
|
|
3642
3884
|
updatedCount++;
|
|
3643
3885
|
}
|
|
3644
3886
|
} else if (stat.isDirectory()) {
|
|
3887
|
+
if (skipDirectories.has(file)) {
|
|
3888
|
+
continue;
|
|
3889
|
+
}
|
|
3645
3890
|
const subCount = await updateFolder(
|
|
3646
3891
|
sourcePath,
|
|
3647
3892
|
targetPath,
|
|
3648
3893
|
force,
|
|
3649
3894
|
replacements,
|
|
3650
|
-
lang
|
|
3895
|
+
lang,
|
|
3896
|
+
options
|
|
3651
3897
|
);
|
|
3652
3898
|
updatedCount += subCount;
|
|
3653
3899
|
}
|
|
@@ -3688,7 +3934,7 @@ function extractPorcelainPaths(line) {
|
|
|
3688
3934
|
function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
|
|
3689
3935
|
const top = getGitTopLevel2(docsDir);
|
|
3690
3936
|
if (!top) return null;
|
|
3691
|
-
const rel =
|
|
3937
|
+
const rel = path6.relative(top, docsDir) || ".";
|
|
3692
3938
|
try {
|
|
3693
3939
|
const output = execFileSync("git", ["status", "--porcelain=v1", "--", rel], {
|
|
3694
3940
|
cwd: top,
|
|
@@ -3700,7 +3946,7 @@ function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
|
|
|
3700
3946
|
}
|
|
3701
3947
|
const ignoredRelPaths = new Set(
|
|
3702
3948
|
ignoredAbsPaths.map(
|
|
3703
|
-
(absPath) => normalizeGitPath2(
|
|
3949
|
+
(absPath) => normalizeGitPath2(path6.relative(top, absPath) || ".")
|
|
3704
3950
|
)
|
|
3705
3951
|
);
|
|
3706
3952
|
const filtered = output.split("\n").filter((line) => {
|
|
@@ -3757,7 +4003,7 @@ ${tr(lang2, "cli", "common.canceled")}`));
|
|
|
3757
4003
|
}
|
|
3758
4004
|
async function runConfig(options) {
|
|
3759
4005
|
const cwd = process.cwd();
|
|
3760
|
-
const targetCwd = options.dir ?
|
|
4006
|
+
const targetCwd = options.dir ? path6.resolve(cwd, options.dir) : cwd;
|
|
3761
4007
|
const config = await getConfig(targetCwd);
|
|
3762
4008
|
if (!config) {
|
|
3763
4009
|
throw createCliError(
|
|
@@ -3765,7 +4011,7 @@ async function runConfig(options) {
|
|
|
3765
4011
|
tr(DEFAULT_LANG, "cli", "common.configNotFound")
|
|
3766
4012
|
);
|
|
3767
4013
|
}
|
|
3768
|
-
const configPath =
|
|
4014
|
+
const configPath = path6.join(config.docsDir, ".lee-spec-kit.json");
|
|
3769
4015
|
if (!options.projectRoot) {
|
|
3770
4016
|
console.log();
|
|
3771
4017
|
console.log(chalk6.blue(tr(config.lang, "cli", "config.currentTitle")));
|
|
@@ -3923,6 +4169,7 @@ function getActionSummary(action) {
|
|
|
3923
4169
|
if (action.category === "issue_create") return "Create and record issue";
|
|
3924
4170
|
if (action.category === "branch_create") return "Create feature branch";
|
|
3925
4171
|
if (action.category === "pr_create") return "Create PR and record link";
|
|
4172
|
+
if (action.category === "pre_pr_review") return "Run pre-PR self review";
|
|
3926
4173
|
if (action.category === "pr_status_update") return "Update PR status";
|
|
3927
4174
|
if (action.category === "code_review") return "Process code review feedback";
|
|
3928
4175
|
if (action.category === "task_execute") return "Proceed with task execution";
|
|
@@ -4160,7 +4407,7 @@ function getCommandExecutionLockPath(action, config) {
|
|
|
4160
4407
|
if (action.scope === "docs") {
|
|
4161
4408
|
return getDocsLockPath(config.docsDir);
|
|
4162
4409
|
}
|
|
4163
|
-
return
|
|
4410
|
+
return path6.join(action.cwd, ".lee-spec-kit.project.lock");
|
|
4164
4411
|
}
|
|
4165
4412
|
function contextCommand(program2) {
|
|
4166
4413
|
program2.command("context [feature-name]").description("Show current feature context and next actions").option("--json", "Output in JSON format for agents").option("--repo <repo>", "Component name for multi projects").option("--component <component>", "Component name for multi projects").option("--all", "Include completed features when auto-detecting").option("--done", "Show completed (workflow-done) features only").option("--approve <reply>", "Approve one labeled option: A or A OK").option("--execute", "Execute approved option when it is a command").option(
|
|
@@ -4196,7 +4443,7 @@ function contextCommand(program2) {
|
|
|
4196
4443
|
}
|
|
4197
4444
|
);
|
|
4198
4445
|
}
|
|
4199
|
-
function getListLabel(f, stepsMap, lang, workflowPolicy) {
|
|
4446
|
+
function getListLabel(f, stepsMap, lang, workflowPolicy, prePrReviewPolicy) {
|
|
4200
4447
|
if (f.completion.implementationDone && !f.completion.workflowDone) {
|
|
4201
4448
|
if (f.git.docsHasUncommittedChanges) {
|
|
4202
4449
|
return tr(lang, "cli", "context.list.docsCommitNeeded");
|
|
@@ -4210,6 +4457,12 @@ function getListLabel(f, stepsMap, lang, workflowPolicy) {
|
|
|
4210
4457
|
if (workflowPolicy.requirePr && (!f.docs.prFieldExists || !f.docs.prStatusFieldExists)) {
|
|
4211
4458
|
return tr(lang, "cli", "context.list.addPrMetadata");
|
|
4212
4459
|
}
|
|
4460
|
+
if (prePrReviewPolicy.enabled && !f.docs.prePrReviewFieldExists) {
|
|
4461
|
+
return tr(lang, "cli", "context.list.addPrePrReviewField");
|
|
4462
|
+
}
|
|
4463
|
+
if (prePrReviewPolicy.enabled && f.prePrReview.status !== "Done") {
|
|
4464
|
+
return tr(lang, "cli", "context.list.completePrePrReview");
|
|
4465
|
+
}
|
|
4213
4466
|
if (workflowPolicy.requirePr && !f.pr.link) {
|
|
4214
4467
|
return tr(lang, "cli", "context.list.recordPrLink");
|
|
4215
4468
|
}
|
|
@@ -4244,6 +4497,7 @@ async function runContext(featureName, options) {
|
|
|
4244
4497
|
const config = await getConfig(cwd);
|
|
4245
4498
|
const lang = config?.lang ?? "en";
|
|
4246
4499
|
const workflowPolicy = resolveWorkflowPolicy(config?.workflow);
|
|
4500
|
+
const prePrReviewPolicy = resolvePrePrReviewPolicy(config?.workflow);
|
|
4247
4501
|
if (!config) {
|
|
4248
4502
|
throw createCliError(
|
|
4249
4503
|
"CONFIG_NOT_FOUND",
|
|
@@ -4311,8 +4565,9 @@ async function runContext(featureName, options) {
|
|
|
4311
4565
|
primaryActionCategory: primaryAction?.action.category ?? null,
|
|
4312
4566
|
primaryActionOperationType: primaryAction?.action.operationType ?? null,
|
|
4313
4567
|
workflowPolicy,
|
|
4568
|
+
prePrReviewPolicy,
|
|
4314
4569
|
checkPolicy: {
|
|
4315
|
-
docPath: "
|
|
4570
|
+
docPath: "builtin://agents/policy",
|
|
4316
4571
|
hint: tr(lang, "cli", "context.checkPolicyHint"),
|
|
4317
4572
|
policyOnly: true,
|
|
4318
4573
|
token: "<LABEL>",
|
|
@@ -4444,7 +4699,13 @@ async function runContext(featureName, options) {
|
|
|
4444
4699
|
)
|
|
4445
4700
|
);
|
|
4446
4701
|
state.inProgressFeatures.forEach((f2) => {
|
|
4447
|
-
const stepName2 = getListLabel(
|
|
4702
|
+
const stepName2 = getListLabel(
|
|
4703
|
+
f2,
|
|
4704
|
+
stepsMap,
|
|
4705
|
+
lang,
|
|
4706
|
+
workflowPolicy,
|
|
4707
|
+
prePrReviewPolicy
|
|
4708
|
+
);
|
|
4448
4709
|
const typeStr = config.projectType === "multi" ? chalk6.cyan(`(${f2.type})`) : "";
|
|
4449
4710
|
console.log(
|
|
4450
4711
|
` \u2022 ${chalk6.bold(f2.folderName)} ${typeStr} - ${chalk6.yellow(stepName2)}`
|
|
@@ -4457,7 +4718,13 @@ async function runContext(featureName, options) {
|
|
|
4457
4718
|
)
|
|
4458
4719
|
);
|
|
4459
4720
|
state.readyToCloseFeatures.forEach((f2) => {
|
|
4460
|
-
const stepName2 = getListLabel(
|
|
4721
|
+
const stepName2 = getListLabel(
|
|
4722
|
+
f2,
|
|
4723
|
+
stepsMap,
|
|
4724
|
+
lang,
|
|
4725
|
+
workflowPolicy,
|
|
4726
|
+
prePrReviewPolicy
|
|
4727
|
+
);
|
|
4461
4728
|
const typeStr = config.projectType === "multi" ? chalk6.cyan(`(${f2.type})`) : "";
|
|
4462
4729
|
console.log(
|
|
4463
4730
|
` \u2022 ${chalk6.bold(f2.folderName)} ${typeStr} - ${chalk6.yellow(stepName2)}`
|
|
@@ -4468,7 +4735,13 @@ async function runContext(featureName, options) {
|
|
|
4468
4735
|
console.log(chalk6.blue(title));
|
|
4469
4736
|
console.log();
|
|
4470
4737
|
state.targetFeatures.forEach((f2) => {
|
|
4471
|
-
const stepName2 = getListLabel(
|
|
4738
|
+
const stepName2 = getListLabel(
|
|
4739
|
+
f2,
|
|
4740
|
+
stepsMap,
|
|
4741
|
+
lang,
|
|
4742
|
+
workflowPolicy,
|
|
4743
|
+
prePrReviewPolicy
|
|
4744
|
+
);
|
|
4472
4745
|
const typeStr = config.projectType === "multi" ? chalk6.cyan(`(${f2.type})`) : "";
|
|
4473
4746
|
console.log(
|
|
4474
4747
|
` \u2022 ${chalk6.bold(f2.folderName)} ${typeStr} - ${chalk6.yellow(stepName2)}`
|
|
@@ -4509,7 +4782,7 @@ async function runContext(featureName, options) {
|
|
|
4509
4782
|
if (f.issueNumber) {
|
|
4510
4783
|
console.log(` \u2022 Issue: #${f.issueNumber}`);
|
|
4511
4784
|
}
|
|
4512
|
-
console.log(` \u2022 Path: ${
|
|
4785
|
+
console.log(` \u2022 Path: ${path6.relative(cwd, f.path)}`);
|
|
4513
4786
|
if (f.git.projectBranch) {
|
|
4514
4787
|
console.log(` \u2022 Project Branch: ${f.git.projectBranch}`);
|
|
4515
4788
|
}
|
|
@@ -4735,7 +5008,7 @@ var FIXABLE_ISSUE_CODES = /* @__PURE__ */ new Set([
|
|
|
4735
5008
|
]);
|
|
4736
5009
|
function formatPath(cwd, p) {
|
|
4737
5010
|
if (!p) return "";
|
|
4738
|
-
return
|
|
5011
|
+
return path6.isAbsolute(p) ? path6.relative(cwd, p) : p;
|
|
4739
5012
|
}
|
|
4740
5013
|
function detectPlaceholders(content) {
|
|
4741
5014
|
const patterns = [
|
|
@@ -4878,7 +5151,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
|
|
|
4878
5151
|
const placeholderContext = {
|
|
4879
5152
|
projectName: config.projectName,
|
|
4880
5153
|
featureName: f.slug,
|
|
4881
|
-
featurePath: f.docs.featurePathFromDocs ||
|
|
5154
|
+
featurePath: f.docs.featurePathFromDocs || path6.relative(config.docsDir, f.path),
|
|
4882
5155
|
repoType: f.type,
|
|
4883
5156
|
featureNumber
|
|
4884
5157
|
};
|
|
@@ -4888,7 +5161,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
|
|
|
4888
5161
|
"tasks.md"
|
|
4889
5162
|
];
|
|
4890
5163
|
for (const file of files) {
|
|
4891
|
-
const fullPath =
|
|
5164
|
+
const fullPath = path6.join(f.path, file);
|
|
4892
5165
|
if (!await fs2.pathExists(fullPath)) continue;
|
|
4893
5166
|
const original = await fs2.readFile(fullPath, "utf-8");
|
|
4894
5167
|
let next = original;
|
|
@@ -4933,7 +5206,7 @@ async function checkDocsStructure(config, cwd) {
|
|
|
4933
5206
|
const issues = [];
|
|
4934
5207
|
const requiredDirs = ["agents", "features", "prd", "designs", "ideas"];
|
|
4935
5208
|
for (const dir of requiredDirs) {
|
|
4936
|
-
const p =
|
|
5209
|
+
const p = path6.join(config.docsDir, dir);
|
|
4937
5210
|
if (!await fs2.pathExists(p)) {
|
|
4938
5211
|
issues.push({
|
|
4939
5212
|
level: "error",
|
|
@@ -4943,7 +5216,7 @@ async function checkDocsStructure(config, cwd) {
|
|
|
4943
5216
|
});
|
|
4944
5217
|
}
|
|
4945
5218
|
}
|
|
4946
|
-
const configPath =
|
|
5219
|
+
const configPath = path6.join(config.docsDir, ".lee-spec-kit.json");
|
|
4947
5220
|
if (!await fs2.pathExists(configPath)) {
|
|
4948
5221
|
issues.push({
|
|
4949
5222
|
level: "warn",
|
|
@@ -4966,7 +5239,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
4966
5239
|
}
|
|
4967
5240
|
const idMap = /* @__PURE__ */ new Map();
|
|
4968
5241
|
for (const f of features) {
|
|
4969
|
-
const rel = f.docs.featurePathFromDocs ||
|
|
5242
|
+
const rel = f.docs.featurePathFromDocs || path6.relative(config.docsDir, f.path);
|
|
4970
5243
|
const id = f.id || "UNKNOWN";
|
|
4971
5244
|
if (!idMap.has(id)) idMap.set(id, []);
|
|
4972
5245
|
idMap.get(id).push(rel);
|
|
@@ -4974,7 +5247,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
4974
5247
|
if (!isInitialTemplateState) {
|
|
4975
5248
|
const featureDocs = ["spec.md", "plan.md", "tasks.md"];
|
|
4976
5249
|
for (const file of featureDocs) {
|
|
4977
|
-
const p =
|
|
5250
|
+
const p = path6.join(f.path, file);
|
|
4978
5251
|
if (!await fs2.pathExists(p)) continue;
|
|
4979
5252
|
const content = await fs2.readFile(p, "utf-8");
|
|
4980
5253
|
const placeholders = detectPlaceholders(content);
|
|
@@ -4989,7 +5262,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
4989
5262
|
});
|
|
4990
5263
|
}
|
|
4991
5264
|
if (decisionsPlaceholderMode !== "off") {
|
|
4992
|
-
const decisionsPath =
|
|
5265
|
+
const decisionsPath = path6.join(f.path, "decisions.md");
|
|
4993
5266
|
if (await fs2.pathExists(decisionsPath)) {
|
|
4994
5267
|
const content = await fs2.readFile(decisionsPath, "utf-8");
|
|
4995
5268
|
const placeholders = detectPlaceholders(content);
|
|
@@ -5018,7 +5291,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
5018
5291
|
level: "warn",
|
|
5019
5292
|
code: "spec_status_unset",
|
|
5020
5293
|
message: tr(config.lang, "cli", "doctor.issue.specStatusUnset"),
|
|
5021
|
-
path: formatPath(cwd,
|
|
5294
|
+
path: formatPath(cwd, path6.join(f.path, "spec.md"))
|
|
5022
5295
|
});
|
|
5023
5296
|
}
|
|
5024
5297
|
if (f.docs.planExists && !f.planStatus && !isInitialTemplateState) {
|
|
@@ -5026,7 +5299,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
5026
5299
|
level: "warn",
|
|
5027
5300
|
code: "plan_status_unset",
|
|
5028
5301
|
message: tr(config.lang, "cli", "doctor.issue.planStatusUnset"),
|
|
5029
|
-
path: formatPath(cwd,
|
|
5302
|
+
path: formatPath(cwd, path6.join(f.path, "plan.md"))
|
|
5030
5303
|
});
|
|
5031
5304
|
}
|
|
5032
5305
|
if (f.docs.tasksExists && f.tasks.total === 0 && !isInitialTemplateState) {
|
|
@@ -5034,7 +5307,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
5034
5307
|
level: "warn",
|
|
5035
5308
|
code: "tasks_empty",
|
|
5036
5309
|
message: tr(config.lang, "cli", "doctor.issue.tasksEmpty"),
|
|
5037
|
-
path: formatPath(cwd,
|
|
5310
|
+
path: formatPath(cwd, path6.join(f.path, "tasks.md"))
|
|
5038
5311
|
});
|
|
5039
5312
|
}
|
|
5040
5313
|
if (f.docs.tasksExists && !f.docs.tasksDocStatusFieldExists && !isInitialTemplateState) {
|
|
@@ -5042,7 +5315,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
5042
5315
|
level: "warn",
|
|
5043
5316
|
code: "tasks_doc_status_missing",
|
|
5044
5317
|
message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusMissing"),
|
|
5045
|
-
path: formatPath(cwd,
|
|
5318
|
+
path: formatPath(cwd, path6.join(f.path, "tasks.md"))
|
|
5046
5319
|
});
|
|
5047
5320
|
}
|
|
5048
5321
|
if (f.docs.tasksExists && f.docs.tasksDocStatusFieldExists && !f.tasksDocStatus && !isInitialTemplateState) {
|
|
@@ -5050,7 +5323,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
5050
5323
|
level: "warn",
|
|
5051
5324
|
code: "tasks_doc_status_unset",
|
|
5052
5325
|
message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusUnset"),
|
|
5053
|
-
path: formatPath(cwd,
|
|
5326
|
+
path: formatPath(cwd, path6.join(f.path, "tasks.md"))
|
|
5054
5327
|
});
|
|
5055
5328
|
}
|
|
5056
5329
|
}
|
|
@@ -5074,7 +5347,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
5074
5347
|
level: "warn",
|
|
5075
5348
|
code: "missing_feature_id",
|
|
5076
5349
|
message: tr(config.lang, "cli", "doctor.issue.missingFeatureId"),
|
|
5077
|
-
path: formatPath(cwd,
|
|
5350
|
+
path: formatPath(cwd, path6.join(config.docsDir, p))
|
|
5078
5351
|
});
|
|
5079
5352
|
}
|
|
5080
5353
|
return issues;
|
|
@@ -5196,7 +5469,7 @@ function doctorCommand(program2) {
|
|
|
5196
5469
|
}
|
|
5197
5470
|
console.log();
|
|
5198
5471
|
console.log(chalk6.bold(tr(lang, "cli", "doctor.title")));
|
|
5199
|
-
console.log(chalk6.gray(`- Docs: ${
|
|
5472
|
+
console.log(chalk6.gray(`- Docs: ${path6.relative(cwd, docsDir)}`));
|
|
5200
5473
|
console.log(chalk6.gray(`- Type: ${projectType}`));
|
|
5201
5474
|
console.log(chalk6.gray(`- Lang: ${lang}`));
|
|
5202
5475
|
console.log();
|
|
@@ -5375,7 +5648,7 @@ async function runView(featureName, options) {
|
|
|
5375
5648
|
}
|
|
5376
5649
|
console.log();
|
|
5377
5650
|
console.log(chalk6.bold("\u{1F4CA} Workflow View"));
|
|
5378
|
-
console.log(chalk6.gray(`- Docs: ${
|
|
5651
|
+
console.log(chalk6.gray(`- Docs: ${path6.relative(cwd, config.docsDir)}`));
|
|
5379
5652
|
console.log(
|
|
5380
5653
|
chalk6.gray(
|
|
5381
5654
|
`- Features: ${state.features.length} (open ${state.openFeatures.length} / done ${state.doneFeatures.length})`
|
|
@@ -5732,7 +6005,7 @@ function ensureSections(body, sections, kind) {
|
|
|
5732
6005
|
}
|
|
5733
6006
|
function ensureDocsExist(docsDir, relativePaths) {
|
|
5734
6007
|
const missing = relativePaths.filter(
|
|
5735
|
-
(relativePath) => !fs2.existsSync(
|
|
6008
|
+
(relativePath) => !fs2.existsSync(path6.join(docsDir, relativePath))
|
|
5736
6009
|
);
|
|
5737
6010
|
if (missing.length > 0) {
|
|
5738
6011
|
throw createCliError(
|
|
@@ -5742,8 +6015,8 @@ function ensureDocsExist(docsDir, relativePaths) {
|
|
|
5742
6015
|
}
|
|
5743
6016
|
}
|
|
5744
6017
|
function toBodyFilePath(raw, fallbackName) {
|
|
5745
|
-
const selected = raw?.trim() ||
|
|
5746
|
-
return
|
|
6018
|
+
const selected = raw?.trim() || path6.join(os.tmpdir(), fallbackName);
|
|
6019
|
+
return path6.resolve(selected);
|
|
5747
6020
|
}
|
|
5748
6021
|
async function resolveFeatureOrThrow(featureName, options) {
|
|
5749
6022
|
const config = await getConfig(process.cwd());
|
|
@@ -5916,7 +6189,7 @@ function ensureCleanWorktree(cwd) {
|
|
|
5916
6189
|
}
|
|
5917
6190
|
}
|
|
5918
6191
|
function commitAndPushPath(cwd, absPath, message) {
|
|
5919
|
-
const relativePath =
|
|
6192
|
+
const relativePath = path6.relative(cwd, absPath) || absPath;
|
|
5920
6193
|
const status = runProcessOrThrow(
|
|
5921
6194
|
"git",
|
|
5922
6195
|
["status", "--porcelain=v1", "--", relativePath],
|
|
@@ -6044,7 +6317,7 @@ function githubCommand(program2) {
|
|
|
6044
6317
|
options.bodyFile,
|
|
6045
6318
|
`lee-spec-kit.issue.${feature.folderName}.md`
|
|
6046
6319
|
);
|
|
6047
|
-
await fs2.ensureDir(
|
|
6320
|
+
await fs2.ensureDir(path6.dirname(bodyFile));
|
|
6048
6321
|
await fs2.writeFile(bodyFile, body, "utf-8");
|
|
6049
6322
|
let issueUrl;
|
|
6050
6323
|
if (options.create) {
|
|
@@ -6142,7 +6415,7 @@ function githubCommand(program2) {
|
|
|
6142
6415
|
options.bodyFile,
|
|
6143
6416
|
`lee-spec-kit.pr.${feature.folderName}.md`
|
|
6144
6417
|
);
|
|
6145
|
-
await fs2.ensureDir(
|
|
6418
|
+
await fs2.ensureDir(path6.dirname(bodyFile));
|
|
6146
6419
|
await fs2.writeFile(bodyFile, body, "utf-8");
|
|
6147
6420
|
const retryCount = toRetryCount(options.retry);
|
|
6148
6421
|
let prUrl = options.pr?.trim() || "";
|
|
@@ -6180,7 +6453,7 @@ function githubCommand(program2) {
|
|
|
6180
6453
|
}
|
|
6181
6454
|
if (prUrl && options.syncTasks !== false) {
|
|
6182
6455
|
const synced = syncTasksPrMetadata(
|
|
6183
|
-
|
|
6456
|
+
path6.join(config.docsDir, paths.tasksPath),
|
|
6184
6457
|
prUrl,
|
|
6185
6458
|
"Review"
|
|
6186
6459
|
);
|
|
@@ -6319,11 +6592,11 @@ ${version}
|
|
|
6319
6592
|
}
|
|
6320
6593
|
return `${ascii}${footer}`;
|
|
6321
6594
|
}
|
|
6322
|
-
var CACHE_FILE =
|
|
6595
|
+
var CACHE_FILE = path6.join(os.homedir(), ".lee-spec-kit-version-cache.json");
|
|
6323
6596
|
var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
|
|
6324
6597
|
function getCurrentVersion() {
|
|
6325
6598
|
try {
|
|
6326
|
-
const packageJsonPath =
|
|
6599
|
+
const packageJsonPath = path6.join(__dirname$1, "..", "package.json");
|
|
6327
6600
|
if (fs2.existsSync(packageJsonPath)) {
|
|
6328
6601
|
const pkg = fs2.readJsonSync(packageJsonPath);
|
|
6329
6602
|
return pkg.version;
|
|
@@ -6419,7 +6692,7 @@ function shouldCheckForUpdates() {
|
|
|
6419
6692
|
if (shouldCheckForUpdates()) checkForUpdates();
|
|
6420
6693
|
function getCliVersion() {
|
|
6421
6694
|
try {
|
|
6422
|
-
const packageJsonPath =
|
|
6695
|
+
const packageJsonPath = path6.join(__dirname$1, "..", "package.json");
|
|
6423
6696
|
if (fs2.existsSync(packageJsonPath)) {
|
|
6424
6697
|
const pkg = fs2.readJsonSync(packageJsonPath);
|
|
6425
6698
|
if (pkg?.version) return String(pkg.version);
|