openfeel 1.1.4 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +25 -4
- package/README.md +13 -3
- package/README.zh-CN.md +25 -4
- package/dist/cli/deploy-check-output.d.ts +35 -0
- package/dist/cli/deploy-check-output.d.ts.map +1 -0
- package/dist/cli/deploy-check-output.js +53 -0
- package/dist/cli/deploy-check-output.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +3 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/repl.d.ts.map +1 -1
- package/dist/cli/repl.js +3 -0
- package/dist/cli/repl.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +56 -2
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +16 -4
- package/dist/commands/update.js.map +1 -1
- package/dist/core/backup.d.ts.map +1 -1
- package/dist/core/backup.js +18 -5
- package/dist/core/backup.js.map +1 -1
- package/dist/core/claude-settings.d.ts +31 -0
- package/dist/core/claude-settings.d.ts.map +1 -0
- package/dist/core/claude-settings.js +120 -0
- package/dist/core/claude-settings.js.map +1 -0
- package/dist/core/config.js +2 -2
- package/dist/core/deployment-check.d.ts +44 -0
- package/dist/core/deployment-check.d.ts.map +1 -0
- package/dist/core/deployment-check.js +98 -0
- package/dist/core/deployment-check.js.map +1 -0
- package/dist/core/global-paths.d.ts +10 -0
- package/dist/core/global-paths.d.ts.map +1 -1
- package/dist/core/global-paths.js +21 -0
- package/dist/core/global-paths.js.map +1 -1
- package/dist/core/i18n-data/en.d.ts.map +1 -1
- package/dist/core/i18n-data/en.js +23 -2
- package/dist/core/i18n-data/en.js.map +1 -1
- package/dist/core/i18n-data/zh-CN.d.ts.map +1 -1
- package/dist/core/i18n-data/zh-CN.js +23 -2
- package/dist/core/i18n-data/zh-CN.js.map +1 -1
- package/dist/core/setup.d.ts +2 -2
- package/dist/core/setup.d.ts.map +1 -1
- package/dist/core/setup.js +84 -54
- package/dist/core/setup.js.map +1 -1
- package/dist/core/template-loader.d.ts +15 -0
- package/dist/core/template-loader.d.ts.map +1 -1
- package/dist/core/template-loader.js +3906 -109
- package/dist/core/template-loader.js.map +1 -1
- package/dist/core/update.d.ts +8 -0
- package/dist/core/update.d.ts.map +1 -1
- package/dist/core/update.js +344 -133
- package/dist/core/update.js.map +1 -1
- package/package.json +1 -1
package/dist/core/update.js
CHANGED
|
@@ -8,11 +8,12 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { existsSync, readFileSync, mkdirSync } from 'node:fs';
|
|
10
10
|
import { resolve, dirname, join, basename, isAbsolute, relative } from 'node:path';
|
|
11
|
-
import { loadAgentTemplate, listAgentIds, loadTemplate } from './template-loader.js';
|
|
11
|
+
import { loadAgentTemplate, listAgentIds, loadTemplate, loadClaudeConstraintTemplate, loadClaudeAgentTemplate, listClaudeAgentIds } from './template-loader.js';
|
|
12
12
|
import { recordProjectLang } from './workspace/identity.js';
|
|
13
13
|
import { t, getCliLang } from './i18n.js';
|
|
14
|
-
import { getOpencodeGlobalDir, getGlobalAgentsDir, getGlobalSkillsDir, getGlobalCoreMdPath, getGlobalAgentsMdPath, getGlobalOpencodeJsoncPath, getGlobalUpdateStatePath } from './global-paths.js';
|
|
14
|
+
import { getOpencodeGlobalDir, getGlobalAgentsDir, getGlobalSkillsDir, getGlobalCoreMdPath, getGlobalAgentsMdPath, getGlobalOpencodeJsoncPath, getGlobalUpdateStatePath, getClaudeAgentsDir, getClaudeSkillsDir, getClaudeMdPath, getClaudeSettingsPath } from './global-paths.js';
|
|
15
15
|
import { mergeGlobalOpencodeJsonc, buildProjectOpencodeJsoncObj } from './opencode-config.js';
|
|
16
|
+
import { applyClaudeSettings } from './claude-settings.js';
|
|
16
17
|
import { atomicWriteFileSync } from './fs/atomic-write.js';
|
|
17
18
|
import { withFileLock, globalLockPath } from './fs/file-lock.js';
|
|
18
19
|
import { detectFileType, normalize, splitFrontmatter, parseRegion, wrapRegion, replaceRegion, mergeFrontmatter, serializeFrontmatter } from './managed-region.js';
|
|
@@ -29,8 +30,13 @@ export const supportedTools = [
|
|
|
29
30
|
description: 'OpenCode AI 编码助手 — 生成 Agent 定义和 openfeel-* 技能',
|
|
30
31
|
enabled: true,
|
|
31
32
|
},
|
|
33
|
+
{
|
|
34
|
+
id: 'claude',
|
|
35
|
+
name: 'Claude Code',
|
|
36
|
+
description: 'Anthropic Claude Code CLI — 生成 CLAUDE.md、Claude subagent 定义与共享 openfeel-* 技能',
|
|
37
|
+
enabled: false,
|
|
38
|
+
},
|
|
32
39
|
// 后续扩展:
|
|
33
|
-
// { id: 'claude', name: 'Claude Code', description: 'Anthropic Claude Code CLI', enabled: false },
|
|
34
40
|
// { id: 'cursor', name: 'Cursor', description: 'Cursor IDE', enabled: false },
|
|
35
41
|
];
|
|
36
42
|
/**
|
|
@@ -55,23 +61,51 @@ export async function selectTools() {
|
|
|
55
61
|
}
|
|
56
62
|
catch {
|
|
57
63
|
// 交互不可用时(如非 TTY 环境),回退到全部启用项
|
|
58
|
-
console.log('
|
|
64
|
+
console.log(t('update.nonInteractiveToolsTmpl', getCliLang(process.cwd())));
|
|
59
65
|
return supportedTools.filter((t) => t.enabled).map((t) => t.id);
|
|
60
66
|
}
|
|
61
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* 解析 `--tools` 逗号分隔列表。
|
|
70
|
+
* @returns tools=合法 id(保序去重);invalid=非法项。空串/纯空白视为非法(触发 exit 1)。
|
|
71
|
+
*/
|
|
72
|
+
export function parseToolList(raw) {
|
|
73
|
+
if (raw.trim() === '') {
|
|
74
|
+
return { tools: [], invalid: [''] };
|
|
75
|
+
}
|
|
76
|
+
const valid = new Set(supportedTools.map((t) => t.id));
|
|
77
|
+
const tools = [];
|
|
78
|
+
const invalid = [];
|
|
79
|
+
for (const part of raw.split(',')) {
|
|
80
|
+
const v = part.trim();
|
|
81
|
+
if (v === '') {
|
|
82
|
+
continue;
|
|
83
|
+
} // 忽略尾随/空段
|
|
84
|
+
if (!valid.has(v)) {
|
|
85
|
+
invalid.push(v);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (!tools.includes(v)) {
|
|
89
|
+
tools.push(v);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { tools, invalid };
|
|
93
|
+
}
|
|
62
94
|
// ─── Agent 定义由 template-loader.ts 统一管理 ──────────────────────────
|
|
63
95
|
// ─── Skill 定义内容 ─────────────────────────────────────────────────
|
|
64
96
|
// AUTO-GENERATED-BEGIN: SKILL_DEFINITIONS
|
|
65
97
|
export const SKILL_DEFINITIONS = {
|
|
66
98
|
'openfeel-agent-model-check': `---
|
|
67
99
|
name: openfeel-agent-model-check
|
|
68
|
-
description: Agent 模型检查与修复。当 Agent 报 "Model not found" 或需要排查模型配置时使用。涵盖 auth.json 校验(opencode 适配器)、provider key 匹配、模型能力确认、openfeel-vision
|
|
100
|
+
description: Agent 模型检查与修复。当 Agent 报 "Model not found" 或需要排查模型配置时使用。涵盖 auth.json 校验(opencode 适配器)、provider key 匹配、模型能力确认、openfeel-vision 多模态专项指南、Claude Code 侧模型(\`/model\`、网关)。
|
|
69
101
|
---
|
|
70
102
|
|
|
71
103
|
# Skill: openfeel-agent-model-check
|
|
72
104
|
|
|
73
105
|
# Agent 模型检查与修复
|
|
74
106
|
|
|
107
|
+
> 正文以**能力名**叙述;平台差异见 \`### opencode 适配器\` / \`### Claude Code 适配器\` 两小节。
|
|
108
|
+
|
|
75
109
|
## 何时使用
|
|
76
110
|
|
|
77
111
|
- Agent 调度时报 \`Model not found: xxx\`
|
|
@@ -89,22 +123,6 @@ Model not found: {provider_key}/{model_id}
|
|
|
89
123
|
|
|
90
124
|
注意是否有 \`Did you mean: xxx\` 提示——如有,直接使用建议的模型名。
|
|
91
125
|
|
|
92
|
-
### 第二步:读取 auth.json 确认实际 provider key
|
|
93
|
-
|
|
94
|
-
\`\`\`bash
|
|
95
|
-
# Windows PowerShell / macOS / Linux
|
|
96
|
-
cat ~/.local/share/opencode/auth.json
|
|
97
|
-
\`\`\`
|
|
98
|
-
|
|
99
|
-
**关键点**:模型引用中的 provider 部分必须与 \`auth.json\` 中的 key 完全一致,而非 \`opencode.jsonc\` 中 \`provider.name\` 或 \`provider.id\`。
|
|
100
|
-
|
|
101
|
-
常见 provider key 示例:
|
|
102
|
-
- \`alibaba-cn\` — 阿里云中国区(DashScope)
|
|
103
|
-
- \`deepseek\` — DeepSeek
|
|
104
|
-
- \`anthropic\` — Anthropic
|
|
105
|
-
- \`openai\` — OpenAI
|
|
106
|
-
- \`zhipuai\` — 智谱 AI
|
|
107
|
-
|
|
108
126
|
### 第三步:确认模型是否支持目标能力
|
|
109
127
|
|
|
110
128
|
查阅 [Models.dev](https://models.dev) 确认模型属性:
|
|
@@ -121,7 +139,31 @@ cat ~/.local/share/opencode/auth.json
|
|
|
121
139
|
- \`deepseek-flash\` 支持多模态图像输入(openfeel-vision 现用模型)
|
|
122
140
|
- 备选视觉模型:\`qwen3-vl-plus\`(模型名中的 \`vl\` 表示 Vision-Language)
|
|
123
141
|
|
|
124
|
-
###
|
|
142
|
+
### 第六步:验证
|
|
143
|
+
|
|
144
|
+
重启后调度目标 Agent 执行简单测试任务,确认无报错。
|
|
145
|
+
|
|
146
|
+
## 平台适配
|
|
147
|
+
|
|
148
|
+
### opencode 适配器
|
|
149
|
+
|
|
150
|
+
#### 第二步:读取 auth.json 确认实际 provider key
|
|
151
|
+
|
|
152
|
+
\`\`\`bash
|
|
153
|
+
# Windows PowerShell / macOS / Linux
|
|
154
|
+
cat ~/.local/share/opencode/auth.json
|
|
155
|
+
\`\`\`
|
|
156
|
+
|
|
157
|
+
**关键点**:模型引用中的 provider 部分必须与 \`auth.json\` 中的 key 完全一致,而非 \`opencode.jsonc\` 中 \`provider.name\` 或 \`provider.id\`。
|
|
158
|
+
|
|
159
|
+
常见 provider key 示例:
|
|
160
|
+
- \`alibaba-cn\` — 阿里云中国区(DashScope)
|
|
161
|
+
- \`deepseek\` — DeepSeek
|
|
162
|
+
- \`anthropic\` — Anthropic
|
|
163
|
+
- \`openai\` — OpenAI
|
|
164
|
+
- \`zhipuai\` — 智谱 AI
|
|
165
|
+
|
|
166
|
+
#### 第四步:检查 opencode.jsonc 配置
|
|
125
167
|
|
|
126
168
|
\`\`\`jsonc
|
|
127
169
|
{
|
|
@@ -139,13 +181,15 @@ cat ~/.local/share/opencode/auth.json
|
|
|
139
181
|
3. 不要随意添加前缀(如 \`alibaba/\`、\`Alibaba(China)/\`)
|
|
140
182
|
4. 如果不需要自定义 provider 选项(如 baseURL),可以完全不写 \`provider\` 块
|
|
141
183
|
|
|
142
|
-
|
|
184
|
+
#### 第五步:修改并重启
|
|
143
185
|
|
|
144
186
|
修改 \`opencode.jsonc\` 后**必须重启 opencode** 才能生效。运行中的会话使用启动时加载的配置。
|
|
145
187
|
|
|
146
|
-
###
|
|
188
|
+
### Claude Code 适配器
|
|
147
189
|
|
|
148
|
-
|
|
190
|
+
- Claude Code **无 \`auth.json\`**。
|
|
191
|
+
- 模型/provider 由 **Claude 侧**决定(\`/model\`、\`settings.json\`/\`env\`、网关如 cc-switch)。
|
|
192
|
+
- Claude Code **无 \`opencode.jsonc\`**,模型引用不进 jsonc。
|
|
149
193
|
|
|
150
194
|
## 快速诊断清单
|
|
151
195
|
|
|
@@ -156,6 +200,7 @@ cat ~/.local/share/opencode/auth.json
|
|
|
156
200
|
| 模型支持目标能力 | 查阅 models.dev | Input/Tool Call 等字段 = Yes |
|
|
157
201
|
| opencode.jsonc 语法 | 检查 JSON 格式 | 无语法错误 |
|
|
158
202
|
| 重启生效 | 重启 opencode 后重新测试 | 无 Model not found 报错 |
|
|
203
|
+
| 模型由 Claude 侧配置 | \`/model\` 或 settings/env | 模型可用 |
|
|
159
204
|
|
|
160
205
|
## 多模态(openfeel-vision)Agent 专项
|
|
161
206
|
|
|
@@ -170,6 +215,8 @@ openfeel-vision Agent 必须配置多模态模型。当前默认使用 \`deepsee
|
|
|
170
215
|
|
|
171
216
|
**推荐**:优先使用 \`deepseek-flash\`(已内置多模态);如需更大上下文可备选 \`qwen3-vl-plus\`。
|
|
172
217
|
|
|
218
|
+
> **Claude Code 适配器**:多模态由 Claude 侧模型决定;\`openfeel-vision\` subagent 须选支持图像输入的模型。
|
|
219
|
+
|
|
173
220
|
## 常见错误与修复
|
|
174
221
|
|
|
175
222
|
| 错误信息 | 原因 | 修复 |
|
|
@@ -178,6 +225,8 @@ openfeel-vision Agent 必须配置多模态模型。当前默认使用 \`deepsee
|
|
|
178
225
|
| \`Model not found: alibaba/xxx\` | 内置 key 与实际注册的 key 不一致 | 检查 auth.json,使用实际 key |
|
|
179
226
|
| \`Model not found: xxx. Did you mean: yyy\` | 模型名拼写错误或不存在 | 使用 \`Did you mean\` 建议的名称 |
|
|
180
227
|
| Agent 调度成功但无法处理图片 | 配置了纯文本模型 | 改为支持图像输入的模型(如 \`deepseek-flash\`) |
|
|
228
|
+
|
|
229
|
+
> **Claude Code 适配器**:以上排查针对 opencode 的 \`auth.json\`/\`opencode.jsonc\`;Claude 侧的模型解析以 \`/model\`、\`settings.json\`/\`env\` 或网关配置为准。
|
|
181
230
|
`,
|
|
182
231
|
'openfeel-bug-acceptance': `---
|
|
183
232
|
name: openfeel-bug-acceptance
|
|
@@ -322,7 +371,10 @@ description: 渐进式查阅 .openfeel/kb/ 项目知识库,按当前任务需
|
|
|
322
371
|
💡 如需确保索引为最新,可运行: python scripts/build_kb_index.py --dry-run
|
|
323
372
|
\`\`\`
|
|
324
373
|
|
|
325
|
-
> 注:语义检索是 \`openfeel-check-kb\`
|
|
374
|
+
> 注:语义检索是 \`openfeel-check-kb\` 的内置回退能力。调用方只需加载一次:
|
|
375
|
+
> - opencode 适配器:\`skill("openfeel-check-kb")\`;
|
|
376
|
+
> - Claude Code 适配器:以 \`Skill\` 工具加载 \`openfeel-check-kb\`(工具名映射见 \`openfeel-tool-usage\`)。
|
|
377
|
+
> 无需再手动调用 \`openfeel-search-kb\`。
|
|
326
378
|
|
|
327
379
|
### 6. 强制检索标记
|
|
328
380
|
|
|
@@ -336,7 +388,7 @@ Skill 加载后,根据步骤 3 的匹配结果决定是否追加提示:
|
|
|
336
388
|
`,
|
|
337
389
|
'openfeel-cli-usage': `---
|
|
338
390
|
name: openfeel-cli-usage
|
|
339
|
-
description: OpenFeel CLI 命令用法参考:命令清单与关键参数、15 个 phase 枚举与转移表、stageId 命名与目录映射约定、v1.1.2 新增能力(自描述/可纠错、--json 结构化输出、health --fix、scheme draft、knowledge dedup、纠正清理侧命令面)、v1.1.4 新增能力(checkpoint 选择性恢复 --stage/--dry-run、flow stage reset
|
|
391
|
+
description: OpenFeel CLI 命令用法参考:命令清单与关键参数、15 个 phase 枚举与转移表、stageId 命名与目录映射约定、v1.1.2 新增能力(自描述/可纠错、--json 结构化输出、health --fix、scheme draft、knowledge dedup、纠正清理侧命令面)、v1.1.4 新增能力(checkpoint 选择性恢复 --stage/--dry-run、flow stage reset 精准复位、故障恢复路径)、v1.1.5 新增能力(setup --check [--json] 诊断 / 被动部署提示)、v1.1.6 新增能力(Claude Code 适配器 \`--tools claude\` / settings 注入)。当需要查询 CLI 命令、参数、phase、stageId、阶段命名时加载。
|
|
340
392
|
---
|
|
341
393
|
|
|
342
394
|
# OpenFeel CLI 用法参考
|
|
@@ -346,8 +398,9 @@ description: OpenFeel CLI 命令用法参考:命令清单与关键参数、15
|
|
|
346
398
|
- 定位:**查询型参考手册**(只读,不自执行)。回答「命令是什么、参数怎么给、phase/stageId 规则如何」。
|
|
347
399
|
- 边界:**执行型交互向导**请用 \`openfeel-wizard\` skill(跑 \`openfeel flow wizard\` 推进流水线);本 skill 只承载静态知识查阅。
|
|
348
400
|
- 与 \`openfeel-tool-usage\` 协同:工具选择规范见该 skill;本 skill 补充 CLI 命令细节。
|
|
401
|
+
- 正文以**能力名**叙述;平台差异见 \`### opencode 适配器\` / \`### Claude Code 适配器\` 两小节。
|
|
349
402
|
|
|
350
|
-
> 本文档为 v1.1.
|
|
403
|
+
> 本文档为 v1.1.6 快照;命令/参数细节以 \`openfeel <cmd> --help\`(本仓执行)实时输出为准(CLI 演进后本文档可能滞后)。
|
|
351
404
|
>
|
|
352
405
|
> ⚠️ **本仓自举**:本仓(openfeel 源码仓库)开发/执行时请用 \`node bin/openfeel.js <cmd>\`;安装后使用 \`openfeel <cmd>\`。
|
|
353
406
|
|
|
@@ -356,8 +409,8 @@ description: OpenFeel CLI 命令用法参考:命令清单与关键参数、15
|
|
|
356
409
|
| 命令 | 用途 | 关键参数 |
|
|
357
410
|
|------|------|----------|
|
|
358
411
|
| \`openfeel init [path]\` | 初始化项目工作区 | \`--lang <zh-CN\\|en>\`、\`--demo\`、\`--workspace-only\`、\`--non-interactive\` |
|
|
359
|
-
| \`openfeel setup\` | 部署全局框架配置(不建项目 \`.openfeel
|
|
360
|
-
| \`openfeel update [path]\` | 部署适配文件到目标项目 | \`--lang\`、\`--force
|
|
412
|
+
| \`openfeel setup\` | 部署全局框架配置(不建项目 \`.openfeel/\`);\`--check\` 只读诊断全局部署一致性 | \`--lang <zh-CN\\|en>\`、\`--check [--json]\`、\`--tools <list>\` |
|
|
413
|
+
| \`openfeel update [path]\` | 部署适配文件到目标项目 | \`--lang\`、\`--force\`、\`--tools <list>\` |
|
|
361
414
|
| \`openfeel migrate [path]\` | Legacy 布局迁移(检测/备份/迁移/回滚) | \`--dry-run\`、\`--remap-assignee\`、\`--clean-global-core-md\`;\`migrate rollback\` |
|
|
362
415
|
| \`openfeel flow ...\` | 流水线状态管理 | 见下「flow 子命令」 |
|
|
363
416
|
| \`openfeel flow checkpoint restore\` | Checkpoint 恢复(全量 / 选择性 / 预览) | \`<file> [--force] [--stage <id>] [--dry-run]\`(无 \`--stage\`=全量覆盖,最后手段;\`--stage\`=按阶段回退;\`--dry-run\`=零写盘预览) |
|
|
@@ -381,6 +434,39 @@ description: OpenFeel CLI 命令用法参考:命令清单与关键参数、15
|
|
|
381
434
|
|
|
382
435
|
> 命令职责分层:\`plan stage add\`(完整,推荐)> \`flow stage add\`(仅注册 \`flow.json\`,不建目录)> \`stage create\`(已弃用)。
|
|
383
436
|
|
|
437
|
+
## v1.1.6 新增能力(stage-68~73)
|
|
438
|
+
|
|
439
|
+
- **多 harness 并存**:\`openfeel setup\` / \`openfeel update\` 新增 \`--tools <逗号列表>\`(如 \`opencode,claude\`);非法值 **exit 1** 前置校验零写盘;默认仅 \`opencode\`。
|
|
440
|
+
- **Claude Code 适配器资产**:\`--tools claude\` 部署 \`~/.claude/\`——\`CLAUDE.md\`(全局约束,用户已有内容 append 不覆盖)、\`agents/\`(**9 个**,含 \`feel\`)、\`skills/\`(**17 个**共享源)、\`settings.json\`(注入 \`"agent":"feel"\` + \`"permissions":{"defaultMode":"auto"}\`,用户既有键逐字保留);默认零触(不带 \`--tools\` 时 \`~/.claude/\` 零变化)。
|
|
441
|
+
|
|
442
|
+
## v1.1.5 新增能力(stage-67)
|
|
443
|
+
|
|
444
|
+
- **主动诊断 \`openfeel setup --check [--json]\`**:只读检测全局部署版本(\`~/.openfeel/update_state.json.openfeel_version\`)与 CLI 版本一致性,**不执行部署**;一致退出 \`0\`,\`mismatch\`/\`missing\`/\`unknown\` 退出 \`1\`;\`--json\` 输出 \`{schemaVersion,status,cliVersion,deployedVersion}\`(\`status\` ∈ \`ok|mismatch|missing|unknown\`)。
|
|
445
|
+
- **被动部署提示**:任意命令 / REPL 在 TTY 下检测到全局部署漂移(\`mismatch\`)或缺失(\`missing\`)时,向 **stderr** 提示运行 \`openfeel setup\`;**每进程一次**;\`--json\`/\`--quiet\`/\`--version\`/\`--help\`、CI、\`OPENFEEL_NO_UPDATE_CHECK\` 及部署类命令(\`setup\`/\`update\`/\`init\`/\`migrate\`)静默;\`unknown\`(state 损坏)被动静默、仅 \`--check\` 显式报告;**不改退出码、不写盘**。
|
|
446
|
+
|
|
447
|
+
## 升级流程
|
|
448
|
+
|
|
449
|
+
### opencode 适配器
|
|
450
|
+
|
|
451
|
+
\`\`\`bash
|
|
452
|
+
npm i -g openfeel@latest # 1. 升级 CLI
|
|
453
|
+
openfeel setup # 2. 重跑部署,刷新全局 AGENTS.md/agents/skills/opencode.jsonc
|
|
454
|
+
# 3. 重启 harness(opencode)以加载新的全局配置
|
|
455
|
+
\`\`\`
|
|
456
|
+
|
|
457
|
+
仅 \`npm i -g\` 不会刷新全局资产,须重跑 \`openfeel setup\` 并重启 harness。\`openfeel setup --check\` 可诊断全局部署版本与 CLI 版本是否一致(一致退出 0,否则退出 1)。
|
|
458
|
+
|
|
459
|
+
### Claude Code 适配器
|
|
460
|
+
|
|
461
|
+
\`\`\`bash
|
|
462
|
+
npm i -g openfeel@latest # 1. 升级 CLI
|
|
463
|
+
openfeel setup --tools claude # 2. 重跑部署(claude 目标)
|
|
464
|
+
\`\`\`
|
|
465
|
+
|
|
466
|
+
- **\`--tools\` 由 v1.1.6 提供**:\`openfeel setup --tools claude\`;首次创建 \`~/.claude/{agents,skills}\` 后**重启会话或 \`/reload-skills\`** 使新 skill/agent 生效。
|
|
467
|
+
- 首次创建 \`~/.claude/{agents,skills}\` 后需**重启会话或 \`/reload-skills\`** 使新 skill/agent 生效。
|
|
468
|
+
- \`CLAUDE.md\` 于**下次会话**生效。
|
|
469
|
+
|
|
384
470
|
## v1.1.2 新增能力(stage-41~55)
|
|
385
471
|
|
|
386
472
|
> 以下为 v1.1.2 相对上版的增量能力(分组摘要,细节以 \`<cmd> --help\` 为准)。
|
|
@@ -454,11 +540,19 @@ plan_pending → plan_review → plan_passed → scheme_pending → scheme_revie
|
|
|
454
540
|
|
|
455
541
|
## 权限模型要点
|
|
456
542
|
|
|
543
|
+
### opencode 适配器
|
|
544
|
+
|
|
457
545
|
- 9 个 agent 内联 \`permission:\` 白名单,均含 \`external_directory: "allow"\`(单一键,无读写粒度)。
|
|
458
546
|
- **合并语义**:agent \`.md\` frontmatter 与 \`opencode.jsonc\` 按权限键**深合并**,同名键以 agent \`.md\` 为准(配置文件无法覆盖已声明键)。
|
|
459
547
|
- **收紧入口**:项目根 \`.opencode/agent/<name>.md\` 覆盖全局同名 agent 并重写完整 \`permission\` 块;勿手改全局 agent frontmatter(会被 \`update\` 覆盖)。
|
|
460
548
|
- 详 \`.openfeel/manual/core/permission.md\`。
|
|
461
549
|
|
|
550
|
+
### Claude Code 适配器
|
|
551
|
+
|
|
552
|
+
- subagent 于 frontmatter 声明 \`tools\`(白名单,未列即不可用)、\`disallowedTools\`(黑名单)与 \`permissionMode\`。
|
|
553
|
+
- **无 \`external_directory\` 对等概念**。
|
|
554
|
+
- \`settings.json\` 由 \`openfeel setup/update --tools claude\` 注入(\`agent:"feel"\` + \`permissions.defaultMode:"auto"\`,用户键保留),**v1.1.6 起**。
|
|
555
|
+
|
|
462
556
|
## 与 openfeel-wizard 的区别
|
|
463
557
|
|
|
464
558
|
| 维度 | \`openfeel-cli-usage\`(本 skill) | \`openfeel-wizard\` |
|
|
@@ -573,7 +667,9 @@ description: 读取 .openfeel/plan/{series}/{stage}/status.md,判断当前子
|
|
|
573
667
|
- \`deps_satisfied = true\`(本 Skill 递归判断)
|
|
574
668
|
- \`状态\` 为 \`ready_for_code\` 或 \`auto_running\`
|
|
575
669
|
- \`自动推进\` 为 \`enabled\`(若 status.md 未填则回退到 config.yaml \`auto_advance\`)
|
|
576
|
-
2. 收集为 \`parallel_candidates\`
|
|
670
|
+
2. 收集为 \`parallel_candidates\` 列表,供主会话(Feel)批量调度执行。
|
|
671
|
+
|
|
672
|
+
> 口径:**subagent 不互调**(不直接调用其他 subagent);需向用户确认的事项一律由主会话负责。
|
|
577
673
|
|
|
578
674
|
### 5. 判断自动推进资格
|
|
579
675
|
|
|
@@ -620,8 +716,8 @@ description: 读取 .openfeel/plan/{series}/{stage}/status.md,判断当前子
|
|
|
620
716
|
| \`coding\` | openfeel-executor 正在开发 |
|
|
621
717
|
| \`ready_for_review\` | openfeel-executor 可启动 openfeel-reviewer 审查,或等待用户触发 |
|
|
622
718
|
| \`review_failed\` | openfeel-reviewer 可启动 openfeel-executor 修复审查问题 |
|
|
623
|
-
| \`review_passed\` |
|
|
624
|
-
| \`ready_for_test\` |
|
|
719
|
+
| \`review_passed\` | 主会话可推进到 ready_for_test |
|
|
720
|
+
| \`ready_for_test\` | 主会话可启动 openfeel-feel-tester |
|
|
625
721
|
| \`test_writing\` | openfeel-feel-tester 正在写测试 |
|
|
626
722
|
| \`testing\` | openfeel-feel-tester 正在测试 |
|
|
627
723
|
| \`bug_found\` | openfeel-feel-tester 可启动 openfeel-executor 修复 Bug |
|
|
@@ -654,16 +750,18 @@ description: 加载流水线健康检查结果,供 Agent 判断 flow.json 与
|
|
|
654
750
|
`,
|
|
655
751
|
'openfeel-model-check': `---
|
|
656
752
|
name: openfeel-model-check
|
|
657
|
-
description: Feel
|
|
753
|
+
description: 主会话(Feel)自检时检查所有 Agent 的模型配置状态,识别期望模型 vs 实际模型的差距,引导用户在目标工具中完成配置。首次配置后存储为部署模板,新项目可直接复用。
|
|
658
754
|
---
|
|
659
755
|
|
|
660
756
|
# 模型配置检查
|
|
661
757
|
|
|
662
758
|
> ⚠️ **本仓自举**:本仓(openfeel 源码仓库)开发/执行时请用 \`node bin/openfeel.js <cmd>\`;安装后使用 \`openfeel <cmd>\`。
|
|
663
759
|
|
|
760
|
+
> 正文以**能力名**叙述;平台差异见 \`### opencode 适配器\` / \`### Claude Code 适配器\` 两小节。
|
|
761
|
+
|
|
664
762
|
## 触发时机
|
|
665
763
|
|
|
666
|
-
|
|
764
|
+
主会话在以下时机加载本 Skill:
|
|
667
765
|
- 会话启动自检(每次)
|
|
668
766
|
- 用户请求检查模型配置(按需)
|
|
669
767
|
- 新项目首次初始化后(\`openfeel init\`)
|
|
@@ -672,19 +770,11 @@ Feel Agent 在以下时机加载本 Skill:
|
|
|
672
770
|
|
|
673
771
|
### 1. 识别当前平台
|
|
674
772
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
| 配置文件 | 平台 |
|
|
678
|
-
|----------|------|
|
|
679
|
-
| \`opencode.jsonc\` | OpenCode(当前适配器) |
|
|
680
|
-
| \`kilo/kilo.json\` | Kilo |
|
|
681
|
-
| \`claude/claude.json\` | Claude |
|
|
682
|
-
|
|
683
|
-
若无平台配置文件,提示用户当前不在支持的平台中。
|
|
773
|
+
读取当前 harness 的平台配置文件,确定当前适配器平台(已知平台配置文件与 agents 目录差异见 \`## 平台适配\`)。
|
|
684
774
|
|
|
685
775
|
### 2. 扫描 Agent 定义
|
|
686
776
|
|
|
687
|
-
扫描
|
|
777
|
+
扫描 agents 目录(目录差异见 \`## 平台适配\`)下所有 \`.md\` 文件,提取每个 Agent 的模型需求。
|
|
688
778
|
|
|
689
779
|
**提取规则(优先级从高到低)**:
|
|
690
780
|
|
|
@@ -761,7 +851,7 @@ models:
|
|
|
761
851
|
\`\`\`markdown
|
|
762
852
|
| Agent | 角色要求 | 实际模型 | 配置来源 | 状态 |
|
|
763
853
|
|-------|----------|----------|----------|:--:|
|
|
764
|
-
|
|
|
854
|
+
| 主会话 | 主力推理 | deepseek-v4-pro | default | ⚠️ |
|
|
765
855
|
| openfeel-reviewer | **异种推理** | deepseek-v4-pro | default | ❌ 与主力相同! |
|
|
766
856
|
| openfeel-executor | 快速 | deepseek-v4-pro | default | ⚠️ 未使用快速模型 |
|
|
767
857
|
\`\`\`
|
|
@@ -773,7 +863,7 @@ models:
|
|
|
773
863
|
\`\`\`markdown
|
|
774
864
|
## 🔍 模型配置检查报告
|
|
775
865
|
|
|
776
|
-
|
|
866
|
+
**平台**:当前 harness(opencode 适配器 / Claude Code 适配器)
|
|
777
867
|
**配置文件**:.openfeel/config.yaml
|
|
778
868
|
**检查时间**:yyyy-mm-dd HH:MM
|
|
779
869
|
|
|
@@ -801,12 +891,12 @@ models:
|
|
|
801
891
|
|
|
802
892
|
### 6. 引导用户配置
|
|
803
893
|
|
|
804
|
-
|
|
894
|
+
若检查发现以下任一问题,**必须**使用向用户提问工具(opencode 适配器:\`question\`;Claude Code 适配器:\`AskUserQuestion\`)引导用户:
|
|
805
895
|
|
|
806
896
|
| 触发条件 | 引导内容 |
|
|
807
897
|
|----------|----------|
|
|
808
898
|
| \`models\` 节不存在 | "未检测到模型配置。你需要为不同角色分配模型吗?" → 引导创建 |
|
|
809
|
-
| openfeel-reviewer 使用与主力相同模型 | "⚠️ openfeel-reviewer
|
|
899
|
+
| openfeel-reviewer 使用与主力相同模型 | "⚠️ openfeel-reviewer 当前与主会话使用相同模型,异种交叉审查的核心优势无法发挥。建议为 openfeel-reviewer 配置不同的模型系列。" |
|
|
810
900
|
| openfeel-executor 使用推理模型 | "⚠️ openfeel-executor 建议使用快速模型以节省成本。是否配置?" |
|
|
811
901
|
| 关键 Agent 无任何配置 | "以下 Agent 无模型配置:{列表}。请配置。" |
|
|
812
902
|
|
|
@@ -826,7 +916,7 @@ models:
|
|
|
826
916
|
# 或直接复制此文件到 .openfeel/ 并重命名为 config.yaml(需合并其他节)
|
|
827
917
|
#
|
|
828
918
|
# 最近配置时间:yyyy-mm-dd HH:MM
|
|
829
|
-
#
|
|
919
|
+
# 平台:当前 harness(opencode 适配器 / Claude Code 适配器)
|
|
830
920
|
|
|
831
921
|
models:
|
|
832
922
|
default:
|
|
@@ -845,6 +935,31 @@ models:
|
|
|
845
935
|
|
|
846
936
|
此模板在下次 \`openfeel init\` 或新项目部署时自动检测并建议复用。
|
|
847
937
|
|
|
938
|
+
## 平台适配
|
|
939
|
+
|
|
940
|
+
### opencode 适配器
|
|
941
|
+
|
|
942
|
+
平台配置文件:
|
|
943
|
+
|
|
944
|
+
| 配置文件 | 平台 |
|
|
945
|
+
|----------|------|
|
|
946
|
+
| \`opencode.jsonc\` | OpenCode(当前适配器) |
|
|
947
|
+
| \`kilo/kilo.json\` | Kilo |
|
|
948
|
+
|
|
949
|
+
若无平台配置文件,提示用户当前不在支持的平台中。
|
|
950
|
+
|
|
951
|
+
- agents 目录:\`.opencode/agents/\`(或对应平台的 agents/ 目录)下所有 \`.md\` 文件。
|
|
952
|
+
- 报告/模板中的平台标签:\`**平台**:OpenCode(当前适配器)\`(原文标签,正文已改为中性表述「当前 harness(opencode 适配器 / Claude Code 适配器)」)。
|
|
953
|
+
- 向用户提问使用 \`question\` 工具。
|
|
954
|
+
|
|
955
|
+
### Claude Code 适配器
|
|
956
|
+
|
|
957
|
+
- Claude Code **无平台配置文件**(原平台识别表中指向 Claude 的错误配置行已删除)。
|
|
958
|
+
- 全局约束:\`~/.claude/CLAUDE.md\`;本期由 **stage-73** 注入 \`settings.json\`。
|
|
959
|
+
- agents 目录:\`~/.claude/agents/\`。
|
|
960
|
+
- 报告平台标签:\`Claude Code 适配器\`。
|
|
961
|
+
- 向用户提问使用 \`AskUserQuestion\` 工具。
|
|
962
|
+
|
|
848
963
|
## 输出规范
|
|
849
964
|
|
|
850
965
|
- 状态图标:✅ 已满足、⚠️ 降级使用(可接受但非最优)、❌ 缺失或严重不匹配
|
|
@@ -853,13 +968,15 @@ models:
|
|
|
853
968
|
`,
|
|
854
969
|
'openfeel-model-config': `---
|
|
855
970
|
name: openfeel-model-config
|
|
856
|
-
description: 查找和配置 Agent 模型。当 Agent 报 "Model not found" 或需要调整/新增 Agent
|
|
971
|
+
description: 查找和配置 Agent 模型。当 Agent 报 "Model not found" 或需要调整/新增 Agent 模型时使用。覆盖模型配置位置与优先级链(opencode 适配器 / Claude Code 适配器)、模型名查找方法、多模态模型(openfeel-vision)特殊注意事项。
|
|
857
972
|
---
|
|
858
973
|
|
|
859
974
|
# Skill: openfeel-model-config
|
|
860
975
|
|
|
861
976
|
# Agent 模型查找与配置
|
|
862
977
|
|
|
978
|
+
> 正文以**能力名**叙述;平台差异见 \`### opencode 适配器\` / \`### Claude Code 适配器\` 两小节。
|
|
979
|
+
|
|
863
980
|
## 何时使用
|
|
864
981
|
|
|
865
982
|
- Agent 调用时报 \`Model not found: xxx\`
|
|
@@ -869,6 +986,8 @@ description: 查找和配置 Agent 模型。当 Agent 报 "Model not found" 或
|
|
|
869
986
|
|
|
870
987
|
## 配置位置
|
|
871
988
|
|
|
989
|
+
### opencode 适配器
|
|
990
|
+
|
|
872
991
|
Agent 模型配置在 **\`opencode.jsonc\`**(项目根目录)中:
|
|
873
992
|
|
|
874
993
|
\`\`\`jsonc
|
|
@@ -885,6 +1004,12 @@ Agent 模型配置在 **\`opencode.jsonc\`**(项目根目录)中:
|
|
|
885
1004
|
|
|
886
1005
|
> 💡 **推荐**:可用 \`openfeel model set/get/list --scope default|global|project\` 一键读写三层级模型(工具默认 / 全局 / 当前项目),无需手工定位文件。
|
|
887
1006
|
|
|
1007
|
+
### Claude Code 适配器
|
|
1008
|
+
|
|
1009
|
+
- Agent 模型配置在 Claude Code 侧的 \`~/.claude/agents/*.md\` frontmatter \`model\` 字段。
|
|
1010
|
+
- 模型配置经 \`/model\`、\`settings.json\`/\`env\` 或网关(如 cc-switch)等途径确定。
|
|
1011
|
+
- \`settings.json\`(本期由 stage-73 注入)仅作参考,模板默认 \`model: inherit\`。
|
|
1012
|
+
|
|
888
1013
|
## 查找可用模型名
|
|
889
1014
|
|
|
890
1015
|
当收到 \`"Model not found: xxx. Did you mean: aaa, bbb?"\` 错误时:
|
|
@@ -894,6 +1019,8 @@ Agent 模型配置在 **\`opencode.jsonc\`**(项目根目录)中:
|
|
|
894
1019
|
|
|
895
1020
|
## Agent 定义文件中的 model 字段
|
|
896
1021
|
|
|
1022
|
+
### opencode 适配器
|
|
1023
|
+
|
|
897
1024
|
\`.opencode/agents/<name>.md\` 的 frontmatter \`model:\` 字段**是生效的,且优先于** \`opencode.jsonc\` 的 \`agent.<name>.model\`(stage-40 REV-1606 实测)。opencode 模型解析优先级链:
|
|
898
1025
|
|
|
899
1026
|
\`\`\`
|
|
@@ -904,21 +1031,31 @@ Agent 模型配置在 **\`opencode.jsonc\`**(项目根目录)中:
|
|
|
904
1031
|
1. 修改 \`.opencode/agents/<name>.md\` 的 frontmatter \`model:\`(模板源 \`src/core/templates-data/opencode/agents/\` 同步)
|
|
905
1032
|
2. (可选,保持一致)同步修改 \`opencode.jsonc\` 的 \`agent.<name>.model\`
|
|
906
1033
|
|
|
1034
|
+
### Claude Code 适配器
|
|
1035
|
+
|
|
1036
|
+
Claude Code 模型解析优先级链:项目 \`.claude/agents\` > 用户 \`~/.claude/agents\`;frontmatter \`model\` **优先于** \`inherit\`。
|
|
1037
|
+
|
|
907
1038
|
## 多模态(openfeel-vision)模型特殊规则
|
|
908
1039
|
|
|
909
|
-
|
|
910
|
-
|
|
1040
|
+
### opencode 适配器
|
|
1041
|
+
|
|
1042
|
+
- 主会话(Feel)的主力模型(DeepSeek V4 Pro)不支持图片输入
|
|
1043
|
+
- 遇到图片输入时主会话会自动委托 openfeel-vision Agent
|
|
911
1044
|
- openfeel-vision Agent 需要配置多模态模型(现用 \`deepseek-flash\`,备选 \`qwen3-vl-plus\`)
|
|
912
1045
|
- 模型名不要随意添加前缀(如 \`alibaba/\`),以平台提示的可用名为准
|
|
913
1046
|
- **模型引用格式**:\`{auth.json中的key}/{模型ID}\`,不是 \`provider.name\` 也不是 \`provider.id\`
|
|
914
1047
|
- 读取 \`~/.local/share/opencode/auth.json\` 确认实际 provider key(常见:\`alibaba-cn\`、\`deepseek\`、\`zhipuai\`)
|
|
915
1048
|
- \`provider\` 块中的 \`name\` 和 \`id\` 仅用于显示,不影响模型解析
|
|
916
1049
|
|
|
1050
|
+
### Claude Code 适配器
|
|
1051
|
+
|
|
1052
|
+
- 多模态由 Claude 侧模型决定;\`openfeel-vision\` subagent 须选支持图像输入的模型。
|
|
1053
|
+
|
|
917
1054
|
## 项目 Agent 模型概览
|
|
918
1055
|
|
|
919
1056
|
| Agent | 模型类型 | 备注 |
|
|
920
1057
|
|-------|---------|------|
|
|
921
|
-
| Feel
|
|
1058
|
+
| 主会话(Feel) | 推理模型 | DeepSeek V4 Pro — 不支持多模态 |
|
|
922
1059
|
| openfeel-planner | 推理模型 | — |
|
|
923
1060
|
| openfeel-schemer | 推理模型 | — |
|
|
924
1061
|
| openfeel-executor | 快速模型 (Flash) | — |
|
|
@@ -1181,7 +1318,7 @@ description: Agent 工具使用规范(todowrite/question/task/skill 四工具
|
|
|
1181
1318
|
触发条件(任一必问):需求歧义 / 多个同等合理方案 / 不可逆后果 / 架构决策。使用要求:(Recommended) 标记、选项附后果、≤3 选项、高风险含"取消"。禁止:模糊时自行假设、多方案不选直接实施。
|
|
1182
1319
|
|
|
1183
1320
|
### 3. task — 子 Agent 调度
|
|
1184
|
-
触发条件:并行探索多代码区 / 复杂多步委托 general / 下游 Agent
|
|
1321
|
+
触发条件:并行探索多代码区 / 复杂多步委托 general / 下游 Agent(经主会话(Feel))。使用要求:并行一条消息多 task、prompt 含任务描述+期望返回、明确只读/可写。
|
|
1185
1322
|
|
|
1186
1323
|
### 4. skill — 技能加载
|
|
1187
1324
|
触发条件:查阶段状态→openfeel-get-stage-status / 查知识库→openfeel-check-kb / 取 Bug→openfeel-get-bugs。使用要求:会话开始载 check-kb、处理阶段任务前载 get-stage-status、不凭记忆跳过。
|
|
@@ -1194,6 +1331,23 @@ description: Agent 工具使用规范(todowrite/question/task/skill 四工具
|
|
|
1194
1331
|
| 探索代码 | task(explore) | 手动逐个 grep/read |
|
|
1195
1332
|
| 获取状态 | skill(openfeel-get-stage-status) | 凭记忆推断 |
|
|
1196
1333
|
| 批量文件操作 | task(general) | 串行逐个处理 |
|
|
1334
|
+
|
|
1335
|
+
## 工具名映射(跨 harness)
|
|
1336
|
+
|
|
1337
|
+
本 skill 的四种核心工具(\`todowrite\`/\`question\`/\`task\`/\`skill\`)在各 harness 的等价工具名见下表;**不改变**既有触发条件/使用要求/优先级表语义。
|
|
1338
|
+
|
|
1339
|
+
| opencode(小写) | Claude Code | 说明 |
|
|
1340
|
+
|------|------|------|
|
|
1341
|
+
| \`bash\` | \`Bash\` | 命令执行 |
|
|
1342
|
+
| \`read\` | \`Read\` | 读文件 |
|
|
1343
|
+
| \`write\` | \`Write\` | 写文件 |
|
|
1344
|
+
| \`edit\` | \`Edit\` | 编辑文件 |
|
|
1345
|
+
| \`glob\` | \`Glob\` | 文件匹配 |
|
|
1346
|
+
| \`grep\` | \`Grep\` | 内容检索 |
|
|
1347
|
+
| \`todowrite\` | \`TodoWrite\` | 任务列表 |
|
|
1348
|
+
| \`skill\` | \`Skill\` | 技能加载 |
|
|
1349
|
+
| \`task\` | \`Task\` | 子 Agent 调度 |
|
|
1350
|
+
| \`question\` | \`AskUserQuestion\` | 向用户提问 |
|
|
1197
1351
|
`,
|
|
1198
1352
|
'openfeel-update-stage-status': `---
|
|
1199
1353
|
name: openfeel-update-stage-status
|
|
@@ -1371,9 +1525,9 @@ description: 会话启动时检查并补齐 .openfeel/ 工作区目录结构与
|
|
|
1371
1525
|
\`.openfeel/users/{username}/dev_last.md\`(**索引**;同名目录 \`dev_last/\` 存主题文件,**文件名英文**)
|
|
1372
1526
|
|
|
1373
1527
|
### 6. 增量更新复核
|
|
1374
|
-
检查 \`~/.openfeel/update_infos.md
|
|
1528
|
+
检查 \`~/.openfeel/update_infos.md\`,若存在未修复条目(追加/异常),提醒用户重启会话或委托主会话(Feel)处理;本 Agent 不自行修改该文件。
|
|
1375
1529
|
|
|
1376
|
-
>
|
|
1530
|
+
> 目录结构语义、公共/私域分区、用户身份约束、路径自校验规则见全局规则文件(opencode 适配器:\`~/.config/opencode/AGENTS.md\`;Claude Code 适配器:\`~/.claude/CLAUDE.md\`)(约束);本 skill 仅承载操作步骤。
|
|
1377
1531
|
`,
|
|
1378
1532
|
};
|
|
1379
1533
|
// AUTO-GENERATED-END: SKILL_DEFINITIONS
|
|
@@ -1522,8 +1676,10 @@ export function updateProject(projectPath, selectedTools = ["opencode"], lang =
|
|
|
1522
1676
|
const state = loadUpdateState(projectPath);
|
|
1523
1677
|
const globalState = loadGlobalUpdateState();
|
|
1524
1678
|
const conflicts = [];
|
|
1525
|
-
//
|
|
1526
|
-
|
|
1679
|
+
// 过滤:仅处理选中的工具(两工具都未选才早退)
|
|
1680
|
+
const wantOpencode = selectedTools.includes('opencode');
|
|
1681
|
+
const wantClaude = selectedTools.includes('claude');
|
|
1682
|
+
if (!wantOpencode && !wantClaude) {
|
|
1527
1683
|
return { created: [], updated: [], skipped: [], conflicts: [], appended: [] };
|
|
1528
1684
|
}
|
|
1529
1685
|
// N8:检测项目内遗留 .opencode/{agents,skills,instructions},只提示不迁移
|
|
@@ -1532,12 +1688,6 @@ export function updateProject(projectPath, selectedTools = ["opencode"], lang =
|
|
|
1532
1688
|
if (legacyDirs.length > 0) {
|
|
1533
1689
|
console.warn(t('update.legacyLayoutWarnTmpl', getCliLang(projectPath), { dirs: legacyDirs.join(',') }));
|
|
1534
1690
|
}
|
|
1535
|
-
// 全局部署目标(D1)
|
|
1536
|
-
const agentsDir = getGlobalAgentsDir();
|
|
1537
|
-
const skillsDir = getGlobalSkillsDir();
|
|
1538
|
-
// 确保全局目标目录存在
|
|
1539
|
-
mkdirSync(agentsDir, { recursive: true });
|
|
1540
|
-
mkdirSync(skillsDir, { recursive: true });
|
|
1541
1691
|
// 记录项目语言映射到全局配置
|
|
1542
1692
|
// 使用 options?.lang(用户通过 --lang 显式指定的语言)优先,
|
|
1543
1693
|
// 回退到 lang(getCliLang 三级回退结果)。确保记录的语言与实际部署语言一致。
|
|
@@ -1557,85 +1707,132 @@ export function updateProject(projectPath, selectedTools = ["opencode"], lang =
|
|
|
1557
1707
|
globalState.files[getGlobalAgentsMdPath()] = globalState.files[staleCoreKey];
|
|
1558
1708
|
delete globalState.files[staleCoreKey];
|
|
1559
1709
|
}
|
|
1560
|
-
//
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
const
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
const
|
|
1570
|
-
const
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
const
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
notifyBackupIfTTY(globalJsoncBackup.backupRel);
|
|
1710
|
+
// opencode 分支(步骤 0~4)——原样保留
|
|
1711
|
+
if (wantOpencode) {
|
|
1712
|
+
// 全局部署目标(D1)
|
|
1713
|
+
const agentsDir = getGlobalAgentsDir();
|
|
1714
|
+
const skillsDir = getGlobalSkillsDir();
|
|
1715
|
+
// 确保全局目标目录存在
|
|
1716
|
+
mkdirSync(agentsDir, { recursive: true });
|
|
1717
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
1718
|
+
// 0. 生成全局框架约束 AGENTS.md(~/.config/opencode/AGENTS.md)
|
|
1719
|
+
const agentsMdPath = getGlobalAgentsMdPath();
|
|
1720
|
+
const agentsMdContent = loadTemplate(lang, 'agents-md');
|
|
1721
|
+
{
|
|
1722
|
+
const action = deployGlobalAsset(agentsMdPath, agentsMdContent, newGlobalState, 'update');
|
|
1723
|
+
pushAction(action, agentsMdPath, created, updated, skipped, appended);
|
|
1724
|
+
}
|
|
1725
|
+
// 1. 生成 Agent 定义文件到全局 agents 目录(绝对路径作 state key)
|
|
1726
|
+
for (const name of listAgentIds(lang)) {
|
|
1727
|
+
const content = loadAgentTemplate(lang, name);
|
|
1728
|
+
const filePath = join(agentsDir, `${name}.md`);
|
|
1729
|
+
const action = deployGlobalAsset(filePath, content, newGlobalState, 'update');
|
|
1730
|
+
pushAction(action, filePath, created, updated, skipped, appended);
|
|
1731
|
+
}
|
|
1732
|
+
// 2. 生成 Skill 定义文件到全局 skills 目录(每个 Skill 一个子目录,包含 SKILL.md)
|
|
1733
|
+
for (const [name, content] of Object.entries(SKILL_DEFINITIONS)) {
|
|
1734
|
+
const skillSubDir = join(skillsDir, name);
|
|
1735
|
+
mkdirSync(skillSubDir, { recursive: true });
|
|
1736
|
+
const filePath = join(skillSubDir, 'SKILL.md');
|
|
1737
|
+
const action = deployGlobalAsset(filePath, content, newGlobalState, 'update');
|
|
1738
|
+
pushAction(action, filePath, created, updated, skipped, appended);
|
|
1590
1739
|
}
|
|
1591
|
-
//
|
|
1592
|
-
const
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1740
|
+
// 3. 全局 opencode.jsonc:深度合并(保留用户字段),加全局锁 + 原子写
|
|
1741
|
+
const globalJsoncPath = getGlobalOpencodeJsoncPath();
|
|
1742
|
+
try {
|
|
1743
|
+
// 写前备份(B3):备份须在 jsonc 锁之外(之前)完成,避免 backup 锁与 jsonc 锁嵌套
|
|
1744
|
+
const globalJsoncBackup = backupFileBeforeWrite(globalJsoncPath, { command: 'update' });
|
|
1745
|
+
if (globalJsoncBackup) {
|
|
1746
|
+
appendUpdateInfo('backed', { absolutePath: globalJsoncPath, backupRel: globalJsoncBackup.backupRel, command: 'update' });
|
|
1747
|
+
notifyBackupIfTTY(globalJsoncBackup.backupRel);
|
|
1597
1748
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1749
|
+
// REV-1805:read-merge-write 全部置于锁内,消除与 model-config 锁内读写的 TOCTOU 竞态
|
|
1750
|
+
const globalJsoncNew = withFileLock(globalLockPath('global-opencode-jsonc'), () => {
|
|
1751
|
+
// 安全读取(T50):目录占位/不可读 → null,跳过合并与写入,按空配置继续
|
|
1752
|
+
const current = readJsoncFile(globalJsoncPath);
|
|
1753
|
+
if (current === null) {
|
|
1754
|
+
return null;
|
|
1755
|
+
}
|
|
1756
|
+
let merged;
|
|
1757
|
+
// 合并可能因块注释 parse 失败抛异常(parseJsonc 边界):降级为「跳过合并、保留原文件」并告警
|
|
1758
|
+
try {
|
|
1759
|
+
merged = mergeGlobalOpencodeJsonc(current);
|
|
1760
|
+
}
|
|
1761
|
+
catch (err) {
|
|
1762
|
+
console.warn(t('update.globalJsoncParseSkipTmpl', getCliLang(projectPath), { err: err.message }));
|
|
1763
|
+
merged = current;
|
|
1764
|
+
}
|
|
1765
|
+
atomicWriteFileSync(globalJsoncPath, merged);
|
|
1766
|
+
return merged;
|
|
1767
|
+
});
|
|
1768
|
+
if (globalJsoncNew !== null) {
|
|
1769
|
+
updateFileHash(newGlobalState, globalJsoncPath, globalJsoncNew);
|
|
1602
1770
|
}
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1771
|
+
}
|
|
1772
|
+
catch (err) {
|
|
1773
|
+
if (err instanceof BackupError) {
|
|
1774
|
+
// 备份失败(REV-011-A):跳过全局 jsonc 写入、记 anomaly、继续其余步骤(对齐 B3 幂等语义)
|
|
1775
|
+
appendUpdateInfo('anomaly', { absolutePath: globalJsoncPath, note: 'backup_failed' });
|
|
1776
|
+
console.warn(t('update.globalJsoncWriteSkipTmpl', getCliLang(projectPath), { err: err.message }));
|
|
1777
|
+
}
|
|
1778
|
+
else {
|
|
1779
|
+
throw err; // 非备份错误照旧上抛(锁超时等不回退)
|
|
1606
1780
|
}
|
|
1607
|
-
atomicWriteFileSync(globalJsoncPath, merged);
|
|
1608
|
-
return merged;
|
|
1609
|
-
});
|
|
1610
|
-
if (globalJsoncNew !== null) {
|
|
1611
|
-
updateFileHash(newGlobalState, globalJsoncPath, globalJsoncNew);
|
|
1612
1781
|
}
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
if (
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
console.warn(t('update.globalJsoncWriteSkipTmpl', getCliLang(projectPath), { err: err.message }));
|
|
1782
|
+
// 4. 项目 opencode.jsonc:不存在则写最小 { $schema };已存在则保留(旧非法字段清理属 stage-39)
|
|
1783
|
+
const projectJsoncPath = resolve(projectPath, 'opencode.jsonc');
|
|
1784
|
+
if (!existsSync(projectJsoncPath)) {
|
|
1785
|
+
atomicWriteFileSync(projectJsoncPath, JSON.stringify(buildProjectOpencodeJsoncObj(), null, 2) + '\n');
|
|
1786
|
+
created.push('opencode.jsonc');
|
|
1619
1787
|
}
|
|
1620
1788
|
else {
|
|
1621
|
-
|
|
1789
|
+
skipped.push('opencode.jsonc (use existing)');
|
|
1622
1790
|
}
|
|
1623
1791
|
}
|
|
1624
|
-
//
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1792
|
+
// claude 分支:CLAUDE.md + 9 agent + 17 共享 skill + settings.json(stage-73)
|
|
1793
|
+
if (wantClaude) {
|
|
1794
|
+
const claudeAgentsDir = getClaudeAgentsDir();
|
|
1795
|
+
const claudeSkillsDir = getClaudeSkillsDir();
|
|
1796
|
+
mkdirSync(claudeAgentsDir, { recursive: true });
|
|
1797
|
+
mkdirSync(claudeSkillsDir, { recursive: true });
|
|
1798
|
+
// 1c. CLAUDE.md(受管区三态复用 deployGlobalAsset;用户已有 → append 不覆盖)
|
|
1799
|
+
const claudeMd = getClaudeMdPath();
|
|
1800
|
+
pushAction(deployGlobalAsset(claudeMd, loadClaudeConstraintTemplate(lang), newGlobalState, 'update'), claudeMd, created, updated, skipped, appended);
|
|
1801
|
+
// 2c. 9 agents(含 feel)
|
|
1802
|
+
for (const name of listClaudeAgentIds(lang)) {
|
|
1803
|
+
const p = join(claudeAgentsDir, `${name}.md`);
|
|
1804
|
+
pushAction(deployGlobalAsset(p, loadClaudeAgentTemplate(lang, name), newGlobalState, 'update'), p, created, updated, skipped, appended);
|
|
1805
|
+
}
|
|
1806
|
+
// 3c. 17 共享 skills
|
|
1807
|
+
for (const [name, content] of Object.entries(SKILL_DEFINITIONS)) {
|
|
1808
|
+
const dir = join(claudeSkillsDir, name);
|
|
1809
|
+
mkdirSync(dir, { recursive: true });
|
|
1810
|
+
const p = join(dir, 'SKILL.md');
|
|
1811
|
+
pushAction(deployGlobalAsset(p, content, newGlobalState, 'update'), p, created, updated, skipped, appended);
|
|
1812
|
+
}
|
|
1813
|
+
// 4c. settings.json(与 setup 对称,共享 applyClaudeSettings,杜绝双实现漂移)
|
|
1814
|
+
const settingsPath = getClaudeSettingsPath();
|
|
1815
|
+
pushAction(applyClaudeSettings(newGlobalState, 'update', getCliLang(projectPath)), settingsPath, created, updated, skipped, appended);
|
|
1632
1816
|
}
|
|
1633
|
-
//
|
|
1634
|
-
if (
|
|
1817
|
+
// 重启提醒(按 harness 组合;isTTY 守卫、非交互静默)
|
|
1818
|
+
if (process.stdout.isTTY) {
|
|
1635
1819
|
const reminderLang = getCliLang(projectPath);
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1820
|
+
const opencodeChanged = updated.some((f) => f.startsWith(getGlobalAgentsDir()));
|
|
1821
|
+
const claudeChanged = [...created, ...updated, ...appended].some((f) => f.startsWith(getClaudeAgentsDir()) || f.startsWith(getClaudeSkillsDir())
|
|
1822
|
+
|| f === getClaudeMdPath() || f === getClaudeSettingsPath());
|
|
1823
|
+
const harnesses = [];
|
|
1824
|
+
if (opencodeChanged) {
|
|
1825
|
+
harnesses.push('opencode');
|
|
1826
|
+
}
|
|
1827
|
+
if (claudeChanged) {
|
|
1828
|
+
harnesses.push('Claude Code');
|
|
1829
|
+
}
|
|
1830
|
+
if (harnesses.length > 0) {
|
|
1831
|
+
console.log(t('update.restartHarnessTmpl', reminderLang, { harness: harnesses.join(' / ') }));
|
|
1832
|
+
if (claudeChanged) {
|
|
1833
|
+
console.log(t('update.claudeReloadHintTmpl', reminderLang));
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1639
1836
|
}
|
|
1640
1837
|
// ── 写入冲突标记文件 ──
|
|
1641
1838
|
if (conflicts.length > 0) {
|
|
@@ -1692,6 +1889,8 @@ export function updateProject(projectPath, selectedTools = ["opencode"], lang =
|
|
|
1692
1889
|
// 更新时间戳
|
|
1693
1890
|
newState.last_update = new Date().toISOString();
|
|
1694
1891
|
newGlobalState.last_update = new Date().toISOString();
|
|
1892
|
+
// stage-66/D-A:部署完成即刷新「已部署版本」事实源(既有 state 与首次创建均覆盖)
|
|
1893
|
+
newGlobalState.openfeel_version = getOpenfeelVersion();
|
|
1695
1894
|
// 持久化双 state
|
|
1696
1895
|
saveUpdateState(projectPath, newState);
|
|
1697
1896
|
saveGlobalUpdateState(newGlobalState);
|
|
@@ -1741,6 +1940,18 @@ function getIncomingContent(relativePath, lang) {
|
|
|
1741
1940
|
const name = basename(dirname(relativePath));
|
|
1742
1941
|
return SKILL_DEFINITIONS[name] ?? '';
|
|
1743
1942
|
}
|
|
1943
|
+
// Claude 全局约束 CLAUDE.md
|
|
1944
|
+
if (relativePath === getClaudeMdPath()) {
|
|
1945
|
+
return loadClaudeConstraintTemplate(lang);
|
|
1946
|
+
}
|
|
1947
|
+
// Claude agent:位于 getClaudeAgentsDir() 下的 {name}.md
|
|
1948
|
+
if (relativePath.startsWith(getClaudeAgentsDir())) {
|
|
1949
|
+
return loadClaudeAgentTemplate(lang, basename(relativePath, '.md'));
|
|
1950
|
+
}
|
|
1951
|
+
// Claude skill:位于 getClaudeSkillsDir() 下的 {name}/SKILL.md
|
|
1952
|
+
if (relativePath.startsWith(getClaudeSkillsDir()) && basename(relativePath) === 'SKILL.md') {
|
|
1953
|
+
return SKILL_DEFINITIONS[basename(dirname(relativePath))] ?? '';
|
|
1954
|
+
}
|
|
1744
1955
|
// 项目 opencode.jsonc
|
|
1745
1956
|
if (relativePath === 'opencode.jsonc') {
|
|
1746
1957
|
return JSON.stringify(buildProjectOpencodeJsoncObj(), null, 2) + '\n';
|