codebee 0.1.35 → 0.1.36

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/CHANGELOG.md CHANGED
@@ -6,6 +6,21 @@ README 元数据带回,供老版本在「发现新版本」时展示新版更
6
6
 
7
7
  ## 未发布
8
8
 
9
+ ## v0.1.36(2026-09-21)
10
+
11
+ ### 🛠 问题修复
12
+
13
+ - 端口占用诊断的「关闭」现在真能关掉:服务类进程对温和关闭信号本就无反应,此前只发信号不看结果——现在温和无效会自动升级强制结束,并以「端口确实释放」为准,不再假报成功。
14
+ - 群摘要:选好 64 位 Python 后直接点「刷新群列表 / 立即扫描」不再误报「请先填路径」(表单会先静默保存);「立即扫描」按钮文字不再重复;新建的群现在也会出现在监听列表(微信本地库还没同步时从最近会话补齐)。
15
+ - 桌面蜜蜂气泡的「点我细看」现在真的能点:点气泡直达群摘要页;网页已打开时也会跳到目标页,而不是只把旧窗口调到前面。
16
+ - 禅道:「测试并保存连接」不再卡住按钮几十秒(产品/账号清单改后台拉取,拉到后下拉自动就位);操作状态面板显示具体操作名(如「拉取禅道产品」);兼容更多老版禅道的模块树与产品清单返回形状,修「模块清单形状不认识」「产品只显示 ID」。
17
+ - 设置页「代码设置」恢复左标签右控件的横排布局(此前被章节评审卡的样式意外覆盖成整列竖排居中)。
18
+ - 修复页面繁忙时「上次使用的任务类型」偶尔没被恢复的时序问题。
19
+
20
+ ### ✨ 界面
21
+
22
+ - 编排模式 / 思考程度 / 对话模型从「更多选项」提到输入框正下方,随开随用,不再折叠。
23
+
9
24
  ## v0.1.35(2026-09-21)
10
25
 
11
26
  ### ✨ 新功能
package/README.md CHANGED
@@ -23,11 +23,26 @@ Kimi Code、MiMo Code、Grok Build、Pi、DeepSeek Harness……),提供
23
23
  <!-- relnotes:start -->
24
24
  ### ✨ 新功能 · New
25
25
 
26
+ - 编排模式 / 思考程度 / 对话模型从「更多选项」提到输入框正下方,随开随用。
27
+ - Orchestration mode, reasoning level and chat model now sit right under the input box instead of being folded away.
28
+
26
29
  - 经验库越用越准:教训按任务结局自动记胜负,真正帮上忙的经验优先注入、帮倒忙的被挤出——随真实运行自我提纯。
27
30
  - The lesson library now tracks outcomes: lessons that helped a task pass gain weight, ones that failed to prevent issues fade — it self-refines with every run.
28
31
 
29
32
  ### 🛠 问题修复 · Fixes
30
33
 
34
+ - 端口诊断的「关闭」真能关掉了:温和信号无效自动升级强杀,以「端口确实释放」为准,不再假报成功。
35
+ - Port diagnostics "Close" now really closes: gentle signal escalates to forced kill when ignored, and success is only reported once the port is actually released.
36
+
37
+ - 群摘要:选好 Python 直接刷新不再误报「请先填路径」;新建的群也会出现在监听列表;蜜蜂气泡「点我细看」真能点了。
38
+ - Digests: refreshing right after picking a Python no longer nags "path required"; brand-new groups show up in the picker; the bee bubble "click to read" actually navigates now.
39
+
40
+ - 禅道:「测试并保存连接」不再卡按钮几十秒;兼容更多老版禅道的模块/产品清单形状;操作状态显示具体操作名。
41
+ - ZenTao: "Test & save" no longer holds the button for tens of seconds; more legacy ZenTao module/product list shapes are supported; the operation panel shows concrete action names.
42
+
43
+ - 设置页「代码设置」恢复横排布局(此前被章节评审卡样式意外覆盖)。
44
+ - Settings → Code display restores its row layout after an accidental style override from the chapter-review card.
45
+
31
46
  - 手机端操作被「其他设备正在控制」挡住时,弹出「接管并重试」——一键接管并自动重做刚才的操作。
32
47
  - When a mobile action is blocked by another device in control, a "Take over & retry" prompt appears — one tap retakes control and redoes the action.
33
48
 
@@ -170,7 +170,8 @@ def installed_ids():
170
170
 
171
171
  def view():
172
172
  """市场目录(给 UI/API):{catalog: [...], categories: [...]}。
173
- 每项带 installed: bool;skills 内置包 installed=True 且 installable=False。"""
173
+ 每项带 installed: bool + enabled: bool(已装包的启停态,卡片开关直接
174
+ 切换,不用再去经验库翻——对齐 ZCode 插件管理形态)。"""
174
175
  installed = installed_ids()
175
176
  items = []
176
177
  for p in skills.BUILTIN_PACKS: # 已内置:已存在,不可安装
@@ -180,13 +181,17 @@ def view():
180
181
  "category": meta.get("category") or skills.LESSON_UNCATEGORIZED,
181
182
  "scopes": list(p.get("scopes") or ["*"]),
182
183
  "builtin": True, "installed": True, "installable": False,
184
+ "enabled": skills._pack_enabled(p["id"]),
183
185
  "chars": len(skills.pack_text(p))})
184
186
  for p in BUILTIN_PACKS: # 可安装市场包
185
187
  files = p.get("files") or {}
186
188
  items.append({"id": p["id"], "name": p["name"], "desc": p["desc"],
187
189
  "category": p["category"], "scopes": list(p["scopes"]),
188
190
  "builtin": False, "installed": p["id"] in installed,
189
- "installable": True, "files": sorted(files),
191
+ "installable": True,
192
+ "enabled": (p["id"] in installed and
193
+ skills._pack_enabled(p["id"])),
194
+ "files": sorted(files),
190
195
  "chars": sum(len(v) for v in files.values())})
191
196
  cats = list(skills.LESSON_CATEGORIES) # 闭集全枚举先给全(UI 下拉不缺项)
192
197
  for it in items:
@@ -620,6 +620,33 @@ def _pairs_to_list(pairs, id_key, name_key):
620
620
  return out
621
621
 
622
622
 
623
+ def _module_items(d):
624
+ """老版模块树形状兼容(2026-09-21 真机反馈「响应形状不认识」):
625
+ sons/modules 数组、{id: {name,...}} 字典、children/sons 嵌套树,统一
626
+ 递归展开成 [{id,name}] 平铺清单(id=0 是树根容器,跳过)。"""
627
+ out = []
628
+
629
+ def walk(node):
630
+ if isinstance(node, list):
631
+ for x in node:
632
+ walk(x)
633
+ elif isinstance(node, dict):
634
+ if node.get("id") not in (None, "", 0):
635
+ out.append(node)
636
+ for k in ("children", "sons"):
637
+ if isinstance(node.get(k), (list, dict)):
638
+ walk(node[k])
639
+ else:
640
+ for v in node.values():
641
+ walk(v)
642
+
643
+ if isinstance(d, dict):
644
+ walk(d.get("sons") or d.get("modules") or d.get("tree") or d)
645
+ elif isinstance(d, list):
646
+ walk(d)
647
+ return out
648
+
649
+
623
650
  def _old_route(api, method, p, q, body):
624
651
  """路径翻译 + 请求 + 归一。会话死抛 _OldSessionDead。"""
625
652
 
@@ -698,16 +725,14 @@ def _old_route(api, method, p, q, body):
698
725
  m = re.match(r"^/products/(\d+)/modules$", p)
699
726
  if m and method == "GET":
700
727
  d = go(get("/tree-browse-%s-module.json" % m.group(1)), "(拉模块清单)")
701
- items = []
702
- if isinstance(d, dict):
703
- items = d.get("sons") or d.get("modules") or []
704
- elif isinstance(d, list):
705
- items = d
706
- return {"_list": [x for x in items if isinstance(x, dict) and x.get("id")]}
728
+ return {"_list": [x for x in _module_items(d) if isinstance(x, dict) and x.get("id")]}
707
729
 
708
730
  if p == "/products" and method == "GET":
709
731
  d = go(get("/api-getmodel-product-getpairs.json"), "(拉产品清单)")
710
- pairs = d.get("products") if isinstance(d, dict) and isinstance(d.get("products"), dict) else d
732
+ prods = d.get("products") if isinstance(d, dict) else None
733
+ if isinstance(prods, list): # 部分老版回数组形状 [{id,name,...}]
734
+ return {"_list": [x for x in prods if isinstance(x, dict) and x.get("id")]}
735
+ pairs = prods if isinstance(prods, dict) else d
711
736
  return {"_list": _pairs_to_list(pairs, "id", "name")}
712
737
 
713
738
  if p == "/users" and method == "GET":
package/app/pet.py CHANGED
@@ -1090,7 +1090,8 @@ class PetApp:
1090
1090
  dg = snap.get("digest") or {}
1091
1091
  self.seen_digests, notify = digest_alert(self.seen_digests, dg)
1092
1092
  if notify:
1093
- self._show_bubble(digest_bubble_text(dg, self.lang), secs=9.0)
1093
+ self._show_bubble(digest_bubble_text(dg, self.lang), secs=9.0,
1094
+ action="digest")
1094
1095
  self.last_busy = now
1095
1096
  if st == "sleep":
1096
1097
  self.mood_kind, self.mood_until = "cheer", now + MOOD_HOLD_S
@@ -1257,8 +1258,14 @@ class PetApp:
1257
1258
  self._tip_body = tk.Label(tip, text="", bg="#1B2430",
1258
1259
  fg="#ECF2F8",
1259
1260
  font=("Microsoft YaHei UI", 9),
1260
- justify="left", wraplength=280)
1261
+ justify="left", wraplength=280,
1262
+ cursor="hand2")
1261
1263
  self._tip_body.pack(anchor="w", padx=10, pady=(2, 8))
1264
+ # 点清单即打开网页(含群摘要「点我细看」行——此前清单窗没有任何
1265
+ # 点击绑定,文案写着「点我细看」点了却没反应,用户实测反馈)
1266
+ for w in (self._tip_head, self._tip_body):
1267
+ w.bind("<ButtonPress-1>",
1268
+ lambda e: self._open_ui("#goto=__wxdigest"))
1262
1269
  self._tip = tip
1263
1270
  self._tip_update()
1264
1271
  try:
@@ -1312,11 +1319,14 @@ class PetApp:
1312
1319
  text = self._L("alert") % bad.get("title", "?")
1313
1320
  self._show_bubble(text)
1314
1321
 
1315
- def _show_bubble(self, text, secs=5.0, force=False):
1322
+ def _show_bubble(self, text, secs=5.0, force=False, action=""):
1323
+ """action:气泡承诺的「点我细看」要兑现(2026-09-21 用户实测点不动——
1324
+ 气泡窗口没绑点击,蜜蜂左键也只开首页)。digest=直达群摘要页。"""
1316
1325
  if not force and time.time() < self.quiet_until:
1317
1326
  return # 「安静一会」期间不碎碎念(系统确认语用 force 放行)
1318
1327
  self._destroy_win("_bubble")
1319
1328
  self.hop_until = time.time() + 0.7 # 冒泡蹦一下,像在说话
1329
+ self._bubble_action = action
1320
1330
  tk = self.tk
1321
1331
  bub = tk.Toplevel(self.root)
1322
1332
  bub.overrideredirect(True)
@@ -1324,10 +1334,14 @@ class PetApp:
1324
1334
  bub.attributes("-topmost", True)
1325
1335
  except Exception:
1326
1336
  pass
1327
- tk.Label(bub, text=text, bg="#FFF8E1", fg="#4A3200",
1328
- bd=1, relief="solid",
1329
- font=("Microsoft YaHei UI", 9), justify="left",
1330
- wraplength=230, padx=8, pady=5).pack()
1337
+ lab = tk.Label(bub, text=text, bg="#FFF8E1", fg="#4A3200",
1338
+ bd=1, relief="solid",
1339
+ font=("Microsoft YaHei UI", 9), justify="left",
1340
+ wraplength=230, padx=8, pady=5, cursor="hand2")
1341
+ lab.pack()
1342
+ # 气泡本体可点:点了按承诺跳转(点蜜蜂也走同一逻辑,见 _on_release)
1343
+ for w in (bub, lab):
1344
+ w.bind("<Button-1>", lambda e: self._bubble_click())
1331
1345
  x = self.root.winfo_x() + self.win_w - 40
1332
1346
  y = max(0, self.root.winfo_y() - 8)
1333
1347
  bub.update_idletasks()
@@ -1338,6 +1352,29 @@ class PetApp:
1338
1352
  self._bubble_until = time.time() + secs
1339
1353
  self.root.after(int(secs * 1000), self._hide_bubble)
1340
1354
 
1355
+ def _bubble_action_live(self):
1356
+ """最近一条气泡是否还在展示期且带跳转承诺。"""
1357
+ if time.time() > getattr(self, "_bubble_until", 0):
1358
+ return ""
1359
+ return getattr(self, "_bubble_action", "") or ""
1360
+
1361
+ def _bubble_click(self):
1362
+ # 点了立刻消失并清展示期(_hide_bubble 是到点才收的延迟逻辑,别用);
1363
+ # 先记 action 再清——_open_by_bubble_action 读的就是这个展示期
1364
+ action = self._bubble_action_live()
1365
+ self._destroy_win("_bubble")
1366
+ self._bubble_until = 0
1367
+ if action == "digest":
1368
+ self._open_digest()
1369
+ else:
1370
+ self._open_ui()
1371
+
1372
+ def _open_by_bubble_action(self):
1373
+ if self._bubble_action_live() == "digest":
1374
+ self._open_digest()
1375
+ else:
1376
+ self._open_ui()
1377
+
1341
1378
  def _hide_bubble(self):
1342
1379
  if time.time() < self._bubble_until:
1343
1380
  self.root.after(500, self._hide_bubble)
@@ -1476,7 +1513,8 @@ class PetApp:
1476
1513
  self._clamp_pos()
1477
1514
  self._save_cfg(x=self.root.winfo_x(), y=self.root.winfo_y())
1478
1515
  else:
1479
- self._open_ui()
1516
+ # 展示期内气泡承诺过「点我细看」就兑现(digest 直达),否则开首页
1517
+ self._open_by_bubble_action()
1480
1518
 
1481
1519
  def _bye(self, write_setting=False):
1482
1520
  """退出。write_setting=True(右键菜单「关闭桌宠」)先把 pet_enabled=False
package/app/ui/app.js CHANGED
@@ -59,7 +59,9 @@ function renderTypeOptions() {
59
59
  const prev = sel.value; sel.innerHTML = (S.flows || []).map((f) => {
60
60
  return '<option value="' + esc(f.id) + '">' + esc(t(f.name)) + t("(") + flowDesc(f) + (f.builtin ? "" : t(" · 自定义")) + t(")") + "</option>";
61
61
  }).join("");
62
- if (prev && flowById(prev)) sel.value = prev;
62
+ // 保住用户手动选择(typeTouched);出厂默认 direct 不算选择——prefs 迟到
63
+ // 竞态下要让位给偏好记忆,否则「上次类型」永远恢复不了(高负载下实测)
64
+ if (prev && flowById(prev) && (S.typeTouched || prev !== "direct")) sel.value = prev;
63
65
  // 偏好记忆:上次创建用的类型优先于出厂默认「直接执行」(老数据/已删流程回落)
64
66
  else if (S.lastPrefs && S.lastPrefs.type && flowById(S.lastPrefs.type)) sel.value = S.lastPrefs.type;
65
67
  // 首屏默认落在「直接执行」(快档位:单 CLI 直达,无拆解/评审);
@@ -153,6 +155,7 @@ function toggleTypeMenu(force) {
153
155
  }
154
156
 
155
157
  function pickType(id) {
158
+ S.typeTouched = true; // 用户手动选过:flows 重渲染时优先保住,不让位偏好记忆
156
159
  const sel = $("f-type");
157
160
  if (sel && sel.value !== id) {
158
161
  sel.value = id;
@@ -8969,19 +8972,39 @@ function mkCharsText(n) {
8969
8972
  return t("约 ") + n + " " + t("字");
8970
8973
  }
8971
8974
 
8972
- function mkCardHtml(p) {
8975
+ function mkCardHtml(p, section) {
8973
8976
  let ops;
8974
8977
  if (p.builtin) ops = '<button class="ghost small" disabled>' + t("已内置") + "</button>";
8975
8978
  else if (p.installed) ops = '<span class="tag ok">' + t("已安装") + "</span>" +
8976
8979
  '<button class="ghost small" onclick="mkRemove(\'' + esc(p.id) + '\')">' + t("卸载") + "</button>";
8977
8980
  else ops = '<button class="primary small" onclick="mkInstall(\'' + esc(p.id) + '\')">' + t("安装") + "</button>";
8981
+ // 已装插件卡片直接给启停开关(对齐 ZCode 插件管理形态:启停不用去经验库翻)
8982
+ const toggle = p.installed
8983
+ ? '<label class="switch" title="' + (p.enabled ? t("已启用") : t("已停用")) + '">' +
8984
+ '<input type="checkbox"' + (p.enabled ? " checked" : "") +
8985
+ ' onchange="mkTogglePack(\'' + esc(p.id) + '\', this.checked)"></label>'
8986
+ : "";
8987
+ const sec = section ? '<span class="tag mk-sec">' + esc(section) + "</span>" : "";
8978
8988
  return '<div class="card mk-card"><div class="head">' +
8979
8989
  '<span class="name">' + esc(t(p.name)) + "</span>" +
8980
- '<span class="tag">' + esc(t(p.category || "")) + "</span>" +
8990
+ '<span class="tag">' + esc(t(p.category || "")) + "</span>" + sec +
8981
8991
  "</div>" +
8982
8992
  '<div class="note">' + esc(t(p.desc || "")) + "</div>" +
8983
8993
  '<div class="mk-meta"><span title="' + esc(t("运行该类型任务时自动注入提示词,无需手动调用")) + '">' + t("适用:") + esc(mkScopeText(p.scopes)) + "</span><span>" + esc(mkCharsText(p.chars)) + "</span></div>" +
8984
- '<div class="ops">' + ops + "</div></div>";
8994
+ '<div class="ops">' + ops + toggle + "</div></div>";
8995
+ }
8996
+
8997
+ /* 已装插件启停:走经验库包启停(同一条链),停用后该插件不再注入任务提示词 */
8998
+ async function mkTogglePack(id, enabled) {
8999
+ try {
9000
+ await api("/api/skills/pack-op", { method: "POST",
9001
+ body: JSON.stringify({ id, op: enabled ? "enable" : "disable" }) });
9002
+ toast(enabled ? t("已启用") : t("已停用(不再注入任务提示词)"));
9003
+ loadMarket();
9004
+ } catch (e) {
9005
+ toast(e.message || t("操作失败"), true);
9006
+ loadMarket();
9007
+ }
8985
9008
  }
8986
9009
 
8987
9010
  function renderMarket() {
@@ -9011,7 +9034,22 @@ function renderMarket() {
9011
9034
  (p.name || "").toLowerCase().indexOf(q) >= 0 || (p.desc || "").toLowerCase().indexOf(q) >= 0);
9012
9035
  const cnt = $("mk-count");
9013
9036
  if (cnt && mkActiveView() === "local") cnt.textContent = t("共 ") + items.length + t(" 个");
9014
- grid.innerHTML = items.map(mkCardHtml).join("") || '<div class="empty">' + t("没有符合条件插件") + "</div>";
9037
+ // 分区渲染(对齐 ZCode 插件管理形态):内置 / 已安装 / 可安装三组,
9038
+ // 各组小标题+卡片;「全部」以外状态只出命中的那组
9039
+ if (st === "all" && !cat && !q) {
9040
+ const builtin = items.filter((p) => p.builtin);
9041
+ const inst = items.filter((p) => !p.builtin && p.installed);
9042
+ const avail = items.filter((p) => !p.builtin && !p.installed);
9043
+ const sec = (title, list) => list.length
9044
+ ? '<h3 class="sec-title">' + title + " " + list.length + "</h3>" +
9045
+ '<div class="mk-grid">' + list.map((p) => mkCardHtml(p, t(title))).join("") + "</div>"
9046
+ : "";
9047
+ grid.innerHTML =
9048
+ sec(t("内置"), builtin) + sec(t("已安装"), inst) + sec(t("可安装"), avail) ||
9049
+ '<div class="empty">' + t("没有符合条件插件") + "</div>";
9050
+ return;
9051
+ }
9052
+ grid.innerHTML = items.map((p) => mkCardHtml(p)).join("") || '<div class="empty">' + t("没有符合条件插件") + "</div>";
9015
9053
  }
9016
9054
 
9017
9055
  async function mkInstall(id) {
package/app/ui/i18n.js CHANGED
@@ -1281,6 +1281,8 @@
1281
1281
  "约 ": "about ",
1282
1282
  "字": "chars",
1283
1283
  "已内置": "Built-in",
1284
+ "已启用": "Enabled",
1285
+ "已停用(不再注入任务提示词)": "Disabled — no longer injected into task prompts",
1284
1286
  "没有符合条件插件": "No plugins match the filters",
1285
1287
  "该插件已安装过": "Already installed",
1286
1288
  "安装成功,运行任务时自动注入提示词;可到「经验库」启停或查看": "Installed — auto-injected into prompts when tasks run; enable/disable or view it in the skill library",
@@ -1327,6 +1329,16 @@
1327
1329
  "更新插件": "Update plugin",
1328
1330
  "更新模型配置": "Update model settings",
1329
1331
  "更新智能体绑定": "Update agent binding",
1332
+ "测试禅道连接": "Test ZenTao connection",
1333
+ "拉取禅道产品": "Fetch ZenTao products",
1334
+ "拉取禅道账号": "Fetch ZenTao accounts",
1335
+ "拉取模块清单": "Fetch module list",
1336
+ "禅道扫描 Bug": "Scan ZenTao bugs",
1337
+ "保存禅道配置": "Save ZenTao settings",
1338
+ "刷新群列表": "Refresh group list",
1339
+ "扫描群摘要": "Scan digests",
1340
+ "保存群摘要设置": "Save digest settings",
1341
+ "关闭端口进程": "Close port process",
1330
1342
  "提交操作": "Submit operation",
1331
1343
  "{0} 项处理中": "{0} in progress",
1332
1344
  "{0} 项失败": "{0} failed",
package/app/ui/index.html CHANGED
@@ -305,6 +305,31 @@
305
305
  <div id="cmp-quick" class="cmp-quick" role="group" aria-label="常用类型" data-i18n-aria="常用类型"></div>
306
306
  <!-- 开跑前成本预估(refreshEstimate 填充;无同类样本时保持隐藏) -->
307
307
  <p class="hint cmp-estimate hidden" id="cmp-estimate"></p>
308
+ <!-- 编排偏好贴输入框(用户拍板「放上来」:不再折叠进更多选项) -->
309
+ <div class="cmp-prefs" id="cmp-prefs">
310
+ <div class="field">
311
+ <label data-i18n="编排模式">编排模式</label>
312
+ <select id="f-mode">
313
+ <option data-i18n="自动(推荐):按任务类型与风险动态决定步骤" value="auto">自动(推荐):按任务类型与风险动态决定步骤</option>
314
+ <option data-i18n="快速:少步骤优先,先做确定性验收" value="fast">快速:少步骤优先,先做确定性验收</option>
315
+ <option data-i18n="专家:完整规划、多评审、失败修复与换将" value="expert">专家:完整规划、多评审、失败修复与换将</option>
316
+ <option data-i18n="手动:自行指定实现者与评审组" value="manual">手动:自行指定实现者与评审组</option>
317
+ </select>
318
+ </div>
319
+ <div class="field">
320
+ <label data-i18n="思考程度">思考程度</label>
321
+ <select id="f-thinking">
322
+ <option data-i18n="标准:平衡质量与速度" value="standard" selected>标准:平衡质量与速度</option>
323
+ <option data-i18n="自动:随任务难度调整" value="auto">自动:随任务难度调整</option>
324
+ <option data-i18n="快速:少推理、优先响应" value="low">快速:少推理、优先响应</option>
325
+ <option data-i18n="深度:更多推理与检查" value="high">深度:更多推理与检查</option>
326
+ </select>
327
+ </div>
328
+ <div id="f-direct-model" class="field hidden"><label data-i18n="对话模型(可选)">对话模型(可选)</label><div class="grid-2">
329
+ <select id="f-direct-provider" aria-label="对话厂商" data-i18n-aria="对话厂商"><option data-i18n="自动推荐">自动推荐</option></select>
330
+ <select id="f-direct-model-name" aria-label="对话模型" data-i18n-aria="对话模型"><option data-i18n="随厂商推荐">随厂商推荐</option></select>
331
+ </div><p class="hint" data-i18n="只对本次对话生效,不会修改全局绑定;指定渠道不可用时会明确报错。">只对本次对话生效,不会修改全局绑定;指定渠道不可用时会明确报错。</p></div>
332
+ </div>
308
333
  <!-- 需求拷问采访卡(renderClarify 渲染;goal 模糊时出现,跳过=直接创建) -->
309
334
  <div id="clarify-box" class="clarify-box hidden"></div>
310
335
  <div class="field hidden" id="row-resume-session">
@@ -326,27 +351,6 @@
326
351
  <div class="field"><label data-i18n="任务标题">任务标题</label><input id="f-title" placeholder="可空,自动取目标首行" data-i18n-ph="可空,自动取目标首行"></div>
327
352
  </div>
328
353
  <div class="field"><label data-i18n="上下文">上下文</label><textarea id="f-context" rows="2" placeholder="背景信息、约束、相关文件(可空)" data-i18n-ph="背景信息、约束、相关文件(可空)"></textarea></div>
329
- <div class="field">
330
- <label data-i18n="编排模式">编排模式</label>
331
- <select id="f-mode">
332
- <option data-i18n="自动(推荐):按任务类型与风险动态决定步骤" value="auto">自动(推荐):按任务类型与风险动态决定步骤</option>
333
- <option data-i18n="快速:少步骤优先,先做确定性验收" value="fast">快速:少步骤优先,先做确定性验收</option>
334
- <option data-i18n="专家:完整规划、多评审、失败修复与换将" value="expert">专家:完整规划、多评审、失败修复与换将</option>
335
- <option data-i18n="手动:自行指定实现者与评审组" value="manual">手动:自行指定实现者与评审组</option>
336
- </select>
337
- </div>
338
- <div class="grid-2">
339
- <div class="field"><label data-i18n="思考程度">思考程度</label><select id="f-thinking">
340
- <option data-i18n="标准:平衡质量与速度" value="standard" selected>标准:平衡质量与速度</option>
341
- <option data-i18n="自动:随任务难度调整" value="auto">自动:随任务难度调整</option>
342
- <option data-i18n="快速:少推理、优先响应" value="low">快速:少推理、优先响应</option>
343
- <option data-i18n="深度:更多推理与检查" value="high">深度:更多推理与检查</option>
344
- </select></div>
345
- <div id="f-direct-model" class="field hidden"><label data-i18n="对话模型(可选)">对话模型(可选)</label><div class="grid-2">
346
- <select id="f-direct-provider" aria-label="对话厂商" data-i18n-aria="对话厂商"><option data-i18n="自动推荐">自动推荐</option></select>
347
- <select id="f-direct-model-name" aria-label="对话模型" data-i18n-aria="对话模型"><option data-i18n="随厂商推荐">随厂商推荐</option></select>
348
- </div><p class="hint" data-i18n="只对本次对话生效,不会修改全局绑定;指定渠道不可用时会明确报错。">只对本次对话生效,不会修改全局绑定;指定渠道不可用时会明确报错。</p></div>
349
- </div>
350
354
 
351
355
  <div id="f-code-only">
352
356
  <div class="field"><label data-i18n="验证命令">验证命令</label><input id="f-verify" placeholder="例:python -m pytest -q(可空=只评审)" data-i18n-ph="例:python -m pytest -q(可空=只评审)"></div>
package/app/ui/style.css CHANGED
@@ -5615,3 +5615,28 @@ button.request-busy > * { opacity: .18; }
5615
5615
  .ch-ev { padding-left: 10px; border-left: 2px solid var(--border); color: var(--muted); }
5616
5616
  .ch-ev.ok { border-left-color: var(--ok); color: inherit; }
5617
5617
  .ch-ev.bad { border-left-color: var(--bad); color: inherit; }
5618
+
5619
+ /* composer 内编排偏好行:编排模式/思考程度/对话模型贴着输入框(2026-09-21
5620
+ * 用户拍板「放上来」,不再折叠进「更多选项」)。字段横向紧凑排布,窄屏换行。 */
5621
+ .cmp-prefs { display: flex; flex-wrap: wrap; gap: 8px 20px; padding: 6px 2px 2px; }
5622
+ .cmp-prefs > .field { display: flex; align-items: center; gap: 8px; flex: 1 1 260px;
5623
+ min-width: 0; margin: 0; }
5624
+ .cmp-prefs > .field > label { white-space: nowrap; flex: none; margin: 0;
5625
+ font-size: 12.5px; color: var(--muted); }
5626
+ .cmp-prefs > .field > select { flex: 1 1 auto; min-width: 0; width: auto; }
5627
+ /* 对话模型(可选)默认整行:label + 双下拉 + 说明文字 */
5628
+ .cmp-prefs > #f-direct-model { flex: 1 1 100%; align-items: center; }
5629
+ .cmp-prefs > #f-direct-model > .grid-2 { flex: 1 1 auto; min-width: 0; }
5630
+ .cmp-prefs > #f-direct-model > .hint { flex: 1 1 100%; margin: 0; }
5631
+
5632
+ /* 插件市场:内置/已安装/可安装分区标题与已装启停开关(对齐 ZCode 形态) */
5633
+ #mk-grid .sec-title { margin: 14px 0 6px; }
5634
+ .mk-sec { background: color-mix(in srgb, var(--accent) 12%, transparent); }
5635
+ .mk-card .switch { display: inline-flex; align-items: center; cursor: pointer; }
5636
+ .mk-card .switch input { appearance: none; width: 34px; height: 19px;
5637
+ border-radius: 999px; background: var(--border, rgba(128,128,128,.4));
5638
+ position: relative; cursor: pointer; transition: background .15s; margin: 0; }
5639
+ .mk-card .switch input:checked { background: var(--accent); }
5640
+ .mk-card .switch input::after { content: ""; position: absolute; top: 2px; left: 2px;
5641
+ width: 15px; height: 15px; border-radius: 50%; background: #fff; transition: left .15s; }
5642
+ .mk-card .switch input:checked::after { left: 17px; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebee",
3
- "version": "0.1.35",
3
+ "version": "0.1.36",
4
4
  "description": "CodeBee · 多智能体编排台 — 统一编排本机 AI 编码 CLI(Codex / Claude Code / QwenCode / OpenCode / Aider…):目标→拆解→路由→执行→验证→跨厂商评审→修复→报告。纯 Python 标准库实现,本地 Web UI。",
5
5
  "license": "MIT",
6
6
  "bin": {