Skip to content

Commit 39ac926

Browse files
committed
fix: address second round of bot review comments
- Replace parsed.type ternaries with Extract<> casts in issue/list.ts and project/list.ts override closures - Fix handleProjectSearch to throw ContextError("Project", ...) instead of ContextError(config.entityName, ...) - Add List Command Infrastructure section to AGENTS.md documenting list-command.ts and org-list.ts helpers
1 parent 4ac5c55 commit 39ac926

File tree

4 files changed

+38
-18
lines changed

4 files changed

+38
-18
lines changed

AGENTS.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,19 @@ import type { SentryContext } from "../../context.js";
319319
import { getAuthToken } from "../../lib/config.js";
320320
```
321321
322+
### List Command Infrastructure
323+
324+
Two abstraction levels exist for list commands:
325+
326+
1. **`src/lib/list-command.ts`** — `buildOrgListCommand` factory + shared Stricli parameter constants (`LIST_TARGET_POSITIONAL`, `LIST_JSON_FLAG`, `LIST_CURSOR_FLAG`, `buildListLimitFlag`). Use this for simple entity lists like `team list` and `repo list`.
327+
328+
2. **`src/lib/org-list.ts`** — `dispatchOrgScopedList` with `OrgListConfig` and a 4-mode handler map: `auto-detect`, `explicit`, `org-all`, `project-search`. Complex commands (`project list`, `issue list`) call `dispatchOrgScopedList` with an `overrides` map directly instead of using `buildOrgListCommand`.
329+
330+
Key rules when writing overrides:
331+
- Use `Extract<ParsedOrgProject, { type: "..." }>` casts to access variant-specific fields — TypeScript cannot narrow discriminated unions across closure boundaries.
332+
- `resolveCursor()` must be called **inside** the `org-all` override closure, not before `dispatchOrgScopedList`, so that `--cursor` validation errors fire correctly for non-org-all modes.
333+
- `handleProjectSearch` errors must use `"Project"` as the `ContextError` resource, not `config.entityName`.
334+
322335
## Commenting & Documentation (JSDoc-first)
323336
324337
### Default Rule

src/commands/issue/list.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import {
1313
listIssuesPaginated,
1414
listProjects,
1515
} from "../../lib/api-client.js";
16-
import { parseOrgProjectArg } from "../../lib/arg-parsing.js";
16+
import {
17+
type ParsedOrgProject,
18+
parseOrgProjectArg,
19+
} from "../../lib/arg-parsing.js";
1720
import { buildCommand } from "../../lib/command.js";
1821
import {
1922
clearPaginationCursor,
@@ -699,7 +702,7 @@ export const listCommand = buildCommand({
699702
"org-all": () =>
700703
handleOrgAllIssues({
701704
stdout,
702-
org: parsed.type === "org-all" ? parsed.org : "",
705+
org: (parsed as Extract<ParsedOrgProject, { type: "org-all" }>).org,
703706
flags,
704707
setContext,
705708
}),

src/commands/project/list.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -673,27 +673,31 @@ export const listCommand = buildCommand({
673673
parsed,
674674
overrides: {
675675
"auto-detect": () => handleAutoDetect(stdout, cwd, flags),
676-
explicit: () =>
677-
handleExplicit(
678-
stdout,
679-
parsed.type === "explicit" ? parsed.org : "",
680-
parsed.type === "explicit" ? parsed.project : "",
681-
flags
682-
),
676+
explicit: () => {
677+
const p = parsed as Extract<ParsedOrgProject, { type: "explicit" }>;
678+
return handleExplicit(stdout, p.org, p.project, flags);
679+
},
683680
"org-all": () => {
684681
// Build context key and resolve cursor only in org-all mode, after
685682
// dispatchOrgScopedList has already validated --cursor is allowed here.
686-
const org = parsed.type === "org-all" ? parsed.org : "";
683+
const p = parsed as Extract<ParsedOrgProject, { type: "org-all" }>;
687684
const contextKey = buildContextKey(parsed, flags, getApiBaseUrl());
688685
const cursor = resolveCursor(flags.cursor, contextKey);
689-
return handleOrgAll({ stdout, org, flags, contextKey, cursor });
690-
},
691-
"project-search": () =>
692-
handleProjectSearch(
686+
return handleOrgAll({
693687
stdout,
694-
parsed.type === "project-search" ? parsed.projectSlug : "",
695-
flags
696-
),
688+
org: p.org,
689+
flags,
690+
contextKey,
691+
cursor,
692+
});
693+
},
694+
"project-search": () => {
695+
const p = parsed as Extract<
696+
ParsedOrgProject,
697+
{ type: "project-search" }
698+
>;
699+
return handleProjectSearch(stdout, p.projectSlug, flags);
700+
},
697701
},
698702
});
699703
},

src/lib/org-list.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ export async function handleProjectSearch<TEntity, TWithOrg>(
505505
return;
506506
}
507507
throw new ContextError(
508-
config.entityName,
508+
"Project",
509509
`No project '${projectSlug}' found in any accessible organization.\n\n` +
510510
`Try: ${config.commandPrefix} <org>/${projectSlug}`
511511
);

0 commit comments

Comments
 (0)