pi-multi-viewers 0.5.1 → 0.6.0
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 -3
- package/README.md +8 -2
- package/mv_cli.py +62 -0
- package/package.json +1 -1
- package/prompts/multi-viewers-setup.md +55 -0
- package/prompts/multi-viewers.md +6 -15
- package/spec_gen.py +15 -4
package/AGENTS.md
CHANGED
|
@@ -28,8 +28,9 @@ 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
|
-
mv_cli.py 命令行实现(prepare/start/status/report/wait/cleanup/view/say)
|
|
32
|
-
prompts/multi-viewers.md /multi-viewers
|
|
31
|
+
mv_cli.py 命令行实现(prepare/start/status/report/wait/cleanup/view/say/viewers)
|
|
32
|
+
prompts/multi-viewers.md /multi-viewers 分析入口(审核闸门;视角原则引 README,不复述)
|
|
33
|
+
prompts/multi-viewers-setup.md /multi-viewers-setup 建视角入口(建议→你定→落盘→给审)
|
|
33
34
|
extensions/multi-viewers-say/ /multi-viewers-say 插话(registerCommand,零 LLM)
|
|
34
35
|
docs/design.md 设计文档(fork 源模式与规模口径 + 决策记录)
|
|
35
36
|
package.json npm 包 pi-multi-viewers(pi.prompts 注册;**版本号唯一事实源**)
|
|
@@ -229,7 +230,7 @@ loop、状态从 git 共享事实推导、单一事实源 = protocol.json、无
|
|
|
229
230
|
|
|
230
231
|
## 安装/发版状态(2026-09-11)
|
|
231
232
|
|
|
232
|
-
- **当前形态**:prompt ×
|
|
233
|
+
- **当前形态**:prompt × 2(multi-viewers 分析 / multi-viewers-setup 建视角)+
|
|
233
234
|
extension × 1(multi-viewers-say 插话:零 LLM,直接 spawn human_sayer.py;
|
|
234
235
|
目录发现 = `<cwd>/mv-<sessionId>-*` 最新——**无兜底**:未匹配即报错
|
|
235
236
|
rc 1,需显式传目录)
|
package/README.md
CHANGED
|
@@ -68,11 +68,12 @@ ls ~/.pi/agent/npm/node_modules/pi-multi-viewers/scripts/mv.sh
|
|
|
68
68
|
## 用法
|
|
69
69
|
|
|
70
70
|
```
|
|
71
|
-
/multi-viewers "<主题>" #
|
|
71
|
+
/multi-viewers "<主题>" # 分析入口(推荐;视角来自 viewers/)
|
|
72
|
+
/multi-viewers-setup # 建视角入口(交互式:先建议 → 你定 → 落盘 → 给你审)
|
|
72
73
|
/multi-viewers-say "<文本>" # 插话(extension:零 LLM 直接写入 human 消息)
|
|
73
74
|
```
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
三个 pi 命令入口(分析/建视角走 prompt,插话走 extension——插话是"本地命令
|
|
76
77
|
执行",不需要经过 LLM)。
|
|
77
78
|
|
|
78
79
|
**目录可以省略**:`--view`/`--say`/`--status`/`--report`/`--wait`/`--cleanup`
|
|
@@ -108,10 +109,15 @@ scripts/mv.sh --say "<文本>" # 插话(命令行形态;pi
|
|
|
108
109
|
scripts/mv.sh --status # 状态 + 路径(取值与含义以该命令输出为准)
|
|
109
110
|
scripts/mv.sh --report # 只读报告(流程/配额/进程/LLM/档位对照;冷路径,不持久化)
|
|
110
111
|
scripts/mv.sh --cleanup # 收尾(result.md 自动留存到 <dir>-result.md)
|
|
112
|
+
scripts/mv.sh --viewers # 列出+校验当前项目 viewers/(只读;建视角时用)
|
|
111
113
|
```
|
|
112
114
|
|
|
113
115
|
## 视角文件写什么(`viewers/<视角名>.md`)
|
|
114
116
|
|
|
117
|
+
建视角**推荐**走 `/multi-viewers-setup`(交互式:先给候选建议 → 你定建哪几个 →
|
|
118
|
+
落盘 → 展示给你审);也可以手写。写完用 `scripts/mv.sh --viewers` 自查(列出并
|
|
119
|
+
用代码判据校验名字与空正文)。
|
|
120
|
+
|
|
115
121
|
一个视角文件 = **一份视角说明**,纯内容、无格式要求(无 frontmatter、
|
|
116
122
|
无需标题,**文件名就是全部元数据**)。三个要点(措辞经实验验证):
|
|
117
123
|
|
package/mv_cli.py
CHANGED
|
@@ -28,6 +28,8 @@ import subprocess
|
|
|
28
28
|
import sys
|
|
29
29
|
from datetime import datetime
|
|
30
30
|
|
|
31
|
+
import spec_gen # --viewers 复用其单一判据(列举/名字/集合校验)
|
|
32
|
+
|
|
31
33
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
32
34
|
PYTHON = os.environ.get("PYTHON") or "python3"
|
|
33
35
|
START_DISCUSSION = os.path.join(HERE, "start_discussion.py")
|
|
@@ -49,6 +51,7 @@ USAGE = f"""用法:
|
|
|
49
51
|
{PROG} --cleanup [dir]
|
|
50
52
|
{PROG} --view [dir] [--since <ref>]
|
|
51
53
|
{PROG} --say [dir] "<文本>"
|
|
54
|
+
{PROG} --viewers # 列出并校验当前项目的 viewers/(只读;建视角时用)
|
|
52
55
|
|
|
53
56
|
消费命令的 <dir> 可省略(自动发现本 session 当前分析——按 cwd 下
|
|
54
57
|
mv-<PI_SESSION_ID>-* 最新;无匹配则报错要求显式传目录)
|
|
@@ -67,10 +70,23 @@ human 通道:
|
|
|
67
70
|
|
|
68
71
|
def fail(msg):
|
|
69
72
|
"""错误出口(沿用 bash 约定:`错误: ` 前缀 + stderr + rc 1)。"""
|
|
73
|
+
sys.stdout.flush() # 已打印的正常输出先落地(stderr 无缓冲,否则会插到前面)
|
|
70
74
|
print(f"错误: {msg}", file=sys.stderr)
|
|
71
75
|
raise SystemExit(1)
|
|
72
76
|
|
|
73
77
|
|
|
78
|
+
def fail_verbatim(msg):
|
|
79
|
+
"""错误文本**自带 `错误: ` 前缀**(来自 spec_gen 的单一判据)→ 原样输出。
|
|
80
|
+
|
|
81
|
+
与 fail() 的差别只是前缀归属:判据的实现方(spec_gen)负责文案与前缀
|
|
82
|
+
(`start_discussion` 同样 `print(err)` 原样输出);再包一层会变成
|
|
83
|
+
"错误: 错误: …"(实测踩过)。
|
|
84
|
+
"""
|
|
85
|
+
sys.stdout.flush()
|
|
86
|
+
print(msg, file=sys.stderr)
|
|
87
|
+
raise SystemExit(1)
|
|
88
|
+
|
|
89
|
+
|
|
74
90
|
# ---------------------------------------------------------------
|
|
75
91
|
# 目录参数:解析(显式优先 / 省略则自动发现)与校验
|
|
76
92
|
# ---------------------------------------------------------------
|
|
@@ -127,6 +143,50 @@ def _call(cmd):
|
|
|
127
143
|
# 消费命令
|
|
128
144
|
# ---------------------------------------------------------------
|
|
129
145
|
|
|
146
|
+
def cmd_viewers(args):
|
|
147
|
+
"""列出并校验**当前项目**的 `viewers/`(只读)。
|
|
148
|
+
|
|
149
|
+
为什么需要它:视角是**长期资产**,创建/检查它的时刻通常**还没有任何分析
|
|
150
|
+
目录**(消费命令的目录自动发现对此不适用——它找的是 mv-<sid>-*)。判据全部
|
|
151
|
+
复用 `spec_gen` 的单一实现(列举 `list_agent_md` / 名字 `check_agent_name` /
|
|
152
|
+
集合 `viewer_set_error`),这一层不另写一套规则。
|
|
153
|
+
|
|
154
|
+
数量不足(<2)在这里是**提示**不是错误:建 1 个是合法的中间状态,只有启动
|
|
155
|
+
一次分析时才要求 ≥2(那条判据仍由 `viewer_set_error` 独占)。
|
|
156
|
+
"""
|
|
157
|
+
if args:
|
|
158
|
+
fail(f"未知参数: {' '.join(args)}(--viewers 不接受参数——只检查项目 cwd 的 viewers/)")
|
|
159
|
+
vdir = os.path.join(os.getcwd(), "viewers")
|
|
160
|
+
if not os.path.isdir(vdir):
|
|
161
|
+
fail(f"未找到 {vdir}——视角文件放在项目 cwd 的 viewers/<视角名>.md"
|
|
162
|
+
f"(文件名即视角名;可跑 /multi-viewers-setup 交互式建立)")
|
|
163
|
+
names, _briefs, empty = spec_gen._discover_viewers(vdir)
|
|
164
|
+
if not names:
|
|
165
|
+
fail(f"{vdir} 下没有 *.md——文件名即视角名(如 viewers/效率.md)")
|
|
166
|
+
empty_names = {n for n, _why in empty}
|
|
167
|
+
print(f"[viewers] {vdir}", flush=True) # 与 stderr 的错误行保序(管道下也如此)
|
|
168
|
+
for n in names:
|
|
169
|
+
notes = []
|
|
170
|
+
name_err = spec_gen.check_agent_name(n)
|
|
171
|
+
if name_err:
|
|
172
|
+
notes.append(f"文件名非法:{name_err}")
|
|
173
|
+
if n in empty_names:
|
|
174
|
+
notes.append("空:没有视角内容")
|
|
175
|
+
suffix = f"({';'.join(notes)})" if notes else ""
|
|
176
|
+
print(f" {n}.md{suffix}")
|
|
177
|
+
err = spec_gen.validate_participants(names)
|
|
178
|
+
if err:
|
|
179
|
+
fail_verbatim(err)
|
|
180
|
+
if empty:
|
|
181
|
+
fail_verbatim(spec_gen.viewer_set_error(names, empty))
|
|
182
|
+
gap = spec_gen.viewers_count_gap(names)
|
|
183
|
+
if gap:
|
|
184
|
+
print(f" 校验:{gap}(建 1 个是合法的中间状态)")
|
|
185
|
+
return 0
|
|
186
|
+
print(f" 校验:通过({len(names)} 个视角,名字与内容均合法)")
|
|
187
|
+
return 0
|
|
188
|
+
|
|
189
|
+
|
|
130
190
|
def cmd_status(args):
|
|
131
191
|
d = _dir_only("--status", args)
|
|
132
192
|
return _call([PYTHON, START_DISCUSSION, "--dir", d, "--status"])
|
|
@@ -341,6 +401,8 @@ def main(argv=None):
|
|
|
341
401
|
if not rest:
|
|
342
402
|
fail("--start 需要 spec 目录参数")
|
|
343
403
|
return cmd_start(rest[0], rest[1:])
|
|
404
|
+
if cmd == "--viewers":
|
|
405
|
+
return cmd_viewers(rest)
|
|
344
406
|
if cmd == "--status":
|
|
345
407
|
return cmd_status(rest)
|
|
346
408
|
if cmd == "--report":
|
package/package.json
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 建立多视角分析的 viewers/ 视角文件(交互式:先建议 → 你定 → 落盘 → 给你审)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Multi-Viewers Setup(建立视角文件)
|
|
6
|
+
|
|
7
|
+
这是**建立视角资产**的流程命令,不是分析内容。`viewers/<视角名>.md` 是**长期资产**
|
|
8
|
+
(写好长期复用)。本命令**只新建、不修改已有视角**。
|
|
9
|
+
|
|
10
|
+
## 第 1 步:先看项目,再提**建议**(不落盘)
|
|
11
|
+
|
|
12
|
+
机械地读项目(`README.md` / `AGENTS.md` / 代码结构)——据此给出 **3–5 个候选视角**,
|
|
13
|
+
每个一行:
|
|
14
|
+
|
|
15
|
+
1. <名字> —— <一行镜头>(与其它候选怎样互补/对立)
|
|
16
|
+
|
|
17
|
+
- 这一步**只提议**:不建文件、不改任何东西
|
|
18
|
+
- 名字:中文、短(≤32 字符)、不含空白与路径分隔符、**不要用 `human`**(保留名)
|
|
19
|
+
- 建几个**由用户决定**(只建 1 个也行)
|
|
20
|
+
|
|
21
|
+
**然后结束本回合,等用户输入。**
|
|
22
|
+
|
|
23
|
+
## 第 2 步:等用户决定
|
|
24
|
+
|
|
25
|
+
用户会告诉你建哪几个(可能只有一个、也可能点名不在候选里的)。**以用户输入为准**;
|
|
26
|
+
若某个视角该用什么镜头没说清 → **问一句**,不要替他定。
|
|
27
|
+
|
|
28
|
+
## 第 3 步:按用户输入建文件(只新建)
|
|
29
|
+
|
|
30
|
+
写之前**先读 `README.md` 的「视角文件写什么」节**(唯一事实源:三个要点 + 正误对照 +
|
|
31
|
+
命名规则)。要点:
|
|
32
|
+
|
|
33
|
+
- 纯内容、无 frontmatter、无标题——**文件名就是全部元数据**
|
|
34
|
+
- 必含三条:① **单一 lenses**(所有观点必须从该视角出发)② **不越界**(其它视角由
|
|
35
|
+
别的参与者负责;**不要**列举是哪几个——参与者会变,列举会过期)③ **交锋义务**
|
|
36
|
+
(对其它视角的观点可认同或反驳,但要用本视角的论据)
|
|
37
|
+
- **只写视角本身**:身份("你是 X")、参与者名单、消息格式、独立参与者纪律都由脚本
|
|
38
|
+
生成,**不要写进文件**
|
|
39
|
+
|
|
40
|
+
写完跑一次只读检查(会列出视角并用代码判据校验名字与空正文):
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
~/.pi/agent/npm/node_modules/pi-multi-viewers/scripts/mv.sh --viewers
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**已有同名文件 → 不覆盖**:告诉用户"该视角已存在",停下等他决定(改名,或他明确
|
|
47
|
+
要求改动)。
|
|
48
|
+
|
|
49
|
+
## 第 4 步:展示,等审阅
|
|
50
|
+
|
|
51
|
+
把每个新建文件的**路径 + 全文**展示给用户,请他提修改意见或确认:
|
|
52
|
+
|
|
53
|
+
- 有意见 → 改 → **再展示**(反复直到他确认)
|
|
54
|
+
- 确认后一行交接:视角已建好(启动一次分析至少需要 2 个视角),接下来
|
|
55
|
+
`/multi-viewers "<主题>"` 即可
|
package/prompts/multi-viewers.md
CHANGED
|
@@ -28,8 +28,8 @@ argument-hint: '"<主题>"'
|
|
|
28
28
|
|
|
29
29
|
**这条命令非零退出 → 说明 spec 生成失败**(通常是项目缺可用视角:未建
|
|
30
30
|
`viewers/`、视角少于 2 个、或视角文件为空)。此时**停下来问用户**,不要
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
自己选——读报错原文搞清原因,让他跑 `/multi-viewers-setup`(交互式建视角),
|
|
32
|
+
建好后再重跑本步骤。
|
|
33
33
|
|
|
34
34
|
### 2. 编辑并请用户审核 spec
|
|
35
35
|
|
|
@@ -48,20 +48,11 @@ argument-hint: '"<主题>"'
|
|
|
48
48
|
|
|
49
49
|
然后**展示 spec 路径,明确请用户查看/编辑**——用户确认"继续"才执行第 3 步。
|
|
50
50
|
|
|
51
|
-
##
|
|
51
|
+
## 视角设计原则
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
1. **单一 lenses**:明确该 agent 用什么角度看(效率/简单化/安全/……),
|
|
57
|
-
要求"所有观点必须从该视角出发"
|
|
58
|
-
2. **不越界**:"其它视角由别的参与者负责,你不要越界展开"(**不要**列举
|
|
59
|
-
具体是哪几个视角——参与者会变,列举会过期)
|
|
60
|
-
3. **交锋义务**:"对其它视角的观点可以认同或反驳,但要用本视角的论据"
|
|
61
|
-
|
|
62
|
-
**只写视角本身**:身份("你是 X")、参与者名单、消息格式、独立参与者
|
|
63
|
-
纪律都由脚本生成(agent 名 = 文件名,单一来源)——任务书里**不要写**,
|
|
64
|
-
否则重复且易漂移。
|
|
53
|
+
要建或按场微调 `viewers/*.md`、`agents/X.md` 时,**先读 `README.md` 的
|
|
54
|
+
「视角文件写什么」节**(三个要点 + 正误对照 + 命名规则)——那里是唯一事实源,
|
|
55
|
+
本节不再复述以免漂移。
|
|
65
56
|
|
|
66
57
|
### 3. 启动分析
|
|
67
58
|
|
package/spec_gen.py
CHANGED
|
@@ -447,6 +447,19 @@ def validate_participants(participants):
|
|
|
447
447
|
return None
|
|
448
448
|
|
|
449
449
|
|
|
450
|
+
def viewers_count_gap(names, where="viewers/"):
|
|
451
|
+
"""视角数量不足的**事实句**(不带"错误:"前缀)→ 或 None。
|
|
452
|
+
|
|
453
|
+
与 viewer_set_error 共用同一句话(那里把它包成错误):差别只在**场景语义**——
|
|
454
|
+
prepare/start 时数量不足 = 不能启动(错误);建视角时 1 个是**合法中间状态**
|
|
455
|
+
(只读检查 `mv.sh --viewers` 把它当提示,不判错)。
|
|
456
|
+
"""
|
|
457
|
+
if len(names) < 2:
|
|
458
|
+
return (f"{where} 下仅发现 {len(names)} 个视角"
|
|
459
|
+
f"({', '.join(names)})——多视角分析至少需要 2 个")
|
|
460
|
+
return None
|
|
461
|
+
|
|
462
|
+
|
|
450
463
|
def viewer_set_error(names, empty, where="viewers/"):
|
|
451
464
|
"""viewers 集合级校验(**唯一实现**):空正文视角 + 至少 2 个。
|
|
452
465
|
|
|
@@ -461,10 +474,8 @@ def viewer_set_error(names, empty, where="viewers/"):
|
|
|
461
474
|
detail = "、".join(f"{where}{n}.md({why})" for n, why in empty)
|
|
462
475
|
return (f"错误: {detail}——视角任务书不能为空"
|
|
463
476
|
f"(写清该视角用什么 lenses 看分析对象)")
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
f"({', '.join(names)})——多视角分析至少需要 2 个")
|
|
467
|
-
return None
|
|
477
|
+
gap = viewers_count_gap(names, where)
|
|
478
|
+
return f"错误: {gap}" if gap else None
|
|
468
479
|
|
|
469
480
|
|
|
470
481
|
def _discover_viewers(viewers_dir):
|