@andyqiu/codeforge 0.3.10 → 0.3.11

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/install.sh CHANGED
@@ -553,6 +553,14 @@ else
553
553
  ok "项目级安装,跳过用户级 default_agent 配置(要装请加 --global)"
554
554
  fi
555
555
 
556
+ # 如果是 CodeForge 开发仓库本身,重新生成 dev shim(install 会清掉 .opencode/plugins/)
557
+ # ADR-0055:本仓库内 opencode 加载本项目 dist/ 而非全局 stable,依赖 dev shim 让位
558
+ if [ -f ".codeforge/.dev-marker" ] && [ -f "scripts/dev-sync.mjs" ]; then
559
+ node scripts/dev-sync.mjs --no-build 2>/dev/null || true
560
+ echo " ↳ dev shim regenerated (.opencode/plugins/codeforge-dev.js)"
561
+ fi
562
+
563
+
556
564
  # 验证清单
557
565
  hr
558
566
  ok "${C_BOLD}CodeForge v${CF_VERSION} 安装完成${C_RESET}"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andyqiu/codeforge",
3
- "version": "0.3.10",
3
+ "version": "0.3.11",
4
4
  "description": "CodeForge — opencode 的零侵入扩展包",
5
5
  "type": "module",
6
6
  "private": false,
@@ -1,224 +1,230 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "$id": "https://codeforge/schemas/codeforge.schema.json",
4
- "title": "CodeForge Configuration",
5
- "description": "Single source of truth for CodeForge: agent ↔ model mapping + global runtime preferences.",
6
- "type": "object",
7
- "additionalProperties": true,
8
- "properties": {
9
- "$schema": { "type": "string" },
10
- "_doc": { "type": "string" },
11
-
12
- "models": {
13
- "type": "object",
14
- "description": "Agent ↔ model bindings + reusable categories + runtime fallback policy. Synced to agents/*.md frontmatter via `npm run sync:models`.",
15
- "required": ["agents"],
16
- "additionalProperties": true,
17
- "properties": {
18
- "_doc": { "type": "string" },
19
- "default": {
20
- "$ref": "#/$defs/ProviderModelId",
21
- "description": "Fallback for any agent that omits `model` and is not bound by category."
22
- },
23
- "agents": {
24
- "type": "object",
25
- "description": "Per-agent model binding. Key = agent name (matches agents/<name>.md).",
26
- "patternProperties": {
27
- "^[a-z][a-z0-9-]*$": { "$ref": "#/$defs/AgentBinding" }
28
- },
29
- "additionalProperties": false,
30
- "minProperties": 1
31
- },
32
- "categories": {
33
- "type": "object",
34
- "description": "Reusable model presets shared by multiple agents.",
35
- "patternProperties": {
36
- "^[a-z][a-z0-9-]*$": { "$ref": "#/$defs/CategoryBinding" }
37
- },
38
- "additionalProperties": false
39
- },
40
- "runtime_fallback": {
41
- "type": "object",
42
- "description": "Behavior of plugins/model-fallback.ts when primary model fails.",
43
- "additionalProperties": true,
44
- "properties": {
45
- "enabled": { "type": "boolean", "default": true },
46
- "max_fallback_attempts": { "type": "integer", "minimum": 1, "default": 3 },
47
- "notify_on_fallback": { "type": "boolean", "default": true },
48
- "trigger_events": {
49
- "type": "array",
50
- "items": { "type": "string" },
51
- "default": ["session.error", "model.unavailable", "model.rate_limited"]
52
- },
53
- "_doc": { "type": "string" }
54
- }
55
- }
56
- }
57
- },
58
-
59
- "autonomy": {
60
- "type": "object",
61
- "description": "三档自主度默认(AGENTS.md C18)。命令行 --mode 优先生效。",
62
- "additionalProperties": false,
63
- "properties": {
64
- "_doc": { "type": "string" },
65
- "default_mode": {
66
- "type": "string",
67
- "enum": ["step", "semi", "full"],
68
- "default": "semi",
69
- "description": "step=每步确认 / semi=危险工具确认 / full=全自动(强制 worktree)"
70
- },
71
- "downgrade_on_risky": {
72
- "type": "boolean",
73
- "default": true,
74
- "description": "检测到破坏性命令是否自动降级到 semi(不可关)"
75
- }
76
- }
77
- },
78
-
79
- "runtime": {
80
- "type": "object",
81
- "description": "全局运行时安全开关(v1 占位;写入但 plugin 默认仍为 hardcoded 安全值,下个版本接入)",
82
- "additionalProperties": false,
83
- "properties": {
84
- "_doc": { "type": "string" },
85
- "worktree_isolation": {
86
- "type": "boolean",
87
- "default": true,
88
- "description": "subtasks 在独立 worktree 跑(C22)。v1:subtasks plugin 已默认开,此字段暂未读取。"
89
- },
90
- "workspace_snapshot": {
91
- "type": "boolean",
92
- "default": true,
93
- "description": "每步前对工作区做 shadow git 快照。v1:plugin 已默认开,此字段暂未读取。"
94
- },
95
- "auto_commit": {
96
- "type": "boolean",
97
- "default": false,
98
- "description": "step 完成后自动 commit(C23)。v1:plugin 已默认开,此字段暂未读取。"
99
- }
100
- }
101
- },
102
-
103
- "tools": {
104
- "type": "object",
105
- "description": "工具开关(已接 plugins/codeforge-tools.ts)",
106
- "additionalProperties": false,
107
- "properties": {
108
- "_doc": { "type": "string" },
109
- "browser": {
110
- "type": "object",
111
- "additionalProperties": false,
112
- "properties": {
113
- "enabled": {
114
- "type": "boolean",
115
- "default": false,
116
- "description": "Browser Control 工具开关(AGENTS.md C20 默认禁用)。开启会注册 6 个 browser_* 工具。"
117
- }
118
- }
119
- }
120
- }
121
- },
122
-
123
- "context": {
124
- "type": "object",
125
- "description": "Implicit Reasoning + Condenser(已接 plugins/token-manager.ts)",
126
- "additionalProperties": false,
127
- "properties": {
128
- "_doc": { "type": "string" },
129
- "condenser_threshold_ratio": {
130
- "type": "number",
131
- "minimum": 0.1,
132
- "maximum": 0.95,
133
- "default": 0.7,
134
- "description": "Token 使用率超过此比例自动压缩上下文。"
135
- }
136
- }
137
- },
138
-
139
- "update": {
140
- "type": "object",
141
- "description": "自动更新检查(已接 plugins/update-checker.ts)。仿 oh-my-opencode:opencode 启动时后台拉 GitHub Releases,发现新版只通知不自动重装。",
142
- "additionalProperties": false,
143
- "properties": {
144
- "_doc": { "type": "string" },
145
- "auto_check_enabled": {
146
- "type": "boolean",
147
- "default": true,
148
- "description": "是否启用启动时自动检查"
149
- },
150
- "interval_hours": {
151
- "type": "integer",
152
- "minimum": 1,
153
- "maximum": 720,
154
- "default": 24,
155
- "description": "两次检查之间的最短间隔(小时)。命中缓存就不打 GitHub API。"
156
- },
157
- "repo": {
158
- "type": "string",
159
- "pattern": "^[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+$",
160
- "default": "andy-personal/code-forge",
161
- "description": "GitHub 仓库 <owner>/<repo>,私仓需要 GITHUB_TOKEN 环境变量"
162
- },
163
- "channel": {
164
- "type": "string",
165
- "enum": ["stable", "prerelease"],
166
- "default": "stable",
167
- "description": "stable=只看 latest release;prerelease=允许 pre-release(v1 仅声明,未实现)"
168
- }
169
- }
170
- },
171
-
172
- },
173
-
174
- "$defs": {
175
- "ProviderModelId": {
176
- "type": "string",
177
- "pattern": "^[a-z0-9-]+/[a-zA-Z0-9._-]+$",
178
- "description": "Format: <provider>/<model-id>, e.g. anthropic/claude-opus-4-7"
179
- },
180
- "Thinking": {
181
- "type": "object",
182
- "additionalProperties": true,
183
- "properties": {
184
- "type": { "type": "string", "enum": ["enabled", "disabled"] },
185
- "budget_tokens": { "type": "integer", "minimum": 0 }
186
- }
187
- },
188
- "AgentBinding": {
189
- "type": "object",
190
- "required": ["model"],
191
- "additionalProperties": false,
192
- "properties": {
193
- "model": { "$ref": "#/$defs/ProviderModelId" },
194
- "variant": { "type": "string" },
195
- "category": { "type": "string" },
196
- "thinking": { "$ref": "#/$defs/Thinking" },
197
- "fallback_models": {
198
- "type": "array",
199
- "items": { "$ref": "#/$defs/ProviderModelId" },
200
- "uniqueItems": true,
201
- "default": []
202
- },
203
- "_doc": { "type": "string" }
204
- }
205
- },
206
- "CategoryBinding": {
207
- "type": "object",
208
- "required": ["model"],
209
- "additionalProperties": false,
210
- "properties": {
211
- "model": { "$ref": "#/$defs/ProviderModelId" },
212
- "variant": { "type": "string" },
213
- "thinking": { "$ref": "#/$defs/Thinking" },
214
- "fallback_models": {
215
- "type": "array",
216
- "items": { "$ref": "#/$defs/ProviderModelId" },
217
- "uniqueItems": true,
218
- "default": []
219
- },
220
- "_doc": { "type": "string" }
221
- }
222
- }
223
- }
224
- }
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://codeforge/schemas/codeforge.schema.json",
4
+ "title": "CodeForge Configuration",
5
+ "description": "Single source of truth for CodeForge: agent ↔ model mapping + global runtime preferences.",
6
+ "type": "object",
7
+ "additionalProperties": true,
8
+ "properties": {
9
+ "$schema": { "type": "string" },
10
+ "_doc": { "type": "string" },
11
+
12
+ "models": {
13
+ "type": "object",
14
+ "description": "Agent ↔ model bindings + reusable categories + runtime fallback policy. Synced to agents/*.md frontmatter via `npm run sync:models`.",
15
+ "required": ["agents"],
16
+ "additionalProperties": true,
17
+ "properties": {
18
+ "_doc": { "type": "string" },
19
+ "default": {
20
+ "$ref": "#/$defs/ProviderModelId",
21
+ "description": "Fallback for any agent that omits `model` and is not bound by category."
22
+ },
23
+ "agents": {
24
+ "type": "object",
25
+ "description": "Per-agent model binding. Key = agent name (matches agents/<name>.md).",
26
+ "patternProperties": {
27
+ "^[a-z][a-z0-9-]*$": { "$ref": "#/$defs/AgentBinding" }
28
+ },
29
+ "additionalProperties": false,
30
+ "minProperties": 1
31
+ },
32
+ "categories": {
33
+ "type": "object",
34
+ "description": "Reusable model presets shared by multiple agents.",
35
+ "patternProperties": {
36
+ "^[a-z][a-z0-9-]*$": { "$ref": "#/$defs/CategoryBinding" }
37
+ },
38
+ "additionalProperties": false
39
+ },
40
+ "runtime_fallback": {
41
+ "type": "object",
42
+ "description": "Behavior of plugins/model-fallback.ts when primary model fails.",
43
+ "additionalProperties": true,
44
+ "properties": {
45
+ "enabled": { "type": "boolean", "default": true },
46
+ "max_fallback_attempts": { "type": "integer", "minimum": 1, "default": 3 },
47
+ "notify_on_fallback": { "type": "boolean", "default": true },
48
+ "trigger_events": {
49
+ "type": "array",
50
+ "items": { "type": "string" },
51
+ "default": ["session.error", "model.unavailable", "model.rate_limited"]
52
+ },
53
+ "_doc": { "type": "string" }
54
+ }
55
+ }
56
+ }
57
+ },
58
+
59
+ "autonomy": {
60
+ "type": "object",
61
+ "description": "三档自主度默认(AGENTS.md C18)。命令行 --mode 优先生效。",
62
+ "additionalProperties": false,
63
+ "properties": {
64
+ "_doc": { "type": "string" },
65
+ "default_mode": {
66
+ "type": "string",
67
+ "enum": ["step", "semi", "full"],
68
+ "default": "semi",
69
+ "description": "step=每步确认 / semi=危险工具确认 / full=全自动(强制 worktree)"
70
+ },
71
+ "downgrade_on_risky": {
72
+ "type": "boolean",
73
+ "default": true,
74
+ "description": "检测到破坏性命令是否自动降级到 semi(不可关)"
75
+ }
76
+ }
77
+ },
78
+
79
+ "runtime": {
80
+ "type": "object",
81
+ "description": "全局运行时安全开关(v1 占位;写入但 plugin 默认仍为 hardcoded 安全值,下个版本接入)",
82
+ "additionalProperties": false,
83
+ "properties": {
84
+ "_doc": { "type": "string" },
85
+ "worktree_isolation": {
86
+ "type": "boolean",
87
+ "default": true,
88
+ "description": "subtasks 在独立 worktree 跑(C22)。v1:subtasks plugin 已默认开,此字段暂未读取。"
89
+ },
90
+ "workspace_snapshot": {
91
+ "type": "boolean",
92
+ "default": true,
93
+ "description": "每步前对工作区做 shadow git 快照。v1:plugin 已默认开,此字段暂未读取。"
94
+ },
95
+ "auto_commit": {
96
+ "type": "boolean",
97
+ "default": false,
98
+ "description": "step 完成后自动 commit(C23)。v1:plugin 已默认开,此字段暂未读取。"
99
+ }
100
+ }
101
+ },
102
+
103
+ "tools": {
104
+ "type": "object",
105
+ "description": "工具开关(已接 plugins/codeforge-tools.ts)",
106
+ "additionalProperties": false,
107
+ "properties": {
108
+ "_doc": { "type": "string" },
109
+ "browser": {
110
+ "type": "object",
111
+ "additionalProperties": false,
112
+ "properties": {
113
+ "enabled": {
114
+ "type": "boolean",
115
+ "default": false,
116
+ "description": "Browser Control 工具开关(AGENTS.md C20 默认禁用)。开启会注册 6 个 browser_* 工具。"
117
+ }
118
+ }
119
+ }
120
+ }
121
+ },
122
+
123
+ "context": {
124
+ "type": "object",
125
+ "description": "Implicit Reasoning + Condenser(已接 plugins/token-manager.ts)",
126
+ "additionalProperties": false,
127
+ "properties": {
128
+ "_doc": { "type": "string" },
129
+ "condenser_threshold_ratio": {
130
+ "type": "number",
131
+ "minimum": 0.1,
132
+ "maximum": 0.95,
133
+ "default": 0.7,
134
+ "description": "Token 使用率超过此比例自动压缩上下文。"
135
+ }
136
+ }
137
+ },
138
+
139
+ "update": {
140
+ "type": "object",
141
+ "description": "自动更新检查(已接 plugins/update-checker.ts)。仿 oh-my-opencode:opencode 启动时后台拉 GitHub Releases,发现新版只通知不自动重装。",
142
+ "additionalProperties": false,
143
+ "properties": {
144
+ "_doc": { "type": "string" },
145
+ "auto_check_enabled": {
146
+ "type": "boolean",
147
+ "default": true,
148
+ "description": "是否启用启动时自动检查"
149
+ },
150
+ "interval_hours": {
151
+ "type": "integer",
152
+ "minimum": 1,
153
+ "maximum": 720,
154
+ "default": 24,
155
+ "description": "两次检查之间的最短间隔(小时)。命中缓存就不打 GitHub API。"
156
+ },
157
+ "repo": {
158
+ "type": "string",
159
+ "pattern": "^[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+$",
160
+ "default": "andy-personal/code-forge",
161
+ "description": "GitHub 仓库 <owner>/<repo>,私仓需要 GITHUB_TOKEN 环境变量"
162
+ },
163
+ "channel": {
164
+ "type": "string",
165
+ "enum": ["stable", "prerelease"],
166
+ "default": "stable",
167
+ "description": "stable=只看 latest release;prerelease=允许 pre-release(v1 仅声明,未实现)"
168
+ }
169
+ }
170
+ },
171
+
172
+ },
173
+
174
+ "$defs": {
175
+ "ProviderModelId": {
176
+ "type": "string",
177
+ "pattern": "^[a-z0-9-]+/[a-zA-Z0-9._-]+$",
178
+ "description": "Format: <provider>/<model-id>, e.g. anthropic/claude-opus-4-7"
179
+ },
180
+ "Thinking": {
181
+ "type": "object",
182
+ "additionalProperties": true,
183
+ "properties": {
184
+ "type": { "type": "string", "enum": ["enabled", "disabled"] },
185
+ "budget_tokens": { "type": "integer", "minimum": 0 }
186
+ }
187
+ },
188
+ "AgentBinding": {
189
+ "type": "object",
190
+ "required": ["model"],
191
+ "additionalProperties": false,
192
+ "properties": {
193
+ "model": { "$ref": "#/$defs/ProviderModelId" },
194
+ "variant": { "type": "string" },
195
+ "category": { "type": "string" },
196
+ "tier": {
197
+ "type": "string",
198
+ "enum": ["quick", "balanced", "deep", "ultra"],
199
+ "description": "任务难度档位,与 lib/model-tier.ts 的 TierLevel 对应"
200
+ },
201
+
202
+ "thinking": { "$ref": "#/$defs/Thinking" },
203
+ "fallback_models": {
204
+ "type": "array",
205
+ "items": { "$ref": "#/$defs/ProviderModelId" },
206
+ "uniqueItems": true,
207
+ "default": []
208
+ },
209
+ "_doc": { "type": "string" }
210
+ }
211
+ },
212
+ "CategoryBinding": {
213
+ "type": "object",
214
+ "required": ["model"],
215
+ "additionalProperties": false,
216
+ "properties": {
217
+ "model": { "$ref": "#/$defs/ProviderModelId" },
218
+ "variant": { "type": "string" },
219
+ "thinking": { "$ref": "#/$defs/Thinking" },
220
+ "fallback_models": {
221
+ "type": "array",
222
+ "items": { "$ref": "#/$defs/ProviderModelId" },
223
+ "uniqueItems": true,
224
+ "default": []
225
+ },
226
+ "_doc": { "type": "string" }
227
+ }
228
+ }
229
+ }
230
+ }
@@ -7,12 +7,19 @@
7
7
  * 行为:
8
8
  * - 对每个 agent:
9
9
  * * 找不到 agents/<name>.md → 报警告(不创建)
10
- * * 找到 → 在 frontmatter 中写入/更新:model / fallback_models / category / thinking
10
+ * * 找到 → 在 frontmatter 中写入/更新:model / fallback_models / category / thinking / tier
11
11
  * * 删除老字段 preferred_model(统一改名为 model,对齐 opencode 标准)
12
12
  * - --check 模式:只校验,不写盘;任何漂移则 exit(1)
13
13
  * - --dry 模式:打印会改什么,不写盘
14
14
  *
15
- * 设计原则:保留 frontmatter 中其他字段不动,仅替换四个目标 key。
15
+ * 设计原则:保留 frontmatter 中其他字段不动,仅替换目标 key。
16
+ *
17
+ * 关于 tier:
18
+ * - tier 是 ADR-0060 模型难度分级字段(quick / balanced / deep / ultra)
19
+ * - 变体 agent(名字含 -quick / -deep / -ultra 后缀)不在 codeforge.json 的 agents 里,
20
+ * 本脚本只同步 base agent;变体由人工维护或后续 sync 脚本扩展生成。
21
+ * - 当 codeforge.json 未声明 tier 时,保留 .md 中现有 tier 值(不删),
22
+ * 避免变体 agent 的 tier 在 sync 时被静默清除。
16
23
  */
17
24
 
18
25
  import { promises as fs } from "node:fs"
@@ -28,7 +35,7 @@ const CONFIG_FILE = "codeforge.json"
28
35
  const CONFIG_PATH = path.join(ROOT, CONFIG_FILE)
29
36
  const AGENTS_DIR = path.join(ROOT, "agents")
30
37
 
31
- const TARGET_KEYS = ["model", "fallback_models", "model_category", "model_thinking"]
38
+ const TARGET_KEYS = ["model", "fallback_models", "model_category", "model_thinking", "tier"]
32
39
  const REMOVE_KEYS = ["preferred_model"]
33
40
 
34
41
  async function main() {
@@ -108,6 +115,13 @@ function applyToMarkdown(text, agentCfg) {
108
115
 
109
116
  const lines = fmRaw.split(/\r?\n/)
110
117
 
118
+ // 在删除前先解析现有 frontmatter 里的 tier 值(顶层 key,单行)。
119
+ // 用途:当 codeforge.json 未声明 tier 时(如变体 agent),保留 .md 中已有 tier,
120
+ // 避免被本次 sync 静默清除。
121
+ const existingTier = lines
122
+ .map((line) => line.match(/^tier\s*:\s*(.+?)\s*$/)?.[1]?.trim())
123
+ .find(Boolean)
124
+
111
125
  // 删除目标 + REMOVE_KEYS。
112
126
  // 关键:YAML 子结构(list/object)以「行首空白」为续行标志;
113
127
  // 一旦遇到下一个顶层 key(行首非空白且不是注释/空行)就结束 skipping。
@@ -134,9 +148,14 @@ function applyToMarkdown(text, agentCfg) {
134
148
  // 末尾保证空行清洁
135
149
  while (stripped.length > 0 && stripped[stripped.length - 1].trim() === "") stripped.pop()
136
150
 
151
+ // tier 写入策略:优先用 agentCfg.tier(codeforge.json 声明),
152
+ // 否则 fallback 到 .md 中现有 tier;都没有就不写(保持空)。
153
+ const tier = typeof agentCfg.tier === "string" ? agentCfg.tier : existingTier
154
+
137
155
  const newLines = []
138
156
  newLines.push(`model: ${agentCfg.model}`)
139
157
  if (agentCfg.category) newLines.push(`model_category: ${agentCfg.category}`)
158
+ if (tier) newLines.push(`tier: ${tier}`)
140
159
  if (agentCfg.thinking) {
141
160
  newLines.push(`model_thinking:`)
142
161
  newLines.push(` type: ${agentCfg.thinking.type}`)