@te-river/opencode-team-mode 1.7.1 → 1.7.2
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/docs/installation-v2.md +2 -0
- package/docs/v2-port-notes.md +31 -0
- package/package.json +1 -1
- package/scripts/gen-v2-config.mjs +40 -4
package/docs/installation-v2.md
CHANGED
|
@@ -287,6 +287,7 @@ a hard `deny` with no consent path.
|
|
|
287
287
|
| Roles are not in the picker | Step 2 never ran, or ran against a different `--dir`. `ls ~/.config/opencode/agents` |
|
|
288
288
|
| The default silently became `build` | `default_agent` names an agent that does not exist — re-run Step 2, then Step 3 |
|
|
289
289
|
| A hand-written `agents/Team.md` was not updated | By design: the generator refuses files it did not create. Move yours aside, or pass `--force` |
|
|
290
|
+
| The picker shows the lead as `team`, or Team is not the default | The host takes an agent's ID from the FILE NAME. A machine that ever carried the pre-1.7 `team.md` registers the lead as `team`, while the installer writes `default_agent: "Team"` — and the documented behaviour for a default naming an agent it cannot find is a silent fall back to `build`. **Fixed in 1.7.2**: the generator renames a case-only entry it has proven is the same file to the canonical spelling. On 1.7.1 or older a re-run does NOT fix it (a write through `Team.md` keeps the old spelling on Windows) — rename by hand in two steps: `team.md` → `Team.md` |
|
|
290
291
|
| `/team-plan` etc. run but not as the specialist | 2.x documents `mode: subagent` as "runs only in a child session"; if a command selecting a specialist does not behave, the fix is `mode: "all"` in the role definition (report it — this is the one item in this flow still unverified on a live host) |
|
|
291
292
|
| `tm_webfetch` on an odd site says it refused **without asking anyone** | Expected on 2.x: a plugin cannot raise a dialog. Use a source that works, or let the host's own permission rule allow it; the address red line (metadata / private) has no consent path at all, on either generation |
|
|
292
293
|
| `task`/`subagent` seems to block the lead | On 2.x the plugin forces `background: true`; if you also set the old `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS`, unset it — it is a v1 flag and only confuses the picture |
|
|
@@ -522,6 +523,7 @@ env-FILE 面是硬 `deny`、没有同意路径。
|
|
|
522
523
|
| 选择器里没有那六个角色 | 第 2 步没跑,或者 `--dir` 指到了别处。`ls ~/.config/opencode/agents` |
|
|
523
524
|
| 默认 agent 悄悄变成 `build` | `default_agent` 指向了一个不存在的角色——重跑第 2 步,再做第 3 步 |
|
|
524
525
|
| 手写的 `agents/Team.md` 没被更新 | 这是设计:生成器拒绝覆盖不是它生成的文件。把你的文件挪开,或显式加 `--force` |
|
|
526
|
+
| 选择器里领队显示成 `team`,或者 Team 不是默认 | 宿主按**文件名**取 agent id。机器上只要曾经有过 1.7 之前的 `team.md`,领队就以 `team` 注册,而安装器写的是 `default_agent: "Team"`——按文档,默认值指向一个找不到的 agent 时会**静默回退 build**。**1.7.2 已修**:生成器会把"已确认是同一个文件、仅大小写不同"的条目标成规范拼写。1.7.1 及更早版本重跑**修不好**(Windows 上从 `Team.md` 写入会保留旧拼写),需要手工两步改名:`team.md` → `Team.md` |
|
|
525
527
|
| `/team-plan` 能跑但不是以那个专家身份跑 | 2.x 把 `mode: subagent` 文档化为"只在子会话里运行"。如果选定专家的命令行为不对,改法是角色定义里用 `mode: "all"`(请回报——这是本流程里唯一还没在活体宿主上验证过的一项) |
|
|
526
528
|
| `tm_webfetch` 说它"没问任何人就直接拒绝" | v2 的预期行为:插件弹不出对话框。换一个不需要这次访问的源,或让宿主自己的权限规则放行;地址红线(元数据 / 私网)在两代宿主上都没有授权路径 |
|
|
527
529
|
| `subagent` 好像把领队挡住了 | v2 上插件会强制 `background: true`;如果你顺手设了老的 `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS`,请取消它——那是 v1 的开关,在这里只会误导判断 |
|
package/docs/v2-port-notes.md
CHANGED
|
@@ -544,3 +544,34 @@ CALLED" note — a future layer would otherwise have to re-probe a seam the 2.0.
|
|
|
544
544
|
settled. Context Pruning (`TM_PRUNE`, default 70%) is a different mechanism: it rewrites settled
|
|
545
545
|
message BODIES into pointers inside an outgoing request and never asks the host to summarize
|
|
546
546
|
anything. `[R]`
|
|
547
|
+
|
|
548
|
+
### 2026-10-07 — the agent ID is the FILE NAME, and a case-only difference is not cosmetic `[L][R]`
|
|
549
|
+
|
|
550
|
+
**Measured on 2.0.24 / 2026-10-07 (this machine).** The global config directory held
|
|
551
|
+
`agents/team.md` — lowercase, left over from the pre-1.7 naming — while the generator had been
|
|
552
|
+
"writing" `agents/Team.md` on every install since. The host was asked directly:
|
|
553
|
+
|
|
554
|
+
- `opencode-cli run --standalone --agent Team --model nope/nope "x"` → `Agent not found: "Team"`
|
|
555
|
+
- `opencode-cli run --standalone --agent team --model nope/nope "x"` → resolves (fails only on
|
|
556
|
+
the fake model: `Model unavailable: nope/nope`)
|
|
557
|
+
- `opencode-cli api get /api/agent?directory=…` → `{"id":"team","name":"team",…}` for the lead
|
|
558
|
+
|
|
559
|
+
So the on-disk spelling IS the registered id, and the installer's own
|
|
560
|
+
`default_agent: "Team"` named an agent that did not exist. Per the agents doc the fallback for
|
|
561
|
+
an unresolvable default is `build` — silent, which is exactly the failure the installer's
|
|
562
|
+
"write `default_agent` LAST, and read it back" rule exists to prevent; the read-back cannot see
|
|
563
|
+
it, because the key IS on disk and the ID is what is wrong. `[L]`
|
|
564
|
+
|
|
565
|
+
**Why every install failed to fix it.** `writeFileSync(<dir>/Team.md)` on a case-insensitive,
|
|
566
|
+
case-PRESERVING filesystem opens the existing `team.md` and leaves that name alone — and the
|
|
567
|
+
reclaim pass deliberately refused to delete the case-only entry (deleting it would delete the
|
|
568
|
+
file just written: the #47/#48 guard). Nothing in the pipeline could ever change the spelling.
|
|
569
|
+
The fix is an explicit two-step rename (`team.md` → temp → `Team.md`; a case-only `rename` on
|
|
570
|
+
win32 is itself a no-op), performed ONLY on an entry the identity verdict has already PROVEN is
|
|
571
|
+
the same file as a target, with a restore path if the second step throws. `[R]`
|
|
572
|
+
|
|
573
|
+
**The lesson, stated in the general form.** "Is the tool loaded" and "is the file on disk" were
|
|
574
|
+
both yes here — the two checks the installer prints. What no check covered was *which id the
|
|
575
|
+
host derives from it*, and that is the difference between Team being the default and a silent
|
|
576
|
+
`build`. A verifiable claim has to name the artefact the host actually keys on (the FILE NAME),
|
|
577
|
+
not the one we wrote (the path we computed). `[R]`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@te-river/opencode-team-mode",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.2",
|
|
4
4
|
"description": "Team collaboration mode plugin for OpenCode Desktop — injects specialized agents (architect, reviewer, implementer, tester, researcher) and team workflow commands into your OpenCode workspace.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* Refuses to touch a file it did not generate unless --force is given.
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
|
-
import { mkdirSync, readFileSync, writeFileSync, existsSync, readdirSync, realpathSync, statSync } from "node:fs"
|
|
32
|
+
import { mkdirSync, readFileSync, writeFileSync, existsSync, readdirSync, realpathSync, renameSync, statSync } from "node:fs"
|
|
33
33
|
import { join } from "node:path"
|
|
34
34
|
import { homedir } from "node:os"
|
|
35
35
|
import { pathToFileURL } from "node:url"
|
|
@@ -366,9 +366,45 @@ function reclaimStale(dir, keepNames, label) {
|
|
|
366
366
|
}
|
|
367
367
|
}
|
|
368
368
|
if (sameAs) {
|
|
369
|
-
// Same file as a target, stored under a
|
|
370
|
-
// stale
|
|
371
|
-
|
|
369
|
+
// Same file as a target, stored under a DIFFERENT case. Nothing here is
|
|
370
|
+
// stale and deleting it would delete the file we just wrote — but leaving
|
|
371
|
+
// the old spelling is a real defect, not a cosmetic one: the HOST TAKES AN
|
|
372
|
+
// AGENT'S ID FROM THE FILE NAME. A machine that still carries the
|
|
373
|
+
// pre-1.7 `team.md` registers the lead as `team`, so the installer's own
|
|
374
|
+
// `default_agent: "Team"` resolves to nothing and the host falls back to
|
|
375
|
+
// `build` — silently, because that fallback is documented as the normal
|
|
376
|
+
// move for a default that names an agent it cannot find. Measured on
|
|
377
|
+
// 2.0.24 / 2026-10-07 on this very machine: `--agent Team` answered
|
|
378
|
+
// `Agent not found: "Team"`, `--agent team` resolved, and
|
|
379
|
+
// `GET /api/agent` listed `{"id":"team","name":"team"}`.
|
|
380
|
+
//
|
|
381
|
+
// Windows and APFS keep the EXISTING spelling when a write opens that file
|
|
382
|
+
// through the other one, which is how the stale name survives every
|
|
383
|
+
// install, so the rename has to be explicit — and on win32 a case-only
|
|
384
|
+
// rename is itself a no-op, hence the temporary name in the middle.
|
|
385
|
+
if (printOnly) {
|
|
386
|
+
caseNotes.push(`保留 ${file} — 与目标 ${sameAs} 是同一个文件(--print,未修正大小写)`)
|
|
387
|
+
continue
|
|
388
|
+
}
|
|
389
|
+
const target = join(dir, sameAs)
|
|
390
|
+
const tmp = join(dir, `.${entry}.casefix-${process.pid}`)
|
|
391
|
+
try {
|
|
392
|
+
renameSync(file, tmp)
|
|
393
|
+
renameSync(tmp, target)
|
|
394
|
+
caseNotes.push(`已修正大小写 ${entry} → ${sameAs} — 与目标是同一个文件;宿主按文件名取 agent id,不改就一直挂在旧 id 上`)
|
|
395
|
+
} catch (err) {
|
|
396
|
+
// Never leave a half-rename behind: if the second step failed, put the
|
|
397
|
+
// file back under the name we found it (the stale spelling is survivable;
|
|
398
|
+
// a file stranded under the temporary name is not).
|
|
399
|
+
try {
|
|
400
|
+
if (existsSync(tmp) && !existsSync(target)) renameSync(tmp, target)
|
|
401
|
+
} catch {
|
|
402
|
+
/* it stays under the temporary name, and the note below says so */
|
|
403
|
+
}
|
|
404
|
+
caseNotes.push(
|
|
405
|
+
`大小写未修正 ${entry}(目标 ${sameAs}):${String((err && err.message) || err).replace(/\s+/g, " ").slice(0, 80)}`,
|
|
406
|
+
)
|
|
407
|
+
}
|
|
372
408
|
continue
|
|
373
409
|
}
|
|
374
410
|
if (undecidable) {
|