abelworkflow 0.9.2 → 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +55 -182
  2. package/bin/abelworkflow.mjs +5 -3
  3. package/lib/cli/args.mjs +76 -0
  4. package/lib/cli/main.mjs +387 -0
  5. package/lib/cli/prompts.mjs +119 -0
  6. package/lib/config/dotenv.mjs +102 -0
  7. package/lib/config/jsonc.mjs +80 -0
  8. package/lib/config/store.mjs +310 -0
  9. package/lib/config/toml.mjs +638 -0
  10. package/lib/installer/assets.mjs +326 -0
  11. package/lib/installer/install.mjs +167 -0
  12. package/lib/installer/links.mjs +427 -0
  13. package/lib/installer/render.mjs +46 -0
  14. package/lib/installer/state.mjs +182 -0
  15. package/lib/paths.mjs +99 -0
  16. package/lib/providers/claude.mjs +388 -0
  17. package/lib/providers/codex.mjs +433 -0
  18. package/lib/providers/pi.mjs +362 -0
  19. package/lib/providers/skills.mjs +164 -0
  20. package/lib/templates/codex/agents/default.toml +7 -0
  21. package/lib/templates/codex/agents/explorer.toml +7 -0
  22. package/lib/templates/codex/agents/planner.toml +7 -0
  23. package/lib/templates/codex/agents/reviewer.toml +7 -0
  24. package/lib/templates/codex/agents/worker.toml +7 -0
  25. package/lib/templates/codex/config-base.toml +11 -3
  26. package/{AGENTS.md → lib/templates/workflow/AGENTS.md} +8 -3
  27. package/{commands → lib/templates/workflow/commands}/abel-diagnose.md +5 -5
  28. package/{commands → lib/templates/workflow/commands}/abel-implement.md +3 -8
  29. package/{commands → lib/templates/workflow/commands}/abel-init.md +1 -1
  30. package/{commands → lib/templates/workflow/commands}/abel-plan.md +1 -7
  31. package/{commands → lib/templates/workflow/commands}/abel-research.md +1 -1
  32. package/{.gitignore → lib/templates/workflow/gitignore.template} +0 -2
  33. package/lib/tools/cli-installer.mjs +277 -0
  34. package/package.json +43 -10
  35. package/skills/context7-auto-research/SKILL.md +5 -5
  36. package/skills/context7-auto-research/{context7-api.js → context7-api.cjs} +5 -8
  37. package/skills/dev-browser/SKILL.md +53 -33
  38. package/skills/dev-browser/dist/scripts/start.d.ts +2 -0
  39. package/skills/dev-browser/dist/scripts/start.d.ts.map +1 -0
  40. package/skills/dev-browser/dist/scripts/start.js +118 -0
  41. package/skills/dev-browser/dist/scripts/start.js.map +1 -0
  42. package/skills/dev-browser/dist/src/client.d.ts +93 -0
  43. package/skills/dev-browser/dist/src/client.d.ts.map +1 -0
  44. package/skills/dev-browser/dist/src/client.js +310 -0
  45. package/skills/dev-browser/dist/src/client.js.map +1 -0
  46. package/skills/dev-browser/dist/src/entrypoint.d.ts +29 -0
  47. package/skills/dev-browser/dist/src/entrypoint.d.ts.map +1 -0
  48. package/skills/dev-browser/dist/src/entrypoint.js +106 -0
  49. package/skills/dev-browser/dist/src/entrypoint.js.map +1 -0
  50. package/skills/dev-browser/dist/src/index.d.ts +4 -0
  51. package/skills/dev-browser/dist/src/index.d.ts.map +1 -0
  52. package/skills/dev-browser/dist/src/index.js +2 -0
  53. package/skills/dev-browser/dist/src/index.js.map +1 -0
  54. package/skills/dev-browser/dist/src/page-api.d.ts +25 -0
  55. package/skills/dev-browser/dist/src/page-api.d.ts.map +1 -0
  56. package/skills/dev-browser/dist/src/page-api.js +104 -0
  57. package/skills/dev-browser/dist/src/page-api.js.map +1 -0
  58. package/skills/dev-browser/dist/src/relay.d.ts +27 -0
  59. package/skills/dev-browser/dist/src/relay.d.ts.map +1 -0
  60. package/skills/dev-browser/dist/src/relay.js +521 -0
  61. package/skills/dev-browser/dist/src/relay.js.map +1 -0
  62. package/skills/dev-browser/dist/src/runtime.d.ts +45 -0
  63. package/skills/dev-browser/dist/src/runtime.d.ts.map +1 -0
  64. package/skills/dev-browser/dist/src/runtime.js +54 -0
  65. package/skills/dev-browser/dist/src/runtime.js.map +1 -0
  66. package/skills/dev-browser/dist/src/snapshot/browser-script.d.ts +23 -0
  67. package/skills/dev-browser/dist/src/snapshot/browser-script.d.ts.map +1 -0
  68. package/skills/dev-browser/{src/snapshot/browser-script.ts → dist/src/snapshot/browser-script.js} +21 -30
  69. package/skills/dev-browser/dist/src/snapshot/browser-script.js.map +1 -0
  70. package/skills/dev-browser/dist/src/snapshot/index.d.ts +14 -0
  71. package/skills/dev-browser/dist/src/snapshot/index.d.ts.map +1 -0
  72. package/skills/dev-browser/{src/snapshot/index.ts → dist/src/snapshot/index.js} +2 -2
  73. package/skills/dev-browser/dist/src/snapshot/index.js.map +1 -0
  74. package/skills/dev-browser/dist/src/snapshot/inject.d.ts +13 -0
  75. package/skills/dev-browser/dist/src/snapshot/inject.d.ts.map +1 -0
  76. package/skills/dev-browser/{src/snapshot/inject.ts → dist/src/snapshot/inject.js} +2 -2
  77. package/skills/dev-browser/dist/src/snapshot/inject.js.map +1 -0
  78. package/skills/dev-browser/dist/src/standalone.d.ts +32 -0
  79. package/skills/dev-browser/dist/src/standalone.d.ts.map +1 -0
  80. package/skills/dev-browser/dist/src/standalone.js +174 -0
  81. package/skills/dev-browser/dist/src/standalone.js.map +1 -0
  82. package/skills/dev-browser/dist/src/startup.d.ts +55 -0
  83. package/skills/dev-browser/dist/src/startup.d.ts.map +1 -0
  84. package/skills/dev-browser/dist/src/startup.js +81 -0
  85. package/skills/dev-browser/dist/src/startup.js.map +1 -0
  86. package/skills/dev-browser/dist/src/target-registry.d.ts +29 -0
  87. package/skills/dev-browser/dist/src/target-registry.d.ts.map +1 -0
  88. package/skills/dev-browser/dist/src/target-registry.js +135 -0
  89. package/skills/dev-browser/dist/src/target-registry.js.map +1 -0
  90. package/skills/dev-browser/dist/src/types.d.ts +27 -0
  91. package/skills/dev-browser/dist/src/types.d.ts.map +1 -0
  92. package/skills/dev-browser/dist/src/types.js +2 -0
  93. package/skills/dev-browser/dist/src/types.js.map +1 -0
  94. package/skills/dev-browser/package-lock.json +67 -1510
  95. package/skills/dev-browser/package.json +18 -14
  96. package/skills/dev-browser/references/scraping.md +94 -105
  97. package/skills/grok-search/defaults.json +3 -0
  98. package/skills/grok-search/gitignore.template +4 -0
  99. package/skills/grok-search/scripts/_dotenv.py +5 -1
  100. package/skills/grok-search/scripts/groksearch_cli.py +15 -33
  101. package/skills/prompt-enhancer/TEMPLATE.md +0 -4
  102. package/skills/prompt-enhancer/requirements.txt +0 -1
  103. package/skills/prompt-enhancer/scripts/_dotenv.py +5 -1
  104. package/skills/prompt-enhancer/scripts/enhance.py +4 -51
  105. package/skills/prompt-enhancer/scripts/prompt_enhancer_entry.py +1 -16
  106. package/.skill-lock.json +0 -29
  107. package/lib/cli/logic.mjs +0 -213
  108. package/lib/cli.mjs +0 -3016
  109. package/skills/confidence-check/SKILL.md +0 -34
  110. package/skills/confidence-check/confidence.ts +0 -276
  111. package/skills/dev-browser/bun.lock +0 -460
  112. package/skills/dev-browser/scripts/start.ts +0 -280
  113. package/skills/dev-browser/src/client.ts +0 -474
  114. package/skills/dev-browser/src/entrypoint.ts +0 -157
  115. package/skills/dev-browser/src/index.ts +0 -289
  116. package/skills/dev-browser/src/relay.ts +0 -731
  117. package/skills/dev-browser/src/runtime.test.ts +0 -60
  118. package/skills/dev-browser/src/runtime.ts +0 -171
  119. package/skills/dev-browser/src/startup.test.ts +0 -95
  120. package/skills/dev-browser/src/startup.ts +0 -153
  121. package/skills/dev-browser/src/types.ts +0 -35
  122. package/skills/dev-browser/tsconfig.json +0 -36
  123. package/skills/dev-browser/vitest.config.ts +0 -12
  124. package/skills/sequential-think/SKILL.md +0 -198
  125. package/skills/sequential-think/scripts/.env.example +0 -5
  126. package/skills/sequential-think/scripts/sequential_think_cli.py +0 -253
@@ -0,0 +1,362 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { join } from "node:path";
3
+ import * as p from "@clack/prompts";
4
+ import { piInsecureTlsHeader } from "../../extensions/pi-gpt-responses-compat/tls-fetch.mjs";
5
+ import {
6
+ readJsonFileSafe,
7
+ readJsoncFileSafe,
8
+ updateLockedJson,
9
+ writeJsonFileWithBackup,
10
+ writeText
11
+ } from "../config/store.mjs";
12
+ import { defaultPaths, maskSecret, pathToLabel } from "../paths.mjs";
13
+
14
+ const piProviderId = "gpt";
15
+ const piDefaultApi = "openai-completions";
16
+ const piDefaultBaseUrl = "https://api.openai.com/v1";
17
+ const piDefaultModel = "gpt-5.5";
18
+ const minimumPiVersion = [0, 80, 0];
19
+
20
+ async function updatePiAuthFile(path, apiKey) {
21
+ return updateLockedJson(path, (auth) => {
22
+ if (!auth || typeof auth !== "object" || Array.isArray(auth)) {
23
+ throw new TypeError("Pi auth.json must contain a JSON object");
24
+ }
25
+ return buildPiAuthConfig(auth, apiKey);
26
+ }, {
27
+ sensitive: true,
28
+ retries: {
29
+ retries: 10,
30
+ factor: 2,
31
+ minTimeout: 100,
32
+ maxTimeout: 10000,
33
+ randomize: true
34
+ }
35
+ });
36
+ }
37
+
38
+ function parsePiVersion(value) {
39
+ const match = String(value || "").match(/(?:^|\D)v?(\d+)\.(\d+)\.(\d+)(?:\D|$)/u);
40
+ return match ? match.slice(1).map(Number) : null;
41
+ }
42
+
43
+ function assertSupportedPiVersion(value) {
44
+ const version = parsePiVersion(value);
45
+ if (!version) {
46
+ throw new Error("无法检测 Pi 版本;配置自定义 Provider 需要 Pi 0.80.0 或更高版本。");
47
+ }
48
+ for (let index = 0; index < minimumPiVersion.length; index += 1) {
49
+ if (version[index] > minimumPiVersion[index]) return version;
50
+ if (version[index] < minimumPiVersion[index]) {
51
+ throw new Error(`当前 Pi ${version.join(".")} 不支持 auth-only 自定义 Provider;请升级到 Pi 0.80.0 或更高版本。`);
52
+ }
53
+ }
54
+ return version;
55
+ }
56
+
57
+ function detectPiVersion() {
58
+ const result = spawnSync("pi", ["--version"], {
59
+ encoding: "utf8",
60
+ shell: process.platform === "win32",
61
+ stdio: ["ignore", "pipe", "pipe"]
62
+ });
63
+ return result.status === 0 ? `${result.stdout || ""} ${result.stderr || ""}`.trim() : undefined;
64
+ }
65
+
66
+ function parsePiModelIds(value) {
67
+ return [...new Set(String(value || "")
68
+ .split(/[\n,]+/u)
69
+ .map((item) => item.trim())
70
+ .filter(Boolean))];
71
+ }
72
+
73
+ function getOpenAiUrlPathname(value) {
74
+ const input = String(value || "").trim();
75
+ try {
76
+ return new URL(input).pathname.replace(/\/+$/u, "");
77
+ } catch {
78
+ return input.split(/[?#]/u)[0].replace(/\/+$/u, "");
79
+ }
80
+ }
81
+
82
+ function inferPiApiFromBaseUrl(value) {
83
+ const pathname = getOpenAiUrlPathname(value);
84
+ if (pathname.endsWith("/v1/chat/completions")) {
85
+ return "openai-completions";
86
+ }
87
+ if (pathname.endsWith("/v1/responses")) {
88
+ return "openai-responses";
89
+ }
90
+ return null;
91
+ }
92
+
93
+ function normalizeOpenAiBaseUrl(value) {
94
+ const input = String(value || "").trim();
95
+ try {
96
+ const url = new URL(input);
97
+ let pathname = url.pathname.replace(/\/+$/u, "")
98
+ .replace(/\/v1\/(?:chat\/completions|responses)$/u, "/v1");
99
+ if (!pathname.endsWith("/v1")) {
100
+ pathname = `${pathname}/v1`;
101
+ }
102
+ url.pathname = pathname;
103
+ return url.toString();
104
+ } catch {
105
+ const baseUrl = input.replace(/\/+$/u, "")
106
+ .replace(/\/v1\/(?:chat\/completions|responses)$/u, "/v1");
107
+ return baseUrl.endsWith("/v1") ? baseUrl : `${baseUrl}/v1`;
108
+ }
109
+ }
110
+
111
+ function getPiApiPromptOptions() {
112
+ return [
113
+ { value: "openai-completions", label: "OpenAI Chat Completions(推荐)" },
114
+ { value: "openai-responses", label: "OpenAI Responses API" }
115
+ ];
116
+ }
117
+
118
+ function resolveExistingPiApiConfig(modelsConfig = {}, settings = {}, auth = {}) {
119
+ const provider = modelsConfig.providers?.[piProviderId] && typeof modelsConfig.providers[piProviderId] === "object"
120
+ ? modelsConfig.providers[piProviderId]
121
+ : {};
122
+ const credential = auth[piProviderId] && typeof auth[piProviderId] === "object"
123
+ ? auth[piProviderId]
124
+ : {};
125
+ const authApiKey = credential.type === "api_key" && typeof credential.key === "string"
126
+ ? credential.key
127
+ : "";
128
+ const models = Array.isArray(provider.models) ? provider.models.filter((model) => model?.id) : [];
129
+ return {
130
+ baseUrl: provider.baseUrl || piDefaultBaseUrl,
131
+ api: provider.api || piDefaultApi,
132
+ apiKey: authApiKey || provider.apiKey || "",
133
+ modelIds: models.map((model) => model.id),
134
+ defaultModel: settings.defaultProvider === piProviderId && settings.defaultModel
135
+ ? settings.defaultModel
136
+ : models[0]?.id || piDefaultModel
137
+ };
138
+ }
139
+
140
+ function buildPiModelConfig(modelId, existingModel = {}) {
141
+ return {
142
+ ...existingModel,
143
+ id: modelId,
144
+ name: existingModel.name || modelId,
145
+ reasoning: existingModel.reasoning ?? true,
146
+ input: Array.isArray(existingModel.input) ? existingModel.input : ["text", "image"],
147
+ contextWindow: existingModel.contextWindow ?? 262144,
148
+ maxTokens: existingModel.maxTokens ?? 64000
149
+ };
150
+ }
151
+
152
+ function hasPiInsecureTlsSetting(modelsConfig = {}) {
153
+ const headers = modelsConfig.providers?.[piProviderId]?.headers;
154
+ return headers && typeof headers === "object"
155
+ ? Object.keys(headers).some((key) => key.toLowerCase() === piInsecureTlsHeader)
156
+ : false;
157
+ }
158
+
159
+ function buildPiModelsConfig(modelsConfig = {}, { baseUrl, api, modelIds, insecureTls = false }) {
160
+ const providers = modelsConfig.providers && typeof modelsConfig.providers === "object" ? modelsConfig.providers : {};
161
+ const currentProvider = providers[piProviderId] && typeof providers[piProviderId] === "object" ? providers[piProviderId] : {};
162
+ const headers = currentProvider.headers && typeof currentProvider.headers === "object"
163
+ ? { ...currentProvider.headers }
164
+ : {};
165
+ for (const key of Object.keys(headers)) {
166
+ if (key.toLowerCase() === piInsecureTlsHeader) {
167
+ delete headers[key];
168
+ }
169
+ }
170
+ if (insecureTls) {
171
+ headers[piInsecureTlsHeader] = new URL(baseUrl).origin;
172
+ }
173
+ const existingModels = new Map(
174
+ (Array.isArray(currentProvider.models) ? currentProvider.models : [])
175
+ .filter((model) => model?.id)
176
+ .map((model) => [model.id, model])
177
+ );
178
+
179
+ const provider = {
180
+ ...currentProvider,
181
+ baseUrl,
182
+ api,
183
+ compat: {
184
+ ...(currentProvider.compat && typeof currentProvider.compat === "object" ? currentProvider.compat : {}),
185
+ supportsDeveloperRole: false
186
+ },
187
+ models: modelIds.map((modelId) => buildPiModelConfig(modelId, existingModels.get(modelId)))
188
+ };
189
+ delete provider.apiKey;
190
+ if (Object.keys(headers).length) {
191
+ provider.headers = headers;
192
+ } else {
193
+ delete provider.headers;
194
+ }
195
+
196
+ return {
197
+ ...modelsConfig,
198
+ providers: {
199
+ ...providers,
200
+ [piProviderId]: provider
201
+ }
202
+ };
203
+ }
204
+
205
+ function buildPiAuthConfig(auth = {}, apiKey) {
206
+ const credential = auth[piProviderId] && typeof auth[piProviderId] === "object" ? auth[piProviderId] : {};
207
+ return {
208
+ ...auth,
209
+ [piProviderId]: {
210
+ ...credential,
211
+ type: "api_key",
212
+ key: apiKey
213
+ }
214
+ };
215
+ }
216
+
217
+ function buildPiSettingsConfig(settings = {}, defaultModel) {
218
+ return {
219
+ ...settings,
220
+ defaultProvider: piProviderId,
221
+ defaultModel,
222
+ defaultThinkingLevel: settings.defaultThinkingLevel || "high",
223
+ enableSkillCommands: settings.enableSkillCommands ?? true
224
+ };
225
+ }
226
+
227
+ async function readExistingPiConfiguration(paths, operations = {}) {
228
+ const readAuth = operations.readAuth ?? ((path) => readJsonFileSafe(path, {}, { sensitive: true }));
229
+ const readModels = operations.readModels ?? ((path) => readJsoncFileSafe(path, {}, { sensitive: true }));
230
+ const readSettings = operations.readSettings ?? ((path) => readJsonFileSafe(path, {}));
231
+ const auth = await readAuth(paths.piAuthPath);
232
+ const models = await readModels(paths.piModelsPath);
233
+ const settings = await readSettings(paths.piSettingsPath);
234
+ return { auth, models, settings };
235
+ }
236
+
237
+ function buildPiConfiguration({ auth, models, settings }, {
238
+ apiKey,
239
+ baseUrl,
240
+ api,
241
+ modelIds,
242
+ insecureTls,
243
+ defaultModel
244
+ }) {
245
+ return {
246
+ apiKey,
247
+ auth: buildPiAuthConfig(auth, apiKey),
248
+ models: buildPiModelsConfig(models, { baseUrl, api, modelIds, insecureTls }),
249
+ settings: buildPiSettingsConfig(settings, defaultModel)
250
+ };
251
+ }
252
+
253
+ async function persistPiConfiguration(paths, configuration, operations = {}) {
254
+ const updateAuth = operations.updateAuth ?? updatePiAuthFile;
255
+ const writeModels = operations.writeModels ?? ((path, value) => writeText(
256
+ path,
257
+ `${JSON.stringify(value, null, 2)}\n`,
258
+ { sensitive: true }
259
+ ));
260
+ const writeSettings = operations.writeSettings ?? ((path, value) => writeJsonFileWithBackup(path, value));
261
+ await updateAuth(paths.piAuthPath, configuration.apiKey);
262
+ await writeModels(paths.piModelsPath, configuration.models);
263
+ await writeSettings(paths.piSettingsPath, configuration.settings);
264
+ }
265
+
266
+ async function configurePiApi(paths = defaultPaths, ensurePiResourcesLinked = async () => {}, promptApi, runtime = {}) {
267
+ const {
268
+ assertNotCancelled,
269
+ confirmOrCancel,
270
+ required,
271
+ requiredUnlessExisting,
272
+ resolvePasswordValue,
273
+ selectOrCancel
274
+ } = promptApi;
275
+ assertSupportedPiVersion(await (runtime.getPiVersion ?? detectPiVersion)());
276
+ const {
277
+ auth,
278
+ models: modelsConfig,
279
+ settings
280
+ } = await readExistingPiConfiguration(paths);
281
+ const existing = resolveExistingPiApiConfig(modelsConfig, settings, auth);
282
+
283
+ const baseUrlInput = await p.text({
284
+ message: "Pi gpt Base URL",
285
+ defaultValue: existing.baseUrl,
286
+ validate: required()
287
+ });
288
+ assertNotCancelled(baseUrlInput);
289
+ const inferredApi = inferPiApiFromBaseUrl(baseUrlInput);
290
+ const baseUrl = normalizeOpenAiBaseUrl(baseUrlInput);
291
+
292
+ const insecureTls = await confirmOrCancel({
293
+ message: "是否仅为 Pi gpt 中转请求跳过 TLS 证书校验?仅证书无法修复时启用",
294
+ initialValue: hasPiInsecureTlsSetting(modelsConfig)
295
+ });
296
+
297
+ const piApiOptions = getPiApiPromptOptions();
298
+ const api = inferredApi || await selectOrCancel({
299
+ message: "Pi gpt API 类型",
300
+ options: piApiOptions,
301
+ initialValue: piApiOptions.some((option) => option.value === existing.api) ? existing.api : piDefaultApi
302
+ });
303
+
304
+ const apiKey = await p.password({
305
+ message: "Pi gpt API Key(输入 - 清除)",
306
+ mask: "*",
307
+ defaultValue: existing.apiKey || undefined,
308
+ validate: requiredUnlessExisting(existing.apiKey, "API Key 不能为空")
309
+ });
310
+ assertNotCancelled(apiKey);
311
+ const finalApiKey = resolvePasswordValue(apiKey, existing.apiKey);
312
+
313
+ const modelIdsText = await p.text({
314
+ message: "Pi gpt 模型 ID(多个用逗号分隔)",
315
+ defaultValue: (existing.modelIds.length ? existing.modelIds : [existing.defaultModel]).join(","),
316
+ validate: (value) => parsePiModelIds(value).length ? undefined : "至少需要一个模型 ID"
317
+ });
318
+ assertNotCancelled(modelIdsText);
319
+ const modelIds = parsePiModelIds(modelIdsText);
320
+
321
+ const defaultModel = await p.text({
322
+ message: "Pi 默认模型",
323
+ defaultValue: modelIds.includes(existing.defaultModel) ? existing.defaultModel : modelIds[0],
324
+ validate: (value) => modelIds.includes(String(value || "").trim()) ? undefined : "默认模型必须在模型 ID 列表中"
325
+ });
326
+ assertNotCancelled(defaultModel);
327
+
328
+ const finalDefaultModel = String(defaultModel).trim();
329
+ const configuration = buildPiConfiguration({ auth, models: modelsConfig, settings }, {
330
+ apiKey: finalApiKey,
331
+ baseUrl,
332
+ api,
333
+ modelIds,
334
+ insecureTls,
335
+ defaultModel: finalDefaultModel
336
+ });
337
+ await ensurePiResourcesLinked(paths);
338
+ await persistPiConfiguration(paths, configuration);
339
+
340
+ p.log.step(`已更新 ${pathToLabel(paths.piModelsPath, paths.homeDir)} (${piProviderId}, ${baseUrl})`);
341
+ p.log.step(`已更新 ${pathToLabel(paths.piSettingsPath, paths.homeDir)} (默认模型: ${finalDefaultModel})`);
342
+ p.log.step(`已更新 ${pathToLabel(paths.piAuthPath, paths.homeDir)} (${maskSecret(finalApiKey)})`);
343
+ p.log.step(`已链接 Pi 扩展到 ${pathToLabel(join(paths.piAgentDir, "extensions"), paths.homeDir)}`);
344
+ }
345
+
346
+ export {
347
+ assertSupportedPiVersion,
348
+ buildPiAuthConfig,
349
+ buildPiConfiguration,
350
+ buildPiModelsConfig,
351
+ buildPiSettingsConfig,
352
+ configurePiApi,
353
+ getPiApiPromptOptions,
354
+ hasPiInsecureTlsSetting,
355
+ inferPiApiFromBaseUrl,
356
+ normalizeOpenAiBaseUrl,
357
+ parsePiModelIds,
358
+ persistPiConfiguration,
359
+ readExistingPiConfiguration,
360
+ resolveExistingPiApiConfig,
361
+ updatePiAuthFile
362
+ };
@@ -0,0 +1,164 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import * as p from "@clack/prompts";
4
+ import { readDotenvFile, updateDotenvFile } from "../config/store.mjs";
5
+ import { pathToLabel } from "../paths.mjs";
6
+
7
+ const grokDefaults = Object.freeze(JSON.parse(readFileSync(
8
+ new URL("../../skills/grok-search/defaults.json", import.meta.url),
9
+ "utf8"
10
+ )));
11
+
12
+ function readSkillEnvFile(path) {
13
+ return readDotenvFile(path, { sensitive: true });
14
+ }
15
+
16
+ async function updateSkillEnvFile(path, updates) {
17
+ return updateDotenvFile(path, updates, { sensitive: true });
18
+ }
19
+
20
+ async function configureGrokSearchEnv(paths, ensureWorkflowPresent = async () => {}, promptApi) {
21
+ const { assertNotCancelled, confirmOrCancel, required, requiredUnlessExisting, resolvePasswordValue } = promptApi;
22
+ await ensureWorkflowPresent(paths);
23
+ const envPath = join(paths.agentsDir, "skills", "grok-search", ".env");
24
+ const existing = await readSkillEnvFile(envPath);
25
+ const baseUrl = await p.text({
26
+ message: "Grok API URL",
27
+ defaultValue: existing.GROK_API_URL || "https://api.x.ai/v1",
28
+ validate: required()
29
+ });
30
+ assertNotCancelled(baseUrl);
31
+
32
+ const apiKey = await p.password({
33
+ message: "Grok API Key(输入 - 清除)",
34
+ mask: "*",
35
+ defaultValue: existing.GROK_API_KEY || undefined,
36
+ validate: requiredUnlessExisting(existing.GROK_API_KEY, "Grok API Key 不能为空")
37
+ });
38
+ assertNotCancelled(apiKey);
39
+ const finalApiKey = resolvePasswordValue(apiKey, existing.GROK_API_KEY);
40
+
41
+ const model = await p.text({
42
+ message: "Grok 默认模型",
43
+ defaultValue: existing.GROK_MODEL || grokDefaults.model,
44
+ validate: required()
45
+ });
46
+ assertNotCancelled(model);
47
+
48
+ const useTavily = await confirmOrCancel({
49
+ message: "是否同时配置 Tavily 作为额外搜索源?",
50
+ initialValue: Boolean(existing.TAVILY_API_KEY)
51
+ });
52
+
53
+ const tavilyKey = useTavily
54
+ ? await p.password({
55
+ message: "Tavily API Key(输入 - 清除)",
56
+ mask: "*",
57
+ defaultValue: existing.TAVILY_API_KEY || undefined,
58
+ validate: requiredUnlessExisting(existing.TAVILY_API_KEY, "Tavily API Key 不能为空")
59
+ })
60
+ : "";
61
+ if (useTavily) assertNotCancelled(tavilyKey);
62
+ const finalTavilyKey = useTavily ? resolvePasswordValue(tavilyKey, existing.TAVILY_API_KEY) : null;
63
+
64
+ await updateSkillEnvFile(envPath, {
65
+ GROK_API_URL: baseUrl,
66
+ GROK_API_KEY: finalApiKey,
67
+ GROK_MODEL: model,
68
+ TAVILY_API_KEY: finalTavilyKey,
69
+ TAVILY_ENABLED: useTavily ? "true" : null
70
+ });
71
+
72
+ p.log.step(`已写入 ${pathToLabel(envPath)}`);
73
+ }
74
+
75
+ async function configureContext7Env(paths, ensureWorkflowPresent = async () => {}, promptApi) {
76
+ const { assertNotCancelled, resolvePasswordValue } = promptApi;
77
+ await ensureWorkflowPresent(paths);
78
+ const envPath = join(paths.agentsDir, "skills", "context7-auto-research", ".env");
79
+ const existing = await readSkillEnvFile(envPath);
80
+ const apiKey = await p.password({
81
+ message: "Context7 API Key (可选,输入 - 清除)",
82
+ mask: "*",
83
+ defaultValue: existing.CONTEXT7_API_KEY || undefined
84
+ });
85
+ assertNotCancelled(apiKey);
86
+ const finalApiKey = resolvePasswordValue(apiKey, existing.CONTEXT7_API_KEY);
87
+
88
+ await updateSkillEnvFile(envPath, {
89
+ CONTEXT7_API_KEY: finalApiKey
90
+ });
91
+
92
+ p.log.step(`已写入 ${pathToLabel(envPath)}`);
93
+ }
94
+
95
+ function hasPromptEnhancerApiConfig(config) {
96
+ return [config.PE_API_URL, config.PE_API_KEY, config.PE_MODEL]
97
+ .every((value) => typeof value === "string" && value.trim() !== "");
98
+ }
99
+
100
+ function resolvePromptEnhancerMode(existing) {
101
+ return hasPromptEnhancerApiConfig(existing) ? "openai-compatible" : "agent";
102
+ }
103
+
104
+ function buildPromptEnhancerEnvUpdates(apiUrl = null, apiKey = null, model = null) {
105
+ return { PE_API_URL: apiUrl, PE_API_KEY: apiKey, PE_MODEL: model };
106
+ }
107
+
108
+ async function configurePromptEnhancerEnv(paths, ensureWorkflowPresent = async () => {}, promptApi) {
109
+ const { assertNotCancelled, required, requiredUnlessExisting, resolvePasswordValue, selectOrCancel } = promptApi;
110
+ await ensureWorkflowPresent(paths);
111
+ const envPath = join(paths.agentsDir, "skills", "prompt-enhancer", ".env");
112
+ const existing = await readSkillEnvFile(envPath);
113
+ const mode = await selectOrCancel({
114
+ message: "请选择 prompt-enhancer 的运行方式",
115
+ options: [
116
+ { value: "openai-compatible", label: "第三方 OpenAI 兼容接口" },
117
+ { value: "agent", label: "直接使用当前 Agent" }
118
+ ],
119
+ initialValue: resolvePromptEnhancerMode(existing)
120
+ });
121
+
122
+ if (mode === "openai-compatible") {
123
+ const apiUrl = await p.text({
124
+ message: "PE_API_URL",
125
+ defaultValue: existing.PE_API_URL || undefined,
126
+ validate: required()
127
+ });
128
+ assertNotCancelled(apiUrl);
129
+
130
+ const apiKey = await p.password({
131
+ message: "PE_API_KEY(输入 - 清除)",
132
+ mask: "*",
133
+ defaultValue: existing.PE_API_KEY || undefined,
134
+ validate: requiredUnlessExisting(existing.PE_API_KEY, "PE_API_KEY 不能为空")
135
+ });
136
+ assertNotCancelled(apiKey);
137
+ const finalApiKey = resolvePasswordValue(apiKey, existing.PE_API_KEY);
138
+
139
+ const model = await p.text({
140
+ message: "PE_MODEL",
141
+ defaultValue: existing.PE_MODEL || undefined,
142
+ validate: required()
143
+ });
144
+ assertNotCancelled(model);
145
+
146
+ await updateSkillEnvFile(envPath, buildPromptEnhancerEnvUpdates(apiUrl, finalApiKey, model));
147
+ } else {
148
+ await updateSkillEnvFile(envPath, buildPromptEnhancerEnvUpdates());
149
+ }
150
+
151
+ p.log.step(`已写入 ${pathToLabel(envPath)}`);
152
+ }
153
+
154
+ export {
155
+ buildPromptEnhancerEnvUpdates,
156
+ configureContext7Env,
157
+ configureGrokSearchEnv,
158
+ configurePromptEnhancerEnv,
159
+ grokDefaults,
160
+ hasPromptEnhancerApiConfig,
161
+ readSkillEnvFile,
162
+ resolvePromptEnhancerMode,
163
+ updateSkillEnvFile
164
+ };
@@ -21,6 +21,13 @@ You are the general-purpose fallback sub-agent. Be useful quickly, stay within
21
21
  scope, and avoid impersonating a specialist role when the task clearly belongs
22
22
  to one.
23
23
 
24
+ ## Development Context
25
+
26
+ Development context: work is currently in the development phase, and this is a development repository.
27
+ Do not retain runtime user state in the repository or shipped artifacts, and do not preserve compatibility layers for unreleased behavior.
28
+ Prefer deleting obsolete code paths; keep code and prompts concise and avoid over-engineering.
29
+ This context does not by itself authorize destructive changes to user files or credentials outside the repository.
30
+
24
31
  ## Priorities (in order)
25
32
 
26
33
  1. Role fit — identify whether the request truly belongs to default
@@ -20,6 +20,13 @@ developer_instructions = """
20
20
  Your only job is to map. You produce a structured evidence report that the
21
21
  orchestrator or a worker agent uses to plan safe changes.
22
22
 
23
+ ## Development Context
24
+
25
+ Development context: work is currently in the development phase, and this is a development repository.
26
+ Do not retain runtime user state in the repository or shipped artifacts, and do not preserve compatibility layers for unreleased behavior.
27
+ Prefer deleting obsolete code paths; keep code and prompts concise and avoid over-engineering.
28
+ This context does not by itself authorize destructive changes to user files or credentials outside the repository.
29
+
23
30
  ## Rules
24
31
 
25
32
  - Do not edit any file, ever.
@@ -25,6 +25,13 @@ safely, not like someone writing a design essay.
25
25
  Your job is to turn an implementation request into an execution-ready plan
26
26
  that minimizes ambiguity, unnecessary searching, and accidental scope growth.
27
27
 
28
+ ## Development Context
29
+
30
+ Development context: work is currently in the development phase, and this is a development repository.
31
+ Do not retain runtime user state in the repository or shipped artifacts, and do not preserve compatibility layers for unreleased behavior.
32
+ Prefer deleting obsolete code paths; keep code and prompts concise and avoid over-engineering.
33
+ This context does not by itself authorize destructive changes to user files or credentials outside the repository.
34
+
28
35
  ## Priorities (in order)
29
36
 
30
37
  1. Scope clarity — what is actually being changed and what is not
@@ -20,6 +20,13 @@ developer_instructions = """
20
20
  Review code like an owner who will be on-call for what ships.
21
21
  Your job is to find real problems, not to demonstrate thoroughness.
22
22
 
23
+ ## Development Context
24
+
25
+ Development context: work is currently in the development phase, and this is a development repository.
26
+ Do not retain runtime user state in the repository or shipped artifacts, and do not preserve compatibility layers for unreleased behavior.
27
+ Prefer deleting obsolete code paths; keep code and prompts concise and avoid over-engineering.
28
+ This context does not by itself authorize destructive changes to user files or credentials outside the repository.
29
+
23
30
 
24
31
  ## Priorities (in order)
25
32
 
@@ -18,6 +18,13 @@ developer_instructions = """
18
18
  You are a local implementation sub-agent. Execute bounded tasks with
19
19
  precision and minimal footprint. You are not alone in the codebase.
20
20
 
21
+ ## Development Context
22
+
23
+ Development context: work is currently in the development phase, and this is a development repository.
24
+ Do not retain runtime user state in the repository or shipped artifacts, and do not preserve compatibility layers for unreleased behavior.
25
+ Prefer deleting obsolete code paths; keep code and prompts concise and avoid over-engineering.
26
+ This context does not by itself authorize destructive changes to user files or credentials outside the repository.
27
+
21
28
  ## Boundaries
22
29
 
23
30
  - Work only within the file set and module boundary authorized by the
@@ -12,10 +12,17 @@ developer_instructions = """
12
12
  Act as the default orchestrator for specialized subagents.
13
13
 
14
14
  This is a fallback orchestration policy, not the primary workflow definition.
15
- If an active custom command or workflow prompt is present (for example `/oc:*`),
15
+ If an active custom command or workflow prompt is present (for example `/abel-*`),
16
16
  its phase rules, guardrails, write scope, tool requirements, and stop/go gates
17
17
  take precedence over the default routing below.
18
18
 
19
+ ## Development Context
20
+
21
+ Development context: work is currently in the development phase, and this is a development repository.
22
+ Do not retain runtime user state in the repository or shipped artifacts, and do not preserve compatibility layers for unreleased behavior.
23
+ Prefer deleting obsolete code paths; keep code and prompts concise and avoid over-engineering.
24
+ This context does not by itself authorize destructive changes to user files or credentials outside the repository.
25
+
19
26
  ## Global Precedence
20
27
 
21
28
  - Active workflow or command instructions override default orchestration
@@ -42,8 +49,8 @@ take precedence over the default routing below.
42
49
  - During research or discovery phases, do not dispatch implementation workers.
43
50
  - During planning phases, do not generate or apply code changes.
44
51
  - During implementation or diagnose phases, respect command-specific
45
- requirements such as TDD, confidence gates, verification order, and patch or
46
- report format.
52
+ requirements such as TDD, root-cause verification, verification order, and
53
+ patch or report format.
47
54
  - Respect command-specific write boundaries and required tools.
48
55
  - If the active workflow requires explicit user confirmation before advancing
49
56
  to the next phase, do not advance automatically.
@@ -55,6 +62,7 @@ take precedence over the default routing below.
55
62
  - Prefer parallel subagents only when they do not violate workflow ordering or
56
63
  gates.
57
64
  - Assign concrete ownership with explicit scope and disjoint write sets.
65
+ - Copy this development context into every subagent task.
58
66
  - Do not duplicate work across agents. Avoid overlapping exploration,
59
67
  planning, implementation, or review tasks.
60
68
  - Do not hand off blocked or underspecified work without enough context for
@@ -11,6 +11,13 @@
11
11
  - Minimal comments/docs (only when necessary)
12
12
  - Targeted changes only; never touch unrelated functionality
13
13
 
14
+ ## Development Context
15
+
16
+ Development context: work is currently in the development phase, and this is a development repository.
17
+ Do not retain runtime user state in the repository or shipped artifacts, and do not preserve compatibility layers for unreleased behavior.
18
+ Prefer deleting obsolete code paths; keep code and prompts concise and avoid over-engineering.
19
+ This context does not by itself authorize destructive changes to user files or credentials outside the repository.
20
+
14
21
  ## Context Rules
15
22
 
16
23
  - {{CODEBASE_RETRIEVAL_POLICY}}
@@ -27,17 +34,15 @@
27
34
  ## Universal Constraints
28
35
 
29
36
  1. Use `unified diff patch` format for proposed/applied changes
30
- 2. Require confidence ≥90% before applying changes
37
+ 2. Before applying changes, state assumptions and unknowns explicitly; stop and ask the user on any critical unknown
31
38
 
32
39
  ## Stage Skill Matrix
33
40
 
34
41
  | Skill | Research | Plan | Implement | Diagnose | Capability & Triggers |
35
42
  | --- | :---: | :---: | :---: | :---: | --- |
36
- | /sequential-think | ✅ | ✅ | ❌ | ✅ | Multi-step reasoning, hypothesis testing. Trigger: 3+ interconnected components |
37
43
  | /grok-search | ✅ | ○ | ❌ | ✅ | Deep research, concept understanding. Trigger: architectural patterns, best practices |
38
44
  | /context7-auto-research | ✅ | ✅ | ✅ | ✅ | Official docs retrieval. Trigger: framework/library usage, APIs |
39
45
  | /dev-browser | ○ | ○ | ✅ | ✅ | Browser automation. Trigger: E2E testing, UI verification |
40
- | /confidence-check | ❌ | ✅ | ✅ | ✅ | Readiness verification. Trigger: before implementation/fix (≥90%) |
41
46
  | /time | ○ | ✅ | ✅ | ○ | Time/timezone operations. Trigger: scheduling logic |
42
47
 
43
48
  Legend: ✅ Primary, ○ Optional, ❌ Forbidden