ly-workflow-codex 0.2.0 → 0.3.0
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 +20 -16
- package/dist/cli.mjs +410 -70
- package/dist/index.d.mts +27 -31
- package/dist/index.d.ts +27 -31
- package/dist/index.mjs +1 -1
- package/dist/shared/{ly-workflow-codex.K-E7PMp3.mjs → ly-workflow-codex.DZD52_5A.mjs} +494 -356
- package/package.json +1 -1
- package/templates/skills-codex/apply.md +46 -15
- package/templates/skills-codex/archive.md +15 -2
- package/templates/skills-codex/init.md +10 -13
- package/templates/skills-codex/propose.md +39 -19
- package/templates/skills-codex/review-code.md +72 -48
- package/templates/skills-codex/review-plan.md +64 -43
|
@@ -10,7 +10,7 @@ import { parse, stringify } from 'smol-toml';
|
|
|
10
10
|
import i18next from 'i18next';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
12
|
|
|
13
|
-
const version = "0.
|
|
13
|
+
const version = "0.3.0";
|
|
14
14
|
|
|
15
15
|
const i18n = i18next;
|
|
16
16
|
const zhCN = {
|
|
@@ -19,6 +19,7 @@ const zhCN = {
|
|
|
19
19
|
no: "\u5426",
|
|
20
20
|
preflight: {
|
|
21
21
|
cliMissing: "\u672A\u68C0\u6D4B\u5230 openspec CLI\u2014\u2014OpenSpec \u751F\u547D\u5468\u671F\u547D\u4EE4\u4F9D\u8D56\u5B83\uFF08@fission-ai/openspec\uFF09",
|
|
22
|
+
cliUnhealthy: "\u68C0\u6D4B\u5230 openspec CLI \u4F46\u6267\u884C\u5F02\u5E38\uFF08\u8D85\u65F6/\u6302\u8D77\uFF09\u2014\u2014\u8BF7\u68C0\u67E5 openspec \u5B89\u88C5\u662F\u5426\u5065\u5EB7",
|
|
22
23
|
installAsk: "\u662F\u5426\u73B0\u5728\u5168\u5C40\u5B89\u88C5 openspec\uFF1F(npm install -g @fission-ai/openspec@latest)",
|
|
23
24
|
installSuccessWithSkills: "openspec \u5DF2\u5B89\u88C5\uFF0COpenSpec \u547D\u4EE4\u6280\u80FD\u5DF2\u5C31\u7EEA\uFF0C\u5168\u90E8 @lyx-* skills \u53EF\u7528",
|
|
24
25
|
installSuccessNeedInit: "openspec \u5DF2\u5B89\u88C5\uFF1B\u8FD0\u884C @lyx-init \u5B8C\u6210 openspec init \u540E\uFF0COpenSpec \u751F\u547D\u5468\u671F\u547D\u4EE4\u53EF\u7528",
|
|
@@ -26,13 +27,24 @@ const zhCN = {
|
|
|
26
27
|
installNotInPath: "openspec \u5DF2\u5B89\u88C5\u4F46\u5F53\u524D PATH \u4E2D\u672A\u68C0\u6D4B\u5230\u2014\u2014\u8BF7\u91CD\u5F00\u7EC8\u7AEF\uFF0C\u6216\u68C0\u67E5 npm \u5168\u5C40 bin \u76EE\u5F55\u662F\u5426\u5728 PATH \u4E2D",
|
|
27
28
|
unavailableList: "\u4EE5\u4E0B\u547D\u4EE4\u4F9D\u8D56 openspec\uFF0C\u5F53\u524D\u4E0D\u53EF\u7528\uFF1A{{list}}",
|
|
28
29
|
unaffectedNote: "@lyx-apply\u3001@lyx-review-code \u4EC5\u4F9D\u8D56\u9879\u76EE\u5185 change \u76EE\u5F55\u7ED3\u6784\uFF1BGit \u5DE5\u5177\u94FE\u4E0E\u8D28\u91CF\u5173\u5361\u6280\u80FD\u4E0D\u53D7\u5F71\u54CD",
|
|
29
|
-
skillsMissing: "
|
|
30
|
-
|
|
30
|
+
skillsMissing: "OpenSpec skills \u7F3A\u5931\uFF1A{{list}}\u2014\u2014\u8FD0\u884C @lyx-init \u6216\u4FEE\u590D\u540E\u91CD\u8BD5",
|
|
31
|
+
skillsGlobalOnly: "OpenSpec skills \u4EC5\u5168\u5C40\u53EF\u7528\uFF0C\u547D\u4EE4\u53EF\u7EE7\u7EED\u4F46\u672A\u56FA\u5316\u5230\u5F53\u524D\u9879\u76EE\uFF08WARN\uFF09",
|
|
32
|
+
skillsUnknown: "\u65E0\u6CD5\u8BFB\u53D6 OpenSpec workflow profile\uFF0Cskills \u72B6\u6001\u672A\u77E5\uFF08WARN\uFF09",
|
|
33
|
+
skillsMissingCodex: "codex \u5BBF\u4E3B\u7F3A\u5C11 openspec-* skills\u2014\u2014\u8FD0\u884C openspec init \u5B89\u88C5\u5230 ~/.agents/skills/ \u6216\u9879\u76EE .agents/skills/",
|
|
34
|
+
rootMissing: "OpenSpec root \u672A\u521D\u59CB\u5316\u2014\u2014\u8FD0\u884C @lyx-init \u5B8C\u6210 openspec init",
|
|
35
|
+
rootUnhealthy: "OpenSpec root \u4E0D\u5065\u5EB7\u2014\u2014\u8FD0\u884C openspec doctor --json \u67E5\u770B fix \u5EFA\u8BAE"
|
|
31
36
|
},
|
|
32
37
|
doctor: {
|
|
33
38
|
openspecCliMissing: "\u672A\u5B89\u88C5 (npm install -g @fission-ai/openspec)",
|
|
39
|
+
openspecCliUnhealthy: "\u6267\u884C\u5F02\u5E38\uFF08\u8BF7\u68C0\u67E5 openspec \u5B89\u88C5\uFF09",
|
|
34
40
|
skillsInitialized: "\u5DF2\u521D\u59CB\u5316",
|
|
35
|
-
|
|
41
|
+
skillsGlobalOnly: "\u4EC5\u5168\u5C40\u53EF\u7528\uFF08WARN\uFF1B\u8FD0\u884C @lyx-init \u53EF\u56FA\u5316\u5230\u9879\u76EE\uFF09",
|
|
42
|
+
skillsUnknown: "\u672A\u77E5\uFF08profile \u8BFB\u53D6\u5931\u8D25\uFF0CWARN\uFF09",
|
|
43
|
+
skillsMissing: "\u7F3A\u5931\uFF1A{{list}}",
|
|
44
|
+
rootHealthy: "\u5065\u5EB7",
|
|
45
|
+
rootMissing: "\u672A\u521D\u59CB\u5316\uFF08\u8FD0\u884C @lyx-init\uFF09",
|
|
46
|
+
rootUnhealthy: "\u4E0D\u5065\u5EB7\uFF08\u8FD0\u884C openspec doctor --json\uFF09",
|
|
47
|
+
rootNotChecked: "\u672A\u68C0\u67E5\uFF08openspec CLI \u4E0D\u53EF\u7528\uFF09"
|
|
36
48
|
},
|
|
37
49
|
confirm: "\u786E\u8BA4",
|
|
38
50
|
cancel: "\u53D6\u6D88",
|
|
@@ -75,7 +87,8 @@ const zhCN = {
|
|
|
75
87
|
skipAllPrompts: "\u8DF3\u8FC7\u6240\u6709\u4EA4\u4E92\u5F0F\u63D0\u793A\uFF08\u975E\u4EA4\u4E92\u6A21\u5F0F\uFF09",
|
|
76
88
|
skipConfirmation: "\u8DF3\u8FC7\u5378\u8F7D\u786E\u8BA4",
|
|
77
89
|
workflows: "\u8981\u5B89\u88C5\u7684\u5DE5\u4F5C\u6D41",
|
|
78
|
-
installDir: "\u5B89\u88C5\u76EE\u5F55"
|
|
90
|
+
installDir: "\u5B89\u88C5\u76EE\u5F55",
|
|
91
|
+
initOpenspec: "\u521D\u59CB\u5316\u5F53\u524D\u9879\u76EE\u7684 OpenSpec root \u4E0E skills\uFF08\u663E\u5F0F\u9003\u751F\u53E3\uFF09"
|
|
79
92
|
},
|
|
80
93
|
nonInteractiveMode: "\u975E\u4EA4\u4E92\u6A21\u5F0F:",
|
|
81
94
|
examples: "\u793A\u4F8B",
|
|
@@ -89,33 +102,47 @@ const zhCN = {
|
|
|
89
102
|
init: {
|
|
90
103
|
selectLanguage: "\u9009\u62E9\u8BED\u8A00 / Select language",
|
|
91
104
|
host: {
|
|
92
|
-
reviewModelPrompt: "\u5BA1\u67E5 agent A \u6A21\u578B\uFF08\u7559\u7A7A\
|
|
93
|
-
reviewModelHint: "\
|
|
105
|
+
reviewModelPrompt: "\u5BA1\u67E5 agent A \u6A21\u578B\uFF08\u7559\u7A7A = \u9ED8\u8BA4\u7EE7\u627F\u5F53\u524D\u4F1A\u8BDD\u6A21\u578B\uFF1Bagent B / coding \u6A21\u578B\u8BF7\u8FD0\u884C lycx init \u914D\u7F6E\uFF09\u2014\u2014agent \u6A21\u578B\u9700\u989D\u5916\u914D\u7F6E\uFF0C\u80FD\u5426 spawn \u7531\u73AF\u5883\u51B3\u5B9A",
|
|
106
|
+
reviewModelHint: "\u5019\u9009 = \u9ED8\u8BA4\u7EE7\u627F\u5F53\u524D\u4F1A\u8BDD\u6A21\u578B\uFF08\u7559\u7A7A\uFF09 + \u81EA\u5B9A\u4E49\u8F93\u5165 + \u65E2\u6709\u503C\uFF08\u82E5\u6709\uFF0C\u9ED8\u8BA4\u8BE5\u9879\uFF0C\u4E0D\u9759\u9ED8\u4E22\u5F03\uFF09\uFF1B\u4E0D\u505A\u6E05\u5355\u5F3A\u6821\u9A8C\uFF0C\u53EF\u7528\u793A\u4F8B prompt \u5B9E\u6D4B\u6A21\u578B\u662F\u5426\u53EF spawn",
|
|
94
107
|
codexSummaryTitle: "Codex \u914D\u7F6E\u6458\u8981:",
|
|
95
108
|
reviewModelUnset: "\u672A\u914D\u7F6E\uFF08\u56DE\u9000\u5F53\u524D\u4F1A\u8BDD\u6A21\u578B\uFF09"
|
|
96
109
|
},
|
|
97
110
|
mode: {
|
|
98
111
|
providerSelect: "\u9009\u62E9 API \u63D0\u4F9B\u65B9",
|
|
99
112
|
providerExisting: "\u5DF2\u6709 provider\uFF1A{{name}}",
|
|
113
|
+
providerExistingSelected: "\u5DF2\u9009\u7528 provider: {{name}}",
|
|
100
114
|
providerOfficial: "OpenAI \u5B98\u65B9",
|
|
101
|
-
providerCustom: "\u81EA\u5B9A\u4E49\uFF08\u8F93\u5165\u540D\u79F0 / base_url
|
|
115
|
+
providerCustom: "\u81EA\u5B9A\u4E49\uFF08\u8F93\u5165\u540D\u79F0 / base_url\uFF09",
|
|
102
116
|
customNamePrompt: "provider \u540D\u79F0\uFF08\u5199\u5165 [model_providers.<name>]\uFF09",
|
|
103
117
|
nameInvalid: "\u540D\u79F0\u4EC5\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\u3001\u8FDE\u5B57\u7B26",
|
|
104
118
|
customUrlPrompt: "base_url",
|
|
105
|
-
customKeyPrompt: "API key\uFF08\u4EC5\u7528\u4E8E\u62C9\u53D6\u6A21\u578B\u5217\u8868\uFF0C\u4E0D\u5199\u5165 config.toml\uFF09",
|
|
106
|
-
optional: "\u53EF\u9009",
|
|
107
119
|
providerExists: "provider {{name}} \u5DF2\u5B58\u5728\u4E8E ~/.codex/config.toml\uFF0C\u76F4\u63A5\u9009\u7528",
|
|
108
120
|
providerWritten: "\u5DF2\u589E\u91CF\u5199\u5165 ~/.codex/config.toml\uFF1Amodel_provider = {{name}}\uFF08\u539F\u6709\u6CE8\u91CA\u4FDD\u7559\uFF09",
|
|
109
|
-
providerWriteFailed: "\u5199\u5165 ~/.codex/config.toml \u5931\u8D25\uFF1A{{error}}
|
|
110
|
-
configParseFailed: "~/.codex/config.toml \u89E3\u6790\u5931\u8D25\uFF0C\u672A\u8BC6\u522B\u5230\u5DF2\u6709 provider\uFF08\u4E0D\u5F71\u54CD\u7EE7\u7EED\uFF09"
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
121
|
+
providerWriteFailed: "\u5199\u5165 ~/.codex/config.toml \u5931\u8D25\uFF1A{{error}}",
|
|
122
|
+
configParseFailed: "~/.codex/config.toml \u89E3\u6790\u5931\u8D25\uFF0C\u672A\u8BC6\u522B\u5230\u5DF2\u6709 provider\uFF08\u4E0D\u5F71\u54CD\u7EE7\u7EED\uFF09"
|
|
123
|
+
},
|
|
124
|
+
codexStatus: {
|
|
125
|
+
title: "Codex \u73B0\u72B6\u68C0\u6D4B\uFF08\u53EA\u8BFB\u5C55\u793A\uFF0C\u4E0D\u4FEE\u6539\u4EFB\u4F55 Codex \u914D\u7F6E\uFF09",
|
|
126
|
+
mainModelLabel: "\u4E3B\u4F1A\u8BDD\u6A21\u578B",
|
|
127
|
+
mainModel: '{{model}}\uFF08"\u9ED8\u8BA4\u7EE7\u627F"\u7684\u5B9E\u9645\u503C\uFF09',
|
|
128
|
+
providersLabel: "provider \u6761\u76EE",
|
|
129
|
+
noProvider: "\u65E0 [model_providers.*] \u6761\u76EE",
|
|
130
|
+
registeredLabel: "models.json \u6CE8\u518C\u6A21\u578B",
|
|
131
|
+
registeredModels: "{{count}} \u4E2A\uFF08\u4EC5\u5C55\u793A\uFF1B\u5B9E\u6D4B\u5176\u5185\u5BB9 \u2260 spawn \u53EF\u7528\u5217\u8868\uFF0C\u4E0D\u4F5C\u4E3A\u5019\u9009\uFF09",
|
|
132
|
+
notDetected: "\u672A\u68C0\u6D4B\u5230",
|
|
133
|
+
reasoningHint: "\u63D0\u793A\uFF1A\u90E8\u5206\u7B2C\u4E09\u65B9\u6A21\u578B\uFF08\u5982 glm-5.3-flash\uFF09\u9ED8\u8BA4\u63A8\u7406\u53C2\u6570\u4E0D\u53EF\u7528\uFF0C\u9700\u5728 [codexHost] \u7684 reviewReasoningEffort / codingReasoningEffort \u4E2D\u624B\u6539\u5BF9\u5E94\u6863\u4F4D\uFF08\u5982 low\uFF09\u3002\u672C\u5411\u5BFC\u4E0D\u91C7\u96C6\u63A8\u7406\u6863\uFF0C\u53D6\u503C\u4E0D\u505A\u679A\u4E3E\u5F3A\u6821\u9A8C\uFF0C\u76F8\u5173\u62A5\u9519\u5982\u5B9E\u5C55\u793A\u5373\u53EF\u3002",
|
|
134
|
+
spawnableHint: "agent \u6A21\u578B\u9700\u989D\u5916\u914D\u7F6E\uFF08\u4E0D\u505A\u6E05\u5355\u5F3A\u6821\u9A8C\uFF09\uFF1A\u80FD\u5426 spawn \u7531\u73AF\u5883\u5B9E\u9645\u80FD\u529B\u51B3\u5B9A\u3002\u9A8C\u8BC1\u793A\u4F8B\uFF1A\u5BF9\u8BDD\u4E2D\u8BA9 Codex \u7528\u76EE\u6807\u6A21\u578B spawn \u4E00\u4E2A\u5B50\u4EE3\u7406\u6267\u884C\u7B80\u5355\u4EFB\u52A1\uFF08\u5982\u56DE\u590D ok\uFF09\uFF0C\u62A5\u9519\u542B Unknown model ... Available models: ... \u5373\u5F53\u524D\u4E0D\u652F\u6301\uFF1B[codexHost] spawnableModels \u4EC5\u4F5C\u63D0\u793A\u53C2\u8003\uFF0C\u53EF\u624B\u6539 ~/.codex/lyx/config.toml"
|
|
114
135
|
},
|
|
115
|
-
promptsMigrated: "\u89D2\u8272\u8BCD\u5DF2\u8FC1\u79FB\u5230 ~/.ly/prompts/\uFF08codex \u5171\u4EAB\u4F4D\u7F6E\uFF09",
|
|
116
|
-
promptsMigrationFailed: "\u65E7\u89D2\u8272\u8BCD\u8FC1\u79FB\u5931\u8D25\uFF08\u4E0D\u963B\u65AD\u5B89\u88C5\uFF09: {{error}}",
|
|
117
136
|
selectWorkflows: "\u9009\u62E9\u8981\u5B89\u88C5\u7684\u5DE5\u4F5C\u6D41\uFF08\u53EF\u591A\u9009\uFF09",
|
|
118
137
|
confirmInstall: "\u786E\u8BA4\u5B89\u88C5\u4EE5\u4E0A\u914D\u7F6E\uFF1F",
|
|
138
|
+
executor: {
|
|
139
|
+
title: "\u6267\u884C\u8005\u4E8C\u8FDE\uFF08\u5BA1\u67E5 / \u5B9E\u65BD\u7531\u8C01\u6267\u884C\uFF09",
|
|
140
|
+
hint: '\u9ED8\u8BA4"\u4E3B agent \u76F4\u63A5\u6267\u884C"\uFF1A\u4E0D spawn \u5B50\u4EE3\u7406\u3001\u4E0D\u63D0\u4F9B\u72EC\u7ACB\u5BA1\u67E5\u89C6\u89D2\u3002\u9009"spawn \u72EC\u7ACB\u5B50\u4EE3\u7406"\u53EF\u83B7\u5F97\u72EC\u7ACB\u89C6\u89D2\uFF0C\u4F46\u80FD\u5426 spawn \u7531\u5F53\u524D\u73AF\u5883\u5B9E\u9645\u80FD\u529B\u51B3\u5B9A\u3002',
|
|
141
|
+
reviewExecutor: "\u5BA1\u67E5\u6267\u884C\u8005\uFF08reviewExecutor\uFF09",
|
|
142
|
+
codingExecutor: "\u5B9E\u65BD\u6267\u884C\u8005\uFF08codingExecutor\uFF09",
|
|
143
|
+
main: "\u4E3B agent \u76F4\u63A5\u6267\u884C\uFF08\u9ED8\u8BA4\uFF09",
|
|
144
|
+
subagent: "spawn \u72EC\u7ACB\u5B50\u4EE3\u7406"
|
|
145
|
+
},
|
|
119
146
|
installing: "\u6B63\u5728\u5B89\u88C5...",
|
|
120
147
|
installSuccess: "\u5B89\u88C5\u6210\u529F\uFF01",
|
|
121
148
|
installFailed: "\u5B89\u88C5\u5931\u8D25",
|
|
@@ -126,8 +153,12 @@ const zhCN = {
|
|
|
126
153
|
summary: {
|
|
127
154
|
title: "\u914D\u7F6E\u6458\u8981:",
|
|
128
155
|
host: "\u5B89\u88C5\u5BBF\u4E3B",
|
|
156
|
+
reviewExecutor: "\u5BA1\u67E5\u6267\u884C\u8005",
|
|
157
|
+
codingExecutor: "\u5B9E\u65BD\u6267\u884C\u8005",
|
|
158
|
+
executorMain: "\u4E3B agent \u76F4\u63A5\u6267\u884C",
|
|
159
|
+
executorSubagent: "spawn \u72EC\u7ACB\u5B50\u4EE3\u7406",
|
|
160
|
+
modelIneffective: "{{model}}\uFF08\u4E3B agent \u6267\u884C\u65F6\u4E0D\u751F\u6548\uFF09",
|
|
129
161
|
reviewModelA: "\u5BA1\u67E5 agent A \u6A21\u578B",
|
|
130
|
-
reviewModelB: "\u5BA1\u67E5 agent B \u6A21\u578B",
|
|
131
162
|
codingModel: "coding \u5B9E\u65BD\u6A21\u578B",
|
|
132
163
|
reviewModelCodex: "\u5BA1\u67E5\u6A21\u578B",
|
|
133
164
|
workflows: "\u5DE5\u4F5C\u6D41:",
|
|
@@ -140,16 +171,32 @@ const zhCN = {
|
|
|
140
171
|
reinstalling: "\u6B63\u5728\u91CD\u88C5\u547D\u4EE4\u6A21\u677F\u4EE5\u5E94\u7528\u65B0\u914D\u7F6E...",
|
|
141
172
|
reinstallDone: "\u6A21\u677F\u5DF2\u66F4\u65B0",
|
|
142
173
|
reinstallFailed: "\u6A21\u677F\u66F4\u65B0\u5931\u8D25",
|
|
143
|
-
trioTitle: "\u6A21\u578B\
|
|
174
|
+
trioTitle: "\u6A21\u578B\u4E8C\u8FDE\uFF08\u5BA1\u67E5 subagent / coding \u5B9E\u65BD\uFF09",
|
|
175
|
+
trioCandidatesHint: "\u5019\u9009 = \u9ED8\u8BA4\u7EE7\u627F\u5F53\u524D\u4F1A\u8BDD\u6A21\u578B\uFF08\u7559\u7A7A\uFF09 + \u81EA\u5B9A\u4E49\u8F93\u5165 + \u65E2\u6709\u503C\uFF08\u82E5\u6709\uFF0C\u9ED8\u8BA4\uFF09\uFF1Bagent \u6A21\u578B\u9700\u989D\u5916\u914D\u7F6E\uFF0C\u80FD\u5426 spawn \u7531\u73AF\u5883\u51B3\u5B9A",
|
|
144
176
|
reviewModelA: "\u5BA1\u67E5 agent A \u6A21\u578B\uFF08reviewModel\uFF09",
|
|
145
177
|
reviewModelB: "\u5BA1\u67E5 agent B \u6A21\u578B\uFF08reviewModelB\uFF0C\u53EF\u9009\uFF09",
|
|
146
178
|
codingModel: "coding \u5B9E\u65BD\u6A21\u578B\uFF08codingModel\uFF0C\u53EF\u9009\uFF09",
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
179
|
+
unsetChoice: "\u9ED8\u8BA4\u7EE7\u627F\u5F53\u524D\u4F1A\u8BDD\u6A21\u578B\uFF08\u7559\u7A7A\uFF09",
|
|
180
|
+
customChoice: "\u81EA\u5B9A\u4E49\u8F93\u5165\u6A21\u578B\u2026",
|
|
181
|
+
customPrompt: "\u8F93\u5165\u6A21\u578B\u540D\uFF08\u7559\u7A7A\u53EF\u53D6\u6D88\uFF1Bagent \u6A21\u578B\u9700\u989D\u5916\u914D\u7F6E\uFF0C\u53EF\u7528\u793A\u4F8B prompt \u9A8C\u8BC1\u662F\u5426\u53EF spawn\uFF09"
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
doctor: {
|
|
185
|
+
modelConfig: {
|
|
186
|
+
label: "Codex \u5B50\u4EE3\u7406\u6A21\u578B\u914D\u7F6E",
|
|
187
|
+
okUnset: "{{key}} \u672A\u914D\u7F6E\uFF08\u56DE\u9000\u5F53\u524D\u4F1A\u8BDD\u6A21\u578B\uFF09",
|
|
188
|
+
okConfigured: "{{key}}={{model}}\uFF08\u5DF2\u914D\u7F6E\uFF09",
|
|
189
|
+
executorMain: "{{key}}=\u4E3B agent \u76F4\u63A5\u6267\u884C\uFF08\u9ED8\u8BA4\uFF09",
|
|
190
|
+
executorSubagent: "{{key}}=spawn \u72EC\u7ACB\u5B50\u4EE3\u7406",
|
|
191
|
+
warnInvalidExecutor: "{{key}} \u53D6\u503C\u975E\u6CD5\uFF08{{value}}\uFF09\uFF0C\u6309 main \u5904\u7406",
|
|
192
|
+
warnIneffective: "{{key}}={{model}} \u5728\u4E3B agent \u6267\u884C\u65F6\u4E0D\u751F\u6548\uFF08\u4EC5\u5C55\u793A\uFF09",
|
|
193
|
+
removedNote: "{{key}} \u5DF2\u79FB\u9664\uFF08\u4E0D\u518D\u8BFB\u53D6\u6216\u4FDD\u7559\uFF09",
|
|
194
|
+
okReasoningConfigured: "{{key}}={{effort}}\uFF08\u5DF2\u914D\u7F6E\uFF0C\u4EC5\u5C55\u793A\uFF0C\u4E0D\u505A\u679A\u4E3E\u5F3A\u6821\u9A8C\uFF09",
|
|
195
|
+
okReasoningUnset: "{{key}} \u672A\u914D\u7F6E\uFF08\u4E0D\u4F20\u63A8\u7406\u6863\u53C2\u6570\uFF09",
|
|
196
|
+
warnInvalid: "spawnableModels \u5B57\u6BB5\u683C\u5F0F\u975E\u6CD5/\u6E05\u6D17\u540E\u4E3A\u7A7A\uFF08\u4EC5\u5F62\u6001\u63D0\u793A\uFF0C\u53EF\u8FD0\u884C lycx init/update \u4FEE\u590D\uFF09",
|
|
197
|
+
agentNeedConfig: "agent \u6A21\u578B\u9700\u989D\u5916\u914D\u7F6E\uFF1A\u80FD\u5426 spawn \u7531\u5F53\u524D\u73AF\u5883\u5B9E\u9645\u80FD\u529B\u51B3\u5B9A\uFF0C\u5DE5\u5177\u4E0D\u505A\u6E05\u5355\u5F3A\u6821\u9A8C",
|
|
198
|
+
inheritNote: "\u7559\u7A7A\u5B57\u6BB5\u5C06\u7EE7\u627F\u5F53\u524D\u4E3B\u4F1A\u8BDD\u6A21\u578B {{model}}",
|
|
199
|
+
verifyHint: "\u9A8C\u8BC1\u67D0\u6A21\u578B\u662F\u5426\u53EF spawn \u7684\u793A\u4F8B prompt\uFF1A\u8BA9 Codex \u7528\u76EE\u6807\u6A21\u578B spawn \u4E00\u4E2A\u5B50\u4EE3\u7406\u6267\u884C\u7B80\u5355\u4EFB\u52A1\uFF08\u5982\u56DE\u590D ok\uFF09\u2014\u2014\u82E5\u62A5\u9519\u539F\u6587\u542B Unknown model ... Available models: ...\uFF0C\u8BF4\u660E\u8BE5\u6A21\u578B\u5F53\u524D\u4E0D\u652F\u6301 spawn"
|
|
153
200
|
}
|
|
154
201
|
},
|
|
155
202
|
update: {
|
|
@@ -214,13 +261,12 @@ const zhCN = {
|
|
|
214
261
|
step2: "\u5378\u8F7D npm \u5168\u5C40\u5305",
|
|
215
262
|
step2Hint: "\u9700\u8981\u624B\u52A8\u6267\u884C",
|
|
216
263
|
continuePrompt: "\u7EE7\u7EED\u5378\u8F7D\u5DE5\u4F5C\u6D41\u6587\u4EF6\uFF1F",
|
|
217
|
-
confirm: "\u786E\u5B9A\u8981\u5378\u8F7D ly-workflow-codex \u5417\uFF1F\u5C06\u79FB\u9664 ~/.agents/skills/lyx-*\uFF08\u542B\u65E7 ~/.agents/skills/ly-* \u4E0E ~/.codex/prompts/ly-*.md \u6B8B\u7559\uFF09\u4E0E\
|
|
264
|
+
confirm: "\u786E\u5B9A\u8981\u5378\u8F7D ly-workflow-codex \u5417\uFF1F\u5C06\u79FB\u9664 ~/.agents/skills/lyx-*\uFF08\u542B\u65E7 ~/.agents/skills/ly-* \u4E0E ~/.codex/prompts/ly-*.md \u6B8B\u7559\uFF09\u4E0E ~/.codex/lyx/\uFF08\u914D\u7F6E config.toml\u3001\u89D2\u8272\u8BCD prompts/\u3001worktrees\uFF09\uFF1B\u82E5 ~/.codex/lyx/worktrees/ \u4E0B\u5B58\u5728\u672A\u6E05\u7406\u7684\u5B9E\u9645 git worktree\uFF0C\u8BE5\u5B50\u76EE\u5F55\u4F1A\u88AB\u4FDD\u7559\u5E76\u63D0\u793A\u9700\u5148\u624B\u52A8\u6E05\u7406\u3002",
|
|
218
265
|
uninstalling: "\u6B63\u5728\u5378\u8F7D...",
|
|
219
266
|
success: "\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5DF2\u79FB\u9664",
|
|
220
267
|
removedSkills: "\u5DF2\u79FB\u9664 lyx-* skills:",
|
|
221
268
|
removedLegacyPrompts: "\u5DF2\u6E05\u7406\u65E7\u5B89\u88C5\u4F4D\u6B8B\u7559 (~/.agents/skills/ly-*\u3001~/.codex/prompts/ly-*.md):",
|
|
222
|
-
|
|
223
|
-
configTomlKept: "\u5171\u4EAB\u914D\u7F6E (~/.ly/config.toml) \u5DF2\u4FDD\u7559\uFF1A~/.ly \u4E3A ly-workflow \u5171\u4EAB\u547D\u540D\u7A7A\u95F4\uFF08\u542B worktrees \u7B49\u771F\u5B9E\u6570\u636E\uFF09\uFF0C\u5982\u9700\u6E05\u9664\u8BF7\u4EBA\u5DE5\u786E\u8BA4\u3002",
|
|
269
|
+
removedPrompts: "\u5DF2\u6E05\u7406\u89D2\u8272\u8BCD (~/.codex/lyx/prompts/codex/):",
|
|
224
270
|
cancelled: "\u5378\u8F7D\u5DF2\u53D6\u6D88",
|
|
225
271
|
failed: "\u5378\u8F7D\u5931\u8D25",
|
|
226
272
|
lastStep: "\u6700\u540E\u4E00\u6B65\uFF1A\u5378\u8F7D npm \u5168\u5C40\u5305",
|
|
@@ -235,6 +281,7 @@ const en = {
|
|
|
235
281
|
no: "No",
|
|
236
282
|
preflight: {
|
|
237
283
|
cliMissing: "openspec CLI not found \u2014 OpenSpec lifecycle commands depend on it (@fission-ai/openspec)",
|
|
284
|
+
cliUnhealthy: "openspec CLI detected but unhealthy (timeout/hang) \u2014 verify the installation",
|
|
238
285
|
installAsk: "Install openspec globally now? (npm install -g @fission-ai/openspec@latest)",
|
|
239
286
|
installSuccessWithSkills: "openspec installed and OpenSpec command skills ready \u2014 all @lyx-* skills available",
|
|
240
287
|
installSuccessNeedInit: "openspec installed; run @lyx-init to complete openspec init before using OpenSpec lifecycle commands",
|
|
@@ -242,13 +289,24 @@ const en = {
|
|
|
242
289
|
installNotInPath: "openspec was installed but not found on the current PATH \u2014 reopen your terminal or add the npm global bin directory to PATH",
|
|
243
290
|
unavailableList: "These commands depend on openspec and are currently unavailable: {{list}}",
|
|
244
291
|
unaffectedNote: "@lyx-apply and @lyx-review-code only need the in-project change directory; Git tools and quality-gate skills are unaffected",
|
|
245
|
-
skillsMissing: "
|
|
246
|
-
|
|
292
|
+
skillsMissing: "OpenSpec skills missing: {{list}} \u2014 run @lyx-init or repair and retry",
|
|
293
|
+
skillsGlobalOnly: "OpenSpec skills are available only globally; commands can continue but are not pinned to this project (WARN)",
|
|
294
|
+
skillsUnknown: "Unable to read the OpenSpec workflow profile; skills status is unknown (WARN)",
|
|
295
|
+
skillsMissingCodex: "codex host is missing the openspec-* skills \u2014 run openspec init to install them into ~/.agents/skills/ or the project .agents/skills/",
|
|
296
|
+
rootMissing: "OpenSpec root is not initialized \u2014 run @lyx-init to complete openspec init",
|
|
297
|
+
rootUnhealthy: "OpenSpec root is unhealthy \u2014 run openspec doctor --json for fix guidance"
|
|
247
298
|
},
|
|
248
299
|
doctor: {
|
|
249
300
|
openspecCliMissing: "Not found (npm install -g @fission-ai/openspec)",
|
|
301
|
+
openspecCliUnhealthy: "Unhealthy (verify openspec installation)",
|
|
250
302
|
skillsInitialized: "initialized",
|
|
251
|
-
|
|
303
|
+
skillsGlobalOnly: "global only (WARN; run @lyx-init to pin to this project)",
|
|
304
|
+
skillsUnknown: "unknown (profile read failed, WARN)",
|
|
305
|
+
skillsMissing: "missing: {{list}}",
|
|
306
|
+
rootHealthy: "healthy",
|
|
307
|
+
rootMissing: "not initialized (run @lyx-init)",
|
|
308
|
+
rootUnhealthy: "unhealthy (run openspec doctor --json)",
|
|
309
|
+
rootNotChecked: "not checked (openspec CLI unavailable)"
|
|
252
310
|
},
|
|
253
311
|
confirm: "Confirm",
|
|
254
312
|
cancel: "Cancel",
|
|
@@ -291,7 +349,8 @@ const en = {
|
|
|
291
349
|
skipAllPrompts: "Skip all interactive prompts (non-interactive mode)",
|
|
292
350
|
skipConfirmation: "Skip uninstall confirmation",
|
|
293
351
|
workflows: "Workflows to install",
|
|
294
|
-
installDir: "Installation directory"
|
|
352
|
+
installDir: "Installation directory",
|
|
353
|
+
initOpenspec: "Initialize the current project OpenSpec root and skills (explicit escape hatch)"
|
|
295
354
|
},
|
|
296
355
|
nonInteractiveMode: "Non-interactive mode:",
|
|
297
356
|
examples: "Examples",
|
|
@@ -305,33 +364,47 @@ const en = {
|
|
|
305
364
|
init: {
|
|
306
365
|
selectLanguage: "Select language / \u9009\u62E9\u8BED\u8A00",
|
|
307
366
|
host: {
|
|
308
|
-
reviewModelPrompt: "
|
|
309
|
-
reviewModelHint: "
|
|
367
|
+
reviewModelPrompt: "Review agent A model (empty = inherit the current session model; agent B / coding models can be configured via lycx init) \u2014 agent models need extra setup: spawn support depends on the environment",
|
|
368
|
+
reviewModelHint: "Candidates = inherit current session model (empty) + custom input + existing value (if any, defaulted, never silently dropped); no list-based validation \u2014 verify spawn support with a sample prompt",
|
|
310
369
|
codexSummaryTitle: "Codex Configuration Summary:",
|
|
311
370
|
reviewModelUnset: "not configured (fall back to the current session model)"
|
|
312
371
|
},
|
|
313
372
|
mode: {
|
|
314
373
|
providerSelect: "Select API provider",
|
|
315
374
|
providerExisting: "Existing provider: {{name}}",
|
|
375
|
+
providerExistingSelected: "Selected provider: {{name}}",
|
|
316
376
|
providerOfficial: "OpenAI official",
|
|
317
|
-
providerCustom: "Custom (name / base_url
|
|
377
|
+
providerCustom: "Custom (name / base_url)",
|
|
318
378
|
customNamePrompt: "Provider name (written to [model_providers.<name>])",
|
|
319
379
|
nameInvalid: "Name allows letters, digits, underscores and hyphens only",
|
|
320
380
|
customUrlPrompt: "base_url",
|
|
321
|
-
customKeyPrompt: "API key (used only to fetch the model list, not written to config.toml)",
|
|
322
|
-
optional: "optional",
|
|
323
381
|
providerExists: "Provider {{name}} already exists in ~/.codex/config.toml \u2014 selected as-is",
|
|
324
382
|
providerWritten: "Incrementally written to ~/.codex/config.toml: model_provider = {{name}} (existing comments preserved)",
|
|
325
|
-
providerWriteFailed: "Failed to write ~/.codex/config.toml: {{error}}
|
|
326
|
-
configParseFailed: "Failed to parse ~/.codex/config.toml \u2014 no existing providers detected (non-blocking)"
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
383
|
+
providerWriteFailed: "Failed to write ~/.codex/config.toml: {{error}}",
|
|
384
|
+
configParseFailed: "Failed to parse ~/.codex/config.toml \u2014 no existing providers detected (non-blocking)"
|
|
385
|
+
},
|
|
386
|
+
codexStatus: {
|
|
387
|
+
title: "Codex status snapshot (read-only, modifies nothing in Codex config)",
|
|
388
|
+
mainModelLabel: "Main session model",
|
|
389
|
+
mainModel: '{{model}} (the actual value of "inherit default")',
|
|
390
|
+
providersLabel: "Provider entries",
|
|
391
|
+
noProvider: "No [model_providers.*] entries",
|
|
392
|
+
registeredLabel: "models.json registered models",
|
|
393
|
+
registeredModels: "{{count}} (display only; its content \u2260 the spawn-available list, not used as candidates)",
|
|
394
|
+
notDetected: "not detected",
|
|
395
|
+
reasoningHint: "Note: some third-party models (e.g. glm-5.3-flash) cannot spawn with the default reasoning parameters. Hand-edit the matching reviewReasoningEffort / codingReasoningEffort field under [codexHost] (for example low). The wizard does not collect reasoning effort, values are not enum-validated, and related errors are shown as-is.",
|
|
396
|
+
spawnableHint: "Agent models need extra setup (no list-based validation): spawn support depends on the environment. Verification sample: ask Codex in a conversation to spawn a subagent with the target model for a trivial task (e.g. reply ok); an error containing Unknown model ... Available models: ... means it is not currently supported. [codexHost] spawnableModels is reference-only; you may hand-edit ~/.codex/lyx/config.toml"
|
|
330
397
|
},
|
|
331
|
-
promptsMigrated: "Role prompts migrated to ~/.ly/prompts/ (codex shared location)",
|
|
332
|
-
promptsMigrationFailed: "Legacy role prompt migration failed (non-blocking): {{error}}",
|
|
333
398
|
selectWorkflows: "Select workflows to install (multi-select)",
|
|
334
399
|
confirmInstall: "Confirm installation with above configuration?",
|
|
400
|
+
executor: {
|
|
401
|
+
title: "Executor duo (who runs review / implementation)",
|
|
402
|
+
hint: '"Main agent executes directly" is the default: no subagent spawn, no independent review perspective. Choosing "spawn a dedicated subagent" gives an independent perspective, but spawn support depends on the current environment.',
|
|
403
|
+
reviewExecutor: "Review executor (reviewExecutor)",
|
|
404
|
+
codingExecutor: "Implementation executor (codingExecutor)",
|
|
405
|
+
main: "Main agent executes directly (default)",
|
|
406
|
+
subagent: "Spawn a dedicated subagent"
|
|
407
|
+
},
|
|
335
408
|
installing: "Installing...",
|
|
336
409
|
installSuccess: "Installation successful!",
|
|
337
410
|
installFailed: "Installation failed",
|
|
@@ -342,8 +415,12 @@ const en = {
|
|
|
342
415
|
summary: {
|
|
343
416
|
title: "Configuration summary:",
|
|
344
417
|
host: "Install host",
|
|
418
|
+
reviewExecutor: "Review executor",
|
|
419
|
+
codingExecutor: "Implementation executor",
|
|
420
|
+
executorMain: "main agent executes directly",
|
|
421
|
+
executorSubagent: "spawns a dedicated subagent",
|
|
422
|
+
modelIneffective: "{{model}} (has no effect when the main agent executes)",
|
|
345
423
|
reviewModelA: "Review agent A model",
|
|
346
|
-
reviewModelB: "Review agent B model",
|
|
347
424
|
codingModel: "Coding implementation model",
|
|
348
425
|
reviewModelCodex: "Review model",
|
|
349
426
|
workflows: "Workflows:",
|
|
@@ -356,16 +433,32 @@ const en = {
|
|
|
356
433
|
reinstalling: "Reinstalling command templates to apply changes...",
|
|
357
434
|
reinstallDone: "Templates updated",
|
|
358
435
|
reinstallFailed: "Template update failed",
|
|
359
|
-
trioTitle: "Model
|
|
436
|
+
trioTitle: "Model duo (review subagent / coding implementation)",
|
|
437
|
+
trioCandidatesHint: "Candidates = inherit current session model (empty) + custom input + existing value (if any, defaulted); agent models need extra setup \u2014 spawn support depends on the environment",
|
|
360
438
|
reviewModelA: "Review agent A model (reviewModel)",
|
|
361
439
|
reviewModelB: "Review agent B model (reviewModelB, optional)",
|
|
362
440
|
codingModel: "Coding implementation model (codingModel, optional)",
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
441
|
+
unsetChoice: "Inherit current session model (empty)",
|
|
442
|
+
customChoice: "Custom model input\u2026",
|
|
443
|
+
customPrompt: "Enter a model name (empty to cancel; agent models need extra setup \u2014 verify spawn support with a sample prompt)"
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
doctor: {
|
|
447
|
+
modelConfig: {
|
|
448
|
+
label: "Codex subagent model config",
|
|
449
|
+
okUnset: "{{key}} unset (falls back to the current session model)",
|
|
450
|
+
okConfigured: "{{key}}={{model}} (configured)",
|
|
451
|
+
executorMain: "{{key}}=main agent executes directly (default)",
|
|
452
|
+
executorSubagent: "{{key}}=spawns a dedicated subagent",
|
|
453
|
+
warnInvalidExecutor: "{{key}} has an invalid value ({{value}}); treated as main",
|
|
454
|
+
warnIneffective: "{{key}}={{model}} has no effect when the main agent executes (display only)",
|
|
455
|
+
removedNote: "{{key}} has been removed (no longer read or preserved)",
|
|
456
|
+
okReasoningConfigured: "{{key}}={{effort}} (configured, display only, no enum validation)",
|
|
457
|
+
okReasoningUnset: "{{key}} not configured (reasoning effort is not passed)",
|
|
458
|
+
warnInvalid: "spawnableModels is malformed / empty after cleaning (shape hint only; run lycx init/update to fix)",
|
|
459
|
+
agentNeedConfig: "Agent models need extra setup: spawn support depends on the current environment; this tool performs no list-based validation",
|
|
460
|
+
inheritNote: "Unset fields inherit the current main session model {{model}}",
|
|
461
|
+
verifyHint: "Sample prompt to check spawn support: ask Codex to spawn a subagent with the target model for a trivial task (e.g. reply ok) \u2014 an error containing Unknown model ... Available models: ... means the model is not currently spawnable"
|
|
369
462
|
}
|
|
370
463
|
},
|
|
371
464
|
update: {
|
|
@@ -430,13 +523,12 @@ const en = {
|
|
|
430
523
|
step2: "Uninstall npm global package",
|
|
431
524
|
step2Hint: "requires manual execution",
|
|
432
525
|
continuePrompt: "Continue removing workflow files?",
|
|
433
|
-
confirm: "Uninstall ly-workflow-codex? This removes ~/.agents/skills/lyx-* (incl. legacy ~/.agents/skills/ly-* and ~/.codex/prompts/ly-*.md residue) and
|
|
526
|
+
confirm: "Uninstall ly-workflow-codex? This removes ~/.agents/skills/lyx-* (incl. legacy ~/.agents/skills/ly-* and ~/.codex/prompts/ly-*.md residue) and ~/.codex/lyx/ (config.toml, prompts/, worktrees). If ~/.codex/lyx/worktrees/ still holds a live git worktree, that subdirectory is kept and a manual cleanup is suggested.",
|
|
434
527
|
uninstalling: "Uninstalling...",
|
|
435
528
|
success: "Workflow files removed",
|
|
436
529
|
removedSkills: "Removed lyx-* skills:",
|
|
437
530
|
removedLegacyPrompts: "Cleaned legacy install residue (~/.agents/skills/ly-*, ~/.codex/prompts/ly-*.md):",
|
|
438
|
-
|
|
439
|
-
configTomlKept: "Shared config (~/.ly/config.toml) kept: ~/.ly is the ly-workflow shared namespace (incl. worktrees); remove manually only after confirmation.",
|
|
531
|
+
removedPrompts: "Removed prompts (~/.codex/lyx/prompts/codex/):",
|
|
440
532
|
cancelled: "Uninstallation cancelled",
|
|
441
533
|
failed: "Uninstallation failed",
|
|
442
534
|
lastStep: "Last step: Uninstall npm global package",
|
|
@@ -466,131 +558,12 @@ async function changeLanguage(lang) {
|
|
|
466
558
|
await i18n.changeLanguage(lang);
|
|
467
559
|
}
|
|
468
560
|
|
|
469
|
-
const PACKAGE_NAME = "ly-workflow-codex";
|
|
470
|
-
const BIN_NAME = "lycx";
|
|
471
|
-
const CODE_PROMPTS_DIR = join(homedir(), ".codex", "prompts");
|
|
472
|
-
const AGENTS_SKILLS_DIR = join(homedir(), ".agents", "skills");
|
|
473
|
-
const LY_DIR = join(homedir(), ".ly");
|
|
474
|
-
const CONFIG_FILE = join(LY_DIR, "config.toml");
|
|
475
|
-
const PROMPTS_DIR = join(LY_DIR, "prompts");
|
|
476
|
-
const ISSUES_URL = "https://github.com/FE-runner/ly-workflow-codex/issues";
|
|
477
|
-
|
|
478
|
-
const LY_PROMPTS_DIR = PROMPTS_DIR;
|
|
479
|
-
function getLyPromptsDir() {
|
|
480
|
-
return LY_PROMPTS_DIR;
|
|
481
|
-
}
|
|
482
|
-
function getLyDir() {
|
|
483
|
-
return LY_DIR;
|
|
484
|
-
}
|
|
485
|
-
function getConfigPath() {
|
|
486
|
-
return CONFIG_FILE;
|
|
487
|
-
}
|
|
488
|
-
function isValidInstalledHost(value) {
|
|
489
|
-
return value === "codex" || value === "claude";
|
|
490
|
-
}
|
|
491
|
-
function sanitizeInstalledHosts(value) {
|
|
492
|
-
const arr = Array.isArray(value) ? value : [];
|
|
493
|
-
return [...new Set(arr.filter(isValidInstalledHost))];
|
|
494
|
-
}
|
|
495
|
-
async function ensureLyDir() {
|
|
496
|
-
await fs.ensureDir(LY_DIR);
|
|
497
|
-
}
|
|
498
|
-
async function hasCoexistingLegacyLyProducts(home = homedir()) {
|
|
499
|
-
try {
|
|
500
|
-
if (await fs.pathExists(join(home, ".claude", "commands", "ly")))
|
|
501
|
-
return true;
|
|
502
|
-
const legacyConfig = join(home, ".claude", ".ly", "config.toml");
|
|
503
|
-
if (await fs.pathExists(legacyConfig)) {
|
|
504
|
-
const content = await fs.readFile(legacyConfig, "utf-8");
|
|
505
|
-
return /installedHosts\s*=.*claude/i.test(content) || content.includes(".claude");
|
|
506
|
-
}
|
|
507
|
-
} catch {
|
|
508
|
-
}
|
|
509
|
-
return false;
|
|
510
|
-
}
|
|
511
|
-
async function migrateLegacyConfig() {
|
|
512
|
-
const legacyConfig = join(homedir(), ".claude", ".ly", "config.toml");
|
|
513
|
-
try {
|
|
514
|
-
if (!await fs.pathExists(legacyConfig))
|
|
515
|
-
return false;
|
|
516
|
-
if (await fs.pathExists(CONFIG_FILE))
|
|
517
|
-
return false;
|
|
518
|
-
if (await hasCoexistingLegacyLyProducts()) {
|
|
519
|
-
console.warn("[lycx] \u68C0\u6D4B\u5230 ~/.claude/.ly/ \u4ECD\u88AB ly-workflow \u4F7F\u7528\uFF08\u5171\u5B58\u573A\u666F\uFF09\uFF0C\u8DF3\u8FC7\u914D\u7F6E\u8FC1\u79FB\uFF0C\u4FDD\u7559\u4ED6\u65B9\u5728\u7528\u914D\u7F6E");
|
|
520
|
-
return false;
|
|
521
|
-
}
|
|
522
|
-
await fs.ensureDir(LY_DIR);
|
|
523
|
-
await fs.move(legacyConfig, CONFIG_FILE, { overwrite: false });
|
|
524
|
-
return true;
|
|
525
|
-
} catch {
|
|
526
|
-
return false;
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
async function readLyConfig() {
|
|
530
|
-
try {
|
|
531
|
-
await migrateLegacyConfig();
|
|
532
|
-
if (await fs.pathExists(CONFIG_FILE)) {
|
|
533
|
-
const content = await fs.readFile(CONFIG_FILE, "utf-8");
|
|
534
|
-
const parsed = parse(content);
|
|
535
|
-
delete parsed.routing;
|
|
536
|
-
delete parsed.performance;
|
|
537
|
-
return parsed;
|
|
538
|
-
}
|
|
539
|
-
} catch {
|
|
540
|
-
}
|
|
541
|
-
return null;
|
|
542
|
-
}
|
|
543
|
-
async function writeLyConfig(config) {
|
|
544
|
-
await migrateLegacyConfig();
|
|
545
|
-
await ensureLyDir();
|
|
546
|
-
const content = stringify(config);
|
|
547
|
-
await fs.writeFile(CONFIG_FILE, content, "utf-8");
|
|
548
|
-
}
|
|
549
|
-
function createDefaultConfig(options) {
|
|
550
|
-
const config = {
|
|
551
|
-
general: {
|
|
552
|
-
version: version,
|
|
553
|
-
language: options.language,
|
|
554
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
555
|
-
},
|
|
556
|
-
workflows: {
|
|
557
|
-
installed: options.installedWorkflows
|
|
558
|
-
},
|
|
559
|
-
installedHosts: sanitizeInstalledHosts(options.installedHosts).length > 0 ? sanitizeInstalledHosts(options.installedHosts) : ["codex"],
|
|
560
|
-
paths: {
|
|
561
|
-
commands: PROMPTS_DIR,
|
|
562
|
-
prompts: LY_PROMPTS_DIR,
|
|
563
|
-
backup: join(LY_DIR, "backup")
|
|
564
|
-
}
|
|
565
|
-
};
|
|
566
|
-
const reviewModel = sanitizeReviewModel(options.codexHost?.reviewModel);
|
|
567
|
-
const reviewModelB = sanitizeModelField(options.codexHost?.reviewModelB);
|
|
568
|
-
const codingModel = sanitizeModelField(options.codexHost?.codingModel);
|
|
569
|
-
if (reviewModel || reviewModelB || codingModel) {
|
|
570
|
-
config.codexHost = {
|
|
571
|
-
...reviewModel ? { reviewModel } : {},
|
|
572
|
-
...reviewModelB ? { reviewModelB } : {},
|
|
573
|
-
...codingModel ? { codingModel } : {}
|
|
574
|
-
};
|
|
575
|
-
}
|
|
576
|
-
return config;
|
|
577
|
-
}
|
|
578
|
-
function sanitizeReviewModel(value) {
|
|
579
|
-
if (typeof value !== "string")
|
|
580
|
-
return void 0;
|
|
581
|
-
const cleaned = value.trim().replace(/[^\w.:/-]/g, "");
|
|
582
|
-
return cleaned === "" ? void 0 : cleaned;
|
|
583
|
-
}
|
|
584
|
-
function sanitizeModelField(value) {
|
|
585
|
-
if (typeof value !== "string")
|
|
586
|
-
return void 0;
|
|
587
|
-
const cleaned = value.trim();
|
|
588
|
-
return cleaned === "" ? void 0 : cleaned;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
561
|
function codexConfigPath() {
|
|
592
562
|
return join(homedir(), ".codex", "config.toml");
|
|
593
563
|
}
|
|
564
|
+
function codexModelsJsonPath() {
|
|
565
|
+
return join(homedir(), ".codex", "models.json");
|
|
566
|
+
}
|
|
594
567
|
function sanitizeProviderName(value) {
|
|
595
568
|
if (typeof value !== "string")
|
|
596
569
|
return void 0;
|
|
@@ -624,6 +597,35 @@ async function listModelProviders(filePath = codexConfigPath()) {
|
|
|
624
597
|
}
|
|
625
598
|
return providers;
|
|
626
599
|
}
|
|
600
|
+
async function readCodexCurrentModel(filePath = codexConfigPath()) {
|
|
601
|
+
const parsed = await readCodexConfigToml(filePath);
|
|
602
|
+
const model = parsed?.model;
|
|
603
|
+
return typeof model === "string" && model.trim() !== "" ? model.trim() : void 0;
|
|
604
|
+
}
|
|
605
|
+
async function readModelsJson(filePath = codexModelsJsonPath()) {
|
|
606
|
+
let json;
|
|
607
|
+
try {
|
|
608
|
+
if (!await fs.pathExists(filePath))
|
|
609
|
+
return void 0;
|
|
610
|
+
json = JSON.parse(await fs.readFile(filePath, "utf-8"));
|
|
611
|
+
} catch {
|
|
612
|
+
return void 0;
|
|
613
|
+
}
|
|
614
|
+
const collect = (items2) => {
|
|
615
|
+
if (!Array.isArray(items2))
|
|
616
|
+
return [];
|
|
617
|
+
const names = [];
|
|
618
|
+
for (const item of items2) {
|
|
619
|
+
const id = typeof item === "string" ? item : item && typeof item === "object" && typeof item.slug === "string" ? item.slug : "";
|
|
620
|
+
const trimmed = id.trim();
|
|
621
|
+
if (trimmed !== "" && !names.includes(trimmed))
|
|
622
|
+
names.push(trimmed);
|
|
623
|
+
}
|
|
624
|
+
return names;
|
|
625
|
+
};
|
|
626
|
+
const items = Array.isArray(json) ? json : json && typeof json === "object" && Array.isArray(json.models) ? json.models : null;
|
|
627
|
+
return items === null ? void 0 : collect(items);
|
|
628
|
+
}
|
|
627
629
|
async function upsertModelProvider(input, filePath = codexConfigPath()) {
|
|
628
630
|
const name = sanitizeProviderName(input.name);
|
|
629
631
|
if (!name)
|
|
@@ -681,43 +683,155 @@ ${block.join("\n")}
|
|
|
681
683
|
}
|
|
682
684
|
return { status: "written", path: filePath };
|
|
683
685
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
686
|
+
|
|
687
|
+
const PACKAGE_NAME = "ly-workflow-codex";
|
|
688
|
+
const BIN_NAME = "lycx";
|
|
689
|
+
const CODE_PROMPTS_DIR = join(homedir(), ".codex", "prompts");
|
|
690
|
+
const AGENTS_SKILLS_DIR = join(homedir(), ".agents", "skills");
|
|
691
|
+
const LY_DIR = join(homedir(), ".codex", "lyx");
|
|
692
|
+
const CONFIG_FILE = join(LY_DIR, "config.toml");
|
|
693
|
+
const PROMPTS_DIR = join(LY_DIR, "prompts");
|
|
694
|
+
const ISSUES_URL = "https://github.com/FE-runner/ly-workflow-codex/issues";
|
|
695
|
+
|
|
696
|
+
const LY_PROMPTS_DIR = PROMPTS_DIR;
|
|
697
|
+
function getLyPromptsDir() {
|
|
698
|
+
return LY_PROMPTS_DIR;
|
|
699
|
+
}
|
|
700
|
+
function getLyDir() {
|
|
701
|
+
return LY_DIR;
|
|
702
|
+
}
|
|
703
|
+
function getConfigPath() {
|
|
704
|
+
return CONFIG_FILE;
|
|
705
|
+
}
|
|
706
|
+
const SPAWNABLE_MODELS_DEFAULT = [
|
|
707
|
+
"gpt-6-astra",
|
|
708
|
+
"gpt-5.6-sol",
|
|
709
|
+
"gpt-5.6-terra",
|
|
710
|
+
"gpt-5.6-luna",
|
|
711
|
+
"gpt-5.5"
|
|
712
|
+
];
|
|
713
|
+
function isValidInstalledHost(value) {
|
|
714
|
+
return value === "codex" || value === "claude";
|
|
715
|
+
}
|
|
716
|
+
function sanitizeInstalledHosts(value) {
|
|
717
|
+
const arr = Array.isArray(value) ? value : [];
|
|
718
|
+
return [...new Set(arr.filter(isValidInstalledHost))];
|
|
719
|
+
}
|
|
720
|
+
async function ensureLyDir() {
|
|
721
|
+
await fs.ensureDir(LY_DIR);
|
|
722
|
+
}
|
|
723
|
+
async function readLyConfig() {
|
|
703
724
|
try {
|
|
704
|
-
|
|
725
|
+
if (await fs.pathExists(CONFIG_FILE)) {
|
|
726
|
+
const content = await fs.readFile(CONFIG_FILE, "utf-8");
|
|
727
|
+
const parsed = parse(content);
|
|
728
|
+
delete parsed.routing;
|
|
729
|
+
delete parsed.performance;
|
|
730
|
+
return parsed;
|
|
731
|
+
}
|
|
705
732
|
} catch {
|
|
706
|
-
return { ok: false, error: "response is not valid JSON" };
|
|
707
733
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
734
|
+
return null;
|
|
735
|
+
}
|
|
736
|
+
async function writeLyConfig(config) {
|
|
737
|
+
await ensureLyDir();
|
|
738
|
+
const content = stringify(config);
|
|
739
|
+
await fs.writeFile(CONFIG_FILE, content, "utf-8");
|
|
740
|
+
}
|
|
741
|
+
function createDefaultConfig(options) {
|
|
742
|
+
const config = {
|
|
743
|
+
general: {
|
|
744
|
+
version: version,
|
|
745
|
+
language: options.language,
|
|
746
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
747
|
+
},
|
|
748
|
+
workflows: {
|
|
749
|
+
installed: options.installedWorkflows
|
|
750
|
+
},
|
|
751
|
+
installedHosts: sanitizeInstalledHosts(options.installedHosts).length > 0 ? sanitizeInstalledHosts(options.installedHosts) : ["codex"],
|
|
752
|
+
paths: {
|
|
753
|
+
commands: PROMPTS_DIR,
|
|
754
|
+
prompts: LY_PROMPTS_DIR,
|
|
755
|
+
backup: join(LY_DIR, "backup")
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
const reviewExecutor = sanitizeExecutor(options.codexHost?.reviewExecutor);
|
|
759
|
+
const codingExecutor = sanitizeExecutor(options.codexHost?.codingExecutor);
|
|
760
|
+
const reviewModel = sanitizeReviewModel(options.codexHost?.reviewModel);
|
|
761
|
+
const codingModel = sanitizeModelField(options.codexHost?.codingModel);
|
|
762
|
+
const reviewReasoningEffort = sanitizeReasoningEffort(options.codexHost?.reviewReasoningEffort);
|
|
763
|
+
const codingReasoningEffort = sanitizeReasoningEffort(options.codexHost?.codingReasoningEffort);
|
|
764
|
+
const spawnableModels = options.codexHost?.spawnableModels;
|
|
765
|
+
if (reviewExecutor || codingExecutor || reviewModel || codingModel || reviewReasoningEffort || codingReasoningEffort || spawnableModels !== void 0) {
|
|
766
|
+
config.codexHost = {
|
|
767
|
+
...reviewExecutor ? { reviewExecutor } : {},
|
|
768
|
+
...codingExecutor ? { codingExecutor } : {},
|
|
769
|
+
...reviewModel ? { reviewModel } : {},
|
|
770
|
+
...codingModel ? { codingModel } : {},
|
|
771
|
+
...reviewReasoningEffort ? { reviewReasoningEffort } : {},
|
|
772
|
+
...codingReasoningEffort ? { codingReasoningEffort } : {},
|
|
773
|
+
...spawnableModels !== void 0 ? { spawnableModels } : {}
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
return config;
|
|
777
|
+
}
|
|
778
|
+
function sanitizeExecutor(value) {
|
|
779
|
+
if (typeof value !== "string")
|
|
780
|
+
return void 0;
|
|
781
|
+
const cleaned = value.trim();
|
|
782
|
+
return cleaned === "main" || cleaned === "subagent" ? cleaned : void 0;
|
|
783
|
+
}
|
|
784
|
+
function sanitizeReviewModel(value) {
|
|
785
|
+
if (typeof value !== "string")
|
|
786
|
+
return void 0;
|
|
787
|
+
const cleaned = value.trim();
|
|
788
|
+
return cleaned === "" ? void 0 : cleaned;
|
|
789
|
+
}
|
|
790
|
+
function sanitizeModelField(value) {
|
|
791
|
+
if (typeof value !== "string")
|
|
792
|
+
return void 0;
|
|
793
|
+
const cleaned = value.trim();
|
|
794
|
+
return cleaned === "" ? void 0 : cleaned;
|
|
795
|
+
}
|
|
796
|
+
function sanitizeReasoningEffort(value) {
|
|
797
|
+
if (typeof value !== "string")
|
|
798
|
+
return void 0;
|
|
799
|
+
const cleaned = value.trim();
|
|
800
|
+
return cleaned === "" ? void 0 : cleaned;
|
|
801
|
+
}
|
|
802
|
+
function sanitizeCodexHostExtras(codexHost) {
|
|
803
|
+
if (!codexHost)
|
|
804
|
+
return {};
|
|
805
|
+
const reviewExecutor = sanitizeExecutor(codexHost.reviewExecutor);
|
|
806
|
+
const codingExecutor = sanitizeExecutor(codexHost.codingExecutor);
|
|
807
|
+
const codingModel = sanitizeModelField(codexHost.codingModel);
|
|
808
|
+
const reviewReasoningEffort = sanitizeReasoningEffort(codexHost.reviewReasoningEffort);
|
|
809
|
+
const codingReasoningEffort = sanitizeReasoningEffort(codexHost.codingReasoningEffort);
|
|
810
|
+
return {
|
|
811
|
+
...reviewExecutor ? { reviewExecutor } : {},
|
|
812
|
+
...codingExecutor ? { codingExecutor } : {},
|
|
813
|
+
...codingModel ? { codingModel } : {},
|
|
814
|
+
...reviewReasoningEffort ? { reviewReasoningEffort } : {},
|
|
815
|
+
...codingReasoningEffort ? { codingReasoningEffort } : {},
|
|
816
|
+
...codexHost.spawnableModels !== void 0 ? { spawnableModels: codexHost.spawnableModels } : {}
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
function sanitizeSpawnableModels(value) {
|
|
820
|
+
if (value === void 0 || value === null)
|
|
821
|
+
return { state: "unset", models: [] };
|
|
822
|
+
if (!Array.isArray(value))
|
|
823
|
+
return { state: "invalid", models: [] };
|
|
711
824
|
const models = [];
|
|
712
|
-
for (const item of
|
|
713
|
-
|
|
714
|
-
|
|
825
|
+
for (const item of value) {
|
|
826
|
+
if (typeof item !== "string")
|
|
827
|
+
continue;
|
|
828
|
+
const trimmed = item.trim();
|
|
715
829
|
if (trimmed !== "" && !models.includes(trimmed))
|
|
716
830
|
models.push(trimmed);
|
|
717
831
|
}
|
|
718
832
|
if (models.length === 0)
|
|
719
|
-
return {
|
|
720
|
-
return {
|
|
833
|
+
return { state: "empty", models: [] };
|
|
834
|
+
return { state: "ok", models };
|
|
721
835
|
}
|
|
722
836
|
|
|
723
837
|
const __filename$2 = fileURLToPath(import.meta.url);
|
|
@@ -752,6 +866,7 @@ function injectConfigVariables(content, _config) {
|
|
|
752
866
|
processed = processed.replace(/\n?<!--\s*LY:IF:IMPLEMENTER_EXTERNAL\s*-->[\s\S]*?<!--\s*LY:ENDIF\s*-->\n?/g, "");
|
|
753
867
|
processed = processed.replace(/\n?<!--\s*LY:IF:IMPLEMENTER_CLAUDE\s*-->[\s\S]*?<!--\s*LY:ENDIF\s*-->\n?/g, "");
|
|
754
868
|
processed = processed.replace(/\{\{LITE_MODE_FLAG\}\}/g, "");
|
|
869
|
+
processed = processed.replace(/\{\{SPAWNABLE_MODELS_DEFAULT\}\}/g, SPAWNABLE_MODELS_DEFAULT.join(" / "));
|
|
755
870
|
return processed;
|
|
756
871
|
}
|
|
757
872
|
function replaceHomePathsInTemplate(content, _installDir) {
|
|
@@ -801,7 +916,7 @@ const codexAdapter = {
|
|
|
801
916
|
for (const file of CODEX_ROLE_FILE_TARGETS) {
|
|
802
917
|
const target = join(ctx.promptsDir, "codex", file);
|
|
803
918
|
if (!await fs.pathExists(target)) {
|
|
804
|
-
ctx.result.errors.push(`codex ROLE_FILE target missing: ${target} (
|
|
919
|
+
ctx.result.errors.push(`codex ROLE_FILE target missing: ${target} (role prompts not installed)`);
|
|
805
920
|
ctx.result.success = false;
|
|
806
921
|
}
|
|
807
922
|
}
|
|
@@ -838,8 +953,8 @@ const CORE_CONFIGS = [
|
|
|
838
953
|
cmd("apply", 22, "opsx", "\u5B9E\u65BD\u65B9\u6848", "Apply", "coding subagent \u5B9E\u65BD tasks\uFF1A\u8BFB tasks.md \u9010\u4EFB\u52A1\u5B9E\u65BD+\u9A8C\u8BC1+\u52FE\u9009\uFF0C\u56DE\u4F20\u4E3B\u4F1A\u8BDD\u7EDF\u4E00 commit", "Implements tasks via a coding subagent: reads tasks.md, implements + verifies + checks, returns to the main session for commit"),
|
|
839
954
|
cmd("archive", 23, "opsx", "\u5F52\u6863\u65B9\u6848", "Archive", "\u59D4\u6258 opsx:archive\uFF0C\u5B8C\u6210\u540E\u5F52\u6863", "Delegates to opsx:archive \u2014 archives a completed change"),
|
|
840
955
|
// ── Review gates (Codex-backed) ──────────────────────
|
|
841
|
-
cmd("review-plan", 30, "review", "\u65B9\u6848\u5BA1\u67E5", "Review Plan", "\
|
|
842
|
-
cmd("review-code", 31, "review", "\u4EE3\u7801\u5BA1\u67E5", "Review Code", "\
|
|
956
|
+
cmd("review-plan", 30, "review", "\u65B9\u6848\u5BA1\u67E5", "Review Plan", "\u6309 reviewExecutor \u51B3\u5B9A\u5BA1\u67E5\u4E3B\u4F53\uFF08\u4E3B agent \u76F4\u63A5\u5BA1 / spawn \u72EC\u7ACB\u5B50\u4EE3\u7406\uFF09\uFF0C\u5206\u7EA7\u8F93\u51FA Critical/Warning/Info", "Review plan artifacts with a switchable executor (main agent or a dedicated subagent), graded Critical/Warning/Info"),
|
|
957
|
+
cmd("review-code", 31, "review", "\u4EE3\u7801\u5BA1\u67E5", "Review Code", "\u6309 reviewExecutor \u51B3\u5B9A\u5BA1\u67E5\u4E3B\u4F53\uFF08\u4E3B agent \u76F4\u63A5\u5BA1 / spawn \u72EC\u7ACB\u5B50\u4EE3\u7406\uFF09\uFF0C\u5206\u7EA7\u8F93\u51FA Critical/Warning/Info", "Review code changes with a switchable executor (main agent or a dedicated subagent), graded Critical/Warning/Info"),
|
|
843
958
|
// ── Release pipeline ───────────────────────────────────
|
|
844
959
|
cmd("release", 40, "release", "GitFlow \u53D1\u7248", "GitFlow Release", "GitFlow \u56DB\u573A\u666F\u53D1\u7248\u6D41\u7A0B\uFF0CSemVer \u81EA\u52A8\u63A8\u5BFC\u7248\u672C\u53F7\uFF0C\u4E0A\u7EBF\u5408\u5E76\u4E8C\u9009\u4E00 + \u4E3B\u5206\u652F\u540D\u68C0\u6D4B", "GitFlow branching workflow with SemVer auto-detection, dual merge options and master/main detection"),
|
|
845
960
|
cmd("changelog", 41, "release", "\u751F\u6210 Changelog", "Generate Changelog", "Keep a Changelog \u683C\u5F0F\u751F\u6210 CHANGELOG.md\uFF0C\u6309 commit \u524D\u7F00\u5206\u7EC4", "Generate Keep a Changelog format CHANGELOG.md from commit history"),
|
|
@@ -954,35 +1069,6 @@ async function installPromptFiles(ctx) {
|
|
|
954
1069
|
ctx.result.success = false;
|
|
955
1070
|
}
|
|
956
1071
|
}
|
|
957
|
-
async function migrateLegacyPrompts(options) {
|
|
958
|
-
const home = options?.homeDir ?? homedir();
|
|
959
|
-
const oldDir = join(home, ".claude", ".ly", "prompts");
|
|
960
|
-
const newDir = options?.promptsDir || LY_PROMPTS_DIR;
|
|
961
|
-
if (!await fs.pathExists(oldDir)) {
|
|
962
|
-
return { migrated: false };
|
|
963
|
-
}
|
|
964
|
-
try {
|
|
965
|
-
if (await hasCoexistingLegacyLyProducts(home)) {
|
|
966
|
-
console.warn(`[lycx] \u68C0\u6D4B\u5230 ${join(home, ".claude")} \u4ECD\u88AB ly-workflow \u4F7F\u7528\uFF08\u5171\u5B58\u573A\u666F\uFF09\uFF0C\u8DF3\u8FC7\u89D2\u8272\u8BCD\u8FC1\u79FB\uFF0C\u4FDD\u7559\u4ED6\u65B9\u5728\u7528\u6587\u4EF6`);
|
|
967
|
-
return { migrated: false, from: oldDir, to: newDir };
|
|
968
|
-
}
|
|
969
|
-
await fs.ensureDir(newDir);
|
|
970
|
-
const entries = await fs.readdir(oldDir);
|
|
971
|
-
for (const entry of entries) {
|
|
972
|
-
if (entry === ".DS_Store")
|
|
973
|
-
continue;
|
|
974
|
-
const src = join(oldDir, entry);
|
|
975
|
-
const dst = join(newDir, entry);
|
|
976
|
-
if (await fs.pathExists(dst))
|
|
977
|
-
continue;
|
|
978
|
-
await fs.move(src, dst, { overwrite: false });
|
|
979
|
-
}
|
|
980
|
-
await fs.remove(oldDir);
|
|
981
|
-
return { migrated: true, from: oldDir, to: newDir };
|
|
982
|
-
} catch (error) {
|
|
983
|
-
return { migrated: false, from: oldDir, to: newDir, error: String(error) };
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
1072
|
async function installWorkflows(workflowIds, installDir, force = false, config = {}) {
|
|
987
1073
|
const ctx = {
|
|
988
1074
|
installDir,
|
|
@@ -1033,8 +1119,7 @@ async function uninstallWorkflows(installDir, options) {
|
|
|
1033
1119
|
success: true,
|
|
1034
1120
|
removedSkills: [],
|
|
1035
1121
|
removedLegacyPrompts: [],
|
|
1036
|
-
|
|
1037
|
-
configTomlKept: false,
|
|
1122
|
+
removedPrompts: false,
|
|
1038
1123
|
errors: []
|
|
1039
1124
|
};
|
|
1040
1125
|
const codexSkillsDir = options?.codexSkillsDir || AGENTS_SKILLS_DIR;
|
|
@@ -1095,21 +1180,23 @@ async function uninstallWorkflows(installDir, options) {
|
|
|
1095
1180
|
} catch (error) {
|
|
1096
1181
|
result.errors.push(`Failed to clean legacy codex prompts: ${error}`);
|
|
1097
1182
|
}
|
|
1098
|
-
const
|
|
1099
|
-
const
|
|
1100
|
-
if (await fs.pathExists(
|
|
1183
|
+
const codexPromptsDir = options?.lyPromptsDir || LY_PROMPTS_DIR;
|
|
1184
|
+
const codexPromptsSubDir = join(codexPromptsDir, "codex");
|
|
1185
|
+
if (await fs.pathExists(codexPromptsSubDir)) {
|
|
1101
1186
|
try {
|
|
1102
|
-
await fs.remove(
|
|
1103
|
-
result.
|
|
1187
|
+
await fs.remove(codexPromptsSubDir);
|
|
1188
|
+
result.removedPrompts = true;
|
|
1104
1189
|
} catch (error) {
|
|
1105
|
-
result.errors.push(`Failed to remove
|
|
1190
|
+
result.errors.push(`Failed to remove codex prompts directory: ${error}`);
|
|
1191
|
+
result.success = false;
|
|
1106
1192
|
}
|
|
1107
1193
|
}
|
|
1108
1194
|
const lyDir = options?.lyDir || getLyDir();
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1195
|
+
try {
|
|
1196
|
+
await fs.remove(lyDir);
|
|
1197
|
+
} catch (error) {
|
|
1198
|
+
result.errors.push(`Failed to remove ~/.codex/lyx/: ${error}`);
|
|
1199
|
+
result.success = false;
|
|
1113
1200
|
}
|
|
1114
1201
|
try {
|
|
1115
1202
|
const { cleanupLegacyArtifacts, reportCleanupResult } = await import('../chunks/legacy-cleanup.mjs');
|
|
@@ -1120,23 +1207,50 @@ async function uninstallWorkflows(installDir, options) {
|
|
|
1120
1207
|
return result;
|
|
1121
1208
|
}
|
|
1122
1209
|
|
|
1123
|
-
const OPENAI_OFFICIAL_BASE_URL = "https://api.openai.com/v1";
|
|
1124
|
-
const MODEL_CHOICE_CUSTOM = "\0lyx:custom";
|
|
1125
1210
|
const MODEL_CHOICE_UNSET = "\0lyx:unset";
|
|
1211
|
+
const MODEL_CHOICE_CUSTOM = "\0lyx:custom";
|
|
1212
|
+
function buildModelFieldChoices(input) {
|
|
1213
|
+
const { current } = input;
|
|
1214
|
+
const currentClean = current?.trim();
|
|
1215
|
+
const hasCurrent = currentClean !== void 0 && currentClean !== "";
|
|
1216
|
+
const choices = [
|
|
1217
|
+
{ name: ansis.gray(i18n.t("init:model.unsetChoice")), value: MODEL_CHOICE_UNSET },
|
|
1218
|
+
{ name: ansis.cyan(i18n.t("init:model.customChoice")), value: MODEL_CHOICE_CUSTOM }
|
|
1219
|
+
];
|
|
1220
|
+
let defaultChoice = MODEL_CHOICE_UNSET;
|
|
1221
|
+
if (hasCurrent) {
|
|
1222
|
+
choices.push({ name: currentClean, value: currentClean });
|
|
1223
|
+
defaultChoice = currentClean;
|
|
1224
|
+
}
|
|
1225
|
+
return { choices, defaultChoice };
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
const OPENAI_OFFICIAL_BASE_URL = "https://api.openai.com/v1";
|
|
1126
1229
|
const MODEL_FIELDS = [
|
|
1127
|
-
{ key: "reviewModel", labelKey: "init:model.reviewModelA",
|
|
1128
|
-
{ key: "
|
|
1129
|
-
|
|
1230
|
+
{ key: "reviewModel", labelKey: "init:model.reviewModelA", sanitize: sanitizeReviewModel },
|
|
1231
|
+
{ key: "codingModel", labelKey: "init:model.codingModel", sanitize: sanitizeModelField }
|
|
1232
|
+
];
|
|
1233
|
+
const EXECUTOR_FIELDS = [
|
|
1234
|
+
{ key: "reviewExecutor", labelKey: "init:executor.reviewExecutor" },
|
|
1235
|
+
{ key: "codingExecutor", labelKey: "init:executor.codingExecutor" }
|
|
1130
1236
|
];
|
|
1237
|
+
async function pickExecutorField(input) {
|
|
1238
|
+
const { field, current } = input;
|
|
1239
|
+
const { pick } = await inquirer.prompt([{
|
|
1240
|
+
type: "list",
|
|
1241
|
+
name: "pick",
|
|
1242
|
+
message: i18n.t(field.labelKey),
|
|
1243
|
+
choices: [
|
|
1244
|
+
{ name: i18n.t("init:executor.main"), value: "main" },
|
|
1245
|
+
{ name: i18n.t("init:executor.subagent"), value: "subagent" }
|
|
1246
|
+
],
|
|
1247
|
+
default: current ?? "main"
|
|
1248
|
+
}]);
|
|
1249
|
+
return pick === "subagent" ? "subagent" : "main";
|
|
1250
|
+
}
|
|
1131
1251
|
async function pickModelField(input) {
|
|
1132
|
-
const { field,
|
|
1133
|
-
const
|
|
1134
|
-
const choices = [
|
|
1135
|
-
...models.map((id) => ({ name: id, value: id })),
|
|
1136
|
-
{ name: ansis.cyan(`\u270F\uFE0F ${i18n.t("init:model.customChoice")}`), value: MODEL_CHOICE_CUSTOM },
|
|
1137
|
-
{ name: ansis.gray(i18n.t("init:model.unsetChoice")), value: MODEL_CHOICE_UNSET }
|
|
1138
|
-
];
|
|
1139
|
-
const defaultChoice = inList ? current : current !== void 0 ? MODEL_CHOICE_CUSTOM : MODEL_CHOICE_UNSET;
|
|
1252
|
+
const { field, current } = input;
|
|
1253
|
+
const { choices, defaultChoice } = buildModelFieldChoices({ current });
|
|
1140
1254
|
const { pick } = await inquirer.prompt([{
|
|
1141
1255
|
type: "list",
|
|
1142
1256
|
name: "pick",
|
|
@@ -1147,25 +1261,30 @@ async function pickModelField(input) {
|
|
|
1147
1261
|
}]);
|
|
1148
1262
|
if (pick === MODEL_CHOICE_UNSET)
|
|
1149
1263
|
return void 0;
|
|
1150
|
-
if (pick
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1264
|
+
if (pick === MODEL_CHOICE_CUSTOM) {
|
|
1265
|
+
const { custom } = await inquirer.prompt([{
|
|
1266
|
+
type: "input",
|
|
1267
|
+
name: "custom",
|
|
1268
|
+
message: i18n.t("init:model.customPrompt")
|
|
1269
|
+
}]);
|
|
1270
|
+
const model = custom?.trim();
|
|
1271
|
+
return model ? model : void 0;
|
|
1272
|
+
}
|
|
1273
|
+
return typeof pick === "string" ? pick.trim() : void 0;
|
|
1159
1274
|
}
|
|
1160
|
-
async function
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1275
|
+
async function printCodexStatus() {
|
|
1276
|
+
console.log();
|
|
1277
|
+
console.log(ansis.cyan.bold(` \u{1F4CA} ${i18n.t("init:codexStatus.title")}`));
|
|
1278
|
+
console.log();
|
|
1279
|
+
const currentModel = await readCodexCurrentModel();
|
|
1280
|
+
console.log(` \xB7 ${ansis.cyan(i18n.t("init:codexStatus.mainModelLabel"))} ${currentModel ? i18n.t("init:codexStatus.mainModel", { model: currentModel }) : ansis.gray(i18n.t("init:codexStatus.notDetected"))}`);
|
|
1281
|
+
const providers = await listModelProviders();
|
|
1282
|
+
console.log(` \xB7 ${ansis.cyan(i18n.t("init:codexStatus.providersLabel"))} ${providers.length > 0 ? providers.map((p) => p.name).join(", ") : ansis.gray(i18n.t("init:codexStatus.noProvider"))}`);
|
|
1283
|
+
const models = await readModelsJson();
|
|
1284
|
+
console.log(` \xB7 ${ansis.cyan(i18n.t("init:codexStatus.registeredLabel"))} ${models === void 0 ? ansis.gray(i18n.t("init:codexStatus.notDetected")) : i18n.t("init:codexStatus.registeredModels", { count: models.length })}`);
|
|
1285
|
+
console.log();
|
|
1286
|
+
console.log(ansis.yellow(` \u26A0 ${i18n.t("init:codexStatus.reasoningHint")}`));
|
|
1287
|
+
console.log(ansis.gray(` ${i18n.t("init:codexStatus.spawnableHint")}`));
|
|
1169
1288
|
}
|
|
1170
1289
|
async function collectCodexHostConfig(options) {
|
|
1171
1290
|
console.log();
|
|
@@ -1189,20 +1308,8 @@ async function collectCodexHostConfig(options) {
|
|
|
1189
1308
|
{ name: `${ansis.cyan("\u25CF")} ${i18n.t("init:mode.providerCustom")}`, value: { type: "custom" } }
|
|
1190
1309
|
]
|
|
1191
1310
|
}]);
|
|
1192
|
-
let baseUrl = OPENAI_OFFICIAL_BASE_URL;
|
|
1193
|
-
let apiKey = "";
|
|
1194
1311
|
if (provider.type === "existing") {
|
|
1195
|
-
|
|
1196
|
-
const envKey = provider.provider.envKey;
|
|
1197
|
-
if (envKey && !process.env[envKey]) {
|
|
1198
|
-
const { key } = await inquirer.prompt([{
|
|
1199
|
-
type: "password",
|
|
1200
|
-
name: "key",
|
|
1201
|
-
message: i18n.t("init:mode.envKeyMissingPrompt", { envKey }),
|
|
1202
|
-
mask: "*"
|
|
1203
|
-
}]);
|
|
1204
|
-
apiKey = key?.trim() || "";
|
|
1205
|
-
}
|
|
1312
|
+
console.log(ansis.gray(` \u2713 ${i18n.t("init:mode.providerExistingSelected", { name: provider.provider.name })}`));
|
|
1206
1313
|
} else if (provider.type === "custom") {
|
|
1207
1314
|
console.log();
|
|
1208
1315
|
const { name } = await inquirer.prompt([{
|
|
@@ -1218,14 +1325,7 @@ async function collectCodexHostConfig(options) {
|
|
|
1218
1325
|
message: i18n.t("init:mode.customUrlPrompt"),
|
|
1219
1326
|
default: OPENAI_OFFICIAL_BASE_URL
|
|
1220
1327
|
}]);
|
|
1221
|
-
const
|
|
1222
|
-
type: "password",
|
|
1223
|
-
name: "key",
|
|
1224
|
-
message: `${i18n.t("init:mode.customKeyPrompt")} ${ansis.gray(`(${i18n.t("init:mode.optional")})`)}`,
|
|
1225
|
-
mask: "*"
|
|
1226
|
-
}]);
|
|
1227
|
-
baseUrl = (url || OPENAI_OFFICIAL_BASE_URL).trim();
|
|
1228
|
-
apiKey = key?.trim() || "";
|
|
1328
|
+
const baseUrl = (url || OPENAI_OFFICIAL_BASE_URL).trim();
|
|
1229
1329
|
const writeResult = await upsertModelProvider({ name: providerName, baseUrl });
|
|
1230
1330
|
if (writeResult.status === "written") {
|
|
1231
1331
|
console.log(ansis.green(` \u2713 ${i18n.t("init:mode.providerWritten", { name: providerName })}`));
|
|
@@ -1235,34 +1335,57 @@ async function collectCodexHostConfig(options) {
|
|
|
1235
1335
|
console.log(ansis.yellow(` \u26A0 ${i18n.t("init:mode.providerWriteFailed", { error: writeResult.error })}`));
|
|
1236
1336
|
}
|
|
1237
1337
|
}
|
|
1338
|
+
await printCodexStatus();
|
|
1238
1339
|
console.log();
|
|
1239
|
-
console.log(ansis.cyan.bold(`
|
|
1340
|
+
console.log(ansis.cyan.bold(` ${i18n.t("init:executor.title")}`));
|
|
1341
|
+
console.log();
|
|
1342
|
+
console.log(ansis.gray(` ${i18n.t("init:executor.hint")}`));
|
|
1240
1343
|
console.log();
|
|
1241
|
-
if (baseUrl) {
|
|
1242
|
-
console.log(ansis.gray(` ${i18n.t("init:mode.modelFetching", { baseUrl })}`));
|
|
1243
|
-
}
|
|
1244
|
-
const fetchResult = await fetchCodexModels({ baseUrl, apiKey });
|
|
1245
|
-
if (!fetchResult.ok) {
|
|
1246
|
-
console.log(ansis.yellow(` \u26A0 ${i18n.t("init:mode.modelFetchFailed", { reason: fetchResult.error })}`));
|
|
1247
|
-
}
|
|
1248
1344
|
const collected = {};
|
|
1345
|
+
for (const field of EXECUTOR_FIELDS) {
|
|
1346
|
+
collected[field.key] = await pickExecutorField({
|
|
1347
|
+
field,
|
|
1348
|
+
current: options.defaults[field.key]
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
console.log();
|
|
1352
|
+
console.log(ansis.cyan.bold(` \u{1F9E0} ${i18n.t("init:model.trioTitle")}`));
|
|
1353
|
+
console.log();
|
|
1354
|
+
console.log(ansis.gray(` ${i18n.t("init:model.trioCandidatesHint")}`));
|
|
1355
|
+
console.log();
|
|
1249
1356
|
for (const field of MODEL_FIELDS) {
|
|
1357
|
+
const executorKey = field.key === "reviewModel" ? "reviewExecutor" : "codingExecutor";
|
|
1358
|
+
if (collected[executorKey] !== "subagent") {
|
|
1359
|
+
collected[field.key] = options.defaults[field.key]?.trim() || void 0;
|
|
1360
|
+
continue;
|
|
1361
|
+
}
|
|
1250
1362
|
const current = options.defaults[field.key]?.trim() || void 0;
|
|
1251
|
-
const raw =
|
|
1363
|
+
const raw = await pickModelField({ field, current });
|
|
1252
1364
|
collected[field.key] = field.sanitize(raw);
|
|
1253
1365
|
}
|
|
1254
1366
|
return collected;
|
|
1255
1367
|
}
|
|
1256
1368
|
function printSummary(input) {
|
|
1257
1369
|
const { models, commandCount } = input;
|
|
1370
|
+
const executorLabel = (kind) => kind === "subagent" ? ansis.green(i18n.t("init:summary.executorSubagent")) : ansis.gray(i18n.t("init:summary.executorMain"));
|
|
1371
|
+
const modelLabel = (value, effective) => {
|
|
1372
|
+
if (!value)
|
|
1373
|
+
return ansis.gray(i18n.t("init:host.reviewModelUnset"));
|
|
1374
|
+
if (!effective)
|
|
1375
|
+
return ansis.yellow(i18n.t("init:summary.modelIneffective", { model: value }));
|
|
1376
|
+
return ansis.green(value);
|
|
1377
|
+
};
|
|
1378
|
+
const reviewEffective = models.reviewExecutor === "subagent";
|
|
1379
|
+
const codingEffective = models.codingExecutor === "subagent";
|
|
1258
1380
|
console.log();
|
|
1259
1381
|
console.log(ansis.yellow("\u2501".repeat(50)));
|
|
1260
1382
|
console.log(ansis.bold(` ${i18n.t("init:summary.title")}`));
|
|
1261
1383
|
console.log();
|
|
1262
1384
|
console.log(` ${ansis.cyan(i18n.t("init:summary.host"))} ${ansis.green("codex")}`);
|
|
1263
|
-
console.log(` ${ansis.cyan(i18n.t("init:summary.
|
|
1264
|
-
console.log(` ${ansis.cyan(i18n.t("init:summary.
|
|
1265
|
-
console.log(` ${ansis.cyan(i18n.t("init:summary.
|
|
1385
|
+
console.log(` ${ansis.cyan(i18n.t("init:summary.reviewExecutor"))} ${executorLabel(models.reviewExecutor)}`);
|
|
1386
|
+
console.log(` ${ansis.cyan(i18n.t("init:summary.codingExecutor"))} ${executorLabel(models.codingExecutor)}`);
|
|
1387
|
+
console.log(` ${ansis.cyan(i18n.t("init:summary.reviewModelA"))} ${modelLabel(models.reviewModel, reviewEffective)}`);
|
|
1388
|
+
console.log(` ${ansis.cyan(i18n.t("init:summary.codingModel"))} ${modelLabel(models.codingModel, codingEffective)}`);
|
|
1266
1389
|
console.log(` ${ansis.cyan(i18n.t("init:summary.commandCount"))} ${ansis.yellow(commandCount.toString())}`);
|
|
1267
1390
|
console.log(ansis.yellow("\u2501".repeat(50)));
|
|
1268
1391
|
console.log();
|
|
@@ -1301,14 +1424,16 @@ async function init(options = {}) {
|
|
|
1301
1424
|
await initI18n(language);
|
|
1302
1425
|
}
|
|
1303
1426
|
const selectedWorkflows = getCoreCommandIds();
|
|
1304
|
-
const
|
|
1427
|
+
const existingExtras = sanitizeCodexHostExtras(existingConfig?.codexHost);
|
|
1428
|
+
const defaultCollected = {
|
|
1429
|
+
reviewExecutor: sanitizeExecutor(existingConfig?.codexHost?.reviewExecutor),
|
|
1430
|
+
codingExecutor: sanitizeExecutor(existingConfig?.codexHost?.codingExecutor),
|
|
1305
1431
|
reviewModel: sanitizeReviewModel(existingConfig?.codexHost?.reviewModel),
|
|
1306
|
-
|
|
1307
|
-
codingModel: sanitizeModelField(existingConfig?.codexHost?.codingModel)
|
|
1432
|
+
codingModel: existingExtras.codingModel
|
|
1308
1433
|
};
|
|
1309
|
-
let collectedModels = { ...
|
|
1434
|
+
let collectedModels = { ...defaultCollected };
|
|
1310
1435
|
if (!options.skipPrompt) {
|
|
1311
|
-
collectedModels = await collectCodexHostConfig({ defaults:
|
|
1436
|
+
collectedModels = await collectCodexHostConfig({ defaults: defaultCollected });
|
|
1312
1437
|
printSummary({ models: collectedModels, commandCount: selectedWorkflows.length });
|
|
1313
1438
|
const { confirm } = await inquirer.prompt([{
|
|
1314
1439
|
type: "confirm",
|
|
@@ -1330,24 +1455,15 @@ async function init(options = {}) {
|
|
|
1330
1455
|
language,
|
|
1331
1456
|
installedWorkflows: selectedWorkflows,
|
|
1332
1457
|
codexHost: {
|
|
1458
|
+
...existingExtras,
|
|
1459
|
+
reviewExecutor: collectedModels.reviewExecutor,
|
|
1460
|
+
codingExecutor: collectedModels.codingExecutor,
|
|
1333
1461
|
reviewModel: collectedModels.reviewModel,
|
|
1334
|
-
reviewModelB: collectedModels.reviewModelB,
|
|
1335
1462
|
codingModel: collectedModels.codingModel
|
|
1336
1463
|
},
|
|
1337
1464
|
installedHosts: ["codex"]
|
|
1338
1465
|
});
|
|
1339
1466
|
await writeLyConfig(config);
|
|
1340
|
-
try {
|
|
1341
|
-
const migration = await migrateLegacyPrompts();
|
|
1342
|
-
if (migration.migrated) {
|
|
1343
|
-
console.log();
|
|
1344
|
-
console.log(` ${ansis.green("\u2713")} ${i18n.t("init:promptsMigrated")}`);
|
|
1345
|
-
} else if (migration.error) {
|
|
1346
|
-
console.log();
|
|
1347
|
-
console.log(` ${ansis.yellow("\u26A0")} ${i18n.t("init:promptsMigrationFailed", { error: migration.error })}`);
|
|
1348
|
-
}
|
|
1349
|
-
} catch {
|
|
1350
|
-
}
|
|
1351
1467
|
const result = await installWorkflows(selectedWorkflows, "", options.force, {
|
|
1352
1468
|
reviewModel: collectedModels.reviewModel
|
|
1353
1469
|
});
|
|
@@ -1374,7 +1490,7 @@ async function init(options = {}) {
|
|
|
1374
1490
|
console.log();
|
|
1375
1491
|
console.log(` ${ansis.cyan(i18n.t("init:installedPrompts"))}`);
|
|
1376
1492
|
for (const name of result.installedPrompts) {
|
|
1377
|
-
console.log(` ${ansis.green("\u2713")} ${name} ${ansis.gray("\u2192 ~/.
|
|
1493
|
+
console.log(` ${ansis.green("\u2713")} ${name} ${ansis.gray("\u2192 ~/.codex/lyx/prompts/")}`);
|
|
1378
1494
|
}
|
|
1379
1495
|
}
|
|
1380
1496
|
try {
|
|
@@ -1384,7 +1500,7 @@ async function init(options = {}) {
|
|
|
1384
1500
|
}
|
|
1385
1501
|
console.log();
|
|
1386
1502
|
console.log(ansis.green(` \u2713 ${i18n.t("init:installSuccess")}`));
|
|
1387
|
-
console.log(ansis.gray(` Config: ~/.
|
|
1503
|
+
console.log(ansis.gray(` Config: ~/.codex/lyx/config.toml`));
|
|
1388
1504
|
console.log();
|
|
1389
1505
|
} catch (error) {
|
|
1390
1506
|
spinner.fail(ansis.red(i18n.t("init:installFailed")));
|
|
@@ -1935,18 +2051,47 @@ function readLyConfigSync() {
|
|
|
1935
2051
|
async function configReviewModel() {
|
|
1936
2052
|
const config = await readLyConfig();
|
|
1937
2053
|
const currentReviewModel = sanitizeReviewModel(config?.codexHost?.reviewModel);
|
|
2054
|
+
const currentExecutor = sanitizeExecutor(config?.codexHost?.reviewExecutor);
|
|
1938
2055
|
console.log();
|
|
1939
2056
|
console.log(ansis.cyan.bold(` ${i18n.t("init:model.title")}`));
|
|
1940
2057
|
console.log();
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
2058
|
+
const { executor } = await inquirer.prompt([{
|
|
2059
|
+
type: "list",
|
|
2060
|
+
name: "executor",
|
|
2061
|
+
message: i18n.t("init:executor.reviewExecutor"),
|
|
2062
|
+
choices: [
|
|
2063
|
+
{ name: i18n.t("init:executor.main"), value: "main" },
|
|
2064
|
+
{ name: i18n.t("init:executor.subagent"), value: "subagent" }
|
|
2065
|
+
],
|
|
2066
|
+
default: currentExecutor ?? "main"
|
|
1947
2067
|
}]);
|
|
1948
|
-
const
|
|
1949
|
-
|
|
2068
|
+
const nextExecutor = executor === "subagent" ? "subagent" : "main";
|
|
2069
|
+
let next = currentReviewModel;
|
|
2070
|
+
if (nextExecutor === "subagent") {
|
|
2071
|
+
console.log(ansis.gray(` ${i18n.t("init:host.reviewModelHint")}`));
|
|
2072
|
+
const { choices, defaultChoice } = buildModelFieldChoices({
|
|
2073
|
+
current: currentReviewModel
|
|
2074
|
+
});
|
|
2075
|
+
const { model } = await inquirer.prompt([{
|
|
2076
|
+
type: "list",
|
|
2077
|
+
name: "model",
|
|
2078
|
+
message: i18n.t("init:host.reviewModelPrompt"),
|
|
2079
|
+
choices,
|
|
2080
|
+
default: defaultChoice,
|
|
2081
|
+
pageSize: 15
|
|
2082
|
+
}]);
|
|
2083
|
+
if (model === MODEL_CHOICE_CUSTOM) {
|
|
2084
|
+
const { custom } = await inquirer.prompt([{
|
|
2085
|
+
type: "input",
|
|
2086
|
+
name: "custom",
|
|
2087
|
+
message: i18n.t("init:model.customPrompt")
|
|
2088
|
+
}]);
|
|
2089
|
+
next = custom?.trim() || void 0;
|
|
2090
|
+
} else {
|
|
2091
|
+
next = model === MODEL_CHOICE_UNSET ? void 0 : sanitizeReviewModel(model);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
if (next === currentReviewModel && nextExecutor === (currentExecutor ?? "main")) {
|
|
1950
2095
|
console.log(ansis.gray(` ${i18n.t("common:configNotModified")}`));
|
|
1951
2096
|
return;
|
|
1952
2097
|
}
|
|
@@ -1955,16 +2100,13 @@ async function configReviewModel() {
|
|
|
1955
2100
|
console.log(` ${ansis.yellow("\u26A0")} ${PACKAGE_NAME} config not initialized`);
|
|
1956
2101
|
return;
|
|
1957
2102
|
}
|
|
1958
|
-
const
|
|
1959
|
-
|
|
1960
|
-
if (next || existingB || existingCoding) {
|
|
2103
|
+
const existingExtras = sanitizeCodexHostExtras(fresh.codexHost);
|
|
2104
|
+
{
|
|
1961
2105
|
fresh.codexHost = {
|
|
1962
|
-
...
|
|
1963
|
-
|
|
1964
|
-
...
|
|
2106
|
+
...existingExtras,
|
|
2107
|
+
reviewExecutor: nextExecutor,
|
|
2108
|
+
...next ? { reviewModel: next } : {}
|
|
1965
2109
|
};
|
|
1966
|
-
} else {
|
|
1967
|
-
fresh.codexHost = void 0;
|
|
1968
2110
|
}
|
|
1969
2111
|
await writeLyConfig(fresh);
|
|
1970
2112
|
console.log();
|
|
@@ -2038,13 +2180,9 @@ async function uninstall() {
|
|
|
2038
2180
|
console.log(` ${ansis.gray("\u2022")} ${file}`);
|
|
2039
2181
|
}
|
|
2040
2182
|
}
|
|
2041
|
-
if (result.
|
|
2042
|
-
console.log();
|
|
2043
|
-
console.log(ansis.cyan(` ${i18n.t("menu:uninstall.removedSharedPrompts")}`));
|
|
2044
|
-
}
|
|
2045
|
-
if (result.configTomlKept) {
|
|
2183
|
+
if (result.removedPrompts) {
|
|
2046
2184
|
console.log();
|
|
2047
|
-
console.log(ansis.cyan(` ${i18n.t("menu:uninstall.
|
|
2185
|
+
console.log(ansis.cyan(` ${i18n.t("menu:uninstall.removedPrompts")}`));
|
|
2048
2186
|
}
|
|
2049
2187
|
if (isGlobalInstall) {
|
|
2050
2188
|
console.log();
|
|
@@ -2065,4 +2203,4 @@ async function uninstall() {
|
|
|
2065
2203
|
console.log();
|
|
2066
2204
|
}
|
|
2067
2205
|
|
|
2068
|
-
export { ADAPTERS as A,
|
|
2206
|
+
export { ADAPTERS as A, sanitizeModelField as B, sanitizeExecutor as C, BIN_NAME as D, LY_PROMPTS_DIR as L, PACKAGE_NAME as P, i18n as a, initI18n as b, changeLanguage as c, createDefaultConfig as d, getLyDir as e, getLyPromptsDir as f, getConfigPath as g, getWorkflowById as h, init as i, getWorkflowConfigs as j, installWorkflows as k, uninstallWorkflows as l, checkForUpdates as m, compareVersions as n, getCurrentVersion as o, getLatestVersion as p, AGENTS_SKILLS_DIR as q, readLyConfig as r, showMainMenu as s, readCodexCurrentModel as t, update as u, version as v, writeLyConfig as w, sanitizeSpawnableModels as x, sanitizeReasoningEffort as y, sanitizeReviewModel as z };
|