discuss
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesediscuss — Interactive markdown review session
discuss — 交互式Markdown评审会话
Open markdown content in , watch the user drop comments and replies, and respond with takes — the agent's view on each question or thread. Takes are semantically distinct from replies: the human types replies in the browser; the agent posts takes via the API.
discussThe source can be either a file on disk or markdown piped in on stdin (e.g. an ad-hoc summary of a staged diff that the agent generates and pipes straight into discuss without writing to disk).
在中打开Markdown内容,监控用户添加的评论与回复,并通过发布takes(Agent对每个问题或线程的观点)做出响应。Takes与回复在语义上有所区别:人类在浏览器中输入回复;Agent通过API发布takes。
discuss内容来源可以是磁盘上的文件,也可以是通过stdin管道传入的Markdown(例如,Agent生成的暂存差异临时摘要,直接通过管道传入discuss而不写入磁盘)。
Arguments
参数
- — Either a path to the markdown file to review, OR markdown content the user wants to review without writing it to disk. If missing and the user has not described the content, ask which file/content and stop.
$ARGUMENTS
- — 可以是待评审的Markdown文件路径,也可以是用户想要评审但不想写入磁盘的Markdown内容。如果该参数缺失且用户未描述内容,请询问具体文件/内容并停止操作。
$ARGUMENTS
Stdin mode
stdin模式
When you have markdown content already in hand (e.g. a generated summary of staged changes) and don't need it on disk, pipe it in instead of writing a temp file:
- reads markdown from stdin explicitly.
discuss - - also reads stdin (auto-detected when no file arg is given and stdin is not a TTY).
<some-command> | discuss
In stdin mode, the event reports and history archives are written under since there is no source path to derive a folder name from.
session.startedsource_file: "<stdin>".../unnamed/当你已经持有Markdown内容(例如,暂存变更的生成摘要)且不需要将其写入磁盘时,可通过管道将其传入,而非写入临时文件:
- 显式从stdin读取Markdown内容。
discuss - - 同样会读取stdin(当未指定文件参数且stdin不是TTY时自动检测)。
<some-command> | discuss
在stdin模式下,事件会报告,历史归档会写入目录下,因为没有源路径可用于生成文件夹名称。
session.startedsource_file: "<stdin>".../unnamed/Multi-file mode
多文件模式
Pass several paths to review them together in one session with a file sidebar:
discuss plan.md design.md notes.md- Files are identified by (
fileId,f-1, … in CLI order).f-2includes a/api/statearray (files).{id, path, kind} - Every payload carries a
thread.created. When you create threads or push source updates in a multi-file session,fileIdis required — omitting it returnsfileId.400 missing_file_id - Anchor indices are per-file (1-based commentable blocks within that file's document).
- gains
session.started, andfiles_countbecomessource_file.multi-<N>-files
传入多个路径,即可在一个会话中同时评审多个文件,会话会显示文件侧边栏:
discuss plan.md design.md notes.md- 文件通过标识(按CLI顺序为
fileId、f-1……)。f-2包含/api/state数组(files)。{id, path, kind} - 每个负载都会携带
thread.created。在多文件会话中创建线程或推送源更新时,fileId是必填项 —— 省略该参数会返回fileId错误。400 missing_file_id - 锚点索引按文件划分(每个文件文档中可添加评论的块元素为1-based索引)。
- 会新增
session.started字段,files_count字段变为source_file。multi-<N>-files
Diff review mode
差异评审模式
Prefer over generating a markdown wrapper of a git diff. It skips the summarize-and-fence round trip entirely — the binary runs , splits it per file, and renders each hunk as a block with line-anchored threads working out of the box:
discuss diffgit diffdiff-<lang>discuss diff # staged (git diff --cached)
discuss diff --unstaged # working tree
discuss diff HEAD~3..HEAD # arbitrary range forwarded to git diff
discuss plan.md diff # markdown file(s) + diff in one session- gains
session.started(mode/"markdown"/"diff") and"mixed"so you know what's under review.git_args - Each changed file is its own sidebar entry with its own ; per-file prose is optional — post takes on file threads when intent needs explaining, stay silent on mechanical changes.
fileId - Diff output is capped at 5 MB (/
--max-diff-bytes/DISCUSS_MAX_DIFF_BYTESconfig to override;max_diff_bytesdisables).0
优先使用,而非生成git diff的Markdown包装器。它会完全跳过摘要与代码块的往返流程 —— 二进制文件运行,按文件拆分差异,并将每个代码块渲染为块,支持基于行的锚点线程:
discuss diffgit diffdiff-<lang>discuss diff # 暂存区(git diff --cached)
discuss diff --unstaged # 工作区
discuss diff HEAD~3..HEAD # 任意范围,直接传递给git diff
discuss plan.md diff # 同时评审Markdown文件与差异内容- 会新增
session.started(mode/"markdown"/"diff")与"mixed"字段,方便你了解评审内容。git_args - 每个变更文件都是独立的侧边栏条目,拥有自己的;每个文件的说明内容为可选 —— 当需要解释意图时,在文件线程中发布takes;对于机械性变更则保持沉默。
fileId - 差异输出上限为5 MB(可通过/
--max-diff-bytes/DISCUSS_MAX_DIFF_BYTES配置项覆盖;设置为max_diff_bytes可禁用上限)。0
Verdict options
verdict选项
When the user wants an explicit final review decision (for example, "review this plan, then tell me approved or declined"), pass before any subcommand. Keep it to 2-4 short labels; decline/blocker-style options should usually require feedback with so the transcript explains why.
--verdict-optionsdiff!DSL grammar: separated by .
id[:label][:style][!]|- is required and must match
id; it becomes[a-z0-9_-]+.optionId - defaults to the title-cased id.
label - is
style,positive, orneutral; default isnegative.neutral - trailing makes feedback required for that option.
! - specs need at least 2 options; duplicate ids and case-insensitive duplicate labels are rejected with exit code 2.
discuss --verdict-options 'approved:Approve|declined:Decline:negative!' plan.mdUse only when the default finish-review prompt needs project-specific wording; without it warns on stderr and does nothing. Shell-quote the options because the DSL uses between choices and for required feedback, and both are shell metacharacters.
--verdict-prompt "..."--verdict-options|!When arrives, read if present: is the stable choice id, is the displayed button text, is the human explanation when supplied, and is the decision timestamp.
session.donepayload.verdictoptionIdlabelfeedbackdecidedAt当用户需要明确的最终评审结论时(例如,“评审此计划,然后告诉我通过或拒绝”),在任何子命令前传递参数。选项数量控制在2-4个简短标签;拒绝/阻塞类选项通常需要添加以要求反馈,这样会话记录会解释原因。
diff--verdict-options!DSL语法:,多个选项用分隔。
id[:label][:style][!]|- 为必填项,必须匹配
id;它会成为[a-z0-9_-]+。optionId - 默认将id转换为标题格式。
label - 可选值为
style、positive或neutral;默认值为negative。neutral - 末尾的表示该选项需要必填反馈。
! - 配置至少需要2个选项;重复的id或大小写不敏感的重复标签会被拒绝,退出码为2。
discuss --verdict-options 'approved:Approve|declined:Decline:negative!' plan.md仅当默认的评审完成提示需要项目特定措辞时,才使用;如果未指定,该参数会在stderr输出警告且不生效。由于DSL使用分隔选项、表示必填反馈,而这两个字符都是shell元字符,因此需要用shell引号包裹选项。
--verdict-prompt "..."--verdict-options|!当事件到达时,读取(如果存在):是稳定的选项id,是按钮显示文本,是用户提供的解释(如果有),是决策时间戳。
session.donepayload.verdictoptionIdlabelfeedbackdecidedAtPreflight: Ensure discuss
is installed
discuss预检查:确保已安装discuss
discussRun (via Bash). If it resolves to a path, skip ahead to Step 0.
command -v discussIf it doesn't resolve, the binary isn't on PATH. Ask the user:
isn't on your PATH. Install it now? (runsdiscuss)curl -sSL https://raw.githubusercontent.com/codesoda/discuss-cli/main/install.sh | sh
On yes, run the install command via Bash. On completion, retry .
command -v discussIf it still doesn't resolve, fall back to the absolute install path: . Check it exists and is executable — if so, use that path for every subsequent call to in this session. If it also doesn't exist, report the install failed and stop.
~/.discuss/bin/discussdiscussIf the user declines the install, stop.
运行(通过Bash)。如果返回路径,则跳至步骤0。
command -v discuss如果未返回路径,说明二进制文件不在PATH中。询问用户:
不在你的PATH中。现在安装它吗?(执行discuss)curl -sSL https://raw.githubusercontent.com/codesoda/discuss-cli/main/install.sh | sh
如果用户同意,通过Bash执行安装命令。安装完成后,重新运行。
command -v discuss如果仍未返回路径,回退到绝对安装路径:。检查该路径是否存在且可执行 —— 如果是,在此会话中后续所有调用时都使用该路径。如果该路径也不存在,报告安装失败并停止操作。
~/.discuss/bin/discussdiscuss如果用户拒绝安装,停止操作。
Step 0: Find the monitor-type tool
步骤0:找到监控类工具
discuss must be launched through a monitor-type tool whenever one exists. A monitor-type tool is any primitive that (a) runs a long-lived command in the background and (b) delivers each stdout line back to you as a notification. That is exactly the contract discuss is built for: the process stays up for the whole review, and every newline-delimited JSON event it prints wakes you with the user's latest thread or reply. No polling, no log scraping, no blocked turn.
Harnesses name these tools differently. Look for whichever pair exists in the current context:
| Harness | Launch | Stop |
|---|---|---|
| Claude Code | | |
| pi | | |
| other | any "run in background + stream stdout to me" tool | its stop/kill call |
In Claude Code, and may be deferred tools. Load their schemas before calling them:
MonitorTaskStopToolSearch(query: "select:Monitor,TaskStop", max_results: 2)只要存在监控类工具,就必须通过它启动discuss。监控类工具是满足以下条件的工具:(a) 在后台运行长命令;(b) 将每个stdout行作为通知返回给你。这正是discuss的设计契约:进程会在整个评审期间保持运行,它输出的每个换行分隔的JSON事件都会提醒你用户的最新线程或回复。无需轮询、无需日志抓取、无需阻塞。
不同的执行环境对这些工具的命名不同。查找当前环境中存在的工具对:
| 执行环境 | 启动命令 | 停止命令 |
|---|---|---|
| Claude Code | | |
| pi | | |
| 其他环境 | 任何“后台运行 + 将stdout流式传输给我”的工具 | 对应的停止/终止命令 |
在Claude Code中,和可能是延迟加载的工具。调用前先加载它们的 schema:
MonitorTaskStopToolSearch(query: "select:Monitor,TaskStop", max_results: 2)Step 1: Launch discuss and choose an event strategy
步骤1:启动discuss并选择事件策略
Always launch through the monitor-type tool first. Only if no such tool is available in the current context (e.g. ToolSearch finds nothing and invoking it returns a tool-not-enabled error) fall back to the polling fallback described below. Do not use the poller when a monitor-type tool exists — it delivers events push-style with no polling latency. The rest of the steps are the same once you have events flowing.
始终优先通过监控类工具启动。只有当当前环境中没有此类工具时(例如,ToolSearch未找到任何工具,或调用时返回工具未启用错误),才回退到下面描述的轮询方案。当存在监控类工具时,请勿使用轮询器 —— 监控类工具采用推送式交付事件,无轮询延迟。一旦事件开始流动,后续步骤都是相同的。
Option A — monitor-type tool (preferred)
选项A —— 监控类工具(优先选择)
Run directly as the monitor's command. Two things NOT to do:
discuss- Do NOT launch it with a plain blocking Bash call — discuss is a server that runs until the user finishes the review, so the call would hold your turn hostage for the entire session.
- Do NOT launch it via Bash with — you would then have to poll a log file for events, which is the thing the monitor exists to avoid.
run_in_background
The monitor treats each stdout line from its command as an event notification delivered to chat, which is exactly how discuss's newline-delimited JSON events are meant to be consumed.
Never pass — the browser must open by default; the human reviews there. If a session seems to have started silently, check the flags before assuming a server problem.
--no-openThe command string must start with — commands beginning with are pre-approved and start immediately; any prefix (, , ) requires human approval before the monitor can start. Never prefix with : the monitor already runs in the session's working directory, so launch from the right cwd and pass repo-relative or absolute paths instead. When piping content in, prefer the heredoc form (, which starts with ) over an upstream-command pipe.
discussdiscusscd … && discussVAR=x discussgit … | discusscddiscuss - <<'EOF' … EOFdiscussFile mode (Claude Code):
Monitor(
description: "discuss events for <file>",
command: "discuss \"$ARGUMENTS\"",
persistent: true
)The same launch in pi — same command string, same , plus an that rides along with every wake-up:
persistentinstructionmonitor_start(
description: "discuss events for <file>",
command: "discuss \"$ARGUMENTS\"",
persistent: true,
instruction: "Each line is a discuss event. Post a take on thread.created and a follow-up take on reply.added, per the discuss skill."
)Stdin mode — pipe the markdown content into . Use a heredoc to keep the content readable in the monitor command:
discuss -Monitor(
description: "discuss events for staged-diff review",
command: "discuss - <<'DISCUSS_EOF'\n# Staged Diff Review\n\n## src/foo.rs\n\n... markdown body ...\nDISCUSS_EOF",
persistent: true
)Avoid piping another command's output in () — the command no longer starts with and needs human approval. For diffs use (starts with , pre-approved); otherwise capture the content first and use the heredoc form.
git diff … | discuss -discussdiscuss diffdiscussNotes:
- is required — discuss is a long-running server that only exits when the user is done. Without it the monitor will time out mid-review and take discuss down with it.
persistent: true - Do NOT redirect stderr. Monitor-type tools keep stderr out of the event stream (Claude Code writes it to the task output file, pi to a temp log), so discuss's stderr line can't pollute the JSON events — but
listening on …would fold it in.2>&1 - Record the id returned by the launch call (from Monitor, monitor id from
task_id) — you need it to stop the session later.monitor_start - If the port is already bound or the file doesn't exist, discuss exits immediately and the monitor ends without ever emitting a event. Read the monitor's stderr log to surface the error, then stop.
session.started - In stdin mode, you typically already have the markdown in hand (you generated it). Keep a copy in your scratchpad if you need it later for anchor snippets — there's no file to re-read.
直接将作为监控工具的命令运行。请勿执行以下两件事:
discuss- 请勿使用普通的阻塞式Bash调用启动它 —— discuss是一个服务器,会运行到用户完成评审,因此该调用会在整个会话期间占用你的执行权限。
- 请勿通过Bash的启动它 —— 这样你就必须轮询日志文件获取事件,而这正是监控工具要避免的情况。
run_in_background
监控工具会将其命令的每个stdout行作为事件通知发送到聊天窗口,这正是discuss的换行分隔JSON事件的预期消费方式。
永远不要传递参数 —— 浏览器必须默认打开;人类用户在浏览器中进行评审。如果会话似乎静默启动,请先检查标志,再假设服务器出现问题。
--no-open命令字符串必须以开头 —— 以开头的命令是预批准的,可立即启动;任何前缀(如、、)都需要人工批准后监控工具才能启动。永远不要添加前缀:监控工具已经在会话的工作目录中运行,因此请从正确的cwd启动,并传递仓库相对路径或绝对路径。当通过管道传入内容时,优先使用 heredoc 形式(,以开头),而非上游命令管道。
discussdiscusscd … && discussVAR=x discussgit … | discusscddiscuss - <<'EOF' … EOFdiscuss文件模式(Claude Code):
Monitor(
description: "discuss events for <file>",
command: "discuss \"$ARGUMENTS\"",
persistent: true
)在pi环境中的启动方式相同 —— 命令字符串相同,参数相同,另外添加参数,该参数会随每次唤醒传递:
persistentinstructionmonitor_start(
description: "discuss events for <file>",
command: "discuss \"$ARGUMENTS\"",
persistent: true,
instruction: "Each line is a discuss event. Post a take on thread.created and a follow-up take on reply.added, per the discuss skill."
)stdin模式 —— 将Markdown内容通过管道传入。使用heredoc形式使监控命令中的内容更易读:
discuss -Monitor(
description: "discuss events for staged-diff review",
command: "discuss - <<'DISCUSS_EOF'\n# Staged Diff Review\n\n## src/foo.rs\n\n... markdown body ...\nDISCUSS_EOF",
persistent: true
)避免通过其他命令的输出管道传入(如)—— 命令不再以开头,需要人工批准。对于差异内容,使用(以开头,预批准);否则先捕获内容,再使用heredoc形式。
git diff … | discuss -discussdiscuss diffdiscuss注意事项:
- 是必填项 —— discuss是一个长运行服务器,仅在用户完成评审后才会退出。如果没有该参数,监控工具会在评审中途超时,并终止discuss进程。
persistent: true - 请勿重定向stderr。监控工具会将stderr与事件流分离(Claude Code将其写入任务输出文件,pi写入临时日志),因此discuss的stderr行不会污染JSON事件 —— 但
listening on …会将其合并到事件流中。2>&1 - 记录启动调用返回的id(Monitor返回的,
task_id返回的monitor id)—— 后续停止会话时需要使用该id。monitor_start - 如果端口已被占用或文件不存在,discuss会立即退出,监控工具不会发出事件。读取监控工具的stderr日志以显示错误,然后停止操作。
session.started - 在stdin模式下,你通常已经持有Markdown内容(你生成的)。如果后续需要锚点片段,请在临时存储中保留一份副本 —— 因为没有文件可供重新读取。
Option B — Polling fallback (only when no monitor-type tool is available)
选项B —— 轮询回退方案(仅当无监控类工具时使用)
Use this only when no monitor-type background tool is enabled in the current context. If one is available under any name, use Option A.
1. Start discuss in the background:
bash
discuss "$ARGUMENTS" --port <port> > /tmp/discuss-startup.log 2>&1 &
sleep 2
curl -s http://127.0.0.1:<port>/api/state | jq -e 'has("threads")' > /dev/null \
|| { cat /tmp/discuss-startup.log; exit 1; }Pick a free port by checking which of 7777–7782 isn't already bound (). If all are in use, discuss is already running — attach to the existing one.
curl -s http://127.0.0.1:<port>/api/state2. Enter the event loop — blocking poller:
This skill's directory (the directory containing this SKILL.md) also contains . Call it via Bash (blocking, timeout 600000ms). It polls every 5 seconds and exits as soon as something changes:
poller.sh/api/statebash
bash <skill-dir>/poller.sh "http://127.0.0.1:<port>"On the first invocation, pass no baseline — the poller snapshots current state itself. On every subsequent invocation, pass the baseline captured from the previous run's line (see below).
snapshot- Exit 0 → one or more new events; parse stdout (one JSON object per line), handle each, then immediately re-invoke the poller with the new baseline.
- Exit 1 → error (API unreachable); report to user and stop.
- Exit 2 → session ended (discuss exited); summarize threads and stop.
- Bash tool timeout → not an error; the session is just quiet. Re-invoke the poller with the same baseline.
3. Handling events from the poller:
On exit 0, stdout contains one line per changed thread, followed by a final line:
snapshotjson
{"event": "thread.created", "thread": { ...full thread object... }}
{"event": "thread.updated", "thread": { ...full thread object... }, "prev_count": 1, "current_count": 2}
{"event": "snapshot", "baseline": {"<thread-id>": 2, "<thread-id>": 0}}Handle every and line exactly as you would and monitor events (see Step 3). On exit 2 the last line is — treat it as the signal to stop and summarize.
thread.createdthread.updatedthread.createdreply.added{"event": "session.done"}Baseline handling: always pass the object from the line to the next poller invocation — do NOT re-fetch state to rebuild it yourself, or events that arrive in between will be silently dropped. If you post a reply or take while handling an event, bump that thread's count in the baseline first so your own post doesn't re-fire:
baselinesnapshotbash
BASELINE=$(echo "$BASELINE" | jq -c --arg id "$THREAD_ID" '.[$id] += 1')Optionally the markdown source afterward for context on anchor snippets (file mode only).
Read仅当当前环境中未启用监控类后台工具时,才使用此方案。如果存在任何名称的监控类工具,请使用选项A。
1. 在后台启动discuss:
bash
discuss "$ARGUMENTS" --port <port> > /tmp/discuss-startup.log 2>&1 &
sleep 2
curl -s http://127.0.0.1:<port>/api/state | jq -e 'has("threads")' > /dev/null \
|| { cat /tmp/discuss-startup.log; exit 1; }通过检查7777–7782中的哪个端口未被占用(),选择一个空闲端口。如果所有端口都被占用,说明discuss已经在运行 —— 连接到现有实例。
curl -s http://127.0.0.1:<port>/api/state2. 进入事件循环 —— 阻塞式轮询器:
此技能的目录(包含此SKILL.md的目录)中还包含。通过Bash调用它(阻塞式,超时时间600000ms)。它会每5秒轮询一次,一旦有变化就立即退出:
poller.sh/api/statebash
bash <skill-dir>/poller.sh "http://127.0.0.1:<port>"第一次调用时,无需传递基线 —— 轮询器会自行快照当前状态。后续每次调用时,传递上一次运行的行中捕获的基线(见下文)。
snapshot- 退出码0 → 有一个或多个新事件;解析stdout(每行一个JSON对象),处理每个事件,然后立即使用新基线重新调用轮询器。
- 退出码1 → 错误(API不可达);向用户报告错误并停止操作。
- 退出码2 → 会话结束(discuss已退出);总结线程并停止操作。
- Bash工具超时 → 不是错误;会话只是暂时无活动。使用相同基线重新调用轮询器。
3. 处理轮询器的事件:
当退出码为0时,stdout包含每行一个变更线程,最后一行是行:
snapshotjson
{"event": "thread.created", "thread": { ...完整线程对象... }}
{"event": "thread.updated", "thread": { ...完整线程对象... }, "prev_count": 1, "current_count": 2}
{"event": "snapshot", "baseline": {"<thread-id>": 2, "<thread-id>": 0}}处理每个和行,方式与处理监控事件中的和完全相同(见步骤3)。当退出码为2时,最后一行是 —— 将其视为停止并总结的信号。
thread.createdthread.updatedthread.createdreply.added{"event": "session.done"}基线处理: 始终将行中的对象传递给下一次轮询器调用 —— 请勿自行重新获取状态以重建基线,否则期间到达的事件会被静默丢弃。如果在处理事件时发布回复或take,请先在基线中增加该线程的计数,这样你自己的发布不会触发重复事件:
snapshotbaselinebash
BASELINE=$(echo "$BASELINE" | jq -c --arg id "$THREAD_ID" '.[$id] += 1')可选:之后 Markdown源文件以获取锚点片段的上下文(仅文件模式)。
ReadStep 2: Confirm startup and capture URL
步骤2:确认启动并捕获URL
The first notification from the monitor should be a event:
session.startedjson
{"kind":"session.started","at":"...","payload":{"url":"http://127.0.0.1:<port>","source_file":"...","started_at":"..."}}Parse from the payload — use this URL for every subsequent API call. The port is configurable (, config file), so don't hardcode .
url--port7777If the monitor ends without emitting , discuss failed to start. Read its stderr log for the error, report it, and stop.
session.startedPost a short message to chat:
Session open at— watching for threads. Anchor a comment on any part of the doc and I'll post a take.<url>
监控工具的第一个通知应该是事件:
session.startedjson
{"kind":"session.started","at":"...","payload":{"url":"http://127.0.0.1:<port>","source_file":"...","started_at":"..."}}从负载中解析 —— 后续所有API调用都使用此URL。端口是可配置的(、配置文件),因此不要硬编码。
url--port7777如果监控工具未发出就结束,说明discuss启动失败。读取其stderr日志获取错误信息,报告给用户并停止操作。
session.started在聊天窗口中发布一条简短消息:
会话已在打开 —— 正在监控讨论线程。在文档的任意部分添加评论锚点,我会发布take。<url>
Step 3: Event loop
步骤3:事件循环
Notifications arrive on the monitor's own schedule — you don't poll. Each notification line is one JSON event. Takes and drafts are broadcast via SSE only (not stdout), so your own writes never echo back — no self-echo tracking needed.
/takesActionable events: , , , . Lifecycle events (, , , ) are informational — acknowledge in chat if useful but don't post to the API.
thread.createdreply.addedthread.resolvedthread.deletedsession.startedsession.donethread.unresolvedprompt.suggest_done通知会按监控工具自身的计划到达 —— 你无需轮询。每个通知行都是一个JSON事件。Takes和草稿仅通过SSE广播(而非stdout),因此你自己的写入永远不会回显 —— 无需跟踪自回显。
/takes可操作事件:、、、。生命周期事件(、、、)仅用于提供信息 —— 如果有用,可在聊天窗口中确认,但不要发布到API。
thread.createdreply.addedthread.resolvedthread.deletedsession.startedsession.donethread.unresolvedprompt.suggest_donethread.created
(new thread opened by the user)
thread.createdthread.created
(用户开启新线程)
thread.created- Read ,
anchorStart,anchorEnd,snippetfrom the payload.text - Locate the anchored region in the markdown source — the is a reliable search key for the rendered paragraph.
snippet - Read the user's comment in .
text - Form a substantive take — answer the question, critique the anchored text, or add the missing piece. Be specific. Reference the anchored content, not just the question in isolation.
- Post it as a take, not a reply (substitute the URL from ):
session.started
bash
curl -s -X POST "$URL/api/threads/<thread-id>/takes" \
-H 'Content-Type: application/json' \
-d '{"text":"..."}'- 从负载中读取、
anchorStart、anchorEnd、snippet。text - 在Markdown源文件中定位锚定区域 —— 是渲染段落的可靠搜索关键字。
snippet - 读取用户在中的评论。
text - 形成实质性的take —— 回答问题、评论锚定文本,或补充缺失的内容。内容要具体。引用锚定内容,而非孤立地回答问题。
- 将其作为take发布,而非回复(替换中的URL):
session.started
bash
curl -s -X POST "$URL/api/threads/<thread-id>/takes" \
-H 'Content-Type: application/json' \
-d '{"text":"..."}'reply.added
(the user replied in a thread)
reply.addedreply.added
(用户在线程中回复)
reply.addedReplies come only from the human (the API uses for humans, for you). Any event is a new user message.
/replies/takesreply.added- Fetch full state: — parse the thread and all its replies/takes in order.
curl -s "$URL/api/state" - Read the latest reply in context.
- Decide: is this a question, a challenge, or a genuine opening for more commentary? If yes, post a follow-up take. If it's closure ("thanks", "got it", "makes sense"), stay silent.
- If responding, POST another take to the same thread.
回复仅来自人类(API使用供人类使用,供你使用)。任何事件都是新的用户消息。
/replies/takesreply.added- 获取完整状态:—— 按顺序解析线程及其所有回复/takes。
curl -s "$URL/api/state" - 在上下文中读取最新回复。
- 判断:这是问题、挑战,还是需要更多评论的真正契机?如果是,发布后续take。如果是结束语(“谢谢”、“明白了”、“有道理”),保持沉默。
- 如果需要响应,向同一线程发布另一个take。
thread.resolved
/ thread.deleted
thread.resolvedthread.deletedthread.resolved
/ thread.deleted
thread.resolvedthread.deletedAcknowledge in chat (" resolved" / " deleted") but do not post anything to the thread.
u-3u-2在聊天窗口中确认(“已解决” / “已删除”),但不要向线程发布任何内容。
u-3u-2Step 4: Stop conditions
步骤4:停止条件
End the session and shut down when any of these happen:
- The user types "stop", "end session", "kill it", or similar in chat.
- The monitored task exits on its own (user quit the browser, server crashed, event arrived). No further notifications will arrive.
session.done - The user starts a new unrelated task — don't linger.
On stop:
- Stop the monitored task so discuss shuts down with it — in Claude Code,
TaskStop(task_id: <id>)in pi.monitor_stop(id: <id>) - Summarize: each thread, a one-line takeaway, resolution state.
当发生以下任何情况时,结束会话并关闭:
- 用户在聊天窗口中输入“stop”、“end session”、“kill it”或类似内容。
- 监控的任务自行退出(用户关闭浏览器、服务器崩溃、事件到达)。不会再收到通知。
session.done - 用户开始新的无关任务 —— 不要停留。
停止时:
- 停止监控任务,使discuss随之关闭 —— 在Claude Code中使用,在pi中使用
TaskStop(task_id: <id>)。monitor_stop(id: <id>) - 总结:每个线程、一行要点、解决状态。
API reference
API参考
All endpoints at the from . Request/response is JSON.
urlsession.started| Method | Path | Body | Purpose |
|---|---|---|---|
| GET | | — | Full snapshot: threads, replies, takes, drafts, verdictConfig |
| GET | | — | SSE stream (alternative to stdout) |
| POST | | | Create a thread. Rare — usually the user does this. |
| DELETE | | — | Soft delete ( |
| POST | | | Human reply. Do NOT use as the agent. |
| POST | | | Agent take. This is your primary tool. |
| POST | | | Resolve a thread |
| POST | | — | Unresolve |
| POST | | | Live source update with re-anchoring (see below) |
| POST | | | Finish the review. With verdict options, missing body is |
所有端点都位于返回的下。请求/响应均为JSON格式。
session.startedurl| 方法 | 路径 | 请求体 | 用途 |
|---|---|---|---|
| GET | | — | 完整快照:线程、回复、takes、草稿、verdictConfig |
| GET | | — | SSE流(stdout的替代方案) |
| POST | | | 创建线程。很少使用 —— 通常由用户创建。多文件模式下 |
| DELETE | | — | 软删除(仅 |
| POST | | | 人类回复。Agent请勿使用。 |
| POST | | | Agent take。这是你的主要工具。 |
| POST | | | 解决线程 |
| POST | | — | 取消解决 |
| POST | | | 实时源更新与重新锚定(见下文) |
| POST | | 配置verdict选项时为 | 完成评审。配置verdict选项时,缺失请求体返回 |
Live source updates (POST /api/source
)
POST /api/source实时源更新(POST /api/source
)
POST /api/sourceIf you regenerate the markdown mid-session (e.g. the user fixed code under review and you rebuilt the diff summary), push the new source into the running session instead of restarting it. You own the re-anchor decision: send the full new markdown plus one entry per active thread on that file — either its new anchor position or if its content no longer exists. Coverage is strict and scoped per file; the request is rejected (and nothing changes) if any of that file's active threads is missing, or if you reference a thread from another file. In multi-file sessions pass ; single-file sessions default to the only file.
"orphaned": truefileIdjson
{
"markdown": "...entire new document...",
"threadAnchors": [
{ "threadId": "u-1", "anchorStart": 4, "anchorEnd": 4, "snippet": "optional refreshed snippet" },
{ "threadId": "u-2", "orphaned": true }
]
}Anchors are 1-based indices of commentable block elements (headings, paragraphs, list items, code blocks) in document order — the same units as on . On success the server re-renders, bumps (visible in ), and broadcasts on SSE and stdout; the browser swaps the document in place and keeps every conversation. Orphaned threads stay visible to the user, flagged as orphaned. You may pass when creating threads via to get a instead of anchoring against a document that changed under you.
anchorStartthread.createdsourceVersion/api/statesource.updatedsourceVersionPOST /api/threads409 stale_source_version如果你在会话中途重新生成Markdown(例如,用户修复了评审中的代码,你重新构建了差异摘要),请将新源推送到运行中的会话,而非重启会话。你负责重新锚定决策:发送完整的新Markdown,以及每个活跃线程的条目 —— 要么是新的锚点位置,要么是(如果其内容已不存在)。覆盖范围严格按文件划分;如果该文件的任何活跃线程缺失,或引用了其他文件的线程,请求会被拒绝(且不会有任何变更)。多文件会话中需传递;单文件会话默认为唯一文件。
"orphaned": truefileIdjson
{
"markdown": "...完整的新文档...",
"threadAnchors": [
{ "threadId": "u-1", "anchorStart": 4, "anchorEnd": 4, "snippet": "可选的刷新片段" },
{ "threadId": "u-2", "orphaned": true }
]
}锚点是文档中可添加评论的块元素(标题、段落、列表项、代码块)的1-based索引,与中的单位相同。成功后,服务器会重新渲染,递增(在中可见),并在SSE和stdout上广播;浏览器会替换文档并保留所有对话。孤立线程对用户仍然可见,会标记为孤立。你在通过创建线程时可传递,这样如果文档在你创建线程时已变更,会返回错误,而非锚定到已变更的文档。
thread.createdanchorStartsourceVersion/api/statesource.updatedPOST /api/threadssourceVersion409 stale_source_versionStdout event kinds
stdout事件类型
- →
session.started{url, mode, source_file, files_count, started_at, git_args?} - → final transcript payload with optional
session.doneverdict: {optionId, label, feedback?, decidedAt} - →
thread.created{id, fileId, kind, anchorStart, anchorEnd, snippet, text, breadcrumb, createdAt} - →
thread.resolved{threadId, resolution: {decision, resolvedAt}} - →
thread.unresolved{threadId} - →
thread.deleted{threadId} - →
reply.added— human reply{id, threadId, text, createdAt} - →
source.updated— a live source update was applied (echo of your own{markdown, fileId, renderedHtml, threadAnchors, orphanedThreadIds, sourceVersion}, or another agent's)POST /api/source - → lifecycle; informational
prompt.suggest_done
Not on stdout: , , — these are SSE-only (browser UI), so they never surface here.
take.addeddraft.updateddraft.cleared- →
session.started{url, mode, source_file, files_count, started_at, git_args?} - → 最终会话负载,可选包含
session.doneverdict: {optionId, label, feedback?, decidedAt} - →
thread.created{id, fileId, kind, anchorStart, anchorEnd, snippet, text, breadcrumb, createdAt} - →
thread.resolved{threadId, resolution: {decision, resolvedAt}} - →
thread.unresolved{threadId} - →
thread.deleted{threadId} - →
reply.added—— 人类回复{id, threadId, text, createdAt} - →
source.updated—— 已应用实时源更新(你自己的{markdown, fileId, renderedHtml, threadAnchors, orphanedThreadIds, sourceVersion}或其他Agent的更新的回显)POST /api/source - → 生命周期事件;仅用于提供信息
prompt.suggest_done
不在stdout中的事件: 、、 —— 这些仅通过SSE传递(浏览器UI),因此永远不会在此处显示。
take.addeddraft.updateddraft.clearedAuthoring markdown for syntax highlighting
为语法高亮编写Markdown
When you generate the markdown to review (especially in stdin mode), tag every code fence with a language so the browser can highlight it. Untagged fences render as plain text.
Common languages: , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , .
rusttypescripttsxjsxjavascriptpythongojavaccppcsharprubyphpswiftkotlinbashshelljsontomlyamlmarkdownhtmlcssscsssqlhcldockerfilenginxinixmlregexgraphqlDiffs: use for plain diffs, or (e.g. , ) for language-aware highlighting on top of the +/- gutter.
diffdiff-<language>diff-rustdiff-typescriptAnything else: Prism supports ~300 languages. If you need one not listed above, check prismjs.com/#supported-languages — discuss loads grammars on demand. The list above is curated; the website is authoritative and may include languages added after this skill was written.
当你生成要评审的Markdown时(尤其是在stdin模式下),为每个代码块标记语言,以便浏览器进行语法高亮。未标记的代码块会渲染为纯文本。
常见语言: 、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、。
rusttypescripttsxjsxjavascriptpythongojavaccppcsharprubyphpswiftkotlinbashshelljsontomlyamlmarkdownhtmlcssscsssqlhcldockerfilenginxinixmlregexgraphql差异内容: 普通差异使用,语言感知的差异使用(例如,、),在+/- gutter之上添加语言高亮。
diffdiff-<language>diff-rustdiff-typescript其他语言: Prism支持约300种语言。如果需要上述未列出的语言,请查看prismjs.com/#supported-languages —— discuss会按需加载语法。上述列表是精选的;网站是权威来源,可能包含此技能编写后新增的语言。
Tone for takes
Takes的语气
- Be specific to the anchored content, not generic.
- Push back when you disagree; don't flatter.
- Cite the source doc when relevant ("line 24 says X, but...").
- Short is better than long — one or two focused paragraphs beats an essay.
- If you don't know, say so. Don't speculate.
- 内容要针对锚定内容,而非泛泛而谈。
- 不同意时要提出异议;不要奉承。
- 相关时引用源文档(“第24行说X,但……”)。
- 简短为佳 —— 一两个重点段落胜过长篇大论。
- 如果不知道,直接说明。不要猜测。