lee-spec-kit 0.6.24 → 0.6.26
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 +19 -3
- package/README.md +19 -3
- package/dist/index.js +963 -468
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/en/common/agents/agents.md +2 -0
- package/templates/en/common/agents/skills/create-pr.md +1 -1
- package/templates/en/common/agents/skills/execute-task.md +11 -2
- package/templates/en/common/features/README.md +0 -1
- package/templates/en/common/features/feature-base/tasks.md +1 -3
- package/templates/ko/common/agents/agents.md +2 -0
- package/templates/ko/common/agents/skills/create-pr.md +1 -1
- package/templates/ko/common/agents/skills/execute-task.md +9 -2
- package/templates/ko/common/features/README.md +0 -1
- package/templates/ko/common/features/feature-base/tasks.md +1 -3
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import path23 from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { program } from 'commander';
|
|
5
|
-
import
|
|
5
|
+
import fs17 from 'fs-extra';
|
|
6
6
|
import prompts from 'prompts';
|
|
7
7
|
import chalk6 from 'chalk';
|
|
8
8
|
import { spawn, spawnSync, execFileSync, execSync } from 'child_process';
|
|
@@ -10,7 +10,7 @@ import os from 'os';
|
|
|
10
10
|
import { createHash, randomUUID } from 'crypto';
|
|
11
11
|
|
|
12
12
|
var getFilename = () => fileURLToPath(import.meta.url);
|
|
13
|
-
var getDirname = () =>
|
|
13
|
+
var getDirname = () => path23.dirname(getFilename());
|
|
14
14
|
var __dirname$1 = /* @__PURE__ */ getDirname();
|
|
15
15
|
async function walkFiles(rootDir, options = {}) {
|
|
16
16
|
const out = [];
|
|
@@ -21,9 +21,9 @@ async function walkFiles(rootDir, options = {}) {
|
|
|
21
21
|
(options.ignoreDirs || []).map((value) => value.trim().toLowerCase()).filter(Boolean)
|
|
22
22
|
);
|
|
23
23
|
async function visit(current) {
|
|
24
|
-
const entries = await
|
|
24
|
+
const entries = await fs17.readdir(current, { withFileTypes: true });
|
|
25
25
|
for (const entry of entries) {
|
|
26
|
-
const absolute =
|
|
26
|
+
const absolute = path23.join(current, entry.name);
|
|
27
27
|
if (entry.isDirectory()) {
|
|
28
28
|
if (ignored.has(entry.name.trim().toLowerCase())) continue;
|
|
29
29
|
await visit(absolute);
|
|
@@ -31,26 +31,26 @@ async function walkFiles(rootDir, options = {}) {
|
|
|
31
31
|
}
|
|
32
32
|
if (!entry.isFile()) continue;
|
|
33
33
|
if (normalizedExtensions.size > 0) {
|
|
34
|
-
const ext =
|
|
34
|
+
const ext = path23.extname(entry.name).toLowerCase();
|
|
35
35
|
if (!normalizedExtensions.has(ext)) continue;
|
|
36
36
|
}
|
|
37
37
|
out.push(absolute);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
-
if (await
|
|
40
|
+
if (await fs17.pathExists(rootDir)) {
|
|
41
41
|
await visit(rootDir);
|
|
42
42
|
}
|
|
43
43
|
return out;
|
|
44
44
|
}
|
|
45
45
|
async function listSubdirectories(rootDir) {
|
|
46
|
-
if (!await
|
|
47
|
-
const entries = await
|
|
48
|
-
return entries.filter((entry) => entry.isDirectory()).map((entry) =>
|
|
46
|
+
if (!await fs17.pathExists(rootDir)) return [];
|
|
47
|
+
const entries = await fs17.readdir(rootDir, { withFileTypes: true });
|
|
48
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => path23.join(rootDir, entry.name));
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// src/utils/template.ts
|
|
52
52
|
async function copyTemplates(src, dest) {
|
|
53
|
-
await
|
|
53
|
+
await fs17.copy(src, dest, {
|
|
54
54
|
overwrite: true,
|
|
55
55
|
errorOnExist: false
|
|
56
56
|
});
|
|
@@ -66,22 +66,22 @@ function applyReplacements(content, replacements) {
|
|
|
66
66
|
async function replaceInFiles(dir, replacements) {
|
|
67
67
|
const files = await walkFiles(dir, { extensions: [".md"] });
|
|
68
68
|
for (const file of files) {
|
|
69
|
-
let content = await
|
|
69
|
+
let content = await fs17.readFile(file, "utf-8");
|
|
70
70
|
content = applyReplacements(content, replacements);
|
|
71
|
-
await
|
|
71
|
+
await fs17.writeFile(file, content, "utf-8");
|
|
72
72
|
}
|
|
73
73
|
const shFiles = await walkFiles(dir, { extensions: [".sh"] });
|
|
74
74
|
for (const file of shFiles) {
|
|
75
|
-
let content = await
|
|
75
|
+
let content = await fs17.readFile(file, "utf-8");
|
|
76
76
|
content = applyReplacements(content, replacements);
|
|
77
|
-
await
|
|
77
|
+
await fs17.writeFile(file, content, "utf-8");
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
81
|
-
var __dirname2 =
|
|
81
|
+
var __dirname2 = path23.dirname(__filename2);
|
|
82
82
|
function getTemplatesDir() {
|
|
83
|
-
const rootDir =
|
|
84
|
-
return
|
|
83
|
+
const rootDir = path23.resolve(__dirname2, "..");
|
|
84
|
+
return path23.join(rootDir, "templates");
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
// src/utils/locales/ko.ts
|
|
@@ -163,11 +163,16 @@ var ko = {
|
|
|
163
163
|
"context.suggestionHeader": "\uCD94\uCC9C \uB2E4\uC74C \uC120\uD0DD\uC9C0",
|
|
164
164
|
"context.suggestionCommandHint": "\uB77C\uBCA8 \uCC38\uACE0 \uBA85\uB839: {command}",
|
|
165
165
|
"context.suggestionFinalPrompt": "\uD604\uC7AC \uCD94\uCC9C \uB77C\uBCA8: {labels}. \uC751\uB2F5\uC740 \uB77C\uBCA8 \uD1A0\uD070 \uD3EC\uD568 \uD615\uC2DD\uC73C\uB85C \uD574\uC8FC\uC138\uC694. (\uC608: {example}, `A \uC9C4\uD589\uD574`)",
|
|
166
|
+
"context.autoRunUnavailable": "\uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8\uC5D0\uC11C\uB294 \uC790\uB3D9 \uC2E4\uD589\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
167
|
+
"context.autoRunSummary": "config \uAE30\uC900\uC73C\uB85C \uC2B9\uC778 \uD544\uC694 \uCE74\uD14C\uACE0\uB9AC \uC804\uAE4C\uC9C0 \uC5F0\uC18D \uC2E4\uD589\uD558\uC138\uC694: {categories}",
|
|
168
|
+
"context.autoRunCommandHint": "\uC790\uB3D9 \uC2E4\uD589 \uBA85\uB839(config \uAC8C\uC774\uD2B8): {command}",
|
|
169
|
+
"context.subAgentOrchestrationHint": "\uBA54\uC778 \uC5D0\uC774\uC804\uD2B8 \uC624\uCF00\uC2A4\uD2B8\uB808\uC774\uC158: \uC9E7\uC740 \uB2E8\uACC4\uB294 \uBA54\uC778\uC774 \uC9C1\uC811 \uC218\uD589\uD558\uACE0, \uC7A5\uC2DC\uAC04 \uB8E8\uD504(task_execute/code_review/review_fix_commit/pre_pr_review \uB610\uB294 auto)\uB294 \uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8\uC5D0 \uC704\uC784\uD558\uC138\uC694.",
|
|
166
170
|
"context.commandDetail.branchCreateWithWorktree": "({scope}) worktree {worktree}\uB97C \uC0AC\uC6A9\uD574 \uBE0C\uB79C\uCE58 {branch}\uB97C \uC0DD\uC131\uD558\uAC70\uB098 \uC7AC\uC0AC\uC6A9\uD558\uC138\uC694",
|
|
167
171
|
"context.commandDetail.branchCreateWithBranch": "({scope}) \uBE0C\uB79C\uCE58 {branch}\uC6A9 worktree\uB97C \uC0DD\uC131\uD558\uAC70\uB098 \uC7AC\uC0AC\uC6A9\uD558\uC138\uC694",
|
|
168
172
|
"context.commandDetail.branchCreateGeneric": "({scope}) feature \uBE0C\uB79C\uCE58\uC6A9 worktree\uB97C \uC0DD\uC131\uD558\uAC70\uB098 \uC7AC\uC0AC\uC6A9\uD558\uC138\uC694",
|
|
169
173
|
"context.commandDetail.codeReviewMergeAfterOk": "({scope}) \uBA85\uC2DC\uC801 \uC2B9\uC778 \uD6C4 PR\uC744 \uBA38\uC9C0\uD558\uC138\uC694",
|
|
170
174
|
"context.commandDetail.codeReviewPushFix": "({scope}) \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B\uC744 push\uD558\uC138\uC694",
|
|
175
|
+
"context.commandDetail.prePrReviewRun": "({scope}) Pre-PR \uB9AC\uBDF0\uB97C \uC2E4\uD589\uD558\uACE0 decisions.md\uC640 tasks.md\uB97C \uB3D9\uAE30\uD654\uD558\uC138\uC694",
|
|
171
176
|
"context.actionSummary.runDocsCommand": "\uBB38\uC11C \uC791\uC5C5 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uC138\uC694",
|
|
172
177
|
"context.actionSummary.runProjectCommand": "\uD504\uB85C\uC81D\uD2B8 \uC791\uC5C5 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uC138\uC694",
|
|
173
178
|
"context.actionDetail.featureFolder": "Feature \uD3F4\uB354\uC640 \uAE30\uBCF8 \uBB38\uC11C \uACE8\uACA9\uC744 \uC900\uBE44\uD558\uC138\uC694",
|
|
@@ -176,16 +181,38 @@ var ko = {
|
|
|
176
181
|
"context.actionDetail.planWrite": "plan.md\uB97C \uC791\uC131/\uBCF4\uC644\uD558\uACE0 \uC0C1\uD0DC\uB97C \uB9DE\uCD94\uC138\uC694",
|
|
177
182
|
"context.actionDetail.planApprove": "plan.md\uB97C \uC2B9\uC778\uD569\uB2C8\uB2E4",
|
|
178
183
|
"context.actionDetail.tasksWrite": "tasks.md\uB97C \uC791\uC131/\uBCF4\uC644\uD558\uACE0 \uBB38\uC11C \uC0C1\uD0DC\uB97C \uC815\uB82C\uD558\uC138\uC694",
|
|
184
|
+
"context.actionDetail.tasksWriteCreate": "tasks.md\uB97C \uC0DD\uC131\uD558\uACE0 \uBB38\uC11C \uC0C1\uD0DC\uB97C Review\uB85C \uC124\uC815\uD558\uC138\uC694",
|
|
185
|
+
"context.actionDetail.tasksWriteNeedAtLeastOne": "tasks.md\uC5D0 \uCD5C\uC18C 1\uAC1C \uC774\uC0C1\uC758 \uD0DC\uC2A4\uD06C\uB97C \uCD94\uAC00\uD558\uC138\uC694",
|
|
186
|
+
"context.actionDetail.tasksWriteImprove": "tasks.md\uB97C \uBCF4\uC644\uD558\uACE0 \uBB38\uC11C \uC0C1\uD0DC\uB97C \uC815\uB82C\uD558\uC138\uC694",
|
|
179
187
|
"context.actionDetail.tasksApprove": "tasks.md\uB97C \uC2B9\uC778\uD569\uB2C8\uB2E4",
|
|
180
188
|
"context.actionDetail.issueCreate": "\uC774\uC288\uB97C \uC0DD\uC131\uD558\uACE0 tasks.md\uC758 \uC774\uC288 \uC815\uBCF4\uB97C \uB9DE\uCD94\uC138\uC694",
|
|
189
|
+
"context.actionDetail.issueCreateAndWrite": "\uC774\uC288 \uCD08\uC548\uC744 \uBCF4\uC644\uD558\uACE0 \uC2B9\uC778(OK) \uD6C4 \uC774\uC288\uB97C \uC0DD\uC131\uD574 \uBC88\uD638\uB97C \uB3D9\uAE30\uD654\uD558\uC138\uC694",
|
|
190
|
+
"context.actionDetail.issueCreatePrepareFromDoc": "issue.md \uCD08\uC548\uC744 \uBCF4\uC644\uD558\uACE0 \uC0C1\uD0DC\uB97C Ready\uB85C \uC124\uC815\uD558\uC138\uC694",
|
|
191
|
+
"context.actionDetail.issueCreateFromDoc": "Ready \uC0C1\uD0DC issue.md\uB85C \uC774\uC288\uB97C \uC0DD\uC131\uD558\uACE0 \uBC88\uD638\uB97C \uB3D9\uAE30\uD654\uD558\uC138\uC694",
|
|
181
192
|
"context.actionDetail.taskExecute": "\uD604\uC7AC \uD0DC\uC2A4\uD06C\uB97C \uC9C4\uD589\uD558\uC138\uC694",
|
|
182
193
|
"context.actionDetail.reviewFixCommit": "\uD574\uACB0\uD55C \uB9AC\uBDF0 \uD56D\uBAA9 \uC694\uC57D\uC73C\uB85C \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B\uC744 \uB9CC\uB4DC\uC138\uC694",
|
|
183
194
|
"context.actionDetail.prePrReview": "PR \uC804 \uB9AC\uBDF0\uB97C \uC218\uD589\uD558\uACE0 \uACB0\uACFC\uB97C \uAE30\uB85D\uD558\uC138\uC694",
|
|
184
195
|
"context.actionDetail.prCreate": "PR\uC744 \uC0DD\uC131\uD558\uACE0 tasks.md\uC758 PR \uC815\uBCF4\uB97C \uB9DE\uCD94\uC138\uC694",
|
|
196
|
+
"context.actionDetail.prCreateRequiredSequence": "PR 2\uB2E8\uACC4(\uCD08\uC548/\uC2B9\uC778 \uD6C4 \uC0DD\uC131/\uB3D9\uAE30\uD654)\uB97C \uC21C\uC11C\uB300\uB85C \uC644\uB8CC\uD558\uC138\uC694",
|
|
197
|
+
"context.actionDetail.prCreatePrepareFromDoc": "pr.md \uCD08\uC548\uC744 \uBCF4\uC644\uD558\uACE0 \uC0C1\uD0DC\uB97C Ready\uB85C \uC124\uC815\uD558\uC138\uC694",
|
|
198
|
+
"context.actionDetail.prCreateExecuteFromDoc": "Ready \uC0C1\uD0DC pr.md\uB85C PR\uC744 \uC0DD\uC131\uD558\uACE0 \uB9C1\uD06C/\uC0C1\uD0DC\uB97C \uB3D9\uAE30\uD654\uD558\uC138\uC694",
|
|
185
199
|
"context.actionDetail.prStatusUpdate": "tasks.md\uC758 PR \uC0C1\uD0DC\uB97C \uCD5C\uC2E0\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694",
|
|
200
|
+
"context.actionDetail.prStatusUpdateSetReview": "tasks.md\uC758 PR \uC0C1\uD0DC\uB97C Review\uB85C \uC124\uC815\uD558\uC138\uC694",
|
|
201
|
+
"context.actionDetail.prStatusUpdateSyncApproved": "\uC6D0\uACA9 \uBA38\uC9C0 \uC0C1\uD0DC\uB97C \uBC18\uC601\uD574 PR \uC0C1\uD0DC\uB97C Approved\uB85C \uB3D9\uAE30\uD654\uD558\uC138\uC694",
|
|
186
202
|
"context.actionDetail.codeReview": "\uCF54\uB4DC \uB9AC\uBDF0 \uC9C0\uC801\uC0AC\uD56D\uC744 \uBC18\uC601\uD558\uACE0 PR \uB9AC\uBDF0 \uC815\uBCF4\uB97C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694",
|
|
203
|
+
"context.actionDetail.codeReviewNeedEvidenceField": "tasks.md\uC5D0 PR \uB9AC\uBDF0 Evidence \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694",
|
|
204
|
+
"context.actionDetail.codeReviewNeedEvidence": "PR \uB9AC\uBDF0 Evidence \uC694\uC57D\uC744 \uAE30\uB85D\uD558\uC138\uC694",
|
|
205
|
+
"context.actionDetail.codeReviewNeedDecisionField": "tasks.md\uC5D0 PR \uB9AC\uBDF0 Decision \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694",
|
|
206
|
+
"context.actionDetail.codeReviewNeedDecision": "PR \uB9AC\uBDF0 Decision\uC744 \uAE30\uB85D\uD558\uC138\uC694",
|
|
207
|
+
"context.actionDetail.codeReviewResolve": "\uB9AC\uBDF0 \uCF54\uBA58\uD2B8\uB97C \uBC18\uC601\uD558\uACE0 PR \uB9AC\uBDF0 \uBB38\uC11C\uB97C \uCD5C\uC2E0\uD654\uD558\uC138\uC694",
|
|
208
|
+
"context.actionDetail.codeReviewNeedProjectRoot": "\uB9AC\uBDF0 \uC791\uC5C5\uC744 \uACC4\uC18D\uD558\uB824\uBA74 projectRoot\uB97C \uC124\uC815\uD558\uC138\uC694",
|
|
209
|
+
"context.actionDetail.codeReviewRemoteBlocked": "\uC6D0\uACA9 PR \uCC28\uB2E8 \uC0AC\uC720\uB97C \uD574\uC18C\uD55C \uB4A4 \uBA38\uC9C0\uB97C \uC9C4\uD589\uD558\uC138\uC694",
|
|
210
|
+
"context.actionDetail.codeReviewMergeAfterOk": "\uC0AC\uC6A9\uC790 \uC2B9\uC778(OK) \uD6C4 PR\uC744 \uBA38\uC9C0\uD558\uC138\uC694",
|
|
211
|
+
"context.actionDetail.codeReviewRequestReview": "\uB9AC\uBDF0 \uC694\uCCAD\uC744 \uC9C4\uD589\uD558\uACE0 PR \uC0C1\uD0DC\uB97C Review\uB85C \uC720\uC9C0\uD558\uC138\uC694",
|
|
187
212
|
"context.actionDetail.worktreeCleanup": "\uC644\uB8CC\uB41C feature worktree\uB97C \uC815\uB9AC\uD558\uC138\uC694",
|
|
188
213
|
"context.actionDetail.prMetadataMigrate": "tasks.md\uC758 PR \uD56D\uBAA9 \uD615\uC2DD\uC744 \uCD5C\uC2E0 \uD15C\uD50C\uB9BF\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694",
|
|
214
|
+
"context.actionDetail.prMetadataMigratePrFields": "tasks.md\uC5D0 PR/PR \uC0C1\uD0DC \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694",
|
|
215
|
+
"context.actionDetail.prMetadataMigratePrePrReviewField": "tasks.md\uC5D0 PR \uC804 \uB9AC\uBDF0 \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694",
|
|
189
216
|
"context.actionDetail.userRequestReplan": "\uC0C8 \uC0AC\uC6A9\uC790 \uC694\uAD6C\uB97C \uBA3C\uC800 \uBC18\uC601\uD55C \uB4A4 context\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694",
|
|
190
217
|
"context.actionDetail.featureDone": "\uC774 Feature\uC758 \uC644\uB8CC \uC870\uAC74\uC774 \uBAA8\uB450 \uCDA9\uC871\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
|
|
191
218
|
"context.actionDetail.fallback": "\uD604\uC7AC \uC0C1\uD0DC\uB97C \uD655\uC778\uD55C \uB4A4 context\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694",
|
|
@@ -197,7 +224,7 @@ var ko = {
|
|
|
197
224
|
"context.suggestion.showOpen": "\uC9C4\uD589 \uC911 Feature \uBAA9\uB85D\uC744 \uD655\uC778\uD569\uB2C8\uB2E4",
|
|
198
225
|
"context.finalLabelCommandHint": "\uB77C\uBCA8\uC744 \uBC1B\uC73C\uBA74 \uC2B9\uC778 \uC120\uD0DD \uC2E4\uD589: {command}",
|
|
199
226
|
"context.finalTicketCommandHint": "\uBA85\uB839 \uC2E4\uD589\uC740 \uC2B9\uC778 \uACB0\uACFC\uC758 \uD2F0\uCF13\uC73C\uB85C \uC2E4\uD589: {command}",
|
|
200
|
-
"context.readBuiltinDocFirst": "\uC774\uBC88 \uC138\uC158\uC5D0 \uC544\uC9C1 \uC77D\uC9C0 \uC54A\uC558\uAC70\uB098 \uBCC0\uACBD \uAC00\uB2A5\uC131\uC774 \uC788\uC744 \uB54C\uB9CC \uD544\uC694\uD55C \uB0B4\uC7A5 \uBB38\uC11C\uB97C \uD655\uC778\uD558\uC138\uC694
|
|
227
|
+
"context.readBuiltinDocFirst": "\uC774\uBC88 \uC138\uC158\uC5D0 \uC544\uC9C1 \uC77D\uC9C0 \uC54A\uC558\uAC70\uB098 \uBCC0\uACBD \uAC00\uB2A5\uC131\uC774 \uC788\uC744 \uB54C\uB9CC \uD544\uC694\uD55C \uB0B4\uC7A5 \uBB38\uC11C\uB97C \uD655\uC778\uD558\uC138\uC694: {command}",
|
|
201
228
|
"context.tipDocsCommitRules": "\uCEE4\uBC0B \uBA54\uC2DC\uC9C0 \uADDC\uCE59\uC740 git-workflow \uAC00\uC774\uB4DC\uB97C \uAE30\uC900\uC73C\uB85C \uD655\uC778\uD558\uC138\uC694.",
|
|
202
229
|
"context.list.docsCommitNeeded": "\uBB38\uC11C \uCEE4\uBC0B \uD544\uC694",
|
|
203
230
|
"context.list.projectCommitNeeded": "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uCEE4\uBC0B \uD544\uC694",
|
|
@@ -206,7 +233,6 @@ var ko = {
|
|
|
206
233
|
"context.list.recordPrLink": "PR \uB9C1\uD06C \uAE30\uB85D",
|
|
207
234
|
"context.list.addPrePrReviewField": "Pre-PR Review \uD544\uB4DC \uCD94\uAC00",
|
|
208
235
|
"context.list.completePrePrReview": "Pre-PR \uB9AC\uBDF0 \uC644\uB8CC \uCC98\uB9AC",
|
|
209
|
-
"context.list.addPrePrFindings": "Pre-PR Findings \uAE30\uB85D",
|
|
210
236
|
"context.list.addPrePrEvidence": "Pre-PR Evidence \uADFC\uAC70 \uCD94\uAC00",
|
|
211
237
|
"context.list.addPrePrDecision": "Pre-PR Decision \uAE30\uB85D",
|
|
212
238
|
"context.list.resolvePrePrDecision": "Pre-PR Decision\uC744 approve\uB85C \uC815\uB9AC",
|
|
@@ -492,8 +518,8 @@ var ko = {
|
|
|
492
518
|
worktreeCleanupCommand: 'cd "{projectGitCwd}" && git worktree remove "{worktreePath}" && git worktree prune && CURRENT_BRANCH=$(git branch --show-current) && DEFAULT_BRANCH=$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | cut -d/ -f2-) && TARGET_BRANCH="${DEFAULT_BRANCH:-$CURRENT_BRANCH}" && if [ -n "$TARGET_BRANCH" ]; then git checkout "$TARGET_BRANCH" >/dev/null 2>&1 || true; fi && if git rev-parse --abbrev-ref --symbolic-full-name "@{u}" >/dev/null 2>&1 && [ -z "$(git status --porcelain)" ]; then git pull --ff-only || true; fi',
|
|
493
519
|
tasksAllDoneButNoChecklist: '\uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\uB97C \uC791\uC131\uD558\uC138\uC694. tasks.md\uC758 "\uC644\uB8CC \uC870\uAC74" \uC139\uC158\uC5D0 \uAC80\uC99D \uD56D\uBAA9\uC744 \uCD94\uAC00\uD558\uACE0, \uC0AC\uC6A9\uC790\uC640 \uD655\uC778 \uD6C4 \uCDA9\uC871 \uD56D\uBAA9\uC744 [x]\uB85C \uCCB4\uD06C\uD558\uC138\uC694. \uCD5C\uC885 \uC2B9\uC778(OK)\uB3C4 \uBC18\uC601\uD558\uC138\uC694.',
|
|
494
520
|
tasksAllDoneButChecklist: "\uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\uC758 \uB0A8\uC740 \uD56D\uBAA9\uC744 \uC9C4\uD589\uD558\uC138\uC694. \uD604\uC7AC \uC9C4\uD589: ({checked}/{total}) \uC0AC\uC6A9\uC790\uC640 \uD655\uC778 \uD6C4 \uCDA9\uC871 \uD56D\uBAA9\uC744 [x]\uB85C \uCCB4\uD06C\uD558\uACE0 \uCD5C\uC885 \uC2B9\uC778(OK)\uC744 \uBC18\uC601\uD558\uC138\uC694.",
|
|
495
|
-
finishDoingTask: '\uD604\uC7AC DOING/REVIEW \uD0DC\uC2A4\uD06C\uB97C \uC218\uD589\uD558\uC138\uC694: "{title}" ({done}/{total}) \uC644\uB8CC \uC2DC \uACB0\uACFC/\uAC80\uC99D\uC744 \uACF5\uC720\uD558\uACE0
|
|
496
|
-
startNextTodoTask: '\uB2E4\uC74C TODO \uD0DC\uC2A4\uD06C\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4: "{title}" ({done}/{total}) \
|
|
521
|
+
finishDoingTask: '\uD604\uC7AC DOING/REVIEW \uD0DC\uC2A4\uD06C\uB97C \uC218\uD589\uD558\uC138\uC694: "{title}" ({done}/{total}) \uC644\uB8CC \uC2DC \uACB0\uACFC/\uAC80\uC99D\uC744 \uACF5\uC720\uD558\uACE0 DONE \uCC98\uB9AC',
|
|
522
|
+
startNextTodoTask: '\uB2E4\uC74C TODO \uD0DC\uC2A4\uD06C\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4: "{title}" ({done}/{total}) \uC791\uC5C5\uC744 \uC2DC\uC791\uD558\uBA74 DOING \uCC98\uB9AC',
|
|
497
523
|
checkTaskStatuses: "\uD0DC\uC2A4\uD06C \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694. ({done}/{total})",
|
|
498
524
|
taskCommitGateStrictBlock: "\uB2E4\uC74C TODO \uD0DC\uC2A4\uD06C\uB85C \uB118\uC5B4\uAC00\uAE30 \uC804\uC5D0 `1 \uD0DC\uC2A4\uD06C = 1 \uCEE4\uBC0B` \uADDC\uCE59\uC744 \uCDA9\uC871\uD574\uC57C \uD569\uB2C8\uB2E4. \uC810\uAC80 \uACB0\uACFC: {reason}. \uD0DC\uC2A4\uD06C \uCEE4\uBC0B \uB2E8\uC704\uB97C \uC815\uB9AC\uD55C \uB4A4 \uB2E4\uC2DC \uC9C4\uD589\uD558\uC138\uC694.",
|
|
499
525
|
taskCommitGateWarnProceed: "\u26A0\uFE0F \uD0DC\uC2A4\uD06C \uCEE4\uBC0B \uB2E8\uC704 \uC810\uAC80 \uACBD\uACE0: {reason}. \uD604\uC7AC\uB294 \uC9C4\uD589 \uAC00\uB2A5\uD558\uC9C0\uB9CC `1 \uD0DC\uC2A4\uD06C = 1 \uCEE4\uBC0B`\uC744 \uAD8C\uC7A5\uD569\uB2C8\uB2E4.",
|
|
@@ -544,7 +570,6 @@ var ko = {
|
|
|
544
570
|
legacyTasksDocStatusField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. `\uBB38\uC11C \uC0C1\uD0DC` \uD544\uB4DC(Draft/Review/Approved)\uB97C \uCD94\uAC00\uD574 \uD0DC\uC2A4\uD06C \uC2B9\uC778 \uB2E8\uACC4\uB97C \uD65C\uC131\uD654\uD558\uC138\uC694.",
|
|
545
571
|
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.",
|
|
546
572
|
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`)",
|
|
547
|
-
legacyTasksPrePrFindingsField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR \uC804 \uB9AC\uBDF0 Findings` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694. (`- **PR \uC804 \uB9AC\uBDF0 Findings**: major=0, minor=0`)",
|
|
548
573
|
legacyTasksPrePrEvidenceField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR \uC804 \uB9AC\uBDF0 Evidence` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694.",
|
|
549
574
|
legacyTasksPrePrDecisionField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. PR \uB2E8\uACC4 \uC804\uC5D0 `PR \uC804 \uB9AC\uBDF0 Decision` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694. (`- **PR \uC804 \uB9AC\uBDF0 Decision**: \uACB0\uC815: ...`)",
|
|
550
575
|
legacyTasksPrReviewEvidenceField: "\uAD6C\uBC84\uC804 tasks.md \uD3EC\uB9F7\uC785\uB2C8\uB2E4. \uB9AC\uBDF0 \uB2E8\uACC4 \uC804\uC5D0 `PR \uB9AC\uBDF0 Evidence` \uD544\uB4DC\uB97C \uCD94\uAC00\uD558\uC138\uC694.",
|
|
@@ -563,10 +588,9 @@ var ko = {
|
|
|
563
588
|
workflowPrRemoteChecksPending: "\uC6D0\uACA9 PR \uCCB4\uD06C \uB300\uAE30\uAC00 {count}\uAC74 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uCCB4\uD06C \uC644\uB8CC \uD6C4 \uB2E4\uC2DC \uD655\uC778\uD558\uC138\uC694.",
|
|
564
589
|
workflowPrePrReviewMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0` \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. (tasks.md\uC5D0 `- **PR \uC804 \uB9AC\uBDF0**: Pending | Done`\uC744 \uCD94\uAC00\uD558\uC138\uC694.)",
|
|
565
590
|
workflowPrePrReviewNotDone: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0`\uAC00 Done\uC774 \uC544\uB2D9\uB2C8\uB2E4. (\uC0AC\uC804 \uCF54\uB4DC\uB9AC\uBDF0 \uD6C4 Done\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.)",
|
|
566
|
-
workflowPrePrFindingsMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Findings`\uAC00 \uC5C6\uAC70\uB098 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. (`major=<n>, minor=<n>` \uD615\uC2DD\uC73C\uB85C \uAE30\uB85D)",
|
|
567
591
|
workflowPrePrEvidenceMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Evidence`\uAC00 \uBE44\uC5B4\uC788\uAC70\uB098 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. (path_required \uC815\uCC45\uC774\uBA74 \uC2E4\uC81C \uC874\uC7AC\uD558\uB294 \uACBD\uB85C\uB97C \uAE30\uB85D\uD558\uC138\uC694.)",
|
|
568
592
|
workflowPrePrDecisionMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Decision`\uC774 \uBE44\uC5B4\uC788\uAC70\uB098 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. (`decision: approve|changes_requested|blocked ...` \uD615\uC2DD)",
|
|
569
|
-
workflowPrePrDecisionNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Decision`\uC774 `{outcome}`\uC785\uB2C8\uB2E4.
|
|
593
|
+
workflowPrePrDecisionNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC `PR \uC804 \uB9AC\uBDF0 Decision`\uC774 `{outcome}`\uC785\uB2C8\uB2E4. \uB9AC\uBDF0 \uB9AC\uC2A4\uD06C\uB97C \uD574\uC18C\uD55C \uB4A4 pre-pr-review\uB97C \uC7AC\uC2E4\uD589\uD574 `approve`\uB85C \uB9DE\uCD94\uC138\uC694."
|
|
570
594
|
}
|
|
571
595
|
};
|
|
572
596
|
var ko_default = ko;
|
|
@@ -650,11 +674,16 @@ var en = {
|
|
|
650
674
|
"context.suggestionHeader": "Suggested Next Options",
|
|
651
675
|
"context.suggestionCommandHint": "Reference command: {command}",
|
|
652
676
|
"context.suggestionFinalPrompt": "Recommended labels now: {labels}. Please reply with a format that includes a label token. (e.g. {example}, `A proceed`)",
|
|
677
|
+
"context.autoRunUnavailable": "Auto-run is not available in the current context.",
|
|
678
|
+
"context.autoRunSummary": "Run continuously by config until approval-required categories appear: {categories}",
|
|
679
|
+
"context.autoRunCommandHint": "Auto-run command (config-based gate): {command}",
|
|
680
|
+
"context.subAgentOrchestrationHint": "Main-agent orchestration: keep short steps in the main agent, and delegate only long-running loops (task_execute/code_review/review_fix_commit/pre_pr_review or auto mode) to a sub-agent.",
|
|
653
681
|
"context.commandDetail.branchCreateWithWorktree": "({scope}) create or reuse worktree {worktree} for branch {branch}",
|
|
654
682
|
"context.commandDetail.branchCreateWithBranch": "({scope}) create or reuse worktree for branch {branch}",
|
|
655
683
|
"context.commandDetail.branchCreateGeneric": "({scope}) create or reuse feature branch worktree",
|
|
656
684
|
"context.commandDetail.codeReviewMergeAfterOk": "({scope}) merge PR after explicit OK",
|
|
657
685
|
"context.commandDetail.codeReviewPushFix": "({scope}) push review-fix commits",
|
|
686
|
+
"context.commandDetail.prePrReviewRun": "({scope}) run pre-PR review and sync decisions.md + tasks.md",
|
|
658
687
|
"context.actionSummary.runDocsCommand": "Run docs command",
|
|
659
688
|
"context.actionSummary.runProjectCommand": "Run project command",
|
|
660
689
|
"context.actionDetail.featureFolder": "Prepare feature folder and baseline docs",
|
|
@@ -663,16 +692,38 @@ var en = {
|
|
|
663
692
|
"context.actionDetail.planWrite": "Write or refine plan.md and set status",
|
|
664
693
|
"context.actionDetail.planApprove": "Approve plan.md",
|
|
665
694
|
"context.actionDetail.tasksWrite": "Write or refine tasks.md and align document status",
|
|
695
|
+
"context.actionDetail.tasksWriteCreate": "Create tasks.md and set Doc Status to Review",
|
|
696
|
+
"context.actionDetail.tasksWriteNeedAtLeastOne": "Add at least one task to tasks.md",
|
|
697
|
+
"context.actionDetail.tasksWriteImprove": "Refine tasks.md and align Doc Status",
|
|
666
698
|
"context.actionDetail.tasksApprove": "Approve tasks.md",
|
|
667
699
|
"context.actionDetail.issueCreate": "Create the issue and sync issue fields in tasks.md",
|
|
700
|
+
"context.actionDetail.issueCreateAndWrite": "Draft issue content, get explicit OK, then create and sync Issue",
|
|
701
|
+
"context.actionDetail.issueCreatePrepareFromDoc": "Refine issue.md draft and set Status to Ready",
|
|
702
|
+
"context.actionDetail.issueCreateFromDoc": "Create GitHub Issue from ready issue.md and sync Issue",
|
|
668
703
|
"context.actionDetail.taskExecute": "Proceed with the current task",
|
|
669
704
|
"context.actionDetail.reviewFixCommit": "Create a review-fix commit with resolved feedback summary",
|
|
670
705
|
"context.actionDetail.prePrReview": "Run pre-PR review and record results",
|
|
671
706
|
"context.actionDetail.prCreate": "Create PR and sync PR fields in tasks.md",
|
|
707
|
+
"context.actionDetail.prCreateRequiredSequence": "Complete PR 2-step flow: prepare draft + OK, then create and sync",
|
|
708
|
+
"context.actionDetail.prCreatePrepareFromDoc": "Refine pr.md draft and set Status to Ready",
|
|
709
|
+
"context.actionDetail.prCreateExecuteFromDoc": "Create PR from ready pr.md and sync PR link/status",
|
|
672
710
|
"context.actionDetail.prStatusUpdate": "Sync PR status in tasks.md with remote status",
|
|
711
|
+
"context.actionDetail.prStatusUpdateSetReview": "Set PR Status to Review",
|
|
712
|
+
"context.actionDetail.prStatusUpdateSyncApproved": "PR merged remotely; sync PR Status to Approved",
|
|
673
713
|
"context.actionDetail.codeReview": "Address review feedback and update PR review fields",
|
|
714
|
+
"context.actionDetail.codeReviewNeedEvidenceField": "Add PR Review Evidence field in tasks.md",
|
|
715
|
+
"context.actionDetail.codeReviewNeedEvidence": "Record PR Review Evidence summary",
|
|
716
|
+
"context.actionDetail.codeReviewNeedDecisionField": "Add PR Review Decision field in tasks.md",
|
|
717
|
+
"context.actionDetail.codeReviewNeedDecision": "Record PR Review Decision",
|
|
718
|
+
"context.actionDetail.codeReviewResolve": "Address review feedback and keep PR review docs updated",
|
|
719
|
+
"context.actionDetail.codeReviewNeedProjectRoot": "Set projectRoot to continue review actions",
|
|
720
|
+
"context.actionDetail.codeReviewRemoteBlocked": "Resolve remote PR blockers before merge",
|
|
721
|
+
"context.actionDetail.codeReviewMergeAfterOk": "Merge PR after explicit OK",
|
|
722
|
+
"context.actionDetail.codeReviewRequestReview": "Request review and keep PR Status as Review",
|
|
674
723
|
"context.actionDetail.worktreeCleanup": "Clean up the completed feature worktree",
|
|
675
724
|
"context.actionDetail.prMetadataMigrate": "Update tasks.md PR fields to the latest template format",
|
|
725
|
+
"context.actionDetail.prMetadataMigratePrFields": "Update tasks.md with PR/PR Status fields",
|
|
726
|
+
"context.actionDetail.prMetadataMigratePrePrReviewField": "Add Pre-PR Review field in tasks.md",
|
|
676
727
|
"context.actionDetail.userRequestReplan": "Handle the new user request first and re-run context",
|
|
677
728
|
"context.actionDetail.featureDone": "All completion checks are satisfied for this feature",
|
|
678
729
|
"context.actionDetail.fallback": "Verify current status and re-run context",
|
|
@@ -684,7 +735,7 @@ var en = {
|
|
|
684
735
|
"context.suggestion.showOpen": "Show open features",
|
|
685
736
|
"context.finalLabelCommandHint": "When a label is provided, run approval selection: {command}",
|
|
686
737
|
"context.finalTicketCommandHint": "Execute commands using the ticket from approval result: {command}",
|
|
687
|
-
"context.readBuiltinDocFirst": "Read required built-in docs only if not read in this session yet or likely changed
|
|
738
|
+
"context.readBuiltinDocFirst": "Read required built-in docs only if not read in this session yet or likely changed: {command}",
|
|
688
739
|
"context.tipDocsCommitRules": "Check commit message rules against the git-workflow guide.",
|
|
689
740
|
"context.list.docsCommitNeeded": "Commit docs changes",
|
|
690
741
|
"context.list.projectCommitNeeded": "Commit project code changes",
|
|
@@ -693,7 +744,6 @@ var en = {
|
|
|
693
744
|
"context.list.recordPrLink": "Record PR link",
|
|
694
745
|
"context.list.addPrePrReviewField": "Add Pre-PR Review field",
|
|
695
746
|
"context.list.completePrePrReview": "Complete Pre-PR review",
|
|
696
|
-
"context.list.addPrePrFindings": "Record Pre-PR Findings",
|
|
697
747
|
"context.list.addPrePrEvidence": "Add Pre-PR Evidence",
|
|
698
748
|
"context.list.addPrePrDecision": "Add Pre-PR Decision",
|
|
699
749
|
"context.list.resolvePrePrDecision": "Resolve Pre-PR decision to approve",
|
|
@@ -979,8 +1029,8 @@ var en = {
|
|
|
979
1029
|
worktreeCleanupCommand: 'cd "{projectGitCwd}" && git worktree remove "{worktreePath}" && git worktree prune && CURRENT_BRANCH=$(git branch --show-current) && DEFAULT_BRANCH=$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | cut -d/ -f2-) && TARGET_BRANCH="${DEFAULT_BRANCH:-$CURRENT_BRANCH}" && if [ -n "$TARGET_BRANCH" ]; then git checkout "$TARGET_BRANCH" >/dev/null 2>&1 || true; fi && if git rev-parse --abbrev-ref --symbolic-full-name "@{u}" >/dev/null 2>&1 && [ -z "$(git status --porcelain)" ]; then git pull --ff-only || true; fi',
|
|
980
1030
|
tasksAllDoneButNoChecklist: 'Create the completion checklist. Add verification items to the tasks.md "Completion Criteria" section, then mark satisfied items as [x] after user confirmation. Record final approval (OK) as well.',
|
|
981
1031
|
tasksAllDoneButChecklist: "Proceed with remaining completion checklist items. Current progress: ({checked}/{total}) Mark items as [x] only after user confirmation and real verification. Record final approval (OK) as well.",
|
|
982
|
-
finishDoingTask: 'Continue working on the current DOING/REVIEW task: "{title}" ({done}/{total}) After it is complete, share outcome/verification and
|
|
983
|
-
startNextTodoTask: 'Start the next TODO task: "{title}" ({done}/{total})
|
|
1032
|
+
finishDoingTask: 'Continue working on the current DOING/REVIEW task: "{title}" ({done}/{total}) After it is complete, share outcome/verification and mark it DONE',
|
|
1033
|
+
startNextTodoTask: 'Start the next TODO task: "{title}" ({done}/{total}) Mark it DOING when you begin work',
|
|
984
1034
|
checkTaskStatuses: "Check task statuses. ({done}/{total})",
|
|
985
1035
|
taskCommitGateStrictBlock: "Before moving to the next TODO task, you must satisfy the `1 task = 1 commit` rule. Check result: {reason}. Re-align task commit boundaries, then continue.",
|
|
986
1036
|
taskCommitGateWarnProceed: "\u26A0\uFE0F Task commit boundary warning: {reason}. You may continue, but `1 task = 1 commit` is recommended.",
|
|
@@ -1031,7 +1081,6 @@ var en = {
|
|
|
1031
1081
|
legacyTasksDocStatusField: "Legacy tasks.md format detected. Add a `Doc Status` field (Draft/Review/Approved) to enable tasks approval.",
|
|
1032
1082
|
legacyTasksPrFields: "Legacy tasks.md format detected. Add `PR` and `PR Status` fields before PR steps.",
|
|
1033
1083
|
legacyTasksPrePrReviewField: "Legacy tasks.md format detected. Add `Pre-PR Review` before PR steps. (`- **Pre-PR Review**: Pending | Done`)",
|
|
1034
|
-
legacyTasksPrePrFindingsField: "Legacy tasks.md format detected. Add `Pre-PR Findings` before PR steps. (`- **Pre-PR Findings**: major=0, minor=0`)",
|
|
1035
1084
|
legacyTasksPrePrEvidenceField: "Legacy tasks.md format detected. Add `Pre-PR Evidence` before PR steps.",
|
|
1036
1085
|
legacyTasksPrePrDecisionField: "Legacy tasks.md format detected. Add `Pre-PR Decision` before PR steps. (`- **Pre-PR Decision**: decision: ...`)",
|
|
1037
1086
|
legacyTasksPrReviewEvidenceField: "Legacy tasks.md format detected. Add `PR Review Evidence` before review iteration.",
|
|
@@ -1050,10 +1099,9 @@ var en = {
|
|
|
1050
1099
|
workflowPrRemoteChecksPending: "Remote PR has {count} pending check(s). Wait for checks to complete, then re-check.",
|
|
1051
1100
|
workflowPrePrReviewMissing: "Implementation is done but `Pre-PR Review` is missing. (Add `- **Pre-PR Review**: Pending | Done` in tasks.md.)",
|
|
1052
1101
|
workflowPrePrReviewNotDone: "Implementation is done but `Pre-PR Review` is not Done. (Run pre-PR review, then update it to Done.)",
|
|
1053
|
-
workflowPrePrFindingsMissing: "Implementation is done but `Pre-PR Findings` is missing/invalid. (Use `major=<n>, minor=<n>`.)",
|
|
1054
1102
|
workflowPrePrEvidenceMissing: "Implementation is done but `Pre-PR Evidence` is empty/invalid. (Record a real existing path when path_required policy is enabled.)",
|
|
1055
1103
|
workflowPrePrDecisionMissing: "Implementation is done but `Pre-PR Decision` is empty/invalid. (Use `decision: approve|changes_requested|blocked ...`.)",
|
|
1056
|
-
workflowPrePrDecisionNotApproved: "Implementation is done but `Pre-PR Decision` is `{outcome}`. Resolve
|
|
1104
|
+
workflowPrePrDecisionNotApproved: "Implementation is done but `Pre-PR Decision` is `{outcome}`. Resolve review risks and re-run pre-PR review until decision becomes `approve`."
|
|
1057
1105
|
}
|
|
1058
1106
|
};
|
|
1059
1107
|
var en_default = en;
|
|
@@ -1479,10 +1527,10 @@ var DEFAULT_STALE_MS = 2 * 6e4;
|
|
|
1479
1527
|
var RUNTIME_GIT_DIRNAME = "lee-spec-kit.runtime";
|
|
1480
1528
|
var RUNTIME_TEMP_DIRNAME = "lee-spec-kit-runtime";
|
|
1481
1529
|
function toScopeKey(value) {
|
|
1482
|
-
return createHash("sha1").update(
|
|
1530
|
+
return createHash("sha1").update(path23.resolve(value)).digest("hex").slice(0, 16);
|
|
1483
1531
|
}
|
|
1484
1532
|
function getTempRuntimeDir(scopePath) {
|
|
1485
|
-
return
|
|
1533
|
+
return path23.join(os.tmpdir(), RUNTIME_TEMP_DIRNAME, toScopeKey(scopePath));
|
|
1486
1534
|
}
|
|
1487
1535
|
function resolveGitRuntimeDir(cwd) {
|
|
1488
1536
|
try {
|
|
@@ -1496,42 +1544,42 @@ function resolveGitRuntimeDir(cwd) {
|
|
|
1496
1544
|
}
|
|
1497
1545
|
).trim();
|
|
1498
1546
|
if (!out) return null;
|
|
1499
|
-
return
|
|
1547
|
+
return path23.isAbsolute(out) ? out : path23.resolve(cwd, out);
|
|
1500
1548
|
} catch {
|
|
1501
1549
|
return null;
|
|
1502
1550
|
}
|
|
1503
1551
|
}
|
|
1504
1552
|
function getRuntimeStateDir(cwd) {
|
|
1505
|
-
const resolved =
|
|
1553
|
+
const resolved = path23.resolve(cwd);
|
|
1506
1554
|
return resolveGitRuntimeDir(resolved) ?? getTempRuntimeDir(resolved);
|
|
1507
1555
|
}
|
|
1508
1556
|
function getDocsLockPath(docsDir) {
|
|
1509
|
-
return
|
|
1557
|
+
return path23.join(
|
|
1510
1558
|
getRuntimeStateDir(docsDir),
|
|
1511
1559
|
"locks",
|
|
1512
1560
|
`docs-${toScopeKey(docsDir)}.lock`
|
|
1513
1561
|
);
|
|
1514
1562
|
}
|
|
1515
1563
|
function getInitLockPath(targetDir) {
|
|
1516
|
-
return
|
|
1517
|
-
getRuntimeStateDir(
|
|
1564
|
+
return path23.join(
|
|
1565
|
+
getRuntimeStateDir(path23.dirname(path23.resolve(targetDir))),
|
|
1518
1566
|
"locks",
|
|
1519
1567
|
`init-${toScopeKey(targetDir)}.lock`
|
|
1520
1568
|
);
|
|
1521
1569
|
}
|
|
1522
1570
|
function getApprovalTicketStorePath(docsDir) {
|
|
1523
|
-
return
|
|
1571
|
+
return path23.join(
|
|
1524
1572
|
getRuntimeStateDir(docsDir),
|
|
1525
1573
|
"tickets",
|
|
1526
1574
|
`approval-${toScopeKey(docsDir)}.json`
|
|
1527
1575
|
);
|
|
1528
1576
|
}
|
|
1529
1577
|
function getProjectExecutionLockPath(cwd) {
|
|
1530
|
-
return
|
|
1578
|
+
return path23.join(getRuntimeStateDir(cwd), "locks", "project.lock");
|
|
1531
1579
|
}
|
|
1532
1580
|
async function isStaleLock(lockPath, staleMs) {
|
|
1533
1581
|
try {
|
|
1534
|
-
const stat = await
|
|
1582
|
+
const stat = await fs17.stat(lockPath);
|
|
1535
1583
|
if (Date.now() - stat.mtimeMs <= staleMs) {
|
|
1536
1584
|
return false;
|
|
1537
1585
|
}
|
|
@@ -1546,7 +1594,7 @@ async function isStaleLock(lockPath, staleMs) {
|
|
|
1546
1594
|
}
|
|
1547
1595
|
async function readLockPayload(lockPath) {
|
|
1548
1596
|
try {
|
|
1549
|
-
const raw = await
|
|
1597
|
+
const raw = await fs17.readFile(lockPath, "utf8");
|
|
1550
1598
|
const parsed = JSON.parse(raw);
|
|
1551
1599
|
if (!parsed || typeof parsed !== "object") return null;
|
|
1552
1600
|
return parsed;
|
|
@@ -1568,17 +1616,17 @@ function isProcessAlive(pid) {
|
|
|
1568
1616
|
}
|
|
1569
1617
|
}
|
|
1570
1618
|
async function tryAcquire(lockPath, owner) {
|
|
1571
|
-
await
|
|
1619
|
+
await fs17.ensureDir(path23.dirname(lockPath));
|
|
1572
1620
|
try {
|
|
1573
|
-
const fd = await
|
|
1621
|
+
const fd = await fs17.open(lockPath, "wx");
|
|
1574
1622
|
const payload = JSON.stringify(
|
|
1575
1623
|
{ pid: process.pid, owner: owner ?? "unknown", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
|
|
1576
1624
|
null,
|
|
1577
1625
|
2
|
|
1578
1626
|
);
|
|
1579
|
-
await
|
|
1627
|
+
await fs17.writeFile(fd, `${payload}
|
|
1580
1628
|
`, { encoding: "utf8" });
|
|
1581
|
-
await
|
|
1629
|
+
await fs17.close(fd);
|
|
1582
1630
|
return true;
|
|
1583
1631
|
} catch (error) {
|
|
1584
1632
|
if (error.code === "EEXIST") {
|
|
@@ -1592,9 +1640,9 @@ async function waitForLockRelease(lockPath, options = {}) {
|
|
|
1592
1640
|
const pollMs = options.pollMs ?? DEFAULT_POLL_MS;
|
|
1593
1641
|
const staleMs = options.staleMs ?? DEFAULT_STALE_MS;
|
|
1594
1642
|
const startedAt = Date.now();
|
|
1595
|
-
while (await
|
|
1643
|
+
while (await fs17.pathExists(lockPath)) {
|
|
1596
1644
|
if (await isStaleLock(lockPath, staleMs)) {
|
|
1597
|
-
await
|
|
1645
|
+
await fs17.remove(lockPath);
|
|
1598
1646
|
break;
|
|
1599
1647
|
}
|
|
1600
1648
|
if (Date.now() - startedAt > timeoutMs) {
|
|
@@ -1612,7 +1660,7 @@ async function withFileLock(lockPath, task, options = {}) {
|
|
|
1612
1660
|
const acquired = await tryAcquire(lockPath, options.owner);
|
|
1613
1661
|
if (acquired) break;
|
|
1614
1662
|
if (await isStaleLock(lockPath, staleMs)) {
|
|
1615
|
-
await
|
|
1663
|
+
await fs17.remove(lockPath);
|
|
1616
1664
|
continue;
|
|
1617
1665
|
}
|
|
1618
1666
|
if (Date.now() - startedAt > timeoutMs) {
|
|
@@ -1626,7 +1674,7 @@ async function withFileLock(lockPath, task, options = {}) {
|
|
|
1626
1674
|
try {
|
|
1627
1675
|
return await task();
|
|
1628
1676
|
} finally {
|
|
1629
|
-
await
|
|
1677
|
+
await fs17.remove(lockPath).catch(() => {
|
|
1630
1678
|
});
|
|
1631
1679
|
}
|
|
1632
1680
|
}
|
|
@@ -1645,30 +1693,30 @@ var ENGINE_MANAGED_AGENT_FILES = [
|
|
|
1645
1693
|
"pr-template.md"
|
|
1646
1694
|
];
|
|
1647
1695
|
var ENGINE_MANAGED_AGENT_DIRS = ["skills"];
|
|
1648
|
-
var ENGINE_MANAGED_FEATURE_PATH =
|
|
1696
|
+
var ENGINE_MANAGED_FEATURE_PATH = path23.join(
|
|
1649
1697
|
"features",
|
|
1650
1698
|
"feature-base"
|
|
1651
1699
|
);
|
|
1652
1700
|
async function pruneEngineManagedDocs(docsDir) {
|
|
1653
1701
|
const removed = [];
|
|
1654
1702
|
for (const file of ENGINE_MANAGED_AGENT_FILES) {
|
|
1655
|
-
const target =
|
|
1656
|
-
if (await
|
|
1657
|
-
await
|
|
1658
|
-
removed.push(
|
|
1703
|
+
const target = path23.join(docsDir, "agents", file);
|
|
1704
|
+
if (await fs17.pathExists(target)) {
|
|
1705
|
+
await fs17.remove(target);
|
|
1706
|
+
removed.push(path23.relative(docsDir, target));
|
|
1659
1707
|
}
|
|
1660
1708
|
}
|
|
1661
1709
|
for (const dir of ENGINE_MANAGED_AGENT_DIRS) {
|
|
1662
|
-
const target =
|
|
1663
|
-
if (await
|
|
1664
|
-
await
|
|
1665
|
-
removed.push(
|
|
1710
|
+
const target = path23.join(docsDir, "agents", dir);
|
|
1711
|
+
if (await fs17.pathExists(target)) {
|
|
1712
|
+
await fs17.remove(target);
|
|
1713
|
+
removed.push(path23.relative(docsDir, target));
|
|
1666
1714
|
}
|
|
1667
1715
|
}
|
|
1668
|
-
const featureBasePath =
|
|
1669
|
-
if (await
|
|
1670
|
-
await
|
|
1671
|
-
removed.push(
|
|
1716
|
+
const featureBasePath = path23.join(docsDir, ENGINE_MANAGED_FEATURE_PATH);
|
|
1717
|
+
if (await fs17.pathExists(featureBasePath)) {
|
|
1718
|
+
await fs17.remove(featureBasePath);
|
|
1719
|
+
removed.push(path23.relative(docsDir, featureBasePath));
|
|
1672
1720
|
}
|
|
1673
1721
|
return removed;
|
|
1674
1722
|
}
|
|
@@ -1825,7 +1873,7 @@ ${tr(lang2, "cli", "common.canceled")}`)
|
|
|
1825
1873
|
}
|
|
1826
1874
|
async function runInit(options) {
|
|
1827
1875
|
const cwd = process.cwd();
|
|
1828
|
-
const defaultName =
|
|
1876
|
+
const defaultName = path23.basename(cwd);
|
|
1829
1877
|
let projectName = options.name || defaultName;
|
|
1830
1878
|
let projectType = options.type;
|
|
1831
1879
|
let components = parseComponentsOption(options.components);
|
|
@@ -1836,7 +1884,7 @@ async function runInit(options) {
|
|
|
1836
1884
|
let docsRemote = options.docsRemote;
|
|
1837
1885
|
let projectRoot;
|
|
1838
1886
|
const componentProjectRoots = options.componentProjectRoots ? parseComponentProjectRootsOption(options.componentProjectRoots) : {};
|
|
1839
|
-
const targetDir =
|
|
1887
|
+
const targetDir = path23.resolve(cwd, options.dir || "./docs");
|
|
1840
1888
|
const skipPrompts = !!options.yes || !!options.nonInteractive;
|
|
1841
1889
|
if (options.docsRepo && !["embedded", "standalone"].includes(options.docsRepo)) {
|
|
1842
1890
|
throw createCliError(
|
|
@@ -2188,8 +2236,8 @@ async function runInit(options) {
|
|
|
2188
2236
|
await withFileLock(
|
|
2189
2237
|
initLockPath,
|
|
2190
2238
|
async () => {
|
|
2191
|
-
if (await
|
|
2192
|
-
const files = await
|
|
2239
|
+
if (await fs17.pathExists(targetDir)) {
|
|
2240
|
+
const files = await fs17.readdir(targetDir);
|
|
2193
2241
|
if (files.length > 0) {
|
|
2194
2242
|
if (options.force) {
|
|
2195
2243
|
} else if (options.nonInteractive) {
|
|
@@ -2225,21 +2273,21 @@ async function runInit(options) {
|
|
|
2225
2273
|
);
|
|
2226
2274
|
console.log();
|
|
2227
2275
|
const templatesDir = getTemplatesDir();
|
|
2228
|
-
const commonPath =
|
|
2229
|
-
if (!await
|
|
2276
|
+
const commonPath = path23.join(templatesDir, lang, "common");
|
|
2277
|
+
if (!await fs17.pathExists(commonPath)) {
|
|
2230
2278
|
throw new Error(
|
|
2231
2279
|
tr(lang, "cli", "init.error.templateNotFound", { path: commonPath })
|
|
2232
2280
|
);
|
|
2233
2281
|
}
|
|
2234
2282
|
await copyTemplates(commonPath, targetDir);
|
|
2235
2283
|
if (projectType === "multi") {
|
|
2236
|
-
const featuresRoot =
|
|
2284
|
+
const featuresRoot = path23.join(targetDir, "features");
|
|
2237
2285
|
for (const component of components) {
|
|
2238
|
-
const componentDir =
|
|
2239
|
-
await
|
|
2240
|
-
const readmePath =
|
|
2241
|
-
if (!await
|
|
2242
|
-
await
|
|
2286
|
+
const componentDir = path23.join(featuresRoot, component);
|
|
2287
|
+
await fs17.ensureDir(componentDir);
|
|
2288
|
+
const readmePath = path23.join(componentDir, "README.md");
|
|
2289
|
+
if (!await fs17.pathExists(readmePath)) {
|
|
2290
|
+
await fs17.writeFile(
|
|
2243
2291
|
readmePath,
|
|
2244
2292
|
getComponentFeaturesReadme(lang, component),
|
|
2245
2293
|
"utf-8"
|
|
@@ -2274,7 +2322,6 @@ async function runInit(options) {
|
|
|
2274
2322
|
skills: ["code-review-excellence"],
|
|
2275
2323
|
fallback: "builtin-checklist",
|
|
2276
2324
|
evidenceMode: "path_required",
|
|
2277
|
-
findings: "required",
|
|
2278
2325
|
decisionEnum: ["approve", "changes_requested", "blocked"]
|
|
2279
2326
|
}
|
|
2280
2327
|
},
|
|
@@ -2296,8 +2343,8 @@ async function runInit(options) {
|
|
|
2296
2343
|
config.projectRoot = projectRoot;
|
|
2297
2344
|
}
|
|
2298
2345
|
}
|
|
2299
|
-
const configPath =
|
|
2300
|
-
await
|
|
2346
|
+
const configPath = path23.join(targetDir, ".lee-spec-kit.json");
|
|
2347
|
+
await fs17.writeJson(configPath, config, { spaces: 2 });
|
|
2301
2348
|
console.log(chalk6.green(tr(lang, "cli", "init.log.docsCreated")));
|
|
2302
2349
|
console.log();
|
|
2303
2350
|
await initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote);
|
|
@@ -2360,7 +2407,7 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
|
|
|
2360
2407
|
console.log(chalk6.blue(tr(lang, "cli", "init.log.gitInit")));
|
|
2361
2408
|
runGitOrThrow(["init"], gitWorkdir);
|
|
2362
2409
|
}
|
|
2363
|
-
const relativePath = docsRepo === "standalone" ? "." :
|
|
2410
|
+
const relativePath = docsRepo === "standalone" ? "." : path23.relative(cwd, targetDir);
|
|
2364
2411
|
const stagedBeforeAdd = getCachedStagedFiles(gitWorkdir);
|
|
2365
2412
|
if (relativePath === "." && stagedBeforeAdd && stagedBeforeAdd.length > 0) {
|
|
2366
2413
|
console.log(
|
|
@@ -2417,17 +2464,17 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
|
|
|
2417
2464
|
}
|
|
2418
2465
|
function getAncestorDirs(startDir) {
|
|
2419
2466
|
const dirs = [];
|
|
2420
|
-
let current =
|
|
2467
|
+
let current = path23.resolve(startDir);
|
|
2421
2468
|
while (true) {
|
|
2422
2469
|
dirs.push(current);
|
|
2423
|
-
const parent =
|
|
2470
|
+
const parent = path23.dirname(current);
|
|
2424
2471
|
if (parent === current) break;
|
|
2425
2472
|
current = parent;
|
|
2426
2473
|
}
|
|
2427
2474
|
return dirs;
|
|
2428
2475
|
}
|
|
2429
2476
|
function hasWorkspaceBoundary(dir) {
|
|
2430
|
-
return
|
|
2477
|
+
return fs17.existsSync(path23.join(dir, "package.json")) || fs17.existsSync(path23.join(dir, ".git"));
|
|
2431
2478
|
}
|
|
2432
2479
|
function getSearchBaseDirs(cwd) {
|
|
2433
2480
|
const ancestors = getAncestorDirs(cwd);
|
|
@@ -2443,9 +2490,9 @@ function normalizeComponentKeys(value) {
|
|
|
2443
2490
|
return Object.keys(value).map((key) => key.trim().toLowerCase()).filter(Boolean);
|
|
2444
2491
|
}
|
|
2445
2492
|
async function inferComponentsFromFeaturesDir(docsDir) {
|
|
2446
|
-
const featuresPath =
|
|
2447
|
-
if (!await
|
|
2448
|
-
const entries = await
|
|
2493
|
+
const featuresPath = path23.join(docsDir, "features");
|
|
2494
|
+
if (!await fs17.pathExists(featuresPath)) return [];
|
|
2495
|
+
const entries = await fs17.readdir(featuresPath, { withFileTypes: true });
|
|
2449
2496
|
const inferred = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name.trim().toLowerCase()).filter(
|
|
2450
2497
|
(name) => !!name && name !== "feature-base" && !FEATURE_FOLDER_PATTERN.test(name)
|
|
2451
2498
|
);
|
|
@@ -2454,24 +2501,24 @@ async function inferComponentsFromFeaturesDir(docsDir) {
|
|
|
2454
2501
|
async function getConfig(cwd) {
|
|
2455
2502
|
const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
|
|
2456
2503
|
const baseDirs = [
|
|
2457
|
-
...explicitDocsDir ? [
|
|
2504
|
+
...explicitDocsDir ? [path23.resolve(explicitDocsDir)] : [],
|
|
2458
2505
|
...getSearchBaseDirs(cwd)
|
|
2459
2506
|
];
|
|
2460
2507
|
const visitedBaseDirs = /* @__PURE__ */ new Set();
|
|
2461
2508
|
const visitedDocsDirs = /* @__PURE__ */ new Set();
|
|
2462
2509
|
for (const baseDir of baseDirs) {
|
|
2463
|
-
const resolvedBaseDir =
|
|
2510
|
+
const resolvedBaseDir = path23.resolve(baseDir);
|
|
2464
2511
|
if (visitedBaseDirs.has(resolvedBaseDir)) continue;
|
|
2465
2512
|
visitedBaseDirs.add(resolvedBaseDir);
|
|
2466
|
-
const possibleDocsDirs = [
|
|
2513
|
+
const possibleDocsDirs = [path23.join(resolvedBaseDir, "docs"), resolvedBaseDir];
|
|
2467
2514
|
for (const docsDir of possibleDocsDirs) {
|
|
2468
|
-
const resolvedDocsDir =
|
|
2515
|
+
const resolvedDocsDir = path23.resolve(docsDir);
|
|
2469
2516
|
if (visitedDocsDirs.has(resolvedDocsDir)) continue;
|
|
2470
2517
|
visitedDocsDirs.add(resolvedDocsDir);
|
|
2471
|
-
const configPath =
|
|
2472
|
-
if (await
|
|
2518
|
+
const configPath = path23.join(resolvedDocsDir, ".lee-spec-kit.json");
|
|
2519
|
+
if (await fs17.pathExists(configPath)) {
|
|
2473
2520
|
try {
|
|
2474
|
-
const configFile = await
|
|
2521
|
+
const configFile = await fs17.readJson(configPath);
|
|
2475
2522
|
const projectType = normalizeProjectType(configFile.projectType);
|
|
2476
2523
|
const inferredComponents = [
|
|
2477
2524
|
...normalizeComponentKeys(configFile.projectRoot),
|
|
@@ -2498,21 +2545,21 @@ async function getConfig(cwd) {
|
|
|
2498
2545
|
} catch {
|
|
2499
2546
|
}
|
|
2500
2547
|
}
|
|
2501
|
-
const agentsPath =
|
|
2502
|
-
const featuresPath =
|
|
2503
|
-
if (await
|
|
2548
|
+
const agentsPath = path23.join(resolvedDocsDir, "agents");
|
|
2549
|
+
const featuresPath = path23.join(resolvedDocsDir, "features");
|
|
2550
|
+
if (await fs17.pathExists(agentsPath) && await fs17.pathExists(featuresPath)) {
|
|
2504
2551
|
const inferredComponents = await inferComponentsFromFeaturesDir(resolvedDocsDir);
|
|
2505
2552
|
const projectType = inferredComponents.length > 0 ? "multi" : "single";
|
|
2506
2553
|
const components = projectType === "multi" ? resolveProjectComponents("multi", inferredComponents) : void 0;
|
|
2507
2554
|
const langProbeCandidates = [
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2555
|
+
path23.join(agentsPath, "custom.md"),
|
|
2556
|
+
path23.join(agentsPath, "constitution.md"),
|
|
2557
|
+
path23.join(agentsPath, "agents.md")
|
|
2511
2558
|
];
|
|
2512
2559
|
let lang = "en";
|
|
2513
2560
|
for (const candidate of langProbeCandidates) {
|
|
2514
|
-
if (!await
|
|
2515
|
-
const content = await
|
|
2561
|
+
if (!await fs17.pathExists(candidate)) continue;
|
|
2562
|
+
const content = await fs17.readFile(candidate, "utf-8");
|
|
2516
2563
|
if (/[가-힣]/.test(content)) {
|
|
2517
2564
|
lang = "ko";
|
|
2518
2565
|
break;
|
|
@@ -2547,7 +2594,7 @@ function sanitizeTasksForLocal(content, lang) {
|
|
|
2547
2594
|
)) {
|
|
2548
2595
|
continue;
|
|
2549
2596
|
}
|
|
2550
|
-
if (/^\s*-\s*\*\*(Pre-PR
|
|
2597
|
+
if (/^\s*-\s*\*\*(Pre-PR Evidence|PR 전 리뷰 Evidence)\*\*\s*:/.test(
|
|
2551
2598
|
line
|
|
2552
2599
|
)) {
|
|
2553
2600
|
continue;
|
|
@@ -2561,8 +2608,6 @@ function sanitizeTasksForLocal(content, lang) {
|
|
|
2561
2608
|
if (/^\s*-\s*(예|값)\s*:/.test(line)) continue;
|
|
2562
2609
|
if (/^\s*-\s*Mark\s+`?Done`?/i.test(line)) continue;
|
|
2563
2610
|
if (/^\s*-\s*사전 코드리뷰 완료 후/.test(line)) continue;
|
|
2564
|
-
if (/^\s*-\s*Update with final findings counts from pre-PR review/i.test(line))
|
|
2565
|
-
continue;
|
|
2566
2611
|
if (/^\s*-\s*Record your key review decision/i.test(line)) continue;
|
|
2567
2612
|
if (/^\s*-\s*사전 리뷰 주요 판단 근거를/.test(line)) continue;
|
|
2568
2613
|
if (/^\s*-\s*Example:\s*review note link/i.test(line)) continue;
|
|
@@ -2575,18 +2620,18 @@ function sanitizeTasksForLocal(content, lang) {
|
|
|
2575
2620
|
return normalizeTrailingBlankLines(next);
|
|
2576
2621
|
}
|
|
2577
2622
|
async function patchMarkdownIfExists(filePath, transform) {
|
|
2578
|
-
if (!await
|
|
2579
|
-
const content = await
|
|
2580
|
-
await
|
|
2623
|
+
if (!await fs17.pathExists(filePath)) return;
|
|
2624
|
+
const content = await fs17.readFile(filePath, "utf-8");
|
|
2625
|
+
await fs17.writeFile(filePath, transform(content), "utf-8");
|
|
2581
2626
|
}
|
|
2582
2627
|
async function applyLocalWorkflowTemplateToFeatureDir(featureDir, lang) {
|
|
2583
|
-
await patchMarkdownIfExists(
|
|
2628
|
+
await patchMarkdownIfExists(path23.join(featureDir, "spec.md"), sanitizeSpecForLocal);
|
|
2584
2629
|
await patchMarkdownIfExists(
|
|
2585
|
-
|
|
2630
|
+
path23.join(featureDir, "tasks.md"),
|
|
2586
2631
|
(content) => sanitizeTasksForLocal(content, lang)
|
|
2587
2632
|
);
|
|
2588
|
-
await
|
|
2589
|
-
await
|
|
2633
|
+
await fs17.remove(path23.join(featureDir, "issue.md"));
|
|
2634
|
+
await fs17.remove(path23.join(featureDir, "pr.md"));
|
|
2590
2635
|
}
|
|
2591
2636
|
|
|
2592
2637
|
// src/commands/feature.ts
|
|
@@ -2733,29 +2778,29 @@ async function runFeature(name, options) {
|
|
|
2733
2778
|
}
|
|
2734
2779
|
let featuresDir;
|
|
2735
2780
|
if (projectType === "multi") {
|
|
2736
|
-
featuresDir =
|
|
2781
|
+
featuresDir = path23.join(docsDir, "features", component);
|
|
2737
2782
|
} else {
|
|
2738
|
-
featuresDir =
|
|
2783
|
+
featuresDir = path23.join(docsDir, "features");
|
|
2739
2784
|
}
|
|
2740
2785
|
const featureFolderName = `${featureId}-${name}`;
|
|
2741
|
-
const featureDir =
|
|
2742
|
-
if (await
|
|
2786
|
+
const featureDir = path23.join(featuresDir, featureFolderName);
|
|
2787
|
+
if (await fs17.pathExists(featureDir)) {
|
|
2743
2788
|
throw createCliError(
|
|
2744
2789
|
"INVALID_ARGUMENT",
|
|
2745
2790
|
tr(lang, "cli", "feature.folderExists", { path: featureDir })
|
|
2746
2791
|
);
|
|
2747
2792
|
}
|
|
2748
|
-
const featureBasePath =
|
|
2793
|
+
const featureBasePath = path23.join(
|
|
2749
2794
|
getTemplatesDir(),
|
|
2750
2795
|
lang,
|
|
2751
2796
|
"common",
|
|
2752
2797
|
"features",
|
|
2753
2798
|
"feature-base"
|
|
2754
2799
|
);
|
|
2755
|
-
if (!await
|
|
2800
|
+
if (!await fs17.pathExists(featureBasePath)) {
|
|
2756
2801
|
throw createCliError("DOCS_NOT_FOUND", tr(lang, "cli", "feature.baseNotFound"));
|
|
2757
2802
|
}
|
|
2758
|
-
await
|
|
2803
|
+
await fs17.copy(featureBasePath, featureDir);
|
|
2759
2804
|
const idNumber = featureId.replace("F", "");
|
|
2760
2805
|
const repoName = projectType === "multi" ? `{{projectName}}-${component}` : "{{projectName}}";
|
|
2761
2806
|
const replacements = {
|
|
@@ -2804,7 +2849,7 @@ async function runFeature(name, options) {
|
|
|
2804
2849
|
featureName: name,
|
|
2805
2850
|
component: projectType === "multi" ? component : void 0,
|
|
2806
2851
|
featurePath: featureDir,
|
|
2807
|
-
featurePathFromDocs:
|
|
2852
|
+
featurePathFromDocs: path23.relative(docsDir, featureDir)
|
|
2808
2853
|
};
|
|
2809
2854
|
},
|
|
2810
2855
|
{ owner: "feature" }
|
|
@@ -2813,9 +2858,9 @@ async function runFeature(name, options) {
|
|
|
2813
2858
|
async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
|
|
2814
2859
|
const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
|
|
2815
2860
|
const candidates = [
|
|
2816
|
-
...explicitDocsDir ? [
|
|
2817
|
-
|
|
2818
|
-
|
|
2861
|
+
...explicitDocsDir ? [path23.resolve(explicitDocsDir)] : [],
|
|
2862
|
+
path23.resolve(cwd, "docs"),
|
|
2863
|
+
path23.resolve(cwd)
|
|
2819
2864
|
];
|
|
2820
2865
|
const endAt = Date.now() + timeoutMs;
|
|
2821
2866
|
while (Date.now() < endAt) {
|
|
@@ -2826,7 +2871,7 @@ async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
|
|
|
2826
2871
|
const initLockPath = getInitLockPath(dir);
|
|
2827
2872
|
const docsLockPath = getDocsLockPath(dir);
|
|
2828
2873
|
for (const lockPath of [initLockPath, docsLockPath]) {
|
|
2829
|
-
if (await
|
|
2874
|
+
if (await fs17.pathExists(lockPath)) {
|
|
2830
2875
|
sawLock = true;
|
|
2831
2876
|
await waitForLockRelease(lockPath, {
|
|
2832
2877
|
timeoutMs: Math.max(200, endAt - Date.now()),
|
|
@@ -2842,17 +2887,17 @@ async function waitForConfigAfterInit(cwd, timeoutMs = 8e3) {
|
|
|
2842
2887
|
return getConfig(cwd);
|
|
2843
2888
|
}
|
|
2844
2889
|
async function getNextFeatureId(docsDir, projectType, components) {
|
|
2845
|
-
const featuresDir =
|
|
2890
|
+
const featuresDir = path23.join(docsDir, "features");
|
|
2846
2891
|
let max = 0;
|
|
2847
2892
|
const scanDirs = [];
|
|
2848
2893
|
if (projectType === "multi") {
|
|
2849
|
-
scanDirs.push(...components.map((component) =>
|
|
2894
|
+
scanDirs.push(...components.map((component) => path23.join(featuresDir, component)));
|
|
2850
2895
|
} else {
|
|
2851
2896
|
scanDirs.push(featuresDir);
|
|
2852
2897
|
}
|
|
2853
2898
|
for (const dir of scanDirs) {
|
|
2854
|
-
if (!await
|
|
2855
|
-
const entries = await
|
|
2899
|
+
if (!await fs17.pathExists(dir)) continue;
|
|
2900
|
+
const entries = await fs17.readdir(dir, { withFileTypes: true });
|
|
2856
2901
|
for (const entry of entries) {
|
|
2857
2902
|
if (!entry.isDirectory()) continue;
|
|
2858
2903
|
const match = entry.name.match(/^F(\d+)-/);
|
|
@@ -2994,7 +3039,6 @@ function resolvePrePrReviewPolicy(workflow) {
|
|
|
2994
3039
|
skills: configuredSkills.length > 0 ? configuredSkills : DEFAULT_PRE_PR_REVIEW_SKILLS,
|
|
2995
3040
|
fallback: configured?.fallback === "builtin-checklist" ? configured.fallback : "builtin-checklist",
|
|
2996
3041
|
evidenceMode: configured?.evidenceMode === "any" ? "any" : "path_required",
|
|
2997
|
-
findings: configured?.findings === "optional" ? "optional" : "required",
|
|
2998
3042
|
decisionEnum: configuredDecisionEnum.length > 0 ? configuredDecisionEnum : DEFAULT_PRE_PR_DECISION_ENUM
|
|
2999
3043
|
};
|
|
3000
3044
|
}
|
|
@@ -3023,9 +3067,6 @@ function isPrePrReviewSatisfied(feature, prePrReviewPolicy) {
|
|
|
3023
3067
|
if (!feature.docs.prePrEvidenceFieldExists || !feature.prePrReview.evidenceProvided) {
|
|
3024
3068
|
return false;
|
|
3025
3069
|
}
|
|
3026
|
-
if (prePrReviewPolicy.findings === "required" && (!feature.docs.prePrFindingsFieldExists || !feature.prePrReview.findingsProvided)) {
|
|
3027
|
-
return false;
|
|
3028
|
-
}
|
|
3029
3070
|
if (!feature.docs.prePrDecisionFieldExists || !feature.prePrReview.decisionProvided) {
|
|
3030
3071
|
return false;
|
|
3031
3072
|
}
|
|
@@ -3095,8 +3136,8 @@ function resolveProjectCommitTopic(feature) {
|
|
|
3095
3136
|
}
|
|
3096
3137
|
function resolveManagedWorktreeCleanupPaths(projectGitCwd) {
|
|
3097
3138
|
if (!projectGitCwd) return null;
|
|
3098
|
-
const normalized =
|
|
3099
|
-
const marker = `${
|
|
3139
|
+
const normalized = path23.resolve(projectGitCwd);
|
|
3140
|
+
const marker = `${path23.sep}.worktrees${path23.sep}`;
|
|
3100
3141
|
const markerIndex = normalized.lastIndexOf(marker);
|
|
3101
3142
|
if (markerIndex <= 0) return null;
|
|
3102
3143
|
const projectRoot = normalized.slice(0, markerIndex);
|
|
@@ -3140,7 +3181,7 @@ function toTaskKey(rawTitle) {
|
|
|
3140
3181
|
function countDoneTransitionsInLatestTasksCommit(feature) {
|
|
3141
3182
|
const docsGitCwd = feature.git.docsGitCwd;
|
|
3142
3183
|
const tasksRelativePath = normalizeGitRelativePath(
|
|
3143
|
-
|
|
3184
|
+
path23.join(feature.docs.featurePathFromDocs, "tasks.md")
|
|
3144
3185
|
);
|
|
3145
3186
|
const diff = readGitText(docsGitCwd, [
|
|
3146
3187
|
"diff",
|
|
@@ -3213,7 +3254,7 @@ function checkTaskCommitGate(feature) {
|
|
|
3213
3254
|
return { pass: true };
|
|
3214
3255
|
}
|
|
3215
3256
|
const args = ["log", "-n", "1", "--pretty=%s", "--", "."];
|
|
3216
|
-
const relativeDocsDir =
|
|
3257
|
+
const relativeDocsDir = path23.relative(projectGitCwd, feature.git.docsGitCwd);
|
|
3217
3258
|
const normalizedDocsDir = normalizeGitRelativePath(relativeDocsDir);
|
|
3218
3259
|
if (normalizedDocsDir && normalizedDocsDir !== "." && normalizedDocsDir !== ".." && !normalizedDocsDir.startsWith("../")) {
|
|
3219
3260
|
args.push(`:(exclude)${normalizedDocsDir}/**`);
|
|
@@ -3339,6 +3380,7 @@ function getStepDefinitions(lang, workflow) {
|
|
|
3339
3380
|
{
|
|
3340
3381
|
type: "instruction",
|
|
3341
3382
|
category: "tasks_write",
|
|
3383
|
+
uiDetailKey: "context.actionDetail.tasksWriteCreate",
|
|
3342
3384
|
message: tr(lang, "messages", "tasksCreate")
|
|
3343
3385
|
}
|
|
3344
3386
|
];
|
|
@@ -3348,6 +3390,7 @@ function getStepDefinitions(lang, workflow) {
|
|
|
3348
3390
|
{
|
|
3349
3391
|
type: "instruction",
|
|
3350
3392
|
category: "tasks_write",
|
|
3393
|
+
uiDetailKey: "context.actionDetail.tasksWriteNeedAtLeastOne",
|
|
3351
3394
|
message: tr(lang, "messages", "tasksNeedAtLeastOne")
|
|
3352
3395
|
}
|
|
3353
3396
|
];
|
|
@@ -3357,6 +3400,7 @@ function getStepDefinitions(lang, workflow) {
|
|
|
3357
3400
|
{
|
|
3358
3401
|
type: "instruction",
|
|
3359
3402
|
category: "tasks_write",
|
|
3403
|
+
uiDetailKey: "context.actionDetail.tasksWriteImprove",
|
|
3360
3404
|
message: tr(lang, "messages", "tasksImprove")
|
|
3361
3405
|
}
|
|
3362
3406
|
];
|
|
@@ -3375,6 +3419,7 @@ function getStepDefinitions(lang, workflow) {
|
|
|
3375
3419
|
{
|
|
3376
3420
|
type: "instruction",
|
|
3377
3421
|
category: "tasks_write",
|
|
3422
|
+
uiDetailKey: "context.actionDetail.tasksWriteImprove",
|
|
3378
3423
|
message: tr(lang, "messages", "tasksImprove")
|
|
3379
3424
|
}
|
|
3380
3425
|
];
|
|
@@ -3443,6 +3488,7 @@ function getStepDefinitions(lang, workflow) {
|
|
|
3443
3488
|
type: "instruction",
|
|
3444
3489
|
category: "issue_create",
|
|
3445
3490
|
requiresUserCheck: true,
|
|
3491
|
+
uiDetailKey: "context.actionDetail.issueCreateAndWrite",
|
|
3446
3492
|
message: tr(lang, "messages", "issueCreateAndWrite", {
|
|
3447
3493
|
featureRef: f.id || f.folderName
|
|
3448
3494
|
})
|
|
@@ -3455,6 +3501,7 @@ function getStepDefinitions(lang, workflow) {
|
|
|
3455
3501
|
type: "instruction",
|
|
3456
3502
|
category: "issue_create",
|
|
3457
3503
|
requiresUserCheck: true,
|
|
3504
|
+
uiDetailKey: "context.actionDetail.issueCreateFromDoc",
|
|
3458
3505
|
message: tr(lang, "messages", "issueCreateFromDoc", {
|
|
3459
3506
|
featureRef: f.id || f.folderName
|
|
3460
3507
|
})
|
|
@@ -3466,6 +3513,7 @@ function getStepDefinitions(lang, workflow) {
|
|
|
3466
3513
|
type: "instruction",
|
|
3467
3514
|
category: "issue_create",
|
|
3468
3515
|
requiresUserCheck: true,
|
|
3516
|
+
uiDetailKey: "context.actionDetail.issueCreatePrepareFromDoc",
|
|
3469
3517
|
message: tr(lang, "messages", "issuePrepareFromDoc", {
|
|
3470
3518
|
featureRef: f.id || f.folderName
|
|
3471
3519
|
})
|
|
@@ -3610,6 +3658,7 @@ function getStepDefinitions(lang, workflow) {
|
|
|
3610
3658
|
type: "instruction",
|
|
3611
3659
|
category: "pr_metadata_migrate",
|
|
3612
3660
|
requiresUserCheck: true,
|
|
3661
|
+
uiDetailKey: "context.actionDetail.prMetadataMigratePrFields",
|
|
3613
3662
|
message: tr(lang, "messages", "prLegacyAsk")
|
|
3614
3663
|
});
|
|
3615
3664
|
}
|
|
@@ -3845,6 +3894,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
3845
3894
|
type: "instruction",
|
|
3846
3895
|
category: "pr_metadata_migrate",
|
|
3847
3896
|
requiresUserCheck: true,
|
|
3897
|
+
uiDetailKey: "context.actionDetail.prMetadataMigratePrePrReviewField",
|
|
3848
3898
|
message: tr(lang, "messages", "prePrReviewFieldMissing")
|
|
3849
3899
|
}
|
|
3850
3900
|
];
|
|
@@ -3882,6 +3932,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
3882
3932
|
type: "instruction",
|
|
3883
3933
|
category: "pr_metadata_migrate",
|
|
3884
3934
|
requiresUserCheck: true,
|
|
3935
|
+
uiDetailKey: "context.actionDetail.prMetadataMigratePrFields",
|
|
3885
3936
|
message: tr(lang, "messages", "prLegacyAsk")
|
|
3886
3937
|
}
|
|
3887
3938
|
];
|
|
@@ -3892,6 +3943,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
3892
3943
|
type: "instruction",
|
|
3893
3944
|
category: "pr_create",
|
|
3894
3945
|
requiresUserCheck: true,
|
|
3946
|
+
uiDetailKey: "context.actionDetail.prCreateRequiredSequence",
|
|
3895
3947
|
message: tr(lang, "messages", "prCreateRequiredSequence", {
|
|
3896
3948
|
featureRef: f.id || f.folderName
|
|
3897
3949
|
})
|
|
@@ -3904,6 +3956,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
3904
3956
|
type: "instruction",
|
|
3905
3957
|
category: "pr_create",
|
|
3906
3958
|
requiresUserCheck: true,
|
|
3959
|
+
uiDetailKey: "context.actionDetail.prCreateExecuteFromDoc",
|
|
3907
3960
|
message: tr(lang, "messages", "prCreateExecuteFromDoc", {
|
|
3908
3961
|
featureRef: f.id || f.folderName
|
|
3909
3962
|
})
|
|
@@ -3915,6 +3968,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
3915
3968
|
type: "instruction",
|
|
3916
3969
|
category: "pr_create",
|
|
3917
3970
|
requiresUserCheck: true,
|
|
3971
|
+
uiDetailKey: "context.actionDetail.prCreatePrepareFromDoc",
|
|
3918
3972
|
message: tr(lang, "messages", "prCreatePrepareFromDoc", {
|
|
3919
3973
|
featureRef: f.id || f.folderName
|
|
3920
3974
|
})
|
|
@@ -3938,6 +3992,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
3938
3992
|
type: "instruction",
|
|
3939
3993
|
category: "pr_status_update",
|
|
3940
3994
|
requiresUserCheck: true,
|
|
3995
|
+
uiDetailKey: "context.actionDetail.prStatusUpdateSetReview",
|
|
3941
3996
|
message: tr(lang, "messages", "prFillStatus")
|
|
3942
3997
|
}
|
|
3943
3998
|
];
|
|
@@ -3949,6 +4004,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
3949
4004
|
type: "instruction",
|
|
3950
4005
|
category: "pr_status_update",
|
|
3951
4006
|
requiresUserCheck: true,
|
|
4007
|
+
uiDetailKey: "context.actionDetail.prStatusUpdateSyncApproved",
|
|
3952
4008
|
message: tr(lang, "messages", "prReviewMergedSyncStatus")
|
|
3953
4009
|
}
|
|
3954
4010
|
];
|
|
@@ -3959,6 +4015,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
3959
4015
|
type: "instruction",
|
|
3960
4016
|
category: "code_review",
|
|
3961
4017
|
requiresUserCheck: true,
|
|
4018
|
+
uiDetailKey: "context.actionDetail.codeReviewNeedEvidenceField",
|
|
3962
4019
|
message: tr(lang, "messages", "prReviewEvidenceFieldMissing")
|
|
3963
4020
|
}
|
|
3964
4021
|
];
|
|
@@ -3969,6 +4026,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
3969
4026
|
type: "instruction",
|
|
3970
4027
|
category: "code_review",
|
|
3971
4028
|
requiresUserCheck: true,
|
|
4029
|
+
uiDetailKey: "context.actionDetail.codeReviewNeedEvidence",
|
|
3972
4030
|
message: tr(lang, "messages", "prReviewEvidenceMissing")
|
|
3973
4031
|
}
|
|
3974
4032
|
];
|
|
@@ -3979,6 +4037,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
3979
4037
|
type: "instruction",
|
|
3980
4038
|
category: "code_review",
|
|
3981
4039
|
requiresUserCheck: true,
|
|
4040
|
+
uiDetailKey: "context.actionDetail.codeReviewNeedDecisionField",
|
|
3982
4041
|
message: tr(lang, "messages", "prReviewDecisionFieldMissing")
|
|
3983
4042
|
}
|
|
3984
4043
|
];
|
|
@@ -3989,6 +4048,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
3989
4048
|
type: "instruction",
|
|
3990
4049
|
category: "code_review",
|
|
3991
4050
|
requiresUserCheck: true,
|
|
4051
|
+
uiDetailKey: "context.actionDetail.codeReviewNeedDecision",
|
|
3992
4052
|
message: tr(lang, "messages", "prReviewDecisionMissing")
|
|
3993
4053
|
}
|
|
3994
4054
|
];
|
|
@@ -4000,6 +4060,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
4000
4060
|
type: "instruction",
|
|
4001
4061
|
category: "code_review",
|
|
4002
4062
|
requiresUserCheck: true,
|
|
4063
|
+
uiDetailKey: "context.actionDetail.codeReviewResolve",
|
|
4003
4064
|
message: tr(lang, "messages", "prReviewResolve")
|
|
4004
4065
|
}
|
|
4005
4066
|
];
|
|
@@ -4008,6 +4069,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
4008
4069
|
type: "instruction",
|
|
4009
4070
|
category: "code_review",
|
|
4010
4071
|
requiresUserCheck: true,
|
|
4072
|
+
uiDetailKey: "context.actionDetail.codeReviewNeedProjectRoot",
|
|
4011
4073
|
message: tr(lang, "messages", "standaloneNeedsProjectRoot")
|
|
4012
4074
|
});
|
|
4013
4075
|
} else if ((f.git.projectBranchAhead || 0) > 0) {
|
|
@@ -4031,6 +4093,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
4031
4093
|
type: "instruction",
|
|
4032
4094
|
category: "code_review",
|
|
4033
4095
|
requiresUserCheck: true,
|
|
4096
|
+
uiDetailKey: "context.actionDetail.codeReviewRemoteBlocked",
|
|
4034
4097
|
message: tr(lang, "messages", "prReviewRemoteBlocked", {
|
|
4035
4098
|
reasons: reasons.join("; ")
|
|
4036
4099
|
})
|
|
@@ -4052,6 +4115,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
4052
4115
|
type: "instruction",
|
|
4053
4116
|
category: "code_review",
|
|
4054
4117
|
requiresUserCheck: true,
|
|
4118
|
+
uiDetailKey: "context.actionDetail.codeReviewMergeAfterOk",
|
|
4055
4119
|
message: tr(lang, "messages", "prReviewMerge", {
|
|
4056
4120
|
featureRef: f.id || f.folderName
|
|
4057
4121
|
})
|
|
@@ -4063,6 +4127,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
4063
4127
|
{
|
|
4064
4128
|
type: "instruction",
|
|
4065
4129
|
category: "code_review",
|
|
4130
|
+
uiDetailKey: "context.actionDetail.codeReviewRequestReview",
|
|
4066
4131
|
message: tr(lang, "messages", "prRequestReview")
|
|
4067
4132
|
}
|
|
4068
4133
|
];
|
|
@@ -4366,7 +4431,7 @@ function getGitTopLevel(cwd) {
|
|
|
4366
4431
|
}
|
|
4367
4432
|
function listGitWorktrees(cwd) {
|
|
4368
4433
|
const topLevel = getGitTopLevel(cwd) || cwd;
|
|
4369
|
-
const cacheKey =
|
|
4434
|
+
const cacheKey = path23.resolve(topLevel);
|
|
4370
4435
|
const cached = GIT_WORKTREE_CACHE.get(cacheKey);
|
|
4371
4436
|
if (cached) return cached;
|
|
4372
4437
|
try {
|
|
@@ -4525,19 +4590,6 @@ function parsePrePrReviewStatus(value) {
|
|
|
4525
4590
|
if (/^pending$/i.test(trimmed)) return "Pending";
|
|
4526
4591
|
return void 0;
|
|
4527
4592
|
}
|
|
4528
|
-
function parseReviewFindings(value) {
|
|
4529
|
-
if (!value) return void 0;
|
|
4530
|
-
const trimmed = value.trim();
|
|
4531
|
-
if (!trimmed || trimmed.includes("|")) return void 0;
|
|
4532
|
-
const majorMatch = trimmed.match(/\bmajor\s*[:=]\s*(\d+)\b/i);
|
|
4533
|
-
const minorMatch = trimmed.match(/\bminor\s*[:=]\s*(\d+)\b/i);
|
|
4534
|
-
if (!majorMatch || !minorMatch) return void 0;
|
|
4535
|
-
const major = Number(majorMatch[1]);
|
|
4536
|
-
const minor = Number(minorMatch[1]);
|
|
4537
|
-
if (!Number.isInteger(major) || !Number.isInteger(minor)) return void 0;
|
|
4538
|
-
if (major < 0 || minor < 0) return void 0;
|
|
4539
|
-
return { major, minor };
|
|
4540
|
-
}
|
|
4541
4593
|
function isPlaceholderReviewEvidence(value) {
|
|
4542
4594
|
if (!value) return true;
|
|
4543
4595
|
const trimmed = value.trim();
|
|
@@ -4592,15 +4644,15 @@ async function isPrePrEvidenceProvided(rawValue, policy, context) {
|
|
|
4592
4644
|
if (!evidencePath) return false;
|
|
4593
4645
|
if (/^https?:\/\//i.test(evidencePath)) return false;
|
|
4594
4646
|
const candidates = /* @__PURE__ */ new Set();
|
|
4595
|
-
if (
|
|
4596
|
-
candidates.add(
|
|
4647
|
+
if (path23.isAbsolute(evidencePath)) {
|
|
4648
|
+
candidates.add(path23.resolve(evidencePath));
|
|
4597
4649
|
} else {
|
|
4598
|
-
candidates.add(
|
|
4599
|
-
candidates.add(
|
|
4600
|
-
candidates.add(
|
|
4650
|
+
candidates.add(path23.resolve(context.featurePath, evidencePath));
|
|
4651
|
+
candidates.add(path23.resolve(context.docsDir, evidencePath));
|
|
4652
|
+
candidates.add(path23.resolve(path23.dirname(context.docsDir), evidencePath));
|
|
4601
4653
|
}
|
|
4602
4654
|
for (const candidate of candidates) {
|
|
4603
|
-
if (await
|
|
4655
|
+
if (await fs17.pathExists(candidate)) return true;
|
|
4604
4656
|
}
|
|
4605
4657
|
return false;
|
|
4606
4658
|
}
|
|
@@ -4621,13 +4673,13 @@ function parsePrLink(value) {
|
|
|
4621
4673
|
return trimmed;
|
|
4622
4674
|
}
|
|
4623
4675
|
function normalizeGitPath(value) {
|
|
4624
|
-
return value.split(
|
|
4676
|
+
return value.split(path23.sep).join("/");
|
|
4625
4677
|
}
|
|
4626
4678
|
function resolveProjectStatusPaths(projectGitCwd, docsDir) {
|
|
4627
|
-
const relativeDocsDir =
|
|
4679
|
+
const relativeDocsDir = path23.relative(projectGitCwd, docsDir);
|
|
4628
4680
|
if (!relativeDocsDir) return [];
|
|
4629
|
-
if (
|
|
4630
|
-
if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${
|
|
4681
|
+
if (path23.isAbsolute(relativeDocsDir)) return [];
|
|
4682
|
+
if (relativeDocsDir === ".." || relativeDocsDir.startsWith(`..${path23.sep}`)) {
|
|
4631
4683
|
return [];
|
|
4632
4684
|
}
|
|
4633
4685
|
const normalizedDocsDir = normalizeGitPath(relativeDocsDir).replace(/\/+$/, "");
|
|
@@ -4806,10 +4858,10 @@ async function resolveComponentStatusPaths(projectGitCwd, component, workflow) {
|
|
|
4806
4858
|
const normalizedCandidates = uniqueNormalizedPaths(
|
|
4807
4859
|
candidates.map((candidate) => {
|
|
4808
4860
|
if (!candidate) return "";
|
|
4809
|
-
if (!
|
|
4810
|
-
const relative =
|
|
4861
|
+
if (!path23.isAbsolute(candidate)) return candidate;
|
|
4862
|
+
const relative = path23.relative(projectGitCwd, candidate);
|
|
4811
4863
|
if (!relative) return "";
|
|
4812
|
-
if (relative === ".." || relative.startsWith(`..${
|
|
4864
|
+
if (relative === ".." || relative.startsWith(`..${path23.sep}`)) return "";
|
|
4813
4865
|
return relative;
|
|
4814
4866
|
}).filter(Boolean)
|
|
4815
4867
|
);
|
|
@@ -4822,7 +4874,7 @@ async function resolveComponentStatusPaths(projectGitCwd, component, workflow) {
|
|
|
4822
4874
|
if (cached) return [...cached];
|
|
4823
4875
|
const existing = [];
|
|
4824
4876
|
for (const candidate of normalizedCandidates) {
|
|
4825
|
-
if (await
|
|
4877
|
+
if (await fs17.pathExists(path23.join(projectGitCwd, candidate))) {
|
|
4826
4878
|
existing.push(candidate);
|
|
4827
4879
|
}
|
|
4828
4880
|
}
|
|
@@ -4896,9 +4948,6 @@ function isPrePrReviewSatisfied2(feature, policy) {
|
|
|
4896
4948
|
if (!feature.docs.prePrEvidenceFieldExists || !feature.prePrReview.evidenceProvided) {
|
|
4897
4949
|
return false;
|
|
4898
4950
|
}
|
|
4899
|
-
if (policy.findings === "required" && (!feature.docs.prePrFindingsFieldExists || !feature.prePrReview.findingsProvided)) {
|
|
4900
|
-
return false;
|
|
4901
|
-
}
|
|
4902
4951
|
if (!feature.docs.prePrDecisionFieldExists || !feature.prePrReview.decisionProvided) {
|
|
4903
4952
|
return false;
|
|
4904
4953
|
}
|
|
@@ -4911,20 +4960,20 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4911
4960
|
const lang = options.lang;
|
|
4912
4961
|
const workflowPolicy = resolveWorkflowPolicy(options.workflow);
|
|
4913
4962
|
const prePrReviewPolicy = resolvePrePrReviewPolicy(options.workflow);
|
|
4914
|
-
const folderName =
|
|
4963
|
+
const folderName = path23.basename(featurePath);
|
|
4915
4964
|
const match = folderName.match(/^(F\d+)-(.+)$/);
|
|
4916
4965
|
const id = match?.[1];
|
|
4917
4966
|
const slug = match?.[2] || folderName;
|
|
4918
|
-
const specPath =
|
|
4919
|
-
const planPath =
|
|
4920
|
-
const tasksPath =
|
|
4921
|
-
const issueDocPath =
|
|
4922
|
-
const prDocPath =
|
|
4967
|
+
const specPath = path23.join(featurePath, "spec.md");
|
|
4968
|
+
const planPath = path23.join(featurePath, "plan.md");
|
|
4969
|
+
const tasksPath = path23.join(featurePath, "tasks.md");
|
|
4970
|
+
const issueDocPath = path23.join(featurePath, "issue.md");
|
|
4971
|
+
const prDocPath = path23.join(featurePath, "pr.md");
|
|
4923
4972
|
let specStatus;
|
|
4924
4973
|
let issueNumber;
|
|
4925
|
-
const specExists = await
|
|
4974
|
+
const specExists = await fs17.pathExists(specPath);
|
|
4926
4975
|
if (specExists) {
|
|
4927
|
-
const content = await
|
|
4976
|
+
const content = await fs17.readFile(specPath, "utf-8");
|
|
4928
4977
|
const statusValue = extractFirstSpecValue(content, ["\uC0C1\uD0DC", "Status"]);
|
|
4929
4978
|
specStatus = parseDocStatus(statusValue);
|
|
4930
4979
|
const issueValue = extractFirstSpecValue(content, ["\uC774\uC288 \uBC88\uD638", "Issue Number", "Issue"]);
|
|
@@ -4955,13 +5004,13 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4955
5004
|
}
|
|
4956
5005
|
}
|
|
4957
5006
|
let planStatus;
|
|
4958
|
-
const planExists = await
|
|
5007
|
+
const planExists = await fs17.pathExists(planPath);
|
|
4959
5008
|
if (planExists) {
|
|
4960
|
-
const content = await
|
|
5009
|
+
const content = await fs17.readFile(planPath, "utf-8");
|
|
4961
5010
|
const statusValue = extractFirstSpecValue(content, ["\uC0C1\uD0DC", "Status"]);
|
|
4962
5011
|
planStatus = parseDocStatus(statusValue);
|
|
4963
5012
|
}
|
|
4964
|
-
const tasksExists = await
|
|
5013
|
+
const tasksExists = await fs17.pathExists(tasksPath);
|
|
4965
5014
|
const tasksSummary = { total: 0, todo: 0, doing: 0, done: 0 };
|
|
4966
5015
|
let activeTask;
|
|
4967
5016
|
let lastDoneTask;
|
|
@@ -4970,14 +5019,11 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4970
5019
|
let tasksDocStatusFieldExists = false;
|
|
4971
5020
|
let completionChecklist;
|
|
4972
5021
|
let prePrReviewStatus;
|
|
4973
|
-
let prePrFindings;
|
|
4974
|
-
let prePrFindingsProvided = false;
|
|
4975
5022
|
let prePrEvidence;
|
|
4976
5023
|
let prePrEvidenceProvided = false;
|
|
4977
5024
|
let prePrDecision;
|
|
4978
5025
|
let prePrDecisionOutcome;
|
|
4979
5026
|
let prePrDecisionProvided = false;
|
|
4980
|
-
let prReviewFindings;
|
|
4981
5027
|
let prReviewEvidence;
|
|
4982
5028
|
let prReviewEvidenceProvided = false;
|
|
4983
5029
|
let prReviewDecision;
|
|
@@ -4995,14 +5041,12 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
4995
5041
|
let prDocPrFieldExists = false;
|
|
4996
5042
|
let prDocReviewStatusFieldExists = false;
|
|
4997
5043
|
let prePrReviewFieldExists = false;
|
|
4998
|
-
let prePrFindingsFieldExists = false;
|
|
4999
5044
|
let prePrEvidenceFieldExists = false;
|
|
5000
5045
|
let prePrDecisionFieldExists = false;
|
|
5001
|
-
let prReviewFindingsFieldExists = false;
|
|
5002
5046
|
let prReviewEvidenceFieldExists = false;
|
|
5003
5047
|
let prReviewDecisionFieldExists = false;
|
|
5004
5048
|
if (tasksExists) {
|
|
5005
|
-
const content = await
|
|
5049
|
+
const content = await fs17.readFile(tasksPath, "utf-8");
|
|
5006
5050
|
const {
|
|
5007
5051
|
summary,
|
|
5008
5052
|
activeTask: active,
|
|
@@ -5041,16 +5085,6 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
5041
5085
|
"Pre-PR Review"
|
|
5042
5086
|
]);
|
|
5043
5087
|
prePrReviewStatus = parsePrePrReviewStatus(prePrReviewValue);
|
|
5044
|
-
const prePrFindingsValue = extractFirstSpecValue(content, [
|
|
5045
|
-
"PR \uC804 \uB9AC\uBDF0 Findings",
|
|
5046
|
-
"Pre-PR Findings"
|
|
5047
|
-
]);
|
|
5048
|
-
prePrFindingsFieldExists = hasAnySpecKey(content, [
|
|
5049
|
-
"PR \uC804 \uB9AC\uBDF0 Findings",
|
|
5050
|
-
"Pre-PR Findings"
|
|
5051
|
-
]);
|
|
5052
|
-
prePrFindings = parseReviewFindings(prePrFindingsValue);
|
|
5053
|
-
prePrFindingsProvided = !!prePrFindings;
|
|
5054
5088
|
const prePrEvidenceValue = extractFirstSpecValue(content, [
|
|
5055
5089
|
"PR \uC804 \uB9AC\uBDF0 Evidence",
|
|
5056
5090
|
"Pre-PR Evidence"
|
|
@@ -5076,15 +5110,6 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
5076
5110
|
prePrDecision = prePrDecisionValue?.trim();
|
|
5077
5111
|
prePrDecisionOutcome = parsePrePrDecisionOutcome(prePrDecisionValue);
|
|
5078
5112
|
prePrDecisionProvided = !isPlaceholderReviewEvidence(prePrDecisionValue) && hasStructuredReviewDecision(prePrDecisionValue) && !!prePrDecisionOutcome && prePrReviewPolicy.decisionEnum.includes(prePrDecisionOutcome);
|
|
5079
|
-
const prReviewFindingsValue = extractFirstSpecValue(content, [
|
|
5080
|
-
"PR \uB9AC\uBDF0 Findings",
|
|
5081
|
-
"PR Review Findings"
|
|
5082
|
-
]);
|
|
5083
|
-
prReviewFindingsFieldExists = hasAnySpecKey(content, [
|
|
5084
|
-
"PR \uB9AC\uBDF0 Findings",
|
|
5085
|
-
"PR Review Findings"
|
|
5086
|
-
]);
|
|
5087
|
-
prReviewFindings = parseReviewFindings(prReviewFindingsValue);
|
|
5088
5113
|
const prReviewEvidenceValue = extractFirstSpecValue(content, [
|
|
5089
5114
|
"PR \uB9AC\uBDF0 Evidence",
|
|
5090
5115
|
"PR Review Evidence"
|
|
@@ -5127,9 +5152,9 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
5127
5152
|
}
|
|
5128
5153
|
}
|
|
5129
5154
|
}
|
|
5130
|
-
const issueDocExists = await
|
|
5155
|
+
const issueDocExists = await fs17.pathExists(issueDocPath);
|
|
5131
5156
|
if (issueDocExists) {
|
|
5132
|
-
const content = await
|
|
5157
|
+
const content = await fs17.readFile(issueDocPath, "utf-8");
|
|
5133
5158
|
const issueDocStatusValue = extractFirstSpecValue(content, ["\uC0C1\uD0DC", "Status"]);
|
|
5134
5159
|
issueDocStatusFieldExists = hasAnySpecKey(content, ["\uC0C1\uD0DC", "Status"]);
|
|
5135
5160
|
issueDocStatus = parseWorkflowDocStatus(issueDocStatusValue);
|
|
@@ -5139,9 +5164,9 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
5139
5164
|
"Issue"
|
|
5140
5165
|
]);
|
|
5141
5166
|
}
|
|
5142
|
-
const prDocExists = await
|
|
5167
|
+
const prDocExists = await fs17.pathExists(prDocPath);
|
|
5143
5168
|
if (prDocExists) {
|
|
5144
|
-
const content = await
|
|
5169
|
+
const content = await fs17.readFile(prDocPath, "utf-8");
|
|
5145
5170
|
const prDocStatusValue = extractFirstSpecValue(content, ["\uC0C1\uD0DC", "Status"]);
|
|
5146
5171
|
prDocStatusFieldExists = hasAnySpecKey(content, ["\uC0C1\uD0DC", "Status"]);
|
|
5147
5172
|
prDocStatus = parseWorkflowDocStatus(prDocStatusValue);
|
|
@@ -5161,7 +5186,7 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
5161
5186
|
slug,
|
|
5162
5187
|
folderName
|
|
5163
5188
|
);
|
|
5164
|
-
const relativeFeaturePathFromDocs =
|
|
5189
|
+
const relativeFeaturePathFromDocs = path23.relative(context.docsDir, featurePath);
|
|
5165
5190
|
const normalizedFeaturePathFromDocs = normalizeGitPath(relativeFeaturePathFromDocs);
|
|
5166
5191
|
const docsPathIgnored = typeof context.docsPathIgnored === "boolean" ? context.docsPathIgnored : isGitPathIgnored(context.docsGitCwd, normalizedFeaturePathFromDocs);
|
|
5167
5192
|
let docsHasUncommittedChanges = typeof context.docsHasUncommittedChanges === "boolean" ? context.docsHasUncommittedChanges : false;
|
|
@@ -5250,9 +5275,6 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
5250
5275
|
if (tasksExists && prePrReviewPolicy.enabled && !prePrReviewFieldExists) {
|
|
5251
5276
|
warnings.push(tr(lang, "warnings", "legacyTasksPrePrReviewField"));
|
|
5252
5277
|
}
|
|
5253
|
-
if (tasksExists && prePrReviewPolicy.enabled && prePrReviewPolicy.findings === "required" && !prePrFindingsFieldExists) {
|
|
5254
|
-
warnings.push(tr(lang, "warnings", "legacyTasksPrePrFindingsField"));
|
|
5255
|
-
}
|
|
5256
5278
|
if (tasksExists && prePrReviewPolicy.enabled && !prePrEvidenceFieldExists) {
|
|
5257
5279
|
warnings.push(tr(lang, "warnings", "legacyTasksPrePrEvidenceField"));
|
|
5258
5280
|
}
|
|
@@ -5297,13 +5319,11 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
5297
5319
|
{
|
|
5298
5320
|
docs: {
|
|
5299
5321
|
prePrReviewFieldExists,
|
|
5300
|
-
prePrFindingsFieldExists,
|
|
5301
5322
|
prePrEvidenceFieldExists,
|
|
5302
5323
|
prePrDecisionFieldExists
|
|
5303
5324
|
},
|
|
5304
5325
|
prePrReview: {
|
|
5305
5326
|
status: prePrReviewStatus,
|
|
5306
|
-
findingsProvided: prePrFindingsProvided,
|
|
5307
5327
|
evidenceProvided: prePrEvidenceProvided,
|
|
5308
5328
|
decisionOutcome: prePrDecisionOutcome,
|
|
5309
5329
|
decisionProvided: prePrDecisionProvided
|
|
@@ -5346,8 +5366,6 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
5346
5366
|
warnings.push(tr(lang, "warnings", "workflowPrePrReviewMissing"));
|
|
5347
5367
|
} else if (prePrReviewStatus !== "Done") {
|
|
5348
5368
|
warnings.push(tr(lang, "warnings", "workflowPrePrReviewNotDone"));
|
|
5349
|
-
} else if (prePrReviewPolicy.findings === "required" && (!prePrFindingsFieldExists || !prePrFindingsProvided)) {
|
|
5350
|
-
warnings.push(tr(lang, "warnings", "workflowPrePrFindingsMissing"));
|
|
5351
5369
|
} else if (!prePrEvidenceFieldExists || !prePrEvidenceProvided) {
|
|
5352
5370
|
warnings.push(tr(lang, "warnings", "workflowPrePrEvidenceMissing"));
|
|
5353
5371
|
} else if (!prePrDecisionFieldExists || !prePrDecisionProvided) {
|
|
@@ -5382,8 +5400,6 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
5382
5400
|
completionChecklist,
|
|
5383
5401
|
prePrReview: {
|
|
5384
5402
|
status: prePrReviewStatus,
|
|
5385
|
-
findings: prePrFindings,
|
|
5386
|
-
findingsProvided: prePrFindingsProvided,
|
|
5387
5403
|
evidence: prePrEvidence,
|
|
5388
5404
|
evidenceProvided: prePrEvidenceProvided,
|
|
5389
5405
|
decision: prePrDecision,
|
|
@@ -5391,7 +5407,6 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
5391
5407
|
decisionProvided: prePrDecisionProvided
|
|
5392
5408
|
},
|
|
5393
5409
|
prReview: {
|
|
5394
|
-
findings: prReviewFindings,
|
|
5395
5410
|
evidence: prReviewEvidence,
|
|
5396
5411
|
evidenceProvided: prReviewEvidenceProvided,
|
|
5397
5412
|
decision: prReviewDecision,
|
|
@@ -5431,10 +5446,8 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
5431
5446
|
prFieldExists,
|
|
5432
5447
|
prStatusFieldExists,
|
|
5433
5448
|
prePrReviewFieldExists,
|
|
5434
|
-
prePrFindingsFieldExists,
|
|
5435
5449
|
prePrEvidenceFieldExists,
|
|
5436
5450
|
prePrDecisionFieldExists,
|
|
5437
|
-
prReviewFindingsFieldExists,
|
|
5438
5451
|
prReviewEvidenceFieldExists,
|
|
5439
5452
|
prReviewDecisionFieldExists
|
|
5440
5453
|
}
|
|
@@ -5450,7 +5463,7 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
5450
5463
|
async function listFeatureDirs(rootDir) {
|
|
5451
5464
|
const dirs = await listSubdirectories(rootDir);
|
|
5452
5465
|
return dirs.filter(
|
|
5453
|
-
(value) =>
|
|
5466
|
+
(value) => path23.basename(value).trim().toLowerCase() !== "feature-base"
|
|
5454
5467
|
);
|
|
5455
5468
|
}
|
|
5456
5469
|
function normalizeRelPath(value) {
|
|
@@ -5570,21 +5583,21 @@ async function scanFeatures(config) {
|
|
|
5570
5583
|
const allFeatureDirs = [];
|
|
5571
5584
|
const componentFeatureDirs = /* @__PURE__ */ new Map();
|
|
5572
5585
|
if (config.projectType === "single") {
|
|
5573
|
-
const featureDirs = await listFeatureDirs(
|
|
5586
|
+
const featureDirs = await listFeatureDirs(path23.join(config.docsDir, "features"));
|
|
5574
5587
|
componentFeatureDirs.set("single", featureDirs);
|
|
5575
5588
|
allFeatureDirs.push(...featureDirs);
|
|
5576
5589
|
} else {
|
|
5577
5590
|
const components = resolveProjectComponents(config.projectType, config.components);
|
|
5578
5591
|
for (const component of components) {
|
|
5579
5592
|
const componentDirs = await listFeatureDirs(
|
|
5580
|
-
|
|
5593
|
+
path23.join(config.docsDir, "features", component)
|
|
5581
5594
|
);
|
|
5582
5595
|
componentFeatureDirs.set(component, componentDirs);
|
|
5583
5596
|
allFeatureDirs.push(...componentDirs);
|
|
5584
5597
|
}
|
|
5585
5598
|
}
|
|
5586
5599
|
const relativeFeaturePaths = allFeatureDirs.map(
|
|
5587
|
-
(dir) => normalizeRelPath(
|
|
5600
|
+
(dir) => normalizeRelPath(path23.relative(config.docsDir, dir))
|
|
5588
5601
|
);
|
|
5589
5602
|
const docsGitMeta = buildDocsFeatureGitMeta(config.docsDir, relativeFeaturePaths);
|
|
5590
5603
|
const parseTargets = config.projectType === "single" ? [{ type: "single", dirs: componentFeatureDirs.get("single") || [] }] : resolveProjectComponents(config.projectType, config.components).map((component) => ({
|
|
@@ -5595,7 +5608,7 @@ async function scanFeatures(config) {
|
|
|
5595
5608
|
const parsed = await Promise.all(
|
|
5596
5609
|
target.dirs.map(async (dir) => {
|
|
5597
5610
|
const relativeFeaturePathFromDocs = normalizeRelPath(
|
|
5598
|
-
|
|
5611
|
+
path23.relative(config.docsDir, dir)
|
|
5599
5612
|
);
|
|
5600
5613
|
const docsMeta = docsGitMeta.get(relativeFeaturePathFromDocs);
|
|
5601
5614
|
return parseFeature(
|
|
@@ -5665,13 +5678,13 @@ async function runStatus(options) {
|
|
|
5665
5678
|
);
|
|
5666
5679
|
}
|
|
5667
5680
|
const { docsDir, projectType, projectName, lang } = config;
|
|
5668
|
-
const featuresDir =
|
|
5681
|
+
const featuresDir = path23.join(docsDir, "features");
|
|
5669
5682
|
const scan = await scanFeatures(config);
|
|
5670
5683
|
const features = [];
|
|
5671
5684
|
const idMap = /* @__PURE__ */ new Map();
|
|
5672
5685
|
for (const f of scan.features) {
|
|
5673
5686
|
const id = f.id || "UNKNOWN";
|
|
5674
|
-
const relPath =
|
|
5687
|
+
const relPath = path23.relative(docsDir, f.path);
|
|
5675
5688
|
if (!idMap.has(id)) idMap.set(id, []);
|
|
5676
5689
|
idMap.get(id).push(relPath);
|
|
5677
5690
|
if (!f.docs.specExists || !f.docs.tasksExists) continue;
|
|
@@ -5752,7 +5765,7 @@ async function runStatus(options) {
|
|
|
5752
5765
|
}
|
|
5753
5766
|
console.log();
|
|
5754
5767
|
if (options.write) {
|
|
5755
|
-
const outputPath =
|
|
5768
|
+
const outputPath = path23.join(featuresDir, "status.md");
|
|
5756
5769
|
const date = getLocalDateString();
|
|
5757
5770
|
const content = [
|
|
5758
5771
|
"# Feature Status",
|
|
@@ -5767,7 +5780,7 @@ async function runStatus(options) {
|
|
|
5767
5780
|
),
|
|
5768
5781
|
""
|
|
5769
5782
|
].join("\n");
|
|
5770
|
-
await
|
|
5783
|
+
await fs17.writeFile(outputPath, content, "utf-8");
|
|
5771
5784
|
console.log(
|
|
5772
5785
|
chalk6.green(
|
|
5773
5786
|
tr(lang, "cli", "status.wrote", { path: outputPath })
|
|
@@ -5780,9 +5793,9 @@ function escapeRegExp2(value) {
|
|
|
5780
5793
|
}
|
|
5781
5794
|
async function getFeatureNameFromSpec(featureDir, fallbackSlug, fallbackFolderName) {
|
|
5782
5795
|
try {
|
|
5783
|
-
const specPath =
|
|
5784
|
-
if (!await
|
|
5785
|
-
const content = await
|
|
5796
|
+
const specPath = path23.join(featureDir, "spec.md");
|
|
5797
|
+
if (!await fs17.pathExists(specPath)) return fallbackSlug;
|
|
5798
|
+
const content = await fs17.readFile(specPath, "utf-8");
|
|
5786
5799
|
const keys = ["\uAE30\uB2A5\uBA85", "Feature Name"];
|
|
5787
5800
|
for (const key of keys) {
|
|
5788
5801
|
const regex = new RegExp(
|
|
@@ -5863,8 +5876,8 @@ async function runUpdate(options) {
|
|
|
5863
5876
|
console.log(chalk6.blue(tr(lang, "cli", "update.updatingAgents")));
|
|
5864
5877
|
}
|
|
5865
5878
|
if (agentsMode === "all") {
|
|
5866
|
-
const commonAgentsBase =
|
|
5867
|
-
const targetAgentsBase =
|
|
5879
|
+
const commonAgentsBase = path23.join(templatesDir, lang, "common", "agents");
|
|
5880
|
+
const targetAgentsBase = path23.join(docsDir, "agents");
|
|
5868
5881
|
const commonAgents = commonAgentsBase;
|
|
5869
5882
|
const targetAgents = targetAgentsBase;
|
|
5870
5883
|
const featurePath = projectType === "multi" ? "docs/features/{component}" : "docs/features";
|
|
@@ -5873,7 +5886,7 @@ async function runUpdate(options) {
|
|
|
5873
5886
|
"{{projectName}}": projectName,
|
|
5874
5887
|
"{{featurePath}}": featurePath
|
|
5875
5888
|
};
|
|
5876
|
-
if (await
|
|
5889
|
+
if (await fs17.pathExists(commonAgents)) {
|
|
5877
5890
|
const count = await updateFolder(
|
|
5878
5891
|
commonAgents,
|
|
5879
5892
|
targetAgents,
|
|
@@ -5958,11 +5971,11 @@ function normalizeDecisionEnumList2(raw) {
|
|
|
5958
5971
|
return [...deduped];
|
|
5959
5972
|
}
|
|
5960
5973
|
async function backfillMissingConfigDefaults(docsDir) {
|
|
5961
|
-
const configPath =
|
|
5962
|
-
if (!await
|
|
5974
|
+
const configPath = path23.join(docsDir, ".lee-spec-kit.json");
|
|
5975
|
+
if (!await fs17.pathExists(configPath)) {
|
|
5963
5976
|
return { changed: false, changedPaths: [] };
|
|
5964
5977
|
}
|
|
5965
|
-
const raw = await
|
|
5978
|
+
const raw = await fs17.readJson(configPath);
|
|
5966
5979
|
if (!isPlainObject(raw)) {
|
|
5967
5980
|
return { changed: false, changedPaths: [] };
|
|
5968
5981
|
}
|
|
@@ -6015,14 +6028,8 @@ async function backfillMissingConfigDefaults(docsDir) {
|
|
|
6015
6028
|
prePrReview.evidenceMode = "path_required";
|
|
6016
6029
|
changedPaths.push("workflow.prePrReview.evidenceMode");
|
|
6017
6030
|
}
|
|
6018
|
-
|
|
6019
|
-
prePrReview
|
|
6020
|
-
"findings",
|
|
6021
|
-
"required",
|
|
6022
|
-
"workflow.prePrReview.findings"
|
|
6023
|
-
);
|
|
6024
|
-
if (prePrReview.findings !== void 0 && prePrReview.findings !== "required" && prePrReview.findings !== "optional") {
|
|
6025
|
-
prePrReview.findings = "required";
|
|
6031
|
+
if ("findings" in prePrReview) {
|
|
6032
|
+
delete prePrReview.findings;
|
|
6026
6033
|
changedPaths.push("workflow.prePrReview.findings");
|
|
6027
6034
|
}
|
|
6028
6035
|
if (prePrReview.decisionEnum === void 0) {
|
|
@@ -6058,30 +6065,30 @@ async function backfillMissingConfigDefaults(docsDir) {
|
|
|
6058
6065
|
if (changedPaths.length === 0) {
|
|
6059
6066
|
return { changed: false, changedPaths: [] };
|
|
6060
6067
|
}
|
|
6061
|
-
await
|
|
6068
|
+
await fs17.writeJson(configPath, raw, { spaces: 2 });
|
|
6062
6069
|
return { changed: true, changedPaths };
|
|
6063
6070
|
}
|
|
6064
6071
|
async function updateFolder(sourceDir, targetDir, force, replacements, lang = DEFAULT_LANG, options = {}) {
|
|
6065
6072
|
const protectedFiles = options.protectedFiles ?? /* @__PURE__ */ new Set(["custom.md", "constitution.md"]);
|
|
6066
6073
|
const skipDirectories = options.skipDirectories ?? /* @__PURE__ */ new Set();
|
|
6067
|
-
await
|
|
6068
|
-
const files = await
|
|
6074
|
+
await fs17.ensureDir(targetDir);
|
|
6075
|
+
const files = await fs17.readdir(sourceDir);
|
|
6069
6076
|
let updatedCount = 0;
|
|
6070
6077
|
for (const file of files) {
|
|
6071
|
-
const sourcePath =
|
|
6072
|
-
const targetPath =
|
|
6073
|
-
const stat = await
|
|
6078
|
+
const sourcePath = path23.join(sourceDir, file);
|
|
6079
|
+
const targetPath = path23.join(targetDir, file);
|
|
6080
|
+
const stat = await fs17.stat(sourcePath);
|
|
6074
6081
|
if (stat.isFile()) {
|
|
6075
6082
|
if (protectedFiles.has(file)) {
|
|
6076
6083
|
continue;
|
|
6077
6084
|
}
|
|
6078
|
-
let sourceContent = await
|
|
6085
|
+
let sourceContent = await fs17.readFile(sourcePath, "utf-8");
|
|
6079
6086
|
if (replacements) {
|
|
6080
6087
|
sourceContent = applyReplacements(sourceContent, replacements);
|
|
6081
6088
|
}
|
|
6082
6089
|
let shouldUpdate = true;
|
|
6083
|
-
if (await
|
|
6084
|
-
const targetContent = await
|
|
6090
|
+
if (await fs17.pathExists(targetPath)) {
|
|
6091
|
+
const targetContent = await fs17.readFile(targetPath, "utf-8");
|
|
6085
6092
|
if (sourceContent === targetContent) {
|
|
6086
6093
|
continue;
|
|
6087
6094
|
}
|
|
@@ -6095,7 +6102,7 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
|
|
|
6095
6102
|
}
|
|
6096
6103
|
}
|
|
6097
6104
|
if (shouldUpdate) {
|
|
6098
|
-
await
|
|
6105
|
+
await fs17.writeFile(targetPath, sourceContent);
|
|
6099
6106
|
console.log(
|
|
6100
6107
|
chalk6.gray(` \u{1F4C4} ${tr(lang, "cli", "update.fileUpdated", { file })}`)
|
|
6101
6108
|
);
|
|
@@ -6152,7 +6159,7 @@ function extractPorcelainPaths(line) {
|
|
|
6152
6159
|
function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
|
|
6153
6160
|
const top = getGitTopLevel2(docsDir);
|
|
6154
6161
|
if (!top) return null;
|
|
6155
|
-
const rel =
|
|
6162
|
+
const rel = path23.relative(top, docsDir) || ".";
|
|
6156
6163
|
try {
|
|
6157
6164
|
const output = execFileSync("git", ["status", "--porcelain=v1", "--", rel], {
|
|
6158
6165
|
cwd: top,
|
|
@@ -6164,7 +6171,7 @@ function getDocsPorcelainStatus(docsDir, ignoredAbsPaths = []) {
|
|
|
6164
6171
|
}
|
|
6165
6172
|
const ignoredRelPaths = new Set(
|
|
6166
6173
|
ignoredAbsPaths.map(
|
|
6167
|
-
(absPath) => normalizeGitPath2(
|
|
6174
|
+
(absPath) => normalizeGitPath2(path23.relative(top, absPath) || ".")
|
|
6168
6175
|
)
|
|
6169
6176
|
);
|
|
6170
6177
|
const filtered = output.split("\n").filter((line) => {
|
|
@@ -6222,7 +6229,7 @@ ${tr(lang2, "cli", "common.canceled")}`));
|
|
|
6222
6229
|
}
|
|
6223
6230
|
async function runConfig(options) {
|
|
6224
6231
|
const cwd = process.cwd();
|
|
6225
|
-
const targetCwd = options.dir ?
|
|
6232
|
+
const targetCwd = options.dir ? path23.resolve(cwd, options.dir) : cwd;
|
|
6226
6233
|
const config = await getConfig(targetCwd);
|
|
6227
6234
|
if (!config) {
|
|
6228
6235
|
throw createCliError(
|
|
@@ -6230,7 +6237,7 @@ async function runConfig(options) {
|
|
|
6230
6237
|
tr(DEFAULT_LANG, "cli", "common.configNotFound")
|
|
6231
6238
|
);
|
|
6232
6239
|
}
|
|
6233
|
-
const configPath =
|
|
6240
|
+
const configPath = path23.join(config.docsDir, ".lee-spec-kit.json");
|
|
6234
6241
|
if (!options.projectRoot) {
|
|
6235
6242
|
console.log();
|
|
6236
6243
|
console.log(chalk6.blue(tr(config.lang, "cli", "config.currentTitle")));
|
|
@@ -6241,7 +6248,7 @@ async function runConfig(options) {
|
|
|
6241
6248
|
)
|
|
6242
6249
|
);
|
|
6243
6250
|
console.log();
|
|
6244
|
-
const configFile = await
|
|
6251
|
+
const configFile = await fs17.readJson(configPath);
|
|
6245
6252
|
console.log(JSON.stringify(configFile, null, 2));
|
|
6246
6253
|
console.log();
|
|
6247
6254
|
return;
|
|
@@ -6250,7 +6257,7 @@ async function runConfig(options) {
|
|
|
6250
6257
|
await withFileLock(
|
|
6251
6258
|
getDocsLockPath(config.docsDir),
|
|
6252
6259
|
async () => {
|
|
6253
|
-
const configFile = await
|
|
6260
|
+
const configFile = await fs17.readJson(configPath);
|
|
6254
6261
|
if (configFile.docsRepo !== "standalone") {
|
|
6255
6262
|
console.log(
|
|
6256
6263
|
chalk6.yellow(tr(config.lang, "cli", "config.projectRootStandaloneOnly"))
|
|
@@ -6326,7 +6333,7 @@ async function runConfig(options) {
|
|
|
6326
6333
|
)
|
|
6327
6334
|
);
|
|
6328
6335
|
}
|
|
6329
|
-
await
|
|
6336
|
+
await fs17.writeJson(configPath, configFile, { spaces: 2 });
|
|
6330
6337
|
console.log();
|
|
6331
6338
|
},
|
|
6332
6339
|
{ owner: "config" }
|
|
@@ -6407,6 +6414,14 @@ function annotateActions(actions) {
|
|
|
6407
6414
|
return actions.map((action) => annotateActionOperationType(action));
|
|
6408
6415
|
}
|
|
6409
6416
|
function getActionSummary(action, lang) {
|
|
6417
|
+
if (action.uiSummaryKey) {
|
|
6418
|
+
const localized = tr(lang, "cli", action.uiSummaryKey);
|
|
6419
|
+
if (localized !== `cli.${action.uiSummaryKey}`) return localized;
|
|
6420
|
+
}
|
|
6421
|
+
if (action.uiDetailKey) {
|
|
6422
|
+
const localized = tr(lang, "cli", action.uiDetailKey);
|
|
6423
|
+
if (localized !== `cli.${action.uiDetailKey}`) return localized;
|
|
6424
|
+
}
|
|
6410
6425
|
const detailKey = action.category ? ACTION_DETAIL_KEY_BY_CATEGORY[action.category] : void 0;
|
|
6411
6426
|
if (detailKey) {
|
|
6412
6427
|
const localized = tr(lang, "cli", detailKey);
|
|
@@ -6428,6 +6443,10 @@ function toOneLine(text) {
|
|
|
6428
6443
|
return `${normalized.slice(0, 157).trimEnd()}...`;
|
|
6429
6444
|
}
|
|
6430
6445
|
function buildActionDetail(action, lang) {
|
|
6446
|
+
if (action.uiDetailKey) {
|
|
6447
|
+
const localized = tr(lang, "cli", action.uiDetailKey);
|
|
6448
|
+
if (localized !== `cli.${action.uiDetailKey}`) return localized;
|
|
6449
|
+
}
|
|
6431
6450
|
const formatBranchCreateDetail = (command) => {
|
|
6432
6451
|
const worktreeMatch = command.match(/\.worktrees\/([A-Za-z0-9._-]+)/);
|
|
6433
6452
|
const branchMatch = command.match(/\bfeat\/([A-Za-z0-9._-]+)/);
|
|
@@ -6483,6 +6502,11 @@ function buildActionDetail(action, lang) {
|
|
|
6483
6502
|
});
|
|
6484
6503
|
}
|
|
6485
6504
|
}
|
|
6505
|
+
if (action.category === "pre_pr_review") {
|
|
6506
|
+
return tr(lang, "cli", "context.commandDetail.prePrReviewRun", {
|
|
6507
|
+
scope: action.scope
|
|
6508
|
+
});
|
|
6509
|
+
}
|
|
6486
6510
|
if (action.category === "worktree_cleanup") {
|
|
6487
6511
|
return `(${action.scope}) ${tr(lang, "cli", "context.actionDetail.worktreeCleanup")}`;
|
|
6488
6512
|
}
|
|
@@ -6531,7 +6555,9 @@ function buildActionSnapshot(actionOptions) {
|
|
|
6531
6555
|
cmd: action.cmd,
|
|
6532
6556
|
category: action.category,
|
|
6533
6557
|
operationType: action.operationType,
|
|
6534
|
-
requiresUserCheck: !!action.requiresUserCheck
|
|
6558
|
+
requiresUserCheck: !!action.requiresUserCheck,
|
|
6559
|
+
uiSummaryKey: action.uiSummaryKey,
|
|
6560
|
+
uiDetailKey: action.uiDetailKey
|
|
6535
6561
|
};
|
|
6536
6562
|
}
|
|
6537
6563
|
return {
|
|
@@ -6540,7 +6566,9 @@ function buildActionSnapshot(actionOptions) {
|
|
|
6540
6566
|
message: action.message,
|
|
6541
6567
|
category: action.category,
|
|
6542
6568
|
operationType: action.operationType,
|
|
6543
|
-
requiresUserCheck: !!action.requiresUserCheck
|
|
6569
|
+
requiresUserCheck: !!action.requiresUserCheck,
|
|
6570
|
+
uiSummaryKey: action.uiSummaryKey,
|
|
6571
|
+
uiDetailKey: action.uiDetailKey
|
|
6544
6572
|
};
|
|
6545
6573
|
});
|
|
6546
6574
|
}
|
|
@@ -6726,13 +6754,13 @@ function getApprovalSessionId() {
|
|
|
6726
6754
|
function getApprovalTicketPaths(config) {
|
|
6727
6755
|
return {
|
|
6728
6756
|
runtimePath: getApprovalTicketStorePath(config.docsDir),
|
|
6729
|
-
legacyPath:
|
|
6757
|
+
legacyPath: path23.join(config.docsDir, LEGACY_APPROVAL_TICKET_FILENAME)
|
|
6730
6758
|
};
|
|
6731
6759
|
}
|
|
6732
6760
|
async function loadApprovalTicketStore(storePath) {
|
|
6733
|
-
if (!await
|
|
6761
|
+
if (!await fs17.pathExists(storePath)) return { tickets: [] };
|
|
6734
6762
|
try {
|
|
6735
|
-
const parsed = await
|
|
6763
|
+
const parsed = await fs17.readJson(storePath);
|
|
6736
6764
|
if (!parsed || !Array.isArray(parsed.tickets)) return { tickets: [] };
|
|
6737
6765
|
return { tickets: parsed.tickets };
|
|
6738
6766
|
} catch {
|
|
@@ -6740,8 +6768,8 @@ async function loadApprovalTicketStore(storePath) {
|
|
|
6740
6768
|
}
|
|
6741
6769
|
}
|
|
6742
6770
|
async function saveApprovalTicketStore(storePath, payload) {
|
|
6743
|
-
await
|
|
6744
|
-
await
|
|
6771
|
+
await fs17.ensureDir(path23.dirname(storePath));
|
|
6772
|
+
await fs17.writeJson(storePath, payload, { spaces: 2 });
|
|
6745
6773
|
}
|
|
6746
6774
|
function pruneApprovalTickets(tickets, nowMs) {
|
|
6747
6775
|
return tickets.filter((ticket) => {
|
|
@@ -6753,13 +6781,13 @@ function pruneApprovalTickets(tickets, nowMs) {
|
|
|
6753
6781
|
}
|
|
6754
6782
|
async function resolveApprovalTicketStoreAndPath(config, nowMs) {
|
|
6755
6783
|
const { runtimePath, legacyPath } = getApprovalTicketPaths(config);
|
|
6756
|
-
if (await
|
|
6784
|
+
if (await fs17.pathExists(runtimePath)) {
|
|
6757
6785
|
return {
|
|
6758
6786
|
storePath: runtimePath,
|
|
6759
6787
|
store: await loadApprovalTicketStore(runtimePath)
|
|
6760
6788
|
};
|
|
6761
6789
|
}
|
|
6762
|
-
if (!await
|
|
6790
|
+
if (!await fs17.pathExists(legacyPath)) {
|
|
6763
6791
|
return {
|
|
6764
6792
|
storePath: runtimePath,
|
|
6765
6793
|
store: { tickets: [] }
|
|
@@ -6775,7 +6803,7 @@ async function resolveApprovalTicketStoreAndPath(config, nowMs) {
|
|
|
6775
6803
|
migratedFrom: legacyPath
|
|
6776
6804
|
}
|
|
6777
6805
|
);
|
|
6778
|
-
await
|
|
6806
|
+
await fs17.remove(legacyPath).catch(() => {
|
|
6779
6807
|
});
|
|
6780
6808
|
return {
|
|
6781
6809
|
storePath: runtimePath,
|
|
@@ -6907,42 +6935,42 @@ var BUILTIN_DOC_DEFINITIONS = [
|
|
|
6907
6935
|
{
|
|
6908
6936
|
id: "agents",
|
|
6909
6937
|
title: { ko: "\uC5D0\uC774\uC804\uD2B8 \uC6B4\uC601 \uADDC\uCE59", en: "Agent Operating Rules" },
|
|
6910
|
-
relativePath: (_, lang) =>
|
|
6938
|
+
relativePath: (_, lang) => path23.join(lang, "common", "agents", "agents.md")
|
|
6911
6939
|
},
|
|
6912
6940
|
{
|
|
6913
6941
|
id: "git-workflow",
|
|
6914
6942
|
title: { ko: "Git \uC6CC\uD06C\uD50C\uB85C\uC6B0", en: "Git Workflow" },
|
|
6915
|
-
relativePath: (_, lang) =>
|
|
6943
|
+
relativePath: (_, lang) => path23.join(lang, "common", "agents", "git-workflow.md")
|
|
6916
6944
|
},
|
|
6917
6945
|
{
|
|
6918
6946
|
id: "issue-doc",
|
|
6919
6947
|
title: { ko: "Issue \uBB38\uC11C \uD15C\uD50C\uB9BF", en: "Issue Document Template" },
|
|
6920
|
-
relativePath: (_, lang) =>
|
|
6948
|
+
relativePath: (_, lang) => path23.join(lang, "common", "features", "feature-base", "issue.md")
|
|
6921
6949
|
},
|
|
6922
6950
|
{
|
|
6923
6951
|
id: "pr-doc",
|
|
6924
6952
|
title: { ko: "PR \uBB38\uC11C \uD15C\uD50C\uB9BF", en: "PR Document Template" },
|
|
6925
|
-
relativePath: (_, lang) =>
|
|
6953
|
+
relativePath: (_, lang) => path23.join(lang, "common", "features", "feature-base", "pr.md")
|
|
6926
6954
|
},
|
|
6927
6955
|
{
|
|
6928
6956
|
id: "create-feature",
|
|
6929
6957
|
title: { ko: "create-feature \uC2A4\uD0AC", en: "create-feature skill" },
|
|
6930
|
-
relativePath: (_, lang) =>
|
|
6958
|
+
relativePath: (_, lang) => path23.join(lang, "common", "agents", "skills", "create-feature.md")
|
|
6931
6959
|
},
|
|
6932
6960
|
{
|
|
6933
6961
|
id: "execute-task",
|
|
6934
6962
|
title: { ko: "execute-task \uC2A4\uD0AC", en: "execute-task skill" },
|
|
6935
|
-
relativePath: (_, lang) =>
|
|
6963
|
+
relativePath: (_, lang) => path23.join(lang, "common", "agents", "skills", "execute-task.md")
|
|
6936
6964
|
},
|
|
6937
6965
|
{
|
|
6938
6966
|
id: "create-issue",
|
|
6939
6967
|
title: { ko: "create-issue \uC2A4\uD0AC", en: "create-issue skill" },
|
|
6940
|
-
relativePath: (_, lang) =>
|
|
6968
|
+
relativePath: (_, lang) => path23.join(lang, "common", "agents", "skills", "create-issue.md")
|
|
6941
6969
|
},
|
|
6942
6970
|
{
|
|
6943
6971
|
id: "create-pr",
|
|
6944
6972
|
title: { ko: "create-pr \uC2A4\uD0AC", en: "create-pr skill" },
|
|
6945
|
-
relativePath: (_, lang) =>
|
|
6973
|
+
relativePath: (_, lang) => path23.join(lang, "common", "agents", "skills", "create-pr.md")
|
|
6946
6974
|
}
|
|
6947
6975
|
];
|
|
6948
6976
|
var DOC_FOLLOWUPS = {
|
|
@@ -7031,7 +7059,7 @@ function listBuiltinDocs(projectType, lang) {
|
|
|
7031
7059
|
id: doc.id,
|
|
7032
7060
|
title: doc.title[lang],
|
|
7033
7061
|
relativePath,
|
|
7034
|
-
absolutePath:
|
|
7062
|
+
absolutePath: path23.join(templatesDir, relativePath)
|
|
7035
7063
|
};
|
|
7036
7064
|
});
|
|
7037
7065
|
}
|
|
@@ -7040,7 +7068,7 @@ async function getBuiltinDoc(docId, projectType, lang) {
|
|
|
7040
7068
|
if (!entry) {
|
|
7041
7069
|
throw new Error(`Unknown builtin doc: ${docId}`);
|
|
7042
7070
|
}
|
|
7043
|
-
const content = await
|
|
7071
|
+
const content = await fs17.readFile(entry.absolutePath, "utf-8");
|
|
7044
7072
|
const hash = createHash("sha256").update(content).digest("hex").slice(0, 12);
|
|
7045
7073
|
return {
|
|
7046
7074
|
entry,
|
|
@@ -7051,6 +7079,56 @@ async function getBuiltinDoc(docId, projectType, lang) {
|
|
|
7051
7079
|
}
|
|
7052
7080
|
|
|
7053
7081
|
// src/commands/context.ts
|
|
7082
|
+
var LONG_RUNNING_DELEGATION_CATEGORIES = [
|
|
7083
|
+
"task_execute",
|
|
7084
|
+
"code_review",
|
|
7085
|
+
"review_fix_commit",
|
|
7086
|
+
"pre_pr_review"
|
|
7087
|
+
];
|
|
7088
|
+
function shouldDelegateCurrentAction(actionOptions, autoRunAvailable) {
|
|
7089
|
+
const primaryCategory = actionOptions[0]?.action?.category || null;
|
|
7090
|
+
const longRunningSet = new Set(LONG_RUNNING_DELEGATION_CATEGORIES);
|
|
7091
|
+
const shouldDelegate = autoRunAvailable || !!primaryCategory && longRunningSet.has(primaryCategory);
|
|
7092
|
+
return {
|
|
7093
|
+
shouldDelegate,
|
|
7094
|
+
category: primaryCategory
|
|
7095
|
+
};
|
|
7096
|
+
}
|
|
7097
|
+
function buildAgentOrchestrationPolicy(actionOptions, autoRunAvailable) {
|
|
7098
|
+
const delegation = shouldDelegateCurrentAction(actionOptions, autoRunAvailable);
|
|
7099
|
+
return {
|
|
7100
|
+
mode: "main_orchestrates_subagent_execution",
|
|
7101
|
+
delegationPolicy: "prefer_main_delegate_long_running_fallback_main",
|
|
7102
|
+
delegateCommandExecution: "long_running_only",
|
|
7103
|
+
delegateAutoRunExecution: true,
|
|
7104
|
+
fallbackToMainAgentWhenSubAgentUnavailable: true,
|
|
7105
|
+
longRunningCategories: [...LONG_RUNNING_DELEGATION_CATEGORIES],
|
|
7106
|
+
currentActionShouldDelegate: delegation.shouldDelegate,
|
|
7107
|
+
currentActionCategory: delegation.category,
|
|
7108
|
+
mainAgentResponsibilities: [
|
|
7109
|
+
"Keep user conversation state and approval boundaries",
|
|
7110
|
+
"Run the same execution loop directly when sub-agent is unavailable",
|
|
7111
|
+
"Delegate only long-running command/auto loops to sub-agents",
|
|
7112
|
+
"Report only on approval/manual/error boundaries"
|
|
7113
|
+
],
|
|
7114
|
+
subAgentResponsibilities: [
|
|
7115
|
+
"Run flow/context command loops",
|
|
7116
|
+
"Execute only currently selected atomic command actions",
|
|
7117
|
+
"Return structured status to main agent"
|
|
7118
|
+
],
|
|
7119
|
+
pauseAndReportWhen: [
|
|
7120
|
+
"approvalRequest.required=true",
|
|
7121
|
+
"AUTO_GATE_REACHED",
|
|
7122
|
+
"AUTO_MANUAL_REQUIRED",
|
|
7123
|
+
"command execution error"
|
|
7124
|
+
],
|
|
7125
|
+
resumePriority: [
|
|
7126
|
+
"flow --resume <RUN_ID>",
|
|
7127
|
+
"autoRun.resume.flowCommand",
|
|
7128
|
+
"context --json"
|
|
7129
|
+
]
|
|
7130
|
+
};
|
|
7131
|
+
}
|
|
7054
7132
|
async function resolveContextState(config, featureName, options) {
|
|
7055
7133
|
if (!config) {
|
|
7056
7134
|
throw createCliError(
|
|
@@ -7116,6 +7194,72 @@ function buildSuggestionFinalPrompt(lang, suggestionOptions) {
|
|
|
7116
7194
|
example
|
|
7117
7195
|
});
|
|
7118
7196
|
}
|
|
7197
|
+
function normalizeCategoryToken(value) {
|
|
7198
|
+
if (typeof value !== "string") return null;
|
|
7199
|
+
const normalized = value.trim().toLowerCase();
|
|
7200
|
+
if (!normalized) return null;
|
|
7201
|
+
return normalized;
|
|
7202
|
+
}
|
|
7203
|
+
function resolveAutoRunCategories(approval) {
|
|
7204
|
+
const known = new Set(ACTION_CATEGORIES);
|
|
7205
|
+
const unique2 = /* @__PURE__ */ new Set();
|
|
7206
|
+
const unknown = /* @__PURE__ */ new Set();
|
|
7207
|
+
for (const raw of approval?.requireCheckCategories ?? approval?.requireOkCategories ?? []) {
|
|
7208
|
+
const normalized = normalizeCategoryToken(raw);
|
|
7209
|
+
if (!normalized) continue;
|
|
7210
|
+
if (known.has(normalized)) {
|
|
7211
|
+
unique2.add(normalized);
|
|
7212
|
+
} else {
|
|
7213
|
+
unknown.add(normalized);
|
|
7214
|
+
}
|
|
7215
|
+
}
|
|
7216
|
+
return {
|
|
7217
|
+
untilCategories: Array.from(unique2),
|
|
7218
|
+
unknownCategories: Array.from(unknown)
|
|
7219
|
+
};
|
|
7220
|
+
}
|
|
7221
|
+
function buildAutoRunCommand(state, featureName, selectedComponent, untilCategories) {
|
|
7222
|
+
if (untilCategories.length === 0) return "";
|
|
7223
|
+
const featureRef = resolveFeatureRefForApproval(state, featureName);
|
|
7224
|
+
const componentArg = selectedComponent ? ` --component ${selectedComponent}` : "";
|
|
7225
|
+
return `npx lee-spec-kit flow ${featureRef}${componentArg} --auto-until-category ${untilCategories.join(",")}`;
|
|
7226
|
+
}
|
|
7227
|
+
function resolveAutoRunPlan(lang, state, featureName, selectedComponent, approval, approvalRequired) {
|
|
7228
|
+
const base = (reasonCode, untilCategories2 = [], unknownCategories2 = []) => ({
|
|
7229
|
+
available: false,
|
|
7230
|
+
reasonCode,
|
|
7231
|
+
summary: tr(lang, "cli", "context.autoRunUnavailable"),
|
|
7232
|
+
command: "",
|
|
7233
|
+
untilCategories: untilCategories2,
|
|
7234
|
+
unknownCategories: unknownCategories2
|
|
7235
|
+
});
|
|
7236
|
+
if (state.status !== "single_matched") return base("NOT_SINGLE_MATCHED");
|
|
7237
|
+
if (state.actionOptions.length === 0) return base("NO_ACTION_OPTIONS");
|
|
7238
|
+
if (approvalRequired) return base("APPROVAL_REQUIRED");
|
|
7239
|
+
const mode = approval?.mode ?? "builtin";
|
|
7240
|
+
if (mode !== "category") return base("APPROVAL_MODE_NOT_CATEGORY");
|
|
7241
|
+
const defaultPolicy = approval?.default ?? "keep";
|
|
7242
|
+
if (defaultPolicy !== "skip") return base("DEFAULT_NOT_SKIP");
|
|
7243
|
+
const { untilCategories, unknownCategories } = resolveAutoRunCategories(approval);
|
|
7244
|
+
if (untilCategories.length === 0) {
|
|
7245
|
+
return base("NO_REQUIRE_CHECK_CATEGORIES", [], unknownCategories);
|
|
7246
|
+
}
|
|
7247
|
+
return {
|
|
7248
|
+
available: true,
|
|
7249
|
+
reasonCode: "AVAILABLE",
|
|
7250
|
+
summary: tr(lang, "cli", "context.autoRunSummary", {
|
|
7251
|
+
categories: untilCategories.join(", ")
|
|
7252
|
+
}),
|
|
7253
|
+
command: buildAutoRunCommand(
|
|
7254
|
+
state,
|
|
7255
|
+
featureName,
|
|
7256
|
+
selectedComponent,
|
|
7257
|
+
untilCategories
|
|
7258
|
+
),
|
|
7259
|
+
untilCategories,
|
|
7260
|
+
unknownCategories
|
|
7261
|
+
};
|
|
7262
|
+
}
|
|
7119
7263
|
function toSuggestionLabel(index) {
|
|
7120
7264
|
let n = index + 1;
|
|
7121
7265
|
let label = "";
|
|
@@ -7309,9 +7453,6 @@ function getListLabel(f, stepsMap, lang, workflowPolicy, prePrReviewPolicy) {
|
|
|
7309
7453
|
if (prePrReviewPolicy.enabled && f.prePrReview.status !== "Done") {
|
|
7310
7454
|
return tr(lang, "cli", "context.list.completePrePrReview");
|
|
7311
7455
|
}
|
|
7312
|
-
if (prePrReviewPolicy.enabled && prePrReviewPolicy.findings === "required" && (!f.docs.prePrFindingsFieldExists || !f.prePrReview.findingsProvided)) {
|
|
7313
|
-
return tr(lang, "cli", "context.list.addPrePrFindings");
|
|
7314
|
-
}
|
|
7315
7456
|
if (prePrReviewPolicy.enabled && (!f.docs.prePrEvidenceFieldExists || !f.prePrReview.evidenceProvided)) {
|
|
7316
7457
|
return tr(lang, "cli", "context.list.addPrePrEvidence");
|
|
7317
7458
|
}
|
|
@@ -7376,14 +7517,11 @@ function toCompactFeature(feature) {
|
|
|
7376
7517
|
tasks: feature.tasks,
|
|
7377
7518
|
prePrReview: {
|
|
7378
7519
|
status: feature.prePrReview.status,
|
|
7379
|
-
findings: feature.prePrReview.findings,
|
|
7380
|
-
findingsProvided: feature.prePrReview.findingsProvided,
|
|
7381
7520
|
evidenceProvided: feature.prePrReview.evidenceProvided,
|
|
7382
7521
|
decisionOutcome: feature.prePrReview.decisionOutcome,
|
|
7383
7522
|
decisionProvided: feature.prePrReview.decisionProvided
|
|
7384
7523
|
},
|
|
7385
7524
|
prReview: {
|
|
7386
|
-
findings: feature.prReview.findings,
|
|
7387
7525
|
evidenceProvided: feature.prReview.evidenceProvided,
|
|
7388
7526
|
decisionProvided: feature.prReview.decisionProvided
|
|
7389
7527
|
},
|
|
@@ -7415,10 +7553,8 @@ function toCompactFeature(feature) {
|
|
|
7415
7553
|
prFieldExists: feature.docs.prFieldExists,
|
|
7416
7554
|
prStatusFieldExists: feature.docs.prStatusFieldExists,
|
|
7417
7555
|
prePrReviewFieldExists: feature.docs.prePrReviewFieldExists,
|
|
7418
|
-
prePrFindingsFieldExists: feature.docs.prePrFindingsFieldExists,
|
|
7419
7556
|
prePrEvidenceFieldExists: feature.docs.prePrEvidenceFieldExists,
|
|
7420
7557
|
prePrDecisionFieldExists: feature.docs.prePrDecisionFieldExists,
|
|
7421
|
-
prReviewFindingsFieldExists: feature.docs.prReviewFindingsFieldExists,
|
|
7422
7558
|
prReviewEvidenceFieldExists: feature.docs.prReviewEvidenceFieldExists,
|
|
7423
7559
|
prReviewDecisionFieldExists: feature.docs.prReviewDecisionFieldExists
|
|
7424
7560
|
},
|
|
@@ -7520,6 +7656,30 @@ async function runContext(featureName, options) {
|
|
|
7520
7656
|
selectedComponent
|
|
7521
7657
|
);
|
|
7522
7658
|
const suggestionFinalPrompt = buildSuggestionFinalPrompt(lang, suggestionOptions);
|
|
7659
|
+
const checkRequiredLabels = state.actionOptions.filter((option) => !!option.action.requiresUserCheck).map((option) => option.label);
|
|
7660
|
+
const checkRequiredCategories = [
|
|
7661
|
+
...new Set(
|
|
7662
|
+
state.actionOptions.filter((option) => !!option.action.requiresUserCheck).map((option) => option.action.category || "uncategorized")
|
|
7663
|
+
)
|
|
7664
|
+
];
|
|
7665
|
+
const approvalRequired = checkRequiredLabels.length > 0;
|
|
7666
|
+
const finalApprovalPrompt = approvalRequired ? buildFinalApprovalPrompt(lang, state.actionOptions) : "";
|
|
7667
|
+
const approvalUserFacingLines = approvalRequired ? [
|
|
7668
|
+
...state.actionOptions.map((o) => o.approvalPrompt),
|
|
7669
|
+
finalApprovalPrompt
|
|
7670
|
+
].filter((line) => line.length > 0) : [];
|
|
7671
|
+
const autoRunPlan = resolveAutoRunPlan(
|
|
7672
|
+
lang,
|
|
7673
|
+
state,
|
|
7674
|
+
featureName,
|
|
7675
|
+
selectedComponent,
|
|
7676
|
+
config.approval,
|
|
7677
|
+
approvalRequired
|
|
7678
|
+
);
|
|
7679
|
+
const agentOrchestration = buildAgentOrchestrationPolicy(
|
|
7680
|
+
state.actionOptions,
|
|
7681
|
+
autoRunPlan.available
|
|
7682
|
+
);
|
|
7523
7683
|
if (options.approve || options.execute) {
|
|
7524
7684
|
await runApprovedOption(
|
|
7525
7685
|
state,
|
|
@@ -7534,18 +7694,6 @@ async function runContext(featureName, options) {
|
|
|
7534
7694
|
const jsonMode = !!options.json || !!options.jsonCompact;
|
|
7535
7695
|
if (jsonMode) {
|
|
7536
7696
|
const primaryAction = state.actionOptions[0] ?? null;
|
|
7537
|
-
const checkRequiredLabels = state.actionOptions.filter((option) => !!option.action.requiresUserCheck).map((option) => option.label);
|
|
7538
|
-
const checkRequiredCategories = [
|
|
7539
|
-
...new Set(
|
|
7540
|
-
state.actionOptions.filter((option) => !!option.action.requiresUserCheck).map((option) => option.action.category || "uncategorized")
|
|
7541
|
-
)
|
|
7542
|
-
];
|
|
7543
|
-
const approvalRequired = checkRequiredLabels.length > 0;
|
|
7544
|
-
const finalApprovalPrompt = approvalRequired ? buildFinalApprovalPrompt(lang, state.actionOptions) : "";
|
|
7545
|
-
const approvalUserFacingLines = approvalRequired ? [
|
|
7546
|
-
...state.actionOptions.map((o) => o.approvalPrompt),
|
|
7547
|
-
finalApprovalPrompt
|
|
7548
|
-
].filter((line) => line.length > 0) : [];
|
|
7549
7697
|
const approveCommand = buildApprovalCommand(
|
|
7550
7698
|
state,
|
|
7551
7699
|
featureName,
|
|
@@ -7607,6 +7755,15 @@ async function runContext(featureName, options) {
|
|
|
7607
7755
|
contextVersion: state.contextVersion,
|
|
7608
7756
|
config: config.approval ?? { mode: "builtin" }
|
|
7609
7757
|
},
|
|
7758
|
+
agentOrchestration,
|
|
7759
|
+
autoRun: {
|
|
7760
|
+
available: autoRunPlan.available,
|
|
7761
|
+
reasonCode: autoRunPlan.reasonCode,
|
|
7762
|
+
summary: autoRunPlan.summary,
|
|
7763
|
+
command: autoRunPlan.command,
|
|
7764
|
+
untilCategories: autoRunPlan.untilCategories,
|
|
7765
|
+
unknownCategories: autoRunPlan.unknownCategories
|
|
7766
|
+
},
|
|
7610
7767
|
approvalRequest: {
|
|
7611
7768
|
required: approvalRequired,
|
|
7612
7769
|
finalPrompt: finalApprovalPrompt,
|
|
@@ -7680,14 +7837,24 @@ async function runContext(featureName, options) {
|
|
|
7680
7837
|
"actionOptions[].detail",
|
|
7681
7838
|
"actionOptions[].approvalPrompt"
|
|
7682
7839
|
] : [],
|
|
7683
|
-
recommendation: "Before asking for approval, show only `actionOptions[].approvalPrompt` lines and `approvalRequest.finalPrompt` to the user. Keep `requiredDocs`, `checkPolicy`, and raw execution commands as internal guidance. For commit actions, include scope (`docs`/`project`) and commit message in the visible prompt. User replies should include the label token (e.g. `A`, `A OK`, `A proceed`, `A \uC9C4\uD589\uD574`). For command execution, prefer one-shot `npx lee-spec-kit flow <featureRef> --approve <LABEL> --execute` to avoid session mismatch after context compression/reset. Use ticket-based `context --execute --ticket` only when explicitly needed.",
|
|
7840
|
+
recommendation: "Before asking for approval, show only `actionOptions[].approvalPrompt` lines and `approvalRequest.finalPrompt` to the user. Keep `requiredDocs`, `checkPolicy`, and raw execution commands as internal guidance. For commit actions, include scope (`docs`/`project`) and commit message in the visible prompt. User replies should include the label token (e.g. `A`, `A OK`, `A proceed`, `A \uC9C4\uD589\uD574`). For command execution, prefer one-shot `npx lee-spec-kit flow <featureRef> --approve <LABEL> --execute` to avoid session mismatch after context compression/reset. Use ticket-based `context --execute --ticket` only when explicitly needed. Use main-agent orchestration: keep short steps in main agent, and delegate only long-running command/auto loops to sub-agents.",
|
|
7684
7841
|
oneApprovalPerAction: approvalRequired,
|
|
7685
7842
|
requireFreshContext: true,
|
|
7686
7843
|
contextVersion: state.contextVersion,
|
|
7687
7844
|
config: config.approval ?? { mode: "builtin" }
|
|
7688
7845
|
},
|
|
7846
|
+
agentOrchestration,
|
|
7847
|
+
autoRun: {
|
|
7848
|
+
available: autoRunPlan.available,
|
|
7849
|
+
reasonCode: autoRunPlan.reasonCode,
|
|
7850
|
+
summary: autoRunPlan.summary,
|
|
7851
|
+
command: autoRunPlan.command,
|
|
7852
|
+
untilCategories: autoRunPlan.untilCategories,
|
|
7853
|
+
unknownCategories: autoRunPlan.unknownCategories,
|
|
7854
|
+
guidance: "Use auto-run only when `autoRun.available=true`. Stop and request approval when `approvalRequest.required=true` or when auto mode reaches configured gate categories."
|
|
7855
|
+
},
|
|
7689
7856
|
approvalRequest: {
|
|
7690
|
-
guidance: "User-facing output must include only approval prompts (`A: ...`) and `finalPrompt`. Do not expose `requiredDocs`, `checkPolicy`, or raw `cmd` unless explicitly requested. For approved command actions, prefer one-shot `flow --approve <LABEL> --execute`.",
|
|
7857
|
+
guidance: "User-facing output must include only approval prompts (`A: ...`) and `finalPrompt`. Do not expose `requiredDocs`, `checkPolicy`, or raw `cmd` unless explicitly requested. For approved command actions, prefer one-shot `flow --approve <LABEL> --execute`. Keep short steps in main agent and delegate only long-running command/auto loops to sub-agents.",
|
|
7691
7858
|
required: approvalRequired,
|
|
7692
7859
|
finalPrompt: finalApprovalPrompt,
|
|
7693
7860
|
userFacingLines: approvalUserFacingLines,
|
|
@@ -7899,7 +8066,7 @@ async function runContext(featureName, options) {
|
|
|
7899
8066
|
const f = state.targetFeatures[0];
|
|
7900
8067
|
const stepName = stepsMap[f.currentStep] || "Unknown";
|
|
7901
8068
|
const checkTag = (requiresUserCheck) => requiresUserCheck ? chalk6.yellow(tr(lang, "cli", "context.checkRequired")) : "";
|
|
7902
|
-
const hasCheckAction =
|
|
8069
|
+
const hasCheckAction = approvalRequired;
|
|
7903
8070
|
console.log(
|
|
7904
8071
|
`\u{1F539} Feature: ${chalk6.bold(f.folderName)} ${config.projectType === "multi" ? chalk6.cyan(`(${f.type})`) : ""}`
|
|
7905
8072
|
);
|
|
@@ -7909,7 +8076,7 @@ async function runContext(featureName, options) {
|
|
|
7909
8076
|
if (f.issueNumber) {
|
|
7910
8077
|
console.log(` \u2022 Issue: #${f.issueNumber}`);
|
|
7911
8078
|
}
|
|
7912
|
-
console.log(` \u2022 Path: ${
|
|
8079
|
+
console.log(` \u2022 Path: ${path23.relative(cwd, f.path)}`);
|
|
7913
8080
|
if (f.git.projectBranch) {
|
|
7914
8081
|
console.log(` \u2022 Project Branch: ${f.git.projectBranch}`);
|
|
7915
8082
|
}
|
|
@@ -7943,6 +8110,11 @@ async function runContext(featureName, options) {
|
|
|
7943
8110
|
return;
|
|
7944
8111
|
}
|
|
7945
8112
|
const actionOptions = state.actionOptions;
|
|
8113
|
+
const hasCommandOption = actionOptions.some((option) => option.action.type === "command");
|
|
8114
|
+
const longRunningDelegation = shouldDelegateCurrentAction(
|
|
8115
|
+
actionOptions,
|
|
8116
|
+
autoRunPlan.available
|
|
8117
|
+
);
|
|
7946
8118
|
console.log(chalk6.green(chalk6.bold("\u{1F449} Next Options (Atomic):")));
|
|
7947
8119
|
let hasDocsCommand = false;
|
|
7948
8120
|
actionOptions.forEach((option) => {
|
|
@@ -7956,6 +8128,9 @@ async function runContext(featureName, options) {
|
|
|
7956
8128
|
if (hasDocsCommand) {
|
|
7957
8129
|
console.log(chalk6.gray(` \u21B3 ${tr(lang, "cli", "context.tipDocsCommitRules")}`));
|
|
7958
8130
|
}
|
|
8131
|
+
if (hasCommandOption && longRunningDelegation.shouldDelegate) {
|
|
8132
|
+
console.log(chalk6.gray(` \u21B3 ${tr(lang, "cli", "context.subAgentOrchestrationHint")}`));
|
|
8133
|
+
}
|
|
7959
8134
|
if (hasCheckAction) {
|
|
7960
8135
|
console.log(chalk6.gray(` \u21B3 ${tr(lang, "cli", "context.actionOptionHint")}`));
|
|
7961
8136
|
console.log(chalk6.gray(` \u21B3 ${tr(lang, "cli", "context.actionExplainHint")}`));
|
|
@@ -7971,8 +8146,17 @@ async function runContext(featureName, options) {
|
|
|
7971
8146
|
);
|
|
7972
8147
|
}
|
|
7973
8148
|
}
|
|
8149
|
+
if (autoRunPlan.available) {
|
|
8150
|
+
console.log(chalk6.gray(` \u21B3 ${autoRunPlan.summary}`));
|
|
8151
|
+
console.log(
|
|
8152
|
+
chalk6.gray(
|
|
8153
|
+
` \u21B3 ${tr(lang, "cli", "context.autoRunCommandHint", {
|
|
8154
|
+
command: autoRunPlan.command
|
|
8155
|
+
})}`
|
|
8156
|
+
)
|
|
8157
|
+
);
|
|
8158
|
+
}
|
|
7974
8159
|
if (actionOptions.length > 0 && hasCheckAction) {
|
|
7975
|
-
const finalApprovalPrompt = buildFinalApprovalPrompt(lang, actionOptions);
|
|
7976
8160
|
const approveCommand = buildApprovalCommand(
|
|
7977
8161
|
state,
|
|
7978
8162
|
featureName,
|
|
@@ -8264,7 +8448,7 @@ var FIXABLE_ISSUE_CODES = /* @__PURE__ */ new Set([
|
|
|
8264
8448
|
]);
|
|
8265
8449
|
function formatPath(cwd, p) {
|
|
8266
8450
|
if (!p) return "";
|
|
8267
|
-
return
|
|
8451
|
+
return path23.isAbsolute(p) ? path23.relative(cwd, p) : p;
|
|
8268
8452
|
}
|
|
8269
8453
|
function detectPlaceholders(content) {
|
|
8270
8454
|
const patterns = [
|
|
@@ -8413,7 +8597,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
|
|
|
8413
8597
|
const placeholderContext = {
|
|
8414
8598
|
projectName: config.projectName,
|
|
8415
8599
|
featureName: f.slug,
|
|
8416
|
-
featurePath: f.docs.featurePathFromDocs ||
|
|
8600
|
+
featurePath: f.docs.featurePathFromDocs || path23.relative(config.docsDir, f.path),
|
|
8417
8601
|
repoType: f.type,
|
|
8418
8602
|
featureNumber
|
|
8419
8603
|
};
|
|
@@ -8423,9 +8607,9 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
|
|
|
8423
8607
|
"tasks.md"
|
|
8424
8608
|
];
|
|
8425
8609
|
for (const file of files) {
|
|
8426
|
-
const fullPath =
|
|
8427
|
-
if (!await
|
|
8428
|
-
const original = await
|
|
8610
|
+
const fullPath = path23.join(f.path, file);
|
|
8611
|
+
if (!await fs17.pathExists(fullPath)) continue;
|
|
8612
|
+
const original = await fs17.readFile(fullPath, "utf-8");
|
|
8429
8613
|
let next = original;
|
|
8430
8614
|
const changes = [];
|
|
8431
8615
|
const placeholderFix = applyPlaceholderFixes(next, placeholderContext, config.lang);
|
|
@@ -8449,7 +8633,7 @@ async function applyDoctorFixes(config, cwd, features, dryRun) {
|
|
|
8449
8633
|
}
|
|
8450
8634
|
if (next === original) continue;
|
|
8451
8635
|
if (!dryRun) {
|
|
8452
|
-
await
|
|
8636
|
+
await fs17.writeFile(fullPath, next, "utf-8");
|
|
8453
8637
|
}
|
|
8454
8638
|
entries.push({
|
|
8455
8639
|
path: formatPath(cwd, fullPath),
|
|
@@ -8468,8 +8652,8 @@ async function checkDocsStructure(config, cwd) {
|
|
|
8468
8652
|
const issues = [];
|
|
8469
8653
|
const requiredDirs = ["agents", "features", "prd", "designs", "ideas"];
|
|
8470
8654
|
for (const dir of requiredDirs) {
|
|
8471
|
-
const p =
|
|
8472
|
-
if (!await
|
|
8655
|
+
const p = path23.join(config.docsDir, dir);
|
|
8656
|
+
if (!await fs17.pathExists(p)) {
|
|
8473
8657
|
issues.push({
|
|
8474
8658
|
level: "error",
|
|
8475
8659
|
code: "missing_dir",
|
|
@@ -8478,8 +8662,8 @@ async function checkDocsStructure(config, cwd) {
|
|
|
8478
8662
|
});
|
|
8479
8663
|
}
|
|
8480
8664
|
}
|
|
8481
|
-
const configPath =
|
|
8482
|
-
if (!await
|
|
8665
|
+
const configPath = path23.join(config.docsDir, ".lee-spec-kit.json");
|
|
8666
|
+
if (!await fs17.pathExists(configPath)) {
|
|
8483
8667
|
issues.push({
|
|
8484
8668
|
level: "warn",
|
|
8485
8669
|
code: "missing_config",
|
|
@@ -8501,7 +8685,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
8501
8685
|
}
|
|
8502
8686
|
const idMap = /* @__PURE__ */ new Map();
|
|
8503
8687
|
for (const f of features) {
|
|
8504
|
-
const rel = f.docs.featurePathFromDocs ||
|
|
8688
|
+
const rel = f.docs.featurePathFromDocs || path23.relative(config.docsDir, f.path);
|
|
8505
8689
|
const id = f.id || "UNKNOWN";
|
|
8506
8690
|
if (!idMap.has(id)) idMap.set(id, []);
|
|
8507
8691
|
idMap.get(id).push(rel);
|
|
@@ -8509,9 +8693,9 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
8509
8693
|
if (!isInitialTemplateState) {
|
|
8510
8694
|
const featureDocs = ["spec.md", "plan.md", "tasks.md"];
|
|
8511
8695
|
for (const file of featureDocs) {
|
|
8512
|
-
const p =
|
|
8513
|
-
if (!await
|
|
8514
|
-
const content = await
|
|
8696
|
+
const p = path23.join(f.path, file);
|
|
8697
|
+
if (!await fs17.pathExists(p)) continue;
|
|
8698
|
+
const content = await fs17.readFile(p, "utf-8");
|
|
8515
8699
|
const placeholders = detectPlaceholders(content);
|
|
8516
8700
|
if (placeholders.length === 0) continue;
|
|
8517
8701
|
issues.push({
|
|
@@ -8524,9 +8708,9 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
8524
8708
|
});
|
|
8525
8709
|
}
|
|
8526
8710
|
if (decisionsPlaceholderMode !== "off") {
|
|
8527
|
-
const decisionsPath =
|
|
8528
|
-
if (await
|
|
8529
|
-
const content = await
|
|
8711
|
+
const decisionsPath = path23.join(f.path, "decisions.md");
|
|
8712
|
+
if (await fs17.pathExists(decisionsPath)) {
|
|
8713
|
+
const content = await fs17.readFile(decisionsPath, "utf-8");
|
|
8530
8714
|
const placeholders = detectPlaceholders(content);
|
|
8531
8715
|
if (placeholders.length > 0) {
|
|
8532
8716
|
issues.push({
|
|
@@ -8553,7 +8737,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
8553
8737
|
level: "warn",
|
|
8554
8738
|
code: "spec_status_unset",
|
|
8555
8739
|
message: tr(config.lang, "cli", "doctor.issue.specStatusUnset"),
|
|
8556
|
-
path: formatPath(cwd,
|
|
8740
|
+
path: formatPath(cwd, path23.join(f.path, "spec.md"))
|
|
8557
8741
|
});
|
|
8558
8742
|
}
|
|
8559
8743
|
if (f.docs.planExists && !f.planStatus && !isInitialTemplateState) {
|
|
@@ -8561,7 +8745,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
8561
8745
|
level: "warn",
|
|
8562
8746
|
code: "plan_status_unset",
|
|
8563
8747
|
message: tr(config.lang, "cli", "doctor.issue.planStatusUnset"),
|
|
8564
|
-
path: formatPath(cwd,
|
|
8748
|
+
path: formatPath(cwd, path23.join(f.path, "plan.md"))
|
|
8565
8749
|
});
|
|
8566
8750
|
}
|
|
8567
8751
|
if (f.docs.tasksExists && f.tasks.total === 0 && !isInitialTemplateState) {
|
|
@@ -8569,7 +8753,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
8569
8753
|
level: "warn",
|
|
8570
8754
|
code: "tasks_empty",
|
|
8571
8755
|
message: tr(config.lang, "cli", "doctor.issue.tasksEmpty"),
|
|
8572
|
-
path: formatPath(cwd,
|
|
8756
|
+
path: formatPath(cwd, path23.join(f.path, "tasks.md"))
|
|
8573
8757
|
});
|
|
8574
8758
|
}
|
|
8575
8759
|
if (f.docs.tasksExists && !f.docs.tasksDocStatusFieldExists && !isInitialTemplateState) {
|
|
@@ -8577,7 +8761,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
8577
8761
|
level: "warn",
|
|
8578
8762
|
code: "tasks_doc_status_missing",
|
|
8579
8763
|
message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusMissing"),
|
|
8580
|
-
path: formatPath(cwd,
|
|
8764
|
+
path: formatPath(cwd, path23.join(f.path, "tasks.md"))
|
|
8581
8765
|
});
|
|
8582
8766
|
}
|
|
8583
8767
|
if (f.docs.tasksExists && f.docs.tasksDocStatusFieldExists && !f.tasksDocStatus && !isInitialTemplateState) {
|
|
@@ -8585,7 +8769,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
8585
8769
|
level: "warn",
|
|
8586
8770
|
code: "tasks_doc_status_unset",
|
|
8587
8771
|
message: tr(config.lang, "cli", "doctor.issue.tasksDocStatusUnset"),
|
|
8588
|
-
path: formatPath(cwd,
|
|
8772
|
+
path: formatPath(cwd, path23.join(f.path, "tasks.md"))
|
|
8589
8773
|
});
|
|
8590
8774
|
}
|
|
8591
8775
|
}
|
|
@@ -8609,7 +8793,7 @@ async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
|
8609
8793
|
level: "warn",
|
|
8610
8794
|
code: "missing_feature_id",
|
|
8611
8795
|
message: tr(config.lang, "cli", "doctor.issue.missingFeatureId"),
|
|
8612
|
-
path: formatPath(cwd,
|
|
8796
|
+
path: formatPath(cwd, path23.join(config.docsDir, p))
|
|
8613
8797
|
});
|
|
8614
8798
|
}
|
|
8615
8799
|
return issues;
|
|
@@ -8732,7 +8916,7 @@ function doctorCommand(program2) {
|
|
|
8732
8916
|
}
|
|
8733
8917
|
console.log();
|
|
8734
8918
|
console.log(chalk6.bold(tr(lang, "cli", "doctor.title")));
|
|
8735
|
-
console.log(chalk6.gray(`- Docs: ${
|
|
8919
|
+
console.log(chalk6.gray(`- Docs: ${path23.relative(cwd, docsDir)}`));
|
|
8736
8920
|
console.log(chalk6.gray(`- Type: ${projectType}`));
|
|
8737
8921
|
console.log(chalk6.gray(`- Lang: ${lang}`));
|
|
8738
8922
|
console.log();
|
|
@@ -8904,7 +9088,7 @@ async function runView(featureName, options) {
|
|
|
8904
9088
|
}
|
|
8905
9089
|
console.log();
|
|
8906
9090
|
console.log(chalk6.bold("\u{1F4CA} Workflow View"));
|
|
8907
|
-
console.log(chalk6.gray(`- Docs: ${
|
|
9091
|
+
console.log(chalk6.gray(`- Docs: ${path23.relative(cwd, config.docsDir)}`));
|
|
8908
9092
|
console.log(
|
|
8909
9093
|
chalk6.gray(
|
|
8910
9094
|
`- Features: ${state.features.length} (open ${state.openFeatures.length} / done ${state.doneFeatures.length})`
|
|
@@ -8974,9 +9158,145 @@ async function runView(featureName, options) {
|
|
|
8974
9158
|
}
|
|
8975
9159
|
console.log();
|
|
8976
9160
|
}
|
|
9161
|
+
function normalizeRunId(raw) {
|
|
9162
|
+
const value = raw.trim();
|
|
9163
|
+
if (!/^[A-Za-z0-9_-]{6,64}$/.test(value)) {
|
|
9164
|
+
throw createCliError(
|
|
9165
|
+
"INVALID_ARGUMENT",
|
|
9166
|
+
"Invalid flow run id format. Use an id returned by `flow --start-auto --json`."
|
|
9167
|
+
);
|
|
9168
|
+
}
|
|
9169
|
+
return value;
|
|
9170
|
+
}
|
|
9171
|
+
function getFlowRunBaseDir(cwd) {
|
|
9172
|
+
return path23.join(getRuntimeStateDir(cwd), "flow-runs");
|
|
9173
|
+
}
|
|
9174
|
+
function getFlowRunPath(cwd, runId) {
|
|
9175
|
+
return path23.join(getFlowRunBaseDir(cwd), `${runId}.json`);
|
|
9176
|
+
}
|
|
9177
|
+
function getFlowRunLockPath(cwd, runId) {
|
|
9178
|
+
return path23.join(getRuntimeStateDir(cwd), "locks", `flow-run-${runId}.lock`);
|
|
9179
|
+
}
|
|
9180
|
+
async function readFlowRunRecordUnsafe(cwd, runId) {
|
|
9181
|
+
const normalized = normalizeRunId(runId);
|
|
9182
|
+
const filePath = getFlowRunPath(cwd, normalized);
|
|
9183
|
+
if (!await fs17.pathExists(filePath)) {
|
|
9184
|
+
throw createCliError(
|
|
9185
|
+
"INVALID_ARGUMENT",
|
|
9186
|
+
`Unknown flow run id: ${normalized}. Start with \`flow <feature> --auto-... --start-auto\`.`
|
|
9187
|
+
);
|
|
9188
|
+
}
|
|
9189
|
+
try {
|
|
9190
|
+
const parsed = await fs17.readJson(filePath);
|
|
9191
|
+
if (!parsed || typeof parsed !== "object" || parsed.runId !== normalized) {
|
|
9192
|
+
throw new Error("invalid payload");
|
|
9193
|
+
}
|
|
9194
|
+
return parsed;
|
|
9195
|
+
} catch {
|
|
9196
|
+
throw createCliError(
|
|
9197
|
+
"INVALID_ARGUMENT",
|
|
9198
|
+
`Cannot load flow run record: ${normalized}`
|
|
9199
|
+
);
|
|
9200
|
+
}
|
|
9201
|
+
}
|
|
9202
|
+
async function writeFlowRunRecord(cwd, record) {
|
|
9203
|
+
const filePath = getFlowRunPath(cwd, record.runId);
|
|
9204
|
+
await fs17.ensureDir(path23.dirname(filePath));
|
|
9205
|
+
await fs17.writeJson(filePath, record, { spaces: 2 });
|
|
9206
|
+
}
|
|
9207
|
+
async function createFlowRunRecord(cwd, input) {
|
|
9208
|
+
const runId = randomUUID().replace(/-/g, "").slice(0, 16);
|
|
9209
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
9210
|
+
const record = {
|
|
9211
|
+
runId,
|
|
9212
|
+
featureName: input.featureName,
|
|
9213
|
+
selection: {
|
|
9214
|
+
component: input.selection.component || void 0,
|
|
9215
|
+
all: !!input.selection.all,
|
|
9216
|
+
done: !!input.selection.done
|
|
9217
|
+
},
|
|
9218
|
+
auto: {
|
|
9219
|
+
untilCategories: [...input.auto.untilCategories],
|
|
9220
|
+
requestText: input.auto.requestText?.trim() || void 0,
|
|
9221
|
+
requestPending: input.auto.requestPending,
|
|
9222
|
+
preset: input.auto.preset ?? null,
|
|
9223
|
+
source: input.auto.source ?? null
|
|
9224
|
+
},
|
|
9225
|
+
status: "running",
|
|
9226
|
+
createdAt: nowIso,
|
|
9227
|
+
updatedAt: nowIso
|
|
9228
|
+
};
|
|
9229
|
+
const lockPath = getFlowRunLockPath(cwd, runId);
|
|
9230
|
+
return withFileLock(
|
|
9231
|
+
lockPath,
|
|
9232
|
+
async () => {
|
|
9233
|
+
await writeFlowRunRecord(cwd, record);
|
|
9234
|
+
return record;
|
|
9235
|
+
},
|
|
9236
|
+
{ owner: "flow-run:create" }
|
|
9237
|
+
);
|
|
9238
|
+
}
|
|
9239
|
+
async function getFlowRunRecord(cwd, runId) {
|
|
9240
|
+
const normalized = normalizeRunId(runId);
|
|
9241
|
+
return readFlowRunRecordUnsafe(cwd, normalized);
|
|
9242
|
+
}
|
|
9243
|
+
async function updateFlowRunRecord(cwd, runId, updater) {
|
|
9244
|
+
const normalized = normalizeRunId(runId);
|
|
9245
|
+
const lockPath = getFlowRunLockPath(cwd, normalized);
|
|
9246
|
+
return withFileLock(
|
|
9247
|
+
lockPath,
|
|
9248
|
+
async () => {
|
|
9249
|
+
const current = await readFlowRunRecordUnsafe(cwd, normalized);
|
|
9250
|
+
const next = updater(current);
|
|
9251
|
+
const updated = {
|
|
9252
|
+
...next,
|
|
9253
|
+
runId: normalized,
|
|
9254
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9255
|
+
};
|
|
9256
|
+
await writeFlowRunRecord(cwd, updated);
|
|
9257
|
+
return updated;
|
|
9258
|
+
},
|
|
9259
|
+
{ owner: "flow-run:update" }
|
|
9260
|
+
);
|
|
9261
|
+
}
|
|
9262
|
+
|
|
9263
|
+
// src/commands/flow.ts
|
|
9264
|
+
var LONG_RUNNING_DELEGATION_CATEGORIES2 = [
|
|
9265
|
+
"task_execute",
|
|
9266
|
+
"code_review",
|
|
9267
|
+
"review_fix_commit",
|
|
9268
|
+
"pre_pr_review"
|
|
9269
|
+
];
|
|
8977
9270
|
var BUILTIN_AUTO_PRESETS = {
|
|
8978
9271
|
"pr-handoff": ["pr_create", "code_review", "pr_status_update"]
|
|
8979
9272
|
};
|
|
9273
|
+
function shellEscape(arg) {
|
|
9274
|
+
if (/^[A-Za-z0-9_./:@%-]+$/.test(arg)) return arg;
|
|
9275
|
+
return `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
9276
|
+
}
|
|
9277
|
+
function toLeeSpecKitCommand(args) {
|
|
9278
|
+
return ["npx", "lee-spec-kit", ...args].map((arg) => shellEscape(arg)).join(" ");
|
|
9279
|
+
}
|
|
9280
|
+
function buildResumeRunCommand(runId) {
|
|
9281
|
+
return toLeeSpecKitCommand(["flow", "--resume", runId]);
|
|
9282
|
+
}
|
|
9283
|
+
function buildAutoResume(featureName, selectionOptions, untilCategories, requestText, requestPending) {
|
|
9284
|
+
const selectionArgs = buildSelectionArgs(featureName, selectionOptions);
|
|
9285
|
+
const flowArgs = ["flow", ...selectionArgs];
|
|
9286
|
+
if (requestPending && requestText) {
|
|
9287
|
+
flowArgs.push("--request", requestText);
|
|
9288
|
+
}
|
|
9289
|
+
flowArgs.push("--auto-until-category", untilCategories.join(","));
|
|
9290
|
+
const contextArgs = ["context", ...selectionArgs];
|
|
9291
|
+
return {
|
|
9292
|
+
flowArgs,
|
|
9293
|
+
flowCommand: toLeeSpecKitCommand(flowArgs),
|
|
9294
|
+
contextArgs,
|
|
9295
|
+
contextCommand: toLeeSpecKitCommand(contextArgs),
|
|
9296
|
+
requiresFreshContext: true,
|
|
9297
|
+
requestPending
|
|
9298
|
+
};
|
|
9299
|
+
}
|
|
8980
9300
|
function runSelfCli(args) {
|
|
8981
9301
|
const entry = process.argv[1];
|
|
8982
9302
|
const result = spawnSync(process.execPath, [entry, "--no-banner", ...args], {
|
|
@@ -9165,6 +9485,51 @@ function toAutoReasonCode(status) {
|
|
|
9165
9485
|
function isAutoRunFailureStatus(status) {
|
|
9166
9486
|
return status === "manual_required" || status === "selection_required" || status === "no_progress" || status === "request_label_missing" || status === "request_failed" || status === "execution_failed";
|
|
9167
9487
|
}
|
|
9488
|
+
function toFlowRunStatus(status) {
|
|
9489
|
+
switch (status) {
|
|
9490
|
+
case "gate_reached":
|
|
9491
|
+
case "manual_required":
|
|
9492
|
+
return "paused";
|
|
9493
|
+
case "no_action_options":
|
|
9494
|
+
return "completed";
|
|
9495
|
+
case "selection_required":
|
|
9496
|
+
case "no_progress":
|
|
9497
|
+
case "request_label_missing":
|
|
9498
|
+
case "request_failed":
|
|
9499
|
+
case "execution_failed":
|
|
9500
|
+
default:
|
|
9501
|
+
return "failed";
|
|
9502
|
+
}
|
|
9503
|
+
}
|
|
9504
|
+
function buildAgentOrchestrationPolicy2(autoRun) {
|
|
9505
|
+
const preferredResumeCommand = autoRun?.run?.resumeCommand || autoRun?.resume?.flowCommand || null;
|
|
9506
|
+
return {
|
|
9507
|
+
mode: "main_orchestrates_subagent_execution",
|
|
9508
|
+
delegationPolicy: "prefer_main_delegate_long_running_fallback_main",
|
|
9509
|
+
delegateCommandExecution: "long_running_only",
|
|
9510
|
+
delegateAutoRunExecution: true,
|
|
9511
|
+
fallbackToMainAgentWhenSubAgentUnavailable: true,
|
|
9512
|
+
longRunningCategories: [...LONG_RUNNING_DELEGATION_CATEGORIES2],
|
|
9513
|
+
mainAgentResponsibilities: [
|
|
9514
|
+
"Keep user conversation state and approval boundaries",
|
|
9515
|
+
"Run the same execution loop directly when sub-agent is unavailable",
|
|
9516
|
+
"Delegate only long-running command/auto loops to sub-agents",
|
|
9517
|
+
"Report only on approval/manual/error boundaries"
|
|
9518
|
+
],
|
|
9519
|
+
subAgentResponsibilities: [
|
|
9520
|
+
"Run flow/context command loops",
|
|
9521
|
+
"Execute selected atomic command actions",
|
|
9522
|
+
"Return structured status and errors to main agent"
|
|
9523
|
+
],
|
|
9524
|
+
pauseAndReportWhen: [
|
|
9525
|
+
"approvalRequest.required=true",
|
|
9526
|
+
"AUTO_GATE_REACHED",
|
|
9527
|
+
"AUTO_MANUAL_REQUIRED",
|
|
9528
|
+
"command execution error"
|
|
9529
|
+
],
|
|
9530
|
+
preferredResumeCommand
|
|
9531
|
+
};
|
|
9532
|
+
}
|
|
9168
9533
|
async function runAutoUntilCategory(config, featureName, selectionOptions, untilCategories, requestText, metadata) {
|
|
9169
9534
|
const contextArgs = ["context", ...buildSelectionArgs(featureName, selectionOptions)];
|
|
9170
9535
|
const gateSet = new Set(untilCategories);
|
|
@@ -9175,6 +9540,13 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
|
|
|
9175
9540
|
let requestHandled = !requestText;
|
|
9176
9541
|
let iterations = 0;
|
|
9177
9542
|
while (true) {
|
|
9543
|
+
const resume = buildAutoResume(
|
|
9544
|
+
featureName,
|
|
9545
|
+
selectionOptions,
|
|
9546
|
+
untilCategories,
|
|
9547
|
+
requestText,
|
|
9548
|
+
!requestHandled
|
|
9549
|
+
);
|
|
9178
9550
|
iterations += 1;
|
|
9179
9551
|
const state = await resolveContextSelection(config, featureName, selectionOptions);
|
|
9180
9552
|
const actionOptions = state.actionOptions;
|
|
@@ -9200,6 +9572,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
|
|
|
9200
9572
|
request: requestText,
|
|
9201
9573
|
preset: metadata?.preset ?? null,
|
|
9202
9574
|
source: metadata?.source ?? null,
|
|
9575
|
+
resume,
|
|
9203
9576
|
status: "no_progress",
|
|
9204
9577
|
reasonCode: toAutoReasonCode("no_progress"),
|
|
9205
9578
|
iterations,
|
|
@@ -9216,6 +9589,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
|
|
|
9216
9589
|
request: requestText,
|
|
9217
9590
|
preset: metadata?.preset ?? null,
|
|
9218
9591
|
source: metadata?.source ?? null,
|
|
9592
|
+
resume,
|
|
9219
9593
|
status: "selection_required",
|
|
9220
9594
|
reasonCode: toAutoReasonCode("selection_required"),
|
|
9221
9595
|
iterations,
|
|
@@ -9232,6 +9606,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
|
|
|
9232
9606
|
request: requestText,
|
|
9233
9607
|
preset: metadata?.preset ?? null,
|
|
9234
9608
|
source: metadata?.source ?? null,
|
|
9609
|
+
resume,
|
|
9235
9610
|
status: "no_action_options",
|
|
9236
9611
|
reasonCode: toAutoReasonCode("no_action_options"),
|
|
9237
9612
|
iterations,
|
|
@@ -9251,6 +9626,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
|
|
|
9251
9626
|
request: requestText,
|
|
9252
9627
|
preset: metadata?.preset ?? null,
|
|
9253
9628
|
source: metadata?.source ?? null,
|
|
9629
|
+
resume,
|
|
9254
9630
|
status: "request_label_missing",
|
|
9255
9631
|
reasonCode: toAutoReasonCode("request_label_missing"),
|
|
9256
9632
|
iterations,
|
|
@@ -9284,6 +9660,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
|
|
|
9284
9660
|
request: requestText,
|
|
9285
9661
|
preset: metadata?.preset ?? null,
|
|
9286
9662
|
source: metadata?.source ?? null,
|
|
9663
|
+
resume,
|
|
9287
9664
|
status: "request_failed",
|
|
9288
9665
|
reasonCode: toAutoReasonCode("request_failed"),
|
|
9289
9666
|
iterations,
|
|
@@ -9307,6 +9684,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
|
|
|
9307
9684
|
request: requestText,
|
|
9308
9685
|
preset: metadata?.preset ?? null,
|
|
9309
9686
|
source: metadata?.source ?? null,
|
|
9687
|
+
resume,
|
|
9310
9688
|
status: "gate_reached",
|
|
9311
9689
|
reasonCode: toAutoReasonCode("gate_reached"),
|
|
9312
9690
|
iterations,
|
|
@@ -9329,6 +9707,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
|
|
|
9329
9707
|
request: requestText,
|
|
9330
9708
|
preset: metadata?.preset ?? null,
|
|
9331
9709
|
source: metadata?.source ?? null,
|
|
9710
|
+
resume,
|
|
9332
9711
|
status: "manual_required",
|
|
9333
9712
|
reasonCode: toAutoReasonCode("manual_required"),
|
|
9334
9713
|
iterations,
|
|
@@ -9364,6 +9743,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
|
|
|
9364
9743
|
request: requestText,
|
|
9365
9744
|
preset: metadata?.preset ?? null,
|
|
9366
9745
|
source: metadata?.source ?? null,
|
|
9746
|
+
resume,
|
|
9367
9747
|
status: "execution_failed",
|
|
9368
9748
|
reasonCode: toAutoReasonCode("execution_failed"),
|
|
9369
9749
|
iterations,
|
|
@@ -9399,6 +9779,9 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
|
|
|
9399
9779
|
enabled: true,
|
|
9400
9780
|
untilCategories,
|
|
9401
9781
|
request: requestText,
|
|
9782
|
+
preset: metadata?.preset ?? null,
|
|
9783
|
+
source: metadata?.source ?? null,
|
|
9784
|
+
resume,
|
|
9402
9785
|
status: "execution_failed",
|
|
9403
9786
|
reasonCode: toAutoReasonCode("execution_failed"),
|
|
9404
9787
|
iterations,
|
|
@@ -9420,6 +9803,12 @@ function flowCommand(program2) {
|
|
|
9420
9803
|
).option(
|
|
9421
9804
|
"--auto-until-category <categories>",
|
|
9422
9805
|
"Auto-run command actions until one of categories appears (comma-separated)"
|
|
9806
|
+
).option(
|
|
9807
|
+
"--start-auto",
|
|
9808
|
+
"Persist auto-run checkpoint and emit resumable run id in JSON output"
|
|
9809
|
+
).option(
|
|
9810
|
+
"--resume <run-id>",
|
|
9811
|
+
"Resume previously started auto-run checkpoint by run id"
|
|
9423
9812
|
).option(
|
|
9424
9813
|
"--approve <reply>",
|
|
9425
9814
|
"Approve one labeled context option (examples: A, A OK, A proceed, A \uC9C4\uD589\uD574)"
|
|
@@ -9475,14 +9864,79 @@ async function runFlow(featureName, options) {
|
|
|
9475
9864
|
"`--execute` requires `--approve <reply>`."
|
|
9476
9865
|
);
|
|
9477
9866
|
}
|
|
9478
|
-
const
|
|
9867
|
+
const resumeRunId = (options.resume || "").trim() || void 0;
|
|
9868
|
+
if (options.startAuto && resumeRunId) {
|
|
9869
|
+
throw createCliError(
|
|
9870
|
+
"INVALID_ARGUMENT",
|
|
9871
|
+
"`--start-auto` cannot be combined with `--resume`."
|
|
9872
|
+
);
|
|
9873
|
+
}
|
|
9874
|
+
if (resumeRunId) {
|
|
9875
|
+
if (featureName) {
|
|
9876
|
+
throw createCliError(
|
|
9877
|
+
"INVALID_ARGUMENT",
|
|
9878
|
+
"`--resume` cannot be combined with <feature-name>."
|
|
9879
|
+
);
|
|
9880
|
+
}
|
|
9881
|
+
if (options.autoPreset || options.autoUntilCategory || options.request) {
|
|
9882
|
+
throw createCliError(
|
|
9883
|
+
"INVALID_ARGUMENT",
|
|
9884
|
+
"`--resume` cannot be combined with `--auto-*` or `--request`."
|
|
9885
|
+
);
|
|
9886
|
+
}
|
|
9887
|
+
if (options.component || options.all || options.done) {
|
|
9888
|
+
throw createCliError(
|
|
9889
|
+
"INVALID_ARGUMENT",
|
|
9890
|
+
"`--resume` cannot be combined with `--component`, `--all`, or `--done`."
|
|
9891
|
+
);
|
|
9892
|
+
}
|
|
9893
|
+
}
|
|
9894
|
+
let requestText = options.request?.trim() || void 0;
|
|
9479
9895
|
if (options.autoPreset && options.autoUntilCategory) {
|
|
9480
9896
|
throw createCliError(
|
|
9481
9897
|
"INVALID_ARGUMENT",
|
|
9482
9898
|
"`--auto-preset` cannot be combined with `--auto-until-category`."
|
|
9483
9899
|
);
|
|
9484
9900
|
}
|
|
9485
|
-
|
|
9901
|
+
let resolvedFeatureName = featureName;
|
|
9902
|
+
let selectedComponent = resolveComponentOption(options.component);
|
|
9903
|
+
const selectionOptions = {
|
|
9904
|
+
component: selectedComponent,
|
|
9905
|
+
all: options.all,
|
|
9906
|
+
done: options.done
|
|
9907
|
+
};
|
|
9908
|
+
let autoMode = resolveAutoMode(config, options, requestText);
|
|
9909
|
+
let flowRunRecord = null;
|
|
9910
|
+
let flowRunMode = null;
|
|
9911
|
+
if (resumeRunId) {
|
|
9912
|
+
flowRunRecord = await getFlowRunRecord(process.cwd(), resumeRunId);
|
|
9913
|
+
if (flowRunRecord.status === "completed") {
|
|
9914
|
+
throw createCliError(
|
|
9915
|
+
"INVALID_ARGUMENT",
|
|
9916
|
+
`Flow run ${resumeRunId} is already completed.`
|
|
9917
|
+
);
|
|
9918
|
+
}
|
|
9919
|
+
resolvedFeatureName = flowRunRecord.featureName;
|
|
9920
|
+
selectedComponent = resolveComponentOption(flowRunRecord.selection.component);
|
|
9921
|
+
selectionOptions.component = selectedComponent;
|
|
9922
|
+
selectionOptions.all = !!flowRunRecord.selection.all;
|
|
9923
|
+
selectionOptions.done = !!flowRunRecord.selection.done;
|
|
9924
|
+
requestText = flowRunRecord.auto.requestPending ? flowRunRecord.auto.requestText?.trim() || void 0 : void 0;
|
|
9925
|
+
autoMode = {
|
|
9926
|
+
untilCategories: [...flowRunRecord.auto.untilCategories],
|
|
9927
|
+
preset: flowRunRecord.auto.preset ?? null,
|
|
9928
|
+
source: `resume:${resumeRunId}`
|
|
9929
|
+
};
|
|
9930
|
+
flowRunMode = "resumed";
|
|
9931
|
+
flowRunRecord = await updateFlowRunRecord(
|
|
9932
|
+
process.cwd(),
|
|
9933
|
+
resumeRunId,
|
|
9934
|
+
(current) => ({
|
|
9935
|
+
...current,
|
|
9936
|
+
status: "running"
|
|
9937
|
+
})
|
|
9938
|
+
);
|
|
9939
|
+
}
|
|
9486
9940
|
if (autoMode && options.approve) {
|
|
9487
9941
|
throw createCliError(
|
|
9488
9942
|
"INVALID_ARGUMENT",
|
|
@@ -9502,26 +9956,46 @@ async function runFlow(featureName, options) {
|
|
|
9502
9956
|
);
|
|
9503
9957
|
}
|
|
9504
9958
|
if (autoMode && !featureName) {
|
|
9959
|
+
if (!resolvedFeatureName) {
|
|
9960
|
+
throw createCliError(
|
|
9961
|
+
"CONTEXT_SELECTION_REQUIRED",
|
|
9962
|
+
"Auto mode requires explicit <feature-name> (e.g. F004)."
|
|
9963
|
+
);
|
|
9964
|
+
}
|
|
9965
|
+
}
|
|
9966
|
+
if (options.startAuto && !autoMode) {
|
|
9505
9967
|
throw createCliError(
|
|
9506
|
-
"
|
|
9507
|
-
"
|
|
9968
|
+
"INVALID_ARGUMENT",
|
|
9969
|
+
"`--start-auto` requires auto mode (`--auto-until-category` or `--auto-preset`)."
|
|
9508
9970
|
);
|
|
9509
9971
|
}
|
|
9510
|
-
|
|
9511
|
-
|
|
9512
|
-
|
|
9513
|
-
|
|
9514
|
-
|
|
9515
|
-
|
|
9972
|
+
if (options.startAuto && autoMode && !flowRunRecord && resolvedFeatureName) {
|
|
9973
|
+
flowRunRecord = await createFlowRunRecord(process.cwd(), {
|
|
9974
|
+
featureName: resolvedFeatureName,
|
|
9975
|
+
selection: {
|
|
9976
|
+
component: selectedComponent || void 0,
|
|
9977
|
+
all: !!selectionOptions.all,
|
|
9978
|
+
done: !!selectionOptions.done
|
|
9979
|
+
},
|
|
9980
|
+
auto: {
|
|
9981
|
+
untilCategories: [...autoMode.untilCategories],
|
|
9982
|
+
requestText,
|
|
9983
|
+
requestPending: !!requestText,
|
|
9984
|
+
preset: autoMode.preset ?? null,
|
|
9985
|
+
source: autoMode.source
|
|
9986
|
+
}
|
|
9987
|
+
});
|
|
9988
|
+
flowRunMode = "started";
|
|
9989
|
+
}
|
|
9516
9990
|
const componentHint = selectedComponent ? ` --component ${selectedComponent}` : "";
|
|
9517
|
-
const before = await resolveContextSelection(config,
|
|
9991
|
+
const before = await resolveContextSelection(config, resolvedFeatureName, selectionOptions);
|
|
9518
9992
|
let approvalResult = null;
|
|
9519
9993
|
let autoRun = null;
|
|
9520
|
-
const contextArgs = ["context", ...buildSelectionArgs(
|
|
9994
|
+
const contextArgs = ["context", ...buildSelectionArgs(resolvedFeatureName, selectionOptions)];
|
|
9521
9995
|
if (autoMode) {
|
|
9522
9996
|
autoRun = await runAutoUntilCategory(
|
|
9523
9997
|
config,
|
|
9524
|
-
|
|
9998
|
+
resolvedFeatureName,
|
|
9525
9999
|
selectionOptions,
|
|
9526
10000
|
autoMode.untilCategories,
|
|
9527
10001
|
requestText,
|
|
@@ -9553,7 +10027,31 @@ async function runFlow(featureName, options) {
|
|
|
9553
10027
|
approvalResult = selected;
|
|
9554
10028
|
}
|
|
9555
10029
|
}
|
|
9556
|
-
|
|
10030
|
+
if (autoRun && flowRunRecord && flowRunMode) {
|
|
10031
|
+
const runStatus2 = toFlowRunStatus(autoRun.status);
|
|
10032
|
+
flowRunRecord = await updateFlowRunRecord(
|
|
10033
|
+
process.cwd(),
|
|
10034
|
+
flowRunRecord.runId,
|
|
10035
|
+
(current) => ({
|
|
10036
|
+
...current,
|
|
10037
|
+
status: runStatus2,
|
|
10038
|
+
auto: {
|
|
10039
|
+
...current.auto,
|
|
10040
|
+
requestPending: autoRun.resume.requestPending
|
|
10041
|
+
},
|
|
10042
|
+
lastAutoStatus: autoRun.status,
|
|
10043
|
+
lastReasonCode: autoRun.reasonCode,
|
|
10044
|
+
lastError: autoRun.error
|
|
10045
|
+
})
|
|
10046
|
+
);
|
|
10047
|
+
autoRun.run = {
|
|
10048
|
+
runId: flowRunRecord.runId,
|
|
10049
|
+
mode: flowRunMode,
|
|
10050
|
+
status: flowRunRecord.status,
|
|
10051
|
+
resumeCommand: buildResumeRunCommand(flowRunRecord.runId)
|
|
10052
|
+
};
|
|
10053
|
+
}
|
|
10054
|
+
const after = await resolveContextSelection(config, resolvedFeatureName, selectionOptions);
|
|
9557
10055
|
const statusReport = runSelfCliJson(["status"]);
|
|
9558
10056
|
const doctorReport = runSelfCliJson(["doctor"]);
|
|
9559
10057
|
let strictChecks = null;
|
|
@@ -9574,6 +10072,7 @@ async function runFlow(featureName, options) {
|
|
|
9574
10072
|
}
|
|
9575
10073
|
if (options.json) {
|
|
9576
10074
|
const autoRunFailed = !!(autoRun && isAutoRunFailureStatus(autoRun.status));
|
|
10075
|
+
const agentOrchestration2 = buildAgentOrchestrationPolicy2(autoRun);
|
|
9577
10076
|
const payload = {
|
|
9578
10077
|
status: autoRunFailed ? "error" : "ok",
|
|
9579
10078
|
reasonCode: autoRunFailed ? autoRun?.reasonCode || "AUTO_EXECUTION_FAILED" : "FLOW_SUMMARY",
|
|
@@ -9599,6 +10098,7 @@ async function runFlow(featureName, options) {
|
|
|
9599
10098
|
},
|
|
9600
10099
|
approval: approvalResult,
|
|
9601
10100
|
autoRun,
|
|
10101
|
+
agentOrchestration: agentOrchestration2,
|
|
9602
10102
|
statusReport,
|
|
9603
10103
|
doctorReport,
|
|
9604
10104
|
strictChecks,
|
|
@@ -9632,7 +10132,22 @@ async function runFlow(featureName, options) {
|
|
|
9632
10132
|
`- Auto: ${autoRun.status} (${autoRun.reasonCode}), iterations ${autoRun.iterations}, executions ${autoRun.executions.length}${presetSuffix}`
|
|
9633
10133
|
)
|
|
9634
10134
|
);
|
|
10135
|
+
console.log(chalk6.gray(`- Auto resume: ${autoRun.resume.flowCommand}`));
|
|
10136
|
+
if (autoRun.run) {
|
|
10137
|
+
console.log(
|
|
10138
|
+
chalk6.gray(
|
|
10139
|
+
`- Auto run: ${autoRun.run.mode} ${autoRun.run.runId} (${autoRun.run.status})`
|
|
10140
|
+
)
|
|
10141
|
+
);
|
|
10142
|
+
console.log(chalk6.gray(`- Resume with: ${autoRun.run.resumeCommand}`));
|
|
10143
|
+
}
|
|
9635
10144
|
}
|
|
10145
|
+
const agentOrchestration = buildAgentOrchestrationPolicy2(autoRun);
|
|
10146
|
+
console.log(
|
|
10147
|
+
chalk6.gray(
|
|
10148
|
+
`- Orchestration: ${agentOrchestration.mode}, delegate long-running loops to sub-agent`
|
|
10149
|
+
)
|
|
10150
|
+
);
|
|
9636
10151
|
const statusCounts = statusReport.counts;
|
|
9637
10152
|
const doctorCounts = doctorReport.counts;
|
|
9638
10153
|
console.log(chalk6.gray(`- Status features: ${statusCounts?.features ?? 0}`));
|
|
@@ -9780,40 +10295,40 @@ function tg(lang, key, vars = {}) {
|
|
|
9780
10295
|
}
|
|
9781
10296
|
function detectGithubCliLangSync(cwd) {
|
|
9782
10297
|
const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
|
|
9783
|
-
const startDirs = [explicitDocsDir ?
|
|
10298
|
+
const startDirs = [explicitDocsDir ? path23.resolve(explicitDocsDir) : "", path23.resolve(cwd)].filter(Boolean);
|
|
9784
10299
|
const scanOrder = [];
|
|
9785
10300
|
const seen = /* @__PURE__ */ new Set();
|
|
9786
10301
|
for (const start of startDirs) {
|
|
9787
10302
|
let current = start;
|
|
9788
10303
|
while (true) {
|
|
9789
|
-
const abs =
|
|
10304
|
+
const abs = path23.resolve(current);
|
|
9790
10305
|
if (!seen.has(abs)) {
|
|
9791
10306
|
scanOrder.push(abs);
|
|
9792
10307
|
seen.add(abs);
|
|
9793
10308
|
}
|
|
9794
|
-
const parent =
|
|
10309
|
+
const parent = path23.dirname(abs);
|
|
9795
10310
|
if (parent === abs) break;
|
|
9796
10311
|
current = parent;
|
|
9797
10312
|
}
|
|
9798
10313
|
}
|
|
9799
10314
|
for (const base of scanOrder) {
|
|
9800
|
-
for (const docsDir of [
|
|
9801
|
-
const configPath =
|
|
9802
|
-
if (
|
|
10315
|
+
for (const docsDir of [path23.join(base, "docs"), base]) {
|
|
10316
|
+
const configPath = path23.join(docsDir, ".lee-spec-kit.json");
|
|
10317
|
+
if (fs17.existsSync(configPath)) {
|
|
9803
10318
|
try {
|
|
9804
|
-
const parsed =
|
|
10319
|
+
const parsed = fs17.readJsonSync(configPath);
|
|
9805
10320
|
if (parsed?.lang === "ko" || parsed?.lang === "en") return parsed.lang;
|
|
9806
10321
|
} catch {
|
|
9807
10322
|
}
|
|
9808
10323
|
}
|
|
9809
|
-
const agentsPath =
|
|
9810
|
-
const featuresPath =
|
|
9811
|
-
if (!
|
|
10324
|
+
const agentsPath = path23.join(docsDir, "agents");
|
|
10325
|
+
const featuresPath = path23.join(docsDir, "features");
|
|
10326
|
+
if (!fs17.existsSync(agentsPath) || !fs17.existsSync(featuresPath)) continue;
|
|
9812
10327
|
for (const probe of ["custom.md", "constitution.md", "agents.md"]) {
|
|
9813
|
-
const file =
|
|
9814
|
-
if (!
|
|
10328
|
+
const file = path23.join(agentsPath, probe);
|
|
10329
|
+
if (!fs17.existsSync(file)) continue;
|
|
9815
10330
|
try {
|
|
9816
|
-
const content =
|
|
10331
|
+
const content = fs17.readFileSync(file, "utf-8");
|
|
9817
10332
|
if (/[가-힣]/.test(content)) return "ko";
|
|
9818
10333
|
} catch {
|
|
9819
10334
|
}
|
|
@@ -9890,8 +10405,8 @@ async function prepareGithubBody(params) {
|
|
|
9890
10405
|
kindLabel,
|
|
9891
10406
|
lang
|
|
9892
10407
|
} = params;
|
|
9893
|
-
if (create && explicitBodyFile && await
|
|
9894
|
-
const body = await
|
|
10408
|
+
if (create && explicitBodyFile && await fs17.pathExists(defaultBodyFile)) {
|
|
10409
|
+
const body = await fs17.readFile(defaultBodyFile, "utf-8");
|
|
9895
10410
|
ensureSections(body, requiredSections, kindLabel, lang);
|
|
9896
10411
|
return {
|
|
9897
10412
|
body,
|
|
@@ -9899,8 +10414,8 @@ async function prepareGithubBody(params) {
|
|
|
9899
10414
|
source: "explicit"
|
|
9900
10415
|
};
|
|
9901
10416
|
}
|
|
9902
|
-
if (create && !explicitBodyFile && await
|
|
9903
|
-
const body = await
|
|
10417
|
+
if (create && !explicitBodyFile && await fs17.pathExists(workflowDraftPath)) {
|
|
10418
|
+
const body = await fs17.readFile(workflowDraftPath, "utf-8");
|
|
9904
10419
|
const draftMetadata = parseWorkflowDraftMetadata(body);
|
|
9905
10420
|
if (draftMetadata.status === "ready") {
|
|
9906
10421
|
ensureSections(body, requiredSections, kindLabel, lang);
|
|
@@ -9912,8 +10427,8 @@ async function prepareGithubBody(params) {
|
|
|
9912
10427
|
};
|
|
9913
10428
|
}
|
|
9914
10429
|
}
|
|
9915
|
-
await
|
|
9916
|
-
await
|
|
10430
|
+
await fs17.ensureDir(path23.dirname(defaultBodyFile));
|
|
10431
|
+
await fs17.writeFile(defaultBodyFile, generatedBody, "utf-8");
|
|
9917
10432
|
return {
|
|
9918
10433
|
body: generatedBody,
|
|
9919
10434
|
bodyFile: defaultBodyFile,
|
|
@@ -9973,7 +10488,7 @@ function ensureSections(body, sections, kind, lang) {
|
|
|
9973
10488
|
}
|
|
9974
10489
|
function ensureDocsExist(docsDir, relativePaths, lang) {
|
|
9975
10490
|
const missing = relativePaths.filter(
|
|
9976
|
-
(relativePath) => !
|
|
10491
|
+
(relativePath) => !fs17.existsSync(path23.join(docsDir, relativePath))
|
|
9977
10492
|
);
|
|
9978
10493
|
if (missing.length > 0) {
|
|
9979
10494
|
throw createCliError(
|
|
@@ -9983,18 +10498,18 @@ function ensureDocsExist(docsDir, relativePaths, lang) {
|
|
|
9983
10498
|
}
|
|
9984
10499
|
}
|
|
9985
10500
|
function buildDefaultBodyFileName(kind, docsDir, component) {
|
|
9986
|
-
const key = `${
|
|
10501
|
+
const key = `${path23.resolve(docsDir)}::${component.trim().toLowerCase()}`;
|
|
9987
10502
|
const digest = createHash("sha1").update(key).digest("hex").slice(0, 12);
|
|
9988
10503
|
return `lee-spec-kit.${digest}.${kind}.md`;
|
|
9989
10504
|
}
|
|
9990
10505
|
function toBodyFilePath(raw, kind, docsDir, component, lang) {
|
|
9991
|
-
const selected = raw?.trim() ||
|
|
10506
|
+
const selected = raw?.trim() || path23.join(os.tmpdir(), buildDefaultBodyFileName(kind, docsDir, component));
|
|
9992
10507
|
assertValid(
|
|
9993
10508
|
validatePathWithLang(selected, lang),
|
|
9994
10509
|
`github.${kind}.bodyFile`,
|
|
9995
10510
|
lang
|
|
9996
10511
|
);
|
|
9997
|
-
return
|
|
10512
|
+
return path23.resolve(selected);
|
|
9998
10513
|
}
|
|
9999
10514
|
function toProjectRootDocsPath(relativePathFromDocs) {
|
|
10000
10515
|
const normalized = relativePathFromDocs.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
|
|
@@ -10761,10 +11276,10 @@ function insertFieldInGithubIssueSection(content, key, value) {
|
|
|
10761
11276
|
return { content: lines.join("\n"), changed: true };
|
|
10762
11277
|
}
|
|
10763
11278
|
function syncTasksPrMetadata(tasksPath, prUrl, nextStatus, lang) {
|
|
10764
|
-
if (!
|
|
11279
|
+
if (!fs17.existsSync(tasksPath)) {
|
|
10765
11280
|
throw createCliError("DOCS_NOT_FOUND", tg(lang, "tasksNotFound", { path: tasksPath }));
|
|
10766
11281
|
}
|
|
10767
|
-
const original =
|
|
11282
|
+
const original = fs17.readFileSync(tasksPath, "utf-8");
|
|
10768
11283
|
let next = original;
|
|
10769
11284
|
let changed = false;
|
|
10770
11285
|
const prReplaced = replaceListField(next, ["PR", "Pull Request"], prUrl);
|
|
@@ -10788,7 +11303,7 @@ function syncTasksPrMetadata(tasksPath, prUrl, nextStatus, lang) {
|
|
|
10788
11303
|
changed = changed || inserted.changed;
|
|
10789
11304
|
}
|
|
10790
11305
|
if (changed) {
|
|
10791
|
-
|
|
11306
|
+
fs17.writeFileSync(tasksPath, next, "utf-8");
|
|
10792
11307
|
}
|
|
10793
11308
|
return { changed, path: tasksPath };
|
|
10794
11309
|
}
|
|
@@ -10816,7 +11331,7 @@ function ensureCleanWorktree(cwd, lang) {
|
|
|
10816
11331
|
}
|
|
10817
11332
|
}
|
|
10818
11333
|
function commitAndPushPath(cwd, absPath, message, lang, options) {
|
|
10819
|
-
const relativePath =
|
|
11334
|
+
const relativePath = path23.relative(cwd, absPath) || absPath;
|
|
10820
11335
|
const status = runProcessOrThrow(
|
|
10821
11336
|
"git",
|
|
10822
11337
|
["status", "--porcelain=v1", "--", relativePath],
|
|
@@ -10985,9 +11500,9 @@ function githubCommand(program2) {
|
|
|
10985
11500
|
[paths.specPath, paths.planPath, paths.tasksPath],
|
|
10986
11501
|
config.lang
|
|
10987
11502
|
);
|
|
10988
|
-
const specContent = await
|
|
10989
|
-
const planContent = await
|
|
10990
|
-
const tasksContent = await
|
|
11503
|
+
const specContent = await fs17.readFile(path23.join(config.docsDir, paths.specPath), "utf-8");
|
|
11504
|
+
const planContent = await fs17.readFile(path23.join(config.docsDir, paths.planPath), "utf-8");
|
|
11505
|
+
const tasksContent = await fs17.readFile(path23.join(config.docsDir, paths.tasksPath), "utf-8");
|
|
10991
11506
|
const overview = resolveOverviewFromSpec(specContent, feature, config.lang);
|
|
10992
11507
|
const defaultTitle = tg(config.lang, "issueDefaultTitle", {
|
|
10993
11508
|
slug: feature.slug,
|
|
@@ -11020,7 +11535,7 @@ function githubCommand(program2) {
|
|
|
11020
11535
|
create: options.create,
|
|
11021
11536
|
explicitBodyFile,
|
|
11022
11537
|
defaultBodyFile,
|
|
11023
|
-
workflowDraftPath:
|
|
11538
|
+
workflowDraftPath: path23.join(config.docsDir, paths.issuePath),
|
|
11024
11539
|
generatedBody,
|
|
11025
11540
|
requiredSections: getRequiredIssueSections(config.lang),
|
|
11026
11541
|
kindLabel: tg(config.lang, "kindIssue"),
|
|
@@ -11131,10 +11646,10 @@ function githubCommand(program2) {
|
|
|
11131
11646
|
const generatedLabels = parseLabels(optionLabels || void 0, config.lang);
|
|
11132
11647
|
const paths = getFeatureDocPaths(feature);
|
|
11133
11648
|
ensureDocsExist(config.docsDir, [paths.specPath, paths.tasksPath], config.lang);
|
|
11134
|
-
const specContent = await
|
|
11135
|
-
const planPath =
|
|
11136
|
-
const planContent = await
|
|
11137
|
-
const tasksContent = await
|
|
11649
|
+
const specContent = await fs17.readFile(path23.join(config.docsDir, paths.specPath), "utf-8");
|
|
11650
|
+
const planPath = path23.join(config.docsDir, paths.planPath);
|
|
11651
|
+
const planContent = await fs17.pathExists(planPath) ? await fs17.readFile(planPath, "utf-8") : "";
|
|
11652
|
+
const tasksContent = await fs17.readFile(path23.join(config.docsDir, paths.tasksPath), "utf-8");
|
|
11138
11653
|
const overview = resolveOverviewFromSpec(specContent, feature, config.lang);
|
|
11139
11654
|
const defaultTitle = feature.issueNumber ? tg(config.lang, "prDefaultTitleWithIssue", {
|
|
11140
11655
|
issue: feature.issueNumber,
|
|
@@ -11173,7 +11688,7 @@ function githubCommand(program2) {
|
|
|
11173
11688
|
create: options.create,
|
|
11174
11689
|
explicitBodyFile,
|
|
11175
11690
|
defaultBodyFile,
|
|
11176
|
-
workflowDraftPath:
|
|
11691
|
+
workflowDraftPath: path23.join(config.docsDir, paths.prPath),
|
|
11177
11692
|
generatedBody,
|
|
11178
11693
|
requiredSections: getRequiredPrSections(config.lang),
|
|
11179
11694
|
kindLabel: tg(config.lang, "kindPr"),
|
|
@@ -11244,7 +11759,7 @@ function githubCommand(program2) {
|
|
|
11244
11759
|
}
|
|
11245
11760
|
if (prUrl && options.syncTasks !== false) {
|
|
11246
11761
|
const synced = syncTasksPrMetadata(
|
|
11247
|
-
|
|
11762
|
+
path23.join(config.docsDir, paths.tasksPath),
|
|
11248
11763
|
prUrl,
|
|
11249
11764
|
"Review",
|
|
11250
11765
|
config.lang
|
|
@@ -11280,7 +11795,7 @@ function githubCommand(program2) {
|
|
|
11280
11795
|
mergeAlreadyMerged = merged.alreadyMerged;
|
|
11281
11796
|
if (prUrl && options.syncTasks !== false) {
|
|
11282
11797
|
const mergedSync = syncTasksPrMetadata(
|
|
11283
|
-
|
|
11798
|
+
path23.join(config.docsDir, paths.tasksPath),
|
|
11284
11799
|
prUrl,
|
|
11285
11800
|
"Approved",
|
|
11286
11801
|
config.lang
|
|
@@ -11524,7 +12039,7 @@ function docsCommand(program2) {
|
|
|
11524
12039
|
);
|
|
11525
12040
|
return;
|
|
11526
12041
|
}
|
|
11527
|
-
const relativeFromCwd =
|
|
12042
|
+
const relativeFromCwd = path23.relative(process.cwd(), loaded.entry.absolutePath);
|
|
11528
12043
|
console.log();
|
|
11529
12044
|
console.log(chalk6.bold(`\u{1F4C4} ${loaded.entry.id}: ${loaded.entry.title}`));
|
|
11530
12045
|
console.log(
|
|
@@ -11604,7 +12119,7 @@ function detectCommand(program2) {
|
|
|
11604
12119
|
}
|
|
11605
12120
|
async function runDetect(options) {
|
|
11606
12121
|
const cwd = process.cwd();
|
|
11607
|
-
const targetCwd = options.dir ?
|
|
12122
|
+
const targetCwd = options.dir ? path23.resolve(cwd, options.dir) : cwd;
|
|
11608
12123
|
const config = await getConfig(targetCwd);
|
|
11609
12124
|
const detected = !!config;
|
|
11610
12125
|
const reasonCode = detected ? "PROJECT_DETECTED" : "PROJECT_NOT_DETECTED";
|
|
@@ -11631,8 +12146,8 @@ async function runDetect(options) {
|
|
|
11631
12146
|
);
|
|
11632
12147
|
return;
|
|
11633
12148
|
}
|
|
11634
|
-
const configPath2 =
|
|
11635
|
-
const configFilePresent2 = await
|
|
12149
|
+
const configPath2 = path23.join(config.docsDir, ".lee-spec-kit.json");
|
|
12150
|
+
const configFilePresent2 = await fs17.pathExists(configPath2);
|
|
11636
12151
|
const detectionSource2 = configFilePresent2 ? "config" : "heuristic";
|
|
11637
12152
|
console.log(
|
|
11638
12153
|
JSON.stringify(
|
|
@@ -11665,8 +12180,8 @@ async function runDetect(options) {
|
|
|
11665
12180
|
console.log();
|
|
11666
12181
|
return;
|
|
11667
12182
|
}
|
|
11668
|
-
const configPath =
|
|
11669
|
-
const configFilePresent = await
|
|
12183
|
+
const configPath = path23.join(config.docsDir, ".lee-spec-kit.json");
|
|
12184
|
+
const configFilePresent = await fs17.pathExists(configPath);
|
|
11670
12185
|
const detectionSource = configFilePresent ? "config" : "heuristic";
|
|
11671
12186
|
console.log(chalk6.green(`- ${tr(lang, "cli", "detect.resultDetected")}`));
|
|
11672
12187
|
console.log(chalk6.gray(`- ${tr(lang, "cli", "detect.labelDocsDir")}: ${config.docsDir}`));
|
|
@@ -11742,28 +12257,28 @@ function hasTemplateMarkers(content) {
|
|
|
11742
12257
|
return patterns.some((pattern) => pattern.test(content));
|
|
11743
12258
|
}
|
|
11744
12259
|
async function countFeatureDirs(docsDir, projectType) {
|
|
11745
|
-
const featuresRoot =
|
|
12260
|
+
const featuresRoot = path23.join(docsDir, "features");
|
|
11746
12261
|
if (projectType === "single") {
|
|
11747
12262
|
const dirs = await listSubdirectories(featuresRoot);
|
|
11748
|
-
return dirs.filter((value) =>
|
|
12263
|
+
return dirs.filter((value) => path23.basename(value) !== "feature-base").length;
|
|
11749
12264
|
}
|
|
11750
12265
|
const components = await listSubdirectories(featuresRoot);
|
|
11751
12266
|
let total = 0;
|
|
11752
12267
|
for (const componentDir of components) {
|
|
11753
|
-
const componentName =
|
|
12268
|
+
const componentName = path23.basename(componentDir).trim().toLowerCase();
|
|
11754
12269
|
if (!componentName || componentName === "feature-base") continue;
|
|
11755
12270
|
const dirs = await listSubdirectories(componentDir);
|
|
11756
|
-
total += dirs.filter((value) =>
|
|
12271
|
+
total += dirs.filter((value) => path23.basename(value) !== "feature-base").length;
|
|
11757
12272
|
}
|
|
11758
12273
|
return total;
|
|
11759
12274
|
}
|
|
11760
12275
|
async function hasUserPrdFile(prdDir) {
|
|
11761
|
-
if (!await
|
|
12276
|
+
if (!await fs17.pathExists(prdDir)) return false;
|
|
11762
12277
|
const files = await walkFiles(prdDir, {
|
|
11763
12278
|
extensions: [".md"],
|
|
11764
12279
|
ignoreDirs: ["node_modules"]
|
|
11765
12280
|
});
|
|
11766
|
-
return files.some((absolutePath) =>
|
|
12281
|
+
return files.some((absolutePath) => path23.basename(absolutePath).toLowerCase() !== "readme.md");
|
|
11767
12282
|
}
|
|
11768
12283
|
function finalizeChecks(checks) {
|
|
11769
12284
|
const summary = checks.reduce(
|
|
@@ -11892,8 +12407,8 @@ async function runOnboardChecks(config) {
|
|
|
11892
12407
|
});
|
|
11893
12408
|
}
|
|
11894
12409
|
}
|
|
11895
|
-
const constitutionPath =
|
|
11896
|
-
if (!await
|
|
12410
|
+
const constitutionPath = path23.join(docsDir, "agents", "constitution.md");
|
|
12411
|
+
if (!await fs17.pathExists(constitutionPath)) {
|
|
11897
12412
|
checks.push({
|
|
11898
12413
|
id: "constitution_exists",
|
|
11899
12414
|
status: "block",
|
|
@@ -11907,7 +12422,7 @@ async function runOnboardChecks(config) {
|
|
|
11907
12422
|
suggestedCommand: `npx lee-spec-kit update --agents`
|
|
11908
12423
|
});
|
|
11909
12424
|
} else {
|
|
11910
|
-
const content = await
|
|
12425
|
+
const content = await fs17.readFile(constitutionPath, "utf-8");
|
|
11911
12426
|
if (hasTemplateMarkers(content)) {
|
|
11912
12427
|
checks.push({
|
|
11913
12428
|
id: "constitution_filled",
|
|
@@ -11930,9 +12445,9 @@ async function runOnboardChecks(config) {
|
|
|
11930
12445
|
});
|
|
11931
12446
|
}
|
|
11932
12447
|
}
|
|
11933
|
-
const customPath =
|
|
11934
|
-
if (await
|
|
11935
|
-
const content = await
|
|
12448
|
+
const customPath = path23.join(docsDir, "agents", "custom.md");
|
|
12449
|
+
if (await fs17.pathExists(customPath)) {
|
|
12450
|
+
const content = await fs17.readFile(customPath, "utf-8");
|
|
11936
12451
|
if (hasTemplateMarkers(content)) {
|
|
11937
12452
|
checks.push({
|
|
11938
12453
|
id: "custom_optional",
|
|
@@ -11955,7 +12470,7 @@ async function runOnboardChecks(config) {
|
|
|
11955
12470
|
});
|
|
11956
12471
|
}
|
|
11957
12472
|
}
|
|
11958
|
-
const prdDir =
|
|
12473
|
+
const prdDir = path23.join(docsDir, "prd");
|
|
11959
12474
|
const featureCount = await countFeatureDirs(docsDir, config.projectType);
|
|
11960
12475
|
const prdReady = await hasUserPrdFile(prdDir);
|
|
11961
12476
|
if (!prdReady) {
|
|
@@ -11973,7 +12488,7 @@ async function runOnboardChecks(config) {
|
|
|
11973
12488
|
"PRD is empty. If features already exist, fill PRD as soon as possible."
|
|
11974
12489
|
),
|
|
11975
12490
|
path: prdDir,
|
|
11976
|
-
suggestedCommand: `touch ${quotePath(
|
|
12491
|
+
suggestedCommand: `touch ${quotePath(path23.join(prdDir, `${toSlug(config.projectName || "project")}-prd.md`))}`
|
|
11977
12492
|
});
|
|
11978
12493
|
} else {
|
|
11979
12494
|
checks.push({
|
|
@@ -12157,17 +12672,6 @@ function normalizeDecision(raw) {
|
|
|
12157
12672
|
if (value === "blocked" || value === "block") return "blocked";
|
|
12158
12673
|
return null;
|
|
12159
12674
|
}
|
|
12160
|
-
function parseNonNegativeInt(raw, label) {
|
|
12161
|
-
if (raw === void 0) return null;
|
|
12162
|
-
const value = Number(raw);
|
|
12163
|
-
if (!Number.isInteger(value) || value < 0) {
|
|
12164
|
-
throw createCliError(
|
|
12165
|
-
"INVALID_ARGUMENT",
|
|
12166
|
-
`\`${label}\` must be a non-negative integer.`
|
|
12167
|
-
);
|
|
12168
|
-
}
|
|
12169
|
-
return value;
|
|
12170
|
-
}
|
|
12171
12675
|
function findSpecLineIndex(lines, keys) {
|
|
12172
12676
|
const escaped = keys.map((key) => escapeRegExp4(key));
|
|
12173
12677
|
const re = new RegExp(`^\\s*-\\s*\\*\\*(?:${escaped.join("|")})\\*\\*\\s*:\\s*`);
|
|
@@ -12214,7 +12718,6 @@ function getPreferredKeys(lang) {
|
|
|
12214
12718
|
if (lang === "ko") {
|
|
12215
12719
|
return {
|
|
12216
12720
|
review: "PR \uC804 \uB9AC\uBDF0",
|
|
12217
|
-
findings: "PR \uC804 \uB9AC\uBDF0 Findings",
|
|
12218
12721
|
evidence: "PR \uC804 \uB9AC\uBDF0 Evidence",
|
|
12219
12722
|
decision: "PR \uC804 \uB9AC\uBDF0 Decision",
|
|
12220
12723
|
prStatus: "PR \uC0C1\uD0DC"
|
|
@@ -12222,7 +12725,6 @@ function getPreferredKeys(lang) {
|
|
|
12222
12725
|
}
|
|
12223
12726
|
return {
|
|
12224
12727
|
review: "Pre-PR Review",
|
|
12225
|
-
findings: "Pre-PR Findings",
|
|
12226
12728
|
evidence: "Pre-PR Evidence",
|
|
12227
12729
|
decision: "Pre-PR Decision",
|
|
12228
12730
|
prStatus: "PR Status"
|
|
@@ -12230,28 +12732,30 @@ function getPreferredKeys(lang) {
|
|
|
12230
12732
|
}
|
|
12231
12733
|
function buildReportContent(input) {
|
|
12232
12734
|
const skills = input.skills.length > 0 ? input.skills.join(", ") : "code-review-excellence";
|
|
12233
|
-
return
|
|
12735
|
+
return `## Pre-PR Review Log (${input.date})
|
|
12234
12736
|
|
|
12235
|
-
-
|
|
12236
|
-
- Baseline
|
|
12237
|
-
- Skills
|
|
12238
|
-
-
|
|
12239
|
-
-
|
|
12240
|
-
-
|
|
12241
|
-
|
|
12242
|
-
|
|
12737
|
+
- **Feature**: ${input.folderName}
|
|
12738
|
+
- **Baseline**: ${input.fallback}
|
|
12739
|
+
- **Skills**: ${skills}
|
|
12740
|
+
- **Decision**: ${input.decision}
|
|
12741
|
+
- **Note**: ${input.note}
|
|
12742
|
+
- **Trace**: pre-pr-review command executed and synced with tasks.md
|
|
12743
|
+
`;
|
|
12744
|
+
}
|
|
12745
|
+
function appendDecisionLog(content, entry) {
|
|
12746
|
+
const normalized = content.trimEnd();
|
|
12747
|
+
if (!normalized) return `${entry.trim()}
|
|
12748
|
+
`;
|
|
12749
|
+
return `${normalized}
|
|
12243
12750
|
|
|
12244
|
-
|
|
12245
|
-
- [x] Reviewed risk areas (regression, security, side effects, release readiness).
|
|
12246
|
-
- [x] Reviewed maintainability (structure, reuse, obsolete code cleanup).
|
|
12247
|
-
- [x] Reviewed test/verification coverage (or recorded reason if not run).
|
|
12751
|
+
${entry.trim()}
|
|
12248
12752
|
`;
|
|
12249
12753
|
}
|
|
12250
12754
|
function prePrReviewCommand(program2) {
|
|
12251
12755
|
program2.command("pre-pr-review [feature-name]").description("Run and record pre-PR review evidence for a feature").option("--component <component>", "Component name for multi projects").option(
|
|
12252
12756
|
"--decision <outcome>",
|
|
12253
12757
|
"Decision outcome: approve | changes_requested | blocked"
|
|
12254
|
-
).option("--
|
|
12758
|
+
).option("--note <text>", "Decision note text").option("--json", "Output JSON").action(async (featureName, options) => {
|
|
12255
12759
|
try {
|
|
12256
12760
|
await runPrePrReview(featureName, options);
|
|
12257
12761
|
} catch (error) {
|
|
@@ -12301,13 +12805,11 @@ async function runPrePrReview(featureName, options) {
|
|
|
12301
12805
|
`tasks.md not found for feature: ${feature.folderName}`
|
|
12302
12806
|
);
|
|
12303
12807
|
}
|
|
12304
|
-
const tasksPath =
|
|
12305
|
-
const tasksContent = await
|
|
12808
|
+
const tasksPath = path23.join(feature.path, "tasks.md");
|
|
12809
|
+
const tasksContent = await fs17.readFile(tasksPath, "utf-8");
|
|
12306
12810
|
const policy = resolvePrePrReviewPolicy(config.workflow);
|
|
12307
12811
|
const preferred = getPreferredKeys(config.lang);
|
|
12308
12812
|
const date = getLocalDateString();
|
|
12309
|
-
const major = parseNonNegativeInt(options.major, "--major") ?? feature.prePrReview.findings?.major ?? 0;
|
|
12310
|
-
const minor = parseNonNegativeInt(options.minor, "--minor") ?? feature.prePrReview.findings?.minor ?? 0;
|
|
12311
12813
|
const explicitDecision = normalizeDecision(options.decision);
|
|
12312
12814
|
if (options.decision && !explicitDecision) {
|
|
12313
12815
|
throw createCliError(
|
|
@@ -12315,31 +12817,32 @@ async function runPrePrReview(featureName, options) {
|
|
|
12315
12817
|
"`--decision` must be one of: approve, changes_requested, blocked."
|
|
12316
12818
|
);
|
|
12317
12819
|
}
|
|
12318
|
-
const
|
|
12319
|
-
const decision = explicitDecision || inferredDecision;
|
|
12820
|
+
const decision = explicitDecision || feature.prePrReview.decisionOutcome || "approve";
|
|
12320
12821
|
if (!policy.decisionEnum.includes(decision)) {
|
|
12321
12822
|
throw createCliError(
|
|
12322
12823
|
"INVALID_ARGUMENT",
|
|
12323
12824
|
`Decision "${decision}" is not allowed by workflow.prePrReview.decisionEnum.`
|
|
12324
12825
|
);
|
|
12325
12826
|
}
|
|
12326
|
-
const note = options.note?.trim() || (decision === "approve" ? "baseline review completed
|
|
12327
|
-
const
|
|
12328
|
-
const
|
|
12827
|
+
const note = options.note?.trim() || (decision === "approve" ? "baseline review completed" : decision === "changes_requested" ? "follow-up changes are required before PR creation" : "blocked until prerequisite risk is resolved");
|
|
12828
|
+
const decisionsPath = path23.join(feature.path, "decisions.md");
|
|
12829
|
+
const decisionLogEntry = buildReportContent({
|
|
12329
12830
|
folderName: feature.folderName,
|
|
12330
12831
|
date,
|
|
12331
12832
|
decision,
|
|
12332
|
-
major,
|
|
12333
|
-
minor,
|
|
12334
12833
|
note,
|
|
12335
12834
|
fallback: policy.fallback,
|
|
12336
12835
|
skills: policy.skills
|
|
12337
12836
|
});
|
|
12338
|
-
await
|
|
12339
|
-
const
|
|
12340
|
-
|
|
12837
|
+
const decisionsContent = await fs17.pathExists(decisionsPath) ? await fs17.readFile(decisionsPath, "utf-8") : "";
|
|
12838
|
+
const nextDecisions = appendDecisionLog(decisionsContent, decisionLogEntry);
|
|
12839
|
+
if (nextDecisions !== decisionsContent) {
|
|
12840
|
+
await fs17.writeFile(decisionsPath, nextDecisions, "utf-8");
|
|
12841
|
+
}
|
|
12842
|
+
const decisionsPathFromDocs = normalizePathForDoc(
|
|
12843
|
+
path23.join(feature.docs.featurePathFromDocs, "decisions.md")
|
|
12341
12844
|
);
|
|
12342
|
-
const evidencePath =
|
|
12845
|
+
const evidencePath = path23.basename(config.docsDir) === "docs" ? normalizePathForDoc(path23.join("docs", decisionsPathFromDocs)) : decisionsPathFromDocs;
|
|
12343
12846
|
let nextTasks = tasksContent;
|
|
12344
12847
|
nextTasks = upsertSpecLine(
|
|
12345
12848
|
nextTasks,
|
|
@@ -12348,19 +12851,12 @@ async function runPrePrReview(featureName, options) {
|
|
|
12348
12851
|
"Done",
|
|
12349
12852
|
["PR \uC0C1\uD0DC", "PR Status"]
|
|
12350
12853
|
);
|
|
12351
|
-
nextTasks = upsertSpecLine(
|
|
12352
|
-
nextTasks,
|
|
12353
|
-
["PR \uC804 \uB9AC\uBDF0 Findings", "Pre-PR Findings"],
|
|
12354
|
-
preferred.findings,
|
|
12355
|
-
`major=${major}, minor=${minor}`,
|
|
12356
|
-
["PR \uC804 \uB9AC\uBDF0", "Pre-PR Review"]
|
|
12357
|
-
);
|
|
12358
12854
|
nextTasks = upsertSpecLine(
|
|
12359
12855
|
nextTasks,
|
|
12360
12856
|
["PR \uC804 \uB9AC\uBDF0 Evidence", "Pre-PR Evidence"],
|
|
12361
12857
|
preferred.evidence,
|
|
12362
12858
|
evidencePath,
|
|
12363
|
-
["PR \uC804 \uB9AC\uBDF0
|
|
12859
|
+
["PR \uC804 \uB9AC\uBDF0", "Pre-PR Review"]
|
|
12364
12860
|
);
|
|
12365
12861
|
nextTasks = upsertSpecLine(
|
|
12366
12862
|
nextTasks,
|
|
@@ -12370,7 +12866,7 @@ async function runPrePrReview(featureName, options) {
|
|
|
12370
12866
|
["PR \uC804 \uB9AC\uBDF0 Evidence", "Pre-PR Evidence"]
|
|
12371
12867
|
);
|
|
12372
12868
|
if (nextTasks !== tasksContent) {
|
|
12373
|
-
await
|
|
12869
|
+
await fs17.writeFile(tasksPath, nextTasks, "utf-8");
|
|
12374
12870
|
}
|
|
12375
12871
|
if (options.json) {
|
|
12376
12872
|
console.log(
|
|
@@ -12379,10 +12875,10 @@ async function runPrePrReview(featureName, options) {
|
|
|
12379
12875
|
status: "ok",
|
|
12380
12876
|
reasonCode: "PRE_PR_REVIEW_RECORDED",
|
|
12381
12877
|
feature: feature.folderName,
|
|
12382
|
-
reportPath: normalizePathForDoc(
|
|
12878
|
+
reportPath: normalizePathForDoc(decisionsPath),
|
|
12879
|
+
decisionsPath: normalizePathForDoc(decisionsPath),
|
|
12383
12880
|
evidencePath,
|
|
12384
12881
|
decision,
|
|
12385
|
-
findings: { major, minor },
|
|
12386
12882
|
tasksUpdated: nextTasks !== tasksContent
|
|
12387
12883
|
},
|
|
12388
12884
|
null,
|
|
@@ -12394,8 +12890,7 @@ async function runPrePrReview(featureName, options) {
|
|
|
12394
12890
|
console.log();
|
|
12395
12891
|
console.log(chalk6.green(`\u2705 pre-pr-review completed: ${feature.folderName}`));
|
|
12396
12892
|
console.log(chalk6.gray(`- Decision: ${decision}`));
|
|
12397
|
-
console.log(chalk6.gray(`-
|
|
12398
|
-
console.log(chalk6.gray(`- Report: ${reportPath}`));
|
|
12893
|
+
console.log(chalk6.gray(`- Decisions log: ${decisionsPath}`));
|
|
12399
12894
|
if (nextTasks !== tasksContent) {
|
|
12400
12895
|
console.log(chalk6.gray(`- tasks.md updated: ${tasksPath}`));
|
|
12401
12896
|
}
|
|
@@ -12444,13 +12939,13 @@ ${version}
|
|
|
12444
12939
|
}
|
|
12445
12940
|
return `${ascii}${footer}`;
|
|
12446
12941
|
}
|
|
12447
|
-
var CACHE_FILE =
|
|
12942
|
+
var CACHE_FILE = path23.join(os.homedir(), ".lee-spec-kit-version-cache.json");
|
|
12448
12943
|
var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
|
|
12449
12944
|
function getCurrentVersion() {
|
|
12450
12945
|
try {
|
|
12451
|
-
const packageJsonPath =
|
|
12452
|
-
if (
|
|
12453
|
-
const pkg =
|
|
12946
|
+
const packageJsonPath = path23.join(__dirname$1, "..", "package.json");
|
|
12947
|
+
if (fs17.existsSync(packageJsonPath)) {
|
|
12948
|
+
const pkg = fs17.readJsonSync(packageJsonPath);
|
|
12454
12949
|
return pkg.version;
|
|
12455
12950
|
}
|
|
12456
12951
|
} catch {
|
|
@@ -12459,8 +12954,8 @@ function getCurrentVersion() {
|
|
|
12459
12954
|
}
|
|
12460
12955
|
function readCache() {
|
|
12461
12956
|
try {
|
|
12462
|
-
if (
|
|
12463
|
-
return
|
|
12957
|
+
if (fs17.existsSync(CACHE_FILE)) {
|
|
12958
|
+
return fs17.readJsonSync(CACHE_FILE);
|
|
12464
12959
|
}
|
|
12465
12960
|
} catch {
|
|
12466
12961
|
}
|
|
@@ -12552,9 +13047,9 @@ function shouldCheckForUpdates() {
|
|
|
12552
13047
|
if (shouldCheckForUpdates()) checkForUpdates();
|
|
12553
13048
|
function getCliVersion() {
|
|
12554
13049
|
try {
|
|
12555
|
-
const packageJsonPath =
|
|
12556
|
-
if (
|
|
12557
|
-
const pkg =
|
|
13050
|
+
const packageJsonPath = path23.join(__dirname$1, "..", "package.json");
|
|
13051
|
+
if (fs17.existsSync(packageJsonPath)) {
|
|
13052
|
+
const pkg = fs17.readJsonSync(packageJsonPath);
|
|
12558
13053
|
if (pkg?.version) return String(pkg.version);
|
|
12559
13054
|
}
|
|
12560
13055
|
} catch {
|