lee-spec-kit 0.4.3 → 0.4.4
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.md +2 -2
- package/dist/index.js +727 -328
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -43,6 +43,368 @@ function getTemplatesDir() {
|
|
|
43
43
|
return path4.join(rootDir, "templates");
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// src/utils/i18n.ts
|
|
47
|
+
var DEFAULT_LANG = "en";
|
|
48
|
+
function normalizeLang(lang) {
|
|
49
|
+
if (lang === "ko" || lang === "en") return lang;
|
|
50
|
+
return DEFAULT_LANG;
|
|
51
|
+
}
|
|
52
|
+
function formatTemplate(template, vars) {
|
|
53
|
+
return template.replace(/\{(\w+)\}/g, (_, key) => {
|
|
54
|
+
const value = vars[key];
|
|
55
|
+
return value === void 0 ? `{${key}}` : String(value);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
var I18N = {
|
|
59
|
+
ko: {
|
|
60
|
+
cli: {
|
|
61
|
+
"common.errorLabel": "\uC624\uB958:",
|
|
62
|
+
"common.canceled": "\uC791\uC5C5\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
|
|
63
|
+
"common.configNotFound": "\uC124\uC815 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 init\uC744 \uC2E4\uD589\uD574\uC8FC\uC138\uC694.",
|
|
64
|
+
"common.docsNotFound": "docs \uD3F4\uB354\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 init\uC744 \uC2E4\uD589\uD558\uC138\uC694.",
|
|
65
|
+
"status.noFeatures": "Feature\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
66
|
+
"status.duplicateIds": "\uC911\uBCF5 Feature ID \uBC1C\uACAC:",
|
|
67
|
+
"status.missingIds": "Feature ID\uAC00 \uC5C6\uB294 \uD56D\uBAA9:",
|
|
68
|
+
"status.wrote": "\u2705 {path} \uC0DD\uC131 \uC644\uB8CC",
|
|
69
|
+
"feature.selectRepo": "\uB808\uD3EC\uC9C0\uD1A0\uB9AC\uB97C \uC120\uD0DD\uD558\uC138\uC694:",
|
|
70
|
+
"feature.folderExists": "\uC774\uBBF8 \uC874\uC7AC\uD558\uB294 \uD3F4\uB354\uC785\uB2C8\uB2E4: {path}",
|
|
71
|
+
"feature.baseNotFound": "feature-base \uD15C\uD50C\uB9BF\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
72
|
+
"feature.created": "\u2705 Feature \uD3F4\uB354 \uC0DD\uC131 \uC644\uB8CC: {path}",
|
|
73
|
+
"feature.nextStepsTitle": "\uB2E4\uC74C \uB2E8\uACC4:",
|
|
74
|
+
"feature.nextSteps1": " 1. {path}/spec.md \uC791\uC131",
|
|
75
|
+
"feature.nextSteps2": " 2. \uC0AC\uC6A9\uC790 \uB9AC\uBDF0 \uC694\uCCAD",
|
|
76
|
+
"feature.nextSteps3": " 3. \uC2B9\uC778 \uD6C4 plan.md \uC791\uC131",
|
|
77
|
+
"config.currentTitle": "\u{1F4CB} \uD604\uC7AC \uC124\uC815:",
|
|
78
|
+
"config.pathLabel": "\uACBD\uB85C",
|
|
79
|
+
"config.projectRootStandaloneOnly": "\u26A0\uFE0F projectRoot\uB294 standalone \uBAA8\uB4DC\uC5D0\uC11C\uB9CC \uC124\uC815 \uAC00\uB2A5\uD569\uB2C8\uB2E4.",
|
|
80
|
+
"config.selectRepoToUpdate": "\uC218\uC815\uD560 \uB808\uD3EC\uC9C0\uD1A0\uB9AC\uB97C \uC120\uD0DD\uD558\uC138\uC694:",
|
|
81
|
+
"config.fullstackRepoRequired": "Fullstack \uD504\uB85C\uC81D\uD2B8\uB294 --repo fe \uB610\uB294 --repo be\uB97C \uC9C0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4.",
|
|
82
|
+
"config.projectRootSet": "\u2705 {repo} projectRoot \uC124\uC815 \uC644\uB8CC: {path}",
|
|
83
|
+
"config.projectRootSetSingle": "\u2705 projectRoot \uC124\uC815 \uC644\uB8CC: {path}",
|
|
84
|
+
"update.start": "\u{1F4E6} \uD15C\uD50C\uB9BF \uC5C5\uB370\uC774\uD2B8\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...",
|
|
85
|
+
"update.langLabel": "\uC5B8\uC5B4",
|
|
86
|
+
"update.typeLabel": "\uD0C0\uC785",
|
|
87
|
+
"update.updatingAgents": "\u{1F4C1} agents/ \uD3F4\uB354 \uC5C5\uB370\uC774\uD2B8 \uC911...",
|
|
88
|
+
"update.agentsUpdated": "agents/ \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC",
|
|
89
|
+
"update.updatingFeatureBase": "\u{1F4C1} features/feature-base/ \uD3F4\uB354 \uC5C5\uB370\uC774\uD2B8 \uC911...",
|
|
90
|
+
"update.filesUpdated": "{count}\uAC1C \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC",
|
|
91
|
+
"update.updatedTotal": "\uCD1D {count}\uAC1C \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC!",
|
|
92
|
+
"update.changeDetected": "\uBCC0\uACBD \uAC10\uC9C0 (--force\uB85C \uB36E\uC5B4\uC4F0\uAE30)",
|
|
93
|
+
"update.fileUpdated": "{file} \uC5C5\uB370\uC774\uD2B8",
|
|
94
|
+
"doctor.title": "\u{1F50E} \uBB38\uC11C \uC9C4\uB2E8",
|
|
95
|
+
"doctor.envWarnings": "\u26A0\uFE0F \uD658\uACBD \uACBD\uACE0:",
|
|
96
|
+
"doctor.noIssues": "\u2705 \uBB38\uC81C\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
97
|
+
"doctor.errorsTitle": "\uC624\uB958",
|
|
98
|
+
"doctor.warningsTitle": "\uACBD\uACE0",
|
|
99
|
+
"doctor.tipJson": "Tip: \uC5D0\uC774\uC804\uD2B8\uC6A9 JSON \uCD9C\uB825: npx lee-spec-kit doctor --json{strictFlag}",
|
|
100
|
+
"doctor.issue.missingRequiredDir": "\uD544\uC218 \uD3F4\uB354\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4: {dir}",
|
|
101
|
+
"doctor.issue.missingConfig": "\uC124\uC815 \uD30C\uC77C(.lee-spec-kit.json)\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC77C\uBD80 \uAE30\uB2A5\uC774 \uD3F4\uB354 \uAD6C\uC870 \uCD94\uC815\uC73C\uB85C \uB3D9\uC791\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
102
|
+
"doctor.issue.noFeatures": "Feature \uD3F4\uB354\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. (feature-base\uB9CC \uC874\uC7AC\uD558\uAC70\uB098 \uC544\uC9C1 feature\uB97C \uB9CC\uB4E4\uC9C0 \uC54A\uC558\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.)",
|
|
103
|
+
"doctor.issue.placeholdersLeft": "\uD50C\uB808\uC774\uC2A4\uD640\uB354\uAC00 \uB0A8\uC544\uC788\uC2B5\uB2C8\uB2E4: {placeholders}",
|
|
104
|
+
"doctor.issue.missingSpec": "spec.md\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
105
|
+
"doctor.issue.specStatusUnset": "spec.md\uC758 Status(\uC0C1\uD0DC)\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (\uD15C\uD50C\uB9BF \uADF8\uB300\uB85C\uC77C \uC218 \uC788\uC74C)",
|
|
106
|
+
"doctor.issue.planStatusUnset": "plan.md\uC758 Status(\uC0C1\uD0DC)\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (\uD15C\uD50C\uB9BF \uADF8\uB300\uB85C\uC77C \uC218 \uC788\uC74C)",
|
|
107
|
+
"doctor.issue.tasksEmpty": "tasks.md\uC5D0 \uD0DC\uC2A4\uD06C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
108
|
+
"doctor.issue.duplicateFeatureId": "\uC911\uBCF5 Feature ID \uAC10\uC9C0: {id} ({count}\uAC1C)",
|
|
109
|
+
"doctor.issue.missingFeatureId": "Feature \uD3F4\uB354\uBA85\uC774 F001-... \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4. (ID\uB97C \uCD94\uCD9C\uD560 \uC218 \uC5C6\uC74C)",
|
|
110
|
+
"context.noActiveFeatures": "\u26A0\uFE0F \uC9C4\uD589 \uC911\uC778 Feature\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
111
|
+
"context.envWarnings": "\u26A0\uFE0F \uD658\uACBD \uACBD\uACE0:",
|
|
112
|
+
"context.openFallbackSummary": "(\uBE0C\uB79C\uCE58\uB85C Feature\uB97C \uD2B9\uC815\uD558\uC9C0 \uBABB\uD574 \uBBF8\uC644\uB8CC Feature\uB9CC \uD45C\uC2DC\uD569\uB2C8\uB2E4. \uC9C4\uD589 \uC911: {inProgress}\uAC1C / \uC885\uB8CC \uB300\uAE30: {readyToClose}\uAC1C / \uC644\uB8CC: {done}\uAC1C)",
|
|
113
|
+
"context.sectionInProgress": "\uC9C4\uD589 \uC911",
|
|
114
|
+
"context.sectionReadyToClose": "\uC885\uB8CC \uC900\uBE44",
|
|
115
|
+
"context.tipDetails": "Tip: \uD2B9\uC815 Feature\uC758 \uC0C1\uC138 \uC815\uBCF4\uB97C \uBCF4\uB824\uBA74:",
|
|
116
|
+
"context.tipShowAll": "\uC804\uCCB4 \uBCF4\uAE30",
|
|
117
|
+
"context.tipShowDone": "\uC644\uB8CC\uB9CC \uBCF4\uAE30",
|
|
118
|
+
"context.okRequired": "[OK \uD544\uC694] ",
|
|
119
|
+
"context.list.docsCommitNeeded": "\uBB38\uC11C \uCEE4\uBC0B \uD544\uC694",
|
|
120
|
+
"context.list.issueNumberNeeded": "\uC774\uC288 \uBC88\uD638 \uAE30\uB85D \uD544\uC694",
|
|
121
|
+
"context.list.addPrMetadata": "PR \uBA54\uD0C0\uB370\uC774\uD130(PR/PR \uC0C1\uD0DC) \uCD94\uAC00",
|
|
122
|
+
"context.list.recordPrLink": "PR \uB9C1\uD06C \uAE30\uB85D",
|
|
123
|
+
"context.list.setPrStatus": "PR \uC0C1\uD0DC \uC124\uC815",
|
|
124
|
+
"context.list.prStatusToApproved": "PR \uC0C1\uD0DC {status} \u2192 Approved",
|
|
125
|
+
"context.list.approveSpec": "spec \uC2B9\uC778 \uD544\uC694",
|
|
126
|
+
"context.list.approvePlan": "plan \uC2B9\uC778 \uD544\uC694",
|
|
127
|
+
"init.selectLangPrompt": "\uBB38\uC11C \uC5B8\uC5B4\uB97C \uC120\uD0DD\uD558\uC138\uC694:",
|
|
128
|
+
"init.currentDirectoryLabel": "\u{1F4CD} \uD604\uC7AC \uC704\uCE58",
|
|
129
|
+
"init.gitDetected": "\u2705 Git \uB808\uD3EC\uC9C0\uD1A0\uB9AC \uAC10\uC9C0\uB428",
|
|
130
|
+
"init.insideProjectRoot": "\uD604\uC7AC \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8 \uB0B4\uC5D0\uC11C \uC2E4\uD589\uD558\uACE0 \uACC4\uC2ED\uB2C8\uB2E4.",
|
|
131
|
+
"init.modeEmbeddedDesc": "\u2022 embedded: \uC5EC\uAE30\uC5D0 ./docs \uD3F4\uB354\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4. \uD504\uB85C\uC81D\uD2B8\uC640 \uD568\uAED8 \uAD00\uB9AC\uB429\uB2C8\uB2E4.",
|
|
132
|
+
"init.modeStandaloneDesc": "\u2022 standalone: \uBCC4\uB3C4 \uD3F4\uB354\uC5D0\uC11C \uB3C5\uB9BD docs \uB808\uD3EC\uB85C \uAD00\uB9AC\uD558\uB824\uBA74,",
|
|
133
|
+
"init.modeStandaloneMove": " \uD574\uB2F9 \uD3F4\uB354\uB85C \uC774\uB3D9 \uD6C4 \uB2E4\uC2DC \uC2E4\uD589\uD574\uC8FC\uC138\uC694.",
|
|
134
|
+
"init.gitNotDetected": "\u26A0\uFE0F Git \uB808\uD3EC\uC9C0\uD1A0\uB9AC\uAC00 \uAC10\uC9C0\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.",
|
|
135
|
+
"init.gitNotDetectedDetail": "\uC0C8\uB85C\uC6B4 Git \uB808\uD3EC\uC9C0\uD1A0\uB9AC\uAC00 \uC0DD\uC131\uB429\uB2C8\uB2E4.",
|
|
136
|
+
"init.prompt.projectName": "\uD504\uB85C\uC81D\uD2B8 \uC774\uB984\uC744 \uC785\uB825\uD558\uC138\uC694:",
|
|
137
|
+
"init.prompt.projectType": "\uD504\uB85C\uC81D\uD2B8 \uD0C0\uC785\uC744 \uC120\uD0DD\uD558\uC138\uC694:",
|
|
138
|
+
"init.choice.projectType.single.title": "Single - \uB2E8\uC77C \uB808\uD3EC \uD504\uB85C\uC81D\uD2B8",
|
|
139
|
+
"init.choice.projectType.single.desc": "features/ \uD3F4\uB354 \uD558\uB098\uB85C \uAD00\uB9AC",
|
|
140
|
+
"init.choice.projectType.fullstack.title": "Fullstack - FE/BE \uBD84\uB9AC \uD504\uB85C\uC81D\uD2B8",
|
|
141
|
+
"init.choice.projectType.fullstack.desc": "features/be/, features/fe/ \uBD84\uB9AC \uAD00\uB9AC",
|
|
142
|
+
"init.prompt.docsMode": "Docs \uAD00\uB9AC \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:",
|
|
143
|
+
"init.choice.docsRepo.embedded.title": "embedded - \uD504\uB85C\uC81D\uD2B8 \uB0B4 \uD3EC\uD568 (./docs)",
|
|
144
|
+
"init.choice.docsRepo.embedded.desc": "\uD504\uB85C\uC81D\uD2B8\uC640 \uD568\uAED8 push\uB429\uB2C8\uB2E4",
|
|
145
|
+
"init.choice.docsRepo.standalone.title": "standalone - \uBCC4\uB3C4 \uB3C5\uB9BD \uB808\uD3EC",
|
|
146
|
+
"init.choice.docsRepo.standalone.desc": "push \uC5EC\uBD80\uB97C \uBCC4\uB3C4\uB85C \uC124\uC815\uD569\uB2C8\uB2E4",
|
|
147
|
+
"init.prompt.feRepoPath": "Frontend \uB808\uD3EC\uC9C0\uD1A0\uB9AC \uACBD\uB85C\uB97C \uC785\uB825\uD558\uC138\uC694:",
|
|
148
|
+
"init.prompt.beRepoPath": "Backend \uB808\uD3EC\uC9C0\uD1A0\uB9AC \uACBD\uB85C\uB97C \uC785\uB825\uD558\uC138\uC694:",
|
|
149
|
+
"init.prompt.projectRepoPath": "\uD504\uB85C\uC81D\uD2B8 \uB808\uD3EC\uC9C0\uD1A0\uB9AC \uACBD\uB85C\uB97C \uC785\uB825\uD558\uC138\uC694:",
|
|
150
|
+
"init.validation.enterPath": "\uACBD\uB85C\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694",
|
|
151
|
+
"init.prompt.pushMode": "Docs push \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:",
|
|
152
|
+
"init.choice.push.local": "local - \uB85C\uCEEC\uC5D0\uC11C\uB9CC \uAD00\uB9AC (push \uC548 \uD568)",
|
|
153
|
+
"init.choice.push.remote": "remote - \uC6D0\uACA9\uC5D0\uB3C4 push",
|
|
154
|
+
"init.prompt.remoteUrl": "\uC6D0\uACA9 \uB808\uD3EC URL\uC744 \uC785\uB825\uD558\uC138\uC694:",
|
|
155
|
+
"init.validation.enterUrl": "URL\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694",
|
|
156
|
+
"init.prompt.overwrite": "{dir} \uD3F4\uB354\uAC00 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4. \uB36E\uC5B4\uC4F0\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
|
|
157
|
+
"init.log.creatingDocs": "\u{1F4C1} docs \uAD6C\uC870 \uC0DD\uC131 \uC911...",
|
|
158
|
+
"init.log.projectLabel": "\uD504\uB85C\uC81D\uD2B8",
|
|
159
|
+
"init.log.typeLabel": "\uD0C0\uC785",
|
|
160
|
+
"init.log.langLabel": "\uC5B8\uC5B4",
|
|
161
|
+
"init.log.pathLabel": "\uACBD\uB85C",
|
|
162
|
+
"init.log.docsCreated": "\u2705 docs \uAD6C\uC870 \uC0DD\uC131 \uC644\uB8CC!",
|
|
163
|
+
"init.log.nextStepsTitle": "\uB2E4\uC74C \uB2E8\uACC4:",
|
|
164
|
+
"init.log.nextSteps1": " 1. {docsDir}/prd/README.md \uC791\uC131",
|
|
165
|
+
"init.log.nextSteps2": " 2. npx lee-spec-kit feature <name> \uC73C\uB85C \uAE30\uB2A5 \uCD94\uAC00",
|
|
166
|
+
"init.log.gitRepoDetectedCommit": "\u{1F4E6} Git \uB808\uD3EC\uC9C0\uD1A0\uB9AC \uAC10\uC9C0, docs \uCEE4\uBC0B \uC911...",
|
|
167
|
+
"init.log.gitInit": "\u{1F4E6} Git \uCD08\uAE30\uD654 \uC911...",
|
|
168
|
+
"init.warn.stagedChangesSkip": '\u26A0\uFE0F \uD604\uC7AC Git index\uC5D0 \uC774\uBBF8 stage\uB41C \uBCC0\uACBD\uC774 \uC788\uC2B5\uB2C8\uB2E4. (--dir "." \uC778 \uACBD\uC6B0 \uCEE4\uBC0B \uBC94\uC704\uB97C \uC548\uC804\uD558\uAC8C \uC81C\uD55C\uD560 \uC218 \uC5C6\uC5B4 \uC790\uB3D9 \uCEE4\uBC0B\uC744 \uAC74\uB108\uB701\uB2C8\uB2E4)',
|
|
169
|
+
"init.warn.commitManually": " \uC218\uB3D9\uC73C\uB85C \uBCC0\uACBD \uB0B4\uC6A9\uC744 \uD655\uC778\uD55C \uB4A4 \uCEE4\uBC0B\uD574\uC8FC\uC138\uC694.",
|
|
170
|
+
"init.log.gitRemoteSet": "\u2705 Git remote \uC124\uC815 \uC644\uB8CC: {remote}",
|
|
171
|
+
"init.warn.gitRemoteExists": "\u26A0\uFE0F Git remote\uAC00 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4.",
|
|
172
|
+
"init.log.gitInitialCommitDone": "\u2705 Git \uCD08\uAE30 \uCEE4\uBC0B \uC644\uB8CC!",
|
|
173
|
+
"init.warn.skipGitInit": "\u26A0\uFE0F Git \uCD08\uAE30\uD654\uB97C \uAC74\uB108\uB701\uB2C8\uB2E4 (\uC218\uB3D9\uC73C\uB85C \uCEE4\uBC0B\uD574\uC8FC\uC138\uC694)",
|
|
174
|
+
"init.error.templateNotFound": "\uD15C\uD50C\uB9BF\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: {path}"
|
|
175
|
+
},
|
|
176
|
+
steps: {
|
|
177
|
+
featureFolder: "Feature \uD3F4\uB354 \uC0DD\uC131",
|
|
178
|
+
specWrite: "spec.md \uC791\uC131",
|
|
179
|
+
specApprove: "spec.md \uC2B9\uC778",
|
|
180
|
+
planWrite: "plan.md \uC791\uC131",
|
|
181
|
+
planApprove: "plan.md \uC2B9\uC778",
|
|
182
|
+
tasksWrite: "tasks.md \uC791\uC131",
|
|
183
|
+
docsCommitPlanning: "\uBB38\uC11C \uCEE4\uBC0B(\uB3D9\uAE30\uD654)",
|
|
184
|
+
issueCreate: "GitHub Issue \uC0DD\uC131",
|
|
185
|
+
branchCreate: "\uBE0C\uB79C\uCE58 \uC0DD\uC131",
|
|
186
|
+
tasksExecute: "\uD0DC\uC2A4\uD06C \uC2E4\uD589",
|
|
187
|
+
prCreate: "PR \uC0DD\uC131",
|
|
188
|
+
codeReview: "\uCF54\uB4DC \uB9AC\uBDF0",
|
|
189
|
+
featureDone: "Feature \uC644\uB8CC"
|
|
190
|
+
},
|
|
191
|
+
messages: {
|
|
192
|
+
specCreate: "spec.md \uD15C\uD50C\uB9BF\uC744 \uBCF5\uC0AC\uD574 \uC791\uC131\uD558\uC138\uC694. (features/feature-base/spec.md \uCC38\uACE0)",
|
|
193
|
+
specImprove: "spec.md\uB97C \uBCF4\uC644\uD558\uACE0 \uC0C1\uD0DC\uB97C Review\uB85C \uBCC0\uACBD\uD558\uC138\uC694.",
|
|
194
|
+
specApproval: "spec.md \uB0B4\uC6A9\uC744 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uACF5\uC720\uD558\uACE0 \uC2B9\uC778(OK)\uC744 \uBC1B\uC73C\uC138\uC694.",
|
|
195
|
+
planCreate: "plan.md \uD15C\uD50C\uB9BF\uC744 \uBCF5\uC0AC\uD574 \uC791\uC131\uD558\uC138\uC694. (features/feature-base/plan.md \uCC38\uACE0)",
|
|
196
|
+
planImprove: "plan.md\uB97C \uBCF4\uC644\uD558\uACE0 \uC0C1\uD0DC\uB97C Review\uB85C \uBCC0\uACBD\uD558\uC138\uC694.",
|
|
197
|
+
planApproval: "plan.md \uB0B4\uC6A9\uC744 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uACF5\uC720\uD558\uACE0 \uC2B9\uC778(OK)\uC744 \uBC1B\uC73C\uC138\uC694.",
|
|
198
|
+
tasksCreate: "tasks.md \uD15C\uD50C\uB9BF\uC744 \uBCF5\uC0AC\uD574 \uD0DC\uC2A4\uD06C\uB97C \uC791\uC131\uD558\uC138\uC694. (features/feature-base/tasks.md \uCC38\uACE0)",
|
|
199
|
+
tasksNeedAtLeastOne: "tasks.md\uC5D0 \uCD5C\uC18C 1\uAC1C \uC774\uC0C1\uC758 \uD0DC\uC2A4\uD06C\uB97C \uC791\uC131\uD558\uC138\uC694.",
|
|
200
|
+
docsCommitPlanning: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(planning): {folderName} \uAE30\uD68D \uBB38\uC11C"',
|
|
201
|
+
issueCreateAndWrite: "GitHub Issue\uB97C \uC0DD\uC131\uD55C \uB4A4, spec.md/tasks.md\uC758 \uC774\uC288 \uBC88\uD638\uB97C \uCC44\uC6B0\uACE0 \uBB38\uC11C \uCEE4\uBC0B\uC744 \uC900\uBE44\uD558\uC138\uC694. (skills/create-issue.md \uCC38\uACE0)",
|
|
202
|
+
docsCommitIssueUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(#{issueNumber}): {folderName} \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8"',
|
|
203
|
+
standaloneNeedsProjectRoot: "standalone \uBAA8\uB4DC\uC5D0\uC11C\uB294 projectRoot \uC124\uC815\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. (npx lee-spec-kit config --project-root ...)",
|
|
204
|
+
createBranch: 'cd "{projectGitCwd}" && git checkout -b feat/{issueNumber}-{slug}',
|
|
205
|
+
tasksAllDoneButNoChecklist: '\uBAA8\uB4E0 \uD0DC\uC2A4\uD06C\uAC00 DONE\uC774\uC9C0\uB9CC \uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 \uC139\uC158\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. tasks.md\uC758 "\uC644\uB8CC \uC870\uAC74" \uC139\uC158\uC744 \uCD94\uAC00/\uD655\uC778\uD558\uC138\uC694.',
|
|
206
|
+
tasksAllDoneButChecklist: "\uBAA8\uB4E0 \uD0DC\uC2A4\uD06C\uAC00 DONE\uC774\uC9C0\uB9CC \uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\uAC00 \uC644\uC804\uD788 \uCCB4\uD06C\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. ({checked}/{total})",
|
|
207
|
+
finishDoingTask: '\uD604\uC7AC DOING/REVIEW \uC911\uC778 \uD0DC\uC2A4\uD06C\uB97C \uC644\uB8CC\uD558\uC138\uC694: "{title}" ({done}/{total}) (skills/execute-task.md \uCC38\uACE0)',
|
|
208
|
+
startNextTodoTask: '\uB2E4\uC74C TODO \uD0DC\uC2A4\uD06C\uB97C \uC2DC\uC791\uD558\uC138\uC694: "{title}" ({done}/{total}) (skills/execute-task.md \uCC38\uACE0)',
|
|
209
|
+
checkTaskStatuses: "\uD0DC\uC2A4\uD06C \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694. ({done}/{total}) (skills/execute-task.md \uCC38\uACE0)",
|
|
210
|
+
prLegacyAsk: "tasks.md\uC5D0 PR/PR \uC0C1\uD0DC \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uD15C\uD50C\uB9BF\uC744 \uCD5C\uC2E0 \uD3EC\uB9F7\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD560\uAE4C\uC694? (OK \uD544\uC694)",
|
|
211
|
+
prCreate: "PR\uC744 \uC0DD\uC131\uD558\uACE0 tasks.md\uC5D0 PR \uB9C1\uD06C\uB97C \uAE30\uB85D\uD558\uC138\uC694. (skills/create-pr.md \uCC38\uACE0)",
|
|
212
|
+
prFillStatus: "tasks.md\uC758 PR \uC0C1\uD0DC\uB97C Draft/Review/Approved \uC911 \uD558\uB098\uB85C \uC124\uC815\uD558\uC138\uC694. (merge \uD6C4 Approved\uB85C \uC5C5\uB370\uC774\uD2B8)",
|
|
213
|
+
prResolveReview: "\uB9AC\uBDF0 \uCF54\uBA58\uD2B8\uB97C \uD574\uACB0\uD558\uACE0 PR \uC0C1\uD0DC\uB97C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694. (PR \uC0C1\uD0DC: Review \u2192 Approved)",
|
|
214
|
+
prRequestReview: "\uB9AC\uBDF0\uC5B4\uC5D0\uAC8C \uB9AC\uBDF0\uB97C \uC694\uCCAD\uD558\uACE0 PR \uC0C1\uD0DC\uB97C Review\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.",
|
|
215
|
+
featureDone: "PR\uC774 Approved\uC774\uACE0 \uBAA8\uB4E0 \uD0DC\uC2A4\uD06C/\uC644\uB8CC \uC870\uAC74\uC774 \uCDA9\uC871\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC774 Feature\uB294 \uC644\uB8CC \uC0C1\uD0DC\uC785\uB2C8\uB2E4.",
|
|
216
|
+
fallbackRerunContext: "\uC0C1\uD0DC\uB97C \uD310\uBCC4\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBB38\uC11C\uB97C \uD655\uC778\uD55C \uB4A4 \uB2E4\uC2DC context\uB97C \uC2E4\uD589\uD558\uC138\uC694."
|
|
217
|
+
},
|
|
218
|
+
warnings: {
|
|
219
|
+
projectBranchUnavailable: "\uD504\uB85C\uC81D\uD2B8 \uBE0C\uB79C\uCE58\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. (standalone \uBAA8\uB4DC\uC5D0\uC11C\uB294 projectRoot\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.)",
|
|
220
|
+
docsGitUnavailable: "docs \uB808\uD3EC\uC758 git \uC0C1\uD0DC\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. (\uB808\uD3EC \uC704\uCE58 / git init \uD655\uC778)",
|
|
221
|
+
docsUncommittedChanges: "\uBB38\uC11C \uBCC0\uACBD\uC0AC\uD56D\uC774 \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (\uCD94\uAC00 \uBB38\uC11C \uCEE4\uBC0B \uD544\uC694)",
|
|
222
|
+
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.",
|
|
223
|
+
workflowSpecNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC spec.md \uC0C1\uD0DC\uAC00 Approved\uAC00 \uC544\uB2D9\uB2C8\uB2E4. (spec.md\uC758 \uC0C1\uD0DC\uB97C Approved\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.)",
|
|
224
|
+
workflowPlanNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC plan.md \uC0C1\uD0DC\uAC00 Approved\uAC00 \uC544\uB2D9\uB2C8\uB2E4. (plan.md\uC758 \uC0C1\uD0DC\uB97C Approved\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.)",
|
|
225
|
+
workflowPrLinkMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC PR \uB9C1\uD06C\uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4. (tasks.md\uC758 PR \uD544\uB4DC\uB97C \uCC44\uC6B0\uC138\uC694.)",
|
|
226
|
+
workflowPrStatusMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC PR \uC0C1\uD0DC\uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4. (tasks.md\uC758 PR \uC0C1\uD0DC\uB97C Draft/Review/Approved \uC911 \uD558\uB098\uB85C \uC124\uC815\uD558\uC138\uC694.)",
|
|
227
|
+
workflowPrStatusNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC PR \uC0C1\uD0DC\uAC00 Approved\uAC00 \uC544\uB2D9\uB2C8\uB2E4. (merge \uD6C4 PR \uC0C1\uD0DC\uB97C Approved\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.)"
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
en: {
|
|
231
|
+
cli: {
|
|
232
|
+
"common.errorLabel": "Error:",
|
|
233
|
+
"common.canceled": "Operation canceled.",
|
|
234
|
+
"common.configNotFound": "Config file not found. Run `init` first.",
|
|
235
|
+
"common.docsNotFound": "docs folder not found. Run `init` first.",
|
|
236
|
+
"status.noFeatures": "No features found.",
|
|
237
|
+
"status.duplicateIds": "Duplicate Feature IDs found:",
|
|
238
|
+
"status.missingIds": "Entries missing Feature ID:",
|
|
239
|
+
"status.wrote": "\u2705 Wrote {path}",
|
|
240
|
+
"feature.selectRepo": "Select a repository:",
|
|
241
|
+
"feature.folderExists": "Folder already exists: {path}",
|
|
242
|
+
"feature.baseNotFound": "feature-base template not found.",
|
|
243
|
+
"feature.created": "\u2705 Feature folder created: {path}",
|
|
244
|
+
"feature.nextStepsTitle": "Next steps:",
|
|
245
|
+
"feature.nextSteps1": " 1. Write {path}/spec.md",
|
|
246
|
+
"feature.nextSteps2": " 2. Ask for review",
|
|
247
|
+
"feature.nextSteps3": " 3. After approval, write plan.md",
|
|
248
|
+
"config.currentTitle": "\u{1F4CB} Current config:",
|
|
249
|
+
"config.pathLabel": "Path",
|
|
250
|
+
"config.projectRootStandaloneOnly": "\u26A0\uFE0F projectRoot can only be set in standalone mode.",
|
|
251
|
+
"config.selectRepoToUpdate": "Select a repository to update:",
|
|
252
|
+
"config.fullstackRepoRequired": "For fullstack projects, you must specify `--repo fe` or `--repo be`.",
|
|
253
|
+
"config.projectRootSet": "\u2705 {repo} projectRoot set: {path}",
|
|
254
|
+
"config.projectRootSetSingle": "\u2705 projectRoot set: {path}",
|
|
255
|
+
"update.start": "\u{1F4E6} Starting template update...",
|
|
256
|
+
"update.langLabel": "Lang",
|
|
257
|
+
"update.typeLabel": "Type",
|
|
258
|
+
"update.updatingAgents": "\u{1F4C1} Updating agents/ folder...",
|
|
259
|
+
"update.agentsUpdated": "agents/ updated",
|
|
260
|
+
"update.updatingFeatureBase": "\u{1F4C1} Updating features/feature-base/ folder...",
|
|
261
|
+
"update.filesUpdated": "{count} files updated",
|
|
262
|
+
"update.updatedTotal": "Updated {count} files!",
|
|
263
|
+
"update.changeDetected": "changes detected (use --force to overwrite)",
|
|
264
|
+
"update.fileUpdated": "{file} updated",
|
|
265
|
+
"doctor.title": "\u{1F50E} Docs Doctor",
|
|
266
|
+
"doctor.envWarnings": "\u26A0\uFE0F Environment warnings:",
|
|
267
|
+
"doctor.noIssues": "\u2705 No issues found.",
|
|
268
|
+
"doctor.errorsTitle": "Errors",
|
|
269
|
+
"doctor.warningsTitle": "Warnings",
|
|
270
|
+
"doctor.tipJson": "Tip: Agent JSON output: npx lee-spec-kit doctor --json{strictFlag}",
|
|
271
|
+
"doctor.issue.missingRequiredDir": "Missing required directory: {dir}",
|
|
272
|
+
"doctor.issue.missingConfig": "Missing .lee-spec-kit.json. Some commands may rely on folder-structure heuristics.",
|
|
273
|
+
"doctor.issue.noFeatures": "No feature folders found. (Only feature-base exists, or no features created yet.)",
|
|
274
|
+
"doctor.issue.placeholdersLeft": "Leftover placeholders detected: {placeholders}",
|
|
275
|
+
"doctor.issue.missingSpec": "Missing spec.md.",
|
|
276
|
+
"doctor.issue.specStatusUnset": "spec.md Status is not set. (May still be a template)",
|
|
277
|
+
"doctor.issue.planStatusUnset": "plan.md Status is not set. (May still be a template)",
|
|
278
|
+
"doctor.issue.tasksEmpty": "tasks.md has no tasks.",
|
|
279
|
+
"doctor.issue.duplicateFeatureId": "Duplicate Feature ID detected: {id} ({count})",
|
|
280
|
+
"doctor.issue.missingFeatureId": "Feature folder name is not in F001-... format. (Cannot extract ID)",
|
|
281
|
+
"context.noActiveFeatures": "\u26A0\uFE0F No active features found.",
|
|
282
|
+
"context.envWarnings": "\u26A0\uFE0F Environment warnings:",
|
|
283
|
+
"context.openFallbackSummary": "(Could not detect a feature from the branch, so showing only open features. In Progress: {inProgress} / Ready To Close: {readyToClose} / Done: {done})",
|
|
284
|
+
"context.sectionInProgress": "In Progress",
|
|
285
|
+
"context.sectionReadyToClose": "Ready To Close",
|
|
286
|
+
"context.tipDetails": "Tip: To view details for a feature:",
|
|
287
|
+
"context.tipShowAll": "Show all",
|
|
288
|
+
"context.tipShowDone": "Show done only",
|
|
289
|
+
"context.okRequired": "[OK required] ",
|
|
290
|
+
"context.list.docsCommitNeeded": "Commit docs changes",
|
|
291
|
+
"context.list.issueNumberNeeded": "Fill issue number in docs",
|
|
292
|
+
"context.list.addPrMetadata": "Add PR metadata (PR/PR Status)",
|
|
293
|
+
"context.list.recordPrLink": "Record PR link",
|
|
294
|
+
"context.list.setPrStatus": "Set PR Status",
|
|
295
|
+
"context.list.prStatusToApproved": "PR Status {status} \u2192 Approved",
|
|
296
|
+
"context.list.approveSpec": "Approve spec",
|
|
297
|
+
"context.list.approvePlan": "Approve plan",
|
|
298
|
+
"init.selectLangPrompt": "Select docs language:",
|
|
299
|
+
"init.currentDirectoryLabel": "\u{1F4CD} Current directory",
|
|
300
|
+
"init.gitDetected": "\u2705 Git repository detected",
|
|
301
|
+
"init.insideProjectRoot": "You are running inside your project root.",
|
|
302
|
+
"init.modeEmbeddedDesc": "\u2022 embedded: creates ./docs here and manages it with the project.",
|
|
303
|
+
"init.modeStandaloneDesc": "\u2022 standalone: to manage docs as a separate repo,",
|
|
304
|
+
"init.modeStandaloneMove": " move to that folder and run again.",
|
|
305
|
+
"init.gitNotDetected": "\u26A0\uFE0F Git repository not detected.",
|
|
306
|
+
"init.gitNotDetectedDetail": "A new Git repo will be initialized.",
|
|
307
|
+
"init.prompt.projectName": "Enter project name:",
|
|
308
|
+
"init.prompt.projectType": "Select project type:",
|
|
309
|
+
"init.choice.projectType.single.title": "Single - single repo project",
|
|
310
|
+
"init.choice.projectType.single.desc": "Manage with a single features/ folder",
|
|
311
|
+
"init.choice.projectType.fullstack.title": "Fullstack - split FE/BE repos",
|
|
312
|
+
"init.choice.projectType.fullstack.desc": "Manage with features/be/ and features/fe/",
|
|
313
|
+
"init.prompt.docsMode": "Select docs mode:",
|
|
314
|
+
"init.choice.docsRepo.embedded.title": "embedded - inside the project (./docs)",
|
|
315
|
+
"init.choice.docsRepo.embedded.desc": "Pushed together with the project",
|
|
316
|
+
"init.choice.docsRepo.standalone.title": "standalone - separate docs repo",
|
|
317
|
+
"init.choice.docsRepo.standalone.desc": "Configure push settings separately",
|
|
318
|
+
"init.prompt.feRepoPath": "Enter frontend repository path:",
|
|
319
|
+
"init.prompt.beRepoPath": "Enter backend repository path:",
|
|
320
|
+
"init.prompt.projectRepoPath": "Enter project repository path:",
|
|
321
|
+
"init.validation.enterPath": "Please enter a path",
|
|
322
|
+
"init.prompt.pushMode": "Select docs push mode:",
|
|
323
|
+
"init.choice.push.local": "local - manage locally (no push)",
|
|
324
|
+
"init.choice.push.remote": "remote - push to remote",
|
|
325
|
+
"init.prompt.remoteUrl": "Enter remote repository URL:",
|
|
326
|
+
"init.validation.enterUrl": "Please enter a URL",
|
|
327
|
+
"init.prompt.overwrite": "{dir} already exists. Overwrite?",
|
|
328
|
+
"init.log.creatingDocs": "\u{1F4C1} Creating docs structure...",
|
|
329
|
+
"init.log.projectLabel": "Project",
|
|
330
|
+
"init.log.typeLabel": "Type",
|
|
331
|
+
"init.log.langLabel": "Lang",
|
|
332
|
+
"init.log.pathLabel": "Path",
|
|
333
|
+
"init.log.docsCreated": "\u2705 Docs structure created!",
|
|
334
|
+
"init.log.nextStepsTitle": "Next steps:",
|
|
335
|
+
"init.log.nextSteps1": " 1. Write {docsDir}/prd/README.md",
|
|
336
|
+
"init.log.nextSteps2": " 2. Add a feature with: npx lee-spec-kit feature <name>",
|
|
337
|
+
"init.log.gitRepoDetectedCommit": "\u{1F4E6} Git repo detected, committing docs...",
|
|
338
|
+
"init.log.gitInit": "\u{1F4E6} Initializing Git...",
|
|
339
|
+
"init.warn.stagedChangesSkip": '\u26A0\uFE0F There are already staged changes in the Git index. (With --dir ".", commit scope cannot be safely restricted, so auto-commit is skipped.)',
|
|
340
|
+
"init.warn.commitManually": " Review the changes and commit manually.",
|
|
341
|
+
"init.log.gitRemoteSet": "\u2705 Git remote set: {remote}",
|
|
342
|
+
"init.warn.gitRemoteExists": "\u26A0\uFE0F Git remote already exists.",
|
|
343
|
+
"init.log.gitInitialCommitDone": "\u2705 Initial Git commit created!",
|
|
344
|
+
"init.warn.skipGitInit": "\u26A0\uFE0F Skipping Git initialization (please commit manually)",
|
|
345
|
+
"init.error.templateNotFound": "Template not found: {path}"
|
|
346
|
+
},
|
|
347
|
+
steps: {
|
|
348
|
+
featureFolder: "Create feature folder",
|
|
349
|
+
specWrite: "Write spec.md",
|
|
350
|
+
specApprove: "Approve spec.md",
|
|
351
|
+
planWrite: "Write plan.md",
|
|
352
|
+
planApprove: "Approve plan.md",
|
|
353
|
+
tasksWrite: "Write tasks.md",
|
|
354
|
+
docsCommitPlanning: "Commit docs (sync)",
|
|
355
|
+
issueCreate: "Create GitHub Issue",
|
|
356
|
+
branchCreate: "Create branch",
|
|
357
|
+
tasksExecute: "Execute tasks",
|
|
358
|
+
prCreate: "Create PR",
|
|
359
|
+
codeReview: "Code review",
|
|
360
|
+
featureDone: "Feature done"
|
|
361
|
+
},
|
|
362
|
+
messages: {
|
|
363
|
+
specCreate: "Create spec.md by copying the template. (See features/feature-base/spec.md)",
|
|
364
|
+
specImprove: "Improve spec.md and change Status to Review.",
|
|
365
|
+
specApproval: "Share spec.md with the user and get approval (OK).",
|
|
366
|
+
planCreate: "Create plan.md by copying the template. (See features/feature-base/plan.md)",
|
|
367
|
+
planImprove: "Improve plan.md and change Status to Review.",
|
|
368
|
+
planApproval: "Share plan.md with the user and get approval (OK).",
|
|
369
|
+
tasksCreate: "Create tasks.md by copying the template. (See features/feature-base/tasks.md)",
|
|
370
|
+
tasksNeedAtLeastOne: "Write at least 1 task in tasks.md.",
|
|
371
|
+
docsCommitPlanning: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(planning): {folderName} planning docs"',
|
|
372
|
+
issueCreateAndWrite: "Create a GitHub Issue, fill the issue number in spec.md/tasks.md, then prepare a docs commit. (See skills/create-issue.md)",
|
|
373
|
+
docsCommitIssueUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(#{issueNumber}): {folderName} docs update"',
|
|
374
|
+
standaloneNeedsProjectRoot: "Standalone mode requires projectRoot. (npx lee-spec-kit config --project-root ...)",
|
|
375
|
+
createBranch: 'cd "{projectGitCwd}" && git checkout -b feat/{issueNumber}-{slug}',
|
|
376
|
+
tasksAllDoneButNoChecklist: 'All tasks are DONE, but no completion checklist section was found. Add/verify the "Completion Criteria" section in tasks.md.',
|
|
377
|
+
tasksAllDoneButChecklist: "All tasks are DONE, but the completion checklist is not fully checked. ({checked}/{total})",
|
|
378
|
+
finishDoingTask: 'Finish the current DOING/REVIEW task: "{title}" ({done}/{total}) (See skills/execute-task.md)',
|
|
379
|
+
startNextTodoTask: 'Start the next TODO task: "{title}" ({done}/{total}) (See skills/execute-task.md)',
|
|
380
|
+
checkTaskStatuses: "Check task statuses. ({done}/{total}) (See skills/execute-task.md)",
|
|
381
|
+
prLegacyAsk: "tasks.md is missing PR/PR Status fields. Update to the latest template format? (OK required)",
|
|
382
|
+
prCreate: "Create a PR and record the PR link in tasks.md. (See skills/create-pr.md)",
|
|
383
|
+
prFillStatus: "Set PR Status in tasks.md to Draft/Review/Approved. (After merge, update it to Approved.)",
|
|
384
|
+
prResolveReview: "Resolve review comments and update PR Status. (PR Status: Review \u2192 Approved)",
|
|
385
|
+
prRequestReview: "Request review and update PR Status to Review.",
|
|
386
|
+
featureDone: "PR is Approved and all tasks/completion criteria are satisfied. This feature is done.",
|
|
387
|
+
fallbackRerunContext: "Cannot determine status. Check the docs and run context again."
|
|
388
|
+
},
|
|
389
|
+
warnings: {
|
|
390
|
+
projectBranchUnavailable: "Cannot determine project branch. (In standalone mode, projectRoot is required.)",
|
|
391
|
+
docsGitUnavailable: "Cannot read git status for the docs repo. (Check repo location / git init.)",
|
|
392
|
+
docsUncommittedChanges: "Docs changes are not committed. (Additional docs commit needed.)",
|
|
393
|
+
legacyTasksPrFields: "Legacy tasks.md format detected. Add `PR` and `PR Status` fields before PR steps.",
|
|
394
|
+
workflowSpecNotApproved: "Implementation is done but spec.md Status is not Approved. (Update spec.md Status to Approved.)",
|
|
395
|
+
workflowPlanNotApproved: "Implementation is done but plan.md Status is not Approved. (Update plan.md Status to Approved.)",
|
|
396
|
+
workflowPrLinkMissing: "Implementation is done but PR link is missing. (Fill the PR field in tasks.md.)",
|
|
397
|
+
workflowPrStatusMissing: "Implementation is done but PR Status is missing. (Set PR Status to Draft/Review/Approved in tasks.md.)",
|
|
398
|
+
workflowPrStatusNotApproved: "Implementation is done but PR Status is not Approved. (After merge, update PR Status to Approved in tasks.md.)"
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
function tr(lang, category, key, vars = {}) {
|
|
403
|
+
const safeLang = normalizeLang(lang);
|
|
404
|
+
const template = I18N[safeLang]?.[category]?.[key] ?? I18N[DEFAULT_LANG]?.[category]?.[key] ?? I18N.ko?.[category]?.[key] ?? `${category}.${key}`;
|
|
405
|
+
return formatTemplate(template, vars);
|
|
406
|
+
}
|
|
407
|
+
|
|
46
408
|
// src/utils/validation.ts
|
|
47
409
|
var VALID_PROJECT_TYPES = ["single", "fullstack"];
|
|
48
410
|
var VALID_LANGUAGES = ["ko", "en"];
|
|
@@ -149,15 +511,19 @@ function checkGitRepo(cwd) {
|
|
|
149
511
|
}
|
|
150
512
|
}
|
|
151
513
|
function initCommand(program2) {
|
|
152
|
-
program2.command("init").description("Initialize project documentation structure").option("-n, --name <name>", "Project name (default: current folder name)").option("-t, --type <type>", "Project type: single | fullstack").option("-l, --lang <lang>", "Language: ko | en (default:
|
|
514
|
+
program2.command("init").description("Initialize project documentation structure").option("-n, --name <name>", "Project name (default: current folder name)").option("-t, --type <type>", "Project type: single | fullstack").option("-l, --lang <lang>", "Language: ko | en (default: en)").option("-d, --dir <dir>", "Target directory (default: ./docs)", "./docs").option("-y, --yes", "Skip prompts and use defaults").action(async (options) => {
|
|
153
515
|
try {
|
|
154
516
|
await runInit(options);
|
|
155
517
|
} catch (error) {
|
|
156
518
|
if (error instanceof Error && error.message === "canceled") {
|
|
157
|
-
|
|
519
|
+
const lang = options.lang ?? DEFAULT_LANG;
|
|
520
|
+
console.log(
|
|
521
|
+
chalk6.yellow(`
|
|
522
|
+
${tr(lang, "cli", "common.canceled")}`)
|
|
523
|
+
);
|
|
158
524
|
process.exit(0);
|
|
159
525
|
}
|
|
160
|
-
console.error(chalk6.red("
|
|
526
|
+
console.error(chalk6.red(tr(DEFAULT_LANG, "cli", "common.errorLabel")), error);
|
|
161
527
|
process.exit(1);
|
|
162
528
|
}
|
|
163
529
|
});
|
|
@@ -167,7 +533,7 @@ async function runInit(options) {
|
|
|
167
533
|
const defaultName = path4.basename(cwd);
|
|
168
534
|
let projectName = options.name || defaultName;
|
|
169
535
|
let projectType = options.type;
|
|
170
|
-
let lang = options.lang || "
|
|
536
|
+
let lang = options.lang || "en";
|
|
171
537
|
let docsRepo = "embedded";
|
|
172
538
|
let pushDocs;
|
|
173
539
|
let docsRemote;
|
|
@@ -175,24 +541,62 @@ async function runInit(options) {
|
|
|
175
541
|
const targetDir = path4.resolve(cwd, options.dir || "./docs");
|
|
176
542
|
const isInsideGitRepo = checkGitRepo(cwd);
|
|
177
543
|
if (!options.yes) {
|
|
544
|
+
if (!options.lang) {
|
|
545
|
+
const langResponse = await prompts(
|
|
546
|
+
[
|
|
547
|
+
{
|
|
548
|
+
type: "select",
|
|
549
|
+
name: "lang",
|
|
550
|
+
message: tr(DEFAULT_LANG, "cli", "init.selectLangPrompt"),
|
|
551
|
+
choices: [
|
|
552
|
+
{ title: "English (en)", value: "en" },
|
|
553
|
+
{ title: "\uD55C\uAD6D\uC5B4 (ko)", value: "ko" }
|
|
554
|
+
],
|
|
555
|
+
initial: 0
|
|
556
|
+
}
|
|
557
|
+
],
|
|
558
|
+
{
|
|
559
|
+
onCancel: () => {
|
|
560
|
+
throw new Error("canceled");
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
);
|
|
564
|
+
lang = langResponse.lang || lang;
|
|
565
|
+
}
|
|
178
566
|
console.log();
|
|
179
|
-
console.log(
|
|
567
|
+
console.log(
|
|
568
|
+
chalk6.blue(`${tr(lang, "cli", "init.currentDirectoryLabel")}: ${cwd}`)
|
|
569
|
+
);
|
|
180
570
|
if (isInsideGitRepo) {
|
|
181
|
-
console.log(chalk6.green("
|
|
571
|
+
console.log(chalk6.green(tr(lang, "cli", "init.gitDetected")));
|
|
182
572
|
console.log();
|
|
183
|
-
console.log(chalk6.gray("\uD604\uC7AC \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8 \uB0B4\uC5D0\uC11C \uC2E4\uD589\uD558\uACE0 \uACC4\uC2ED\uB2C8\uB2E4."));
|
|
184
573
|
console.log(
|
|
185
574
|
chalk6.gray(
|
|
186
|
-
"
|
|
575
|
+
tr(lang, "cli", "init.insideProjectRoot")
|
|
576
|
+
)
|
|
577
|
+
);
|
|
578
|
+
console.log(
|
|
579
|
+
chalk6.gray(
|
|
580
|
+
tr(lang, "cli", "init.modeEmbeddedDesc")
|
|
187
581
|
)
|
|
188
582
|
);
|
|
189
583
|
console.log(
|
|
190
|
-
chalk6.gray(
|
|
584
|
+
chalk6.gray(
|
|
585
|
+
tr(lang, "cli", "init.modeStandaloneDesc")
|
|
586
|
+
)
|
|
587
|
+
);
|
|
588
|
+
console.log(
|
|
589
|
+
chalk6.gray(
|
|
590
|
+
tr(lang, "cli", "init.modeStandaloneMove")
|
|
591
|
+
)
|
|
191
592
|
);
|
|
192
|
-
console.log(chalk6.gray(" \uD574\uB2F9 \uD3F4\uB354\uB85C \uC774\uB3D9 \uD6C4 \uB2E4\uC2DC \uC2E4\uD589\uD574\uC8FC\uC138\uC694."));
|
|
193
593
|
} else {
|
|
194
|
-
console.log(
|
|
195
|
-
|
|
594
|
+
console.log(
|
|
595
|
+
chalk6.yellow(
|
|
596
|
+
tr(lang, "cli", "init.gitNotDetected")
|
|
597
|
+
)
|
|
598
|
+
);
|
|
599
|
+
console.log(chalk6.gray(tr(lang, "cli", "init.gitNotDetectedDetail")));
|
|
196
600
|
}
|
|
197
601
|
console.log();
|
|
198
602
|
const response = await prompts(
|
|
@@ -200,51 +604,41 @@ async function runInit(options) {
|
|
|
200
604
|
{
|
|
201
605
|
type: options.name ? null : "text",
|
|
202
606
|
name: "projectName",
|
|
203
|
-
message: "
|
|
607
|
+
message: tr(lang, "cli", "init.prompt.projectName"),
|
|
204
608
|
initial: defaultName
|
|
205
609
|
},
|
|
206
610
|
{
|
|
207
611
|
type: options.type ? null : "select",
|
|
208
612
|
name: "projectType",
|
|
209
|
-
message: "
|
|
613
|
+
message: tr(lang, "cli", "init.prompt.projectType"),
|
|
210
614
|
choices: [
|
|
211
615
|
{
|
|
212
|
-
title: "
|
|
616
|
+
title: tr(lang, "cli", "init.choice.projectType.single.title"),
|
|
213
617
|
value: "single",
|
|
214
|
-
description: "
|
|
618
|
+
description: tr(lang, "cli", "init.choice.projectType.single.desc")
|
|
215
619
|
},
|
|
216
620
|
{
|
|
217
|
-
title: "
|
|
621
|
+
title: tr(lang, "cli", "init.choice.projectType.fullstack.title"),
|
|
218
622
|
value: "fullstack",
|
|
219
|
-
description: "
|
|
623
|
+
description: tr(lang, "cli", "init.choice.projectType.fullstack.desc")
|
|
220
624
|
}
|
|
221
625
|
],
|
|
222
626
|
initial: 0
|
|
223
627
|
},
|
|
224
|
-
{
|
|
225
|
-
type: options.lang ? null : "select",
|
|
226
|
-
name: "lang",
|
|
227
|
-
message: "\uBB38\uC11C \uC5B8\uC5B4\uB97C \uC120\uD0DD\uD558\uC138\uC694:",
|
|
228
|
-
choices: [
|
|
229
|
-
{ title: "\uD55C\uAD6D\uC5B4 (ko)", value: "ko" },
|
|
230
|
-
{ title: "English (en)", value: "en" }
|
|
231
|
-
],
|
|
232
|
-
initial: 0
|
|
233
|
-
},
|
|
234
628
|
{
|
|
235
629
|
type: "select",
|
|
236
630
|
name: "docsRepo",
|
|
237
|
-
message: "
|
|
631
|
+
message: tr(lang, "cli", "init.prompt.docsMode"),
|
|
238
632
|
choices: [
|
|
239
633
|
{
|
|
240
|
-
title: "embedded
|
|
634
|
+
title: tr(lang, "cli", "init.choice.docsRepo.embedded.title"),
|
|
241
635
|
value: "embedded",
|
|
242
|
-
description: "
|
|
636
|
+
description: tr(lang, "cli", "init.choice.docsRepo.embedded.desc")
|
|
243
637
|
},
|
|
244
638
|
{
|
|
245
|
-
title: "
|
|
639
|
+
title: tr(lang, "cli", "init.choice.docsRepo.standalone.title"),
|
|
246
640
|
value: "standalone",
|
|
247
|
-
description: "
|
|
641
|
+
description: tr(lang, "cli", "init.choice.docsRepo.standalone.desc")
|
|
248
642
|
}
|
|
249
643
|
],
|
|
250
644
|
initial: 0
|
|
@@ -258,7 +652,6 @@ async function runInit(options) {
|
|
|
258
652
|
);
|
|
259
653
|
projectName = response.projectName || projectName;
|
|
260
654
|
projectType = response.projectType || projectType;
|
|
261
|
-
lang = response.lang || lang;
|
|
262
655
|
docsRepo = response.docsRepo || "embedded";
|
|
263
656
|
if (docsRepo === "standalone") {
|
|
264
657
|
const resolvedType = projectType || response.projectType || "single";
|
|
@@ -268,14 +661,14 @@ async function runInit(options) {
|
|
|
268
661
|
{
|
|
269
662
|
type: "text",
|
|
270
663
|
name: "feRoot",
|
|
271
|
-
message: "
|
|
272
|
-
validate: (value) => value.trim() ? true : "
|
|
664
|
+
message: tr(lang, "cli", "init.prompt.feRepoPath"),
|
|
665
|
+
validate: (value) => value.trim() ? true : tr(lang, "cli", "init.validation.enterPath")
|
|
273
666
|
},
|
|
274
667
|
{
|
|
275
668
|
type: "text",
|
|
276
669
|
name: "beRoot",
|
|
277
|
-
message: "
|
|
278
|
-
validate: (value) => value.trim() ? true : "
|
|
670
|
+
message: tr(lang, "cli", "init.prompt.beRepoPath"),
|
|
671
|
+
validate: (value) => value.trim() ? true : tr(lang, "cli", "init.validation.enterPath")
|
|
279
672
|
}
|
|
280
673
|
],
|
|
281
674
|
{
|
|
@@ -294,8 +687,8 @@ async function runInit(options) {
|
|
|
294
687
|
{
|
|
295
688
|
type: "text",
|
|
296
689
|
name: "projectRoot",
|
|
297
|
-
message: "
|
|
298
|
-
validate: (value) => value.trim() ? true : "
|
|
690
|
+
message: tr(lang, "cli", "init.prompt.projectRepoPath"),
|
|
691
|
+
validate: (value) => value.trim() ? true : tr(lang, "cli", "init.validation.enterPath")
|
|
299
692
|
}
|
|
300
693
|
],
|
|
301
694
|
{
|
|
@@ -311,14 +704,14 @@ async function runInit(options) {
|
|
|
311
704
|
{
|
|
312
705
|
type: "select",
|
|
313
706
|
name: "pushDocs",
|
|
314
|
-
message: "
|
|
707
|
+
message: tr(lang, "cli", "init.prompt.pushMode"),
|
|
315
708
|
choices: [
|
|
316
709
|
{
|
|
317
|
-
title: "
|
|
710
|
+
title: tr(lang, "cli", "init.choice.push.local"),
|
|
318
711
|
value: false
|
|
319
712
|
},
|
|
320
713
|
{
|
|
321
|
-
title: "
|
|
714
|
+
title: tr(lang, "cli", "init.choice.push.remote"),
|
|
322
715
|
value: true
|
|
323
716
|
}
|
|
324
717
|
],
|
|
@@ -338,8 +731,8 @@ async function runInit(options) {
|
|
|
338
731
|
{
|
|
339
732
|
type: "text",
|
|
340
733
|
name: "docsRemote",
|
|
341
|
-
message: "
|
|
342
|
-
validate: (value) => value.trim() ? true : "
|
|
734
|
+
message: tr(lang, "cli", "init.prompt.remoteUrl"),
|
|
735
|
+
validate: (value) => value.trim() ? true : tr(lang, "cli", "init.validation.enterUrl")
|
|
343
736
|
}
|
|
344
737
|
],
|
|
345
738
|
{
|
|
@@ -364,21 +757,27 @@ async function runInit(options) {
|
|
|
364
757
|
const { overwrite } = await prompts({
|
|
365
758
|
type: "confirm",
|
|
366
759
|
name: "overwrite",
|
|
367
|
-
message:
|
|
760
|
+
message: tr(lang, "cli", "init.prompt.overwrite", { dir: targetDir }),
|
|
368
761
|
initial: false
|
|
369
762
|
});
|
|
370
763
|
if (!overwrite) {
|
|
371
|
-
console.log(chalk6.yellow("
|
|
764
|
+
console.log(chalk6.yellow(tr(lang, "cli", "common.canceled")));
|
|
372
765
|
return;
|
|
373
766
|
}
|
|
374
767
|
}
|
|
375
768
|
}
|
|
376
769
|
console.log();
|
|
377
|
-
console.log(chalk6.blue("
|
|
378
|
-
console.log(
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
console.log(
|
|
770
|
+
console.log(chalk6.blue(tr(lang, "cli", "init.log.creatingDocs")));
|
|
771
|
+
console.log(
|
|
772
|
+
chalk6.gray(` ${tr(lang, "cli", "init.log.projectLabel")}: ${projectName}`)
|
|
773
|
+
);
|
|
774
|
+
console.log(
|
|
775
|
+
chalk6.gray(` ${tr(lang, "cli", "init.log.typeLabel")}: ${projectType}`)
|
|
776
|
+
);
|
|
777
|
+
console.log(chalk6.gray(` ${tr(lang, "cli", "init.log.langLabel")}: ${lang}`));
|
|
778
|
+
console.log(
|
|
779
|
+
chalk6.gray(` ${tr(lang, "cli", "init.log.pathLabel")}: ${targetDir}`)
|
|
780
|
+
);
|
|
382
781
|
console.log();
|
|
383
782
|
const templatesDir = getTemplatesDir();
|
|
384
783
|
const commonPath = path4.join(templatesDir, lang, "common");
|
|
@@ -387,7 +786,7 @@ async function runInit(options) {
|
|
|
387
786
|
await copyTemplates(commonPath, targetDir);
|
|
388
787
|
}
|
|
389
788
|
if (!await fs8.pathExists(typePath)) {
|
|
390
|
-
throw new Error(
|
|
789
|
+
throw new Error(tr(lang, "cli", "init.error.templateNotFound", { path: typePath }));
|
|
391
790
|
}
|
|
392
791
|
await copyTemplates(typePath, targetDir);
|
|
393
792
|
const featurePath = projectType === "fullstack" ? "docs/features/{be|fe}" : "docs/features";
|
|
@@ -415,17 +814,17 @@ async function runInit(options) {
|
|
|
415
814
|
}
|
|
416
815
|
const configPath = path4.join(targetDir, ".lee-spec-kit.json");
|
|
417
816
|
await fs8.writeJson(configPath, config, { spaces: 2 });
|
|
418
|
-
console.log(chalk6.green("
|
|
817
|
+
console.log(chalk6.green(tr(lang, "cli", "init.log.docsCreated")));
|
|
419
818
|
console.log();
|
|
420
|
-
await initGit(cwd, targetDir, docsRepo, pushDocs, docsRemote);
|
|
421
|
-
console.log(chalk6.blue("
|
|
422
|
-
console.log(chalk6.gray(` 1. ${targetDir}/prd/README.md \uC791\uC131`));
|
|
819
|
+
await initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote);
|
|
820
|
+
console.log(chalk6.blue(tr(lang, "cli", "init.log.nextStepsTitle")));
|
|
423
821
|
console.log(
|
|
424
|
-
chalk6.gray("
|
|
822
|
+
chalk6.gray(tr(lang, "cli", "init.log.nextSteps1", { docsDir: targetDir }))
|
|
425
823
|
);
|
|
824
|
+
console.log(chalk6.gray(tr(lang, "cli", "init.log.nextSteps2")));
|
|
426
825
|
console.log();
|
|
427
826
|
}
|
|
428
|
-
async function initGit(cwd, targetDir, docsRepo, pushDocs, docsRemote) {
|
|
827
|
+
async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
|
|
429
828
|
try {
|
|
430
829
|
const runGit = (args, workdir) => {
|
|
431
830
|
execFileSync("git", args, { cwd: workdir, stdio: "ignore" });
|
|
@@ -445,9 +844,9 @@ async function initGit(cwd, targetDir, docsRepo, pushDocs, docsRemote) {
|
|
|
445
844
|
};
|
|
446
845
|
try {
|
|
447
846
|
runGit(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
448
|
-
console.log(chalk6.blue("
|
|
847
|
+
console.log(chalk6.blue(tr(lang, "cli", "init.log.gitRepoDetectedCommit")));
|
|
449
848
|
} catch {
|
|
450
|
-
console.log(chalk6.blue("
|
|
849
|
+
console.log(chalk6.blue(tr(lang, "cli", "init.log.gitInit")));
|
|
451
850
|
runGit(["init"], cwd);
|
|
452
851
|
}
|
|
453
852
|
const relativePath = path4.relative(cwd, targetDir);
|
|
@@ -455,10 +854,10 @@ async function initGit(cwd, targetDir, docsRepo, pushDocs, docsRemote) {
|
|
|
455
854
|
if (relativePath === "." && stagedBeforeAdd && stagedBeforeAdd.length > 0) {
|
|
456
855
|
console.log(
|
|
457
856
|
chalk6.yellow(
|
|
458
|
-
|
|
857
|
+
tr(lang, "cli", "init.warn.stagedChangesSkip")
|
|
459
858
|
)
|
|
460
859
|
);
|
|
461
|
-
console.log(chalk6.gray("
|
|
860
|
+
console.log(chalk6.gray(tr(lang, "cli", "init.warn.commitManually")));
|
|
462
861
|
console.log();
|
|
463
862
|
return;
|
|
464
863
|
}
|
|
@@ -470,16 +869,18 @@ async function initGit(cwd, targetDir, docsRepo, pushDocs, docsRemote) {
|
|
|
470
869
|
if (docsRepo === "standalone" && pushDocs && docsRemote) {
|
|
471
870
|
try {
|
|
472
871
|
runGit(["remote", "add", "origin", docsRemote], cwd);
|
|
473
|
-
console.log(
|
|
872
|
+
console.log(
|
|
873
|
+
chalk6.green(tr(lang, "cli", "init.log.gitRemoteSet", { remote: docsRemote }))
|
|
874
|
+
);
|
|
474
875
|
} catch {
|
|
475
|
-
console.log(chalk6.yellow("
|
|
876
|
+
console.log(chalk6.yellow(tr(lang, "cli", "init.warn.gitRemoteExists")));
|
|
476
877
|
}
|
|
477
878
|
}
|
|
478
|
-
console.log(chalk6.green("
|
|
879
|
+
console.log(chalk6.green(tr(lang, "cli", "init.log.gitInitialCommitDone")));
|
|
479
880
|
console.log();
|
|
480
881
|
} catch {
|
|
481
882
|
console.log(
|
|
482
|
-
chalk6.yellow(
|
|
883
|
+
chalk6.yellow(tr(lang, "cli", "init.warn.skipGitInit"))
|
|
483
884
|
);
|
|
484
885
|
console.log();
|
|
485
886
|
}
|
|
@@ -536,12 +937,10 @@ async function getConfig(cwd) {
|
|
|
536
937
|
const fePath = path4.join(featuresPath, "fe");
|
|
537
938
|
const projectType = await fs8.pathExists(bePath) || await fs8.pathExists(fePath) ? "fullstack" : "single";
|
|
538
939
|
const agentsMdPath = path4.join(agentsPath, "agents.md");
|
|
539
|
-
let lang = "
|
|
940
|
+
let lang = "en";
|
|
540
941
|
if (await fs8.pathExists(agentsMdPath)) {
|
|
541
942
|
const content = await fs8.readFile(agentsMdPath, "utf-8");
|
|
542
|
-
if (
|
|
543
|
-
lang = "en";
|
|
544
|
-
}
|
|
943
|
+
if (/[가-힣]/.test(content)) lang = "ko";
|
|
545
944
|
}
|
|
546
945
|
return { docsDir: resolvedDocsDir, projectType, lang };
|
|
547
946
|
}
|
|
@@ -557,10 +956,13 @@ function featureCommand(program2) {
|
|
|
557
956
|
await runFeature(name, options);
|
|
558
957
|
} catch (error) {
|
|
559
958
|
if (error instanceof Error && error.message === "canceled") {
|
|
560
|
-
|
|
959
|
+
const config = await getConfig(process.cwd());
|
|
960
|
+
const lang = config?.lang ?? DEFAULT_LANG;
|
|
961
|
+
console.log(chalk6.yellow(`
|
|
962
|
+
${tr(lang, "cli", "common.canceled")}`));
|
|
561
963
|
process.exit(0);
|
|
562
964
|
}
|
|
563
|
-
console.error(chalk6.red("
|
|
965
|
+
console.error(chalk6.red(tr(DEFAULT_LANG, "cli", "common.errorLabel")), error);
|
|
564
966
|
process.exit(1);
|
|
565
967
|
}
|
|
566
968
|
});
|
|
@@ -570,7 +972,9 @@ async function runFeature(name, options) {
|
|
|
570
972
|
const config = await getConfig(cwd);
|
|
571
973
|
if (!config) {
|
|
572
974
|
console.error(
|
|
573
|
-
chalk6.red(
|
|
975
|
+
chalk6.red(
|
|
976
|
+
tr(DEFAULT_LANG, "cli", "common.docsNotFound")
|
|
977
|
+
)
|
|
574
978
|
);
|
|
575
979
|
process.exit(1);
|
|
576
980
|
}
|
|
@@ -582,7 +986,7 @@ async function runFeature(name, options) {
|
|
|
582
986
|
{
|
|
583
987
|
type: "select",
|
|
584
988
|
name: "repo",
|
|
585
|
-
message: "
|
|
989
|
+
message: tr(lang, "cli", "feature.selectRepo"),
|
|
586
990
|
choices: [
|
|
587
991
|
{ title: "Backend (be)", value: "be" },
|
|
588
992
|
{ title: "Frontend (fe)", value: "fe" }
|
|
@@ -615,12 +1019,20 @@ async function runFeature(name, options) {
|
|
|
615
1019
|
const featureFolderName = `${featureId}-${name}`;
|
|
616
1020
|
const featureDir = path4.join(featuresDir, featureFolderName);
|
|
617
1021
|
if (await fs8.pathExists(featureDir)) {
|
|
618
|
-
console.error(
|
|
1022
|
+
console.error(
|
|
1023
|
+
chalk6.red(
|
|
1024
|
+
tr(lang, "cli", "feature.folderExists", { path: featureDir })
|
|
1025
|
+
)
|
|
1026
|
+
);
|
|
619
1027
|
process.exit(1);
|
|
620
1028
|
}
|
|
621
1029
|
const featureBasePath = path4.join(docsDir, "features", "feature-base");
|
|
622
1030
|
if (!await fs8.pathExists(featureBasePath)) {
|
|
623
|
-
console.error(
|
|
1031
|
+
console.error(
|
|
1032
|
+
chalk6.red(
|
|
1033
|
+
tr(lang, "cli", "feature.baseNotFound")
|
|
1034
|
+
)
|
|
1035
|
+
);
|
|
624
1036
|
process.exit(1);
|
|
625
1037
|
}
|
|
626
1038
|
await fs8.copy(featureBasePath, featureDir);
|
|
@@ -650,12 +1062,24 @@ async function runFeature(name, options) {
|
|
|
650
1062
|
}
|
|
651
1063
|
await replaceInFiles(featureDir, replacements);
|
|
652
1064
|
console.log();
|
|
653
|
-
console.log(
|
|
1065
|
+
console.log(
|
|
1066
|
+
chalk6.green(
|
|
1067
|
+
tr(lang, "cli", "feature.created", { path: featureDir })
|
|
1068
|
+
)
|
|
1069
|
+
);
|
|
654
1070
|
console.log();
|
|
655
|
-
console.log(chalk6.blue("
|
|
656
|
-
console.log(
|
|
657
|
-
|
|
658
|
-
|
|
1071
|
+
console.log(chalk6.blue(tr(lang, "cli", "feature.nextStepsTitle")));
|
|
1072
|
+
console.log(
|
|
1073
|
+
chalk6.gray(
|
|
1074
|
+
tr(lang, "cli", "feature.nextSteps1", { path: featureDir })
|
|
1075
|
+
)
|
|
1076
|
+
);
|
|
1077
|
+
console.log(chalk6.gray(tr(lang, "cli", "feature.nextSteps2")));
|
|
1078
|
+
console.log(
|
|
1079
|
+
chalk6.gray(
|
|
1080
|
+
tr(lang, "cli", "feature.nextSteps3")
|
|
1081
|
+
)
|
|
1082
|
+
);
|
|
659
1083
|
console.log();
|
|
660
1084
|
}
|
|
661
1085
|
async function getNextFeatureId(docsDir, projectType) {
|
|
@@ -685,128 +1109,6 @@ async function getNextFeatureId(docsDir, projectType) {
|
|
|
685
1109
|
return `F${String(next).padStart(width, "0")}`;
|
|
686
1110
|
}
|
|
687
1111
|
|
|
688
|
-
// src/utils/context/i18n.ts
|
|
689
|
-
function formatTemplate(template, vars) {
|
|
690
|
-
return template.replace(/\{(\w+)\}/g, (_, key) => {
|
|
691
|
-
const value = vars[key];
|
|
692
|
-
return value === void 0 ? `{${key}}` : String(value);
|
|
693
|
-
});
|
|
694
|
-
}
|
|
695
|
-
var I18N = {
|
|
696
|
-
ko: {
|
|
697
|
-
steps: {
|
|
698
|
-
featureFolder: "Feature \uD3F4\uB354 \uC0DD\uC131",
|
|
699
|
-
specWrite: "spec.md \uC791\uC131",
|
|
700
|
-
specApprove: "spec.md \uC2B9\uC778",
|
|
701
|
-
planWrite: "plan.md \uC791\uC131",
|
|
702
|
-
planApprove: "plan.md \uC2B9\uC778",
|
|
703
|
-
tasksWrite: "tasks.md \uC791\uC131",
|
|
704
|
-
docsCommitPlanning: "\uBB38\uC11C \uCEE4\uBC0B(\uB3D9\uAE30\uD654)",
|
|
705
|
-
issueCreate: "GitHub Issue \uC0DD\uC131",
|
|
706
|
-
branchCreate: "\uBE0C\uB79C\uCE58 \uC0DD\uC131",
|
|
707
|
-
tasksExecute: "\uD0DC\uC2A4\uD06C \uC2E4\uD589",
|
|
708
|
-
prCreate: "PR \uC0DD\uC131",
|
|
709
|
-
codeReview: "\uCF54\uB4DC \uB9AC\uBDF0",
|
|
710
|
-
featureDone: "Feature \uC644\uB8CC"
|
|
711
|
-
},
|
|
712
|
-
messages: {
|
|
713
|
-
specCreate: "spec.md \uD15C\uD50C\uB9BF\uC744 \uBCF5\uC0AC\uD574 \uC791\uC131\uD558\uC138\uC694. (features/feature-base/spec.md \uCC38\uACE0)",
|
|
714
|
-
specImprove: "spec.md\uB97C \uBCF4\uC644\uD558\uACE0 \uC0C1\uD0DC\uB97C Review\uB85C \uBCC0\uACBD\uD558\uC138\uC694.",
|
|
715
|
-
specApproval: "spec.md \uB0B4\uC6A9\uC744 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uACF5\uC720\uD558\uACE0 \uC2B9\uC778(OK)\uC744 \uBC1B\uC73C\uC138\uC694.",
|
|
716
|
-
planCreate: "plan.md \uD15C\uD50C\uB9BF\uC744 \uBCF5\uC0AC\uD574 \uC791\uC131\uD558\uC138\uC694. (features/feature-base/plan.md \uCC38\uACE0)",
|
|
717
|
-
planImprove: "plan.md\uB97C \uBCF4\uC644\uD558\uACE0 \uC0C1\uD0DC\uB97C Review\uB85C \uBCC0\uACBD\uD558\uC138\uC694.",
|
|
718
|
-
planApproval: "plan.md \uB0B4\uC6A9\uC744 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uACF5\uC720\uD558\uACE0 \uC2B9\uC778(OK)\uC744 \uBC1B\uC73C\uC138\uC694.",
|
|
719
|
-
tasksCreate: "tasks.md \uD15C\uD50C\uB9BF\uC744 \uBCF5\uC0AC\uD574 \uD0DC\uC2A4\uD06C\uB97C \uC791\uC131\uD558\uC138\uC694. (features/feature-base/tasks.md \uCC38\uACE0)",
|
|
720
|
-
tasksNeedAtLeastOne: "tasks.md\uC5D0 \uCD5C\uC18C 1\uAC1C \uC774\uC0C1\uC758 \uD0DC\uC2A4\uD06C\uB97C \uC791\uC131\uD558\uC138\uC694.",
|
|
721
|
-
docsCommitPlanning: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(planning): {folderName} \uAE30\uD68D \uBB38\uC11C"',
|
|
722
|
-
issueCreateAndWrite: "GitHub Issue\uB97C \uC0DD\uC131\uD55C \uB4A4, spec.md/tasks.md\uC758 \uC774\uC288 \uBC88\uD638\uB97C \uCC44\uC6B0\uACE0 \uBB38\uC11C \uCEE4\uBC0B\uC744 \uC900\uBE44\uD558\uC138\uC694. (skills/create-issue.md \uCC38\uACE0)",
|
|
723
|
-
docsCommitIssueUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(#{issueNumber}): {folderName} \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8"',
|
|
724
|
-
standaloneNeedsProjectRoot: "standalone \uBAA8\uB4DC\uC5D0\uC11C\uB294 projectRoot \uC124\uC815\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. (npx lee-spec-kit config --project-root ...)",
|
|
725
|
-
createBranch: 'cd "{projectGitCwd}" && git checkout -b feat/{issueNumber}-{slug}',
|
|
726
|
-
tasksAllDoneButNoChecklist: '\uBAA8\uB4E0 \uD0DC\uC2A4\uD06C\uAC00 DONE\uC774\uC9C0\uB9CC \uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 \uC139\uC158\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. tasks.md\uC758 "\uC644\uB8CC \uC870\uAC74" \uC139\uC158\uC744 \uCD94\uAC00/\uD655\uC778\uD558\uC138\uC694.',
|
|
727
|
-
tasksAllDoneButChecklist: "\uBAA8\uB4E0 \uD0DC\uC2A4\uD06C\uAC00 DONE\uC774\uC9C0\uB9CC \uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\uAC00 \uC644\uC804\uD788 \uCCB4\uD06C\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. ({checked}/{total})",
|
|
728
|
-
finishDoingTask: '\uD604\uC7AC DOING/REVIEW \uC911\uC778 \uD0DC\uC2A4\uD06C\uB97C \uC644\uB8CC\uD558\uC138\uC694: "{title}" ({done}/{total}) (skills/execute-task.md \uCC38\uACE0)',
|
|
729
|
-
startNextTodoTask: '\uB2E4\uC74C TODO \uD0DC\uC2A4\uD06C\uB97C \uC2DC\uC791\uD558\uC138\uC694: "{title}" ({done}/{total}) (skills/execute-task.md \uCC38\uACE0)',
|
|
730
|
-
checkTaskStatuses: "\uD0DC\uC2A4\uD06C \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694. ({done}/{total}) (skills/execute-task.md \uCC38\uACE0)",
|
|
731
|
-
prLegacyAsk: "tasks.md\uC5D0 PR/PR \uC0C1\uD0DC \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uD15C\uD50C\uB9BF\uC744 \uCD5C\uC2E0 \uD3EC\uB9F7\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD560\uAE4C\uC694? (OK \uD544\uC694)",
|
|
732
|
-
prCreate: "PR\uC744 \uC0DD\uC131\uD558\uACE0 tasks.md\uC5D0 PR \uB9C1\uD06C\uB97C \uAE30\uB85D\uD558\uC138\uC694. (skills/create-pr.md \uCC38\uACE0)",
|
|
733
|
-
prFillStatus: "tasks.md\uC758 PR \uC0C1\uD0DC\uB97C Draft/Review/Approved \uC911 \uD558\uB098\uB85C \uC124\uC815\uD558\uC138\uC694. (merge \uD6C4 Approved\uB85C \uC5C5\uB370\uC774\uD2B8)",
|
|
734
|
-
prResolveReview: "\uB9AC\uBDF0 \uCF54\uBA58\uD2B8\uB97C \uD574\uACB0\uD558\uACE0 PR \uC0C1\uD0DC\uB97C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694. (PR \uC0C1\uD0DC: Review \u2192 Approved)",
|
|
735
|
-
prRequestReview: "\uB9AC\uBDF0\uC5B4\uC5D0\uAC8C \uB9AC\uBDF0\uB97C \uC694\uCCAD\uD558\uACE0 PR \uC0C1\uD0DC\uB97C Review\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.",
|
|
736
|
-
featureDone: "PR\uC774 Approved\uC774\uACE0 \uBAA8\uB4E0 \uD0DC\uC2A4\uD06C/\uC644\uB8CC \uC870\uAC74\uC774 \uCDA9\uC871\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC774 Feature\uB294 \uC644\uB8CC \uC0C1\uD0DC\uC785\uB2C8\uB2E4.",
|
|
737
|
-
fallbackRerunContext: "\uC0C1\uD0DC\uB97C \uD310\uBCC4\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBB38\uC11C\uB97C \uD655\uC778\uD55C \uB4A4 \uB2E4\uC2DC context\uB97C \uC2E4\uD589\uD558\uC138\uC694."
|
|
738
|
-
},
|
|
739
|
-
warnings: {
|
|
740
|
-
projectBranchUnavailable: "\uD504\uB85C\uC81D\uD2B8 \uBE0C\uB79C\uCE58\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. (standalone \uBAA8\uB4DC\uC5D0\uC11C\uB294 projectRoot\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.)",
|
|
741
|
-
docsGitUnavailable: "docs \uB808\uD3EC\uC758 git \uC0C1\uD0DC\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. (\uB808\uD3EC \uC704\uCE58 / git init \uD655\uC778)",
|
|
742
|
-
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.",
|
|
743
|
-
workflowSpecNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC spec.md \uC0C1\uD0DC\uAC00 Approved\uAC00 \uC544\uB2D9\uB2C8\uB2E4. (spec.md\uC758 \uC0C1\uD0DC\uB97C Approved\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.)",
|
|
744
|
-
workflowPlanNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC plan.md \uC0C1\uD0DC\uAC00 Approved\uAC00 \uC544\uB2D9\uB2C8\uB2E4. (plan.md\uC758 \uC0C1\uD0DC\uB97C Approved\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.)",
|
|
745
|
-
workflowPrLinkMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC PR \uB9C1\uD06C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. (tasks.md\uC758 PR \uD544\uB4DC\uB97C \uCC44\uC6B0\uC138\uC694.)",
|
|
746
|
-
workflowPrStatusMissing: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC PR \uC0C1\uD0DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. (tasks.md\uC758 PR \uC0C1\uD0DC\uB97C Draft/Review/Approved \uC911 \uD558\uB098\uB85C \uC124\uC815\uD558\uC138\uC694.)",
|
|
747
|
-
workflowPrStatusNotApproved: "\uC644\uB8CC \uC0C1\uD0DC\uC774\uC9C0\uB9CC PR \uC0C1\uD0DC\uAC00 Approved\uAC00 \uC544\uB2D9\uB2C8\uB2E4. (merge \uD6C4 tasks.md\uC758 PR \uC0C1\uD0DC\uB97C Approved\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.)"
|
|
748
|
-
}
|
|
749
|
-
},
|
|
750
|
-
en: {
|
|
751
|
-
steps: {
|
|
752
|
-
featureFolder: "Create feature folder",
|
|
753
|
-
specWrite: "Write spec.md",
|
|
754
|
-
specApprove: "Approve spec.md",
|
|
755
|
-
planWrite: "Write plan.md",
|
|
756
|
-
planApprove: "Approve plan.md",
|
|
757
|
-
tasksWrite: "Write tasks.md",
|
|
758
|
-
docsCommitPlanning: "Commit docs (sync)",
|
|
759
|
-
issueCreate: "Create GitHub Issue",
|
|
760
|
-
branchCreate: "Create branch",
|
|
761
|
-
tasksExecute: "Execute tasks",
|
|
762
|
-
prCreate: "Create PR",
|
|
763
|
-
codeReview: "Code review",
|
|
764
|
-
featureDone: "Feature done"
|
|
765
|
-
},
|
|
766
|
-
messages: {
|
|
767
|
-
specCreate: "Copy the spec.md template and write it. (See features/feature-base/spec.md)",
|
|
768
|
-
specImprove: "Improve spec.md and set Status to Review.",
|
|
769
|
-
specApproval: "Share spec.md with the user and get approval (OK).",
|
|
770
|
-
planCreate: "Copy the plan.md template and write it. (See features/feature-base/plan.md)",
|
|
771
|
-
planImprove: "Improve plan.md and set Status to Review.",
|
|
772
|
-
planApproval: "Share plan.md with the user and get approval (OK).",
|
|
773
|
-
tasksCreate: "Copy the tasks.md template and write tasks. (See features/feature-base/tasks.md)",
|
|
774
|
-
tasksNeedAtLeastOne: "Add at least one task to tasks.md.",
|
|
775
|
-
docsCommitPlanning: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(planning): {folderName} planning docs"',
|
|
776
|
-
issueCreateAndWrite: "Create a GitHub Issue, then fill in the issue number in spec.md/tasks.md and prepare to commit docs. (See skills/create-issue.md)",
|
|
777
|
-
docsCommitIssueUpdate: 'cd "{docsGitCwd}" && git add "{featurePath}" && git commit -m "docs(#{issueNumber}): {folderName} docs update"',
|
|
778
|
-
standaloneNeedsProjectRoot: "In standalone mode, projectRoot is required. (npx lee-spec-kit config --project-root ...)",
|
|
779
|
-
createBranch: 'cd "{projectGitCwd}" && git checkout -b feat/{issueNumber}-{slug}',
|
|
780
|
-
tasksAllDoneButNoChecklist: 'All tasks are DONE but no completion checklist section was found. Add/verify the "Completion Criteria" section in tasks.md.',
|
|
781
|
-
tasksAllDoneButChecklist: "All tasks are DONE but the completion checklist is not fully checked. ({checked}/{total})",
|
|
782
|
-
finishDoingTask: 'Finish the active DOING/REVIEW task: "{title}" ({done}/{total}) (See skills/execute-task.md)',
|
|
783
|
-
startNextTodoTask: 'Start the next TODO task: "{title}" ({done}/{total}) (See skills/execute-task.md)',
|
|
784
|
-
checkTaskStatuses: "Check task statuses. ({done}/{total}) (See skills/execute-task.md)",
|
|
785
|
-
prLegacyAsk: "Legacy tasks.md format detected (missing PR/PR Status fields). Update to the latest format? (OK required)",
|
|
786
|
-
prCreate: "Create a PR and record the PR link in tasks.md. (See skills/create-pr.md)",
|
|
787
|
-
prFillStatus: "Set PR Status in tasks.md to Draft/Review/Approved. (After merge, update to Approved)",
|
|
788
|
-
prResolveReview: "Resolve review comments and update PR status. (PR Status: Review \u2192 Approved)",
|
|
789
|
-
prRequestReview: "Request reviews and update PR status to Review.",
|
|
790
|
-
featureDone: "PR is Approved and all tasks/completion criteria are satisfied. This feature is done.",
|
|
791
|
-
fallbackRerunContext: "Unable to determine current state. Verify docs and run context again."
|
|
792
|
-
},
|
|
793
|
-
warnings: {
|
|
794
|
-
projectBranchUnavailable: "Cannot determine project branch. (In standalone mode, projectRoot is required.)",
|
|
795
|
-
docsGitUnavailable: "Cannot read git status for the docs repo. (Check repo location / git init.)",
|
|
796
|
-
legacyTasksPrFields: "Legacy tasks.md format detected. Add `PR` and `PR Status` fields before PR steps.",
|
|
797
|
-
workflowSpecNotApproved: "Implementation is done but spec.md Status is not Approved. (Update spec.md Status to Approved.)",
|
|
798
|
-
workflowPlanNotApproved: "Implementation is done but plan.md Status is not Approved. (Update plan.md Status to Approved.)",
|
|
799
|
-
workflowPrLinkMissing: "Implementation is done but PR link is missing. (Fill the PR field in tasks.md.)",
|
|
800
|
-
workflowPrStatusMissing: "Implementation is done but PR Status is missing. (Set PR Status to Draft/Review/Approved in tasks.md.)",
|
|
801
|
-
workflowPrStatusNotApproved: "Implementation is done but PR Status is not Approved. (After merge, update PR Status to Approved in tasks.md.)"
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
};
|
|
805
|
-
function tr(lang, category, key, vars = {}) {
|
|
806
|
-
const template = I18N[lang][category][key] ?? I18N.ko[category][key] ?? `${category}.${key}`;
|
|
807
|
-
return formatTemplate(template, vars);
|
|
808
|
-
}
|
|
809
|
-
|
|
810
1112
|
// src/utils/context/steps.ts
|
|
811
1113
|
function isCompletionChecklistDone(feature) {
|
|
812
1114
|
return !!feature.completionChecklist && feature.completionChecklist.total > 0 && feature.completionChecklist.checked === feature.completionChecklist.total;
|
|
@@ -893,21 +1195,12 @@ function getStepDefinitions(lang) {
|
|
|
893
1195
|
step: 6,
|
|
894
1196
|
name: tr(lang, "steps", "tasksWrite"),
|
|
895
1197
|
checklist: {
|
|
896
|
-
done: (f) => f.docs.tasksExists && f.tasks.total > 0
|
|
1198
|
+
done: (f) => f.docs.tasksExists && f.tasks.total > 0,
|
|
897
1199
|
detail: (f) => f.tasks.total > 0 ? `(${f.tasks.total})` : ""
|
|
898
1200
|
},
|
|
899
1201
|
current: {
|
|
900
|
-
when: (f) => f.planStatus === "Approved" && (!f.docs.tasksExists || f.tasks.total === 0
|
|
1202
|
+
when: (f) => f.planStatus === "Approved" && (!f.docs.tasksExists || f.tasks.total === 0),
|
|
901
1203
|
actions: (f) => {
|
|
902
|
-
if (f.docs.tasksExists && f.tasks.total > 0 && (!f.docs.prFieldExists || !f.docs.prStatusFieldExists)) {
|
|
903
|
-
return [
|
|
904
|
-
{
|
|
905
|
-
type: "instruction",
|
|
906
|
-
requiresUserOk: true,
|
|
907
|
-
message: tr(lang, "messages", "prLegacyAsk")
|
|
908
|
-
}
|
|
909
|
-
];
|
|
910
|
-
}
|
|
911
1204
|
if (!f.docs.tasksExists) {
|
|
912
1205
|
return [
|
|
913
1206
|
{
|
|
@@ -929,10 +1222,10 @@ function getStepDefinitions(lang) {
|
|
|
929
1222
|
step: 7,
|
|
930
1223
|
name: tr(lang, "steps", "docsCommitPlanning"),
|
|
931
1224
|
checklist: {
|
|
932
|
-
done: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" &&
|
|
1225
|
+
done: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" && f.git.docsEverCommitted
|
|
933
1226
|
},
|
|
934
1227
|
current: {
|
|
935
|
-
when: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" && !f.activeTask && f.git.docsHasUncommittedChanges,
|
|
1228
|
+
when: (f) => f.docs.tasksExists && f.tasks.total > 0 && f.specStatus === "Approved" && f.planStatus === "Approved" && !f.activeTask && !f.git.docsEverCommitted && f.git.docsHasUncommittedChanges,
|
|
936
1229
|
actions: (f) => {
|
|
937
1230
|
if (f.issueNumber) {
|
|
938
1231
|
return [
|
|
@@ -1208,6 +1501,18 @@ function getGitStatusPorcelain(cwd, relativePaths) {
|
|
|
1208
1501
|
return void 0;
|
|
1209
1502
|
}
|
|
1210
1503
|
}
|
|
1504
|
+
function getLastCommitForPath(cwd, relativePath) {
|
|
1505
|
+
try {
|
|
1506
|
+
const out = execSync(`git rev-list -n 1 HEAD -- "${relativePath}"`, {
|
|
1507
|
+
cwd,
|
|
1508
|
+
encoding: "utf-8",
|
|
1509
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1510
|
+
}).trim();
|
|
1511
|
+
return out || void 0;
|
|
1512
|
+
} catch {
|
|
1513
|
+
return void 0;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1211
1516
|
function getGitTopLevel(cwd) {
|
|
1212
1517
|
try {
|
|
1213
1518
|
return execSync("git rev-parse --show-toplevel", {
|
|
@@ -1420,12 +1725,20 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
1420
1725
|
const relativeFeaturePathFromDocs = path4.relative(context.docsDir, featurePath);
|
|
1421
1726
|
const docsStatus = getGitStatusPorcelain(context.docsGitCwd, [relativeFeaturePathFromDocs]);
|
|
1422
1727
|
const docsHasUncommittedChanges = docsStatus === void 0 ? true : docsStatus.trim().length > 0;
|
|
1728
|
+
const docsLastCommit = getLastCommitForPath(
|
|
1729
|
+
context.docsGitCwd,
|
|
1730
|
+
relativeFeaturePathFromDocs
|
|
1731
|
+
);
|
|
1732
|
+
const docsEverCommitted = !!docsLastCommit;
|
|
1423
1733
|
if (docsStatus === void 0) {
|
|
1424
1734
|
warnings.push(tr(lang, "warnings", "docsGitUnavailable"));
|
|
1425
1735
|
}
|
|
1426
1736
|
if (tasksExists && (!prFieldExists || !prStatusFieldExists)) {
|
|
1427
1737
|
warnings.push(tr(lang, "warnings", "legacyTasksPrFields"));
|
|
1428
1738
|
}
|
|
1739
|
+
if (docsEverCommitted && docsHasUncommittedChanges) {
|
|
1740
|
+
warnings.push(tr(lang, "warnings", "docsUncommittedChanges"));
|
|
1741
|
+
}
|
|
1429
1742
|
const implementationDone = tasksExists && tasksSummary.total > 0 && tasksSummary.total === tasksSummary.done && isCompletionChecklistDone2({ completionChecklist });
|
|
1430
1743
|
const workflowDone = implementationDone && specStatus === "Approved" && planStatus === "Approved" && isPrMetadataConfigured2({ docs: { prFieldExists, prStatusFieldExists } }) && !!prLink && prStatus === "Approved";
|
|
1431
1744
|
if (implementationDone && !workflowDone) {
|
|
@@ -1468,6 +1781,7 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
1468
1781
|
docsGitCwd: context.docsGitCwd,
|
|
1469
1782
|
projectGitCwd: context.projectGitCwd,
|
|
1470
1783
|
onExpectedBranch,
|
|
1784
|
+
docsEverCommitted,
|
|
1471
1785
|
docsHasUncommittedChanges
|
|
1472
1786
|
},
|
|
1473
1787
|
docs: {
|
|
@@ -1594,7 +1908,7 @@ function statusCommand(program2) {
|
|
|
1594
1908
|
try {
|
|
1595
1909
|
await runStatus(options);
|
|
1596
1910
|
} catch (error) {
|
|
1597
|
-
console.error(chalk6.red("
|
|
1911
|
+
console.error(chalk6.red(tr(DEFAULT_LANG, "cli", "common.errorLabel")), error);
|
|
1598
1912
|
process.exit(1);
|
|
1599
1913
|
}
|
|
1600
1914
|
});
|
|
@@ -1603,12 +1917,15 @@ async function runStatus(options) {
|
|
|
1603
1917
|
const cwd = process.cwd();
|
|
1604
1918
|
const config = await getConfig(cwd);
|
|
1605
1919
|
if (!config) {
|
|
1920
|
+
console.error(chalk6.red(tr(DEFAULT_LANG, "cli", "common.errorLabel")));
|
|
1606
1921
|
console.error(
|
|
1607
|
-
chalk6.red(
|
|
1922
|
+
chalk6.red(
|
|
1923
|
+
tr(DEFAULT_LANG, "cli", "common.docsNotFound")
|
|
1924
|
+
)
|
|
1608
1925
|
);
|
|
1609
1926
|
process.exit(1);
|
|
1610
1927
|
}
|
|
1611
|
-
const { docsDir, projectType, projectName } = config;
|
|
1928
|
+
const { docsDir, projectType, projectName, lang } = config;
|
|
1612
1929
|
const featuresDir = path4.join(docsDir, "features");
|
|
1613
1930
|
const scan = await scanFeatures(config);
|
|
1614
1931
|
const features = [];
|
|
@@ -1645,7 +1962,7 @@ async function runStatus(options) {
|
|
|
1645
1962
|
});
|
|
1646
1963
|
}
|
|
1647
1964
|
if (features.length === 0) {
|
|
1648
|
-
console.log(chalk6.yellow("
|
|
1965
|
+
console.log(chalk6.yellow(tr(lang, "cli", "status.noFeatures")));
|
|
1649
1966
|
return;
|
|
1650
1967
|
}
|
|
1651
1968
|
if (options.strict) {
|
|
@@ -1653,7 +1970,7 @@ async function runStatus(options) {
|
|
|
1653
1970
|
([, paths]) => paths.length > 1
|
|
1654
1971
|
);
|
|
1655
1972
|
if (duplicates.length > 0) {
|
|
1656
|
-
console.error(chalk6.red("
|
|
1973
|
+
console.error(chalk6.red(tr(lang, "cli", "status.duplicateIds")));
|
|
1657
1974
|
for (const [id, paths] of duplicates) {
|
|
1658
1975
|
console.error(chalk6.red(` ${id}:`));
|
|
1659
1976
|
for (const p of paths) {
|
|
@@ -1664,7 +1981,7 @@ async function runStatus(options) {
|
|
|
1664
1981
|
}
|
|
1665
1982
|
const unknowns = [...idMap.entries()].filter(([id]) => id === "UNKNOWN");
|
|
1666
1983
|
if (unknowns.length > 0) {
|
|
1667
|
-
console.error(chalk6.red("
|
|
1984
|
+
console.error(chalk6.red(tr(lang, "cli", "status.missingIds")));
|
|
1668
1985
|
for (const [, paths] of unknowns) {
|
|
1669
1986
|
for (const p of paths) {
|
|
1670
1987
|
console.error(chalk6.red(` - ${p}`));
|
|
@@ -1703,7 +2020,11 @@ async function runStatus(options) {
|
|
|
1703
2020
|
""
|
|
1704
2021
|
].join("\n");
|
|
1705
2022
|
await fs8.writeFile(outputPath, content, "utf-8");
|
|
1706
|
-
console.log(
|
|
2023
|
+
console.log(
|
|
2024
|
+
chalk6.green(
|
|
2025
|
+
tr(lang, "cli", "status.wrote", { path: outputPath })
|
|
2026
|
+
)
|
|
2027
|
+
);
|
|
1707
2028
|
}
|
|
1708
2029
|
}
|
|
1709
2030
|
function escapeRegExp2(value) {
|
|
@@ -1734,10 +2055,13 @@ function updateCommand(program2) {
|
|
|
1734
2055
|
await runUpdate(options);
|
|
1735
2056
|
} catch (error) {
|
|
1736
2057
|
if (error instanceof Error && error.message === "canceled") {
|
|
1737
|
-
|
|
2058
|
+
const config = await getConfig(process.cwd());
|
|
2059
|
+
const lang = config?.lang ?? DEFAULT_LANG;
|
|
2060
|
+
console.log(chalk6.yellow(`
|
|
2061
|
+
${tr(lang, "cli", "common.canceled")}`));
|
|
1738
2062
|
process.exit(0);
|
|
1739
2063
|
}
|
|
1740
|
-
console.error(chalk6.red("
|
|
2064
|
+
console.error(chalk6.red(tr(DEFAULT_LANG, "cli", "common.errorLabel")), error);
|
|
1741
2065
|
process.exit(1);
|
|
1742
2066
|
}
|
|
1743
2067
|
});
|
|
@@ -1746,8 +2070,11 @@ async function runUpdate(options) {
|
|
|
1746
2070
|
const cwd = process.cwd();
|
|
1747
2071
|
const config = await getConfig(cwd);
|
|
1748
2072
|
if (!config) {
|
|
2073
|
+
console.error(chalk6.red(tr(DEFAULT_LANG, "cli", "common.errorLabel")));
|
|
1749
2074
|
console.error(
|
|
1750
|
-
chalk6.red(
|
|
2075
|
+
chalk6.red(
|
|
2076
|
+
tr(DEFAULT_LANG, "cli", "common.docsNotFound")
|
|
2077
|
+
)
|
|
1751
2078
|
);
|
|
1752
2079
|
process.exit(1);
|
|
1753
2080
|
}
|
|
@@ -1756,13 +2083,15 @@ async function runUpdate(options) {
|
|
|
1756
2083
|
const sourceDir = path4.join(templatesDir, lang, projectType);
|
|
1757
2084
|
const updateAgents = options.agents || !options.agents && !options.templates;
|
|
1758
2085
|
const updateTemplates = options.templates || !options.agents && !options.templates;
|
|
1759
|
-
console.log(chalk6.blue("
|
|
1760
|
-
console.log(chalk6.gray(` -
|
|
1761
|
-
console.log(
|
|
2086
|
+
console.log(chalk6.blue(tr(lang, "cli", "update.start")));
|
|
2087
|
+
console.log(chalk6.gray(` - ${tr(lang, "cli", "update.langLabel")}: ${lang}`));
|
|
2088
|
+
console.log(
|
|
2089
|
+
chalk6.gray(` - ${tr(lang, "cli", "update.typeLabel")}: ${projectType}`)
|
|
2090
|
+
);
|
|
1762
2091
|
console.log();
|
|
1763
2092
|
let updatedCount = 0;
|
|
1764
2093
|
if (updateAgents) {
|
|
1765
|
-
console.log(chalk6.blue("
|
|
2094
|
+
console.log(chalk6.blue(tr(lang, "cli", "update.updatingAgents")));
|
|
1766
2095
|
const commonAgents = path4.join(templatesDir, lang, "common", "agents");
|
|
1767
2096
|
const typeAgents = path4.join(templatesDir, lang, projectType, "agents");
|
|
1768
2097
|
const targetAgents = path4.join(docsDir, "agents");
|
|
@@ -1775,34 +2104,51 @@ async function runUpdate(options) {
|
|
|
1775
2104
|
commonAgents,
|
|
1776
2105
|
targetAgents,
|
|
1777
2106
|
options.force,
|
|
1778
|
-
replacements
|
|
2107
|
+
replacements,
|
|
2108
|
+
lang
|
|
1779
2109
|
);
|
|
1780
2110
|
updatedCount += count;
|
|
1781
2111
|
}
|
|
1782
2112
|
if (await fs8.pathExists(typeAgents)) {
|
|
1783
|
-
const count = await updateFolder(
|
|
2113
|
+
const count = await updateFolder(
|
|
2114
|
+
typeAgents,
|
|
2115
|
+
targetAgents,
|
|
2116
|
+
options.force,
|
|
2117
|
+
void 0,
|
|
2118
|
+
lang
|
|
2119
|
+
);
|
|
1784
2120
|
updatedCount += count;
|
|
1785
2121
|
}
|
|
1786
|
-
console.log(chalk6.green(` \u2705
|
|
2122
|
+
console.log(chalk6.green(` \u2705 ${tr(lang, "cli", "update.agentsUpdated")}`));
|
|
1787
2123
|
}
|
|
1788
2124
|
if (updateTemplates) {
|
|
1789
|
-
console.log(chalk6.blue("
|
|
2125
|
+
console.log(chalk6.blue(tr(lang, "cli", "update.updatingFeatureBase")));
|
|
1790
2126
|
const sourceFeatureBase = path4.join(sourceDir, "features", "feature-base");
|
|
1791
2127
|
const targetFeatureBase = path4.join(docsDir, "features", "feature-base");
|
|
1792
2128
|
if (await fs8.pathExists(sourceFeatureBase)) {
|
|
1793
2129
|
const count = await updateFolder(
|
|
1794
2130
|
sourceFeatureBase,
|
|
1795
2131
|
targetFeatureBase,
|
|
1796
|
-
options.force
|
|
2132
|
+
options.force,
|
|
2133
|
+
void 0,
|
|
2134
|
+
lang
|
|
1797
2135
|
);
|
|
1798
2136
|
updatedCount += count;
|
|
1799
|
-
console.log(
|
|
2137
|
+
console.log(
|
|
2138
|
+
chalk6.green(
|
|
2139
|
+
` \u2705 ${tr(lang, "cli", "update.filesUpdated", { count })}`
|
|
2140
|
+
)
|
|
2141
|
+
);
|
|
1800
2142
|
}
|
|
1801
2143
|
}
|
|
1802
2144
|
console.log();
|
|
1803
|
-
console.log(
|
|
2145
|
+
console.log(
|
|
2146
|
+
chalk6.green(
|
|
2147
|
+
`\u2705 ${tr(lang, "cli", "update.updatedTotal", { count: updatedCount })}`
|
|
2148
|
+
)
|
|
2149
|
+
);
|
|
1804
2150
|
}
|
|
1805
|
-
async function updateFolder(sourceDir, targetDir, force, replacements) {
|
|
2151
|
+
async function updateFolder(sourceDir, targetDir, force, replacements, lang = DEFAULT_LANG) {
|
|
1806
2152
|
const protectedFiles = /* @__PURE__ */ new Set(["custom.md", "constitution.md"]);
|
|
1807
2153
|
await fs8.ensureDir(targetDir);
|
|
1808
2154
|
const files = await fs8.readdir(sourceDir);
|
|
@@ -1829,14 +2175,18 @@ async function updateFolder(sourceDir, targetDir, force, replacements) {
|
|
|
1829
2175
|
}
|
|
1830
2176
|
if (!force) {
|
|
1831
2177
|
console.log(
|
|
1832
|
-
chalk6.yellow(
|
|
2178
|
+
chalk6.yellow(
|
|
2179
|
+
` \u26A0\uFE0F ${file} - ${tr(lang, "cli", "update.changeDetected")}`
|
|
2180
|
+
)
|
|
1833
2181
|
);
|
|
1834
2182
|
shouldUpdate = false;
|
|
1835
2183
|
}
|
|
1836
2184
|
}
|
|
1837
2185
|
if (shouldUpdate) {
|
|
1838
2186
|
await fs8.writeFile(targetPath, sourceContent);
|
|
1839
|
-
console.log(
|
|
2187
|
+
console.log(
|
|
2188
|
+
chalk6.gray(` \u{1F4C4} ${tr(lang, "cli", "update.fileUpdated", { file })}`)
|
|
2189
|
+
);
|
|
1840
2190
|
updatedCount++;
|
|
1841
2191
|
}
|
|
1842
2192
|
} else if (stat.isDirectory()) {
|
|
@@ -1844,7 +2194,8 @@ async function updateFolder(sourceDir, targetDir, force, replacements) {
|
|
|
1844
2194
|
sourcePath,
|
|
1845
2195
|
targetPath,
|
|
1846
2196
|
force,
|
|
1847
|
-
replacements
|
|
2197
|
+
replacements,
|
|
2198
|
+
lang
|
|
1848
2199
|
);
|
|
1849
2200
|
updatedCount += subCount;
|
|
1850
2201
|
}
|
|
@@ -1857,10 +2208,13 @@ function configCommand(program2) {
|
|
|
1857
2208
|
await runConfig(options);
|
|
1858
2209
|
} catch (error) {
|
|
1859
2210
|
if (error instanceof Error && error.message === "canceled") {
|
|
1860
|
-
|
|
2211
|
+
const config = await getConfig(process.cwd());
|
|
2212
|
+
const lang = config?.lang ?? DEFAULT_LANG;
|
|
2213
|
+
console.log(chalk6.yellow(`
|
|
2214
|
+
${tr(lang, "cli", "common.canceled")}`));
|
|
1861
2215
|
process.exit(0);
|
|
1862
2216
|
}
|
|
1863
|
-
console.error(chalk6.red("
|
|
2217
|
+
console.error(chalk6.red(tr(DEFAULT_LANG, "cli", "common.errorLabel")), error);
|
|
1864
2218
|
process.exit(1);
|
|
1865
2219
|
}
|
|
1866
2220
|
});
|
|
@@ -1870,16 +2224,22 @@ async function runConfig(options) {
|
|
|
1870
2224
|
const config = await getConfig(cwd);
|
|
1871
2225
|
if (!config) {
|
|
1872
2226
|
console.log(
|
|
1873
|
-
chalk6.red(
|
|
2227
|
+
chalk6.red(
|
|
2228
|
+
tr(DEFAULT_LANG, "cli", "common.configNotFound")
|
|
2229
|
+
)
|
|
1874
2230
|
);
|
|
1875
2231
|
process.exit(1);
|
|
1876
2232
|
}
|
|
1877
2233
|
const configPath = path4.join(config.docsDir, ".lee-spec-kit.json");
|
|
1878
2234
|
if (!options.projectRoot) {
|
|
1879
2235
|
console.log();
|
|
1880
|
-
console.log(chalk6.blue("
|
|
2236
|
+
console.log(chalk6.blue(tr(config.lang, "cli", "config.currentTitle")));
|
|
1881
2237
|
console.log();
|
|
1882
|
-
console.log(
|
|
2238
|
+
console.log(
|
|
2239
|
+
chalk6.gray(
|
|
2240
|
+
` ${tr(config.lang, "cli", "config.pathLabel")}: ${configPath}`
|
|
2241
|
+
)
|
|
2242
|
+
);
|
|
1883
2243
|
console.log();
|
|
1884
2244
|
const configFile2 = await fs8.readJson(configPath);
|
|
1885
2245
|
console.log(JSON.stringify(configFile2, null, 2));
|
|
@@ -1889,7 +2249,9 @@ async function runConfig(options) {
|
|
|
1889
2249
|
const configFile = await fs8.readJson(configPath);
|
|
1890
2250
|
if (configFile.docsRepo !== "standalone") {
|
|
1891
2251
|
console.log(
|
|
1892
|
-
chalk6.yellow(
|
|
2252
|
+
chalk6.yellow(
|
|
2253
|
+
tr(config.lang, "cli", "config.projectRootStandaloneOnly")
|
|
2254
|
+
)
|
|
1893
2255
|
);
|
|
1894
2256
|
return;
|
|
1895
2257
|
}
|
|
@@ -1901,7 +2263,7 @@ async function runConfig(options) {
|
|
|
1901
2263
|
{
|
|
1902
2264
|
type: "select",
|
|
1903
2265
|
name: "repo",
|
|
1904
|
-
message: "
|
|
2266
|
+
message: tr(config.lang, "cli", "config.selectRepoToUpdate"),
|
|
1905
2267
|
choices: [
|
|
1906
2268
|
{ title: "Frontend (fe)", value: "fe" },
|
|
1907
2269
|
{ title: "Backend (be)", value: "be" }
|
|
@@ -1919,7 +2281,7 @@ async function runConfig(options) {
|
|
|
1919
2281
|
if (!options.repo || !["fe", "be"].includes(options.repo)) {
|
|
1920
2282
|
console.log(
|
|
1921
2283
|
chalk6.red(
|
|
1922
|
-
"
|
|
2284
|
+
tr(config.lang, "cli", "config.fullstackRepoRequired")
|
|
1923
2285
|
)
|
|
1924
2286
|
);
|
|
1925
2287
|
return;
|
|
@@ -1936,13 +2298,20 @@ async function runConfig(options) {
|
|
|
1936
2298
|
}
|
|
1937
2299
|
console.log(
|
|
1938
2300
|
chalk6.green(
|
|
1939
|
-
|
|
2301
|
+
tr(config.lang, "cli", "config.projectRootSet", {
|
|
2302
|
+
repo: options.repo.toUpperCase(),
|
|
2303
|
+
path: options.projectRoot
|
|
2304
|
+
})
|
|
1940
2305
|
)
|
|
1941
2306
|
);
|
|
1942
2307
|
} else {
|
|
1943
2308
|
configFile.projectRoot = options.projectRoot;
|
|
1944
2309
|
console.log(
|
|
1945
|
-
chalk6.green(
|
|
2310
|
+
chalk6.green(
|
|
2311
|
+
tr(config.lang, "cli", "config.projectRootSetSingle", {
|
|
2312
|
+
path: options.projectRoot
|
|
2313
|
+
})
|
|
2314
|
+
)
|
|
1946
2315
|
);
|
|
1947
2316
|
}
|
|
1948
2317
|
await fs8.writeJson(configPath, configFile, { spaces: 2 });
|
|
@@ -1962,7 +2331,10 @@ function contextCommand(program2) {
|
|
|
1962
2331
|
})
|
|
1963
2332
|
);
|
|
1964
2333
|
} else {
|
|
1965
|
-
console.error(
|
|
2334
|
+
console.error(
|
|
2335
|
+
chalk6.red(tr(DEFAULT_LANG, "cli", "common.errorLabel")),
|
|
2336
|
+
error
|
|
2337
|
+
);
|
|
1966
2338
|
}
|
|
1967
2339
|
process.exit(1);
|
|
1968
2340
|
}
|
|
@@ -1985,12 +2357,43 @@ function detectFromBranch(branchName, features) {
|
|
|
1985
2357
|
(f) => f.slug.toLowerCase() === detected.toLowerCase() || f.folderName.toLowerCase() === detected.toLowerCase()
|
|
1986
2358
|
);
|
|
1987
2359
|
}
|
|
2360
|
+
function getListLabel(f, stepsMap, lang) {
|
|
2361
|
+
if (f.completion.implementationDone && !f.completion.workflowDone) {
|
|
2362
|
+
if (f.git.docsHasUncommittedChanges) {
|
|
2363
|
+
return tr(lang, "cli", "context.list.docsCommitNeeded");
|
|
2364
|
+
}
|
|
2365
|
+
if (!f.issueNumber) {
|
|
2366
|
+
return tr(lang, "cli", "context.list.issueNumberNeeded");
|
|
2367
|
+
}
|
|
2368
|
+
if (!f.docs.prFieldExists || !f.docs.prStatusFieldExists) {
|
|
2369
|
+
return tr(lang, "cli", "context.list.addPrMetadata");
|
|
2370
|
+
}
|
|
2371
|
+
if (!f.pr.link) {
|
|
2372
|
+
return tr(lang, "cli", "context.list.recordPrLink");
|
|
2373
|
+
}
|
|
2374
|
+
if (!f.pr.status) {
|
|
2375
|
+
return tr(lang, "cli", "context.list.setPrStatus");
|
|
2376
|
+
}
|
|
2377
|
+
if (f.pr.status !== "Approved") {
|
|
2378
|
+
return tr(lang, "cli", "context.list.prStatusToApproved", {
|
|
2379
|
+
status: f.pr.status
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2382
|
+
if (f.specStatus !== "Approved") {
|
|
2383
|
+
return tr(lang, "cli", "context.list.approveSpec");
|
|
2384
|
+
}
|
|
2385
|
+
if (f.planStatus !== "Approved") {
|
|
2386
|
+
return tr(lang, "cli", "context.list.approvePlan");
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
return stepsMap[f.currentStep] || "Unknown";
|
|
2390
|
+
}
|
|
1988
2391
|
async function runContext(featureName, options) {
|
|
1989
2392
|
const cwd = process.cwd();
|
|
1990
2393
|
const config = await getConfig(cwd);
|
|
1991
|
-
const lang = config?.lang ?? "
|
|
2394
|
+
const lang = config?.lang ?? "en";
|
|
1992
2395
|
if (!config) {
|
|
1993
|
-
throw new Error("
|
|
2396
|
+
throw new Error(tr(DEFAULT_LANG, "cli", "common.configNotFound"));
|
|
1994
2397
|
}
|
|
1995
2398
|
const stepDefinitions = getStepDefinitions(lang);
|
|
1996
2399
|
const stepsMap = getStepsMap(lang);
|
|
@@ -2104,12 +2507,16 @@ async function runContext(featureName, options) {
|
|
|
2104
2507
|
console.log(chalk6.gray("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
2105
2508
|
console.log();
|
|
2106
2509
|
if (features.length === 0) {
|
|
2107
|
-
console.log(
|
|
2510
|
+
console.log(
|
|
2511
|
+
chalk6.yellow(
|
|
2512
|
+
tr(lang, "cli", "context.noActiveFeatures")
|
|
2513
|
+
)
|
|
2514
|
+
);
|
|
2108
2515
|
console.log();
|
|
2109
2516
|
return;
|
|
2110
2517
|
}
|
|
2111
2518
|
if (warnings.length > 0) {
|
|
2112
|
-
console.log(chalk6.yellow("
|
|
2519
|
+
console.log(chalk6.yellow(tr(lang, "cli", "context.envWarnings")));
|
|
2113
2520
|
warnings.forEach((w) => console.log(chalk6.yellow(` - ${w}`)));
|
|
2114
2521
|
console.log();
|
|
2115
2522
|
}
|
|
@@ -2117,24 +2524,36 @@ async function runContext(featureName, options) {
|
|
|
2117
2524
|
if (selectionMode === "open") {
|
|
2118
2525
|
console.log(
|
|
2119
2526
|
chalk6.gray(
|
|
2120
|
-
`
|
|
2527
|
+
` ${tr(lang, "cli", "context.openFallbackSummary", {
|
|
2528
|
+
inProgress: inProgressFeatures.length,
|
|
2529
|
+
readyToClose: readyToCloseFeatures.length,
|
|
2530
|
+
done: doneFeatures.length
|
|
2531
|
+
})}`
|
|
2121
2532
|
)
|
|
2122
2533
|
);
|
|
2123
2534
|
console.log();
|
|
2124
2535
|
}
|
|
2125
2536
|
if (selectionMode === "open") {
|
|
2126
|
-
console.log(
|
|
2537
|
+
console.log(
|
|
2538
|
+
chalk6.blue(
|
|
2539
|
+
`\u{1F539} ${tr(lang, "cli", "context.sectionInProgress")} (${inProgressFeatures.length})`
|
|
2540
|
+
)
|
|
2541
|
+
);
|
|
2127
2542
|
inProgressFeatures.forEach((f2) => {
|
|
2128
|
-
const stepName2 =
|
|
2543
|
+
const stepName2 = getListLabel(f2, stepsMap, lang);
|
|
2129
2544
|
const typeStr = config.projectType === "fullstack" ? chalk6.cyan(`(${f2.type})`) : "";
|
|
2130
2545
|
console.log(
|
|
2131
2546
|
` \u2022 ${chalk6.bold(f2.folderName)} ${typeStr} - ${chalk6.yellow(stepName2)}`
|
|
2132
2547
|
);
|
|
2133
2548
|
});
|
|
2134
2549
|
console.log();
|
|
2135
|
-
console.log(
|
|
2550
|
+
console.log(
|
|
2551
|
+
chalk6.blue(
|
|
2552
|
+
`\u{1F538} ${tr(lang, "cli", "context.sectionReadyToClose")} (${readyToCloseFeatures.length})`
|
|
2553
|
+
)
|
|
2554
|
+
);
|
|
2136
2555
|
readyToCloseFeatures.forEach((f2) => {
|
|
2137
|
-
const stepName2 =
|
|
2556
|
+
const stepName2 = getListLabel(f2, stepsMap, lang);
|
|
2138
2557
|
const typeStr = config.projectType === "fullstack" ? chalk6.cyan(`(${f2.type})`) : "";
|
|
2139
2558
|
console.log(
|
|
2140
2559
|
` \u2022 ${chalk6.bold(f2.folderName)} ${typeStr} - ${chalk6.yellow(stepName2)}`
|
|
@@ -2145,7 +2564,7 @@ async function runContext(featureName, options) {
|
|
|
2145
2564
|
console.log(chalk6.blue(title));
|
|
2146
2565
|
console.log();
|
|
2147
2566
|
targetFeatures.forEach((f2) => {
|
|
2148
|
-
const stepName2 =
|
|
2567
|
+
const stepName2 = getListLabel(f2, stepsMap, lang);
|
|
2149
2568
|
const typeStr = config.projectType === "fullstack" ? chalk6.cyan(`(${f2.type})`) : "";
|
|
2150
2569
|
console.log(
|
|
2151
2570
|
` \u2022 ${chalk6.bold(f2.folderName)} ${typeStr} - ${chalk6.yellow(stepName2)}`
|
|
@@ -2153,20 +2572,28 @@ async function runContext(featureName, options) {
|
|
|
2153
2572
|
});
|
|
2154
2573
|
}
|
|
2155
2574
|
console.log();
|
|
2156
|
-
console.log(chalk6.gray("
|
|
2575
|
+
console.log(chalk6.gray(tr(lang, "cli", "context.tipDetails")));
|
|
2157
2576
|
console.log(
|
|
2158
2577
|
chalk6.gray(" $ npx lee-spec-kit context <slug|F001|F001-slug> [--repo fe|be]")
|
|
2159
2578
|
);
|
|
2160
2579
|
if (selectionMode === "open") {
|
|
2161
|
-
console.log(
|
|
2162
|
-
|
|
2580
|
+
console.log(
|
|
2581
|
+
chalk6.gray(
|
|
2582
|
+
` $ npx lee-spec-kit context --all # ${tr(lang, "cli", "context.tipShowAll")}`
|
|
2583
|
+
)
|
|
2584
|
+
);
|
|
2585
|
+
console.log(
|
|
2586
|
+
chalk6.gray(
|
|
2587
|
+
` $ npx lee-spec-kit context --done # ${tr(lang, "cli", "context.tipShowDone")}`
|
|
2588
|
+
)
|
|
2589
|
+
);
|
|
2163
2590
|
}
|
|
2164
2591
|
console.log();
|
|
2165
2592
|
return;
|
|
2166
2593
|
}
|
|
2167
2594
|
const f = targetFeatures[0];
|
|
2168
2595
|
const stepName = stepsMap[f.currentStep] || "Unknown";
|
|
2169
|
-
const okTag = (requiresUserOk) => requiresUserOk ? chalk6.yellow(lang
|
|
2596
|
+
const okTag = (requiresUserOk) => requiresUserOk ? chalk6.yellow(tr(lang, "cli", "context.okRequired")) : "";
|
|
2170
2597
|
console.log(
|
|
2171
2598
|
`\u{1F539} Feature: ${chalk6.bold(f.folderName)} ${config.projectType === "fullstack" ? chalk6.cyan(`(${f.type})`) : ""}`
|
|
2172
2599
|
);
|
|
@@ -2240,9 +2667,6 @@ function printChecklist(f, stepDefinitions) {
|
|
|
2240
2667
|
console.log(` ${mark} ${definition.step}. ${label} ${detail}`);
|
|
2241
2668
|
});
|
|
2242
2669
|
}
|
|
2243
|
-
function msg(lang, ko, en) {
|
|
2244
|
-
return lang === "en" ? en : ko;
|
|
2245
|
-
}
|
|
2246
2670
|
function formatPath(cwd, p) {
|
|
2247
2671
|
if (!p) return "";
|
|
2248
2672
|
return path4.isAbsolute(p) ? path4.relative(cwd, p) : p;
|
|
@@ -2277,11 +2701,7 @@ async function checkDocsStructure(config, cwd) {
|
|
|
2277
2701
|
issues.push({
|
|
2278
2702
|
level: "error",
|
|
2279
2703
|
code: "missing_dir",
|
|
2280
|
-
message:
|
|
2281
|
-
config.lang,
|
|
2282
|
-
`\uD544\uC218 \uD3F4\uB354\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4: ${dir}`,
|
|
2283
|
-
`Missing required directory: ${dir}`
|
|
2284
|
-
),
|
|
2704
|
+
message: tr(config.lang, "cli", "doctor.issue.missingRequiredDir", { dir }),
|
|
2285
2705
|
path: formatPath(cwd, p)
|
|
2286
2706
|
});
|
|
2287
2707
|
}
|
|
@@ -2291,11 +2711,7 @@ async function checkDocsStructure(config, cwd) {
|
|
|
2291
2711
|
issues.push({
|
|
2292
2712
|
level: "warn",
|
|
2293
2713
|
code: "missing_config",
|
|
2294
|
-
message:
|
|
2295
|
-
config.lang,
|
|
2296
|
-
"\uC124\uC815 \uD30C\uC77C(.lee-spec-kit.json)\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC77C\uBD80 \uAE30\uB2A5\uC774 \uD3F4\uB354 \uAD6C\uC870 \uCD94\uC815\uC73C\uB85C \uB3D9\uC791\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
2297
|
-
"Missing .lee-spec-kit.json. Some commands may rely on folder-structure heuristics."
|
|
2298
|
-
),
|
|
2714
|
+
message: tr(config.lang, "cli", "doctor.issue.missingConfig"),
|
|
2299
2715
|
path: formatPath(cwd, configPath)
|
|
2300
2716
|
});
|
|
2301
2717
|
}
|
|
@@ -2307,11 +2723,7 @@ async function checkFeatures(config, cwd, features) {
|
|
|
2307
2723
|
issues.push({
|
|
2308
2724
|
level: "warn",
|
|
2309
2725
|
code: "no_features",
|
|
2310
|
-
message:
|
|
2311
|
-
config.lang,
|
|
2312
|
-
"Feature \uD3F4\uB354\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. (feature-base\uB9CC \uC874\uC7AC\uD558\uAC70\uB098 \uC544\uC9C1 feature\uB97C \uB9CC\uB4E4\uC9C0 \uC54A\uC558\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.)",
|
|
2313
|
-
"No feature folders found. (Only feature-base exists, or no features created yet.)"
|
|
2314
|
-
)
|
|
2726
|
+
message: tr(config.lang, "cli", "doctor.issue.noFeatures")
|
|
2315
2727
|
});
|
|
2316
2728
|
return issues;
|
|
2317
2729
|
}
|
|
@@ -2331,11 +2743,9 @@ async function checkFeatures(config, cwd, features) {
|
|
|
2331
2743
|
issues.push({
|
|
2332
2744
|
level: "warn",
|
|
2333
2745
|
code: "placeholder_left",
|
|
2334
|
-
message:
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
`Leftover placeholders detected: ${placeholders.join(", ")}`
|
|
2338
|
-
),
|
|
2746
|
+
message: tr(config.lang, "cli", "doctor.issue.placeholdersLeft", {
|
|
2747
|
+
placeholders: placeholders.join(", ")
|
|
2748
|
+
}),
|
|
2339
2749
|
path: formatPath(cwd, p)
|
|
2340
2750
|
});
|
|
2341
2751
|
}
|
|
@@ -2343,22 +2753,14 @@ async function checkFeatures(config, cwd, features) {
|
|
|
2343
2753
|
issues.push({
|
|
2344
2754
|
level: "warn",
|
|
2345
2755
|
code: "missing_spec",
|
|
2346
|
-
message:
|
|
2347
|
-
config.lang,
|
|
2348
|
-
"spec.md\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
2349
|
-
"Missing spec.md."
|
|
2350
|
-
),
|
|
2756
|
+
message: tr(config.lang, "cli", "doctor.issue.missingSpec"),
|
|
2351
2757
|
path: formatPath(cwd, f.path)
|
|
2352
2758
|
});
|
|
2353
2759
|
} else if (!f.specStatus) {
|
|
2354
2760
|
issues.push({
|
|
2355
2761
|
level: "warn",
|
|
2356
2762
|
code: "spec_status_unset",
|
|
2357
|
-
message:
|
|
2358
|
-
config.lang,
|
|
2359
|
-
"spec.md\uC758 Status(\uC0C1\uD0DC)\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (\uD15C\uD50C\uB9BF \uADF8\uB300\uB85C\uC77C \uC218 \uC788\uC74C)",
|
|
2360
|
-
"spec.md Status is not set. (May still be a template)"
|
|
2361
|
-
),
|
|
2763
|
+
message: tr(config.lang, "cli", "doctor.issue.specStatusUnset"),
|
|
2362
2764
|
path: formatPath(cwd, path4.join(f.path, "spec.md"))
|
|
2363
2765
|
});
|
|
2364
2766
|
}
|
|
@@ -2366,11 +2768,7 @@ async function checkFeatures(config, cwd, features) {
|
|
|
2366
2768
|
issues.push({
|
|
2367
2769
|
level: "warn",
|
|
2368
2770
|
code: "plan_status_unset",
|
|
2369
|
-
message:
|
|
2370
|
-
config.lang,
|
|
2371
|
-
"plan.md\uC758 Status(\uC0C1\uD0DC)\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (\uD15C\uD50C\uB9BF \uADF8\uB300\uB85C\uC77C \uC218 \uC788\uC74C)",
|
|
2372
|
-
"plan.md Status is not set. (May still be a template)"
|
|
2373
|
-
),
|
|
2771
|
+
message: tr(config.lang, "cli", "doctor.issue.planStatusUnset"),
|
|
2374
2772
|
path: formatPath(cwd, path4.join(f.path, "plan.md"))
|
|
2375
2773
|
});
|
|
2376
2774
|
}
|
|
@@ -2378,11 +2776,7 @@ async function checkFeatures(config, cwd, features) {
|
|
|
2378
2776
|
issues.push({
|
|
2379
2777
|
level: "warn",
|
|
2380
2778
|
code: "tasks_empty",
|
|
2381
|
-
message:
|
|
2382
|
-
config.lang,
|
|
2383
|
-
"tasks.md\uC5D0 \uD0DC\uC2A4\uD06C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
2384
|
-
"tasks.md has no tasks."
|
|
2385
|
-
),
|
|
2779
|
+
message: tr(config.lang, "cli", "doctor.issue.tasksEmpty"),
|
|
2386
2780
|
path: formatPath(cwd, path4.join(f.path, "tasks.md"))
|
|
2387
2781
|
});
|
|
2388
2782
|
}
|
|
@@ -2394,11 +2788,10 @@ async function checkFeatures(config, cwd, features) {
|
|
|
2394
2788
|
issues.push({
|
|
2395
2789
|
level: "warn",
|
|
2396
2790
|
code: "duplicate_feature_id",
|
|
2397
|
-
message:
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
),
|
|
2791
|
+
message: tr(config.lang, "cli", "doctor.issue.duplicateFeatureId", {
|
|
2792
|
+
id,
|
|
2793
|
+
count: String(paths.length)
|
|
2794
|
+
}),
|
|
2402
2795
|
path: formatPath(cwd, paths[0])
|
|
2403
2796
|
});
|
|
2404
2797
|
}
|
|
@@ -2407,11 +2800,7 @@ async function checkFeatures(config, cwd, features) {
|
|
|
2407
2800
|
issues.push({
|
|
2408
2801
|
level: "warn",
|
|
2409
2802
|
code: "missing_feature_id",
|
|
2410
|
-
message:
|
|
2411
|
-
config.lang,
|
|
2412
|
-
"Feature \uD3F4\uB354\uBA85\uC774 F001-... \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4. (ID\uB97C \uCD94\uCD9C\uD560 \uC218 \uC5C6\uC74C)",
|
|
2413
|
-
"Feature folder name is not in F001-... format. (Cannot extract ID)"
|
|
2414
|
-
),
|
|
2803
|
+
message: tr(config.lang, "cli", "doctor.issue.missingFeatureId"),
|
|
2415
2804
|
path: formatPath(cwd, path4.join(config.docsDir, p))
|
|
2416
2805
|
});
|
|
2417
2806
|
}
|
|
@@ -2422,11 +2811,11 @@ function doctorCommand(program2) {
|
|
|
2422
2811
|
const cwd = process.cwd();
|
|
2423
2812
|
const config = await getConfig(cwd);
|
|
2424
2813
|
if (!config) {
|
|
2425
|
-
const message = "
|
|
2814
|
+
const message = tr(DEFAULT_LANG, "cli", "common.configNotFound");
|
|
2426
2815
|
if (options.json) {
|
|
2427
2816
|
console.log(JSON.stringify({ status: "error", error: message }, null, 2));
|
|
2428
2817
|
} else {
|
|
2429
|
-
console.error(chalk6.red("
|
|
2818
|
+
console.error(chalk6.red(tr(DEFAULT_LANG, "cli", "common.errorLabel")), message);
|
|
2430
2819
|
}
|
|
2431
2820
|
process.exit(1);
|
|
2432
2821
|
}
|
|
@@ -2461,32 +2850,40 @@ function doctorCommand(program2) {
|
|
|
2461
2850
|
process.exit(exitCode);
|
|
2462
2851
|
}
|
|
2463
2852
|
console.log();
|
|
2464
|
-
console.log(chalk6.bold("
|
|
2853
|
+
console.log(chalk6.bold(tr(lang, "cli", "doctor.title")));
|
|
2465
2854
|
console.log(chalk6.gray(`- Docs: ${path4.relative(cwd, docsDir)}`));
|
|
2466
2855
|
console.log(chalk6.gray(`- Type: ${projectType}`));
|
|
2467
2856
|
console.log(chalk6.gray(`- Lang: ${lang}`));
|
|
2468
2857
|
console.log();
|
|
2469
2858
|
if (warnings.length > 0) {
|
|
2470
|
-
console.log(
|
|
2859
|
+
console.log(
|
|
2860
|
+
chalk6.yellow(tr(lang, "cli", "doctor.envWarnings"))
|
|
2861
|
+
);
|
|
2471
2862
|
warnings.forEach((w) => console.log(chalk6.yellow(` - ${w}`)));
|
|
2472
2863
|
console.log();
|
|
2473
2864
|
}
|
|
2474
2865
|
if (!hasIssues) {
|
|
2475
|
-
console.log(chalk6.green("
|
|
2866
|
+
console.log(chalk6.green(tr(lang, "cli", "doctor.noIssues")));
|
|
2476
2867
|
console.log();
|
|
2477
2868
|
process.exit(0);
|
|
2478
2869
|
}
|
|
2479
2870
|
const errors = issues.filter((i) => i.level === "error");
|
|
2480
2871
|
const warns = issues.filter((i) => i.level === "warn");
|
|
2481
2872
|
if (errors.length > 0) {
|
|
2482
|
-
console.log(
|
|
2873
|
+
console.log(
|
|
2874
|
+
chalk6.red(`\u274C ${tr(lang, "cli", "doctor.errorsTitle")} (${errors.length})`)
|
|
2875
|
+
);
|
|
2483
2876
|
errors.forEach(
|
|
2484
2877
|
(i) => console.log(chalk6.red(` - ${i.message}${i.path ? ` (${i.path})` : ""}`))
|
|
2485
2878
|
);
|
|
2486
2879
|
console.log();
|
|
2487
2880
|
}
|
|
2488
2881
|
if (warns.length > 0) {
|
|
2489
|
-
console.log(
|
|
2882
|
+
console.log(
|
|
2883
|
+
chalk6.yellow(
|
|
2884
|
+
`\u26A0\uFE0F ${tr(lang, "cli", "doctor.warningsTitle")} (${warns.length})`
|
|
2885
|
+
)
|
|
2886
|
+
);
|
|
2490
2887
|
warns.forEach(
|
|
2491
2888
|
(i) => console.log(
|
|
2492
2889
|
chalk6.yellow(` - ${i.message}${i.path ? ` (${i.path})` : ""}`)
|
|
@@ -2496,7 +2893,9 @@ function doctorCommand(program2) {
|
|
|
2496
2893
|
}
|
|
2497
2894
|
console.log(
|
|
2498
2895
|
chalk6.gray(
|
|
2499
|
-
|
|
2896
|
+
tr(lang, "cli", "doctor.tipJson", {
|
|
2897
|
+
strictFlag: options.strict ? " --strict" : ""
|
|
2898
|
+
})
|
|
2500
2899
|
)
|
|
2501
2900
|
);
|
|
2502
2901
|
console.log();
|