From 3c9bcebef86b34373bb8258748e9e04b90304513 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 3 Jun 2026 05:40:54 +0000 Subject: [PATCH] docs(cli): document owner/repo#number shorthand for issue write commands Documents that gh issue comment create/delete, close, reopen, update, edit, and board-row all now accept the owner/repo#number positional shorthand that gh issue read/view and gh pr * already accept, completing the shorthand parity across the gh subcommand. The companion code change (scripts/src/gh.ts) was committed in the previous commit; this doc update adds the long-term reference for the new behavior and links it to the HWLAB #621 CLI acceptance friction that motivated it. --- docs/reference/cli.md | 59 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 4347d292..a45d0139 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -4,6 +4,8 @@ UniDesk 的统一 CLI 实现入口是根目录 `scripts/cli.ts`,运行方式 主 server 必须在 PATH 上提供 `/root/.local/bin/trans` 可执行 wrapper,内容委托 repo 内版本化 `scripts/trans` 并执行 `bun scripts/cli.ts ssh "$@"`;交互 shell 可额外提供 alias,但非交互 Codex `exec` 和脚本不能依赖 alias 展开。 +`trans` wrapper 是 SSH/WSL/k3s 透传的唯一默认入口:人工/Codex 远端操作、长期参考文档、AGENTS 索引、CLI help、非交互脚本和非交互 `exec` 都必须直接调主 server PATH 上的 `/root/.local/bin/trans`;禁止把 `bun scripts/cli.ts ssh ...`、`bun scripts/cli.ts trans ...` 或任何带 `bun scripts/cli.ts` 前缀的透传写法作为默认入口。`bun scripts/cli.ts help`、`config`、`server`、`provider`、`microservice` 等普通根 CLI 子命令不受这条限制,仍使用 `bun scripts/cli.ts `,避免透传命令和根子命令在调用前缀上互相混淆。 + CLI 可以从 `master` 快速演进,但必须兼容 `deploy.json` 固定的 CI/CD server 和生产运行面。CLI/server 能力协商、unsupported-version 失败语义和 release-line 边界由 `docs/reference/release-governance.md` 与 [GitHub issue #6](https://github.com/pikasTech/unidesk/issues/6) 约束。 ## CI/CD Control Boundary @@ -64,6 +66,8 @@ CI/CD、GitOps、rollout、artifact 发布、PR 合并后的 DEV/PROD 滚动、P - `codex prompt-lint [prompt|--prompt-file path|--prompt-stdin]` 是派发/steer 前的本地 dry-run prompt lint。它只读取 prompt 文本,返回 `dryRun=true`、`mutation=false`、`declaredClass`、`effectiveClass`、`requiredClass`、`dispatchDisposition`、缺失或矛盾项和有界 evidence,不访问 live service、不提交任务、不打印完整 prompt。分级固定为 `read-only`、`live-read`、`live-mutating`;未声明时按 `read-only` 处理。`codex submit --dry-run` 与 `codex steer --dry-run` 会嵌入同一 `promptLint` 结果,帮助指挥官在 dispatch/steer 前发现缺失或矛盾的 live mutation 授权。长期规则见 `docs/reference/code-queue-supervision.md` 的 DEV 测试授权分级。 - `gh issue list [owner/repo] [--state open|closed|all] [--limit N] [--search text] [--label label[,label...]]... [--repo owner/name] [--json number,title,state,url,updatedAt,createdAt,author,labels] [--raw|--full]` 通过 GitHub REST 列出 issue,默认 `state=open`、`limit=30`,输出稳定 JSON 且不依赖系统 `gh` binary。`owner/repo` 位置参数是 `--repo owner/repo` 的兼容别名;若位置 repo 与 `--repo` 冲突,或位置参数不是 `owner/repo`,必须结构化失败,禁止静默 fallback 到默认 repo。`--limit` 是 CLI 返回上限,不等同 GitHub 单页 `per_page`:当 `--limit > 100` 或默认页中混入 PR 时,CLI 必须分页抓取 GitHub REST/Search page,过滤 PR 后再返回 issue,并在输出中披露 `pagination.fetchedPages/rawCount/hasMore`;`hasMore=true` 时只能说明当前有界扫描未穷尽,禁止把它当作“仓库没有更多 issue”。`--search` 使用 GitHub Search Issues API,并自动追加 `repo:/`、`type:issue` 和 state qualifier,用于创建新 issue 前做低摩擦查重;未知 state 或未知 `--json` 字段必须结构化失败并带 `runnerDisposition=business-failed`。`--label` 是 GitHub REST `labels=label1,label2` 或 Search `label:` 服务端过滤,支持重复 `--label` 和逗号分隔;filter 不在本命令上下文中使用(如 `issue read`、`pr list`)必须结构化失败并指明 `gh issue create/list/stale-close` 才是合法作用域。GitHub issues API 可能混入 PR,CLI 会从 `.data.issues` 中过滤 pull request。`--raw|--full` 在 `gh issue list` 上是绕过 20 KiB stdout 截断的显式开关:响应结果会带 `noDump=true`,`output.ts` 据此跳过 head/tail 替换并把完整数据 inline 输出;当响应未超阈值时 `--raw|--full` 行为等价默认。 - `gh issue lifecycle`:`--state` 只能作为 `gh issue list` / `gh issue board-row list` / `gh pr list` 的过滤参数;`gh issue update` / `gh issue edit` 只写 body/title,**不接受** `--state` 改 open/closed。把 `gh issue update --state closed` 落到错命令上时,CLI 必须返回 `validation-failed` 并显式提示 `gh issue close ` / `gh issue reopen `(PR 用 `gh pr close|reopen `),并把 5 条受支持命令放进 `supportedCommands`,禁止把"无 `--state` 改 issue 状态"的命令升级为"接受 `--state`"。`gh issue close|reopen` 成功输出默认是 compact issue 摘要,不得回显完整 `issue.body`;需要正文时后续使用返回的 `readCommands` 或 `gh issue read --json body|--full|--raw`。生命周期 close/reopen 的评论只接受短 `--comment`(或无评论),不接受 `--comment-file` 或 `--body-file`;需要附长篇 CLI 验收证据时,先用 `gh issue comment create --body-file ` 写证据评论,再用 `gh issue close --comment <短引用>` 关闭。issue 硬删除走 `close`,PR 硬删除走 `close`,两者都没有"delete"语义。 +- `gh issue comment create --repo owner/name|--body-file `、`gh issue comment delete --repo owner/name`、`gh issue close --repo owner/name [--comment ]`、`gh issue reopen --repo owner/name [--comment ]`、`gh issue update --repo owner/name [--title ...] [--body-file ]`、`gh issue edit ...`、`gh issue board-row get|update|add|move|delete|upsert --repo owner/name ...` 都接受与 `gh issue read|view`、`gh pr *` 一致的 `owner/repo#number` 位置 shorthand;shorthand 与显式 `--repo` 冲突时结构化失败并把两者都回显到错误对象里,避免静默改写目标 repo。`gh issue read|view`、`gh pr read|view|files|diff|preflight|closeout|comment create|comment delete|close|reopen|merge|edit|update` 已长期支持该 shorthand,issue 写命令对齐后整个 `gh` 子命令在 shorthand 行为上保持一致,不再需要把 `pikasTech/HWLAB#621` 拆成 `621 --repo pikasTech/HWLAB`。来源:HWLAB #621 CLI 验收 `gh issue comment create pikasTech/HWLAB#621` 摩擦改进。 + - `gh issue stale-close [--repo owner/name] [--inactive-hours N] [--limit N] [--label label[,label...]]... [--dry-run]` 是可复用批量生命周期清理入口,用于“超过 N 小时无回复或修改的 open issue 一律关闭”这类策略。判定基准固定为 GitHub `updatedAt < observedAt - inactiveHours`,issue comment、body/title 修改和 state 变化都会刷新 `updatedAt` 并视为活跃;PR 必须过滤,不参与 issue 关闭。默认 `inactive-hours=48`,默认扫描预算为 issue list 上限,输出必须包含 `observedAt`、`cutoffAt`、`scannedCount`、`staleCount`、`pagination.hasMore`、候选/关闭 issue 的 compact 摘要和失败列表,不得打印完整正文。正式关闭前建议先跑 `--dry-run`;真实执行后用同一命令加 `--dry-run` 验证 `staleCount=0`,且只有 `hasMore=false` 才能把当前扫描视为完整穷尽。HWLAB 当前长期策略使用 `bun scripts/cli.ts gh issue stale-close --repo pikasTech/HWLAB --inactive-hours 48 --dry-run` 观察,再移除 `--dry-run` 关闭。 - `gh issue read [--repo owner/name] [--json body,title,state,comments] [--raw|--full]` 通过 GitHub REST 读取 issue title/body/state/url 和 comments,默认输出 JSON;`view` 只保留为兼容别名。`owner/repo#number` shorthand 会自动派生 `--repo owner/repo` 和 issue number;若同时提供冲突的显式 `--repo`,CLI 必须结构化失败并给出 `gh issue read --repo owner/repo --json body,title,state,comments` 与 shorthand raw 的可执行命令。兼容旧脚本的 `--json body` 和 `--json body,title,state,comments` 字段选择,且正文仍稳定暴露在 `.data.issue.body`,避免调用方因为 JSON 路径变化把空值当成正文。字段白名单是 `body,title,state,comments,number,url,author,createdAt,updatedAt`,未知字段必须结构化失败并带 `runnerDisposition=business-failed`。`--raw` 与 `--full` 是显式完整披露别名:read/view 会选择完整支持字段集;issue update/edit 只有显式传入时才在成功响应里包含完整 `.data.issue.body`。当最终 `gh` JSON 超过 20 KiB 时,CLI 必须把完整 JSON 写入 `/tmp/unidesk-cli-output/*.json`,stdout 只返回 `outputTruncated=true`、dump path、总 bytes/lines 和 head/tail 预览。默认 list/read 输出仍不得扩散到无界非 JSON 文本。`gh issue create --title --body-file <file|-> [--label label[,label...]]... [--dry-run]`、`gh issue update <number> --mode replace|append --body-file <file|-> [--title ...] [--dry-run] [--full|--raw]`、`gh issue comment create <number> (--body-file <file|->|--body <short-text>) [--dry-run]`、`gh issue comment delete <commentId> [--dry-run]`、`gh issue close|reopen <number> [--comment <short-text>] [--dry-run]`、`gh issue stale-close [--inactive-hours N] [--dry-run]` 都走 REST,不依赖 `gh` binary。`--body` 仅用于 issue comment 的短单行文本;空白、多行、疑似 shell 污染、secret-like 或过长 inline body 必须结构化失败,Markdown/生成内容/长评论继续用 `--body-file <file|->`。`--label` 用于 `issue create`、`issue list` 和 `issue stale-close`,支持重复传入和逗号分隔;`issue create --dry-run` 会展示解析后的 labels 与 request plan,正式创建时把 labels 放入 GitHub REST create-issue payload,GitHub 返回不存在 label 等 422 校验失败时 CLI 结构化返回 `validation-failed`,不静默成功。`gh issue delete <number>` 是结构化 `unsupported-command`,因为 GitHub REST 不支持 issue 硬删除;生命周期删除语义请使用 `close`。 - `gh issue update <number> --mode replace|append --body-file <file|->` 是正文更新主入口,`edit` 保留为兼容别名。`replace` 用文件或 stdin 正文替换现有 body;`append` 先读取当前 body,再按 UTF-8 文件或 stdin 字节追加,保留真实换行、反引号和 Markdown 表格。更新默认拒绝字面量 `null`、空白正文和过短正文;只有真实需要写短正文时才允许显式加 `--allow-short-body`,返回 JSON 会报告该风险。#20 总看板和指挥简报类 issue 是长期 body-only issue,`--body-profile auto` 会按 issue number 自动启用 #20/#24 legacy guard:#20 必须包含 `## 看板(OPEN)`,#24 legacy 指挥简报必须包含 `## 常驻观察与长期建议`。显式 `--body-profile commander-brief` 不再固定 #24;#24 仍兼容,标题为 `YYYY-MM-DD 指挥简报(北京时间)` 或既有正文首行/关键 heading 表明为每日滚动指挥简报的 issue 也合法,并仍必须包含 `## 常驻观察与长期建议`。对非简报 issue 显式使用 `commander-brief` 会结构化失败为 `profile-issue-mismatch`。`--dry-run` 不 PATCH GitHub,输出有界 `bodyPreview`/`bodyPreviewLines`、新正文长度、SHA、关键标题检查结果、字面量 `\n`、反引号、Markdown 表格、shell 污染信号、`guard`、`concurrency`、`bodyOnlySafety` 和 `wouldPatch`;若环境里有 `GH_TOKEN` 或 `GITHUB_TOKEN`,dry-run 还会只读抓取旧正文长度、SHA 和 `updatedAt` 作为更新前对照。正式写入默认先读取当前 issue,执行 guard 和显式 `--expect-*` 并发校验,再 PATCH;成功输出 compact issue 摘要、old/new body SHA、updatedAt、bodySource 和 drill-down `readCommands`,不包含完整 `issue.body`。完整正文必须显式 `--full|--raw` 或后续执行 `readCommands.body/full/raw` 获取。 @@ -153,9 +157,51 @@ bun scripts/cli.ts microservice proxy todo-note /api/instances/<id>/redo --metho 看到 `404 {"error":"Todo Note is running in backend-only mode"}` 时**第一反应**是路径错(用了不存在的 REST 端点被 catch-all 兜底),不是 mode 锁了写;详见 `docs/reference/microservices.md` 的 Todo Note 段。 +**Todo Note 错路径结构化诊断(issue #198,CLI 侧 interim 修)**:上游 `gitee.com/Lyon1998/todo_note` 的 catch-all handler 当前把任何未注册路径都回 `404 {"ok":false,"error":"Todo Note is running in backend-only mode"}`,与 `TODO_NOTE_BACKEND_ONLY=1` 的真实语义(只关 Vite 前端)混淆。`microservice proxy todo-note` 看到这个特征时会把响应 body 改写成结构化诊断: + +```json +{ + "ok": false, + "error": "Todo Note route not found", + "backendOnly": true, + "method": "POST", + "path": "/api/instances/<id>/todos", + "writableApiEndpoints": [ + { "method": "POST", "path": "/api/instances", "hint": "Create a new todo list; body {name}." }, + { "method": "DELETE", "path": "/api/instances/:instanceId", "hint": "Delete a todo list." }, + { "method": "POST", "path": "/api/instances/:instanceId/actions", "hint": "Apply a typed action; body {action: {type, ...}}." }, + { "method": "POST", "path": "/api/instances/:instanceId/undo", "hint": "Undo the last applied action." }, + { "method": "POST", "path": "/api/instances/:instanceId/redo", "hint": "Redo the last undone action." } + ], + "actionTypes": ["addTodo", "updateTodoTitle", "toggleTodoCompleted", "toggleTodoExpanded", "setAllTodosExpanded", "moveTodo", "deleteTodo", "renameInstance", "setTodoReminder"], + "hint": "...", + "issueReference": "pikasTech/unidesk#198" +} +``` + +改写后的响应同时带 `bodyRewritten: true`、`rewriteReason` 和原始 `upstreamBody`(`{"ok":false,"error":"Todo Note is running in backend-only mode"}`),所以审计 / 排障仍能拿到上游真相;上游 PR 落地后这个改写会自动退化为 no-op,CLI 不再覆盖。 + +**`--check-path` 预检(同样针对 todo-note)**:在真正发请求前先校验 path/method 是否命中 CLI 侧 endpoint catalog,命中返回 `ok: true, matched: true, endpoint: {...}`,未命中返回与 rewrite 同源的结构化诊断且**不调用上游**。这能在 1 步内识别 typo、错端口、错 host 等引起的 catch-all 404。命令样例: + +```sh +# 命中:POST /api/instances(创建清单) +bun scripts/cli.ts microservice proxy todo-note /api/instances --method POST --check-path + +# 命中:POST /api/instances/<id>/actions(action 队列写) +bun scripts/cli.ts microservice proxy todo-note /api/instances/instance_xxx/actions --method POST --check-path + +# 未命中:POST /api/todos(典型 REST 笔误,触发结构化诊断,不打 upstream) +bun scripts/cli.ts microservice proxy todo-note /api/todos --method POST --check-path + +# 非 todo-note 服务:--check-path 当前只支持 todo-note,其他服务返回结构化 unsupported 错误(不阻塞现有 proxy 行为) +bun scripts/cli.ts microservice proxy code-queue /api/foo --method GET --check-path +``` + +`--check-path` 与默认 proxy 行为是正交的:默认 proxy 仍然打 upstream 并对误导 404 做 rewrite;`--check-path` 完全跳过 upstream 调用,只走 CLI 侧 catalog。详见 `scripts/src/microservices.ts` 的 `TODO_NOTE_WRITABLE_ENDPOINTS` / `matchTodoNoteEndpoint` / `rewriteTodoNoteMisleadingRouteNotFound` / `runTodoNoteCheckPath`,以及 `scripts/src/e2e.ts` 的 `microservice:todo-note-route-diagnostic` 门禁。 + **复杂 JSON body 优先 `--body-file <path>` 而非 `--body-json '<inline>'`**(2026-06-01 摩擦改进验证)。`--body-json` 在 shell 里要把双引号 escape 成 `\"`,遇到 action 对象里有嵌套字段或中英文混排标题几乎必坏;`--body-file` 走文件直读,反引号、反斜杠、中文、嵌套 JSON 都安全,且支持 stdin(`--body-file -`)。推荐做法:先 `cat > /tmp/x.json <<'EOF' ... EOF`(heredoc quoted 防 shell 展开),再 `bun scripts/cli.ts microservice proxy todo-note .../actions --method POST --body-file /tmp/x.json`。已交互验证 200 OK 全套 write actions(addTodo / toggleTodoCompleted / deleteTodo),probe 写入 + 删除完整 lifecycle 通。 -GitHub issue/PR 写操作必须优先使用 `bun scripts/cli.ts gh issue|pr ... --body-file <file|->`。不要把 Markdown 正文拼进 shell 参数或 `gh api -f body=...`;这些路径容易把真实换行污染成字面量 `\n`。从 shell 生成正文文件时使用 quoted heredoc,例如 `cat <<'EOF' > /tmp/body.md`,保证反引号、反斜杠和 Markdown 表格不被 shell 展开;一次性写入可直接走 stdin,例如 `cat /tmp/body.md | bun scripts/cli.ts gh issue update <number> --repo owner/name --body-file -` 或 `cat /tmp/body.md | bun scripts/cli.ts gh issue comment create <number> --repo owner/name --body-file -`。`gh issue update/edit` 正式写入默认先读取当前 issue 元数据,执行 body guard 并在结果里返回旧 `bodySha`/`updatedAt` 与新正文摘要;一般看板和评论写入不需要人工先 `read` 再手填 `--expect-body-sha`。对高风险整篇替换仍可显式加 `--expect-body-sha` 或 `--expect-updated-at`,CLI 会在 PATCH 前校验,不匹配则结构化失败。`gh issue comment create --body <short-text>` 只适合人工短单行评论,默认输出只给 bounded preview、bodyChars、bodySha、source 和 readCommands,不回显长正文;同时传 `--body` 与 `--body-file` 必须结构化失败。PR 安全写入口同样支持 `--body-file <file|->`;`--body` 只适合短单行内容。JSON 请求体场景使用各命名空间自己的 `--body-file` 或 `--body-stdin`,避免长 JSON 直接塞进 shell 参数。`update --mode append` 用 REST 读取旧正文后追加文件或 stdin 字节,不引入 shell 拼接正文路径。`gh pr merge` 是 guarded write:先读 closeout metadata 并拒绝非 ready PR,`--dry-run` 只输出计划不写远端;没有 `--confirm` 之类绕过 preflight 的路径。CLI 会按 UTF-8 原样读取文件或 stdin 内容并用 JSON body 调用 REST API;issue/PR 写入输出不会默认回显完整正文。 +GitHub issue/PR 写操作必须优先使用 `bun scripts/cli.ts gh issue|pr ... --body-file <file|->`。不要把 Markdown 正文拼进 shell 参数或 `gh api -f body=...`;这些路径容易把真实换行污染成字面量 `\n`。从 shell 生成正文文件时使用 quoted heredoc,例如 `cat <<'EOF' > /tmp/body.md`,保证反引号、反斜杠和 Markdown 表格不被 shell 展开;一次性写入可直接走 stdin,例如 `cat /tmp/body.md | bun scripts/cli.ts gh issue update <number> --repo owner/name --body-file -` 或 `cat /tmp/body.md | bun scripts/cli.ts gh issue comment create <number|owner/repo#number> --repo owner/name|--body-file -`(issue 写命令支持 `owner/repo#number` shorthand,详见 `gh` 子命令条目)。`gh issue update/edit` 正式写入默认先读取当前 issue 元数据,执行 body guard 并在结果里返回旧 `bodySha`/`updatedAt` 与新正文摘要;一般看板和评论写入不需要人工先 `read` 再手填 `--expect-body-sha`。对高风险整篇替换仍可显式加 `--expect-body-sha` 或 `--expect-updated-at`,CLI 会在 PATCH 前校验,不匹配则结构化失败。`gh issue comment create --body <short-text>` 只适合人工短单行评论,默认输出只给 bounded preview、bodyChars、bodySha、source 和 readCommands,不回显长正文;同时传 `--body` 与 `--body-file` 必须结构化失败。PR 安全写入口同样支持 `--body-file <file|->`;`--body` 只适合短单行内容。JSON 请求体场景使用各命名空间自己的 `--body-file` 或 `--body-stdin`,避免长 JSON 直接塞进 shell 参数。`update --mode append` 用 REST 读取旧正文后追加文件或 stdin 字节,不引入 shell 拼接正文路径。`gh pr merge` 是 guarded write:先读 closeout metadata 并拒绝非 ready PR,`--dry-run` 只输出计划不写远端;没有 `--confirm` 之类绕过 preflight 的路径。CLI 会按 UTF-8 原样读取文件或 stdin 内容并用 JSON body 调用 REST API;issue/PR 写入输出不会默认回显完整正文。 `network perf` 用于生成组网性能前后对比数据。标准 Code Queue overview 读路径基准命令是 `bun scripts/cli.ts network perf --service code-queue --path /api/tasks/overview?limit=30 --count 30 --concurrency 1 --label before`,远程主 server 可用 `bun scripts/cli.ts --main-server-ip 74.48.78.17 network perf ...`。输出包含成功/失败数、状态码分布、`x-unidesk-cache`、`x-unidesk-proxy-mode`、`x-unidesk-upstream-proxy-mode` 分布和 min/p50/p90/p95/max;provider-gateway 长连接数据面验收应看到 `proxyModeCounts.provider-ws-http-tunnel`,adapter native Service 数据面验收应看到 upstream proxy mode 为 `kubernetes-native-service`,若出现 `kubernetes-api-service-proxy` 必须结合 `/api/control-plane.nativeServiceProxy.failedServices` 解释 fallback 原因。 @@ -180,6 +226,17 @@ exec /root/unidesk/scripts/trans "$@" `trans` 同样遵守 route/operation 解析器;route 后面的第一个 token 不是原生 ssh 命令字符串。不要写 `trans G14:/root/hwlab sh -lc '...'`,因为 `sh` 会被解析为 stdin script helper 的别名,`-lc` 会变成不受支持的 script 选项。带变量展开、管道、重定向或多条命令的远端逻辑,默认使用 `trans G14:/root/hwlab script <<'SCRIPT'`;默认 `script` 走目标节点 `/bin/sh`,并继承 provider-gateway/G14 已长期化的 proxy 环境。需要临时单步执行一行远端 shell 逻辑、且不想先创建脚本文件或 heredoc 时,优先使用 `trans G14:/root/hwlab script -- 'sed -n "1,20p" a && sed -n "1,20p" b'`,CLI 会把单个字符串放进目标节点的 `sh -c`,第二个 `sed`、管道和重定向都会留在远端;等价 `shell '<command>'` 仍保留为显式 shell operation。`script` 和 `shell` helper 会在用户 shell 文本前注入一个极小的 POSIX 兼容 `printf` wrapper,使 `printf "--- section ---\n"` 这类高频排障分隔标题在 dash/sh 与 bash 下行为一致;direct argv 形态不注入该 wrapper。`script --` 后跟多个 token 时保持 direct argv,例如 `trans G14:/root/hwlab script -- sed -n '1,20p' AGENTS.md`。只有脚本确实使用 `pipefail`、数组、`[[ ... ]]` 等 bash 专有语义时才加 `--shell bash`,不能把 `--shell bash` 当作 proxy 修复手段。单进程命令才直接写成 argv,例如 `trans G14:/root/hwlab git status --short --branch`。遇到分布式开发摩擦时,优先补强 `trans`/`tran` 的 route/operation、stdin helper 或目标节点环境,并把稳定解法写回长期参考文档,不要退回多层 shell 字符串拼接。 + +### Standard Workspace-Prefixed Passthrough + +- 长期参考、AGENTS 索引、CLI help、Codex 任务脚本、CI/CD 排障和人工远端操作必须统一把已知的远端 workspace 写在 route 的第一个 token,而不是塞进 `cd` 串。`route` 段只表达分布式定位,operation 段才执行命令;workspace 路径是定位信息,不是命令。 +- 标准形态是 `trans <provider>:/absolute/workspace <operation> [args...]`:例如 `trans G14:/root/hwlab git status --short --branch`、`trans G14:/root/hwlab-v02 script -- 'git fetch origin v0.2 && git pull --ff-only origin v0.2'`、`trans D601:/home/ubuntu/workspace/unidesk-dev script <<'SCRIPT'`、`trans G14:/root/hwlab apply-patch < patch.diff` 和 `trans G14:/root/hwlab glob --root . --pattern 'web/hwlab-cloud-web/*.ts' --contains session-tabs`。 +- 反面形态必须删除或迁移:`trans G14 script -- 'cd /root/hwlab && git status --short --branch'`、`trans G14 script <<'SCRIPT' cd /root/hwlab-v02 git fetch origin v0.2 SCRIPT`、`tran G14 script -- 'cd /home/ubuntu/workspace/unidesk-dev && ...'`、`bun scripts/cli.ts ssh G14 -- 'cd /root/hwlab && ...'`。这些写法把已知 workspace 写进 command 字符串,破坏 route/operation 分离,引入本地 shell 二次解析、远端 cwd 漂移和并行 worktree 切换摩擦。 +- 例外只限于一次性探测、临时 heredoc 草稿或旧文档复用;任何被复用第二次的 `cd <workspace> && ...` 都必须重写成 `trans <provider>:/absolute/workspace` 形式。 +- 当远端存在多个并行 workspace(例如 `G14:/root/hwlab` 与 `G14:/root/hwlab-v02`)时,route 必须显式带 workspace,CLI 的 `pwd` 输出、后续 `apply-patch` 的相对路径和 `script` 的 cwd 全部跟随该 workspace;切换 workspace 必须切换 route,不允许在同一次 `trans` 链里再 `cd`。 +- 本规则覆盖所有 host workspace 形态,包括 `G14:/root/hwlab`、`G14:/root/hwlab-v02`、`G14:/root/agentrun-v01`、`D601:/home/ubuntu/workspace/unidesk-dev`、`D601:/home/ubuntu/workspace/hwlab-dev`;provider-gateway 侧已经把它们注册为 host workspace route。 +- 与 `k3s` route 的分工不变:定位控制面继续写 `trans G14:k3s`、定位 workload 继续写 `trans G14:k3s:<namespace>:<workload>[/<workspace>]`;host workspace 路径里的 `cd` 才需要被替换,控制面或 pod 内的多层 shell 不在本规则的清理范围。 + 非交互 `ssh`/`trans`/`tran` 不是登录 shell,不能依赖 `.bashrc`、`.profile` 或交互 alias。 CLI 会在 Host route、workspace route、k3s 控制面脚本和 pod route 的 shell/script helper 前统一注入用户级工具 PATH:`$HOME/.bun/bin`、`$HOME/.local/bin`、`$HOME/bin`