@@ -37,7 +37,6 @@ import { validateTraceId } from "../../lib/trace-id.js";
3737const log = logger . withTag ( "span.view" ) ;
3838
3939type ViewFlags = {
40- readonly trace : string ;
4140 readonly json : boolean ;
4241 readonly spans : number ;
4342 readonly fresh : boolean ;
@@ -49,7 +48,7 @@ const SPAN_ID_RE = /^[0-9a-f]{16}$/i;
4948
5049/** Usage hint for ContextError messages */
5150const USAGE_HINT =
52- "sentry span view [<org>/<project>] <span-id> [<span-id>...] --trace <trace-id> " ;
51+ "sentry span view [<org>/<project>/]<trace-id> <span-id> [<span-id>...]" ;
5352
5453/**
5554 * Validate that a string is a 16-character hexadecimal span ID.
@@ -69,67 +68,55 @@ export function validateSpanId(value: string): string {
6968 return trimmed ;
7069}
7170
72- /**
73- * Check if a string looks like a 16-char hex span ID.
74- * Used to distinguish span IDs from target args without throwing.
75- */
76- function looksLikeSpanId ( value : string ) : boolean {
77- return SPAN_ID_RE . test ( value . trim ( ) ) ;
78- }
79-
8071/**
8172 * Parse positional arguments for span view.
82- * Handles:
83- * - `<span-id>` — single span ID (auto-detect org/project)
84- * - `<span-id> <span-id> ...` — multiple span IDs
85- * - `<target> <span-id> [<span-id>...]` — explicit target + span IDs
8673 *
87- * The first arg is treated as a target if it contains "/" or doesn't look
88- * like a 16-char hex span ID.
74+ * Uses the same `[<org>/<project>/]<id>` pattern as other commands.
75+ * The first positional is the trace ID (optionally slash-prefixed with
76+ * org/project), and the remaining positionals are span IDs.
77+ *
78+ * Formats:
79+ * - `<trace-id> <span-id> [...]` — auto-detect org/project
80+ * - `<org>/<project>/<trace-id> <span-id> [...]` — explicit target
81+ * - `<project>/<trace-id> <span-id> [...]` — project search
8982 *
9083 * @param args - Positional arguments from CLI
91- * @returns Parsed span IDs and optional target arg
92- * @throws {ContextError } If no arguments provided
93- * @throws {ValidationError } If any span ID has an invalid format
84+ * @returns Parsed trace ID, span IDs, and optional target arg
85+ * @throws {ContextError } If insufficient arguments
86+ * @throws {ValidationError } If any ID has an invalid format
9487 */
9588export function parsePositionalArgs ( args : string [ ] ) : {
89+ traceId : string ;
9690 spanIds : string [ ] ;
9791 targetArg : string | undefined ;
9892} {
9993 if ( args . length === 0 ) {
100- throw new ContextError ( "Span ID" , USAGE_HINT ) ;
94+ throw new ContextError ( "Trace ID and span ID" , USAGE_HINT ) ;
10195 }
10296
10397 const first = args [ 0 ] ;
10498 if ( first === undefined ) {
105- throw new ContextError ( "Span ID" , USAGE_HINT ) ;
99+ throw new ContextError ( "Trace ID and span ID" , USAGE_HINT ) ;
106100 }
107101
108- if ( args . length === 1 ) {
109- // Single arg — could be slash-separated or a plain span ID
110- const { id, targetArg } = parseSlashSeparatedArg (
111- first ,
112- "Span ID" ,
113- USAGE_HINT
114- ) ;
115- const spanIds = [ validateSpanId ( id ) ] ;
116- return { spanIds, targetArg } ;
117- }
118-
119- // Multiple args — determine if first is a target or span ID
120- if ( first . includes ( "/" ) || ! looksLikeSpanId ( first ) ) {
121- // First arg is a target
122- const rawIds = args . slice ( 1 ) ;
123- const spanIds = rawIds . map ( ( v ) => validateSpanId ( v ) ) ;
124- if ( spanIds . length === 0 ) {
125- throw new ContextError ( "Span ID" , USAGE_HINT ) ;
126- }
127- return { spanIds, targetArg : first } ;
102+ // First arg is trace ID (possibly with org/project prefix)
103+ const { id, targetArg } = parseSlashSeparatedArg (
104+ first ,
105+ "Trace ID" ,
106+ USAGE_HINT
107+ ) ;
108+ const traceId = validateTraceId ( id ) ;
109+
110+ // Remaining args are span IDs
111+ const rawSpanIds = args . slice ( 1 ) ;
112+ if ( rawSpanIds . length === 0 ) {
113+ throw new ContextError ( "Span ID" , USAGE_HINT , [
114+ `Use 'sentry span list ${ first } ' to find span IDs within this trace` ,
115+ ] ) ;
128116 }
117+ const spanIds = rawSpanIds . map ( ( v ) => validateSpanId ( v ) ) ;
129118
130- // All args are span IDs
131- const spanIds = args . map ( ( v ) => validateSpanId ( v ) ) ;
132- return { spanIds, targetArg : undefined } ;
119+ return { traceId, spanIds, targetArg } ;
133120}
134121
135122/**
@@ -159,7 +146,6 @@ type ResolvedSpanTarget = { org: string; project: string };
159146 */
160147async function resolveTarget (
161148 parsed : ReturnType < typeof parseOrgProjectArg > ,
162- spanIds : string [ ] ,
163149 traceId : string ,
164150 cwd : string
165151) : Promise < ResolvedSpanTarget | null > {
@@ -171,7 +157,7 @@ async function resolveTarget(
171157 return await resolveProjectBySlug (
172158 parsed . projectSlug ,
173159 USAGE_HINT ,
174- `sentry span view <org>/${ parsed . projectSlug } ${ spanIds [ 0 ] } --trace ${ traceId } `
160+ `sentry span view <org>/${ parsed . projectSlug } / ${ traceId } <span-id> `
175161 ) ;
176162
177163 case "org-all" :
@@ -287,14 +273,15 @@ export const viewCommand = buildCommand({
287273 fullDescription :
288274 "View detailed information about one or more spans within a trace.\n\n" +
289275 "Target specification:\n" +
290- " sentry span view <span -id> --trace <trace -id> # auto-detect\n" +
291- " sentry span view <org>/<proj> <span -id> --trace <trace -id> # explicit\n" +
292- " sentry span view <project> <span -id> --trace <trace -id> # project search\n\n" +
293- "The --trace flag is required to identify which trace contains the span(s). \n" +
294- "Multiple span IDs can be passed as separate arguments .\n\n" +
276+ " sentry span view <trace -id> <span -id> # auto-detect\n" +
277+ " sentry span view <org>/<project>/<trace -id> <span -id> # explicit\n" +
278+ " sentry span view <project>/<trace -id> <span -id> # project search\n\n" +
279+ "The first argument is the trace ID (optionally prefixed with org/project), \n" +
280+ "followed by one or more span IDs .\n\n" +
295281 "Examples:\n" +
296- " sentry span view a1b2c3d4e5f67890 --trace <trace-id>\n" +
297- " sentry span view a1b2c3d4e5f67890 b2c3d4e5f6789012 --trace <trace-id>" ,
282+ " sentry span view <trace-id> a1b2c3d4e5f67890\n" +
283+ " sentry span view <trace-id> a1b2c3d4e5f67890 b2c3d4e5f6789012\n" +
284+ " sentry span view sentry/my-project/<trace-id> a1b2c3d4e5f67890" ,
298285 } ,
299286 output : {
300287 human : formatSpanViewHuman ,
@@ -304,38 +291,31 @@ export const viewCommand = buildCommand({
304291 positional : {
305292 kind : "array" ,
306293 parameter : {
307- placeholder : "args " ,
294+ placeholder : "trace-id/span-id " ,
308295 brief :
309- "[<org>/<project>] <span-id> [<span-id>...] - Target (optional) and one or more span IDs" ,
296+ "[<org>/<project>/]<trace-id> <span-id> [<span-id>...] - Trace ID and one or more span IDs" ,
310297 parse : String ,
311298 } ,
312299 } ,
313300 flags : {
314- trace : {
315- kind : "parsed" ,
316- parse : validateTraceId ,
317- brief : "Trace ID containing the span(s) (required)" ,
318- } ,
319301 ...spansFlag ,
320302 fresh : FRESH_FLAG ,
321303 } ,
322- aliases : { ...FRESH_ALIASES , t : "trace" } ,
304+ aliases : { ...FRESH_ALIASES } ,
323305 } ,
324306 async * func ( this : SentryContext , flags : ViewFlags , ...args : string [ ] ) {
325307 applyFreshFlag ( flags ) ;
326308 const { cwd, setContext } = this ;
327309 const cmdLog = logger . withTag ( "span.view" ) ;
328310
329- const traceId = flags . trace ;
330-
331- // Parse positional args
332- const { spanIds, targetArg } = parsePositionalArgs ( args ) ;
311+ // Parse positional args: first is trace ID (with optional target), rest are span IDs
312+ const { traceId, spanIds, targetArg } = parsePositionalArgs ( args ) ;
333313 const parsed = parseOrgProjectArg ( targetArg ) ;
334314 if ( parsed . type !== "auto-detect" && parsed . normalized ) {
335315 cmdLog . warn ( "Normalized slug (Sentry slugs use dashes, not underscores)" ) ;
336316 }
337317
338- const target = await resolveTarget ( parsed , spanIds , traceId , cwd ) ;
318+ const target = await resolveTarget ( parsed , traceId , cwd ) ;
339319
340320 if ( ! target ) {
341321 throw new ContextError ( "Organization and project" , USAGE_HINT ) ;
0 commit comments