abelworkflow 1.2.3 → 1.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 +26 -25
- package/extensions/gpt-responses-compat.ts +166 -27
- package/lib/cli/main.mjs +18 -86
- package/lib/cli/pi.mjs +43 -0
- package/lib/cli/prompts.mjs +3 -3
- package/lib/installer/assets.mjs +35 -31
- package/lib/installer/install.mjs +29 -9
- package/lib/installer/links.mjs +97 -84
- package/lib/paths.mjs +0 -2
- package/lib/providers/claude.mjs +14 -3
- package/lib/providers/codex.mjs +14 -9
- package/lib/providers/pi.mjs +61 -68
- package/lib/providers/skills.mjs +22 -22
- package/lib/providers/url.mjs +32 -1
- package/lib/templates/codex/agents/default.toml +16 -54
- package/lib/templates/codex/agents/explorer.toml +25 -52
- package/lib/templates/codex/agents/planner.toml +23 -77
- package/lib/templates/codex/agents/reviewer.toml +16 -62
- package/lib/templates/codex/agents/worker.toml +30 -64
- package/lib/templates/codex/config-base.toml +0 -6
- package/lib/templates/{workflow/gitignore.template → gitignore.template} +0 -3
- package/lib/tools/cli-installer.mjs +40 -12
- package/package.json +13 -18
- package/lib/templates/workflow/AGENTS.md +0 -50
- package/lib/templates/workflow/commands/abel-design.md +0 -175
- package/lib/templates/workflow/commands/abel-diagnose.md +0 -63
- package/lib/templates/workflow/commands/abel-implement.md +0 -170
- package/lib/templates/workflow/commands/abel-init.md +0 -25
- package/skills/dev-browser/SKILL.md +0 -281
- package/skills/dev-browser/dist/scripts/start.d.ts +0 -1
- package/skills/dev-browser/dist/scripts/start.js +0 -90
- package/skills/dev-browser/dist/src/client.d.ts +0 -92
- package/skills/dev-browser/dist/src/client.js +0 -310
- package/skills/dev-browser/dist/src/entrypoint.d.ts +0 -29
- package/skills/dev-browser/dist/src/entrypoint.js +0 -113
- package/skills/dev-browser/dist/src/index.d.ts +0 -3
- package/skills/dev-browser/dist/src/index.js +0 -1
- package/skills/dev-browser/dist/src/page-api.d.ts +0 -24
- package/skills/dev-browser/dist/src/page-api.js +0 -103
- package/skills/dev-browser/dist/src/relay.d.ts +0 -26
- package/skills/dev-browser/dist/src/relay.js +0 -567
- package/skills/dev-browser/dist/src/runtime.d.ts +0 -34
- package/skills/dev-browser/dist/src/runtime.js +0 -44
- package/skills/dev-browser/dist/src/snapshot/browser-script.d.ts +0 -22
- package/skills/dev-browser/dist/src/snapshot/browser-script.js +0 -868
- package/skills/dev-browser/dist/src/snapshot/index.d.ts +0 -13
- package/skills/dev-browser/dist/src/snapshot/index.js +0 -13
- package/skills/dev-browser/dist/src/snapshot/inject.d.ts +0 -12
- package/skills/dev-browser/dist/src/snapshot/inject.js +0 -12
- package/skills/dev-browser/dist/src/standalone.d.ts +0 -31
- package/skills/dev-browser/dist/src/standalone.js +0 -173
- package/skills/dev-browser/dist/src/startup.d.ts +0 -46
- package/skills/dev-browser/dist/src/startup.js +0 -77
- package/skills/dev-browser/dist/src/target-registry.d.ts +0 -28
- package/skills/dev-browser/dist/src/target-registry.js +0 -134
- package/skills/dev-browser/dist/src/types.d.ts +0 -26
- package/skills/dev-browser/dist/src/types.js +0 -1
- package/skills/dev-browser/package-lock.json +0 -1545
- package/skills/dev-browser/package.json +0 -35
- package/skills/dev-browser/references/scraping.md +0 -144
- package/skills/git-commit/SKILL.md +0 -124
- package/skills/time/SKILL.md +0 -119
- package/skills/time/scripts/time_cli.py +0 -143
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AbelWorkflow
|
|
2
2
|
|
|
3
|
-
AbelWorkflow 为
|
|
3
|
+
AbelWorkflow 为 Claude Code、Codex 和 Pi 配置 CLI 与 API,并为 Pi 部署独立 Skills 与 Extensions。Abel 工作流由 cadence 接管,不再安装到 Codex 或 Claude Code。
|
|
4
4
|
|
|
5
5
|
## 环境要求
|
|
6
6
|
|
|
@@ -8,21 +8,17 @@ AbelWorkflow 为 Codex、Claude Code 和 Pi 部署统一的 Skills、工作流
|
|
|
8
8
|
- npm 10 或兼容版本
|
|
9
9
|
- Linux、macOS 或原生 Windows
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Cadence 工作流
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
| --- | --- |
|
|
15
|
-
| `/abel-init` | 初始化 OpenSpec 环境并验证工具链 |
|
|
16
|
-
| `/abel-design` | 将需求整理为可实施、可验证的 OpenSpec 规格 |
|
|
17
|
-
| `/abel-implement` | 按 Red → Green → Refactor 实施变更 |
|
|
18
|
-
| `/abel-diagnose` | 基于证据定位根因并完成回归验证 |
|
|
13
|
+
`abel-*` 工作流已迁移到 Pi 扩展 cadence,本安装器不再安装这些命令或 skills,也不再为 Codex / Claude Code 建立工作流链接。安装/更新 Pi CLI 成功后会自动执行,也可在菜单选择“安装/更新 cadence”单独安装或重试:
|
|
19
14
|
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
↘ /abel-diagnose (bug fix)
|
|
15
|
+
```bash
|
|
16
|
+
pi install npm:@abelxiaoxing/cadence
|
|
23
17
|
```
|
|
24
18
|
|
|
25
|
-
|
|
19
|
+
cadence 自带 grok-search、context7 等配置;本安装器不再向 Pi 链接这两个独立 skills。Claude/Codex 的 CLI 安装与 API 配置继续保留,但不安装 `abel-*` 工作流。自动安装 cadence 失败时会提示重试方式,其余配置仍可继续。
|
|
20
|
+
|
|
21
|
+
配置 Pi API 只部署仓库自带扩展并保存本地配置,不调用 cadence 安装,不要求 `pi` 命令已安装;单独安装 cadence 时需要 `pi` 命令可用。
|
|
26
22
|
|
|
27
23
|
## 安装与更新
|
|
28
24
|
|
|
@@ -32,18 +28,18 @@ npx abelworkflow install
|
|
|
32
28
|
npx abelworkflow@latest
|
|
33
29
|
```
|
|
34
30
|
|
|
35
|
-
无 TTY 环境必须显式运行 `npx abelworkflow install
|
|
31
|
+
无 TTY 环境必须显式运行 `npx abelworkflow install`。该命令只刷新已选择的 skills、extensions 和 Pi 链接,不会安装尚未配置的 skill。已有目标与托管内容冲突时,安装会失败并保留原内容;确认目标后再处理冲突。
|
|
36
32
|
|
|
37
33
|
### 源码安装
|
|
38
34
|
|
|
35
|
+
源码开发与验证需要 Node.js 22.19 或更高版本,以满足锁定的 Pi SDK 要求。
|
|
36
|
+
|
|
39
37
|
源码目录与部署目录必须分离,不能相同或互相嵌套。不要把源码仓库直接克隆到 `~/.agents`。
|
|
40
38
|
|
|
41
39
|
```bash
|
|
42
40
|
git clone https://github.com/abelxiaoxing/AbelWorkflow ~/src/AbelWorkflow
|
|
43
41
|
cd ~/src/AbelWorkflow
|
|
44
42
|
npm ci
|
|
45
|
-
npm ci --prefix skills/dev-browser
|
|
46
|
-
npm run build --prefix skills/dev-browser
|
|
47
43
|
node bin/abelworkflow.mjs install --agents-dir ~/.agents
|
|
48
44
|
```
|
|
49
45
|
|
|
@@ -55,25 +51,26 @@ node .\bin\abelworkflow.mjs install --agents-dir "$HOME\.agents"
|
|
|
55
51
|
|
|
56
52
|
### 可选 Skill 依赖
|
|
57
53
|
|
|
58
|
-
|
|
54
|
+
Grok 依赖安装到用户自行选择的 Python 环境。普通运行不会自动安装依赖:
|
|
59
55
|
|
|
60
56
|
```bash
|
|
61
|
-
npm ci --omit=dev --prefix ~/.agents/skills/dev-browser
|
|
62
|
-
node ~/.agents/skills/dev-browser/node_modules/playwright/cli.js install chromium
|
|
63
|
-
python -m pip install tzdata
|
|
64
57
|
python -m pip install -r skills/grok-search/requirements.txt
|
|
65
58
|
```
|
|
66
59
|
|
|
67
|
-
部署后的 Skill 目录在普通运行时按只读处理。显式 Skill 密钥配置会写入部署目录的 `.env
|
|
60
|
+
部署后的 Skill 目录在普通运行时按只读处理。显式 Skill 密钥配置会写入部署目录的 `.env`;临时文件、虚拟环境和其他运行状态应放在操作系统临时目录或用户数据目录。
|
|
61
|
+
|
|
62
|
+
菜单里配置某个 skill 时才会安装对应 skill。grok-search 与 context7 只写入 `~/.agents`,不链到 Pi。
|
|
68
63
|
|
|
69
64
|
## 配置行为
|
|
70
65
|
|
|
66
|
+
本安装器不再分发全局 `AGENTS.md` / `CLAUDE.md` 或工作流命令。旧安装中未修改的托管文件与链接按安装记录清理;用户自建或修改过的文件保留并按既有冲突规则处理。仓库根 `AGENTS.md` 仅约束本项目开发,不参与分发。
|
|
67
|
+
|
|
71
68
|
- Claude API 配置会合并个人默认模板(`$schema`、`attribution`、`hooks`、`alwaysThinkingEnabled`、`language` 与全局权限白名单),删除已弃用的 `includeCoAuthoredBy`;白名单使用当前任务工具 `TaskCreate`、`TaskGet`、`TaskUpdate`、`TaskList`,缺失项补入默认模板但保留已有权限数组。
|
|
72
69
|
- Claude API 使用 `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` 统一关闭非必要流量并保留 `API_TIMEOUT_MS`,清理重叠的 `DISABLE_TELEMETRY` 与 `DISABLE_ERROR_REPORTING`。
|
|
73
70
|
- Claude API 管理 `ANTHROPIC_BASE_URL`、`ANTHROPIC_API_KEY`,并将所选模型同步到 `ANTHROPIC_MODEL`、Opus/Sonnet/Haiku 默认模型与 `CLAUDE_CODE_SUBAGENT_MODEL`;冲突的 `ANTHROPIC_AUTH_TOKEN` 会被删除,其他用户配置保持不变。
|
|
74
|
-
- Codex 配置以 Codex 最新稳定版为基线,不保留旧版本兼容配置;配置会合并个人默认模板,使用 `approval_policy = "never"` 与 `sandbox_mode = "danger-full-access"` 赋予 Agent 完全权限,同时管理 AbelWorkflow Provider 路由、`OPENAI_API_KEY` 与 `auth_mode`,清理冲突的 `preferred_auth_method`、`temp_env_key` 及目标 Provider
|
|
71
|
+
- Codex 配置以 Codex 最新稳定版为基线,不保留旧版本兼容配置;配置会合并个人默认模板,使用 `approval_policy = "never"` 与 `sandbox_mode = "danger-full-access"` 赋予 Agent 完全权限,同时管理 AbelWorkflow Provider 路由、`OPENAI_API_KEY` 与 `auth_mode`,清理冲突的 `preferred_auth_method`、`temp_env_key` 及目标 Provider 旧路由字段。写入 API Key 时重建 `auth.json` 为纯 API Key 状态并删除旧登录认证;清空 API Key 时只移除 API Key 字段和模式标记,已删除的登录凭据不会自动恢复。
|
|
75
72
|
- Pi 配置更新 `abelworkflow` Provider、对应凭据及全局 `defaultProvider`、`defaultModel`,保留其他 Provider 与无关设置。
|
|
76
|
-
-
|
|
73
|
+
- Provider 在配置根对象、要求为对象的配置节或路径无法安全合并时直接失败。Claude 的 `permissions.allow`、`permissions.deny` 缺失或不是数组时,分别使用默认工具白名单和空拒绝列表;已有数组保持不变。
|
|
77
74
|
- Skill 密钥写入 `~/.agents/skills/<skill>/.env`,属于用户私密配置。
|
|
78
75
|
- `.skill-lock.json` 完全属于用户:安装器不读取、不修改、不备份、不删除,也不会将其放入发布包。
|
|
79
76
|
- 自定义 CA 应通过标准证书环境变量显式配置,不关闭 TLS 校验。
|
|
@@ -90,11 +87,15 @@ Codex 根据 Provider Base URL 推导 WebSocket 地址。例如,`https://relay
|
|
|
90
87
|
|
|
91
88
|
| 托管内容 | Claude Code | Codex | Pi |
|
|
92
89
|
| --- | --- | --- | --- |
|
|
93
|
-
| 工作流
|
|
94
|
-
|
|
|
95
|
-
|
|
|
90
|
+
| `abel-*` 工作流 | — | — | cadence |
|
|
91
|
+
| 独立 skills / 密钥配置 | 菜单中配置 CLI 与 API | 菜单中配置 CLI 与 API | 配置 skill 后写入 `~/.agents`;grok-search / context7 不链到 Pi |
|
|
92
|
+
| cadence | — | — | 通过 `pi install npm:@abelxiaoxing/cadence` 安装 |
|
|
96
93
|
| `extensions/*` | — | — | `~/.agents/extensions/` → `~/.pi/agent/extensions/` |
|
|
97
94
|
|
|
95
|
+
## 开发验证
|
|
96
|
+
|
|
97
|
+
`npm run check` 执行全部测试及类型检查。`npm run typecheck` 独立检查 `extensions/**/*.ts`,依据锁定的 Pi SDK `0.85.1` 验证接口;Node 测试还会使用真实 SDK 在临时目录加载扩展并检查事件注册,不访问个人配置或请求模型。SDK 和 TypeScript 仅为开发依赖。
|
|
98
|
+
|
|
98
99
|
## 自签名 HTTPS 中转
|
|
99
100
|
|
|
100
101
|
取得中转的 CA PEM 后,在启动客户端前配置:
|
|
@@ -8,6 +8,20 @@ const RESPONSES_PROVIDERS = new Set(["abelworkflow", "gpt"]);
|
|
|
8
8
|
const UNSUPPORTED_SPARK_REASONING_EFFORTS = new Set(["none", "minimal"]);
|
|
9
9
|
const TRANSIENT_UPSTREAM_ERROR = /\bupstream(?:[_ -]error|\s+request\s+failed)\b/i;
|
|
10
10
|
const EXPLICIT_ERROR_STATUS = /(?:^\s*|\b(?:upstream(?:[_ -]error|\s+request\s+failed)|code|error(?:[_ -]?code)?|http(?:\/\d+(?:\.\d+)*)?(?:[_ -]?status(?:[_ -]?code)?)?|status(?:[_ -]?code)?)\b\s*["']?\s*[:=(]?\s*)["']?([45]\d{2})["']?(?!\d)/i;
|
|
11
|
+
const SUMMARIZATION_SYSTEM_PROMPT = "You summarize coding conversations into concise continuation checkpoints. Output only the requested structured summary.";
|
|
12
|
+
const SUMMARIZATION_PROMPT = `Create a structured checkpoint using exactly these sections:
|
|
13
|
+
|
|
14
|
+
## Goal
|
|
15
|
+
## Constraints & Preferences
|
|
16
|
+
## Progress
|
|
17
|
+
### Done
|
|
18
|
+
### In Progress
|
|
19
|
+
### Blocked
|
|
20
|
+
## Key Decisions
|
|
21
|
+
## Next Steps
|
|
22
|
+
## Critical Context
|
|
23
|
+
|
|
24
|
+
Preserve exact file paths, function names, decisions, errors, and unfinished work. Be concise but include everything needed to continue.`;
|
|
11
25
|
|
|
12
26
|
function isRecord(value: unknown): value is JsonRecord {
|
|
13
27
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -98,9 +112,13 @@ function normalizeInputItem(item: unknown): unknown {
|
|
|
98
112
|
if (item.type === "reasoning") return undefined;
|
|
99
113
|
|
|
100
114
|
if (item.type === "message") {
|
|
101
|
-
const
|
|
115
|
+
const role = item.role === "user" ? "user" : item.role === "assistant" ? "assistant" : undefined;
|
|
116
|
+
if (!role) return undefined;
|
|
117
|
+
const content = role === "user"
|
|
118
|
+
? normalizeUserContent(item.content)
|
|
119
|
+
: normalizeAssistantContent(item.content);
|
|
102
120
|
if (Array.isArray(content) && content.length === 0) return undefined;
|
|
103
|
-
return { role
|
|
121
|
+
return { role, content };
|
|
104
122
|
}
|
|
105
123
|
|
|
106
124
|
if (item.role === "user") {
|
|
@@ -157,32 +175,153 @@ function normalizeReasoning(payload: JsonRecord, modelId: string | undefined): v
|
|
|
157
175
|
payload.reasoning = { ...payload.reasoning, effort: "low" };
|
|
158
176
|
}
|
|
159
177
|
|
|
178
|
+
function serializeConversation(messages: unknown[]): string {
|
|
179
|
+
return messages.map((message) => {
|
|
180
|
+
if (!isRecord(message)) return "";
|
|
181
|
+
const content = textFromContent(message.content) ?? "";
|
|
182
|
+
if (message.role === "user") return content ? `[User]: ${content}` : "";
|
|
183
|
+
if (message.role === "toolResult") {
|
|
184
|
+
const text = content.length > 2000 ? `${content.slice(0, 2000)}\n[truncated]` : content;
|
|
185
|
+
return text ? `[Tool result]: ${text}` : "";
|
|
186
|
+
}
|
|
187
|
+
if (message.role === "assistant" && Array.isArray(message.content)) {
|
|
188
|
+
const parts: string[] = [];
|
|
189
|
+
const thinking = message.content
|
|
190
|
+
.filter((part) => isRecord(part) && part.type === "thinking" && typeof part.thinking === "string")
|
|
191
|
+
.map((part) => part.thinking)
|
|
192
|
+
.join("\n");
|
|
193
|
+
if (thinking) parts.push(`[Assistant thinking]: ${thinking}`);
|
|
194
|
+
if (content) parts.push(`[Assistant]: ${content}`);
|
|
195
|
+
const calls = message.content
|
|
196
|
+
.filter((part) => isRecord(part) && part.type === "toolCall")
|
|
197
|
+
.map((part) => `${part.name}(${JSON.stringify(part.arguments ?? {})})`)
|
|
198
|
+
.join("; ");
|
|
199
|
+
if (calls) parts.push(`[Assistant tool calls]: ${calls}`);
|
|
200
|
+
return parts.join("\n");
|
|
201
|
+
}
|
|
202
|
+
if (message.role === "bashExecution") {
|
|
203
|
+
return `[Bash]: ${String(message.command ?? "")}\n${String(message.output ?? "")}`;
|
|
204
|
+
}
|
|
205
|
+
if (typeof message.summary === "string") return `[Previous checkpoint]: ${message.summary}`;
|
|
206
|
+
return content;
|
|
207
|
+
}).filter(Boolean).join("\n\n");
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function fileDetails(fileOps: unknown): {
|
|
211
|
+
readFiles: string[];
|
|
212
|
+
modifiedFiles: string[];
|
|
213
|
+
} {
|
|
214
|
+
if (!isRecord(fileOps)) return { readFiles: [], modifiedFiles: [] };
|
|
215
|
+
const modified = new Set<string>([
|
|
216
|
+
...(fileOps.edited instanceof Set ? fileOps.edited : []),
|
|
217
|
+
...(fileOps.written instanceof Set ? fileOps.written : []),
|
|
218
|
+
]);
|
|
219
|
+
return {
|
|
220
|
+
readFiles: [...(fileOps.read instanceof Set ? fileOps.read : [])]
|
|
221
|
+
.filter((path): path is string => typeof path === "string" && !modified.has(path))
|
|
222
|
+
.sort(),
|
|
223
|
+
modifiedFiles: [...modified].sort(),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function formatFileDetails(details: { readFiles: string[]; modifiedFiles: string[] }): string {
|
|
228
|
+
const sections: string[] = [];
|
|
229
|
+
if (details.readFiles.length > 0) {
|
|
230
|
+
sections.push(`<read-files>\n${details.readFiles.join("\n")}\n</read-files>`);
|
|
231
|
+
}
|
|
232
|
+
if (details.modifiedFiles.length > 0) {
|
|
233
|
+
sections.push(`<modified-files>\n${details.modifiedFiles.join("\n")}\n</modified-files>`);
|
|
234
|
+
}
|
|
235
|
+
return sections.length > 0 ? `\n\n${sections.join("\n\n")}` : "";
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function normalizeResponsesPayload(
|
|
239
|
+
payload: unknown,
|
|
240
|
+
model: unknown,
|
|
241
|
+
systemPrompt: string,
|
|
242
|
+
): JsonRecord | undefined {
|
|
243
|
+
if (!isRecord(payload) || !isCompatibleResponsesModel(model)) return undefined;
|
|
244
|
+
|
|
245
|
+
const input = normalizeInput(payload.input);
|
|
246
|
+
const first = Array.isArray(payload.input) ? payload.input.find(isRecord) : undefined;
|
|
247
|
+
const firstIsPrompt = first?.role === "system" || first?.role === "developer";
|
|
248
|
+
const instructions =
|
|
249
|
+
textFromContent(payload.instructions) ||
|
|
250
|
+
(firstIsPrompt ? textFromContent(first.content) : undefined) ||
|
|
251
|
+
systemPrompt.trim() ||
|
|
252
|
+
DEFAULT_INSTRUCTIONS;
|
|
253
|
+
const nextPayload: JsonRecord = { ...payload, instructions, input, store: false };
|
|
254
|
+
|
|
255
|
+
delete nextPayload.max_output_tokens;
|
|
256
|
+
delete nextPayload.prompt_cache_key;
|
|
257
|
+
delete nextPayload.prompt_cache_retention;
|
|
258
|
+
delete nextPayload.previous_response_id;
|
|
259
|
+
|
|
260
|
+
normalizeReasoning(nextPayload, isRecord(model) && typeof model.id === "string" ? model.id : undefined);
|
|
261
|
+
return nextPayload;
|
|
262
|
+
}
|
|
263
|
+
|
|
160
264
|
export default function (pi: ExtensionAPI) {
|
|
161
|
-
pi.on("before_provider_request", (event, ctx) =>
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
265
|
+
pi.on("before_provider_request", (event, ctx) =>
|
|
266
|
+
normalizeResponsesPayload(event.payload, ctx.model, ctx.getSystemPrompt()));
|
|
267
|
+
|
|
268
|
+
pi.on("session_before_compact", async (event, ctx) => {
|
|
269
|
+
const model = ctx.model;
|
|
270
|
+
if (!model || !isCompatibleResponsesModel(model)) return;
|
|
271
|
+
|
|
272
|
+
const { preparation } = event;
|
|
273
|
+
const conversation = serializeConversation([
|
|
274
|
+
...preparation.messagesToSummarize,
|
|
275
|
+
...preparation.turnPrefixMessages,
|
|
276
|
+
]);
|
|
277
|
+
const prompt = [
|
|
278
|
+
`<conversation>\n${conversation}\n</conversation>`,
|
|
279
|
+
preparation.previousSummary
|
|
280
|
+
? `<previous-summary>\n${preparation.previousSummary}\n</previous-summary>\nPreserve and update this previous checkpoint.`
|
|
281
|
+
: "",
|
|
282
|
+
preparation.isSplitTurn
|
|
283
|
+
? "The conversation ends with the prefix of a split turn; its recent suffix remains in context."
|
|
284
|
+
: "",
|
|
285
|
+
SUMMARIZATION_PROMPT,
|
|
286
|
+
event.customInstructions ? `Additional focus: ${event.customInstructions}` : "",
|
|
287
|
+
].filter(Boolean).join("\n\n");
|
|
288
|
+
const response = await ctx.modelRegistry.complete(
|
|
289
|
+
model,
|
|
290
|
+
{
|
|
291
|
+
systemPrompt: SUMMARIZATION_SYSTEM_PROMPT,
|
|
292
|
+
messages: [{
|
|
293
|
+
role: "user",
|
|
294
|
+
content: [{ type: "text", text: prompt }],
|
|
295
|
+
timestamp: Date.now(),
|
|
296
|
+
}],
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
signal: event.signal,
|
|
300
|
+
cacheRetention: "none",
|
|
301
|
+
...(ctx.thinkingLevel && ctx.thinkingLevel !== "off"
|
|
302
|
+
? { reasoningEffort: ctx.thinkingLevel }
|
|
303
|
+
: {}),
|
|
304
|
+
onPayload: (payload: unknown, payloadModel: unknown) =>
|
|
305
|
+
normalizeResponsesPayload(payload, payloadModel, SUMMARIZATION_SYSTEM_PROMPT),
|
|
306
|
+
},
|
|
307
|
+
);
|
|
308
|
+
if (response.stopReason === "error") {
|
|
309
|
+
throw new Error(`Summarization failed: ${response.errorMessage ?? "Unknown error"}`);
|
|
310
|
+
}
|
|
311
|
+
if (response.stopReason === "aborted") throw new Error("Compaction cancelled");
|
|
312
|
+
|
|
313
|
+
const summaryText = textFromContent(response.content);
|
|
314
|
+
if (!summaryText) throw new Error("Summarization failed: Empty response");
|
|
315
|
+
const details = fileDetails(preparation.fileOps);
|
|
316
|
+
return {
|
|
317
|
+
compaction: {
|
|
318
|
+
summary: `${summaryText}${formatFileDetails(details)}`,
|
|
319
|
+
firstKeptEntryId: preparation.firstKeptEntryId,
|
|
320
|
+
tokensBefore: preparation.tokensBefore,
|
|
321
|
+
usage: response.usage,
|
|
322
|
+
details,
|
|
323
|
+
},
|
|
324
|
+
};
|
|
186
325
|
});
|
|
187
326
|
|
|
188
327
|
pi.on("message_end", (event, ctx) => {
|
package/lib/cli/main.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import * as p from "@clack/prompts";
|
|
|
4
4
|
import c from "picocolors";
|
|
5
5
|
import { assertInteractiveMenuSupported, parseArgs } from "./args.mjs";
|
|
6
6
|
import * as promptApi from "./prompts.mjs";
|
|
7
|
+
import { createPiActions } from "./pi.mjs";
|
|
7
8
|
import {
|
|
8
9
|
ensurePiExtensionsPresent,
|
|
9
10
|
ensureSkillPresent,
|
|
@@ -19,18 +20,16 @@ import {
|
|
|
19
20
|
} from "../installer/state.mjs";
|
|
20
21
|
import { configureClaudeApi } from "../providers/claude.mjs";
|
|
21
22
|
import { configureCodexApi } from "../providers/codex.mjs";
|
|
22
|
-
import { configurePiApi } from "../providers/pi.mjs";
|
|
23
23
|
import {
|
|
24
24
|
configureContext7Env,
|
|
25
25
|
configureGrokSearchEnv
|
|
26
26
|
} from "../providers/skills.mjs";
|
|
27
|
-
import {
|
|
27
|
+
import { installCliTool } from "../tools/cli-installer.mjs";
|
|
28
28
|
import { createPaths, defaultPaths, pathToLabel } from "../paths.mjs";
|
|
29
29
|
|
|
30
30
|
const {
|
|
31
31
|
buildCliToolMenuDescriptors,
|
|
32
32
|
CancelledError,
|
|
33
|
-
confirmOrCancel,
|
|
34
33
|
interactiveMenuDefaultValue,
|
|
35
34
|
interactiveMenuDescriptors
|
|
36
35
|
} = promptApi;
|
|
@@ -91,7 +90,7 @@ function presentInstallReport(report, reportOutput) {
|
|
|
91
90
|
|
|
92
91
|
function assertNoInstallConflicts(report) {
|
|
93
92
|
if (uniqueReportPaths(report.conflicts).length > 0) {
|
|
94
|
-
throw new Error("
|
|
93
|
+
throw new Error("托管资源安装存在冲突");
|
|
95
94
|
}
|
|
96
95
|
}
|
|
97
96
|
|
|
@@ -130,7 +129,7 @@ ${c.bold("Usage:")}
|
|
|
130
129
|
|
|
131
130
|
${c.bold("Default behavior:")}
|
|
132
131
|
- npx abelworkflow: open the interactive setup menu.
|
|
133
|
-
- npx abelworkflow install: sync
|
|
132
|
+
- npx abelworkflow install: sync already selected skills, extensions, and Pi links.
|
|
134
133
|
`);
|
|
135
134
|
}
|
|
136
135
|
|
|
@@ -164,71 +163,9 @@ async function configureClaudeForPaths(paths) {
|
|
|
164
163
|
await configureClaudeApi(paths, promptApi);
|
|
165
164
|
}
|
|
166
165
|
|
|
167
|
-
async function runFullInit(options, runtime = {}) {
|
|
168
|
-
const operations = {
|
|
169
|
-
installWorkflow: () => runManagedInstall({
|
|
170
|
-
paths: options.paths,
|
|
171
|
-
force: options.force
|
|
172
|
-
}),
|
|
173
|
-
confirm: confirmOrCancel,
|
|
174
|
-
commandExists,
|
|
175
|
-
installClaude: () => installCliTool("claude", promptApi),
|
|
176
|
-
configureClaude: () => configureClaudeForPaths(options.paths),
|
|
177
|
-
installCodex: () => installCliTool("codex", promptApi),
|
|
178
|
-
configureCodex: () => configureCodexForPaths(options.paths),
|
|
179
|
-
installPi: () => installCliTool("pi", promptApi),
|
|
180
|
-
configurePi: () => configurePiApi(
|
|
181
|
-
options.paths,
|
|
182
|
-
ensurePiExtensionsPresentWithReport,
|
|
183
|
-
promptApi
|
|
184
|
-
),
|
|
185
|
-
configureGrok: () => configureGrokSearchEnv(
|
|
186
|
-
options.paths,
|
|
187
|
-
(paths, continuation) => ensureSkillPresentWithReport(paths, "grok-search", continuation),
|
|
188
|
-
promptApi
|
|
189
|
-
),
|
|
190
|
-
configureContext7: () => configureContext7Env(
|
|
191
|
-
options.paths,
|
|
192
|
-
(paths, continuation) => ensureSkillPresentWithReport(paths, "context7-auto-research", continuation),
|
|
193
|
-
promptApi
|
|
194
|
-
),
|
|
195
|
-
success: (message) => p.log.success(message),
|
|
196
|
-
...runtime
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
await operations.installWorkflow();
|
|
200
|
-
|
|
201
|
-
if (await operations.confirm({ message: "是否安装或更新 Claude Code CLI?", initialValue: false })) {
|
|
202
|
-
await operations.installClaude();
|
|
203
|
-
}
|
|
204
|
-
if (await operations.confirm({ message: "是否配置 Claude Code 第三方 API?", initialValue: operations.commandExists("claude") })) {
|
|
205
|
-
await operations.configureClaude();
|
|
206
|
-
}
|
|
207
|
-
if (await operations.confirm({ message: "是否安装或更新 Codex CLI?", initialValue: false })) {
|
|
208
|
-
await operations.installCodex();
|
|
209
|
-
}
|
|
210
|
-
if (await operations.confirm({ message: "是否配置 Codex 第三方 API?", initialValue: operations.commandExists("codex") })) {
|
|
211
|
-
await operations.configureCodex();
|
|
212
|
-
}
|
|
213
|
-
if (await operations.confirm({ message: "是否安装或更新 Pi CLI?", initialValue: false })) {
|
|
214
|
-
await operations.installPi();
|
|
215
|
-
}
|
|
216
|
-
if (await operations.confirm({ message: "是否配置 Pi 当前有效 Provider API?", initialValue: operations.commandExists("pi") })) {
|
|
217
|
-
await operations.configurePi();
|
|
218
|
-
}
|
|
219
|
-
if (await operations.confirm({ message: "是否填写 grok-search 环境变量?", initialValue: false })) {
|
|
220
|
-
await operations.configureGrok();
|
|
221
|
-
}
|
|
222
|
-
if (await operations.confirm({ message: "是否填写 context7-auto-research 环境变量?", initialValue: false })) {
|
|
223
|
-
await operations.configureContext7();
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
operations.success(c.green("AbelWorkflow 完整初始化完成"));
|
|
227
|
-
}
|
|
228
|
-
|
|
229
166
|
async function runInteractiveMenu(options) {
|
|
230
167
|
p.intro(c.bold(c.bgCyan(c.black(" AbelWorkflow Setup "))));
|
|
231
|
-
p.log.message(
|
|
168
|
+
p.log.message(`托管目录: ${c.cyan(pathToLabel(options.agentsDir))}`);
|
|
232
169
|
|
|
233
170
|
const buildOption = (d) => {
|
|
234
171
|
const opt = { value: d.value, label: d.label };
|
|
@@ -237,15 +174,13 @@ async function runInteractiveMenu(options) {
|
|
|
237
174
|
}
|
|
238
175
|
return opt;
|
|
239
176
|
};
|
|
177
|
+
const pi = createPiActions(options.paths, ensurePiExtensionsPresentWithReport);
|
|
240
178
|
const cliToolMenus = {
|
|
241
179
|
pi: {
|
|
242
180
|
title: "Pi",
|
|
243
|
-
install:
|
|
244
|
-
configure:
|
|
245
|
-
|
|
246
|
-
ensurePiExtensionsPresentWithReport,
|
|
247
|
-
promptApi
|
|
248
|
-
)
|
|
181
|
+
install: pi.install,
|
|
182
|
+
configure: pi.configure,
|
|
183
|
+
cadence: pi.cadence
|
|
249
184
|
},
|
|
250
185
|
codex: {
|
|
251
186
|
title: "Codex",
|
|
@@ -262,7 +197,8 @@ async function runInteractiveMenu(options) {
|
|
|
262
197
|
const menu = cliToolMenus[tool];
|
|
263
198
|
const actions = {
|
|
264
199
|
[`${tool}-api`]: menu.configure,
|
|
265
|
-
[`${tool}-install`]: menu.install
|
|
200
|
+
[`${tool}-install`]: menu.install,
|
|
201
|
+
...(tool === "pi" ? { "pi-cadence": menu.cadence } : {})
|
|
266
202
|
};
|
|
267
203
|
while (true) {
|
|
268
204
|
const choice = await p.select({
|
|
@@ -280,15 +216,15 @@ async function runInteractiveMenu(options) {
|
|
|
280
216
|
p.log.warn(`未知 CLI 工具菜单选项: ${choice}`);
|
|
281
217
|
continue;
|
|
282
218
|
}
|
|
283
|
-
|
|
219
|
+
try {
|
|
220
|
+
await action();
|
|
221
|
+
} catch (error) {
|
|
222
|
+
if (choice !== "pi-cadence") throw error;
|
|
223
|
+
p.log.warn(`cadence 安装失败:${error.message || String(error)}。可再次选择此项重试。`);
|
|
224
|
+
}
|
|
284
225
|
}
|
|
285
226
|
};
|
|
286
227
|
const menuActions = {
|
|
287
|
-
"full-init": async () => runFullInit(options),
|
|
288
|
-
install: async () => runManagedInstall({
|
|
289
|
-
paths: options.paths,
|
|
290
|
-
force: options.force
|
|
291
|
-
}),
|
|
292
228
|
"grok-search": async () => configureGrokSearchEnv(options.paths, (paths, continuation) => ensureSkillPresentWithReport(paths, "grok-search", continuation), promptApi),
|
|
293
229
|
context7: async () => configureContext7Env(options.paths, (paths, continuation) => ensureSkillPresentWithReport(paths, "context7-auto-research", continuation), promptApi),
|
|
294
230
|
"pi-cli": async () => runCliToolMenu("pi"),
|
|
@@ -298,9 +234,6 @@ async function runInteractiveMenu(options) {
|
|
|
298
234
|
|
|
299
235
|
while (true) {
|
|
300
236
|
const selectOptions = [
|
|
301
|
-
...interactiveMenuDescriptors
|
|
302
|
-
.filter((d) => d.group === "main")
|
|
303
|
-
.map(buildOption),
|
|
304
237
|
{ value: "__sep_skills__", label: "─── 技能配置 ───", disabled: true },
|
|
305
238
|
...interactiveMenuDescriptors
|
|
306
239
|
.filter((d) => d.group === "skill")
|
|
@@ -404,6 +337,5 @@ export {
|
|
|
404
337
|
assertNoInstallConflicts,
|
|
405
338
|
configureProviderWithMetadata,
|
|
406
339
|
main,
|
|
407
|
-
presentInstallReport
|
|
408
|
-
runFullInit
|
|
340
|
+
presentInstallReport
|
|
409
341
|
};
|
package/lib/cli/pi.mjs
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import * as promptApi from "./prompts.mjs";
|
|
3
|
+
import { promptPiConfiguration, persistPiConfiguration } from "../providers/pi.mjs";
|
|
4
|
+
import { installCadence, installCliTool } from "../tools/cli-installer.mjs";
|
|
5
|
+
|
|
6
|
+
function createPiActions(paths, withExtensionsPresent, runtime = {}) {
|
|
7
|
+
const operations = {
|
|
8
|
+
withExtensionsPresent,
|
|
9
|
+
installCli: () => installCliTool("pi", promptApi),
|
|
10
|
+
installCadence,
|
|
11
|
+
warn: (message) => p.log.warn(message),
|
|
12
|
+
success: (message) => p.log.success(message),
|
|
13
|
+
promptConfiguration: () => promptPiConfiguration(paths, promptApi),
|
|
14
|
+
persistConfiguration: (intent) => persistPiConfiguration(paths, intent),
|
|
15
|
+
...runtime
|
|
16
|
+
};
|
|
17
|
+
async function cadence() {
|
|
18
|
+
await operations.installCadence();
|
|
19
|
+
operations.success("cadence 安装/更新完成");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
async install() {
|
|
24
|
+
if (!await operations.installCli()) return { cadenceFailed: false };
|
|
25
|
+
try {
|
|
26
|
+
await cadence();
|
|
27
|
+
return { cadenceFailed: false };
|
|
28
|
+
} catch (error) {
|
|
29
|
+
operations.warn(`Pi CLI 已安装,cadence 安装失败:${error.message || String(error)}。可选择“安装/更新 cadence”重试;API 配置仍可继续。`);
|
|
30
|
+
return { cadenceFailed: true };
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
cadence,
|
|
34
|
+
configure: () => operations.withExtensionsPresent(paths, async (guard) => {
|
|
35
|
+
await guard();
|
|
36
|
+
const intent = await operations.promptConfiguration();
|
|
37
|
+
await guard();
|
|
38
|
+
await operations.persistConfiguration(intent);
|
|
39
|
+
})
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { createPiActions };
|
package/lib/cli/prompts.mjs
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import * as p from "@clack/prompts";
|
|
2
2
|
|
|
3
3
|
const interactiveMenuDescriptors = [
|
|
4
|
-
{ value: "full-init", label: "完整初始化", hint: "同步 + 安装 + 配置", group: "main" },
|
|
5
|
-
{ value: "install", label: "仅同步工作流", group: "main" },
|
|
6
4
|
{ value: "grok-search", label: "配置 grok-search", hint: "技能", group: "skill" },
|
|
7
5
|
{ value: "context7", label: "配置 context7-auto-research", hint: "技能", group: "skill" },
|
|
8
6
|
{ value: "pi-cli", label: "配置/安装 Pi", hint: "CLI", group: "cli" },
|
|
@@ -10,12 +8,13 @@ const interactiveMenuDescriptors = [
|
|
|
10
8
|
{ value: "claude-cli", label: "配置/安装 Claude Code", hint: "CLI", group: "cli" },
|
|
11
9
|
{ value: "exit", label: "退出", group: "exit" }
|
|
12
10
|
];
|
|
13
|
-
const interactiveMenuDefaultValue = "
|
|
11
|
+
const interactiveMenuDefaultValue = "pi-cli";
|
|
14
12
|
const cliToolLabels = {
|
|
15
13
|
pi: "Pi",
|
|
16
14
|
codex: "Codex",
|
|
17
15
|
claude: "Claude Code"
|
|
18
16
|
};
|
|
17
|
+
|
|
19
18
|
class CancelledError extends Error {
|
|
20
19
|
constructor(message = "用户取消") {
|
|
21
20
|
super(message);
|
|
@@ -29,6 +28,7 @@ function buildCliToolMenuDescriptors(tool) {
|
|
|
29
28
|
return [
|
|
30
29
|
{ value: `${tool}-api`, label: `配置 ${label} API` },
|
|
31
30
|
{ value: `${tool}-install`, label: `安装/更新 ${label}` },
|
|
31
|
+
...(tool === "pi" ? [{ value: "pi-cadence", label: "安装/更新 cadence" }] : []),
|
|
32
32
|
{ value: "back", label: "返回上一级" }
|
|
33
33
|
];
|
|
34
34
|
}
|