Skip to content

Commit 4c4fe56

Browse files
committed
Merge branch 'feature/hidden-issues-cli-staff' into 'main'
cli: surface the hidden-issue flag, only when it is set Closes #330 See merge request postgres-ai/postgresai!394
2 parents 5d6728a + b4ba256 commit 4c4fe56

8 files changed

Lines changed: 678 additions & 55 deletions

File tree

cli/CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,30 @@
22

33
## Unreleased
44

5+
### Added
6+
7+
- `issues list` and `issues view` (and their MCP counterparts `list_issues` /
8+
`view_issue`) now surface the staff-only hidden-issue flag. It is rendered
9+
**only when true**: hidden issues are filtered out server-side for everyone
10+
but PostgresAI staff, so a non-staff response can only ever carry
11+
`is_hidden: false`, and printing that would disclose that the mechanism
12+
exists. `issues list --hidden-only` (MCP: `list_issues` with
13+
`hidden_only: true`) lists just the hidden ones.
14+
15+
Requires platform-all !712, which resolves staff from the access-token
16+
header. Until it is deployed the CLI degrades silently rather than erroring:
17+
`--hidden-only` returns an empty list and `is_hidden` never appears. The
18+
same silence applies to a credential that does not qualify as staff — the
19+
token must be personal, live, and, if it is a per-organization token, issued
20+
on or after 2026-08-14.
21+
22+
Issue requests now carry `x-pgai-include-hidden`, the server's opt-in for
23+
hidden rows. It is a client capability declaration — "this client will mark
24+
hidden issues" — not a user preference. The platform default-excludes hidden
25+
rows from any token caller that omits it, so older CLI versions (and curl,
26+
scripts, MCP clients) keep seeing exactly what they see today rather than
27+
receiving staff-internal issues they would render as ordinary ones.
28+
529
### Fixed
630

731
- `checkup --markdown` previously performed server-side conversion and sent the

cli/README.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ required** — the server will not assume an organization. Call `orgs_list` (or
299299
run `pgai orgs`) to discover the available ids.
300300

301301
Tools exposed:
302-
- `list_issues`: returns the same JSON as `postgresai issues list` (args: `{ org_id, status?, limit?, offset?, debug? }`).
302+
- `list_issues`: returns the same JSON as `postgresai issues list` (args: `{ org_id, status?, hidden_only?, limit?, offset?, debug? }`).
303303
- `view_issue`: view a single issue with its comments (args: `{ issue_id, org_id, debug? }`).
304304
- `create_issue`: create a new issue (args: `{ title, description?, org_id, attachments?, debug? }`).
305305
- `update_issue`: update title/description/status/labels (args: `{ issue_id, org_id, title?, description?, status?, labels?, attachments?, debug? }`).
@@ -350,7 +350,8 @@ sensitive.
350350
### Issues management (`issues` group)
351351

352352
```bash
353-
postgresai issues list # List issues (shows: id, title, status, created_at)
353+
postgresai issues list # List issues (shows: id, title, status, created_at; is_hidden only when set)
354+
postgresai issues list --hidden-only # Only hidden issues (PostgresAI staff)
354355
postgresai issues view <issueId> # View issue details and comments
355356
postgresai issues create --org-id <id> --title <t> # Create a new issue
356357
postgresai issues update <issueId> [--title ... --status ...]# Update an existing issue
@@ -364,6 +365,21 @@ postgresai issues files download <url> [-o <path>] # Download a file
364365
# --json Output raw JSON (overrides default YAML)
365366
```
366367

368+
#### Hidden issues (PostgresAI staff)
369+
370+
Hidden issues are staff-internal. `issues list` and `issues view` mark them
371+
with `is_hidden: true`; the key is omitted entirely otherwise, so ordinary
372+
issues look exactly as they always have. `--hidden-only` lists just the hidden
373+
ones, filtered server-side.
374+
375+
Staff access is granted per credential, not per person, and a credential that
376+
does not qualify simply sees nothing — `--hidden-only` returns an empty list
377+
and `is_hidden` never appears, with no error, which is indistinguishable from
378+
"there are no hidden issues". The token must be personal, not revoked, not
379+
expired, and — if it is a per-organization token rather than a global one —
380+
issued on or after 2026-08-14. Re-issue the token if `--hidden-only` comes
381+
back unexpectedly empty.
382+
367383
#### Attaching files to issues and comments (`--attach`)
368384

369385
`create`, `update`, `post-comment`, and `update-comment` accept a repeatable

cli/bin/postgres-ai.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { fileURLToPath } from "url";
1111
import * as crypto from "node:crypto";
1212
import { Client } from "pg";
1313
import { startMcpServer } from "../lib/mcp-server";
14-
import { fetchIssues, fetchIssueComments, createIssueComment, fetchIssue, createIssue, updateIssue, updateIssueComment, fetchActionItem, fetchActionItems, createActionItem, updateActionItem, type ConfigChange } from "../lib/issues";
14+
import { fetchIssues, fetchIssueComments, createIssueComment, fetchIssue, createIssue, updateIssue, updateIssueComment, fetchActionItem, fetchActionItems, createActionItem, updateActionItem, withVisibleHiddenFlag, type ConfigChange } from "../lib/issues";
1515
import { fetchReports, fetchAllReports, fetchReportFiles, fetchReportFileData, renderMarkdownForTerminal, parseFlexibleDate } from "../lib/reports";
1616
import {
1717
executeJoeCommand,
@@ -4798,9 +4798,10 @@ withOrgOptions(issues.command("list"))
47984798
.option("--status <status>", "filter by status: open, closed, or all (default: all)")
47994799
.option("--limit <n>", "max number of issues to return (default: 20)", parseInt)
48004800
.option("--offset <n>", "number of issues to skip (default: 0)", parseInt)
4801+
.option("--hidden-only", "list only hidden issues (PostgresAI staff)")
48014802
.option("--debug", "enable debug output")
48024803
.option("--json", "output raw JSON")
4803-
.action(async (opts: OrgOptions & { status?: string; limit?: number; offset?: number; debug?: boolean; json?: boolean }) => {
4804+
.action(async (opts: OrgOptions & { status?: string; limit?: number; offset?: number; hiddenOnly?: boolean; debug?: boolean; json?: boolean }) => {
48044805
const spinner = createTtySpinner(process.stdout.isTTY ?? false, "Fetching issues...");
48054806
try {
48064807
const rootOpts = program.opts<CliOptions>();
@@ -4839,16 +4840,22 @@ withOrgOptions(issues.command("list"))
48394840
status: statusFilter,
48404841
limit: opts.limit,
48414842
offset: opts.offset,
4843+
hiddenOnly: !!opts.hiddenOnly,
48424844
debug: !!opts.debug,
48434845
});
48444846
spinner.stop();
48454847
const trimmed = Array.isArray(result)
4846-
? (result as any[]).map((r) => ({
4847-
id: (r as any).id,
4848-
title: (r as any).title,
4849-
status: (r as any).status,
4850-
created_at: (r as any).created_at,
4851-
}))
4848+
? (result as any[]).map((r) =>
4849+
// Route through the same helper as `issues view` so the
4850+
// "render is_hidden only when true" rule lives in one place.
4851+
withVisibleHiddenFlag({
4852+
id: (r as any).id,
4853+
title: (r as any).title,
4854+
status: (r as any).status,
4855+
created_at: (r as any).created_at,
4856+
is_hidden: (r as any).is_hidden,
4857+
})
4858+
)
48524859
: result;
48534860
printResult(trimmed, opts.json);
48544861
} catch (err) {
@@ -4889,7 +4896,7 @@ withOrgOptions(issues.command("view <issueId>"))
48894896
spinner.update("Fetching comments...");
48904897
const comments = await fetchIssueComments({ apiKey, apiBaseUrl, issueId, debug: !!opts.debug });
48914898
spinner.stop();
4892-
const combined = { issue, comments };
4899+
const combined = { issue: withVisibleHiddenFlag(issue), comments };
48934900
printResult(combined, opts.json);
48944901
} catch (err) {
48954902
spinner.stop();

0 commit comments

Comments
 (0)