Skip to content

feat: add sentry span list and sentry span view commands#393

Merged
BYK merged 31 commits intomainfrom
feat/span-commands
Mar 16, 2026
Merged

feat: add sentry span list and sentry span view commands#393
BYK merged 31 commits intomainfrom
feat/span-commands

Conversation

@betegon
Copy link
Copy Markdown
Member

@betegon betegon commented Mar 11, 2026

Summary

Adds sentry span list and sentry span view so AI agents can filter/drill-into individual spans within a trace, instead of dealing with the full nested tree dump from sentry trace view --json.

Workflow this enables:

sentry span list <trace-id> --json -q "op:db duration:>100ms" --sort duration
sentry span view <span-id> --trace <trace-id> --json

Changes

Shared utilities (src/lib/formatters/trace.ts):

  • Extracted computeSpanDurationMs from human.ts into trace.ts for reuse
  • flattenSpanTree — converts nested span tree to flat array with depth/child_count
  • findSpanById — searches tree returning span + ancestor chain
  • parseSpanQuery / applySpanFilter — parse and apply -q "op:db duration:>100ms project:backend" filters
  • writeSpanTable / formatSpanDetails — table and detail formatters

sentry span list (src/commands/span/list.ts):

  • Positional: [<org>/<project>] <trace-id> (same pattern as trace view)
  • --query / -q: filter by op, project, description, duration thresholds
  • --sort: time (default, depth-first order) or duration (slowest first)
  • --limit / -n: cap output (default 25, max 1000)
  • JSON mode includes totalSpans and matchedSpans in envelope

sentry span view (src/commands/span/view.ts):

  • Positional: [<org>/<project>] <span-id> [<span-id>...] (multi-ID like log view)
  • --trace / -t: required trace ID
  • --spans: child tree depth (default 3)
  • Shows metadata KV table, ancestor chain, and child span tree
  • JSON mode includes ancestors and children arrays

Route wiring (src/app.ts):

  • sentry span {list, view} route
  • sentry spans plural alias → span list

Test Plan

  • bun run typecheck passes
  • bun run lint passes (0 errors)
  • bun run test:unit — all 3370 passing tests still pass (43 pre-existing failures unrelated)
  • Manual: sentry span list <trace-id> --json --limit 5
  • Manual: sentry span list <trace-id> -q "op:db" --sort duration
  • Manual: sentry span view <span-id> --trace <trace-id> --json
  • Manual: sentry span view <span-id> <span-id> --trace <trace-id>

Closes #391

betegon and others added 2 commits March 11, 2026 14:38
Move computeSpanDurationMs from human.ts to trace.ts and add shared
utilities for the upcoming span commands: flattenSpanTree, findSpanById,
parseSpanQuery, applySpanFilter, writeSpanTable, and formatSpanDetails.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add span as a first-class command group for AI-agent trace debugging.

- span list: flatten and filter spans in a trace with -q "op:db
  duration:>100ms", --sort time|duration, --limit
- span view: drill into specific spans by ID with --trace, shows
  metadata, ancestor chain, and child tree
- spans: plural alias routes to span list

Closes #391

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 11, 2026

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

Init

  • Show feedback hint after successful setup by betegon in #430
  • Add --team flag to relay team selection to project creation by MathurAditya724 in #403
  • Enforce canonical feature display order by betegon in #388
  • Accept multiple delimiter formats for --features flag by betegon in #386
  • Add git safety checks before wizard modifies files by betegon in #379
  • Add experimental warning before wizard runs by betegon in #378
  • Add init command for guided Sentry project setup by betegon in #283

Issue List

  • Auto-compact when table exceeds terminal height by BYK in #395
  • Redesign table to match Sentry web UI by BYK in #372

Other

  • (auth) Allow re-authentication without manual logout by BYK in #417
  • (trial) Auto-prompt for Seer trial + sentry trial list/start commands by BYK in #399
  • Add sentry span list and sentry span view commands by betegon in #393
  • Support SENTRY_HOST as alias for SENTRY_URL by betegon in #409
  • Add --dry-run flag to mutating commands by BYK in #387
  • Return-based output with OutputConfig on buildCommand by BYK in #380
  • Add --fields flag for context-window-friendly JSON output by BYK in #373
  • Magic @ selectors (@latest, @most_frequent) for issue commands by BYK in #371
  • Input hardening against agent hallucinations by BYK in #370
  • Add response caching for read-only API calls by BYK in #330

Bug Fixes 🐛

Dsn

Init

  • Make URLs clickable with OSC 8 terminal hyperlinks by MathurAditya724 in #423
  • Remove implementation detail from help text by betegon in #385
  • Truncate uncommitted file list to first 5 entries by MathurAditya724 in #381

Other

  • (api) Convert --data to query params for GET requests by BYK in #383
  • (docs) Remove double borders and fix column alignment on landing page tables by betegon in #369
  • (trace) Show span IDs in trace view and fix event_id mapping by betegon in #400
  • Show human-friendly names in trial list and surface plan trials by BYK in #412
  • Add trace ID validation to trace view + UUID dash-stripping by BYK in #375

Documentation 📚

  • Update AGENTS.md with patterns from span commands work by BYK in #433
  • Update credential storage docs and remove stale config.json references by betegon in #408

Internal Changes 🔧

Init

  • Remove --force flag by betegon in #377
  • Remove dead determine-pm step label by betegon in #374

Tests

  • Consolidate unit tests subsumed by property tests by BYK in #422
  • Remove redundant and low-value tests by BYK in #418

Other

  • (log/list) Convert non-follow paths to return CommandOutput by BYK in #410
  • Unify commands as generators with HumanRenderer factory, remove stdout plumbing by BYK in #416
  • Convert list command handlers to return data instead of writing stdout by BYK in #404
  • Split api-client.ts into focused domain modules by BYK in #405
  • Migrate non-streaming commands to CommandOutput with markdown rendering by BYK in #398
  • Convert Tier 2-3 commands to return-based output and consola by BYK in #394
  • Convert remaining Tier 1 commands to return-based output by BYK in #382
  • Converge Tier 1 commands to writeOutput helper by BYK in #376

Other

  • Minify JSON on read and pretty-print on write in init local ops by MathurAditya724 in #396

🤖 This preview updates automatically when you update the PR.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 11, 2026

Codecov Results 📊

111 passed | Total: 111 | Pass Rate: 100% | Execution Time: 0ms

📊 Comparison with Base Branch

Metric Change
Total Tests
Passed Tests
Failed Tests
Skipped Tests

✨ No test changes detected

All tests are passing successfully.

✅ Patch coverage is 92.63%. Project has 1101 uncovered lines.
❌ Project coverage is 95.02%. Comparing base (base) to head (head).

Files with missing lines (4)
File Patch % Lines
human.ts 96.32% ⚠️ 44 Missing
traces.ts 73.21% ⚠️ 30 Missing
trace.ts 90.60% ⚠️ 25 Missing
arg-parsing.ts 93.81% ⚠️ 19 Missing
Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
- Coverage    95.10%    95.02%    -0.08%
==========================================
  Files          159       163        +4
  Lines        21516     22103      +587
  Branches         0         0         —
==========================================
+ Hits         20462     21002      +540
- Misses        1054      1101       +47
- Partials         0         0         —

Generated by Codecov Action

betegon and others added 6 commits March 11, 2026 15:40
The UUID dash-stripping is already handled silently by validateHexId.
Remove the validateAndWarn wrappers, mergeWarnings helper, and all
related warning assertions from tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Duration filter `>` vs `>=` was wrong — `duration:>100ms` included 100ms
instead of excluding it. Added exclusive/inclusive tracking to SpanFilter
and extracted duration comparison helpers. Also made `span view --json`
always return an array for consistent output shape.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ce view

Use the server-side spans search endpoint (dataset=spans) for `span list`
instead of fetching the full trace tree and filtering client-side. Add
`translateSpanQuery` to rewrite CLI shorthand keys (op→span.op,
duration→span.duration) for the API.

Also fix trace view showing `undefined` for span IDs — the trace detail
API returns `event_id` instead of `span_id`, so normalize in
`getDetailedTrace`. Append span IDs (dimmed) to each tree line.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move listSpans from monolithic api-client.ts into api/traces.ts module
to align with main's refactored re-export structure. Keep span_id
display without null coalescing (TraceSpan.span_id is required).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@betegon betegon marked this pull request as ready for review March 13, 2026 19:48
…ring

Migrate span list/view from imperative output to auto-rendered OutputConfig
pattern, and fix plain-mode ANSI leaks in span tree/ancestor formatting.

**Output system migration (span/list.ts, span/view.ts):**
- Replace `output: "json"` shorthand with `output: { json: true, human: fn, jsonTransform: fn }`
- Extract human formatters (formatSpanListHuman, formatSpanViewHuman) that
  return strings instead of writing to stdout directly
- Extract JSON transforms (jsonTransformSpanList, jsonTransformSpanView) for
  the { data: [...], hasMore } envelope and --fields filtering
- Return `{ data, hint }` from func() so the wrapper handles rendering
- Remove manual `if (flags.json)` branching and direct stdout writes

**Markdown rendering fixes (trace.ts, human.ts):**
- formatAncestorChain: replace raw `muted()` (chalk ANSI) with
  `colorTag("muted", ...)` + `renderMarkdown()` so output respects
  NO_COLOR/isPlainOutput/non-TTY
- formatSimpleSpanTree/formatSpanSimple: replace `muted()` with
  `plainSafeMuted()` that checks `isPlainOutput()` before applying ANSI
- Span list header now renders via `renderMarkdown()` for proper styling

**Formatter exports (trace.ts):**
- Export SPAN_TABLE_COLUMNS for use by span/list formatter
- Add formatSpanTable() return-based wrapper around formatTable()
BYK added 3 commits March 16, 2026 10:29
After merging main, command.ts uses async generators + CommandOutput class:
- Remove 'json: true' from OutputConfig (no longer needed on main)
- Change async func → async *func (generator)
- Replace return { data, hint } → yield new CommandOutput(data) + return { hint }
- Import CommandOutput from formatters/output.js
- Remove dead code: flattenSpanTree (unused, superseded by EAP API)
- Remove dead code: writeSpanTable (superseded by formatSpanTable)
- Fix case-insensitive span ID matching in findSpanById — lowercase
  the API-returned span_id before comparing with user-lowercased input
- Fix JSON duration consistency: use computeSpanDurationMs() in
  buildJsonResults instead of raw r.span.duration, matching the human
  output path's timestamp-arithmetic fallback
@BYK
Copy link
Copy Markdown
Member

BYK commented Mar 16, 2026

Addressed all Cursor BugBot feedback in f7198cc:

  1. flattenSpanTree dead code — Removed. It was from an earlier design; span list now uses EAP API + spanListItemToFlatSpan.

  2. Case-sensitive span ID matching — Fixed. findSpanById now lowercases API-returned span_id before comparing, matching the user-side .toLowerCase() in validateSpanId.

  3. trace flag not marked required — False positive. In Stricli, kind: "parsed" without optional: true or default IS required — the framework errors if the flag is missing before func runs.

  4. JSON duration inconsistency — Fixed. buildJsonResults now uses computeSpanDurationMs() (with timestamp-arithmetic fallback) instead of raw r.span.duration, matching the human output path.

  5. writeSpanTable dead code — Removed. Superseded by return-based formatSpanTable().

The orgSlugArb and projectSlugArb generators could produce strings
starting with 'xn--' (punycode-encoded IDN labels). When used as a
subdomain (e.g. 'xn--0a.sentry.io'), the URL constructor silently
decodes the punycode, collapsing the hostname to 'sentry.io' and
dropping the org. This caused flaky failures in the buildTraceUrl →
parseSentryUrl round-trip test.

Fix: filter out xn-- prefixed slugs since real Sentry org/project
slugs are never punycode-encoded.
- Restore null fallback for span_id in formatSpanSimple: span.span_id ?? ''
  (defensive against runtime undefined from normalizeTraceSpan edge cases)
- Unexport SPAN_TABLE_COLUMNS — only used internally by formatSpanTable,
  no external consumers
Use optional chaining (span.span_id?.toLowerCase()) to avoid crash
when a span from the API has neither span_id nor event_id.
UX improvement: Replace --trace flag with positional argument to match
the standardized [<org>/<project>/]<id> pattern used by span list,
trace view, and event view.

Before: sentry span view <span-id> --trace <trace-id>
After:  sentry span view [<org>/<project>/]<trace-id> <span-id>

This is consistent with span list which already uses:
  sentry span list [<org>/<project>/]<trace-id>

SKILL.md: Update placeholder text from generic '<args...>' to
descriptive '<org/project/trace-id...>' and '<trace-id/span-id...>'.

Tests: Add comprehensive tests for both span commands:
- span/list: parsePositionalArgs, parseSort, and func body tests
  (API calls, query translation, org/project resolution) → 86.71% coverage
- span/view: validateSpanId, parsePositionalArgs, and func body tests
  (span lookup, JSON output, multi-span, child tree) → 86.89% coverage
- formatters/trace: findSpanById (case-insensitive, undefined span_id),
  computeSpanDurationMs, spanListItemToFlatSpan
BYK and others added 2 commits March 16, 2026 14:34
span list used 'time' while trace list used 'date' — both mapped to
the same -timestamp API sort. Standardize on 'date'/'duration' to
match trace list and issue list conventions.

Also removes 'time' from the listSpans API type since it was redundant
with 'date'.

Refs: PR #393 review feedback
- Use SortValue type alias in ListFlags instead of inline literal union
- Fix MAX_LIMIT JSDoc: removed incorrect claim about internal pagination
  (listSpans passes per_page directly, no client-side pagination)
- Add default params to validateLimit(value, min=1, max=1000)
- Change limit brief to (<=) format
@BYK BYK force-pushed the feat/span-commands branch from f3b7f21 to 22fc1b2 Compare March 16, 2026 14:50
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Extract formatRelativeTime and computeSpanDurationMs into a new
time-utils.ts module. Both are pure utility functions that were
causing a circular dependency:

  trace.ts → human.ts (formatRelativeTime)
  human.ts → trace.ts (computeSpanDurationMs)

Now both import from time-utils.ts which only depends on types/
and markdown.ts — no cycle.

Update all direct importers (span/view.ts, test files) to use
the new module path.
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Add --cursor/-c flag for page-through pagination, matching the pattern
used by trace list, issue list, and log list.

- Wire up shared pagination infrastructure: buildPaginationContextKey,
  resolveOrgCursor, setPaginationCursor, clearPaginationCursor
- Context key scoped to org/project/traceId + sort/query to prevent
  cursor collisions across different queries
- Include nextCursor in JSON output envelope
- Show '-c last' hint in footer when more pages available
- Support 'sentry span list <trace-id> -c last' to resume

The listSpans API already supported cursor — this just wires it up
in the command layer.
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Consolidate two separate imports from time-utils.js into a single
import statement at the top of the file. Removes the mid-file import
that was left from the circular import refactor.
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

- translateSpanQuery: strip leading '!' before alias lookup, re-add
  after resolution. '!op:db' now correctly translates to '!span.op:db'.
- FlatSpan: remove unused depth/child_count fields (remnants of removed
  flattenSpanTree function).
Major updates based on lessons learned during PR #393:

1. CLI Commands section: Replace deprecated stdout.write() pattern with
   the current async generator + CommandOutput + OutputConfig pattern.
   Add explicit rules about buildCommand import source.

2. New sections: Positional Arguments (parseSlashSeparatedArg pattern),
   Markdown Rendering (renderMarkdown/colorTag rules, plainSafeMuted),
   List Command Pagination (cursor infrastructure), ID Validation
   (hex-id.ts validators), Sort Convention ('date' not 'time'),
   SKILL.md (generate:skill, descriptive placeholders).

3. Architecture tree: Add missing directories (span/, trace/, log/,
   trial/, cli/, api/), files (command.ts, hex-id.ts, trace-id.ts,
   pagination.ts, time-utils.ts, markdown.ts, trace.ts, table.ts),
   and fix stale descriptions (api-client.ts is barrel, not ky-based).

4. List Command Infrastructure: Add standalone list command pattern
   (span list, trace list) as third tier alongside buildOrgListCommand
   and dispatchOrgScopedList.

5. Imports: Fix stale @stricli/core import in example.
BYK added a commit that referenced this pull request Mar 16, 2026
Major updates based on lessons learned during PR #393:

1. CLI Commands section: Replace deprecated stdout.write() pattern with
   the current async generator + CommandOutput + OutputConfig pattern.
   Add explicit rules about buildCommand import source.

2. New sections: Positional Arguments (parseSlashSeparatedArg pattern),
   Markdown Rendering (renderMarkdown/colorTag rules, plainSafeMuted),
   List Command Pagination (cursor infrastructure), ID Validation
   (hex-id.ts validators), Sort Convention ('date' not 'time'),
   SKILL.md (generate:skill, descriptive placeholders).

3. Architecture tree: Add missing directories (span/, trace/, log/,
   trial/, cli/, api/), files (command.ts, hex-id.ts, trace-id.ts,
   pagination.ts, time-utils.ts, markdown.ts, trace.ts, table.ts),
   and fix stale descriptions (api-client.ts is barrel, not ky-based).

4. List Command Infrastructure: Add standalone list command pattern
   (span list, trace list) as third tier alongside buildOrgListCommand
   and dispatchOrgScopedList.

5. Imports: Fix stale @stricli/core import in example.
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

BYK added a commit that referenced this pull request Mar 16, 2026
Update AGENTS.md to document patterns established during #393 (span
commands). These were discovered through multiple review cycles and
should prevent future contributors from repeating the same mistakes.

## Changes

**CLI Commands section** — Replace deprecated `stdout.write()` example
with the current `async *func` + `CommandOutput` + `OutputConfig`
pattern. Add explicit warning against importing `buildCommand` from
`@stricli/core`.

**New subsections** under Key Patterns:
- **Positional Arguments** — `parseSlashSeparatedArg` for
`[<org>/<project>/]<id>`
- **Markdown Rendering** — `renderMarkdown()`/`colorTag()` rules;
`plainSafeMuted` for tree output
- **List Command Pagination** — Full cursor infrastructure pattern with
code example
- **ID Validation** — `validateHexId`, `validateSpanId`,
`validateTraceId` from `hex-id.ts`
- **Sort Convention** — Use `"date"` not `"time"` for timestamp sorts
- **SKILL.md** — `generate:skill`, descriptive placeholders

**Architecture tree** — Add 15+ missing directories and files (`span/`,
`trace/`, `log/`, `api/`, `command.ts`, `hex-id.ts`, `pagination.ts`,
`time-utils.ts`, `markdown.ts`, etc.)

**List Command Infrastructure** — Add standalone list command pattern as
third tier.

Refs #393
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

@BYK BYK merged commit ae4ba1c into main Mar 16, 2026
22 checks passed
@BYK BYK deleted the feat/span-commands branch March 16, 2026 18:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add sentry span command group for AI-agent trace debugging

2 participants