Receber por email
← Voltar para o catálogo

linear-fetch

This skill should be used when a user input contains a Linear issue reference (e.g., SOL-39 or linear.app/.../issue/<ID>) and the downstream agent needs the screenshots embedded in the issue as visual context.

14estrelas
Atualizado há 2 meses

Ver no GitHub ↗ Licença: NOASSERTION

Como adicionar

/plugin marketplace add jikig-ai/soleur

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/linear-fetch/svg)](https://www.skillteca.com.br/skills/linear-fetch)
#ai

Alerta por categoria

Receba novas skills de Design e Frontend toda segunda

1 email curto, só com as skills novas de Design e Frontend. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Linear Issue Image Context

This skill detects Linear issue references in caller input, fetches the issue body and recent comments, and streams the embedded screenshots directly into the active model conversation. The caller (typically /soleur:one-shot Step 0a or /soleur:brainstorm Phase 0.4) receives two return artifacts and is responsible for choosing the right one for each downstream consumer.

Caller Contract

This skill returns two artifacts. Mixing them up is the load-bearing failure mode the skill exists to prevent.

  1. Agent context — the full markdown blob (description + comment bodies) with Linear image content blocks streamed into the active conversation by mcp__linear-server__extract_images. Callers MUST NOT write this artifact to disk, a PR body, a commit message, or any file under knowledge-base/. The Linear image URLs inside it (uploads.linear.app/*) are signed bearer credentials that grant public-internet read access for their validity window.
  2. Persist-safe summary — the same text with every uploads.linear.app/* URL redacted to the literal token [linear-image: REDACTED]. Callers MUST use this variant for any persistence: writes to knowledge-base/, PR descriptions, commit messages, learning files, downstream subagent prompts.

The redaction primitive lives at [scripts/redact-linear-urls.sh](./scripts/redact-linear-urls.sh). It uses a case-insensitive hostname match ([Hh][Tt][Tt][Pp][Ss]?://[Uu][Pp][Ll][Oo][Aa][Dd][Ss]\.[Ll][Ii][Nn][Ee][Aa][Rr]\.[Aa][Pp][Pp]/) — DNS is case-insensitive and https://Uploads.Linear.App/... serves the same signed bearer credential as the lowercase form. The URL path uses a positive ASCII-only character class (RFC 3986 unreserved + reserved set), so Unicode separators (U+2028, U+2029, NBSP) and markdown/HTML terminators (< > " ' ) ]) all terminate the match cleanly. The full justification per character is inline in the script. The companion CI pii-grep job in .github/workflows/pr-quality-guards.yml mirrors the case-insensitive matching with grep -iE.

The repository is public on GitHub. A single leaked uploads.linear.app URL is a single-user incident brand-survival event per knowledge-base/project/specs/feat-linear-issue-image-context/spec.md.

Phases

Phase A — Reference detection

Scan the input string for substrings matching either:

  • The identifier shape [A-Z]{2,}-[0-9]+ (matches SOL-39, LIN-12, ENG-100, but also PR-123, HTTP-200, CVE-2024 — false positives are handled in Phase B, not at the regex level), OR
  • The URL shape linear\.app/[^/]+/issue/([A-Z]+-[0-9]+) — extract the captured identifier.

Uppercase each captured identifier and deduplicate (case-sensitive after uppercasing, exact-match on the number — SOL-39 and sol-39 collapse to one entry).

If more than 5 distinct identifiers match, use the AskUserQuestion tool to ask the user how to proceed before any MCP call:

  • Header: "Linear cap"
  • Question: "Found N Linear references; the default cap is 5 to bound prompt size and MCP cost. Process the first 5, all N, or abort?"
  • Options: "Process first 5 (default)", "Process all (may bloat context)", "Abort". The "Other" escape is appended automatically by the runtime.

Take the user's choice as the working set for Phase B.

If 5 or fewer matches, proceed directly.

If zero matches, return immediately with no disclosure line — the skill is a no-op for inputs that do not reference Linear.

Phase B — Fetch + comment scan

For each identifier in the working set:

  1. Invoke mcp__linear-server__get_issue(id=<identifier>). On a 404 Not Found response, silently drop the identifier — this converts false-positive matches like PR-123 or CVE-2024 into no-ops with no user-visible failure. On any other failure (403, network error, token expired), emit the warning from Phase E and skip to the next identifier; do not abort.
  2. Invoke mcp__linear-server__list_comments(issueId=<identifier>, orderBy="createdAt", limit=250). The MCP schema (verified at Phase 0 of the plan) supports orderBy: createdAt | updatedAt and limit up to 250. The schema does NOT expose an order-direction parameter, so the response order is treated as ambiguous: parse each comment's createdAt field and sort client-side descending, then take the first 10 entries as "10 most-recent by creation time." If the response contains 10 or fewer comments, skip the sort.
  3. Concatenate the issue description and the 10 most-recent comment bodies into a single markdown blob, delimited as \n\n--- comment by <author.displayName> on <createdAt> ---\n\n between each block.

Phase B.0 — Pre-fetch preflight (blob-independent; runs BEFORE Phase C)

Run this fence before invoking extract_images, and abort the whole skill on a non-zero exit.

Both checks below are blob-independent — they interrogate the plugin installation, not the issue. Until #7450 they sat in Phase D, after Phase C had already streamed the issue's image content blocks into the conversation. So on every halt path the signed uploads.linear.app bearer URLs were already in the parent transcript, and the "fail closed" refusal protected only the artifact nobody had produced yet. A guard that cannot run before the exposure is not a guard against it. Nothing here depends on the blob, so nothing forced that ordering.

[ -f "${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json" ] \
  && grep -q '"name"[[:space:]]*:[[:space:]]*"soleur"' "${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json" \
  || { echo "SOLEUR_LINEAR_FETCH_HALT reason=plugin-root-unverified root=[${CLAUDE_PLUGIN_ROOT}]"
       echo "linear-fetch: cannot verify the Soleur plugin installation — stopping before anything is fetched." >&2
       echo "  Resolved plugin root: [${CLAUDE_PLUGIN_ROOT}]" >&2
       echo "  If that is EMPTY: no Soleur plugin is loaded in this session. Install it and start a NEW session — re-running here resolves the same empty root." >&2
       echo "  If it names a path: that path is not a Soleur install (a repo checkout is not an install). Run 'claude plugin update soleur', then RESTART Claude Code — plugin changes apply only on restart. If you installed with --scope project or --scope local, pass the same scope. Reinstall only if that does not clear it." >&2
       echo "  Nothing has been fetched yet, so nothing has leaked." >&2
       exit 2; }
SCRUBBER="${CLAUDE_PLUGIN_ROOT}/skills/linear-fetch/scripts/redact-linear-urls.sh"
[[ -r "$SCRUBBER" ]] || { echo "SOLEUR_LINEAR_FETCH_HALT reason=scrubber-unreadable scrubber=[$SCRUBBER]"
       echo "linear-fetch: the redaction primitive is missing from an otherwise valid Soleur install — stopping." >&2
       echo "  Expected at: [$SCRUBBER]" >&2
       echo "  The install is partial or out of date. Run 'claude plugin update soleur', then RESTART Claude Code — plugin changes apply only on restart. If you installed with --scope project or --scope local, pass the same scope. Reinstall only if that does not clear it." >&2
       echo "  Nothing has been fetched yet, so nothing has leaked." >&2
       exit 2; }
echo "SOLEUR_LINEAR_FETCH_PREFLIGHT_OK scrubber=present"

If this fence exits non-zero, stop: do not invoke extract_images, do not emit agent_context, and do not emit a disclosure line. The correct operator-visible outcome is the halt message above, not a partially-fetched issue.

Phase C — Image passthrough

For each issue's combined markdown blob from Phase B, invoke mcp__linear-server__extract_images(markdown=<blob>). The MCP server resolves Linear-authenticated CDN URLs server-side and streams the underlying image bytes back into the active conversation as image content blocks. The skill never sees the bytes, never writes them to disk, never re-fetches them.

Count the image content blocks returned per issue (call this N_images_total), and count how many were extracted from comment bodies spec

Compartilhar X LinkedIn