@zhuxixi/pi-agent-board 0.4.3 → 0.5.1
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/PROGRESS.md +18 -3
- package/README.md +295 -72
- package/VERIFY.md +3 -3
- package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +3 -3
- package/docs/superpowers/plans/2026-08-29-code-refs-badges.md +223 -0
- package/docs/superpowers/plans/2026-08-30-circular-navigation.md +308 -0
- package/docs/superpowers/plans/2026-08-30-post-exit-timing-fix.md +37 -0
- package/docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md +311 -0
- package/docs/superpowers/plans/2026-08-30-readme-v2.md +294 -0
- package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +4 -0
- package/docs/superpowers/specs/2026-08-22-jiggle-shrink-and-hold-design.md +4 -0
- package/docs/superpowers/specs/2026-08-29-code-refs-badges-design.md +128 -0
- package/docs/superpowers/specs/2026-08-30-circular-navigation-design.md +47 -0
- package/docs/superpowers/specs/2026-08-30-eprm-atomicwrite-race-design.md +77 -0
- package/docs/superpowers/specs/2026-08-30-post-exit-timing-fix-design.md +80 -0
- package/docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md +105 -0
- package/docs/superpowers/specs/2026-08-30-readme-v2-design.md +115 -0
- package/package.json +1 -1
- package/runner/job-runner.mjs +29 -3
- package/runner/pty-runner.mjs +139 -25
- package/runner/state-runner.mjs +7 -2
- package/runner/title-runner.mjs +1 -1
- package/src/core/atomic.mjs +40 -1
- package/src/core/code-refs-store.mjs +315 -0
- package/src/core/code-refs.mjs +861 -0
- package/src/core/host-crash.mjs +39 -0
- package/src/core/launch.mjs +6 -0
- package/src/core/paths.mjs +24 -1
- package/src/core/pty-attach-jiggle-controller.mjs +71 -17
- package/src/core/pty-attach-reconnect.mjs +43 -0
- package/src/core/pty-scroll.mjs +4 -3
- package/src/core/repo.mjs +56 -0
- package/src/core/rows.mjs +50 -0
- package/src/core/store.mjs +4 -1
- package/src/core/types.mjs +12 -0
- package/src/core/worktree.mjs +1 -0
- package/src/runtime/service.mjs +7 -1
- package/src/ui/dashboard.ts +20 -4
- package/src/ui/pty-attach.ts +124 -27
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Spec: 行内展示 session 关联的 issue / PR 编号(code-refs)
|
|
2
|
+
|
|
3
|
+
- Issue: https://github.com/zhuxixi/pi-agent-board/issues/40
|
|
4
|
+
- 日期:2026-08-29
|
|
5
|
+
- 状态:待用户确认
|
|
6
|
+
|
|
7
|
+
## 1. 背景与目标
|
|
8
|
+
|
|
9
|
+
Dashboard 每行(一个后台 pi session)目前只有 name + summary + age,看不到「这行在处理哪个 issue、它提交了哪个 PR」。本特性在行内徽章区展示这两个编号,peek 视图给完整信息。
|
|
10
|
+
|
|
11
|
+
**平台无关是硬约束**:GitHub、GitLab、公司内网代码平台都有 issue/PR(MR)概念但 CLI 与 URL 不同。平台差异全部做成正则规则数据,提取引擎保持通用。
|
|
12
|
+
|
|
13
|
+
### 目标(Goals)
|
|
14
|
+
|
|
15
|
+
1. 行内徽章显示最近一个 issue 编号 + 最近一个 PR 编号(如 `#40 ▸#45`),前缀随平台规则(GitLab MR 用 `!`)。
|
|
16
|
+
2. peek 视图显示完整引用列表(编号 + 置信度 + 平台链接)。
|
|
17
|
+
3. 平台规则可由用户配置扩展(内网平台 = 加一段 JSON,引擎零改动)。
|
|
18
|
+
4. 纯本地提取,零网络调用;实时性 = 事件驱动(session 跑到相关命令时徽章即出现)。
|
|
19
|
+
|
|
20
|
+
### 非目标(Non-goals)
|
|
21
|
+
|
|
22
|
+
- 不做网络补全(`gh pr list --head` 查分支对应 PR、抓取 issue/PR 标题与状态)——列为 v2 候选,本期不做。
|
|
23
|
+
- 不做 PR 状态着色(open/merged)、不做 `refs:has` 过滤器——v2 候选。
|
|
24
|
+
- 不修 `outputPreview` 的 `[object Object]` bug——拆独立 issue #41。
|
|
25
|
+
- 不识别「无编号」的平台对象(如纯分支名)。
|
|
26
|
+
|
|
27
|
+
## 2. 决策表(已与用户对齐)
|
|
28
|
+
|
|
29
|
+
| # | 决策点 | 结论 |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| D1 | 评分规则 | 信号分四级(认领/worktree 命名/PR 回链 = 最强;动作 = 强;查看 = 中;裸引用 = 弱兜底),平局时最近的最强信号赢 |
|
|
32
|
+
| D2 | 多候选显示 | 行内只显示最近一个 issue + 一个 PR;完整列表进 peek |
|
|
33
|
+
| D3 | 用户规则与内置同名 provider 冲突 | 按规则追加(用户规则优先匹配,其后是内置规则) |
|
|
34
|
+
| D4 | outputPreview bug | 拆独立 issue #41,本特性不含 |
|
|
35
|
+
| D5 | 隔离测试 | `PI_CODING_AGENT_DIR` + `AGENT_BOARD_ROOT` 双变量隔离,不动 `~/.pi`;软链开发方式不用 |
|
|
36
|
+
|
|
37
|
+
## 3. 架构
|
|
38
|
+
|
|
39
|
+
复刻仓库既有「artifact → summarize → 合并进 Row → renderRow 徽章」模式(evidence/diagnostics/followUps/steering 同构)。新增一个纯函数引擎模块、一个 per-view artifact、五处写入钩子、两处渲染改动。
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
事件流 → reduceEvidence → evidence.json ──┐
|
|
43
|
+
├─→ extractCodeRefs() → github.json → Row.github → 徽章/peek
|
|
44
|
+
providers.json(用户规则)+ 内置规则 ──────┘ ▲
|
|
45
|
+
repo.mjs remoteHost()(带缓存)───────────────────┘
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 组件契约
|
|
49
|
+
|
|
50
|
+
**C1 `src/core/code-refs.mjs`(新,纯函数,零 I/O,主测试对象)**
|
|
51
|
+
|
|
52
|
+
- `extractCodeRefs(input, providerSet) → CodeRefsResult`
|
|
53
|
+
- `input`: `{ commands: EvidenceCommand[], assistantTexts: string[], worktreePath: string|null, branch: string|null }`
|
|
54
|
+
- `providerSet`: 解析后的规则包列表(已按 host 选好 + 用户规则已合并)
|
|
55
|
+
- 输出: `{ issue: {number, confidence, source} | null, pr: {number, confidence, source} | null, repoUrl: string|null }`
|
|
56
|
+
- `loadProviders(builtIns, userConfig) → provider 列表`(实现 D3 追加语义:同名 provider 时用户规则排在内置规则前面)
|
|
57
|
+
- `matchProvider(providers, remoteHost) → provider | null`(host 匹配;无匹配返回 null,调用方走兜底规则)
|
|
58
|
+
- 信号强度枚举:`claim > action > view > mention`,每级带置信度 high/medium/low。
|
|
59
|
+
|
|
60
|
+
**C2 信号来源与评分规则(D1 落地)**
|
|
61
|
+
|
|
62
|
+
| 强度 | 信号 | 检测方式 |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| claim(最强) | 认领命令:provider 规则里 `strength: "claim"` 的命令模式(GitHub 内置:`gh issue edit N --add-assignee`) | commands 正则 |
|
|
65
|
+
| claim | worktree 命名:`issue-<N>-<slug>`(issue-driven 工作流强制规范) | worktreePath / branch 结构化解析(非正则配置,引擎内置) |
|
|
66
|
+
| claim | PR 回链:`gh pr create` 的 body/后续文本中的 `issue #N` / `Closes #N`(同时定 issue + PR 两个值) | commands + assistantTexts |
|
|
67
|
+
| action(强) | `issue comment/edit/close N`、`pr checkout/view/merge N`、`pr create`(编号从 outputUrl 或后续 URL 反查,见 D4 限制) | commands 正则 |
|
|
68
|
+
| view(中) | `issue view N` / `pr view N` | commands 正则,要求频次 ≥2,取最近一次 |
|
|
69
|
+
| mention(弱,兜底) | 裸 `#N` | assistantTexts,要求频次显著最高(≥3 且 ≥ 第二名的 2 倍) |
|
|
70
|
+
|
|
71
|
+
平局:命令序列中位置最靠后的最高强度信号赢(commands 有序,按数组下标比较,不用时间戳)。置信度:claim/action → high;view → medium;mention → low。渲染时 low 用 dim 色(宁可不显示也不错显示——mention 级仅在无任何更强信号时出现)。
|
|
72
|
+
|
|
73
|
+
**C3 `providers.json` 规则 schema(用户配置)**
|
|
74
|
+
|
|
75
|
+
- 位置:`$AGENT_BOARD_ROOT/providers.json`(随 store root 隔离,E2E 天然不污染真实配置)。
|
|
76
|
+
- 结构:`{ providers: [{ name, hosts[], issuePrefix, prPrefix, urlTemplates: { issue, pr }, rules[] }] }`;每条 rule = `{ pattern, kind: "issue"|"pr", strength: "claim"|"action"|"view", numberFrom?: "capture"|"outputUrl" }`。urlTemplates 用占位符拼链接,如 `"https://{host}/{owner}/{repo}/-/issues/{number}"`(owner/repo 从 remote URL 解析)。
|
|
77
|
+
- 内置默认:GitHub + GitLab 两份(含 hosts、URL 正则、CLI 正则、前后缀、链接模板)。
|
|
78
|
+
- 加载失败(JSON 语法错 / 单条正则非法):跳过该条并记 diagnostics(`code_refs_config` 码),不炸 dashboard。
|
|
79
|
+
|
|
80
|
+
**C4 `repo.mjs` 增补**
|
|
81
|
+
|
|
82
|
+
- `gitRemoteHost(repoRoot) → string|null`:`git remote get-url origin` 解析 host,支持 ssh(`git@host:path`)与 https 两种形式;结果按 repoRoot 缓存在模块级 Map(一个仓库只查一次,失败也缓存 null)。
|
|
83
|
+
|
|
84
|
+
**C5 artifact `github.json`(per-view)**
|
|
85
|
+
|
|
86
|
+
- `paths.mjs` 加 `codeRefsPath(root, viewId)` → `views/<id>/github.json`。
|
|
87
|
+
- 内容:`{ version: 1, viewId, updatedAt, provider: string|null, issue: {...}|null, pr: {...}|null, allRefs: [...](peek 用,最多 10 条) }`。
|
|
88
|
+
- 读写走 `atomicWriteJson`(并发写者多,KB 已有教训)。
|
|
89
|
+
- `readViewArtifactSummaries` 增加 `codeRefs:` 汇总;`Row`/`RowView` 加 `codeRefs` 字段。
|
|
90
|
+
|
|
91
|
+
**C6 写入钩子(5 处,`writeEvidence` 的全部调用点)**
|
|
92
|
+
|
|
93
|
+
`runner/job-runner.mjs` ×2(共用 persist())、`src/runtime/service.mjs` ×2、`runner/state-runner.mjs` ×1。统一收敛为一个 helper:`updateCodeRefsFromEvidence(root, viewId, evidence, meta)`——增量不重算:引擎输入只取 evidence 的 commands + 最近若干条 assistantTexts + meta.worktreePath/branch,纯正则,实测成本微秒级;每次 evidence 写入后顺带调用。失败只记 diagnostics,不影响主流程。
|
|
94
|
+
|
|
95
|
+
**C7 渲染**
|
|
96
|
+
|
|
97
|
+
- `rows.mjs` `rowView`:透传 `codeRefs`。
|
|
98
|
+
- `dashboard.ts` `renderRow`:statusBadges 追加 `issuePrefix+number`(issue)与 `prPrefix+number`(PR),low 置信度用 `dim` 色;宽度沿用现有「从 name 预算扣」机制。
|
|
99
|
+
- peek 视图:新增 "Refs" 段(复刻 Auto-state 段模式):provider 名、issue/PR 编号 + 置信度 + 由 `urlTemplate` 拼出的终端超链接、allRefs 完整列表。
|
|
100
|
+
|
|
101
|
+
## 4. 错误处理与降级
|
|
102
|
+
|
|
103
|
+
- 无 git 仓库 / 无 remote / host 不认识 → 只用「通用 URL 兜底规则」(匹配任意 host 的 `/issues/N`、`/pull/N`、`/-/issues/N`、`/-/merge_requests/N`)+ worktree 命名解析;都没有则不显示徽章。
|
|
104
|
+
- 用户 providers.json 损坏 → 内置规则仍生效,diagnostics 记一条 warn。
|
|
105
|
+
- 提取过程任何异常 → catch 后记 diagnostics,evidence 主流程不受影响(与既有 artifact 容错一致)。
|
|
106
|
+
- 无任何引用 → `github.json` 写空结果(`issue: null, pr: null`),渲染跳过徽章,不留 stale 数据。
|
|
107
|
+
|
|
108
|
+
## 5. 测试策略(四层,详见 issue 评论)
|
|
109
|
+
|
|
110
|
+
1. **单元**:`code-refs.mjs` 全分支覆盖——每级信号命中、强度排序、平局规则、mention 兜底阈值、provider 追加合并(D3)、host 匹配、损坏配置容错。
|
|
111
|
+
2. **真实数据回测**:脱敏后的真实 evidence.json 命令序列做 fixture(`moc 439` 行的多引用歧义场景是核心用例)。
|
|
112
|
+
3. **集成**:fake-pi.mjs 注入含 `gh issue edit 40 --add-assignee` / `gh pr create` 的事件流 → 断言 `github.json` 内容与渲染徽章字符串。
|
|
113
|
+
4. **手工 E2E**:`PI_CODING_AGENT_DIR` + `AGENT_BOARD_ROOT` 隔离环境;scratch 仓库换 remote host 验证 provider 匹配;PR 编号用 `echo <url>` 模拟(全程零真实 GitHub 变更)。
|
|
114
|
+
5. 验收:`npm run verify` 全绿(typecheck + test + c8 行 85%/分支 70% + pack dry-run)。
|
|
115
|
+
|
|
116
|
+
## 6. 分阶段
|
|
117
|
+
|
|
118
|
+
- **v1(本 issue)**:C1–C7 全部(纯本地提取 + 渲染 + 配置)。
|
|
119
|
+
- **v2(另开 issue,不在本期)**:网络补全(`pr list --head`、标题/状态)、PR 状态着色、`refs:has` 过滤器、outputPreview 修复后的 `outputUrl` 反查增强(依赖 #41)。
|
|
120
|
+
|
|
121
|
+
## 7. 实现顺序(供 plan 参考)
|
|
122
|
+
|
|
123
|
+
1. `repo.mjs` `gitRemoteHost` + 缓存(含单测)
|
|
124
|
+
2. `code-refs.mjs` 引擎 + 内置规则 + schema 校验(含单测,覆盖率大头)
|
|
125
|
+
3. `github.json` artifact 读写 + `readViewArtifactSummaries` 汇总 + Row/RowView 字段
|
|
126
|
+
4. 5 处写入钩子
|
|
127
|
+
5. 渲染:徽章 + peek Refs 段
|
|
128
|
+
6. 四层测试补齐 + `npm run verify`
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Spec: dashboard 列表方向键导航循环回绕(issue #52)
|
|
2
|
+
|
|
3
|
+
日期:2026-08-30 · 状态:draft(等用户确认)
|
|
4
|
+
|
|
5
|
+
## 根因报告
|
|
6
|
+
|
|
7
|
+
**现象**:dashboard 主列表 ↑/↓ 移动选中项,到达首/尾边界后按键无效,无法循环。
|
|
8
|
+
|
|
9
|
+
**根因**(已通过代码直读确认,systematic-debugging Phase 1 完成):
|
|
10
|
+
`src/ui/dashboard.ts` `moveSelection()`(L250-258)用 `Math.max(0, Math.min(len-1, ...))` 钳制新索引,而非取模回绕。该写法来自 MVP commit `72c0d8f`,非有意设计。
|
|
11
|
+
|
|
12
|
+
**调用链**:`handleListKey`(L334-335,normal 模式)与 `handleSelectKey`(L370-371,multi-select 模式)→ `moveSelection(±1)`。
|
|
13
|
+
|
|
14
|
+
## 设计
|
|
15
|
+
|
|
16
|
+
### 改动点
|
|
17
|
+
|
|
18
|
+
| # | 位置 | 改动 |
|
|
19
|
+
|---|------|------|
|
|
20
|
+
| 1 | `moveSelection()` L250-258 | 钳制 → 取模回绕:`next = ((base + delta) % len + len) % len` |
|
|
21
|
+
| 2 | `peekStep()` L1094-1101 | 同上,保持与主列表一致 |
|
|
22
|
+
|
|
23
|
+
### 决策表
|
|
24
|
+
|
|
25
|
+
| 决策点 | 决定 | 理由 |
|
|
26
|
+
|--------|------|------|
|
|
27
|
+
| `cur < 0`(selectedId 不在列表中) | 取模语义:按 ↓ 得 index 1(第二条,与旧实现一致);按 ↑ 得 index len-1(最后一条,**与旧实现不同**——旧钳制得第一条)。有意为之:与回绕语义一致;该路径实际不可达(`refresh()` 不变量保证按键处理时 selectedId ∈ orderedIds,或 selectedId=null 走 base 0) | 修正记录(Zima CR round 1):旧版写"保持现状语义"不属实,↑ 方向兜底行为确有变化 |
|
|
28
|
+
| 单条列表(len=1) | 取模后索引恒为 0,`nextId === selectedId` early return 挡掉无效更新 | 现有兜底继续生效,无需特判 |
|
|
29
|
+
| 空列表 | 现有 `length === 0` early return 保留 | 不变 |
|
|
30
|
+
| 滚动跟随 | **不改**——`windowBody()` 渲染时强制选中行可见,方向无关,回绕自动跟随 | 已验证 |
|
|
31
|
+
| 按键热路径性能 | 改动只涉及一次取模运算,不影响 #9/PR #12 的 prewarm debounce 设计 | 调研结论 |
|
|
32
|
+
| launch picker(cwd/model/thinking,L539-547) | **本 issue 不改**(非目标),另开 issue 跟踪 | 弹窗内短列表,回绕收益低;避免一次 PR 混两个行为变更 |
|
|
33
|
+
|
|
34
|
+
### 非目标
|
|
35
|
+
|
|
36
|
+
- launch 对话框 picker 的回绕(另议)
|
|
37
|
+
- 任何渲染层、滚动层改动
|
|
38
|
+
- 其他模式的按键行为
|
|
39
|
+
|
|
40
|
+
### 测试
|
|
41
|
+
|
|
42
|
+
目前 `moveSelection`/`peekStep` 无测试覆盖。补一个针对 Dashboard 的轻量测试(参考 `test/ui-smoke.test.mjs` 的实例化方式):构造 3 条 orderedIds,断言 尾→↓→首、首→↑→尾 的回绕行为,以及单条/空列表不炸。
|
|
43
|
+
|
|
44
|
+
## 验证
|
|
45
|
+
|
|
46
|
+
1. `npm test` 全绿(含新增用例)
|
|
47
|
+
2. 手动跑 dashboard:多 session 列表,尾按 ↓ 回首、首按 ↑ 回尾,peek 模式同样验证
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Issue #48 根因报告 + 修复 spec
|
|
2
|
+
|
|
3
|
+
Windows: pty-runner dies with uncaught EPERM when host.json atomicWrite races a reader; attach view stuck in reconnect loop
|
|
4
|
+
|
|
5
|
+
状态:**草稿,待用户确认**(2026-08-30)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. 根因(已代码层验证,非推测)
|
|
10
|
+
|
|
11
|
+
### 机制(Windows 特有)
|
|
12
|
+
libuv 打开文件默认共享模式不含 `FILE_SHARE_DELETE`;Node `renameSync` 在 Windows 映射为 MoveFileExW(MOVEFILE_REPLACE_EXISTING),替换已存在目标需先删除旧目标,而删除要求所有持句柄进程带 FILE_SHARE_DELETE——否则抛 `EPERM: operation not permitted`(本机 100/100 复现,见 issue 正文复现代码)。
|
|
13
|
+
|
|
14
|
+
### 触发链
|
|
15
|
+
1. `runner/pty-runner.mjs` 1s 心跳 `update()` → `persist()` → `writeHost()` → `atomicWriteJson()`(`src/core/atomic.mjs`:写 `.tmp` → `renameSync`,**无重试**)
|
|
16
|
+
2. service 渲染路径高频 `readHost()/loadRow()`(`readFileSync` host.json)——密集工具调用 + 任意面板渲染即构成 reader
|
|
17
|
+
3. 窗口重叠 → renameSync 抛 EPERM → **无 try/catch**(update/persist 均无防护,全文件无 uncaughtException 兜底)→ runner 以 `detached: true, stdio: "ignore"`(`launch.mjs`)静默死亡,留 `.tmp` 残留
|
|
18
|
+
4. 连锁:runner 死 → ConPTY 断 → 托管 child pi 随死
|
|
19
|
+
5. attach 视图:只有 socket `{type:"exit"}` 消息才置 `status="host exited"`(`pty-attach.ts` onSocketData L871);崩溃 runner 不发 → `scheduleReconnect()` 150ms **无限重连**
|
|
20
|
+
6. 逃生失败:`handleInput` 中 `←`/`ctrl+]` 仅当 `childInputLooksEmpty()` 才 detach(崩溃画面停在非空输出行,不满足);`send()`(L852)`!connected` 时**静默丢弃**;pi-tui 无系统级兜底键
|
|
21
|
+
|
|
22
|
+
### 次要发现
|
|
23
|
+
- `failEarly()` 硬编码 `/tmp/pi-agent-board-pty-runner.err`(Windows 无 /tmp,写失败被吞)——崩溃零痕迹的原因之一
|
|
24
|
+
- 二次崩溃无 `host_reconciled` 诊断(reconcile 只在 panel open / session_start 跑)→ idle 行掩盖崩溃
|
|
25
|
+
|
|
26
|
+
## 2. 修复设计(四层,L1-L3 必做,L4 待确认)
|
|
27
|
+
|
|
28
|
+
### L1 `src/core/atomic.mjs` — rename 重试(根因层)
|
|
29
|
+
- 新增内部 `renameWithRetry(tmp, file, opts?)`:
|
|
30
|
+
- 错误码白名单重试:`EPERM` / `EBUSY` / `EACCES`(Windows 共享冲突三兄弟)
|
|
31
|
+
- 3 次重试 + 退避 10ms → 50ms → 250ms(同步调用,上限阻塞 ~310ms,可接受)
|
|
32
|
+
- 重试无需重写 tmp(写文件已在 rename 前完成,tmp 内容完整)
|
|
33
|
+
- 全部失败:`unlinkSync(tmp)`(try/catch 清理残留)→ 抛**原错误**(保持调用方语义,由 L2 降级)
|
|
34
|
+
- `atomicWrite` 调 `renameWithRetry`;导出 `renameWithRetry` 供单测注入失败回调
|
|
35
|
+
|
|
36
|
+
### L2 `runner/pty-runner.mjs` — 持久化防御 + 崩溃兜底(防御层)
|
|
37
|
+
- `persist()` 包 try/catch:失败时 `appendDiagnostic(root, viewId, { type: "persist_error", ... })`(`core/diagnostics.mjs` 仅依赖 atomic/paths,runner 可安全导入)→ 降级继续,下个心跳 tick 再试,**不杀进程**
|
|
38
|
+
- `update()`:persist 失败不影响 `broadcast()`(socket 消息是 attach 主通道,host.json 短暂陈旧可接受)
|
|
39
|
+
- `process.on("uncaughtException")` 一次性兜底:
|
|
40
|
+
1. 移除 handler(防循环)
|
|
41
|
+
2. appendDiagnostic 记录
|
|
42
|
+
3. 尽力最终 persist:`state:"failed", error, endedAt`(try/catch 包住)
|
|
43
|
+
4. `broadcast({ type: "exit", exitCode: 1 })` ← **关键**:让已连 attach 正常退出,不再无限重连
|
|
44
|
+
5. `process.exit(1)`
|
|
45
|
+
- `failEarly` 的 `/tmp` 硬编码 → `os.tmpdir()`(Windows 兼容小修)
|
|
46
|
+
|
|
47
|
+
### L3 `src/ui/pty-attach.ts` — 逃生键 + 重连超时(UI 层)
|
|
48
|
+
- **逃生**:DETACH 分支条件改为 `if (!this.connected || this.childInputLooksEmpty()) this.detach()` —— 断连/启动中随时可按 `←`/`ctrl+]` 退出(`send({type:"detach"})` 在 !connected 时被丢弃,无害)
|
|
49
|
+
- **重连超时**(新增 `everConnected` 标志,首个 socket connect 成功置位):
|
|
50
|
+
- `everConnected === true` 断开后:15s 重连窗口 → 超时置 `status = "host exited"`、停止重连、保持可 detach(覆盖 issue 主场景:host 已崩)
|
|
51
|
+
- `everConnected === false`(host 冷启动中,service 在 attach 前已 launchHost):保留无限重连 + 宽松上限 120s → 超时置 `status = "host not reachable"` 停止重连(覆盖 launchHost 失败场景)
|
|
52
|
+
- 超时到期不自动 `done()`,显示错误状态等用户按 `←` 退出(避免突然弹走)
|
|
53
|
+
|
|
54
|
+
### L4(可选,待确认)— idle 行掩盖崩溃的展示级检测
|
|
55
|
+
- 落点:`loadRow()` 已派生 `hostAlive`;dashboard 渲染时对 `host.state==="alive" && !hostAlive && lastSeenAt 陈旧(>10s)` 的行显示 host-lost 标记(**仅展示级,不改 state.json**,避免与 resume/重启中误判)
|
|
56
|
+
- 不做 state 级 reconcile 触发时机修改(范围外)
|
|
57
|
+
|
|
58
|
+
## 3. 测试计划(TDD 先行)
|
|
59
|
+
|
|
60
|
+
| 层 | 测试 | 方式 |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| L1 | `renameWithRetry` 重试次数/退避/白名单/最终抛错/失败后 tmp 清理/happy path | 单测注入失败回调(新增 `test/atomic-retry.test.mjs`) |
|
|
63
|
+
| L2 | persist 持续失败 → runner 不崩溃 + diagnostics 有记录 + host.json 陈旧但不死 | 集成:host.json 目标路径被同名**目录**占位(rename 必失败,跨平台稳定触发) |
|
|
64
|
+
| L2 | 崩溃兜底 `finalizeCrash`:写 failed 状态 + broadcast exit | 抽纯函数单测(外部无法稳定注入 uncaughtException) |
|
|
65
|
+
| L3 | handleInput 逃生分支(!connected 时 detach) | 现有 pty-attach render 测试基建(mock tui/theme/term) |
|
|
66
|
+
| L3 | 重连超时状态机(everConnected × 超时 × 状态文案) | 抽纯函数或组件级测试 |
|
|
67
|
+
|
|
68
|
+
回归:全量 `node --test test/*.test.mjs` + `npm run typecheck`(Windows 全量已知 6 个既有失败,基线对比排除回归)。
|
|
69
|
+
|
|
70
|
+
## 4. 非目标
|
|
71
|
+
- 不做 unlink-then-rename 兜底(短暂缺失窗口影响并发 reader;重试已覆盖)
|
|
72
|
+
- 不改 reconcile 触发时机(L4 仅展示级)
|
|
73
|
+
- 不做历史 `.tmp` 残留 GC(screen-log-gc 范围外,可后续单列)
|
|
74
|
+
|
|
75
|
+
## 5. 交付
|
|
76
|
+
- worktree:`issue-48-eprm-atomicwrite-race`(spec 批准后)
|
|
77
|
+
- PR 标题:`fix: harden host.json persistence against Windows EPERM rename races (issue #48)`,覆盖 L1-L3(L4 视确认)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Spec: fix deterministic post-exit test failure after #43 (issue #46)
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
`test/runner.integration.test.mjs` → `runner does not clobber a manual completion
|
|
6
|
+
made during post-exit model passes` fails deterministically on Node 24 since
|
|
7
|
+
#43 (`ffc2c8c`). Upstream `main` CI is red (`e14cc41`, `ffc2c8c`) and blocks
|
|
8
|
+
subsequent PRs (#44).
|
|
9
|
+
|
|
10
|
+
## Root cause (research: `~/.claude/github-issue-driven/zhuxixi/pi-agent-board/issue-46/`)
|
|
11
|
+
|
|
12
|
+
#43 added a synchronous `updateCodeRefsFromEvidence()` call (git subprocesses,
|
|
13
|
+
hundreds of ms) into `runner/job-runner.mjs`'s `persist()` chain, widening two
|
|
14
|
+
pre-existing race windows:
|
|
15
|
+
|
|
16
|
+
### Window 1 — `markCompleted` rejected (CI failure point)
|
|
17
|
+
`persist()` order: `writeStatus` (endedAt visible) → `updateCodeRefsFromEvidence`
|
|
18
|
+
(slow) → `writeState` (semanticState converges). The test sees `endedAt` while
|
|
19
|
+
`state.json` is still `working`; the runner process is still alive
|
|
20
|
+
(`pid.json` records the runner pid) → `isAgentBusy(row)` → `markCompleted`
|
|
21
|
+
returns `'Wait for the active run to finish before marking done'`.
|
|
22
|
+
|
|
23
|
+
### Window 2 — manual completion clobbered (the actual bug)
|
|
24
|
+
`finalizeSemanticState` (`src/core/derive.mjs:33`) returns `"idle"` for a clean
|
|
25
|
+
worker exit. `applyHeuristicAutoState` (`runner/job-runner.mjs:360`) calls
|
|
26
|
+
`applyAutoStateToStatus` with the **in-memory** status; `isManualCompletion`
|
|
27
|
+
requires `semanticState === "completed"` (it is `"idle"`), so the guard does not
|
|
28
|
+
fire and the post-exit heuristic classification (`in_progress`, since
|
|
29
|
+
`autoStateDoneDisabled()` defaults to true) overwrites the manual completion —
|
|
30
|
+
`state.json` becomes `idle` + `autoState: {kind: "in_progress"}`.
|
|
31
|
+
`maybeModelAutoState` has a fresh-read guard for this exact case;
|
|
32
|
+
`applyHeuristicAutoState` does not.
|
|
33
|
+
|
|
34
|
+
## Fix (minimal, four changes in `runner/job-runner.mjs`)
|
|
35
|
+
|
|
36
|
+
### Change 1 — persist order
|
|
37
|
+
Move `writeState` before `updateCodeRefsFromEvidence` inside `persist()`:
|
|
38
|
+
```js
|
|
39
|
+
writeStatus(root, status);
|
|
40
|
+
writeRunEvidence(root, evidence);
|
|
41
|
+
writeEvidence(root, evidence);
|
|
42
|
+
writeState(root, projectViewState(status, now, readState(root, viewId)));
|
|
43
|
+
updateCodeRefsFromEvidence(root, viewId, evidence, meta);
|
|
44
|
+
```
|
|
45
|
+
Semantics unchanged (code-refs extraction depends only on evidence + git).
|
|
46
|
+
Verified experimentally: markCompleted assertion passes again.
|
|
47
|
+
|
|
48
|
+
### Change 2 — heuristic persist goes through persistUnlessManual
|
|
49
|
+
In the close handler, the `applyHeuristicAutoState` branch's `persist(true)`
|
|
50
|
+
becomes `persistUnlessManual(true)` so a manual completion racing the
|
|
51
|
+
heuristic classification is not overwritten. `persistUnlessManual` checks
|
|
52
|
+
`isManualCompletion(readState(...))` (state.json — the only place
|
|
53
|
+
`completeView` writes the `completed` + `autoState: null` signal; it only
|
|
54
|
+
clears autoState in status.json) before writing.
|
|
55
|
+
|
|
56
|
+
### Change 3 — applyHeuristicAutoState manual-completion guard
|
|
57
|
+
Skip classification when state.json shows a manual completion, so the
|
|
58
|
+
in-memory status isn't mutated in a way a later drain/persist could replay
|
|
59
|
+
over the user's verdict:
|
|
60
|
+
```js
|
|
61
|
+
const latestState = readState(config.root, config.viewId);
|
|
62
|
+
if (isManualCompletion(latestState)) return false;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Change 4 — drainQueuedFollowUp / finalizeSteeringIfNeeded guards
|
|
66
|
+
`drainQueuedFollowUp` (follow-up runs) and `finalizeSteeringIfNeeded`
|
|
67
|
+
(plan approval resurrection) both early-return on
|
|
68
|
+
`isManualCompletion(readState(...))` so the exit chain never starts new
|
|
69
|
+
work or resurrects a row the user just manually completed.
|
|
70
|
+
|
|
71
|
+
## Non-goals
|
|
72
|
+
- No changes to #44/#45 code (windowsHide / control socket)
|
|
73
|
+
- No handling of Windows-local EPERM cleanup noise (environment-only)
|
|
74
|
+
- No auto-state state machine refactor
|
|
75
|
+
|
|
76
|
+
## Verification
|
|
77
|
+
1. clobber test ≥3× on Node 24: all pass (assertion part)
|
|
78
|
+
2. Full `test/runner.integration.test.mjs`: failure set not worse than baseline
|
|
79
|
+
3. `npm run typecheck` if present
|
|
80
|
+
4. CI green (Node 22/24) after merge
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Spec: pty-attach.ts legacy quality debt cleanup (issue #8)
|
|
2
|
+
|
|
3
|
+
- **Date**: 2026-08-30
|
|
4
|
+
- **Issue**: zhuxixi/pi-agent-board#8
|
|
5
|
+
- **Type**: chore — zero behavior change (comments + signature trim only)
|
|
6
|
+
- **Status**: approved and implemented (final review clean; see docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md)
|
|
7
|
+
|
|
8
|
+
## Background
|
|
9
|
+
|
|
10
|
+
pi-lens flagged 10 legacy quality issues in `src/ui/pty-attach.ts` on 2026-08-21
|
|
11
|
+
(upstream original author's style, pre-existing). The issue tracked them for
|
|
12
|
+
separate cleanup so they wouldn't pollute feature PRs. Since then, #45 and #48
|
|
13
|
+
modified the file, so every line number in the issue has drifted. A fresh scan
|
|
14
|
+
of current main (1324 lines) found:
|
|
15
|
+
|
|
16
|
+
- **9 empty `catch {}` blocks** (issue said 8): L484, 510, 724, 745, 755, 766,
|
|
17
|
+
924, 951, 1010
|
|
18
|
+
- **1 `as unknown as` cast without a SAFETY comment**: L795 (`currentSize()`)
|
|
19
|
+
- **1 unused parameter**: L970 (`project(height, width)` — `width` never read)
|
|
20
|
+
|
|
21
|
+
Two other catches are out of scope: L902 (`onSocketData`, multi-line catch with
|
|
22
|
+
an explanatory comment) and L1048 (`openExternalTarget`, catch returns `false`).
|
|
23
|
+
|
|
24
|
+
## Goals
|
|
25
|
+
|
|
26
|
+
1. Every empty catch documents *why* silence is correct (intentional vs forgot).
|
|
27
|
+
2. The as-cast states the invariant that makes it safe.
|
|
28
|
+
3. No unused parameters in `project()`.
|
|
29
|
+
4. Zero behavior change: no logic, no logging, no reformatting beyond the edits.
|
|
30
|
+
|
|
31
|
+
## Non-goals
|
|
32
|
+
|
|
33
|
+
- No logging infrastructure (no logger is imported in pty-attach today; these
|
|
34
|
+
failures have no consumer; `forwardTerminalProtocols` runs at frame frequency
|
|
35
|
+
and would spam).
|
|
36
|
+
- No changes outside `src/ui/pty-attach.ts`.
|
|
37
|
+
- No touching L902/L1048 (already documented/behavioral).
|
|
38
|
+
- No drive-by refactors of nearby code.
|
|
39
|
+
|
|
40
|
+
## Decision: empty catches stay silent + explanatory comment (Option A)
|
|
41
|
+
|
|
42
|
+
Rejected alternative (Option B): debug-level logging — needs new UI-layer log
|
|
43
|
+
plumbing, has no consumer, and high-frequency paths would flood output.
|
|
44
|
+
|
|
45
|
+
Precedent in this repo: `dashboard.ts` uses `/* best effort: stats must never
|
|
46
|
+
block dispatch */` style comments for the same pattern.
|
|
47
|
+
|
|
48
|
+
Per-site comment text (implementation is mechanical):
|
|
49
|
+
|
|
50
|
+
| Line | Method | Failure tolerated | Comment to add |
|
|
51
|
+
|------|--------|-------------------|----------------|
|
|
52
|
+
| 484 | `enableMouseScroll()` | `terminal.write(XTSHIFTESCAPE/MOUSE_ENABLE)` | `/* best-effort: some terminals reject these sequences; mouse reporting is optional */` |
|
|
53
|
+
| 510 | `disableMouseScroll()` | `terminal.write(MOUSE_DISABLE)` | `/* best-effort: terminal may already be gone at teardown */` |
|
|
54
|
+
| 724 | `copySelectionToClipboard()` | OSC52 write | `/* best-effort: OSC52 clipboard support is optional */` |
|
|
55
|
+
| 745 | `pastePrimarySelection()` inner timer | `child.kill("SIGKILL")` | `/* the child may have already exited before the timeout fired */` |
|
|
56
|
+
| 755 | `pastePrimarySelection()` outer | `spawn("xclip")` | `/* silent no-op when xclip is absent — documented contract of this helper */` |
|
|
57
|
+
| 766 | `writePrimarySelection()` | `spawn("xclip")` | `/* silent no-op when xclip is absent */` |
|
|
58
|
+
| 924 | `forwardTerminalProtocols()` | per-sequence `terminal.write` | `/* best-effort: forwarded sequences are enhancements, never critical */` |
|
|
59
|
+
| 951 | `replayScreenLog()` | screen.log read/replay | `/* best-effort: a missing or racing screen.log must not block attach */` |
|
|
60
|
+
| 1010 | `close()` | `socket.destroy()` | `/* best-effort teardown: socket may already be destroyed */` |
|
|
61
|
+
|
|
62
|
+
## Decision: SAFETY comment for the as-cast (L795)
|
|
63
|
+
|
|
64
|
+
`currentSize()` reads `this.tui.terminal as unknown as { cols?: number;
|
|
65
|
+
columns?: number; rows?: number } | undefined`. Comment to add above the line:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
// SAFETY: duck-typed read — Pi TUI's Terminal type does not consistently expose
|
|
69
|
+
// cols/columns/rows across versions (see resizeIfNeeded below). Runtime
|
|
70
|
+
// fallbacks (120/24) keep this safe when the fields are absent.
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Decision: delete `width` param (not `_` prefix)
|
|
74
|
+
|
|
75
|
+
`private project(height: number, width: number)` → `private project(height: number)`;
|
|
76
|
+
single call site L271 `this.project(bodyHeight, width)` → `this.project(bodyHeight)`.
|
|
77
|
+
Deleting is cleaner than `_width`: private method, exactly one caller, no
|
|
78
|
+
interface stability concerns.
|
|
79
|
+
|
|
80
|
+
## Verification
|
|
81
|
+
|
|
82
|
+
1. `npm run typecheck` — clean.
|
|
83
|
+
2. `npm test` — all pass (attach-related suites must stay green).
|
|
84
|
+
3. `grep -c "catch {}" src/ui/pty-attach.ts` — 0 (each expanded to a documented
|
|
85
|
+
3-line catch block).
|
|
86
|
+
4. `grep -n "project(" src/ui/pty-attach.ts` — signature and call site both
|
|
87
|
+
single-arg.
|
|
88
|
+
5. Coverage thresholds unaffected (comments + signature trim don't move lines/funcs/branches).
|
|
89
|
+
|
|
90
|
+
## Risks & mitigations
|
|
91
|
+
|
|
92
|
+
- **Line drift vs this spec**: implementation re-locates sites by method name
|
|
93
|
+
(as in the table), not by line number.
|
|
94
|
+
- **Untracked files in main checkout** (`scratch/`, `test-support/*`,
|
|
95
|
+
`test/pty-attach-*.test.mjs`): all work happens in a worktree; staging is
|
|
96
|
+
per-file, never `git add -A`.
|
|
97
|
+
- **Zero-behavior guarantee**: no statement is added/removed except the param
|
|
98
|
+
deletion and its call-site argument; review diff must show comments + two-line
|
|
99
|
+
signature/call-site change only.
|
|
100
|
+
|
|
101
|
+
## Rollout
|
|
102
|
+
|
|
103
|
+
- Worktree: `issue-8-pty-attach-quality-debt` (from main).
|
|
104
|
+
- Spec lands in worktree `docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md`
|
|
105
|
+
as the first commit, then plan → implement → local CR → PR.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Design: User-first README v2 for Pi Agent Board
|
|
2
|
+
|
|
3
|
+
**Issue:** #51
|
|
4
|
+
**Date:** 2026-08-30
|
|
5
|
+
**Status:** Approved
|
|
6
|
+
**Language:** English
|
|
7
|
+
|
|
8
|
+
## Outcome
|
|
9
|
+
|
|
10
|
+
Rewrite `README.md` into a user-first English guide that accurately describes the currently shipped Pi Agent Board package, verified by current source, package metadata, tests, and manual verification notes.
|
|
11
|
+
|
|
12
|
+
The README will take a new user from installation to a first background session, then serve as a practical reference for dashboard actions, attach mode, filters, configuration, limitations, troubleshooting, and maintainer entry points.
|
|
13
|
+
|
|
14
|
+
This is documentation-only. No product behavior changes are part of the work.
|
|
15
|
+
|
|
16
|
+
## Source of truth
|
|
17
|
+
|
|
18
|
+
- `package.json` is authoritative for package identity, version, Node engine, repository, scripts, and Pi package metadata.
|
|
19
|
+
- `src/index.ts`, `src/commands/*`, `src/ui/dashboard.ts`, `src/ui/pty-attach.ts`, `src/runtime/service.mjs`, and `src/core/*` are authoritative for shipped behavior.
|
|
20
|
+
- `PRD.md`, `PROGRESS.md`, `REMAINING_WORK.md`, and dated design/plan documents are historical or planning material; they must not advertise disabled or planned behavior.
|
|
21
|
+
- Every command, shortcut, environment variable, default, and limitation must be checked against an exact source location or a verified command.
|
|
22
|
+
- Clearly distinguish shipped behavior, fallback behavior, disabled behavior, and planned/internal behavior.
|
|
23
|
+
- Avoid hard-coded test counts; state that CI and `npm run verify` are authoritative.
|
|
24
|
+
- Use `AGENT_BOARD_*` names for new configuration. Mention selected `AGENT_VIEW_*` names only as migration aliases.
|
|
25
|
+
|
|
26
|
+
## Information architecture
|
|
27
|
+
|
|
28
|
+
Use this task-oriented structure:
|
|
29
|
+
|
|
30
|
+
1. Title, package links, value proposition
|
|
31
|
+
2. What it does / when to use it
|
|
32
|
+
3. Requirements
|
|
33
|
+
4. Installation
|
|
34
|
+
5. Quick start
|
|
35
|
+
6. Entry points
|
|
36
|
+
7. Dashboard workflow
|
|
37
|
+
8. Views and actions
|
|
38
|
+
9. States, grouping, and filters
|
|
39
|
+
10. Attach mode
|
|
40
|
+
11. Persistence, safety, and limitations
|
|
41
|
+
12. Configuration
|
|
42
|
+
13. Troubleshooting
|
|
43
|
+
14. Development
|
|
44
|
+
15. Publishing
|
|
45
|
+
16. Further reading
|
|
46
|
+
|
|
47
|
+
The first half should be readable without knowing Pi internals. Advanced QA and implementation details should be linked rather than expanded inline.
|
|
48
|
+
|
|
49
|
+
## Content requirements
|
|
50
|
+
|
|
51
|
+
### Positioning
|
|
52
|
+
|
|
53
|
+
State that Agent Board is a full-screen TUI dashboard for dispatching, monitoring, inspecting, replying to, attaching to, and managing multiple durable background Pi sessions. Emphasize global cross-project visibility, resumability, dashboard triage, inline reply/evidence, and PTY/JSON fallback. Do not imply cloud execution, multi-user sharing, automatic worktree isolation, or full Claude parity.
|
|
54
|
+
|
|
55
|
+
### Requirements and installation
|
|
56
|
+
|
|
57
|
+
Include Pi, Node.js 20+, working Pi provider authentication, and PTY support for live attach/start-and-attach. Use `pi install npm:@zhuxixi/pi-agent-board` everywhere. Keep local path installation and symlink discovery as separate alternatives. Include a short one-shot auth sanity check and link detailed checks to `VERIFY.md`.
|
|
58
|
+
|
|
59
|
+
### Quick start and entry points
|
|
60
|
+
|
|
61
|
+
Use a concrete five-step first-task flow: `i` INSERT mode → type task → `Enter` to open the Start session dialog → review cwd/model/thinking/action → `Enter` on Start session to launch. Explain Space Peek, `r` in Peek, `v`, `e`, attach with Enter/Right/`>`, and that PTY detach with `Left` is gated by the child input line: it detaches on empty input, is forwarded while editing, and remains unconditional when disconnected. `Ctrl+]` is not a detach key — it is passed through to the child Pi editor.
|
|
62
|
+
|
|
63
|
+
Document `/agent-board`, `pi /agent-board`, `pi --agent-board`, and `/bg [prompt]`, including that `--agent-board` startup cannot attach and normal `/agent-board` is required for attach.
|
|
64
|
+
|
|
65
|
+
### Dashboard and actions
|
|
66
|
+
|
|
67
|
+
Explain Normal vs INSERT mode, draft-vs-empty `Enter`, Ctrl+N entering INSERT mode with a pre-filled `hello` prompt (the next Enter opens the launch flow), cwd favorites/path completion, model/thinking/action fields, persisted launch preferences, and PTY-dependent start-and-attach fallback.
|
|
68
|
+
|
|
69
|
+
Document exact destructive semantics: `d` confirms inactive Done; manual completion is default; Ctrl+X twice quickly archives; archive preserves the session file; X deletes inactive rows in the selected state; `m` batch selection supports Space/a/u/d/Ctrl+X.
|
|
70
|
+
|
|
71
|
+
Keep shortcut reference separated by view. State that `r` is available from Peek/Transcript/Evidence, not directly from the main list, and pending Pi questions must be answered via attach rather than inline reply.
|
|
72
|
+
|
|
73
|
+
### States, views, and filters
|
|
74
|
+
|
|
75
|
+
Document the seven labels: Queued, Running, Needs answer, Needs instructions, Done, Failed, Stopped. Explain separate process liveness, state grouping, folder grouping, pinned-first stable creation ordering, unread indicators, Peek, read-only transcript, Evidence/Diagnostics, durable FIFO follow-up queue, and `qN`.
|
|
76
|
+
|
|
77
|
+
Document filter syntax: `s:`, `review:ready`, `diag:stalled`, `evidence:error`, `queued:true|yes|1`, `steer:`, and free-text AND over name/summary/cwd. Explain aliases and case-insensitivity. Caveat that `diag:stalled` can consume persisted diagnostics but there is no general current provider-stall detector.
|
|
78
|
+
|
|
79
|
+
Explain locally extracted issue/PR badges and optional per-root `providers.json`, without asserting an unverified custom schema.
|
|
80
|
+
|
|
81
|
+
### Attach and persistence
|
|
82
|
+
|
|
83
|
+
Describe PTY attach, conditional `Left` detach (empty child input detaches; edited input forwards the key; disconnected hosts can always be exited), `Ctrl+]` passthrough to the child Pi editor, PageUp/PageDown/Home/End/mouse wheel scrollback, link opening, drag/double-click copy, optional X11 middle-click paste, clipboard/image passthrough, cold-host loading/reconnect, warm host pool, and Windows named-pipe/hidden-console behavior without promising terminal-emulator parity.
|
|
84
|
+
|
|
85
|
+
Explain PTY vs JSON fallback, adopted external-session PTY requirement, and `!` diagnostics. Document the default store at `~/.pi/agent/agent-board/`, high-level artifacts, persistence across reload/restart/worker exit, and stale-row reconciliation.
|
|
86
|
+
|
|
87
|
+
Prominently state that worktree isolation is currently disabled and not automatically created; same-repository concurrent writes are unsafe unless the user manually avoids overlap or supplies isolation. Also state no cloud/multi-user coordination, row deletion preserves session files, auth remains required, startup attach limitation, and PTY native-dependency limitation.
|
|
88
|
+
|
|
89
|
+
### Configuration
|
|
90
|
+
|
|
91
|
+
List supported user-facing settings with exact defaults/disable values: ROOT, AUTO_STATE, AUTO_STATE_MODEL, AUTO_STATE_NO_DONE, SUMMARY_MODEL, TITLE_MODEL, TITLE_THINKING_LEVEL, CODE_REFS, DISABLE_PTY, FORCE_PTY, ATTACH_MOUSE, ENABLE_MOUSE_SCROLL, WHEEL_LINES, MAX_WARM_HOSTS, WARM_HOST_TTL_MS, ATTACH_NATIVE_PASTE, FORWARD_OSC52, and FORWARD_IMAGES.
|
|
92
|
+
|
|
93
|
+
Do not list internal child markers. Do not advertise `AGENT_BOARD_ALLOW_PIPE_FALLBACK` as a normal user toggle because current service dispatch does not pass the ambient variable into the injected PTY runner config. Mention selected legacy `AGENT_VIEW_*` aliases only as compatibility paths.
|
|
94
|
+
|
|
95
|
+
### Troubleshooting and maintainer sections
|
|
96
|
+
|
|
97
|
+
Troubleshoot stuck Running/auth, `node-pty unavailable`, slow/reconnecting attach, start-and-attach fallback, rejected inline replies, and same-repo conflicts. Link `VERIFY.md`.
|
|
98
|
+
|
|
99
|
+
Keep Development to `npm install` and `npm run verify`; explain verify briefly. Keep Publishing to verify, version bump, and publish. Link further reading (`VERIFY.md`, `PRD.md`, `PROGRESS.md`, and relevant design docs) without turning README into a historical log.
|
|
100
|
+
|
|
101
|
+
## Supporting documentation
|
|
102
|
+
|
|
103
|
+
Correct the old unscoped install command in `VERIFY.md` from `pi install npm:pi-agent-board` to `pi install npm:@zhuxixi/pi-agent-board`, because README links to it. Make no other supporting-doc changes.
|
|
104
|
+
|
|
105
|
+
## Validation
|
|
106
|
+
|
|
107
|
+
- Line-by-line compare the final README with source and package metadata.
|
|
108
|
+
- Check Markdown link targets and stale package names/status wording.
|
|
109
|
+
- Run targeted documentation scans.
|
|
110
|
+
- Run verification in the isolated worktree; do not count the main session's untracked PTY tests as evidence.
|
|
111
|
+
- Final diff should contain README, this approved spec, the implementation plan, and the one-line VERIFY correction only.
|
|
112
|
+
|
|
113
|
+
## Non-goals
|
|
114
|
+
|
|
115
|
+
No runtime behavior, worktree implementation, plan-approval UI, provider-stall detection, docs generator, changelog, or broad PRD/progress rewrite.
|
package/package.json
CHANGED