Skip to content

Commit 5584a1d

Browse files
committed
fix(help): document target patterns and trailing-slash significance (#267)
Renames the positional placeholder from `<target>` to `<org/project>` so the usage line is self-documenting. Adds a prose explanation of trailing-slash semantics to the fullDescription of every command that uses parseOrgProjectArg, and improves the --cursor rejection error in dispatchOrgScopedList to include a contextual suggestion when a bare slug was given.
1 parent 981435c commit 5584a1d

File tree

9 files changed

+64
-23
lines changed

9 files changed

+64
-23
lines changed

src/commands/issue/list.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import {
4040
LIST_BASE_ALIASES,
4141
LIST_JSON_FLAG,
4242
LIST_TARGET_POSITIONAL,
43+
targetPatternExplanation,
4344
} from "../../lib/list-command.js";
4445
import {
4546
dispatchOrgScopedList,
@@ -657,11 +658,12 @@ export const listCommand = buildCommand({
657658
brief: "List issues in a project",
658659
fullDescription:
659660
"List issues from Sentry projects.\n\n" +
660-
"Target specification:\n" +
661+
"Target patterns:\n" +
661662
" sentry issue list # auto-detect from DSN or config\n" +
662663
" sentry issue list <org>/<proj> # explicit org and project\n" +
663-
" sentry issue list <org>/ # all projects in org\n" +
664+
" sentry issue list <org>/ # all projects in org (trailing / required)\n" +
664665
" sentry issue list <project> # find project across all orgs\n\n" +
666+
`${targetPatternExplanation()}\n\n` +
665667
"In monorepos with multiple Sentry projects, shows issues from all detected projects.",
666668
},
667669
parameters: {

src/commands/log/list.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
writeFooter,
1919
writeJson,
2020
} from "../../lib/formatters/index.js";
21+
import { TARGET_PATTERN_NOTE } from "../../lib/list-command.js";
2122
import { resolveOrgProjectFromArg } from "../../lib/resolve-target.js";
2223
import { getUpdateNotification } from "../../lib/version-check.js";
2324
import type { SentryLog, Writer } from "../../types/index.js";
@@ -232,10 +233,11 @@ export const listCommand = buildCommand({
232233
brief: "List logs from a project",
233234
fullDescription:
234235
"List and stream logs from Sentry projects.\n\n" +
235-
"Target specification:\n" +
236+
"Target patterns:\n" +
236237
" sentry log list # auto-detect from DSN or config\n" +
237238
" sentry log list <org>/<proj> # explicit org and project\n" +
238239
" sentry log list <project> # find project across all orgs\n\n" +
240+
`${TARGET_PATTERN_NOTE}\n\n` +
239241
"Examples:\n" +
240242
" sentry log list # List last 100 logs\n" +
241243
" sentry log list -f # Stream logs (2s poll interval)\n" +
@@ -248,8 +250,8 @@ export const listCommand = buildCommand({
248250
kind: "tuple",
249251
parameters: [
250252
{
251-
placeholder: "target",
252-
brief: "Target: <org>/<project> or <project>",
253+
placeholder: "org/project",
254+
brief: "<org>/<project> or <project> (search)",
253255
parse: String,
254256
optional: true,
255257
},

src/commands/project/list.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import {
4444
LIST_CURSOR_FLAG,
4545
LIST_JSON_FLAG,
4646
LIST_TARGET_POSITIONAL,
47+
targetPatternExplanation,
4748
} from "../../lib/list-command.js";
4849
import {
4950
dispatchOrgScopedList,
@@ -607,13 +608,14 @@ export const listCommand = buildCommand({
607608
brief: "List projects",
608609
fullDescription:
609610
"List projects in an organization.\n\n" +
610-
"Target specification:\n" +
611+
"Target patterns:\n" +
611612
" sentry project list # auto-detect from DSN or config\n" +
612-
" sentry project list <org>/ # list all projects in org (paginated)\n" +
613+
" sentry project list <org>/ # all projects in org (paginated)\n" +
613614
" sentry project list <org>/<proj> # show specific project\n" +
614615
" sentry project list <project> # find project across all orgs\n\n" +
616+
`${targetPatternExplanation("Cursor pagination (--cursor) requires the <org>/ form.")}\n\n` +
615617
"Pagination:\n" +
616-
" sentry project list <org>/ -c last # continue from last page\n" +
618+
" sentry project list <org>/ -c last # continue from last page\n" +
617619
" sentry project list <org>/ -c <cursor> # resume at specific cursor\n\n" +
618620
"Filtering and output:\n" +
619621
" sentry project list --platform javascript # filter by platform\n" +

src/commands/project/view.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
writeJson,
2121
writeOutput,
2222
} from "../../lib/formatters/index.js";
23+
import { TARGET_PATTERN_NOTE } from "../../lib/list-command.js";
2324
import {
2425
type ResolvedTarget,
2526
resolveAllTargets,
@@ -201,19 +202,20 @@ export const viewCommand = buildCommand({
201202
brief: "View details of a project",
202203
fullDescription:
203204
"View detailed information about Sentry projects.\n\n" +
204-
"Target specification:\n" +
205+
"Target patterns:\n" +
205206
" sentry project view # auto-detect from DSN or config\n" +
206207
" sentry project view <org>/<project> # explicit org and project\n" +
207208
" sentry project view <project> # find project across all orgs\n\n" +
209+
`${TARGET_PATTERN_NOTE}\n\n` +
208210
"In monorepos with multiple Sentry projects, shows details for all detected projects.",
209211
},
210212
parameters: {
211213
positional: {
212214
kind: "tuple",
213215
parameters: [
214216
{
215-
placeholder: "target",
216-
brief: "Target: <org>/<project>, <project>, or omit for auto-detect",
217+
placeholder: "org/project",
218+
brief: "<org>/<project>, <project> (search), or omit for auto-detect",
217219
parse: String,
218220
optional: true,
219221
},

src/commands/trace/list.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
writeFooter,
1515
writeJson,
1616
} from "../../lib/formatters/index.js";
17+
import { TARGET_PATTERN_NOTE } from "../../lib/list-command.js";
1718
import { resolveOrgProjectFromArg } from "../../lib/resolve-target.js";
1819

1920
type ListFlags = {
@@ -67,10 +68,11 @@ export const listCommand = buildCommand({
6768
brief: "List recent traces in a project",
6869
fullDescription:
6970
"List recent traces from Sentry projects.\n\n" +
70-
"Target specification:\n" +
71+
"Target patterns:\n" +
7172
" sentry trace list # auto-detect from DSN or config\n" +
7273
" sentry trace list <org>/<proj> # explicit org and project\n" +
7374
" sentry trace list <project> # find project across all orgs\n\n" +
75+
`${TARGET_PATTERN_NOTE}\n\n` +
7476
"Examples:\n" +
7577
" sentry trace list # List last 10 traces\n" +
7678
" sentry trace list --limit 50 # Show more traces\n" +
@@ -82,8 +84,8 @@ export const listCommand = buildCommand({
8284
kind: "tuple",
8385
parameters: [
8486
{
85-
placeholder: "target",
86-
brief: "Target: <org>/<project> or <project>",
87+
placeholder: "org/project",
88+
brief: "<org>/<project> or <project> (search)",
8789
parse: String,
8890
optional: true,
8991
},

src/lib/list-command.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,49 @@ import { dispatchOrgScopedList, type OrgListConfig } from "./org-list.js";
2424
// ---------------------------------------------------------------------------
2525

2626
/**
27-
* Positional `target` parameter shared by all list commands.
27+
* Positional `org/project` parameter shared by all list commands.
2828
*
29-
* Accepts `<org>/`, `<org>/<project>`, or bare `<org>` / `<project>`.
29+
* Accepts `<org>/`, `<org>/<project>`, or bare `<project>` (search).
3030
* Marked optional so the command falls back to auto-detection when omitted.
3131
*/
3232
export const LIST_TARGET_POSITIONAL = {
3333
kind: "tuple" as const,
3434
parameters: [
3535
{
36-
placeholder: "target",
37-
brief: "Target: <org>/, <org>/<project>, or <org>",
36+
placeholder: "org/project",
37+
brief: "<org>/ (all projects), <org>/<project>, or <project> (search)",
3838
parse: String,
3939
optional: true as const,
4040
},
4141
],
4242
};
4343

44+
/**
45+
* Short note for commands that accept a bare project name but do not support
46+
* org-all mode (e.g. trace list, log list, project view).
47+
*
48+
* Explains that a bare name triggers project-search, not org-scoped listing.
49+
*/
50+
export const TARGET_PATTERN_NOTE =
51+
"A bare name (no slash) is treated as a project search. " +
52+
"Use <org>/<project> for an explicit target.";
53+
54+
/**
55+
* Full explanation of trailing-slash semantics for commands that support all
56+
* four target modes including org-all (e.g. issue list, project list).
57+
*
58+
* @param cursorNote - Optional sentence appended when the command supports
59+
* cursor pagination (e.g. "Cursor pagination (--cursor) requires the <org>/ form.").
60+
*/
61+
export function targetPatternExplanation(cursorNote?: string): string {
62+
const base =
63+
"The trailing slash on <org>/ is significant — without it, the argument " +
64+
"is treated as a project name search (e.g., 'sentry' searches for a " +
65+
"project named 'sentry', while 'sentry/' lists all projects in the " +
66+
"'sentry' org).";
67+
return cursorNote ? `${base} ${cursorNote}` : base;
68+
}
69+
4470
/**
4571
* The `--json` flag shared by all list commands.
4672
* Outputs machine-readable JSON instead of a human-readable table.

src/lib/org-list.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -739,10 +739,15 @@ export async function dispatchOrgScopedList<TEntity, TWithOrg>(
739739

740740
// Cursor pagination is only supported in org-all mode
741741
if (flags.cursor && parsed.type !== "org-all") {
742+
const hint =
743+
parsed.type === "project-search"
744+
? `\n\nDid you mean '${config.commandPrefix} ${parsed.projectSlug}/'? ` +
745+
`A bare name searches for a project — add a trailing slash to list an org's ${config.entityPlural}.`
746+
: "";
742747
throw new ValidationError(
743-
`The --cursor flag is only supported when listing ${config.entityPlural} for a specific organization ` +
744-
`(e.g., ${config.commandPrefix} <org>/). ` +
745-
`Use '${config.commandPrefix} <org>/' for paginated results.`,
748+
"The --cursor flag requires the <org>/ pattern " +
749+
`(e.g., ${config.commandPrefix} my-org/).` +
750+
hint,
746751
"cursor"
747752
);
748753
}

test/lib/list-command.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe("LIST_TARGET_POSITIONAL", () => {
2929
expect(LIST_TARGET_POSITIONAL.kind).toBe("tuple");
3030
expect(LIST_TARGET_POSITIONAL.parameters).toHaveLength(1);
3131
const param = LIST_TARGET_POSITIONAL.parameters[0];
32-
expect(param.placeholder).toBe("target");
32+
expect(param.placeholder).toBe("org/project");
3333
expect(param.optional).toBe(true);
3434
expect(param.parse).toBe(String);
3535
});

test/lib/org-list.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ describe("dispatchOrgScopedList", () => {
686686
expect.unreachable("should have thrown");
687687
} catch (e) {
688688
expect(e).toBeInstanceOf(ValidationError);
689-
expect((e as ValidationError).message).toContain("widgets");
689+
expect((e as ValidationError).message).toContain("<org>/");
690690
}
691691
});
692692

0 commit comments

Comments
 (0)