agentsclimarketplace

Ask

Skill Attuned-Corp/skills/skills/ask

Queries Span for organizational observability across six domains: productivity (cycle time, throughput, review metrics), DORA (deployment frequency, lead time, MTTR, change failure rate), investment (effort allocation, workstreams, cost capitalization), AI transformation (AI code ratio, adoption, spend), calendar (focus time, meeting load), and AI traces (agent sessions — what happened inside them). Use when asked about engineering metrics, team velocity, pull requests, commits, deployments, epics, issues, sprints, investments, teams, or people; or about agent sessions, coding-agent or Claude Code sessions, agent traces, skill usage, prompts, tool calls, or AI-agent behavior.From its SKILL.md

Install
npx -y skills add Attuned-Corp/skills --skill ask

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

20.0 KB, as published. Nobody here has run it

Span

If the user provided arguments with this invocation, treat $ARGUMENTS as their query and proceed directly (still ask for clarification if the time range is missing).

Script & Path Setup

All bash commands in this skill should start by setting these two variables:

SPAN_DIR="${SPAN_CONFIG_DIR:-$HOME/.spanrc}"
SKILL_SCRIPTS="${CLAUDE_SKILL_DIR}/scripts"

Use $SKILL_SCRIPTS/query.sh, $SKILL_SCRIPTS/fetch-metadata.sh, etc. for all API operations. Always prefer the scripts over inline curl commands.

Invocation Flow

  1. Check configuration state — the dynamic context injection below tells you if the skill is configured
  2. If not configured — run the First-Time Setup flow before anything else
  3. Check metadata cache — load $SPAN_DIR/metadata-cache.json, or fetch if missing
  4. Ask clarifying questions — especially for missing time ranges
  5. Plan query — search metadata for metrics, choose asset level, use pre-aggregated metrics
  6. Execute query — write query JSON to a temp file, run $SKILL_SCRIPTS/query.sh
  7. Format and return results — convert units, handle pagination

Configuration State

!${CLAUDE_SKILL_DIR}/scripts/check-config.sh

First-Time Setup

If the configuration state above shows "api version mismatch", warn the user that their installed skill version may be incompatible with the current Span API, and suggest they update the skill. You may still attempt queries, but results may be unreliable.

If the configuration state above shows "not configured", you MUST run the onboarding flow before doing anything else:

  1. Create the configuration folder:

    SPAN_DIR="${SPAN_CONFIG_DIR:-$HOME/.spanrc}"
    mkdir -p "$SPAN_DIR"
    
  2. Prompt user to add their token to $SPAN_DIR/auth.json:

    {
      "token": "your-personal-access-token"
    }
    
  3. Verify the token file exists by running $SKILL_SCRIPTS/check-config.sh (never read auth.json directly).

  4. Fetch initial metadata:

    $SKILL_SCRIPTS/fetch-metadata.sh
    
  5. Confirm setup is complete and proceed with the user's original request.

Configuration Directory

The skill stores configuration in ~/.spanrc/ by default. Override with SPAN_CONFIG_DIR:

~/.spanrc/                    # or $SPAN_CONFIG_DIR
├── auth.json                 # Span Personal Access Token (required)
└── metadata-cache.json       # Cached API metadata (auto-generated)

Token Security (CRITICAL)

NEVER read, print, or expose the Personal Access Token. The scripts in $SKILL_SCRIPTS/ handle authentication internally. You must NOT:

  • Read auth.json (never use the Read tool on this file)
  • Echo, print, or log the token value
  • Construct inline curl commands with the token — use the scripts instead
  • Include the token in error messages or output

Clarifying Questions (IMPORTANT)

Always ask clarifying questions rather than making assumptions.

Time Intervals

If the user's query does not specify a time range, always ask before executing:

"What time period would you like me to query? For example: last 30 days, last quarter, or a specific date range. (If you'd like, I can default to the last 30 days.)"

Do NOT assume a time range. Even if the query sounds like it implies "recent" data, ask explicitly.

Examples requiring clarification:

  • "How many PRs did we merge?" → Ask for time range
  • "What's our cycle time?" → Ask for time range

Examples where time range is provided (no need to ask):

  • "PRs merged last week" → Use last 7 days
  • "Cycle time for Q4" → Use Q4 date range

Other Ambiguities

Also ask for clarification when:

  • Team/person is unclear: "Which team would you like me to query?"
  • Metric is ambiguous: "Did you mean X or Y metric?"
  • Scope is unclear: "Should this be organization-wide or for a specific team?"

For trace questions, don't ask whose sessions you may read — permissions decide that, and it isn't knowable in advance. Query, then report the scope you actually measured (see references/domains.md → AI Traces). Still ask for the time range.

Metadata Caching

API metadata (assets, fields, metrics) is cached at $SPAN_DIR/metadata-cache.json. If the cache exists, use it. If not, run $SKILL_SCRIPTS/fetch-metadata.sh. Only refresh when the user explicitly asks to reload.

Query Planning (MANDATORY)

Before executing any query, you MUST follow this process:

Step 0: Verify Feasibility

Before building any query, confirm:

  1. The requested metric exists in cached metadata
  2. The requested asset type exists (Team, Person, PullRequest, etc.)
  3. The metric is available on that asset type

Trace facades are gated. Trace, TraceEvent, TraceTurn and TraceRecommendation are absent from metadata unless the org has AI traces enabled and the token carries trace access. If they're missing after a refresh, report that — not "no agent activity".

If verification fails, prioritize partial fulfillment:

  • Execute what IS possible, return available data
  • Note what couldn't be included and why
  • Suggest alternatives from available metrics/assets

Only refuse entirely when the core metric/asset is completely unavailable.

Step 1: Check Available Metrics

ALWAYS inspect the cached metadata to find relevant metrics:

SPAN_DIR="${SPAN_CONFIG_DIR:-$HOME/.spanrc}"
cat "$SPAN_DIR/metadata-cache.json" | jq '.data[].metrics[]? | select(.label | test("<keyword>"; "i"))'

NEVER calculate or aggregate metrics yourself if a pre-built metric exists. The API provides pre-computed metrics that are more accurate and efficient than manual calculations.

NEVER guess or fabricate metric IDs. Always use the exact metricId UUIDs returned by the cached metadata. Hallucinated metric IDs will cause query failures.

Step 2: Determine the Right Asset Level

Assets act as aggregation points (like SQL GROUP BY). Choose the appropriate asset:

User asks about...Query this assetFilter by
Organization/company-wide (most metrics)TeamTeam.name = "<root team>" — the org root; see note below
Organization-wide deploymentsTeam (mode: "groups")Team.groupPath DESCENDANT_OF the org root path — Team.name is leaf-only for deploys and does NOT work here; see "Deployment metrics across a team tree"
A specific team's metricsTeamTeam.name = "<team-name>" (use =, NOT DESCENDANT_OF)
People in a team and sub-teamsPersonPerson.Teams.name with DESCENDANT_OF operator
Metrics across a team tree (most metrics)PersonPerson.Teams.name with DESCENDANT_OF + metrics on Person — does NOT work for deployment metrics, see below
Deployment metrics for a team + its sub-teamsTeam (mode: "groups")Team.groupPath DESCENDANT_OF the team's path — see "Deployment metrics across a team tree" below
A specific personPerson or PullRequest.AuthorEmail or name
A specific repositoryRepository or PullRequest.RepositoryRepository name
Individual PRs/commitsPullRequest or CommitAs needed
Volume/cost/adoption of agent sessionsTeam or Person + trace metricsNormal metric loop
Agent sessions broken down by tool/model/task categoryTrace (mode: "groups")Group by a dimension (aiTraceTool, …), never by the Author/Team relation
What happened inside agent sessions (skills, prompts, tool calls, files)Trace → TraceTurn → TraceEventCorpus queries, metrics: [] — see domains.md
Breakdown by dimension (tenure, job level, etc.)Use mode: "groups"See api-reference.md

IMPORTANT: For org-wide metrics, query Team filtered by the root team — the one whose Team.path has no . separator (just <hash>__<slug>). Find it by listing teams: {"select": ["Team.name", "Team.path"]}. Do NOT manually aggregate across repositories or people. Exception — deployments (and other team-attributed metrics): Team.name returns only that team's own deploys, so even the org-wide total must use the Team.groupPath DESCENDANT_OF roll-up anchored at the org root path — see "Deployment metrics across a team tree".

Common mistake — querying the wrong facade for "metric by X":

"PR count by repository" queried on the PullRequest facade returns one row per PR (each with count=1) — useless for aggregation. Query the Repository facade instead to get one row per repo with aggregated counts. Same applies to "by team" (use Team), "by person" (use Person). If "by X" refers to a dimension without its own facade (tenure, job level), use mode: "groups" instead.

Team Hierarchy: = vs DESCENDANT_OF vs IN

  • = — Use for metrics on a single team. Team.name = "Backend" returns that team's aggregated metrics. For most metrics this already rolls up the team's sub-teams (the Team facade aggregates hierarchically). Exception: deployment metrics are leaf-only — see "Deployment metrics across a team tree" below.
  • DESCENDANT_OF — Use for roster discovery (listing people), and for the deployment roll-up below. For most Team metric queries do NOT use it — Team.name = "X" already includes sub-team data, so it would double-count.
  • IN — Use to query a specific set of teams by name.

Most metrics across a team tree: Query Person (not Team) with DESCENDANT_OF on Person.Teams.name, then attach metrics to Person. Individual-level metrics don't double-count.

Deployment metrics across a team tree

Deployment metrics are attributed to teams, not people (a deploy has no author), which makes them behave differently from the rest:

  • The Person + DESCENDANT_OF pattern above does not apply — there's no person to attach them to.
  • Team.name = "X" returns only that team's own deploys; unlike other metrics it does not roll up sub-teams.

To total a team and all of its sub-teams, roll up over the team's subtree with Team.groupPath (metadata describes it: "Filter with DESCENDANT_OF to scope a metric to a team and all of its subteams"):

  1. Get the target team's path — { "select": ["Team.name", "Team.path"] } — and read its Team.path (an ltree, e.g. "<org-id>__organization.platform"). For the org-wide total, use the root team's path (the one with no .).
  2. Roll up in groups mode:
{
  "mode": "groups",
  "select": ["Team.groupPath"],
  "metrics": [{ "metricId": "<deploy metric id from metadata>" }],
  "filters": [{ "field": "Team.groupPath", "operator": "DESCENDANT_OF", "value": "<Team.path>" }],
  "timeDimension": { "timeRange": { "startTime": "...", "endTime": "..." } }
}

You get back one row — the subtree total, de-duplicated across teams (add granularity for a per-period series). That de-duplication is the reason to use the roll-up rather than fetching per-team rows and adding them up.

Complete working example — org-wide deployments for a date range (the value is the org root Team.path; for a single team + its sub-teams, use that team's Team.path instead):

{
  "mode": "groups",
  "select": ["Team.groupPath"],
  "metrics": [
    { "metricId": "c3377814-3805-4803-89ba-6f77bb3a8907", "responseKey": "deploys" }
  ],
  "filters": [
    {
      "field": "Team.groupPath",
      "operator": "DESCENDANT_OF",
      "value": "991ab8636a4c495e9febbd48509e480f__organization"
    }
  ],
  "timeDimension": {
    "timeRange": { "startTime": "2026-04-01", "endTime": "2026-04-11" }
  }
}

Returns { "data": [{ "deploys": <subtree total> }] }. Notes: the value is org-specific — it comes from Team.path (step 1), never hard-code it. The metricId shown is the "Deployments" total-count metric; confirm the current id from metadata (pick by id, not label — labels are shared across metric versions).

Three things to get right:

  • Pick the metric by id, not label — V1 and V2 share labels.
  • Team.groupPath is the subtree anchor only — it's filter-only and dropped from the output, so it can't double as a breakdown (doing so returns a 400).
  • One grain per query — a groups-mode query can't mix deploy (team) metrics with person metrics; run them as separate queries.

For additional patterns (top-level team discovery, manager lookups), see references/api-reference.md.

Step 3: Use Pre-Aggregated Metrics

When using granularity in the time dimension, the API returns already-aggregated values. Do not perform additional aggregation on these results.

Bad pattern (DO NOT DO THIS):

  1. Query individual PRs
  2. Extract cycle times
  3. Calculate average manually

Good pattern (DO THIS):

  1. Query Team with Team.name = "Organization"
  2. Include the cycle time metric
  3. Use granularity for time series
  4. Return the pre-aggregated results directly

When comparing time periods, always use the same asset, metric, and query path for both periods. Inconsistent query paths (e.g., different facades or aggregation levels) produce incomparable numbers.

Exception — trace corpus queries. TraceEvent and TraceTurn have no metrics and no server-side grouping, so "which skills are most used" has no pre-built metric to find. There, send "metrics": [], page the rows, and aggregate them yourself with jq. This is the only domain where client-side aggregation is correct.

Step 4: Plan Multi-Step Queries

For complex queries requiring multiple API calls, use TaskCreate to track steps:

Example: "Compare PR cycle time for top 5 teams over last 30 days"

  • Verify pr_cycle_time metric exists on Team asset
  • Fetch teams sorted by PR volume, limit 5
  • Get pr_cycle_time for each team with date filter
  • Format and present results

Execute independent API calls in parallel, but limit to 2 concurrent calls maximum.

Superlative queries ("most", "least", "highest", "top N"): ensure you paginate through the full dataset. A query with limit=25 may miss the actual top result if there are more than 25 rows.

Querying the API

Always use the query script. Write the query JSON to a temp file, then execute:

cat > /tmp/span-query.json << 'EOF'
{
  "select": ["Team.name"],
  "filters": [{"field": "Team.name", "operator": "=", "value": "Organization"}],
  "metrics": [{"metricId": "<metric-id-from-cache>", "responseKey": "cycleTime"}],
  "timeDimension": {
    "timeRange": {"startTime": "2024-05-01", "endTime": "2024-06-01"},
    "granularity": "week"
  }
}
EOF
$SKILL_SCRIPTS/query.sh /tmp/span-query.json

For paginated results, pass limit and cursor: $SKILL_SCRIPTS/query.sh /tmp/span-query.json 25 <cursor>

See references/api-reference.md for full request body format, filter operators, time dimensions, and groups mode.

Formatting Results

Always check annotations in the API response for each metric's unit. The annotations tell you exactly how to interpret the raw values.

Possible units: seconds, hours, days, usd, count, percentage_as_ratio, score, boolean, estimate.

Convert accordingly:

  • seconds → hours or days for display (e.g., 42483 seconds → "11.8 hours")
  • percentage_as_ratio → multiply by 100 for display (e.g., 0.85 → "85%")
  • score → display as-is (dimensionless score)
  • boolean → display as Yes/No
  • estimate → display as-is (story points or similar)

Error Handling

API Errors:

  • Metric unavailable on asset → "Metric X isn't available on Y. Available: [list]. Try [alternative]?"
  • Empty results → "No data for [filters]. Try expanding time range or removing filters."
  • Rate limits → Return partial results with note about limit reached

Ambiguous Queries (see "Clarifying Questions" section above):

  • Missing time range → Always ask. Suggest 30 days as default option.
  • Unclear entity → "Which team/service? Available: [list top options]"
  • Unknown metric → "Metric not found. Did you mean: [similar metrics]?"

Handle pagination if hasNextPage is true in the response.

Example Workflows by Domain

Productivity

  • "How long does it take to merge PRs?" → Query Team or Person with cycle time / time-to-merge metrics, weekly granularity
  • "Are we completing what we planned this sprint?" → Query Sprint with planning accuracy, velocity, story point metrics
  • "How is the new hire ramping up?" → Query Person filtered by name/email with PR throughput and review metrics over monthly granularity

DORA

  • "How often are we deploying?" → Query Team or Service with deployment frequency metric, weekly granularity
  • "What's our change failure rate?" → Query Service or Team with change failure rate metric (incidents / deployments)
  • "What's our mean time to recovery?" → Query Team with MTTR metric (check for avg, p50, p75, p90 variants)

Investment

  • "Where is engineering effort going?" → Query Team with FTE Days metric, filter by work type (features, maintenance, developer experience)
  • "How much effort is going into this epic?" → Query Epic filtered by name with FTE Days metric
  • "What are our active workstreams?" → Query Team with workstream-related metrics for effort breakdown

AI Transformation

  • "Who is using AI tools?" → Query Team or Person with AI adoption rate, active AI users metrics
  • "What percentage of code is AI-generated?" → Query Team or Person with AI code ratio metric
  • "How much are we spending on AI?" → Query Team with AI spend metrics (base + overage)

Calendar

  • "How much time are engineers in meetings?" → Query Team or Person with meeting hours, focus time metrics
  • "What's our maker time like?" → Query Team with focus time and fragmented time metrics

AI Traces

  • "Which skills do we actually use?" → TraceEvent corpus query (toolChannel = 'skill', no traceId), then group by toolName — and state whose sessions you could actually read
  • "Which agent tool calls are failing?" → TraceEvent corpus query with success = false
  • "Walk me through this session" → Trace.id → TraceTurn(traceId) → TraceEvent(traceId, turnIndex = N)
  • "How much are we running agents?" → trace metrics on Team/Person, or Trace in mode: "groups" by aiTraceTool

Administration

  • "What version is installed?" / "Check for updates" → Run $SKILL_SCRIPTS/check-version.sh
  • "Reload Span metadata" → Run $SKILL_SCRIPTS/fetch-metadata.sh
  • "Reconfigure Span skill" → Delete $SPAN_DIR/auth.json and re-run onboarding

For more detailed step-by-step walkthroughs, see references/workflows.md.

For domain-specific query guidance (investment views, DORA facades, PR time dimensions, AI tool patterns, agent-session traces, calendar caveats), see references/domains.md.

Reference Material

Keep looking

Skills are one crate of 628,853. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.