pi-multi-viewers 0.8.1 → 0.8.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/AGENTS.md +4 -2
- package/README.md +4 -3
- package/docs/design.md +6 -2
- package/docs/reviews/README.md +3 -0
- package/meeting_fs.py +11 -0
- package/mv_cli.py +46 -3
- package/package.json +1 -1
- package/prompts/multi-viewers-setup.md +13 -10
- package/scripts/archive-result.sh +101 -0
- package/start_discussion.py +14 -1
package/AGENTS.md
CHANGED
|
@@ -28,13 +28,15 @@ human_sayer.py 【human 通道】插话命令(单次/stdin/交互 -i)
|
|
|
28
28
|
scripts/mv.sh 稳定入口 shim(exec mv_cli.py;路径被 prompt/README 引用)
|
|
29
29
|
scripts/pi-probe.sh LLM 探针(跑 pi + 登记新 session → 残留检查器可追溯)
|
|
30
30
|
scripts/check-residue.sh 残留检查(session/进程/目录三类;增删 scripts/ 时同步本节)
|
|
31
|
-
|
|
31
|
+
scripts/archive-result.sh 归档 result.md(机械部分:逐字复制+校验/存档头骨架/索引行/删源副本)
|
|
32
|
+
mv_cli.py 命令行实现(prepare/start/status/report/wait/cleanup/view/say/viewers/set-viewer)
|
|
32
33
|
extensions/multi-viewers/ 【单一扩展单元】index.ts = 三命令 + shared.ts = 助手
|
|
33
34
|
/multi-viewers 分析入口(prepare→暂停点弹窗→start→预填+打印观看命令)
|
|
34
35
|
/multi-viewers-finish 收尾(status→确认→cleanup)
|
|
35
36
|
/multi-viewers-say 插话(零 LLM,直接 spawn human_sayer.py)
|
|
36
37
|
⚠ extensions/ 平级禁放 .ts 助手(加载器会把平级文件当独立扩展)
|
|
37
|
-
prompts/multi-viewers-setup.md /multi-viewers-setup
|
|
38
|
+
prompts/multi-viewers-setup.md /multi-viewers-setup 建视角入口(建议→你定→**mv.sh --set-viewer** 落盘→给审)
|
|
39
|
+
# 那四条本来写进 prompt 的纪律(命名/不覆盖/非空/校验)改由命令保证
|
|
38
40
|
docs/design.md 设计文档(fork 源模式与规模口径 + 决策记录)
|
|
39
41
|
package.json npm 包 pi-multi-viewers(pi.prompts 注册;**版本号唯一事实源**)
|
|
40
42
|
templates/ AGENTS.md.tpl / agent.md.tpl / gitignore.tpl / spec-readme.md.tpl
|
package/README.md
CHANGED
|
@@ -112,15 +112,16 @@ scripts/mv.sh --view # 一次性增量查看(主 pi
|
|
|
112
112
|
scripts/mv.sh --say "<文本>" # 插话(命令行形态;pi 内用 /multi-viewers-say)
|
|
113
113
|
scripts/mv.sh --status # 状态 + 路径(取值与含义以该命令输出为准)
|
|
114
114
|
scripts/mv.sh --report # 只读报告(流程/配额/进程/LLM/档位对照;冷路径,不持久化)
|
|
115
|
-
scripts/mv.sh --cleanup # 收尾(result.md
|
|
115
|
+
scripts/mv.sh --cleanup # 收尾(result.md + 报告都留存到 <dir>-*.md/.txt)
|
|
116
116
|
scripts/mv.sh --viewers # 列出+校验当前项目 viewers/(只读;建视角时用)
|
|
117
|
+
scripts/mv.sh --set-viewer <名字> # 新建视角文件(正文从 stdin 读;只新建不覆盖)
|
|
117
118
|
```
|
|
118
119
|
|
|
119
120
|
## 视角文件写什么(`viewers/<视角名>.md`)
|
|
120
121
|
|
|
121
122
|
建视角**推荐**走 `/multi-viewers-setup`(交互式:先给候选建议 → 你定建哪几个 →
|
|
122
|
-
落盘 →
|
|
123
|
-
|
|
123
|
+
`--set-viewer` 落盘 → 展示给你审);也可以手写,写完用 `scripts/mv.sh --viewers`
|
|
124
|
+
自查(列出并用代码判据校验名字与空正文)。
|
|
124
125
|
|
|
125
126
|
一个视角文件 = **一份视角说明**,纯内容、无格式要求(无 frontmatter、
|
|
126
127
|
无需标题,**文件名就是全部元数据**)。三个要点(措辞经实验验证):
|
package/docs/design.md
CHANGED
|
@@ -103,7 +103,7 @@ compaction 的 `firstKeptEntryId` 起 + 其后的条目"——窗口内含 compa
|
|
|
103
103
|
| `status-<agent>.json` | loop | `{"sessionID": ...}` | 流程(崩溃恢复) | 是(恢复用) | O(1) |
|
|
104
104
|
| `pi-sessions/fork-src-*.jsonl` | pi | 文档化 session schema(`usage`/`stopReason`/`timestamp`/`thinkingLevel`) | fork 构建 + `--report` | 否(报告用) | O(MB) 全量 → **禁轮询** |
|
|
105
105
|
| `result.md`(固定位) | resultWriter loop | 结论文档 | 人 | 是(收尾判据) | — |
|
|
106
|
-
| `--report`(视图) | observability |
|
|
106
|
+
| `--report`(视图) | observability | 文本行(`--cleanup` 另落盘 `<base>-report.txt`) | 人(**三个出口**,见下) | **否**(不得升级为验收 gate) | 冷路径一次性 —— **O(session 大小)**:每 agent 读整个 fork-src jsonl(实测 3 × 789KB ≈ 2.4MB/次、50–150ms/次,×3 出口 <0.3s/次分析),**不得进入任何轮询路径**(e2e16 评审量化) |
|
|
107
107
|
|
|
108
108
|
**报告的字段集**(e2e17 评审后定稿,后续增补不计数——字段行以本表为准)——
|
|
109
109
|
**谓词分组 + 对照 + 事实行**,
|
|
@@ -168,7 +168,7 @@ BOUNDARY_TYPE`)——**显式登记"历史(fork 携带)/ 本轮"的分界*
|
|
|
168
168
|
|
|
169
169
|
**报告的打印位置**:`--report`(手动,任意时刻)+ `--cleanup` 前(自动,
|
|
170
170
|
删目录前最后一次可读——目录删后 `--report` 不可用)。cleanup 层对报告
|
|
171
|
-
fail-open
|
|
171
|
+
fail-open(报告失败不阻断清理,且**打印**失败原因不静默)。报告随 `--cleanup` **落盘**一份到 `<base>-report.txt`(与 `-result.md` 同级)——原先只在终端出现一次,目录删掉后无法复查(复盘时长口径时踩到,用户 2026-09-25 定)。
|
|
172
172
|
|
|
173
173
|
消费规则:`meeting_fs.iter_after_boundary` 只产出边界之后的条目;**未找到
|
|
174
174
|
边界(老产物/手工 session)→ 返回空、按 n/a 处理,不得退回全文扫描**
|
|
@@ -575,6 +575,10 @@ commit 是溯源记录、本节是长期引用点——不并存两份权威值
|
|
|
575
575
|
resume 新命令面、env 回退配置、`runCli` 超时、启动路径继续优化(已在 1–2s 地板)、
|
|
576
576
|
给 stalled 加第三种动作。**契约例外**:扩展作为包内第一方消费者**直连**
|
|
577
577
|
`mv_cli.py`(实测 shim 61ms vs 直连 58–66ms,性能上零差异;按契约一致性记例外一行)。
|
|
578
|
+
**建视角流程(`/multi-viewers-setup`,仍是 prompt)**:机械部分下移到
|
|
579
|
+
`mv.sh --set-viewer <名字>`(正文从 stdin 读)——**命名规则 / 不覆盖已有 / 空正文拒绝 /
|
|
580
|
+
写完校验并回显**四条由命令保证(此前是 prompt 里给 LLM 的纪律,会漏);prompt 只负责
|
|
581
|
+
**看项目给候选 + 内容撰写 + 与用户来回**(那才是 LLM 该做的)。
|
|
578
582
|
**UI 通道按 mode 分级(2026-09-25 实测)**:dialog(`select`/`confirm`/`input`/`editor`)
|
|
579
583
|
全模式可用(RPC/web 走请求-响应子协议、阻塞等用户;不带 `timeout` 即不倒计时,
|
|
580
584
|
暂停点成立);`notify` 全模式可用(TUI = showStatus 行;pi-web = **追加进聊天流的
|
package/docs/reviews/README.md
CHANGED
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
真实缺陷(见下)。
|
|
12
12
|
2. **决策溯源**:修复批次(commit)常引用报告中的编号(如"P0/P1/P2"),
|
|
13
13
|
保留原文才能核对当时的事实与推理。
|
|
14
|
+
|
|
15
|
+
归档动作的**机械部分**由 `scripts/archive-result.sh` 做(命名、正文逐字复制+字节校验、
|
|
16
|
+
存档头骨架、索引行、删仓库根松散副本)——判断部分(主题/问题/commit)仍由人写。
|
|
14
17
|
3. **方法论的样本**:报告本身演示了"观察句 vs 机制句"的证据强度要求
|
|
15
18
|
(`docs/test-methodology.md` 方法 16)——其中也有几处机制句在讨论中被
|
|
16
19
|
交叉复核推翻,是活教材。
|
package/meeting_fs.py
CHANGED
|
@@ -47,6 +47,17 @@ def result_path(base):
|
|
|
47
47
|
"""
|
|
48
48
|
return f"{base}-{RESULT_MD}"
|
|
49
49
|
|
|
50
|
+
|
|
51
|
+
def report_path(base):
|
|
52
|
+
"""分析报告的固定落盘位(`<分析目录>-report.txt`,与 result.md 同级)。
|
|
53
|
+
|
|
54
|
+
报告原本只在 `--cleanup` 时往终端打一次,目录一删就再也拿不到——复盘
|
|
55
|
+
(时长口径、配额、provider 失败这类)时无据可查(用户 2026-09-25 复盘
|
|
56
|
+
回算不出实际时长,正是此缺口)。观测数字应当可复查,故随清理落盘一份。
|
|
57
|
+
`--report` 命令本身仍不持久化(冷路径视图,三个出口共享同一实现)。
|
|
58
|
+
"""
|
|
59
|
+
return f"{base}-report.txt"
|
|
60
|
+
|
|
50
61
|
# 协议参数默认值(gen_protocol 固化进 protocol.json)——**唯一声明点**:
|
|
51
62
|
# CLI default、engine 签名默认、observability 的兜底读取都引用这里
|
|
52
63
|
# (此前 10/7 在三处各写一遍,改一处不改另一处就会漂移)。
|
package/mv_cli.py
CHANGED
|
@@ -54,6 +54,7 @@ USAGE = f"""用法:
|
|
|
54
54
|
{PROG} --view [dir] [--since <ref>]
|
|
55
55
|
{PROG} --say [dir] "<文本>"
|
|
56
56
|
{PROG} --viewers # 列出并校验当前项目的 viewers/(只读;建视角时用)
|
|
57
|
+
{PROG} --set-viewer <名字> # 新建一个视角文件(正文从 stdin 读;只新建不覆盖)
|
|
57
58
|
|
|
58
59
|
消费命令的 <dir> 可省略(自动发现本 session 当前分析——按 cwd 下
|
|
59
60
|
mv-<PI_SESSION_ID>-* 最新;无匹配则报错要求显式传目录)
|
|
@@ -153,9 +154,6 @@ def cmd_viewers(args):
|
|
|
153
154
|
目录**(消费命令的目录自动发现对此不适用——它找的是 mv-<sid>-*)。判据全部
|
|
154
155
|
复用 `spec_gen` 的单一实现(列举 `list_agent_md` / 名字 `check_agent_name` /
|
|
155
156
|
集合 `viewer_set_error`),这一层不另写一套规则。
|
|
156
|
-
|
|
157
|
-
数量不足(<2)在这里是**提示**不是错误:建 1 个是合法的中间状态,只有启动
|
|
158
|
-
一次分析时才要求 ≥2(那条判据仍由 `viewer_set_error` 独占)。
|
|
159
157
|
"""
|
|
160
158
|
if args:
|
|
161
159
|
fail(f"未知参数: {' '.join(args)}(--viewers 不接受参数——只检查项目 cwd 的 viewers/)")
|
|
@@ -163,6 +161,17 @@ def cmd_viewers(args):
|
|
|
163
161
|
if not os.path.isdir(vdir):
|
|
164
162
|
fail(f"未找到 {vdir}——视角文件放在项目 cwd 的 viewers/<视角名>.md"
|
|
165
163
|
f"(文件名即视角名;可跑 /multi-viewers-setup 交互式建立)")
|
|
164
|
+
return _validate_and_print_viewers(vdir)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _validate_and_print_viewers(vdir):
|
|
168
|
+
"""列出 + 校验 + 打印(`--viewers` 与 `--set-viewer` 的**同一实现**)。
|
|
169
|
+
|
|
170
|
+
判据全部复用 `spec_gen` 的单一实现(列举 `list_agent_md` / 名字
|
|
171
|
+
`check_agent_name` / 集合 `viewer_set_error`)——这一层不另写规则。
|
|
172
|
+
数量不足(<2)是**提示**不是错误:建 1 个是合法中间状态(只有启动一次
|
|
173
|
+
分析才要求 ≥2,那条判据由 `viewer_set_error` 独占)。
|
|
174
|
+
"""
|
|
166
175
|
names, _briefs, empty = spec_gen._discover_viewers(vdir)
|
|
167
176
|
if not names:
|
|
168
177
|
fail(f"{vdir} 下没有 *.md——文件名即视角名(如 viewers/效率.md)")
|
|
@@ -190,6 +199,38 @@ def cmd_viewers(args):
|
|
|
190
199
|
return 0
|
|
191
200
|
|
|
192
201
|
|
|
202
|
+
def cmd_set_viewer(args):
|
|
203
|
+
"""新建一个视角文件:`--set-viewer <名字>`,正文**从 stdin 读**。
|
|
204
|
+
|
|
205
|
+
为什么是命令而不是"让 LLM 直接写文件":文件名即视角名,于是**命名规则 /
|
|
206
|
+
不覆盖已有 / 空正文拒绝 / 写完校验并回显**这四条本来只能写在 prompt 里当
|
|
207
|
+
纪律(LLM 会漏),现在由机制保证(判据复用 `spec_gen` 单一实现)。LLM/人
|
|
208
|
+
只负责**内容**——那是它该做的部分。不提供 `--force`:本命令语义 = 只新建,
|
|
209
|
+
改已有视角请直接编辑文件。
|
|
210
|
+
"""
|
|
211
|
+
if len(args) != 1:
|
|
212
|
+
fail("用法: --set-viewer <名字>(正文从 stdin 读;如 "
|
|
213
|
+
"`mv.sh --set-viewer 效率 <<'EOF' … EOF`)")
|
|
214
|
+
name = args[0]
|
|
215
|
+
err = spec_gen.check_agent_name(name)
|
|
216
|
+
if err:
|
|
217
|
+
fail(f"非法视角名({err}):{name}")
|
|
218
|
+
vdir = os.path.join(os.getcwd(), "viewers")
|
|
219
|
+
target = os.path.join(vdir, f"{name}.md")
|
|
220
|
+
if os.path.exists(target):
|
|
221
|
+
fail(f"视角已存在,不覆盖: {target}(改名,或直接编辑该文件)")
|
|
222
|
+
body = sys.stdin.read().strip()
|
|
223
|
+
if not body:
|
|
224
|
+
fail(f"视角内容为空({name})——正文从 stdin 传入;空视角没有 lenses,"
|
|
225
|
+
f"分析会退化成同名随机视角")
|
|
226
|
+
os.makedirs(vdir, exist_ok=True)
|
|
227
|
+
with open(target, "w", encoding="utf-8") as f:
|
|
228
|
+
f.write(body + "\n")
|
|
229
|
+
print(f"[set-viewer] 已写入 {target}\n")
|
|
230
|
+
print(body + "\n")
|
|
231
|
+
return _validate_and_print_viewers(vdir)
|
|
232
|
+
|
|
233
|
+
|
|
193
234
|
def cmd_status(args):
|
|
194
235
|
d = _dir_only("--status", args)
|
|
195
236
|
return _call([PYTHON, START_DISCUSSION, "--dir", d, "--status"])
|
|
@@ -418,6 +459,8 @@ def main(argv=None):
|
|
|
418
459
|
return cmd_start(rest[0], rest[1:])
|
|
419
460
|
if cmd == "--viewers":
|
|
420
461
|
return cmd_viewers(rest)
|
|
462
|
+
if cmd == "--set-viewer":
|
|
463
|
+
return cmd_set_viewer(rest)
|
|
421
464
|
if cmd == "--status":
|
|
422
465
|
return cmd_status(rest)
|
|
423
466
|
if cmd == "--report":
|
package/package.json
CHANGED
|
@@ -15,8 +15,8 @@ description: 建立多视角分析的 viewers/ 视角文件(交互式:先建
|
|
|
15
15
|
1. <名字> —— <一行镜头>(与其它候选怎样互补/对立)
|
|
16
16
|
|
|
17
17
|
- 这一步**只提议**:不建文件、不改任何东西
|
|
18
|
-
- 名字:中文、短(≤32 字符)、不含空白与路径分隔符、**不要用 `human`**(保留名)
|
|
19
18
|
- 建几个**由用户决定**(只建 1 个也行)
|
|
19
|
+
- 名字规则由第 3 步的命令强制(顺带一提:中文、短、不含空白与路径分隔符)
|
|
20
20
|
|
|
21
21
|
**然后结束本回合,等用户输入。**
|
|
22
22
|
|
|
@@ -25,10 +25,9 @@ description: 建立多视角分析的 viewers/ 视角文件(交互式:先建
|
|
|
25
25
|
用户会告诉你建哪几个(可能只有一个、也可能点名不在候选里的)。**以用户输入为准**;
|
|
26
26
|
若某个视角该用什么镜头没说清 → **问一句**,不要替他定。
|
|
27
27
|
|
|
28
|
-
## 第 3
|
|
28
|
+
## 第 3 步:用命令建文件(内容经 stdin 传给命令,不要直接写文件)
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
命名规则)。要点:
|
|
30
|
+
先读 `README.md` 的「视角文件写什么」节(唯一事实源:三个要点 + 正误对照)。要点:
|
|
32
31
|
|
|
33
32
|
- 纯内容、无 frontmatter、无标题——**文件名就是全部元数据**
|
|
34
33
|
- 必含三条:① **单一 lenses**(所有观点必须从该视角出发)② **不越界**(其它视角由
|
|
@@ -37,19 +36,23 @@ description: 建立多视角分析的 viewers/ 视角文件(交互式:先建
|
|
|
37
36
|
- **只写视角本身**:身份("你是 X")、参与者名单、消息格式、独立参与者纪律都由脚本
|
|
38
37
|
生成,**不要写进文件**
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
每个视角调用一次(正文经 stdin;**命名合法性、不覆盖已有、空正文拒绝、写完校验并
|
|
40
|
+
回显**都由命令保证,不需要你另外检查):
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
~/.pi/agent/npm/node_modules/pi-multi-viewers/scripts/mv.sh --
|
|
43
|
+
~/.pi/agent/npm/node_modules/pi-multi-viewers/scripts/mv.sh --set-viewer <视角名> <<'EOF'
|
|
44
|
+
<视角正文>
|
|
45
|
+
EOF
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
命令会回显**写入路径 + 正文全文 + 校验结果**——那就是第 4 步要展示的东西。
|
|
49
|
+
若命令报"视角已存在":告诉用户,停下等他决定(改名,或他明确要求改动)。
|
|
48
50
|
|
|
49
51
|
## 第 4 步:展示,等审阅
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
把命令的回显(路径 + 全文 + 校验)给用户看,请他提修改意见或确认:
|
|
52
54
|
|
|
53
|
-
- 有意见 →
|
|
55
|
+
- 有意见 → **直接编辑该文件**(`viewers/<名字>.md`)→ 跑一次
|
|
56
|
+
`~/.pi/agent/npm/node_modules/pi-multi-viewers/scripts/mv.sh --viewers` 复核 → 再展示(反复直到他确认)
|
|
54
57
|
- 确认后一行交接:视角已建好(启动一次分析至少需要 2 个视角),接下来
|
|
55
58
|
`/multi-viewers "<主题>"` 即可
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# archive-result.sh —— 把一场分析的 result.md 归档进 docs/reviews/
|
|
3
|
+
#
|
|
4
|
+
# 为什么是脚本:每场分析后都要做同一串机械动作,靠人/LLM 记(实测会漏——
|
|
5
|
+
# 最容易漏的是索引行)。这里把**机械部分**固化成命令:
|
|
6
|
+
# · 命名(<日期>-<slug>.md)
|
|
7
|
+
# · 正文**逐字复制**并做字节校验(存档不改原文)
|
|
8
|
+
# · 存档头骨架(TODO 标记留在文件里,判断内容仍由人写)
|
|
9
|
+
# · 索引表加一行(插在最后一行存档之后)
|
|
10
|
+
# · 删掉仓库根的松散副本
|
|
11
|
+
# **判断部分不代劳**:写什么主题/抓到什么问题/落地哪个 commit,是人的判断。
|
|
12
|
+
#
|
|
13
|
+
# 用法:
|
|
14
|
+
# scripts/archive-result.sh <result.md> --slug <slug> [--date YYYY-MM-DD] [--dry-run]
|
|
15
|
+
# 例:
|
|
16
|
+
# scripts/archive-result.sh mv-mv-main-20260925-105614-result.md \
|
|
17
|
+
# --slug multi-viewers-postfix-review
|
|
18
|
+
#
|
|
19
|
+
# 退出码:0 成功(或 dry-run);1 参数/环境错;2 目标已存在(不覆盖)
|
|
20
|
+
|
|
21
|
+
set -u
|
|
22
|
+
HERE="$(cd "$(dirname "$0")/.." && pwd)"
|
|
23
|
+
REVIEWS="$HERE/docs/reviews"
|
|
24
|
+
INDEX="$REVIEWS/README.md"
|
|
25
|
+
|
|
26
|
+
SRC=""; SLUG=""; DATE="$(date +%F)"; DRY=0
|
|
27
|
+
while [ $# -gt 0 ]; do
|
|
28
|
+
case "$1" in
|
|
29
|
+
--slug) SLUG="${2:-}"; shift 2 ;;
|
|
30
|
+
--date) DATE="${2:-}"; shift 2 ;;
|
|
31
|
+
--dry-run) DRY=1; shift ;;
|
|
32
|
+
-h|--help) sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
|
33
|
+
-*) echo "错误: 未知参数 $1" >&2; exit 1 ;;
|
|
34
|
+
*) SRC="$1"; shift ;;
|
|
35
|
+
esac
|
|
36
|
+
done
|
|
37
|
+
|
|
38
|
+
[ -n "$SRC" ] || { echo "错误: 缺少 result.md 路径(--help 看用法)" >&2; exit 1; }
|
|
39
|
+
[ -n "$SLUG" ] || { echo "错误: 缺少 --slug <slug>" >&2; exit 1; }
|
|
40
|
+
[ -f "$SRC" ] || { echo "错误: 文件不存在: $SRC" >&2; exit 1; }
|
|
41
|
+
case "$SLUG" in *[!a-zA-Z0-9._-]*) echo "错误: slug 只允许字母/数字/._-(当前: $SLUG)" >&2; exit 1 ;; esac
|
|
42
|
+
[ -f "$INDEX" ] || { echo "错误: 找不到索引 $INDEX" >&2; exit 1; }
|
|
43
|
+
|
|
44
|
+
DEST="$REVIEWS/$DATE-$SLUG.md"
|
|
45
|
+
if [ -e "$DEST" ]; then
|
|
46
|
+
echo "错误: 目标已存在(不覆盖): $DEST" >&2; exit 2
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
if [ "$DRY" -eq 1 ]; then
|
|
50
|
+
echo "[dry-run] 将归档: $SRC → $DEST"
|
|
51
|
+
echo "[dry-run] 正文 $(wc -c < "$SRC") 字节,逐字复制"
|
|
52
|
+
echo "[dry-run] 将写存档头骨架(含 TODO)+ 在 $INDEX 加一行 + 删 $SRC"
|
|
53
|
+
exit 0
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# 1) 存档头骨架 + 正文(逐字)
|
|
57
|
+
{
|
|
58
|
+
cat <<EOF
|
|
59
|
+
<!-- 存档:docs/reviews/$DATE-$SLUG.md
|
|
60
|
+
来源:一次真实多视角分析的 result.md 原文(未删改,仅加本头与下方说明)。
|
|
61
|
+
分析场次目录已随 cleanup 删除;文中消息编号不可再核验,仅作溯源线索
|
|
62
|
+
(与代码注释引用约定一致:行为以自描述为准)。 -->
|
|
63
|
+
|
|
64
|
+
# 存档说明
|
|
65
|
+
|
|
66
|
+
- **主题**:TODO(一句话)
|
|
67
|
+
- **场次**:\`TODO(分析目录名)\`(视角 / 档位 / 收敛方式)
|
|
68
|
+
- **判定/发现**:TODO
|
|
69
|
+
- **落地**:TODO(commit 或"未落地")
|
|
70
|
+
- **备注**:TODO(可不填则删掉本行)
|
|
71
|
+
|
|
72
|
+
EOF
|
|
73
|
+
cat "$SRC"
|
|
74
|
+
} > "$DEST"
|
|
75
|
+
|
|
76
|
+
# 2) 逐字校验(正文必须与源**逐字相同**——存档不改原文)
|
|
77
|
+
if ! cmp -s <(tail -c "$(wc -c < "$SRC")" "$DEST") "$SRC"; then
|
|
78
|
+
echo "错误: 正文校验失败(存档与源不一致)——已保留 $DEST 供排查" >&2
|
|
79
|
+
exit 1
|
|
80
|
+
fi
|
|
81
|
+
echo "[archive] 正文逐字校验通过($(wc -c < "$SRC") 字节)"
|
|
82
|
+
|
|
83
|
+
# 3) 索引加一行(紧跟最后一行存档;TODO 留给写索引的人)
|
|
84
|
+
python3 - "$INDEX" "$DATE-$SLUG.md" <<'PY'
|
|
85
|
+
import sys
|
|
86
|
+
idx_path, fname = sys.argv[1], sys.argv[2]
|
|
87
|
+
lines = open(idx_path, encoding="utf-8").read().splitlines(True)
|
|
88
|
+
hits = [i for i, l in enumerate(lines) if l.startswith("| `2026-")]
|
|
89
|
+
if not hits:
|
|
90
|
+
print("错误: 索引里找不到存档行(| `2026-…)", file=sys.stderr)
|
|
91
|
+
sys.exit(1)
|
|
92
|
+
row = f"| `{fname}` | TODO 主题 | TODO 抓到的问题 | TODO 落地 commit |\n"
|
|
93
|
+
lines.insert(hits[-1] + 1, row)
|
|
94
|
+
open(idx_path, "w", encoding="utf-8").write("".join(lines))
|
|
95
|
+
print(f"[archive] 索引已加一行({idx_path};TODO 待补)")
|
|
96
|
+
PY
|
|
97
|
+
[ $? -eq 0 ] || exit 1
|
|
98
|
+
|
|
99
|
+
# 4) 删仓库根松散副本(**只删源文件**,且必须在写成功后)
|
|
100
|
+
rm -f "$SRC"
|
|
101
|
+
echo "[archive] 已写 $DEST;源副本已删;下一步:补 $DEST 与索引行里的 TODO"
|
package/start_discussion.py
CHANGED
|
@@ -450,11 +450,24 @@ def cleanup_discussion(base):
|
|
|
450
450
|
# 报告是附加信息、清理是主职责:报告生成失败**不阻断**清理
|
|
451
451
|
# (fail-open 只在这一层兜底——build_report 内部各段已各自 fail-open)。
|
|
452
452
|
print("[cleanup] —— 本次分析报告(删除目录前最后一次可读)——")
|
|
453
|
+
lines = None
|
|
453
454
|
try:
|
|
454
|
-
|
|
455
|
+
lines = list(build_report(base))
|
|
456
|
+
for line in lines:
|
|
455
457
|
print(line)
|
|
456
458
|
except Exception as e: # noqa: BLE001(兜底不吞:打印)
|
|
457
459
|
print(f"[cleanup] 报告生成失败(不影响清理): {e!r}")
|
|
460
|
+
if lines is not None:
|
|
461
|
+
# 落盘一份(与 <base>-result.md 同级):报告本来只在终端出现一次,
|
|
462
|
+
# 目录删掉后 --report 也不可用 → 观测数字不可复查(复盘时长口径时
|
|
463
|
+
# 踩过)。与 result.md 同样的 fail-open:写不动不阻断清理。
|
|
464
|
+
rp = meeting_fs.report_path(base)
|
|
465
|
+
try:
|
|
466
|
+
with open(rp, "w", encoding="utf-8") as f:
|
|
467
|
+
f.write("\n".join(lines) + "\n")
|
|
468
|
+
print(f"[cleanup] 报告已保存 → {rp}")
|
|
469
|
+
except OSError as e:
|
|
470
|
+
print(f"[cleanup] 报告保存失败(不影响清理): {e!r}")
|
|
458
471
|
shutil.rmtree(base)
|
|
459
472
|
print(f"[cleanup] 已删除目录 {base}(含 pi-sessions)")
|
|
460
473
|
|