Merge pull request #1661 from pikasTech/fix/1657-gh-json-body
Pipelines as Code CI / hwlab-web-probe-sentinel-nc01- Success
Pipelines as Code CI / unidesk-host- Success

修复 gh view 正文重复导致的无谓 dump
This commit is contained in:
Lyon
2026-07-10 12:33:12 +08:00
committed by GitHub
7 changed files with 25 additions and 4 deletions
+1
View File
@@ -16,6 +16,7 @@ GitHub issue/PR 正式读写必须走 `bun scripts/cli.ts gh ...` 或 `trans gh:
- 规划型、多阶段、架构/API/平台运维类 issue 第一阶段必须 `P0 SPEC 先行`;细则见 [references/issues.md](references/issues.md)。
- `P0 SPEC 先行` 段不得写入硬编码阈值、采样周期、重试次数、并发数等可调参数;必须写明这些参数由指定 YAML/source-of-truth 控制,issue 只列配置路径、字段族和验收读取方式。
- `gh` 默认输出是 k8s 风格 text/table/summary/Next/Disclosure;脚本消费或全量排障必须显式用 `--json``--full``--raw`
- `gh issue view` / `gh pr view` 显式请求 `--json body,...` 时,完整正文只在 `.data.issue.body` / `.data.pullRequest.body` 出现一次;兼容 `.data.json.body` 不再复制大字段,迁移路径由 `.data.compatibility.bodyPath` / `bodyMigrationHint` 披露。
- 多行正文使用 quoted heredoc`--body-stdin <<'EOF'`issue close/reopen 生命周期评论只用 `--comment-stdin <<'EOF'`。不要把 Markdown 塞进 shell 参数。
- PR merge 只走 guarded `gh pr merge``gh pr create` 的 Next 默认是 `--merge --delete-branch`,只有确认 ancestry 可丢弃时才显式 `--squash`
@@ -5,6 +5,7 @@ Issue writes use `bun scripts/cli.ts gh ...` or the `trans gh:` virtual filesyst
- Body and comments default to Chinese.
- Recent issue comment progress should prefer `bun scripts/cli.ts gh issue comments <number> --repo owner/name [--limit N] [--full|--raw]`; structured output is stable at `.data.comments`.
- `gh issue view <number> --json comments` remains the legacy compatibility path at `.data.json.comments`; when comments are requested it should disclose the preferred `gh issue comments <number>` migration path instead of silently pushing operators toward `/tmp` dump recovery.
- `gh issue view <number> --json body,...` emits the full body once at `.data.issue.body`; `.data.json.body` is intentionally omitted, and `.data.compatibility.bodyPath` plus `bodyMigrationHint` disclose the stable machine-readable path.
- New issues include `目标合并分支`.
- Multi-stage architecture/API/platform issues begin with `P0 SPEC 先行`.
- Long body text uses `--body-stdin`.
@@ -3,6 +3,7 @@
PR work uses guarded UniDesk GitHub commands:
- `pr review-plan`, `pr diff --file`, and bounded file drill-down before review.
- `pr view <number> --json body,...` emits the full body once at `.data.pullRequest.body`; `.data.json.body` is intentionally omitted, and `.data.compatibility.bodyPath` plus `bodyMigrationHint` disclose the stable machine-readable path.
- `pr preflight` is optional read-only diagnosis; `pr merge` runs preflight internally.
- `pr merge --merge` by default deletes the merged same-repo head branch, cleans a matching clean local `.worktree`, and fast-forwards the local main worktree on the PR base branch.
- `pr merge --merge --sync-node JD01` additionally runs mapped node source-workspace sync when supported, currently HWLAB `v0.3`.
+2
View File
@@ -175,6 +175,8 @@ UniDesk/HWLAB Web 开发、HWLAB `web-probe run|script|observe|screenshot`、fak
`gh issue view/read --json comments` 是常用人工 drill-down,不得默认输出完整 comment body 或同时在顶层和 `.data.json.comments` 重复整组 comments。默认只返回 comment metadata、body 字符数/SHA 和短 preview`--full` 仍保持评论列表有界,只把 issue 字段扩到完整元数据。单条完整评论正文通过 `gh issue comment view <commentId> --repo owner/name --full` 读取;`--raw` 才是显式展开所有评论正文的排障入口,可能触发 stdout dump。
`gh issue view/read --json body,...``gh pr view/read --json body,...` 的完整正文只允许序列化一次,稳定路径分别是 `.data.issue.body``.data.pullRequest.body`。兼容选字段对象 `.data.json` 保留其他请求字段,但不再复制 `.data.json.body``.data.compatibility.bodyPath``omittedDuplicateBodyPath``duplicateBodyOmitted``bodyMigrationHint` 必须明确唯一读取路径和迁移原因。默认 bounded view 继续使用表格/摘要;显式 `--full` / `--raw` 继续展开相应元数据与正文,但真实单份内容仍超过 YAML stdout 预算时必须保留 dump metadata、SHA 与读取命令。
`microservice proxy` 是面向人工验证和受控调试的私有后端入口。默认 method 为 GET;使用 `--body-json JSON``--body-file path``--body-stdin` 时默认 method 切换为 POST,也可显式加 `--method POST|PUT|PATCH|DELETE`,但 GET/HEAD 不允许携带请求体。所有请求仍受 config 中的 `allowedMethods``allowedPathPrefixes` 限制。为了避免 Pipeline snapshot 这类超大业务 JSON 造成 CLI 输出爆炸,响应 body 超过默认阈值时会返回 `bodyOmitted=true``bodyPreview``bodyBytes``rawHint``--raw` 仍受默认硬限额保护,需要完整 body 时显式添加 `--raw --full`,或用 `--max-body-bytes <N>` 调整预览阈值。正式 frontend 展示仍应优先使用业务控件和 `__unideskArrayLimit` 这类展示级裁剪参数,而不是默认倾倒完整 JSON。
## Todo Note Resource CLI
+9 -1
View File
@@ -181,6 +181,8 @@ export async function prRead(repo: string, token: string, number: number, jsonFi
const metadata = needsPrGraphqlMetadata(jsonFields) ? await prGraphqlMetadata(repo, token, number) : null;
if (isGitHubError(metadata)) return commandError(commandName, repo, metadata, { number, phase: "fetch-pr-closeout-metadata", pullRequest: summary, requestedJsonFields: jsonFields ?? [], closeoutMetadata: prCloseoutMetadataError(metadata) });
const selectionSummary = metadata === null ? fullSummary : { ...fullSummary, ...prMetadataSummary(metadata) };
const selectedJson = jsonFields === undefined ? null : selectedPrJson(selectionSummary, jsonFields);
if (includeBody && selectedJson !== null) delete selectedJson.body;
return {
ok: true,
command: commandName,
@@ -190,10 +192,16 @@ export async function prRead(repo: string, token: string, number: number, jsonFi
...(metadata === null ? {} : { closeoutMetadata: prCloseoutMetadata(metadata) }),
...(jsonFields === undefined ? {} : {
jsonFields,
json: selectedPrJson(selectionSummary, jsonFields),
json: selectedJson,
compatibility: {
legacyJsonBodyPath: includeBody ? ".data.pullRequest.body" : null,
bodyPath: includeBody ? ".data.pullRequest.body" : null,
omittedDuplicateBodyPath: includeBody ? ".data.json.body" : null,
duplicateBodyOmitted: includeBody,
bodyOmitted: !includeBody,
bodyMigrationHint: includeBody
? "Read the full body from .data.pullRequest.body; .data.json.body is intentionally omitted so large bodies are emitted only once."
: null,
},
}),
};
+2 -2
View File
@@ -73,7 +73,7 @@ export function ghHelp(): unknown {
"issue list and pr list accept a single positional owner/repo as a compatibility alias for --repo owner/name. The positional repo and --repo must match if both are supplied; non-repo positionals fail structurally instead of falling back to the default repo.",
"issue list defaults to --state open and bounded --limit 30; it paginates GitHub REST/Search pages internally when --limit exceeds GitHub's per-page cap and discloses pagination/rawCount/hasMore so operators do not mistake a single page for the full repository. --search uses GitHub Search Issues API with repo/type/state qualifiers for low-friction dedupe lookup before creating a new issue. --title-prefix filters the bounded listed issues locally by exact title startsWith, useful for [FEEDBACK] dedupe, and reports titleFilter input/output counts. Supported --json fields are number,title,state,closed,closedAt,url,updatedAt,createdAt,author,labels and unknown fields fail structurally.",
"PR list defaults to --state all for compatibility with earlier UniDesk CLI behavior; supported states are open, closed, and all.",
"issue view is the canonical GitHub CLI-compatible read path; read remains a UniDesk compatibility alias. View/read accept positional numbers, GitHub issue URLs, and owner/repo#number shorthand, deriving --repo unless an explicit conflicting --repo is supplied. --number is accepted on single issue/comment numeric target commands for low-friction compatibility and returns a standard syntax hint; list/create/scan-escape/cleanup-plan/board-audit/board-row list do not accept it. Comment view/read/update/edit/delete treat --number as commentId, not an issue number. View supports lifecycle fields closed/closedAt plus legacy --json field selection; explicit --json fields limit output even with --raw/--full; full issue body is included only when requested with --json body or when --json is omitted and --full/--raw requests all fields. For recent comment progress, prefer `gh issue comments <number>`: it defaults to a bounded recent-comment table and structured output lives at `.data.comments` instead of `.data.json.comments`. `gh issue view --json comments` remains the compatibility path, now with explicit preferredCommand/migrationHint guidance when comments are requested or when large output is compacted. Use gh issue comment view <commentId> --full for one full comment body. Unsupported fields fail structurally.",
"issue view is the canonical GitHub CLI-compatible read path; read remains a UniDesk compatibility alias. View/read accept positional numbers, GitHub issue URLs, and owner/repo#number shorthand, deriving --repo unless an explicit conflicting --repo is supplied. --number is accepted on single issue/comment numeric target commands for low-friction compatibility and returns a standard syntax hint; list/create/scan-escape/cleanup-plan/board-audit/board-row list do not accept it. Comment view/read/update/edit/delete treat --number as commentId, not an issue number. View supports lifecycle fields closed/closedAt plus legacy --json field selection; explicit --json fields limit output even with --raw/--full. When body is requested, the full issue body is emitted once at `.data.issue.body`; `.data.json.body` is omitted and compatibility.bodyPath/bodyMigrationHint disclose the stable replacement path. For recent comment progress, prefer `gh issue comments <number>`: it defaults to a bounded recent-comment table and structured output lives at `.data.comments` instead of `.data.json.comments`. `gh issue view --json comments` remains the compatibility path, now with explicit preferredCommand/migrationHint guidance when comments are requested or when large output is compacted. Use gh issue comment view <commentId> --full for one full comment body. Unsupported fields fail structurally.",
"issue attachment list/download scan issue body and comments for GitHub user attachment URLs (`https://github.com/user-attachments/assets/...`). list is read-only and returns bounded attachment metadata. download writes the selected attachment to --output or /tmp/unidesk-gh-attachments, returns bytes/SHA-256/content-type/path, redacts redirected signed URL query parameters, and never prints binary bytes.",
"--raw and --full are explicit full-disclosure aliases for gh issue list/read/view/comments/update/edit/patch/comment view, gh pr list/read/view/comment view, and gh pr diff --file. For issue read/view commands, --full expands issue fields but keeps comment lists bounded; --raw is the explicit all-comment-body escape hatch. For `gh issue comments`, both --full and --raw keep the list bounded to recent comments and include full comment bodies in structured output. Use issue comment view <commentId> --full for a single full comment body. For issue writes, default success output omits full issue.body and returns bodyChars/bodySha/bodyPreview plus readCommands; --full|--raw includes the full returned issue body only on commands that explicitly support full disclosure.",
"CLI output larger than config/unidesk-cli.yaml output.maxStdoutBytes is automatically written to /tmp/unidesk-cli-output; stdout stays bounded with outputTruncated=true, warning text, dump file metadata, and drill-down read commands.",
@@ -102,7 +102,7 @@ export function ghHelp(): unknown {
"PR review-plan is the review-first bounded patch index. It uses GitHub REST PR files, prints changed files with additions/deletions/hunk counts/patch-line counts/default truncation flags, and emits one per-file gh pr diff --file drill-down command without creating a local worktree.",
"PR diff --file reads one changed file patch from GitHub REST and prints only a bounded excerpt by default. Add --hunk N to inspect one hunk, --limit N to change displayed patch lines, or --full/--raw for explicit structured full-patch disclosure. --stat remains the no-patch file/stat compatibility path.",
"PR edit/update PATCHes /repos/{owner}/{repo}/pulls/{number} through REST only, never GitHub Projects Classic GraphQL/projectCards, and returns low-noise JSON with repo, PR number, changedFields, url, and body size/SHA metadata instead of echoing the full body.",
"PR view is the canonical GitHub CLI-compatible read path; read remains a UniDesk compatibility alias. PR view/read accept positional numbers, GitHub PR URLs, and owner/repo#number shorthand, deriving --repo unless an explicit conflicting --repo is supplied. --number is accepted on single PR/comment numeric target commands for low-friction compatibility and returns a standard syntax hint; list/create do not accept it. PR comment update/edit/delete treat --number as commentId, not a PR number. PR view/read supports REST closeout fields stateDetail, closed, closedAt, merged, mergedAt, mergeCommit, headRefName, and baseRefName; explicit --json fields limit output even with --raw/--full. Mergeable, mergeStateStatus, and statusCheckRollup are fetched through GitHub GraphQL only when requested or when --json is omitted and --raw/--full requests full disclosure, and closeoutMetadata makes GraphQL errors plus UNKNOWN/null metadata explicit.",
"PR view is the canonical GitHub CLI-compatible read path; read remains a UniDesk compatibility alias. PR view/read accept positional numbers, GitHub PR URLs, and owner/repo#number shorthand, deriving --repo unless an explicit conflicting --repo is supplied. --number is accepted on single PR/comment numeric target commands for low-friction compatibility and returns a standard syntax hint; list/create do not accept it. PR comment update/edit/delete treat --number as commentId, not a PR number. PR view/read supports REST closeout fields stateDetail, closed, closedAt, merged, mergedAt, mergeCommit, headRefName, and baseRefName; explicit --json fields limit output even with --raw/--full. When body is requested, the full PR body is emitted once at `.data.pullRequest.body`; `.data.json.body` is omitted and compatibility.bodyPath/bodyMigrationHint disclose the stable replacement path. Mergeable, mergeStateStatus, and statusCheckRollup are fetched through GitHub GraphQL only when requested or when --json is omitted and --raw/--full requests full disclosure, and closeoutMetadata makes GraphQL errors plus UNKNOWN/null metadata explicit.",
"PR preflight/closeout accept the same owner/repo#number shorthand as PR view/read so merge readiness checks do not require repeating --repo after a PR URL has already been normalized.",
"PR list does not fetch mergeability or statusCheckRollup; request those closeout fields with gh pr view <number> --json headRefName,baseRefName,mergeable,mergeStateStatus,statusCheckRollup.",
"PR preflight is a low-noise read-only closeout helper for explicit diagnosis only. It combines redacted auth capability, PR branch/state metadata, mergeability, mergeStateStatus, compact status check counts, and an explicit read-only policy. It is not a required step before gh pr merge. Use --full or --raw to include all fetched status contexts.",
+9 -1
View File
@@ -128,7 +128,9 @@ export function selectedIssueJson(issue: GitHubIssue, comments: GitHubComment[]
const summary = issueSummary(issue);
const selected: Record<string, unknown> = {};
for (const field of fields) {
if (field === "comments") {
if (field === "body") {
continue;
} else if (field === "comments") {
selected.comments = (comments ?? []).map(options.includeFullCommentBodies === true ? commentSummary : compactIssueViewCommentSummary);
} else {
selected[field] = summary[field];
@@ -189,7 +191,13 @@ export async function issueRead(repo: string, token: string, issueNumber: number
json: selectedIssueJson(issue, comments, jsonFields, { includeFullCommentBodies }),
compatibility: {
legacyJsonBodyPath: includeBody ? ".data.issue.body" : null,
bodyPath: includeBody ? ".data.issue.body" : null,
omittedDuplicateBodyPath: includeBody ? ".data.json.body" : null,
duplicateBodyOmitted: includeBody,
bodyOmitted: !includeBody,
bodyMigrationHint: includeBody
? "Read the full body from .data.issue.body; .data.json.body is intentionally omitted so large bodies are emitted only once."
: null,
...commentsCompatibility,
readCommands: {
...(includeBody ? {} : issueBodyReadCommands(repo, issueNumber)),