⏳ This skill is pending AI review.

Scores will appear once the review pipeline completes.

version unknown

agent-transit

@israelvicars⭐ 2 stars

A transit map for your coding-agent sessions. Use when the user wants to visualize one Claude Code, Codex CLI, or Cursor coding-agent session as a subway map of tool calls.

Choose how to use this skill

You do not need every option. Choose the path your AI client supports. The stable page stays the same; versioned files are immutable.

1. Native installer

This listing has no registered native installer command. Use the complete package or source fallback below, depending on what your client supports.

Do not guess an installer command or replace an existing version without reviewing the diff.

2. Complete package recommended

Download the ZIP when available. It includes SKILL.md plus the references, security notes and version metadata.

No complete ProSkills package is published for this listing yet.

3. Prompt-only

Copy the prompt above when the agent can read the stable page or when you want to adopt the workflow without installing a skill.

Need only the instruction file?

Download SKILL.md only if your client requires a single file. The complete ZIP is safer for a full installation because it preserves the references and release context.

No path installs or executes anything by itself. Your agent still needs access to the project files. Before updating, compare the installed version and review the diff.

—/10

// RATINGS

⭐GitHub Stars
⭐ 2 on GitHubGitHub ↗

New / niche

🟢ProSkills Score
—
📍

Not yet listed on ClawHub or SkillsMP

// README

agent-transit

A transit map for your coding-agent sessions.

Animated Transit map of a coding-agent session: tool families are lines, tool calls are stations, with DELAY and SERVICE ALERT markers on slow and failed calls

Example prompts

  • “Make an MP4 of my last Claude Code session.”
  • “Draw my latest Codex session as a transit map. Which tool calls were slow?”
  • “Render my newest Cursor session as a GIF.”
  • “Map this saved session log and show me where the tool errors were.”

What it maps

Tool families become lines and tool calls become stations. Long calls receive DELAY markers, while errors or denials receive SERVICE ALERT markers.

Supported sources are Claude Code, Codex CLI, and Cursor (experimental). Session files and renders remain local.

Install

npx skills add israelvicars/transit --skill agent-transit

Privacy

Runs locally only, with no telemetry. By default it keeps only tool names, timing, status, target basenames, and the agent source.

Back to the Transit README

// HOW IT'S BUILT

// FILES

52 files shipped with this skill. Inline text is the catalog copy. The upstream link is the source repository.

  • skills/agent-transit/SKILL.md4.0 KB2d2af1ec928bupstream ↗
    ---
    name: agent-transit
    description: A transit map for your coding-agent sessions. Use when the user wants to visualize one Claude Code, Codex CLI, or Cursor coding-agent session as a subway map of tool calls.
    ---
    <!-- Generated from src/agent-transit/SKILL.md; do not edit -->
    
    
    # Agent Transit
    
    A local subway map for one AI coding-agent session: tool families are lines and tool calls are stations.
    
    🔒 Session logs can contain prompts, source code, command output, credentials, and other secrets. This skill is default-deny: it keeps only tool name, family, target basename, timing, status, and `agent.source`. Prompt text, assistant text, tool output, file contents, full commands, and arguments are dropped. `--show-args` is an explicit opt-in and includes only a truncated, redacted argument preview.
    
    ## Where things are
    
    `<skill>` means the folder containing this file. Run scripts from it by absolute path, e.g. `node <skill>/src/agent-transit/scripts/find-session.mjs --source codex`. The shared engine is at `<skill>/core/`.
    
    ## Steps
    
    1. Find the newest local session path without reading its contents: `node <skill>/src/agent-transit/scripts/find-session.mjs --source claude-code|codex|cursor`.
    2. Build one page to share: `node <skill>/core/build.mjs <session.jsonl> --format claude-code --share --title "Agent: one coding session, tool by tool"`.
       Without `--out` the file is named after the title and the first failed tool call, in `./transit-out/`, e.g. `transit-out/agent-shell-git-service-alert.html`.
       `--share` scrubs at the strict level, refuses `--no-scrub`, and opens the page paused on the first failed call with a one-line verdict above the map.
       Idle stretches with nothing running (e.g. model thinking time between tool calls) longer than 3 s are shortened to 3 s so the animation isn't dead air; the header says how many were shortened and the root span keeps the wall time as `transit.wall_ms`. Pass `--max-gap-ms N` to change the cap, or `--max-gap-ms 0` for true wall-clock timing.
    3. Share the HTML file: give the user its path and the verdict line the build printed, ready to paste with the file in Slack
       (desktop and iOS open it as the interactive page). Use the printed verdict as is. For example:
       > shell · git failed after 12.0 s: denied · SERVICE ALERT · 18 calls
       > `agent-shell-git-service-alert.html` opens on the denied call. Only tool names and timing are in it.
    4. Optional teaser (GIF + MP4) with the bundled renderer: `node <skill>/core/render.mjs <session.jsonl> --format claude-code --name agent`.
    5. Check it with the bundled verifier: `node <skill>/core/verify.mjs <session.jsonl> --format claude-code --name agent`.
    
    Use `--format codex` or `--format cursor` for those sources.
    
    Cursor: most Cursor installs keep chats only in `state.vscdb`, not as transcripts. Export one composer to a metadata-only JSONL (tool name, status, file basename or command word, timestamps; never message text, params or results), opened read-only:
    `node <skill>/src/agent-transit/scripts/export-cursor.mjs --list` (sessions for the current workspace; `--workspace DIR` or `--all-workspaces`), then `node <skill>/src/agent-transit/scripts/export-cursor.mjs --composer <id or prefix> --out ./transit-out/agent/cursor.cursor.jsonl`, and build that with `--format cursor`.
    Cursor builds since about Sep 2025 stamp each message bubble with `createdAt`, so calls start at real times; Cursor stores no tool end time, so each call ends at the next message (an upper bound). Older bubbles and `agent-transcripts` files have no timestamps: order only, with uniform synthetic durations. Keep session files local and never upload them. Synthetic fixtures are provided in `samples/`; no real home-directory logs belong in this repository.
    
    ## Rules
    
    - Never expose or copy prompts, code, command lines, outputs, or arguments by default.
    - Only use the bundled build/render/verify scripts; never hand-write a substitute renderer.
    - Do not upload a session or render unless the user explicitly asks.
    - Review errors, denials, slow calls, and subagent branches in the map without revealing their payloads.
    
  • skills/agent-transit/README.md1.1 KB62680a7335f6upstream ↗
    # agent-transit
    
    A transit map for your coding-agent sessions.
    
    ![Animated Transit map of a coding-agent session: tool families are lines, tool calls are stations, with DELAY and SERVICE ALERT markers on slow and failed calls](https://raw.githubusercontent.com/israelvicars/transit/main/docs/agent.gif)
    
    ## Example prompts
    
    - “Make an MP4 of my last Claude Code session.”
    - “Draw my latest Codex session as a transit map. Which tool calls were slow?”
    - “Render my newest Cursor session as a GIF.”
    - “Map this saved session log and show me where the tool errors were.”
    
    ## What it maps
    
    Tool families become lines and tool calls become stations. Long calls receive DELAY markers, while errors or denials receive SERVICE ALERT markers.
    
    Supported sources are Claude Code, Codex CLI, and Cursor (experimental). Session files and renders remain local.
    
    ## Install
    
    ```sh
    npx skills add israelvicars/transit --skill agent-transit
    ```
    
    ## Privacy
    
    Runs locally only, with no telemetry. By default it keeps only tool names, timing, status, target basenames, and the agent source.
    
    [Back to the Transit README](https://github.com/israelvicars/transit#readme)
    
  • skills/agent-transit/src/agent-transit/samples/README.md525 B7b371db3cfa5upstream ↗
    # Synthetic agent sessions
    
    These JSONL files are hand-built fixtures, not copied from any real `~/.claude`, `~/.codex`, or `~/.cursor` directory. Claude Code and Codex fixtures carry a synthetic marker; the Cursor fixture follows the native transcript shape and is synthetic by this README/test context. The payloads intentionally contain prompt, output, file-content, and command-argument canaries so tests prove they never reach HTML by default. Cursor timing is synthetic because the format has no timestamps or results.
    
  • skills/agent-transit/core/adapters.mjs1.1 KB9f1cdf5644e6upstream ↗
    import { normalize as normalizeRequest, detectFormat as detectRequestFormat } from '../src/request-transit/adapters/normalize.mjs';
    import { isGitHubActionsRun, normalizeGh } from '../src/ci-transit/adapters/normalize-gh.mjs';
    import { isAgentLog, normalizeAgent } from '../src/agent-transit/adapters/normalize-agent.mjs';
    
    const adapters = new Map([['gh-actions', normalizeGh], ['claude-code', normalizeAgent], ['codex', normalizeAgent], ['cursor', normalizeAgent]]);
    export function registerAdapter(format, adapter) { adapters.set(format, adapter); }
    export function pickAdapter(doc, format) {
      if (format === 'gh-actions' || isGitHubActionsRun(doc)) return { format: 'gh-actions', normalize: adapters.get('gh-actions') };
      if (format === 'claude-code' || format === 'codex' || format === 'cursor' || isAgentLog(doc)) {
        const selected = format || (doc && doc[0] && doc[0]._agentFormat) || 'claude-code';
        return { format: selected, normalize: adapters.get(selected) };
      }
      return { format: format || detectRequestFormat(doc), normalize: format && adapters.get(format) || normalizeRequest };
    }
    
  • skills/agent-transit/core/build.mjs11 KBfaa8e4ddd9a1upstream ↗
    // core/build.mjs: trace file (OTLP JSON, Datadog span JSON, Datadog Spans API v2, or normalized)
    // -> one self-contained transit.html (trace, fonts and code inlined; no network at view time).
    //
    // usage: node core/build.mjs <trace.json> [--share] [--out transit-out/transit.html] [--format otlp|datadog|datadog-v2|internal]
    //        [--trace-id ID] [--title "..."] [--subtitle "..."] [--slow-ms N] [--ms-per-sec N]
    //        [--target-seconds 9] [--redact key1,key2] [--no-default-redact]
    //        [--strict | --share] [--keep-uuids] [--keep-ci-titles] [--no-scrub]   (value scrubbing, see core/scrub.mjs)
    //        --share: strict scrubbing, refuses --no-scrub, opens paused on the first SERVICE ALERT with a verdict headline.
    //        Without --out the file is ./transit-out/<title>-<alert>-service-alert.html (see core/verdict.mjs), else
    //        ./transit-out/transit.html; an existing file is never overwritten (-2, -3, ... is added). --out is used as given.
    import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
    import { dirname, resolve, join } from 'node:path';
    import { fileURLToPath } from 'node:url';
    import { parseJsonSafe } from '../src/request-transit/adapters/normalize.mjs';
    import { pickAdapter } from './adapters.mjs';
    import { existsSync } from 'node:fs';
    import { matchTrace } from '../src/request-transit/contracts/match.mjs';
    import { resolveVocab } from './vocab.mjs';
    import { compressGaps, DEFAULT_MAX_GAP_MS } from './gaps.mjs';
    import { scrubTrace, scrubLevel } from './scrub.mjs';
    import { parseArgs, runCli, UserError } from './cli.mjs';
    import { verdict, defaultName } from './verdict.mjs';
    const DEFAULT_DIR = 'transit-out';
    export const DEFAULT_INDEX = '.transit-cache/contracts/index.json';
    
    const HERE = dirname(fileURLToPath(import.meta.url));
    // attribute keys dropped before anything is rendered (span attributes can carry PII)
    export const DEFAULT_REDACT = /(^|[._-])(user|email|phone|token|auth|authorization|password|passwd|secret|cookie|set-cookie|session|card|ssn|credentials?|jwt|bearer|api[_-]?key|apikey|private[_-]?key|access[_-]?key)([._-]|$)|^(client|user)\.(address|ip)$|^enduser\./i;
    
    export function redact(trace, keys = [], useDefault = true) {
      const set = new Set(keys);
      let n = 0;
      for (const s of trace.spans) {
        if (!s.attrs) continue;
        for (const k of Object.keys(s.attrs)) if (set.has(k) || (useDefault && DEFAULT_REDACT.test(k))) { delete s.attrs[k]; n++; }
      }
      trace.meta.redacted = n;
      return trace;
    }
    
    // A request trace from one service (e.g. telemetrygen, a monolith) would draw one station and no hops.
    // Then stations become operations: each non-root span's service is its span name, with a trailing
    // counter or id dropped ("okey-dokey-7" -> "okey-dokey") so N numbered children share one station.
    export function splitSingleService(trace) {
      const services = new Set(trace.spans.map((s) => s.service));
      if (services.size !== 1 || trace.spans.length < 2) return false;
      // calls to an uninstrumented peer (DB, AWS service, broker) already give the map a second station
      if (trace.spans.some((s) => s.peer && s.peer !== s.service)) return false;
      const svc = [...services][0];
      const op = (name) => String(name || 'span').replace(/[\s._:#/-]*(\d+|[0-9a-f]{8,})$/i, '').slice(0, 40) || String(name);
      const ops = new Set();
      for (const s of trace.spans) if (s.parentId) { s.attrs = Object.assign({}, s.attrs, { 'service.name': svc }); s.service = op(s.name); ops.add(s.service); }
      trace.meta.warnings.push(`single-service trace (${svc}): stations are operations (${ops.size} span name${ops.size === 1 ? '' : 's'})`);
      return true;
    }
    
    // JSON, or JSON Lines (agent sessions); anything else is a UserError with the reason, never a stack trace
    export function readInput(file) {
      let input;
      try { input = readFileSync(file, 'utf8'); }
      catch (e) { throw new UserError(e.code === 'ENOENT' ? `no such file: ${file}` : e.code === 'EISDIR' ? `${file} is a directory, expected a trace file` : `cannot read ${file}: ${e.code || e.message}`); }
      if (!input.trim()) throw new UserError(`${file} is empty; expected a trace export (JSON) or an agent session (JSON Lines)`);
      try { return parseJsonSafe(input); }
      catch (jsonErr) {
        const lines = input.split(/\r?\n/).map((l, i) => [l, i + 1]).filter(([l]) => l.trim());
        if (lines.length < 2) throw new UserError(`${file} is not valid JSON (${jsonErr.message.replace(/\s+/g, ' ').slice(0, 160)})`);
        return lines.map(([l, n]) => { try { return parseJsonSafe(l); } catch (e) { throw new UserError(`${file} is neither JSON nor JSON Lines: line ${n} does not parse (${e.message.replace(/\s+/g, ' ').slice(0, 160)})`); } });
      }
    }
    
    export function build(file, opt = {}) {
      const doc = readInput(file);
      if (doc === null || typeof doc !== 'object') throw new UserError(`${file} holds a JSON ${doc === null ? 'null' : typeof doc}, not a trace; see the supported formats in src/request-transit/SKILL.md`);
      let adapter, trace;
      try {
        adapter = pickAdapter(doc, opt.format);
        trace = adapter.normalize(doc, { format: adapter.format, traceId: opt['trace-id'], showArgs: !!opt['show-args'], maxSteps: opt['max-steps'] != null ? Number(opt['max-steps']) : undefined });
      } catch (e) {
        if (e instanceof UserError) throw e;
        throw new UserError(`could not read ${file} as a trace: ${e.message}`);
      }
      if (opt.share && (opt['no-scrub'] || opt.scrub === 'off')) throw new UserError('--share pages are meant to be dropped into Slack, so value scrubbing cannot be turned off: remove --no-scrub (or build without --share for local debugging)');
      redact(trace, opt.redact ? String(opt.redact).split(',') : [], !opt['no-default-redact']);
      scrubTrace(trace, scrubLevel(opt), adapter.format);
      const vizOpts = {};
      if (!['gh-actions', 'claude-code', 'codex', 'cursor'].includes(adapter.format)) splitSingleService(trace);
      // the verdict's "after N" is wall time, so keep the start/end times from before gap shortening
      const wallSpans = new Map(trace.spans.map((s) => [s.spanId, { start: s.start, duration: s.duration }]));
      // agent sessions and CI runs: shorten idle gaps (--max-gap-ms N, 0 or --no-compress-gaps to keep wall time)
      const family = adapter.format === 'gh-actions' ? 'ci' : ['claude-code', 'codex', 'cursor'].includes(adapter.format) ? 'agent' : null;
      if (family && !opt['no-compress-gaps']) {
        const gaps = compressGaps(trace, opt['max-gap-ms'] != null ? Number(opt['max-gap-ms']) : DEFAULT_MAX_GAP_MS[family]);
        if (gaps) {
          trace.meta.gaps = gaps;
          trace.meta.warnings.push(`shortened ${gaps.count} idle gap(s) longer than ${gaps.maxGapMs / 1000} s (${Math.round(gaps.removedMs / 1000)} s of wall time cut); pass --max-gap-ms 0 for wall time`);
          vizOpts.gapNote = `${gaps.count} idle gap${gaps.count === 1 ? '' : 's'} shortened, not wall-clock`;
        }
      }
      // a large CI run keeps only failed/cancelled steps as stations: say so on the map, not just in the console
      if (adapter.format === 'gh-actions' && trace.meta.warnings.some((w) => w.startsWith('large run:'))) vizOpts.gapNote = [vizOpts.gapNote, 'failed steps only'].filter(Boolean).join('  ·  ');
      if (opt.title) vizOpts.title = opt.title;
      if (opt.subtitle) vizOpts.subtitle = opt.subtitle;
      if (opt['slow-ms']) vizOpts.slowMs = Number(opt['slow-ms']);
      if (opt['ms-per-sec']) vizOpts.msPerSec = Number(opt['ms-per-sec']);
      if (opt['target-seconds']) vizOpts.targetSeconds = Number(opt['target-seconds']);
      if (adapter.format === 'gh-actions') vizOpts.ciStepsAsStations = true;
      vizOpts.vocab = resolveVocab(opt.vocab || (adapter.format === 'gh-actions' ? 'ci' : adapter.format && ['claude-code', 'codex', 'cursor'].includes(adapter.format) ? 'agent' : undefined));
      const v = verdict(trace, vizOpts.vocab, wallSpans);
      if (opt.share) vizOpts.share = { verdict: v.text, origin: v.origin, message: v.message };
      // contracts are opt-in: only an explicit --contracts [index.json] reads an index, and it never builds one
      let contracts = null;
      if (opt.contracts) {
        const ix = opt.contracts === true ? DEFAULT_INDEX : opt.contracts;
        if (!existsSync(ix)) throw new Error(`--contracts: no index at ${ix}. Build one first (ask the user): node src/request-transit/scripts/transit.mjs index --config transit.config.json`);
        contracts = matchTrace(trace, JSON.parse(readFileSync(ix, 'utf8')));
      }
      const js = `window.TRACE = ${JSON.stringify(trace)};\nwindow.VIZ_OPTS = ${JSON.stringify(vizOpts)};\nwindow.CONTRACTS = ${JSON.stringify(contracts)};`.replace(/<\//g, '<\\/');
      const face = (w) => `@font-face { font-family: "Transit Sans"; font-weight: ${w}; font-style: normal; src: url(data:font/woff;base64,${readFileSync(join(HERE, `fonts/arimo-${w}.woff`)).toString('base64')}) format("woff"); }`;
      const tpl = readFileSync(join(HERE, 'transit.template.html'), 'utf8');
      if (!tpl.includes('/*__TRACE_DATA__*/') || !tpl.includes('/*__FONTS__*/')) throw new Error('template placeholder missing');
      const html = tpl.replace('/*__FONTS__*/', () => face(400) + '\n  ' + face(700)).replace('/*__TRACE_DATA__*/', () => js);
      // default: ./transit-out/ (the same folder the render teaser uses); never overwrite an earlier default file
      let out = resolve(opt.out || join(DEFAULT_DIR, defaultName(opt.title, v)));
      if (!opt.out) for (let i = 2; existsSync(out); i++) out = resolve(DEFAULT_DIR, defaultName(opt.title, v).replace(/\.html$/, `-${i}.html`));
      mkdirSync(dirname(out), { recursive: true });
      writeFileSync(out, html);
      return { out, trace, contracts, verdict: v.text };
    }
    
    if (process.argv[1] && process.argv[1].endsWith('/build.mjs')) await runCli(async () => {
      await (await import('./setup.mjs')).ensureSetup({ need: [] });   // node version only; the HTML needs no deps
      const { opt, pos } = parseArgs(process.argv.slice(2));
      if (!pos[0]) throw new UserError('usage: node core/build.mjs <trace.json> [--share] [--out transit-out/transit.html] [--title ...] [--strict]');
      const { out, trace, contracts, verdict: vt } = build(pos[0], opt);
      for (const w of trace.meta.warnings) console.error('warning:', w);
      const sc = trace.meta.scrub;
      console.log(`wrote ${out} (${trace.spans.length} spans, ${trace.meta.format}${trace.meta.synthetic ? ', synthetic' : ''}, ${trace.meta.redacted} attrs redacted, ${sc.level === 'off' ? 'value scrubbing OFF' : `${sc.values} values scrubbed (${sc.level})`}${contracts ? `, contracts: ${Object.values(contracts.bySpan).filter((m) => m.state === 'matched' || m.state === 'fallback').length} matched / ${Object.values(contracts.bySpan).filter((m) => m.state === 'unmatched').length} unmatched / ${Object.values(contracts.bySpan).filter((m) => m.state === 'infra').length} infra` : ''})`);
      if (opt.share) console.log(`share: drop ${out} into Slack. Suggested message: "${vt}"`);
    });
    
  • skills/agent-transit/core/cli.mjs1.8 KB8ac47871be21upstream ↗
    // core/cli.mjs: argument parsing and error reporting shared by the entry scripts.
    // Flags that never take a value; every other --flag takes the next argument.
    export const BOOL_FLAGS = new Set(['strict', 'share', 'keep-uuids', 'keep-ci-titles']);
    // flags that take a value only when one follows (--contracts [index.json], --show-args)
    const OPTIONAL_VALUE = new Set(['contracts', 'show-args']);
    
    export function parseArgs(argv) {
      const opt = {}, pos = [];
      for (let i = 0; i < argv.length; i++) {
        const a = argv[i];
        if (!a.startsWith('--')) { pos.push(a); continue; }
        const eq = a.indexOf('=');
        if (eq > 2) { opt[a.slice(2, eq)] = a.slice(eq + 1); continue; }
        const k = a.slice(2), next = argv[i + 1];
        if (k.startsWith('no-') || BOOL_FLAGS.has(k)) opt[k] = true;
        else if (OPTIONAL_VALUE.has(k) && (next == null || next.startsWith('--'))) opt[k] = true;
        else if (next == null) throw new UserError(`--${k} needs a value`);
        else opt[k] = argv[++i];
      }
      return { opt, pos };
    }
    // back to argv form (for passing build options on to a child script)
    export const toArgs = (opt, skip = []) => Object.entries(opt).flatMap(([k, v]) => skip.includes(k) ? [] : v === true ? ['--' + k] : ['--' + k, String(v)]);
    
    // An error whose message is the whole story for the user: printed without a stack trace.
    export class UserError extends Error {}
    
    // Runs an async CLI body; any error prints as one "transit: ..." line (stack only with TRANSIT_DEBUG=1).
    export async function runCli(fn) {
      try { await fn(); }
      catch (e) {
        console.error(`transit: ${e && e.message ? e.message : e}`);
        if (process.env.TRANSIT_DEBUG === '1' && e && e.stack) console.error(e.stack);
        else if (!(e instanceof UserError)) console.error('  (set TRANSIT_DEBUG=1 for a stack trace)');
        process.exit(1);
      }
    }
    
  • skills/agent-transit/core/export.mjs5.3 KB3f25f9e69ae8upstream ↗
    // scripts/export.mjs: turn out/frames/*.png into an MP4, a single-palette GIF, a contact sheet,
    // and a loop check. Works on macOS, Linux and Windows (needs only node + ffmpeg, see core/setup.mjs).
    //
    // usage: node core/export.mjs [all|mp4|gif|sheet|loop] [--fps 24] [--frames out/frames] [--out out]
    //
    // Runs the article's ffmpeg commands with these fixes:
    //   - GIF is scaled to fit inside 1200x1080 (X's GIF limit is 1280x1080; the article's
    //     scale=1200:-1 made a 1200x1200 GIF for square input and also upscaled 1080 px sources)
    //   - contact sheet always shows 12 frames spread across the whole clip (the article's
    //     "every 12th frame" left 4 black cells on a 4 s / 24 fps loop) and adds -update 1,
    //     which ffmpeg 7 asks for when writing a single image
    //   - loop check also measures the seam: SSIM(frame N-1, frame 0) vs the median
    //     SSIM of neighbouring frames, so you get a number, not just a video to eyeball
    import { execFileSync } from 'node:child_process';
    import { readdirSync, mkdirSync, existsSync } from 'node:fs';
    import { join } from 'node:path';
    import { ffmpegBin, countFrames } from './tools.mjs';
    
    const argv = process.argv.slice(2);
    const what = argv[0] && !argv[0].startsWith('--') ? argv[0] : 'all';
    const opt = {};
    for (let i = 0; i < argv.length; i++) if (argv[i].startsWith('--')) opt[argv[i].slice(2)] = argv[++i];
    const FPS = Number(opt.fps) || 24;
    const FR = opt.frames || 'out/frames';
    const OUT = opt.out || 'out';
    mkdirSync(OUT, { recursive: true });
    
    if (!existsSync(FR)) { console.error(`no ${FR}: run render.mjs first`); process.exit(1); }
    const files = readdirSync(FR).filter((f) => /^\d{4}\.png$/.test(f)).sort();
    const N = files.length;
    if (!N) { console.error(`no frames in ${FR}`); process.exit(1); }
    const pattern = join(FR, '%04d.png');
    const ff = (args) => execFileSync(ffmpegBin(), ['-hide_banner', '-loglevel', 'error', '-y', ...args], { stdio: ['ignore', 'pipe', 'pipe'] });
    const probeFrames = countFrames;   // ffmpeg's own frame count; no ffprobe
    
    console.log(`${N} frames in ${FR} at ${FPS} fps = ${(N / FPS).toFixed(3)} s`);
    
    if (what === 'all' || what === 'mp4') {
      const f = join(OUT, 'anim.mp4');
      ff(['-framerate', String(FPS), '-i', pattern, '-c:v', 'libx264', '-profile:v', 'high', '-pix_fmt', 'yuv420p',
        '-crf', '18', '-movflags', '+faststart', f]);
      console.log(`mp4   ${f} (${probeFrames(f)} frames)`);
    }
    
    if (what === 'all' || what === 'gif') {
      const f = join(OUT, 'anim.gif');
      const vf = "scale='min(1200,iw)':'min(1080,ih)':force_original_aspect_ratio=decrease:flags=lanczos," +
        'split[a][b];[a]palettegen=stats_mode=diff[p];[b][p]paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle';
      ff(['-framerate', String(FPS), '-i', pattern, '-vf', vf, '-loop', '0', f]);
      console.log(`gif   ${f} (${probeFrames(f)} frames${N > 350 ? ', WARNING: X caps GIFs at 350 frames' : ''})`);
    }
    
    if (what === 'all' || what === 'sheet') {
      const f = join(OUT, 'contact.png');
      const step = Math.max(1, Math.floor(N / 12));
      const vf = `select='not(mod(n\\,${step}))*lt(n\\,${step * 12})',scale=400:-1,tile=4x3:padding=4:color=white`;
      ff(['-framerate', String(FPS), '-i', pattern, '-vf', vf, '-frames:v', '1', '-update', '1', f]);
      console.log(`sheet ${f} (frames ${Array.from({ length: Math.min(12, N) }, (_, i) => i * step).join(', ')}; left to right, top to bottom)`);
    }
    
    if (what === 'all' || what === 'loop') {
      const mp4 = join(OUT, 'anim.mp4');
      if (existsSync(mp4)) {
        const f = join(OUT, 'loop_check.mp4');
        ff(['-stream_loop', '2', '-i', mp4, '-c', 'copy', f]);
        console.log(`loop  ${f} (3 plays back to back, watch the seam)`);
      }
      // SSIM between each frame and the next (N-1 values), then the seam: frame N-1 vs frame 0.
      const ssim = (args) => execFileSync(ffmpegBin(), ['-hide_banner', '-loglevel', 'error', ...args,
        '-lavfi', 'ssim=stats_file=-', '-f', 'null', '-'], { maxBuffer: 1 << 26 }).toString()
        .trim().split('\n').map((l) => Number((l.match(/All:([\d.]+)/) || [])[1])).filter((x) => !Number.isNaN(x));
      const inner0 = N > 1 ? ssim(['-framerate', String(FPS), '-i', pattern,
        '-framerate', String(FPS), '-start_number', '1', '-i', pattern]) : [];
      const seam0 = ssim(['-i', join(FR, files[N - 1]), '-i', join(FR, files[0])]);
      const s = [...inner0.slice(0, N - 1), ...seam0];
      if (s.length === N) {
        const seam = s[N - 1], before = s[N - 2], after = s[0];
        const inner = s.slice(0, N - 1).sort((x, y) => x - y), median = inner[Math.floor(inner.length / 2)];
        console.log(`seam  SSIM(frame ${N - 1} -> 0) = ${seam.toFixed(4)}; steps around it: ${N - 2}->${N - 1} ${before.toFixed(4)}, 0->1 ${after.toFixed(4)}; clip median ${median.toFixed(4)}`);
        // Heuristic: a pop at the seam shows up as a much bigger change than the steps on either side.
        const jump = 1 - seam, local = Math.max(1 - before, 1 - after);
        if (seam > 0.99999) console.log('seam  WARNING: frame N-1 is identical to frame 0, you probably rendered frame N (first frame shown twice)');
        else if (jump > 3 * local + 0.002) console.log(`seam  CHECK: the cut from frame ${N - 1} to 0 is a bigger change than its neighbours, look at frames ${N - 2}, ${N - 1}, 0, 1`);
        else console.log('seam  OK: the last frame flows into frame 0 like any other step');
      } else {
        console.log(`seam  could not parse SSIM output (${s.length} values for ${N} frames)`);
      }
    }
    
  • skills/agent-transit/core/fonts/arimo-400.woff12 KBbfb191123ba4upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/core/fonts/arimo-700.woff12 KB71453f041905upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/core/fonts/OFL-Arimo.txt4.3 KBd610cd5937b2upstream ↗
    Copyright 2026 The Arimo Project Authors (https://github.com/googlefonts/arimo)
    
    This Font Software is licensed under the SIL Open Font License, Version 1.1.
    This license is copied below, and is also available with a FAQ at:
    https://openfontlicense.org
    
    
    -----------------------------------------------------------
    SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
    -----------------------------------------------------------
    
    PREAMBLE
    The goals of the Open Font License (OFL) are to stimulate worldwide
    development of collaborative font projects, to support the font creation
    efforts of academic and linguistic communities, and to provide a free and
    open framework in which fonts may be shared and improved in partnership
    with others.
    
    The OFL allows the licensed fonts to be used, studied, modified and
    redistributed freely as long as they are not sold by themselves. The
    fonts, including any derivative works, can be bundled, embedded, 
    redistributed and/or sold with any software provided that any reserved
    names are not used by derivative works. The fonts and derivatives,
    however, cannot be released under any other type of license. The
    requirement for fonts to remain under this license does not apply
    to any document created using the fonts or their derivatives.
    
    DEFINITIONS
    "Font Software" refers to the set of files released by the Copyright
    Holder(s) under this license and clearly marked as such. This may
    include source files, build scripts and documentation.
    
    "Reserved Font Name" refers to any names specified as such after the
    copyright statement(s).
    
    "Original Version" refers to the collection of Font Software components as
    distributed by the Copyright Holder(s).
    
    "Modified Version" refers to any derivative made by adding to, deleting,
    or substituting -- in part or in whole -- any of the components of the
    Original Version, by changing formats or by porting the Font Software to a
    new environment.
    
    "Author" refers to any designer, engineer, programmer, technical
    writer or other person who contributed to the Font Software.
    
    PERMISSION & CONDITIONS
    Permission is hereby granted, free of charge, to any person obtaining
    a copy of the Font Software, to use, study, copy, merge, embed, modify,
    redistribute, and sell modified and unmodified copies of the Font
    Software, subject to the following conditions:
    
    1) Neither the Font Software nor any of its individual components,
    in Original or Modified Versions, may be sold by itself.
    
    2) Original or Modified Versions of the Font Software may be bundled,
    redistributed and/or sold with any software, provided that each copy
    contains the above copyright notice and this license. These can be
    included either as stand-alone text files, human-readable headers or
    in the appropriate machine-readable metadata fields within text or
    binary files as long as those fields can be easily viewed by the user.
    
    3) No Modified Version of the Font Software may use the Reserved Font
    Name(s) unless explicit written permission is granted by the corresponding
    Copyright Holder. This restriction only applies to the primary font name as
    presented to the users.
    
    4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
    Software shall not be used to promote, endorse or advertise any
    Modified Version, except to acknowledge the contribution(s) of the
    Copyright Holder(s) and the Author(s) or with their explicit written
    permission.
    
    5) The Font Software, modified or unmodified, in part or in whole,
    must be distributed entirely under this license, and must not be
    distributed under any other license. The requirement for fonts to
    remain under this license does not apply to any document created
    using the Font Software.
    
    TERMINATION
    This license becomes null and void if any of the above conditions are
    not met.
    
    DISCLAIMER
    THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
    OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
    COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
    DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
    OTHER DEALINGS IN THE FONT SOFTWARE.
    
  • skills/agent-transit/core/fsguard.mjs2.9 KB7111fc2d2e91upstream ↗
    // core/fsguard.mjs: the only way Transit clears output. No recursive deletes: a frames folder is emptied
    // by removing just the files Transit writes there (NNNN.png and the .transit-frames marker), then the folder
    // itself if that left it empty. Filesystem roots, the home directory, the repo root, the working directory
    // and any of their ancestors are refused outright, whatever --frames / --out / --out-dir say.
    import { existsSync, lstatSync, readdirSync, rmdirSync, unlinkSync, writeFileSync, mkdirSync, realpathSync } from 'node:fs';
    import { homedir } from 'node:os';
    import { join, parse, resolve, relative, isAbsolute, sep } from 'node:path';
    import { ROOT } from './tools.mjs';
    import { UserError } from './cli.mjs';
    
    export const FRAME_FILE = /^\d{4}\.png$/;
    export const FRAMES_MARKER = '.transit-frames';
    const real = (p) => { try { return realpathSync(p); } catch { return resolve(p); } };
    const isInside = (child, parent) => { const r = relative(parent, child); return r === '' || (!r.startsWith('..') && !isAbsolute(r)); };
    
    // throws when `dir` is a place no Transit output folder may be
    export function assertSafeOutputDir(dir, what = 'output folder') {
      const d = real(resolve(dir));
      const protectedDirs = [parse(d).root, homedir(), ROOT, process.cwd()].map(real);
      for (const p of protectedDirs) if (isInside(p, d)) throw new UserError(`refusing to use ${dir} as the ${what}: it is ${p === d ? '' : 'an ancestor of '}${p === parse(d).root ? 'the filesystem root' : p === real(homedir()) ? 'your home directory' : p === real(ROOT) ? 'the Transit repo root' : 'the current directory'}. Pick a dedicated folder, e.g. out/frames.`);
      return d;
    }
    
    // empties a frames folder of Transit's own files only; returns the number of files removed
    export function clearFrames(dir) {
      const d = assertSafeOutputDir(dir, 'frames folder');
      if (!existsSync(d)) return 0;
      if (!lstatSync(d).isDirectory()) throw new UserError(`${dir} exists and is not a folder`);
      let n = 0;
      for (const f of readdirSync(d)) if (FRAME_FILE.test(f) || f === FRAMES_MARKER) { const p = join(d, f); if (lstatSync(p).isFile()) { unlinkSync(p); n++; } }
      return n;
    }
    // clears, then removes the folder only if nothing else is left in it
    export function removeFramesDir(dir) {
      const n = clearFrames(dir);
      const d = resolve(dir);
      if (existsSync(d) && readdirSync(d).length === 0) rmdirSync(d);
      return n;
    }
    export function prepareFramesDir(dir) {
      const d = assertSafeOutputDir(dir, 'frames folder');
      mkdirSync(d, { recursive: true });
      writeFileSync(join(d, FRAMES_MARKER), 'frames written by Transit; safe to delete\n');
      return d;
    }
    // a --name becomes a folder under out/: one path segment, no separators or dot-dot
    export function safeName(name) {
      if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(name) || name.includes('..') || name.includes(sep)) throw new UserError(`--name must be a simple folder name (letters, digits, . _ -), got ${JSON.stringify(name)}`);
      return name;
    }
    
  • skills/agent-transit/core/gaps.mjs1.8 KB46312ada9c48upstream ↗
    // core/gaps.mjs: shorten idle stretches (no non-root span running) so long agent sessions and CI runs
    // don't animate as dead air. The time map is monotone, so order and overlaps are kept; only idle time
    // between activity is squeezed down to maxGapMs. Not applied to request traces.
    export const DEFAULT_MAX_GAP_MS = { agent: 3000, ci: 20000 };
    const r3 = (x) => Math.round(x * 1000) / 1000;
    
    export function compressGaps(trace, maxGapMs) {
      if (!(maxGapMs > 0)) return null;
      const spans = trace.spans;
      const root = spans.find((s) => !s.parentId);
      const iv = spans.filter((s) => s !== root).map((s) => [s.start, s.start + s.duration]).sort((a, b) => a[0] - b[0]);
      if (!iv.length) return null;
      const merged = [];
      for (const [a, b] of iv) { const m = merged[merged.length - 1]; if (m && a <= m[1]) m[1] = Math.max(m[1], b); else merged.push([a, b]); }
      const end = Math.max(...spans.map((s) => s.start + s.duration));
      const idle = [];
      let prev = Math.min(0, ...spans.map((s) => s.start));
      for (const [a, b] of merged) { if (a - prev > maxGapMs) idle.push([prev, a]); prev = Math.max(prev, b); }
      if (end - prev > maxGapMs) idle.push([prev, end]);
      if (!idle.length) return null;
      const map = (t) => {
        let cut = 0;
        for (const [a, b] of idle) {
          if (t >= b) { cut += (b - a) - maxGapMs; continue; }
          if (t > a) return r3(a - cut + (t - a) * maxGapMs / (b - a));
          break;
        }
        return r3(t - cut);
      };
      const wall = end;
      for (const s of spans) { const a = map(s.start), b = map(s.start + s.duration); s.start = a; s.duration = r3(b - a); }
      const removedMs = r3(idle.reduce((n, [a, b]) => n + (b - a) - maxGapMs, 0));
      if (root) root.attrs = Object.assign({}, root.attrs, { 'transit.wall_ms': r3(wall), 'transit.idle_gaps_shortened': idle.length });
      return { count: idle.length, maxGapMs, removedMs };
    }
    
  • skills/agent-transit/core/render-frames.mjs4.5 KB5b393d272495upstream ↗
    // render.mjs: freeze an HTML animation at t = i / fps and save every frame as a PNG.
    // Based on the renderer in Florence's "Opus 5.5 Motion Lab" article, with fixes:
    //   - viewport comes from --width/--height, else window.STAGE, else the page's first <canvas>
    //     (the article hardcoded 1200x675 and silently cropped anything else)
    //   - the page's requestAnimationFrame loop is frozen during capture, so a preview loop
    //     cannot overwrite the frame between renderAt(t) and the screenshot (the article's version
    //     produced different PNGs on repeat runs when the page had a rAF loop)
    //   - old NNNN.png frames are removed first (only those files, never the folder's other contents),
    //     so stale frames from a longer earlier render never end up in the MP4/GIF
    //   - --frames renders only named stills (for the review loop), --scale renders at 2x
    //
    // usage:
    //   node render.mjs <file.html> [seconds=6] [fps=24] [options]
    //   node render.mjs anim.html 4 24
    //   node render.mjs anim.html 4 24 --width 1080 --height 1350
    //   node render.mjs anim.html 4 24 --frames 0,24,48,72 --out out/stills
    // options:
    //   --width N --height N   viewport in CSS px (default: window.STAGE, then <canvas>, then 1200x675)
    //   --scale N              deviceScaleFactor, e.g. 2 for crisp text (PNG is W*N x H*N)
    //   --frames a,b,c         only these frame numbers (no folder wipe, existing files are overwritten)
    //   --out DIR              output folder (default out/frames)
    import { launchBrowser } from './tools.mjs';
    import { pathToFileURL } from 'node:url';
    import { clearFrames, prepareFramesDir } from './fsguard.mjs';
    import { resolve, join } from 'node:path';
    
    const pos = [], opt = {};
    const argv = process.argv.slice(2);
    for (let i = 0; i < argv.length; i++) {
      if (argv[i].startsWith('--')) opt[argv[i].slice(2)] = argv[++i];
      else pos.push(argv[i]);
    }
    const [file = 'anim.html', secs = '6', fps = '24'] = pos;
    const FPS = Number(fps), N = Math.round(Number(secs) * FPS);   // frames 0 to N-1, never N
    const OUT = opt.out || 'out/frames';
    const SCALE = Number(opt.scale) || 1;
    const only = opt.frames ? opt.frames.split(',').map(Number) : null;
    if (!(FPS > 0) || !(N > 0)) { console.error('seconds and fps must be positive numbers'); process.exit(1); }
    
    // only Transit's own frame files are removed (core/fsguard.mjs); unsafe folders are refused
    try { if (!only) clearFrames(OUT); prepareFramesDir(OUT); }
    catch (e) { console.error(`transit: ${e.message}`); process.exit(1); }
    
    const browser = await launchBrowser();
    const context = await browser.newContext({ viewport: { width: 1200, height: 675 }, deviceScaleFactor: SCALE });
    // Runs before any page script: tell the page it is being rendered, and wrap rAF so it can be frozen.
    await context.addInitScript(() => {
      window.__RENDER_MODE = true;
      const raf = window.requestAnimationFrame.bind(window);
      window.__origRAF = raf;
      window.requestAnimationFrame = (cb) => (window.__RENDER_FREEZE ? 0 : raf(cb));
    });
    const page = await context.newPage();
    await page.goto(pathToFileURL(resolve(file)).href, { waitUntil: 'networkidle' });
    await page.evaluate(async () => { await document.fonts.ready; });
    
    // Size: CLI args win, then window.STAGE {width,height}, then the first canvas.
    let width = Number(opt.width), height = Number(opt.height);
    if (!width || !height) {
      const detected = await page.evaluate(() => {
        if (window.STAGE && window.STAGE.width) return { width: window.STAGE.width, height: window.STAGE.height };
        const c = document.querySelector('canvas');
        if (!c) return null;
        const r = c.getBoundingClientRect();
        return { width: Math.round(r.width) || c.width, height: Math.round(r.height) || c.height };
      });
      width = width || detected?.width || 1200;
      height = height || detected?.height || 675;
    }
    await page.setViewportSize({ width, height });
    
    // Freeze the page's own animation loop, then let any in-flight frame finish.
    await page.evaluate(() => new Promise((done) => {
      window.__RENDER_FREEZE = true;
      window.__origRAF(() => window.__origRAF(done));
    }));
    
    const list = only || Array.from({ length: N }, (_, i) => i);
    for (const i of list) {
      await page.evaluate((t) => {
        if (window.renderAt) window.renderAt(t);
        document.getAnimations().forEach((a) => { a.pause(); a.currentTime = t * 1000; });
      }, i / FPS);
      await page.screenshot({ path: join(OUT, `${String(i).padStart(4, '0')}.png`) });
    }
    await browser.close();
    console.log(`rendered ${list.length} frame(s) at ${width}x${height}${SCALE !== 1 ? ` @${SCALE}x` : ''} to ${OUT}` +
      (only ? '' : ` (frames 0..${N - 1} = ${secs} s x ${FPS} fps)`));
    
  • skills/agent-transit/core/render.mjs2.3 KBd71bccf606b9upstream ↗
    // core/render.mjs: trace file -> out/<name>/{transit.html, frames/, anim.mp4, anim.gif, contact.png}
    // Frame-exact: the page's rAF loop is frozen and every frame is renderAt(i / fps).
    // usage: node core/render.mjs <trace.json> [--name NAME | --out-dir DIR] [--frames DIR] [build options...]
    //   build options include --strict / --share, --keep-uuids, --keep-ci-titles, --no-scrub (see core/scrub.mjs)
    import { execFileSync } from 'node:child_process';
    import { ensureSetup } from './setup.mjs';
    import { pathToFileURL, fileURLToPath } from 'node:url';
    import { dirname, join, resolve } from 'node:path';
    import { build } from './build.mjs';
    import { parseArgs, runCli, UserError } from './cli.mjs';
    import { assertSafeOutputDir, safeName } from './fsguard.mjs';
    
    await runCli(async () => {
      const { opt, pos } = parseArgs(process.argv.slice(2));
      if (!pos[0]) throw new UserError('usage: node core/render.mjs <trace.json> [--name NAME | --out-dir DIR] [--strict]');
      const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
      const name = opt['out-dir'] ? String(opt.name || 'trace') : safeName(String(opt.name || 'trace'));
      const dir = assertSafeOutputDir(resolve(opt['out-dir'] || join(ROOT, 'out', name)), 'output folder');
      const frames = resolve(opt.frames || join(dir, 'frames'));
      assertSafeOutputDir(frames, 'frames folder');
      // build first: a bad input file fails here with a one-line reason, before any setup or download
      const { out: html } = build(resolve(pos[0]), { ...opt, out: join(dir, 'transit.html') });
      await ensureSetup({ need: ['deps', 'browser', 'ffmpeg'] });
      const { launchBrowser } = await import('./tools.mjs');
      const browser = await launchBrowser();
      const page = await browser.newPage();
      await page.goto(pathToFileURL(html).href + '?paused=1');
      const { seconds, fps, msPerSec } = await page.evaluate(() => window.VIZ);
      await browser.close();
      console.log(`${name}: ${seconds.toFixed(3)} s at ${fps} fps, 1 s of video = ${msPerSec} ms of trace`);
      const run = (args) => execFileSync(process.execPath, args, { cwd: ROOT, stdio: 'inherit' });   // arg arrays, no shell
      run(['core/render-frames.mjs', html, String(seconds), String(fps), '--out', frames]);
      if (opt['frames-only']) return;
      for (const k of ['mp4', 'gif', 'sheet']) run(['core/export.mjs', k, '--fps', String(fps), '--frames', frames, '--out', dir]);
    });
    
  • skills/agent-transit/core/scrub.mjs8.7 KB5d851fa1230fupstream ↗
    // core/scrub.mjs: value-based scrubbing, run on every trace before anything is rendered.
    // Key-based redaction (build.mjs DEFAULT_REDACT) drops whole attributes by name; this pass rewrites the
    // strings that are always drawn or inlined: service and span names, peers, operations, status messages,
    // attribute values, the source note and warnings. The default level is meant to be safe to share.
    //
    // levels (build/render flags):
    //   default         emails, bearer/basic credentials, JWTs, known token prefixes (GitHub, Slack, AWS keys,
    //                   Stripe, OpenAI, GitLab, npm, PostHog...), key=value secrets (password=, token:, api_key=...),
    //                   URL userinfo and query strings, long hex (>= 32) and long base64-like runs, UUIDs,
    //                   home directories (/Users/<name>, /home/<name>, C:\Users\<name>), AWS account ids in ARNs;
    //                   CI runs lose the PR/commit title and the branch name
    //   --strict        (alias --share) also IPv4/IPv6 addresses, internal host names (.internal .local .lan .corp
    //                   .intranet .svc .cluster.local and hosts inside URLs), long numeric ids (>= 6 digits), and keeps
    //                   only a minimal attribute allowlist
    //   --keep-uuids    keep UUIDs (default level only)      --keep-ci-titles   keep CI run titles and branch names
    //   --no-scrub      off (local debugging only; the page is NOT safe to share)
    // Placeholders are bracketed, e.g. [email], so a reader can see something was removed.
    
    const KEYS = 'password|passwd|pwd|pass|secret|client[_-]?secret|token|access[_-]?token|refresh[_-]?token|id[_-]?token|api[_-]?key|apikey|x-api-key|access[_-]?key|private[_-]?key|auth|authorization|credentials?|session(?:[_-]?id)?|sid|sig|signature|key|cookie';
    // base64/base64url secrets vs kebab-case names and paths: a secret has one long unbroken run (>= 24) that mixes
    // upper, lower and digits with high character entropy; "feature/jira-1234-big-customer" has only short words
    const entropy = (x) => { const f = {}; for (const ch of x) f[ch] = (f[ch] || 0) + 1; return Object.values(f).reduce((h, c) => h - (c / x.length) * Math.log2(c / x.length), 0); };
    function looksSecret(m) {
      const run = m.split(/[-_/+.=]/).reduce((a, b) => (b.length > a.length ? b : a), '');
      return run.length >= 24 && /[a-z]/.test(run) && /[A-Z]/.test(run) && /\d/.test(run) && entropy(run) >= 4;
    }
    // key=value always counts; with a colon ("session: tool calls" is prose) only a strong secret key or a value that looks
    // like a secret (has a digit or symbol, or is a long unbroken run) is redacted
    const STRONG = /^(password|passwd|pwd|secret|client[_-]?secret|api[_-]?key|apikey|x-api-key|access[_-]?key|private[_-]?key|access[_-]?token|refresh[_-]?token|id[_-]?token|authorization|credentials?)$/i;
    function kvReplace(m, k, sep, v) {
      if (/^\[[a-z-]+\]$/.test(v) || /^(bearer|basic|token)$/i.test(v)) return m;
      const bare = v.replace(/^["']|["']$/g, '');
      if (sep.includes(':') && !STRONG.test(k) && !/[\d!@#$%^&*+/=_~-]/.test(bare) && bare.length < 16) return m;
      return `${k}${sep}[redacted]`;
    }
    const RULES = [
      // URL userinfo first, then query strings, so the URL rules see whole URLs
      ['userinfo', /\b([a-z][a-z0-9+.-]*:\/\/)[^\s/@:]+(?::[^\s/@]*)?@/gi, '$1[credentials]@'],
      ['query', /((?:\b[a-z][a-z0-9+.-]*:\/\/[^\s?#"'<>]+|(?:^|(?<=[\s"'(=]))\/[^\s?#"'<>]*))\?[^\s#"'<>]+/gi, '$1?[query]'],
      ['jwt', /\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{5,}/g, '[jwt]'],
      ['bearer', /\b(bearer|basic|token)\s+[A-Za-z0-9._~+/=-]{8,}/gi, '$1 [token]'],
      ['token', /\b(?:gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|xox[abprs]-[A-Za-z0-9-]{10,}|(?:sk|pk|rk)_(?:live|test)_[A-Za-z0-9]{10,}|sk-[A-Za-z0-9_-]{20,}|glpat-[A-Za-z0-9_-]{20,}|npm_[A-Za-z0-9]{30,}|ph[cxs]_[A-Za-z0-9]{20,}|(?:AKIA|ASIA)[A-Z0-9]{16}|AIza[A-Za-z0-9_-]{30,})\b/g, '[token]'],
      ['kv', new RegExp(`\\b(${KEYS})(\\s*[=:]\\s*)("[^"]*"|'[^']*'|[^\\s&,;)"'\\]}]+)`, 'gi'), kvReplace],
      ['email', /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g, '[email]'],
      ['arn', /(\barn:aws[a-z-]*:[a-z0-9-]*:[a-z0-9-]*:)\d{12}(?=:)/g, '$1[account]'],
      ['home', /(\/Users\/|\/home\/|[A-Za-z]:\\Users\\)[^\s/\\:"']+/g, (m, p) => (p.includes('\\') ? 'C:\\Users\\[user]' : `${p}[user]`)],
      ['uuid', /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi, '[uuid]', 'uuids'],
      ['hex', /\b[0-9a-f]{32,}\b/gi, '[hex]'],
      ['b64', /(?<![A-Za-z0-9+/_-])[A-Za-z0-9+/_-]{32,}={0,2}(?![A-Za-z0-9+/_-])/g, (m) => (looksSecret(m) ? '[secret]' : m)],
    ];
    const STRICT_RULES = [
      ['ipv4', /\b(?:25[0-5]|2[0-4]\d|1?\d?\d)(?:\.(?:25[0-5]|2[0-4]\d|1?\d?\d)){3}\b/g, '[ip]'],
      // full 8-group form, or a compressed form with "::" and at least one group (clock times like 10:30:00 never match)
      ['ipv6', /(?<![\w:])(?:(?:[0-9a-f]{1,4}:){7}[0-9a-f]{1,4}|(?:[0-9a-f]{1,4}(?::[0-9a-f]{1,4})*)?::(?:[0-9a-f]{1,4}(?::[0-9a-f]{1,4})*)?)(?![\w:])/gi, (m) => (/[0-9a-f]/i.test(m) ? '[ip]' : m)],
      ['url-host', /\b([a-z][a-z0-9+.-]*:\/\/)(?!\[)[^\s/?#"'<>]+/gi, '$1[host]'],
      ['internal-host', /\b[a-z0-9-]+(?:\.[a-z0-9-]+)*\.(?:internal|local|lan|corp|intranet|svc|cluster\.local|home\.arpa)\b/gi, '[internal-host]'],
      ['long-id', /(?<![\w.])\d{6,}(?![\w.])/g, '[id]'],
    ];
    // attributes kept in --strict (values are still scrubbed); ci.step.* and ci.needs.from are structural: CI steps are stations only with them
    const STRICT_ATTRS = /^(http\.(request\.method|method|response\.status_code|status_code|route)|rpc\.(system|service|method|grpc\.status_code)|db\.system|messaging\.(system|operation)|ci\.(job|conclusion|needs|needs\.from|attempt|event|step\.number|step\.name)|agent\.source|component)$/;
    
    export function scrubLevel(opt = {}) {
      if (opt['no-scrub'] || opt.scrub === 'off') return { level: 'off' };
      const strict = !!(opt.strict || opt.share || opt.scrub === 'strict');
      return { level: strict ? 'strict' : 'default', uuids: strict || !opt['keep-uuids'], ciTitles: !opt['keep-ci-titles'] };
    }
    
    export function scrubString(value, cfg = { level: 'default', uuids: true }, counts = {}) {
      if (typeof value !== 'string' || !value || cfg.level === 'off') return value;
      let s = value;
      for (const [name, re, rep, flag] of cfg.level === 'strict' ? [...RULES, ...STRICT_RULES] : RULES) {
        if (flag && !cfg[flag]) continue;
        s = s.replace(re, (...m) => {
          const out = typeof rep === 'function' ? rep(...m) : rep.replace(/\$(\d)/g, (_, i) => (typeof m[i] === 'string' ? m[i] : ''));
          if (out !== m[0]) counts[name] = (counts[name] || 0) + 1;
          return out;
        });
      }
      return s;
    }
    
    const scrubValue = (v, cfg, counts) => typeof v === 'string' ? scrubString(v, cfg, counts) : Array.isArray(v) ? v.map((x) => scrubValue(x, cfg, counts)) : v;
    
    // rewrites the trace in place; returns {level, values} (values = number of replacements)
    export function scrubTrace(trace, cfg = scrubLevel(), format) {
      if (cfg.level === 'off') { trace.meta.scrub = { level: 'off', values: 0 }; trace.meta.warnings.push('value scrubbing is off (--no-scrub): names, messages and URLs are unredacted; do not share this page'); return trace.meta.scrub; }
      const counts = {};
      const str = (v) => scrubString(v, cfg, counts);
      if (format === 'gh-actions' && cfg.ciTitles) {
        const root = trace.spans.find((s) => !s.parentId);
        // the run title is the PR or commit title; the branch name can carry ticket ids or customer names
        if (root && root.name !== root.service) { root.name = root.service; counts['ci-title'] = 1; }
        for (const s of trace.spans) if (s.attrs) for (const k of ['ci.branch', 'ci.title', 'ci.head_sha', 'ci.actor']) if (k in s.attrs) { delete s.attrs[k]; counts['ci-meta'] = (counts['ci-meta'] || 0) + 1; }
      }
      for (const s of trace.spans) {
        // CI step stations are named by their own span id (job-<id>-step-<n>), which the page keeps anyway;
        // scrubbing the id out of the name alone would merge every job's steps into one station
        const stepStation = format === 'gh-actions' && s.service === s.spanId && /^job-\d+-step-\d+$/.test(s.service);
        for (const k of ['service', 'name', 'peer', 'operation', 'statusMessage']) if (typeof s[k] === 'string' && !(k === 'service' && stepStation)) s[k] = str(s[k]);
        if (s.attrs) {
          for (const k of Object.keys(s.attrs)) {
            if (cfg.level === 'strict' && !STRICT_ATTRS.test(k)) { delete s.attrs[k]; counts['strict-attr'] = (counts['strict-attr'] || 0) + 1; continue; }
            s.attrs[k] = scrubValue(s.attrs[k], cfg, counts);
          }
        }
        if (s.events) s.events = scrubValue(s.events, cfg, counts);
      }
      if (trace.meta.source) trace.meta.source = str(String(trace.meta.source));
      trace.meta.warnings = trace.meta.warnings.map(str);
      const values = Object.values(counts).reduce((a, b) => a + b, 0);
      trace.meta.scrub = { level: cfg.level, values, kinds: counts };
      return trace.meta.scrub;
    }
    
  • skills/agent-transit/core/setup.mjs15 KBfce36fddc341upstream ↗
    // core/setup.mjs: first-run check, shared by every entry script. Reuses what the machine already has and
    // downloads only what is missing, always with a one-line size notice first. It never installs system
    // packages (no brew/apt/winget): when something system-level is missing it explains and prints the command.
    //   node >= 20            required (playwright-core 1.64 needs 20), never installed
    //   npm deps              `npm install` (playwright-core + ffmpeg-static, project-local, into node_modules) only
    //                         when node_modules is missing
    //   browser, first that launches:
    //     1. TRANSIT_BROWSER (path, or channel chrome/msedge)
    //     2. Playwright's own build for this playwright-core version, if already downloaded
    //     3. any other Chromium / headless shell already in the Playwright cache (other projects' downloads)
    //     4. system Google Chrome or Microsoft Edge (Playwright channels), or chromium on PATH (Linux)
    //     5. download chromium-headless-shell only (~100 MB download / ~200 MB on disk; full Chromium is ~170 MB / ~370 MB)
    //   ffmpeg                 1. TRANSIT_FFMPEG=/path/to/ffmpeg   2. the pinned ffmpeg-static dependency (package.json +
    //                          lockfile; its install script fetches the binary, approved in package.json allowScripts)
    //                          3. ffmpeg on PATH or in /opt/homebrew/bin, /usr/local/bin, /usr/bin
    //                          else: explain, print the install command, exit. ffprobe is not used.
    // The marker .transit-cache/setup.json records what was reused vs installed; scripts read it via core/tools.mjs.
    // usage: node core/setup.mjs [--force] [--check] [--no-download]
    //   --check        dry run: report what would be used or downloaded, change nothing (exit 1 if something is missing)
    //   --no-download  never download (also TRANSIT_NO_DOWNLOAD=1); fail with instructions instead
    // env: TRANSIT_SKIP_SETUP=1 skips everything (prepared CI images); TRANSIT_NO_SYSTEM_BROWSER=1 ignores system
    //      Chrome/Edge (pin renders to Playwright's build); TRANSIT_BROWSER=... forces one
    import { spawnSync } from 'node:child_process';
    import { existsSync, readFileSync, writeFileSync, mkdirSync, rmSync, readdirSync, statSync } from 'node:fs';
    import { dirname, join } from 'node:path';
    import { homedir } from 'node:os';
    import { lookup } from 'node:dns/promises';
    import { fileURLToPath, pathToFileURL } from 'node:url';
    import { ROOT, MARKER, launchOptions } from './tools.mjs';
    
    const MIN_NODE = 20;
    const log = (m) => process.stderr.write(`transit setup: ${m}\n`);
    const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
    const runs = (cmd, args) => spawnSync(cmd, args, { stdio: 'ignore' }).status === 0;
    const PW = join(ROOT, 'node_modules/playwright-core');
    const pwVersion = () => { try { return JSON.parse(readFileSync(join(PW, 'package.json'), 'utf8')).version; } catch { return null; } };
    const argv = process.argv.slice(2);
    const isMain = process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1];
    
    class SetupError extends Error {}
    const fail = (msg) => { throw new SetupError(msg); };
    
    async function online(host) { try { await lookup(host); return true; } catch { return false; } }
    // Retries with backoff (2 s, 8 s) for network steps; returns true on success.
    async function runRetry(label, cmd, args, { attempts = 3 } = {}) {
      for (let i = 1; i <= attempts; i++) {
        log(`${label}${attempts > 1 ? ` (attempt ${i}/${attempts})` : ''}: ${cmd} ${args.join(' ')}`);
        const r = spawnSync(cmd, args, { cwd: ROOT, stdio: ['ignore', 'inherit', 'inherit'] });   // arg arrays, never a shell
        if (r.status === 0) return true;
        if (i < attempts) await sleep(i === 1 ? 2000 : 8000);
      }
      return false;
    }
    
    // npm without a shell: on Windows npm is a .cmd shim, so run its JS entry with this node instead
    function npmCommand(args) {
      if (process.platform !== 'win32') return ['npm', args];
      const cli = [process.env.npm_execpath, join(dirname(process.execPath), 'node_modules/npm/bin/npm-cli.js')].find((p) => p && /npm-cli\.js$/.test(p) && existsSync(p));
      if (!cli) fail('cannot locate npm-cli.js next to node; run `npm install` in ' + ROOT + ' yourself');
      return [process.execPath, [cli, ...args]];
    }
    
    // ---------------------------------------------------------------- browser
    const pwCacheDir = () => process.env.PLAYWRIGHT_BROWSERS_PATH && process.env.PLAYWRIGHT_BROWSERS_PATH !== '0' ? process.env.PLAYWRIGHT_BROWSERS_PATH
      : process.platform === 'darwin' ? join(homedir(), 'Library/Caches/ms-playwright')
      : process.platform === 'win32' ? join(process.env.LOCALAPPDATA || join(homedir(), 'AppData/Local'), 'ms-playwright')
      : join(homedir(), '.cache/ms-playwright');
    const EXE = new Set(['chrome-headless-shell', 'chrome-headless-shell.exe', 'Chromium', 'chrome', 'chrome.exe', 'Google Chrome for Testing']);
    function findExecutables(dir, depth = 5, out = []) {
      let names; try { names = readdirSync(dir); } catch { return out; }
      for (const n of names) {
        const p = join(dir, n); let st; try { st = statSync(p); } catch { continue; }
        if (st.isFile() && EXE.has(n) && (process.platform === 'win32' || st.mode & 0o111)) out.push(p);
        else if (st.isDirectory() && depth > 0 && !n.endsWith('.framework') && n !== 'Resources' && n !== 'locales') findExecutables(p, depth - 1, out);
      }
      return out;
    }
    // Other Playwright downloads on this machine, newest revision first, headless shells before full Chromium.
    function cachedBrowsers() {
      const base = pwCacheDir(); let dirs = [];
      try { dirs = readdirSync(base).filter((d) => /^chromium(_headless_shell)?-\d+$/.test(d)); } catch {}
      dirs.sort((a, b) => Number(b.split('-').pop()) - Number(a.split('-').pop()) || (b.includes('headless') ? 1 : -1));
      return dirs.flatMap((d) => findExecutables(join(base, d)).map((p) => ({ kind: 'executable', value: p, source: 'playwright-cache', label: d })));
    }
    function systemBrowsers() {
      const c = [{ kind: 'channel', value: 'chrome', source: 'system', label: 'Google Chrome' }, { kind: 'channel', value: 'msedge', source: 'system', label: 'Microsoft Edge' }];
      if (process.platform === 'linux') for (const p of ['/usr/bin/chromium', '/usr/bin/chromium-browser', '/snap/bin/chromium']) if (existsSync(p)) c.push({ kind: 'executable', value: p, source: 'system', label: p });
      return c;
    }
    async function tryLaunch(choice) {
      try {
        const { chromium } = await import(pathToFileURL(join(PW, 'index.mjs')).href);
        const b = await chromium.launch({ ...launchOptions(choice), timeout: 30000 });
        const version = b.version(); await b.close(); return version;
      } catch { return null; }
    }
    async function pickBrowser({ check, noDownload }) {
      const candidates = [];
      if (process.env.TRANSIT_BROWSER) {
        const v = process.env.TRANSIT_BROWSER;
        candidates.push(v === 'bundled' ? { kind: 'bundled', source: 'env' } : /^(chrome|chrome-beta|msedge|msedge-beta|chromium)$/.test(v) ? { kind: 'channel', value: v, source: 'env' } : { kind: 'executable', value: v, source: 'env' });
      } else {
        candidates.push({ kind: 'bundled', source: 'playwright-cache', label: `playwright-core ${pwVersion()} build` }, ...cachedBrowsers(), ...(process.env.TRANSIT_NO_SYSTEM_BROWSER === '1' ? [] : systemBrowsers()));
      }
      for (const c of candidates) {
        const version = await tryLaunch(c);
        if (version) return { ...c, version };
      }
      if (process.env.TRANSIT_BROWSER) fail(`TRANSIT_BROWSER=${process.env.TRANSIT_BROWSER} did not launch. Point it at a Chrome/Chromium/Edge executable or a channel (chrome, msedge), or unset it.`);
      const notice = 'no usable Chrome/Chromium found; Transit needs chromium-headless-shell (~100 MB download, ~200 MB on disk, into ' + pwCacheDir() + ')';
      if (check) return { kind: 'bundled', source: 'would-download', note: notice };
      if (noDownload) fail(`${notice}, but downloads are disabled (--no-download / TRANSIT_NO_DOWNLOAD).\n  Install Google Chrome, or set TRANSIT_BROWSER=/path/to/chrome, or allow the download.`);
      if (!(await online('cdn.playwright.dev')) && !(await online('playwright.download.prss.microsoft.com'))) fail(`${notice}, but this machine looks offline (DNS lookup failed).\n  Connect and rerun, install Google Chrome, or set TRANSIT_BROWSER=/path/to/chrome.`);
      log(`${notice}. Downloading once…`);
      if (!(await runRetry('browser download', process.execPath, [join(PW, 'cli.js'), 'install', '--only-shell', 'chromium']))) fail('chromium-headless-shell download failed 3 times. Check your network/proxy (HTTPS_PROXY), or install Google Chrome and rerun.');
      const version = await tryLaunch({ kind: 'bundled' });
      if (!version) fail('the downloaded headless shell does not start.' + (process.platform === 'linux' ? ' On Linux install its system libraries: npx playwright-core install-deps chromium' : ''));
      return { kind: 'bundled', source: 'downloaded', label: 'chromium-headless-shell', version };
    }
    
    // ---------------------------------------------------------------- ffmpeg
    // ffmpeg only (MP4/GIF/contact sheet and the verify gates); frame counts come from ffmpeg itself, so no ffprobe.
    function staticFfmpeg() {
      try {
        const pkg = join(ROOT, 'node_modules/ffmpeg-static');
        const version = JSON.parse(readFileSync(join(pkg, 'package.json'), 'utf8')).version;
        const bin = join(pkg, process.platform === 'win32' ? 'ffmpeg.exe' : 'ffmpeg');
        return { bin, version, present: existsSync(bin) };
      } catch { return null; }
    }
    function findFfmpeg() {
      if (process.env.TRANSIT_FFMPEG) return runs(process.env.TRANSIT_FFMPEG, ['-version']) ? { ffmpeg: process.env.TRANSIT_FFMPEG, source: 'env' } : null;
      const st = staticFfmpeg();
      if (st && st.present && runs(st.bin, ['-version'])) return { ffmpeg: st.bin, source: `ffmpeg-static ${st.version}` };
      for (const d of ['', '/opt/homebrew/bin/', '/usr/local/bin/', '/usr/bin/']) if (runs(d + 'ffmpeg', ['-version'])) return { ffmpeg: d + 'ffmpeg', source: 'system' };
      return null;
    }
    function pickFfmpeg({ check }) {
      const found = findFfmpeg();
      if (found) return found;
      if (process.env.TRANSIT_FFMPEG) fail(`TRANSIT_FFMPEG=${process.env.TRANSIT_FFMPEG} does not run (\`-version\` failed). Point it at an ffmpeg binary or unset it.`);
      const st = staticFfmpeg();
      const fix = st && !st.present
        ? `The ffmpeg-static dependency is installed but its binary was not downloaded (its install script did not run; npm 11 asks for approval).\n  Fetch it: npm rebuild ffmpeg-static   (from ${ROOT}; downloads ~45 MB into node_modules)`
        : `Run \`npm install\` in ${ROOT} (installs the pinned ffmpeg-static, ~45 MB, into node_modules).`;
      const sys = process.platform === 'darwin' ? 'brew install ffmpeg' : process.platform === 'linux' ? 'sudo apt-get install -y ffmpeg   (Fedora: sudo dnf install ffmpeg)' : 'winget install Gyan.FFmpeg';
      const help = `ffmpeg is needed for MP4/GIF output and was not found.\n  ${fix}\n  Or install a system ffmpeg yourself: ${sys}\n  Or set TRANSIT_FFMPEG=/path/to/ffmpeg.\n  Transit never installs system packages for you. The interactive HTML (core/build.mjs) works without ffmpeg.`;
      if (check) return { source: 'missing', note: help.replace(/\n\s*/g, ' | ') };
      fail(help);
    }
    
    // ---------------------------------------------------------------- main entry
    export async function ensureSetup({ need = ['deps', 'browser', 'ffmpeg'], force = false, check = false } = {}) {
      if (process.env.TRANSIT_SKIP_SETUP === '1') return null;
      const noDownload = argv.includes('--no-download') || process.env.TRANSIT_NO_DOWNLOAD === '1';
      try {
        const major = Number(process.versions.node.split('.')[0]);
        if (major < MIN_NODE) fail(`Node ${process.versions.node} is too old; Transit needs Node ${MIN_NODE}+ (playwright-core 1.64 requires it). Install a current Node from https://nodejs.org or your version manager.`);
        let mark = null;
        try { mark = JSON.parse(readFileSync(MARKER, 'utf8')); } catch {}
        const browserGone = mark?.browser?.kind === 'executable' && !existsSync(mark.browser.value);
        const fresh = !force && !check && mark && mark.version === 3 && mark.node === process.versions.node && mark.playwrightCore === pwVersion() && existsSync(PW) && !browserGone;
        const todo = need.filter((n) => !(fresh && mark.ok?.includes(n)));
        if (!todo.length) return mark;
        if (!check) log(`first run (or changed setup): checking ${todo.join(', ')}`);
        const rec = fresh ? { ...mark } : { version: 3, ok: [] };
        const ok = new Set(rec.ok);
    
        if (todo.includes('deps') || todo.includes('browser')) {
          if (existsSync(PW)) rec.deps = { source: 'present' };
          else if (check) rec.deps = { source: 'missing', note: 'would run npm install (playwright-core ~13 MB + pinned ffmpeg-static ~45 MB, into node_modules)' };
          else {
            if (noDownload) fail('node_modules is missing and downloads are disabled. Run `npm install` in ' + ROOT);
            if (!(await online('registry.npmjs.org'))) fail('node_modules is missing and this machine looks offline (cannot resolve registry.npmjs.org). Connect and rerun, or copy node_modules from another machine.');
            log('installing project dependencies into node_modules (playwright-core ~13 MB, pinned ffmpeg-static ~45 MB); nothing system-wide…');
            if (!(await runRetry('npm install', ...npmCommand(['install', '--no-audit', '--no-fund', '--omit=dev'])))) fail('npm install failed 3 times. Check your network/proxy and run `npm install` in ' + ROOT + ' to see the error.');
            rec.deps = { source: 'installed' };
          }
          ok.add('deps');
        }
        if (todo.includes('browser')) {
          rec.browser = existsSync(PW) ? await pickBrowser({ check, noDownload }) : { source: 'unknown', note: 'needs npm deps first' };
          ok.add('browser');
        }
        if (todo.includes('ffmpeg')) { rec.ffmpeg = pickFfmpeg({ check }); ok.add('ffmpeg'); }
    
        Object.assign(rec, { node: process.versions.node, playwrightCore: pwVersion(), platform: `${process.platform}-${process.arch}`, ok: [...ok].sort() });
        if (check) return rec;
        mkdirSync(dirname(MARKER), { recursive: true });
        writeFileSync(MARKER, JSON.stringify(rec, null, 1) + '\n');
        const b = rec.browser, f = rec.ffmpeg;
        log(`ok: browser = ${b ? `${b.label || b.value || b.kind} (${b.source}${b.version ? ', ' + b.version : ''})` : 'not needed'}; ffmpeg = ${f ? `${f.ffmpeg} (${f.source})` : 'not needed'}. Recorded in .transit-cache/setup.json`);
        return rec;
      } catch (e) {
        if (!(e instanceof SetupError)) throw e;
        log(e.message); if (isMain || !check) process.exit(1); throw e;
      }
    }
    
    if (isMain) {
      const check = argv.includes('--check');
      if (argv.includes('--force')) rmSync(MARKER, { force: true });
      const rec = await ensureSetup({ force: argv.includes('--force'), check });
      if (check) {
        const line = (k, v) => console.log(`${k.padEnd(8)} ${v}`);
        line('node', `${process.versions.node} (needs ${MIN_NODE}+)`);
        line('deps', `${rec.deps?.source}${rec.deps?.note ? ' - ' + rec.deps.note : ''}`);
        const b = rec.browser || {};
        line('browser', b.source === 'would-download' ? `missing - ${b.note}` : `${b.label || b.value || b.kind} [${b.source}]${b.version ? ' ' + b.version : ''}`);
        const f = rec.ffmpeg || {};
        line('ffmpeg', f.ffmpeg ? `${f.ffmpeg} [${f.source}]` : `missing - ${f.note}`);
        const missing = rec.deps?.source === 'missing' || b.source === 'would-download' || !f.ffmpeg;
        console.log(missing ? 'check: something would be installed (nothing was changed)' : 'check: everything present, nothing to download');
        process.exit(missing ? 1 : 0);
      }
      log('all set');
    }
    
  • skills/agent-transit/core/still.mjs923 Bbcc6ed3d3086upstream ↗
    import { ensureSetup } from './setup.mjs'; import { pathToFileURL } from 'node:url'; import { resolve } from 'node:path';
    await ensureSetup({ need: ['deps', 'browser'] });
    const { launchBrowser } = await import('./tools.mjs');
    const [file, ...ts] = process.argv.slice(2);
    const b = await launchBrowser(); const p = await b.newPage({ viewport: { width: 1200, height: 900 } });
    const errs = []; p.on('pageerror', (e) => errs.push(e.message)); p.on('console', (m) => m.type() === 'error' && errs.push(m.text()));
    await p.goto(pathToFileURL(resolve(file)).href + '?paused=1'); await p.evaluate(() => document.fonts.ready);
    console.log(JSON.stringify(await p.evaluate(() => ({ s: window.VIZ && window.VIZ.seconds, f: document.fonts.check('700 14px "Transit Sans"') }))), errs);
    for (const t of ts) { await p.evaluate((t) => window.renderAt(t), Number(t)); await p.screenshot({ path: `/tmp/still-${t}.png` }); }
    await b.close();
    
  • skills/agent-transit/core/tools.mjs2.7 KB663f743d7dfeupstream ↗
    // core/tools.mjs: the browser and ffmpeg that core/setup.mjs chose, recorded in .transit-cache/setup.json.
    // Every script launches Chromium and runs ffmpeg through here, so a reused system Chrome or a reused
    // Playwright cache from another project is used the same way everywhere.
    //   TRANSIT_BROWSER=/path/to/chrome | chrome | msedge | bundled   overrides the recorded choice
    // Determinism: two renders on the same machine with the same browser build are byte-identical. Different
    // browsers or builds (system Chrome vs Playwright's headless shell, or another version) can rasterize text
    // slightly differently, so hashes are only comparable per machine+browser.
    import { readFileSync } from 'node:fs';
    import { spawnSync } from 'node:child_process';
    import { dirname, join, resolve } from 'node:path';
    import { fileURLToPath } from 'node:url';
    
    export const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
    export const MARKER = join(ROOT, '.transit-cache', 'setup.json');
    const marker = () => { try { return JSON.parse(readFileSync(MARKER, 'utf8')); } catch { return {}; } };
    
    // {kind:'bundled'} | {kind:'channel', value:'chrome'} | {kind:'executable', value:'/path'}
    export function browserChoice() {
      const env = process.env.TRANSIT_BROWSER;
      if (env) return env === 'bundled' ? { kind: 'bundled' } : /^(chrome|chrome-beta|msedge|msedge-beta|chromium)$/.test(env) ? { kind: 'channel', value: env } : { kind: 'executable', value: env };
      return marker().browser || { kind: 'bundled' };
    }
    export const launchOptions = (c = browserChoice()) => c.kind === 'channel' ? { channel: c.value } : c.kind === 'executable' ? { executablePath: c.value } : {};
    
    export async function launchBrowser(opts = {}) {
      const { chromium } = await import('playwright-core');
      return chromium.launch({ ...launchOptions(), ...opts });
    }
    
    export const ffmpegBin = () => process.env.TRANSIT_FFMPEG || marker().ffmpeg?.ffmpeg || 'ffmpeg';
    
    // Runs ffmpeg with an argument array (never a shell) and returns stdout + stderr as text, even when it exits non-zero.
    export function ffmpegText(args) {
      const r = spawnSync(ffmpegBin(), ['-hide_banner', ...args], { encoding: 'utf8', maxBuffer: 1 << 26 });
      if (r.error) throw new Error(`cannot run ffmpeg (${ffmpegBin()}): ${r.error.code || r.error.message}`);
      return (r.stdout || '') + (r.stderr || '');
    }
    // Decoded video frame count from ffmpeg's own progress line (replaces ffprobe -count_frames).
    export function countFrames(file) {
      const out = ffmpegText(['-nostdin', '-i', file, '-map', '0:v:0', '-f', 'null', '-']);
      const all = [...out.matchAll(/frame=\s*(\d+)/g)];
      if (!all.length) throw new Error(`ffmpeg reported no frames for ${file}`);
      return Number(all[all.length - 1][1]);
    }
    
  • skills/agent-transit/core/transit.template.html84 KB785dbfbc105eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/core/verdict.mjs6.0 KB9fa093244ea0upstream ↗
    // core/verdict.mjs: a one-line, data-derived summary of a normalized trace, used for the default file
    // name, the --share console tip and the --share page headline. Nothing here is invented: the subject,
    // message, time and count all come from the trace. The hop count mirrors the page's call building
    // (uninstrumented peers + cross-service parent/child edges), without CI needs connectors.
    const CI = 'gh-actions', AGENT = ['claude-code', 'codex', 'cursor'];
    
    export function countHops(trace) {
      const spans = trace.spans, byId = new Map(spans.map((s) => [s.spanId, s]));
      const kids = new Map(spans.map((s) => [s.spanId, []]));
      for (const s of spans) if (s.parentId && kids.has(s.parentId)) kids.get(s.parentId).push(s);
      const crossKids = (s) => kids.get(s.spanId).filter((c) => c.service !== s.service);
      const via = new Map();
      let n = 0;
      for (const s of spans) {
        if ((s.kind === 'client' || s.kind === 'producer') && s.peer && (s.kind === 'producer' || crossKids(s).length === 0) && s.peer !== s.service) { via.set(s.spanId, s.peer); n++; }
      }
      for (const s of spans) {
        const p = s.parentId && byId.get(s.parentId);
        if (!p || p.service === s.service) continue;
        const from = p.kind === 'producer' && via.has(p.spanId) ? via.get(p.spanId) : p.service;
        if (from !== s.service) n++;
      }
      return n;
    }
    
    // the first error that starts here (an error span with no error below it), earliest end first
    export function errorOrigin(trace) {
      const kids = new Map(trace.spans.map((s) => [s.spanId, []]));
      for (const s of trace.spans) if (s.parentId && kids.has(s.parentId)) kids.get(s.parentId).push(s);
      const errBelow = (s) => kids.get(s.spanId).some((k) => k.status === 'error' || errBelow(k));
      const origins = trace.spans.filter((s) => s.status === 'error' && !errBelow(s));
      // CI: only steps carry alerts on the map
      const pool = trace.meta && trace.meta.format === CI ? origins.filter((s) => s.attrs && s.attrs['ci.step.number'] != null) : origins;
      return pool.sort((a, b) => (a.start + a.duration) - (b.start + b.duration) || a.start - b.start || String(a.spanId).localeCompare(String(b.spanId)))[0] || null;
    }
    
    export const fmtMs = (ms) => {
      if (ms < 1000) return `${Math.round(ms)} ms`;
      if (ms < 60000) return `${(ms / 1000).toFixed(ms < 10000 ? 2 : 1)} s`;
      const sec = Math.round(ms / 1000), m = Math.floor(sec / 60), r = sec % 60;
      return m < 60 ? `${m} min${r ? ` ${r} s` : ''}` : `${Math.floor(m / 60)} h ${m % 60} min`;
    };
    const clean = (s) => String(s || '').replace(/\s+/g, ' ').trim();
    // cut at a word boundary, never mid-word
    const cut = (t, max) => { if (t.length <= max) return t; const w = t.slice(0, max).replace(/\s+\S*$/, ''); return (w || t.slice(0, max)).replace(/[\s,;:.-]+$/, '') + '…'; };
    
    // the short cause for the verdict line; the full message stays in the panel and the line's tooltip.
    // "Internal Server Error: failed to send order confirmation (SMTP timeout)" -> "SMTP timeout"
    // "error: connect ECONNREFUSED 10.0.0.1:5432" -> "connect ECONNREFUSED 10.0.0.1:5432"
    // returns '' when the message only repeats the station's own name ("step failed: Run unit tests")
    export function shortReason(message, names = [], max = 40) {
      let t = clean(message);
      if (!t) return '';
      const paren = t.match(/\(([^()]{2,48})\)\s*\.?$/);
      if (paren) t = paren[1].trim();
      else if (t.length > max) {
        const parts = t.split(/:\s+/).filter(Boolean);
        if (parts.length > 1) t = parts[parts.length - 1];
      }
      const low = t.toLowerCase().replace(/^(step|job|tool|call)?\s*failed:?\s*/, '');
      if (!low || names.some((n) => n && clean(n).toLowerCase() === low)) return '';
      return cut(t, max);
    }
    
    function subjectOf(s, format) {
      // a step without ci.job (hand-made or partial run JSON) falls back to its station, never 'undefined'
      if (format === CI) { const job = (s.attrs && s.attrs['ci.job']) || s.service; return job && job !== s.name ? { label: `${job} / ${s.name}`, short: job } : { label: s.name, short: s.name }; }
      if (AGENT.includes(format)) return { label: s.name, short: s.name };
      return { label: s.service, short: s.service };
    }
    
    // wall (optional): spanId -> {start, duration} from before idle gaps were shortened, so times read as wall time
    export function verdict(trace, vocab = {}, wall = null) {
      const format = trace.meta && trace.meta.format;
      const W = (s) => (wall && wall.get(s.spanId)) || s;
      const hops = countHops(trace), noun = vocab.hopNounPlural || 'hops', alert = vocab.serviceAlert || 'SERVICE ALERT';
      const t0 = Math.min(...trace.spans.map((s) => W(s).start)), t1 = Math.max(...trace.spans.map((s) => W(s).start + W(s).duration));
      const o = errorOrigin(trace);
      if (!o) return { text: `no ${alert} · ${hops} ${noun} in ${fmtMs(t1 - t0)}`, origin: null, subject: null, message: null };
      const sub = subjectOf(o, format);
      const reason = shortReason(o.statusMessage, [o.name, o.service, sub.label]);
      return {
        text: `${sub.label} failed after ${fmtMs(W(o).start - t0)}${reason ? `: ${reason}` : ''} · ${alert} · ${hops} ${noun}`,
        origin: o.spanId, subject: sub.short, message: clean(o.statusMessage) || null,
      };
    }
    
    export const slug = (s, max = 40) => String(s || '').normalize('NFKD').replace(/[̀-ͯ]/g, '').toLowerCase()
      .replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, max).replace(/-+$/, '');
    
    // "Checkout: one order through..." + email alert -> "checkout-email-service-alert"
    // Empty slug parts (e.g. CJK-only text) are dropped, so a name never starts with '-' (shells read that as a flag):
    // an alert with nothing nameable is "transit-service-alert".
    export function defaultName(title, v) {
      const t = String(title || '');
      const head = /[:–—|]/.test(t) ? t.split(/[:–—|]/)[0] : t.split(/\s+/).slice(0, 4).join(' ');
      const parts = [slug(head, 32), v && v.subject ? slug(v.subject, 24) : ''].filter(Boolean);
      if (v && v.subject) parts.push(parts.length ? 'service-alert' : 'transit-service-alert');
      const name = parts.join('-').replace(/-+/g, '-').replace(/^-+/, '').slice(0, 80).replace(/-+$/, '');
      return (name || 'transit') + '.html';
    }
    
  • skills/agent-transit/core/verify.mjs4.4 KBd81fc1c55547upstream ↗
    // core/verify.mjs: done-check for story 4 (share a flow).
    //   1. determinism: render every frame twice into separate folders, sha256 each PNG, compare
    //   2. timing: each train leaves on frame ceil((intro + span.start / msPerSec) * fps)
    //   3. ffmpeg gates on anim.mp4: freezedetect (no static stretch longer than intro/outro allow),
    //      blackdetect (no black gaps) and blackdetect on the negated film (no blank white gaps)
    // usage: node core/verify.mjs <trace.json> --name otel [build options]
    import { execFileSync } from 'node:child_process';
    import { readdirSync, readFileSync } from 'node:fs';
    import { createHash } from 'node:crypto';
    import { join, resolve, dirname } from 'node:path';
    import { fileURLToPath, pathToFileURL } from 'node:url';
    import { ensureSetup } from './setup.mjs';
    import { ffmpegText } from './tools.mjs';
    import { removeFramesDir, safeName } from './fsguard.mjs';
    import { parseArgs, toArgs, runCli, UserError } from './cli.mjs';
    await ensureSetup({ need: ['deps', 'browser', 'ffmpeg'] });
    const { launchBrowser } = await import('./tools.mjs');
    
    const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
    await runCli(async () => {
    const { opt, pos } = parseArgs(process.argv.slice(2));
    if (!pos[0]) throw new UserError('usage: node core/verify.mjs <trace.json> --name NAME [build options]');
    const name = safeName(opt.name || 'otel');
    const dir = join(ROOT, 'out', name);
    let fail = 0;
    const ok = (c, m) => { console.log(`${c ? 'PASS' : 'FAIL'}  ${m}`); if (!c) fail++; };
    const pass = (args) => [...args, ...toArgs(opt, ['name'])];
    // child renders: argument arrays (no shell); their stderr shows, so a failure says why
    const node = (args) => execFileSync(process.execPath, args, { cwd: ROOT, stdio: ['ignore', 'ignore', 'inherit'] });
    
    // 1. two independent full renders
    const runA = join(dir, 'frames'), runB = join(dir, 'frames-rerun');
    node(pass(['core/render.mjs', pos[0], '--name', name]));
    node(pass(['core/render.mjs', pos[0], '--name', name, '--frames', runB, '--frames-only', '1']));
    const sha = (f) => createHash('sha256').update(readFileSync(f)).digest('hex');
    const A = readdirSync(runA).filter((f) => f.endsWith('.png')).sort(), B = readdirSync(runB).filter((f) => f.endsWith('.png')).sort();
    const diff = A.filter((f, i) => B[i] !== f || sha(join(runA, f)) !== sha(join(runB, f)));
    const all = createHash('sha256'); for (const f of A) all.update(sha(join(runA, f)));
    ok(A.length === B.length && diff.length === 0, `determinism: ${A.length} frames x 2 renders, sha256 identical (${diff.length} differ); film hash ${all.digest('hex').slice(0, 16)}`);
    removeFramesDir(runB);   // only the NNNN.png files this script rendered, then the folder if empty
    
    // 2. timing from the page model
    const browser = await launchBrowser(); const page = await browser.newPage();
    await page.goto(pathToFileURL(join(dir, 'transit.html')).href + '?paused=1');
    const V = await page.evaluate(() => ({ ...window.VIZ, trainsAt: undefined, frameOfTraceMs: undefined }));
    let bad = 0; const zero = V.calls.filter((c) => !(c.end > c.start)).length;   // zero-duration calls (e.g. skipped CI jobs) have no train
    for (const c of V.calls.filter((c) => c.end > c.start)) {
      const f = await page.evaluate((ms) => window.VIZ.frameOfTraceMs(ms), c.start);
      const on = async (fr) => (await page.evaluate((t) => window.VIZ.trainsAt(t), fr / V.fps)).some((x) => x.spanId === c.spanId && !x.back);
      if (!(await on(f)) || (f > 0 && await on(f - 1))) bad++;
    }
    ok(bad === 0, `timing: ${V.calls.length - zero} trains each leave on the frame of their span start (${bad} off${zero ? `, ${zero} zero-duration skipped` : ''})`);
    await browser.close();
    
    // 3. ffmpeg gates
    const mp4 = join(dir, 'anim.mp4');
    const ffe = (vf) => ffmpegText(['-nostdin', '-i', mp4, '-vf', vf, '-an', '-f', 'null', '-']);   // arg array, the recorded ffmpeg
    const maxStill = V.intro + 1.75 + 0.6;
    const fz = ffe('freezedetect=n=0.0005:d=' + maxStill.toFixed(2));
    const freezes = [...fz.matchAll(/freeze_duration: ([\d.]+)/g)].map((m) => Number(m[1]));
    ok(freezes.length === 0, `freezedetect: no static stretch >= ${maxStill.toFixed(2)} s (${freezes.length ? freezes.join(', ') + ' s' : 'none'})`);
    const bl = ffe('blackdetect=d=0.1:pix_th=0.10');
    ok(!/black_start/.test(bl), 'blackdetect: no black gaps');
    const wh = ffe('negate,blackdetect=d=0.1:pix_th=0.10:pic_th=0.97');
    ok(!/black_start/.test(wh), 'blackdetect (negated): no blank white gaps');
    console.log(fail ? `${fail} check(s) failed` : 'all checks passed');
    process.exit(fail ? 1 : 0);
    });
    
  • skills/agent-transit/core/vocab.mjs5.0 KBa9dfd797b25dupstream ↗
    // User-visible vocabulary for each Transit skill. Packs may be partial: the
    // request pack is the compatibility fallback for every missing string.
    export const vocab = {
      request: {
        lineNoun: 'service', stationNoun: 'hop', hopNoun: 'hop', hopNounPlural: 'hops',
        play: 'Play', pause: 'Pause',
        helpTitle: 'Keyboard shortcuts', space: 'play / pause', nextHop: 'next hop', previousHop: 'previous hop',
        seek: 'seek 0.5 s', frame: 'step one frame', startEnd: 'start / end', speed: 'speed 0.5x / 1x / 2x',
        click: 'click', clickHelp: 'station, train or line: select that hop', inspectClick: 'click a station, train or line', pauseHelp: 'selects the hop in progress',
        escape: 'deselect / close', help: 'this help',
        of: 'of', video: '1 s of video', trace: 'trace', infrastructure: 'infrastructure',
        delay: 'DELAY', serviceAlert: 'SERVICE ALERT', request: 'request', response: 'response',
        legendDelay: 'delay: self time ≥', legendAlert: 'service alert: error starts here',
        legendInfra: 'dotted: infrastructure (no spans of its own)',
        synthetic: 'SYNTHETIC sample data', renderedLocally: 'rendered locally by Transit',
        unmatched: 'unmatched', matched: 'matched', contracts: 'contracts', noContractIndex: 'no contract index',
        contractFooter: true,   // footer counts hops without a contract; only request traces have contracts
        scrubbedValues: 'value(s) scrubbed from names, messages and URLs', scrubOff: 'Value scrubbing was OFF for this page: do not share it.',
        error: 'error', slow: 'slow', ok: 'ok',
        tableTitle: 'Hops', tableStart: 'start', tableFrom: 'from', tableTo: 'to', tableSpan: 'span',
        tableStatus: 'status', tableContract: 'contract', infrastructureHop: 'infrastructure hop', infrastructureHopTitle: 'Infrastructure hop',
        unmatchedContracts: 'unmatched (contracts not indexed)', unmatchedShort: 'unmatched',
        noHopSelected: 'no hop selected', inspectHop: 'Pause, press', or: 'or',
        inspectTail: 'to inspect a hop.', shortcuts: 'Shortcuts', transit: 'Transit',
        hop: 'HOP', span: 'SPAN', from: 'from', client: 'client', server: 'server',
        noServerSpan: 'no server span', notInstrumented: 'is not instrumented',
        status: 'status', unset: 'unset', resource: 'resource', attributes: 'Attributes',
        serverSpan: 'server span', children: 'Children', none: 'none',
        sensitiveRedacted: 'Sensitive-looking keys are redacted at build time.',
        redactedAcross: 'attribute(s) redacted across this trace before rendering.',
        expectedRequestResponse: 'Expected request / response', contractsNotIndexed: 'Contracts not indexed. The contract layer is optional: build an index with',
        thenBuildWith: 'then build the page with', noHopContract: 'No hop contract for this span',
        internalWorkInside: 'internal work inside', noContractExpected: 'No contract expected',
        fallback: 'FALLBACK', matchedTag: 'MATCHED', message: 'Message',
        noRequestSchema: 'No request schema found', noResponseType: 'No response type found',
        responseBody: 'no body (void)', unknown: 'unknown', unresolved: 'unresolved',
        deeperFields: '… deeper fields not shown', optional: 'optional', required: 'required',
        sharedPackage: 'shared package', declared: 'declared',
        clientCallFrom: 'client call from', in: 'in', wholeTrace: 'of the whole trace',
        spanInsideClient: 'server span inside the client call', spanInsideParent: 'span inside its parent',
        deselect: 'Deselect (Esc)', more: 'more',
        confidenceHigh: 'high confidence', confidenceMedium: 'medium confidence', confidenceLow: 'low confidence',
        validator: 'validator', type: 'type', spec: 'spec', schema: 'schema',
        kind: 'kind', start: 'start', duration: 'duration', self: 'self',
        groups: { http: 'HTTP', db: 'DB', messaging: 'Messaging', rpc: 'RPC', other: 'Other' },
      },
      ci: {
        lineNoun: 'job', stationNoun: 'step', hopNoun: 'dependency', hopNounPlural: 'dependencies',
        request: 'workflow run', response: 'completion', delay: 'DELAY', serviceAlert: 'SERVICE ALERT',
        legendDelay: 'delay: self time ≥', legendAlert: 'service alert: error starts here',
        tableTitle: 'Steps', tableSpan: 'step', infrastructure: 'workflow infrastructure',
        hop: 'STEP', span: 'JOB', trace: 'run', error: 'failed', slow: 'slow step', skipped: 'skipped', contractFooter: false,
      },
      agent: {
        lineNoun: 'tool family', stationNoun: 'call', hopNoun: 'call', hopNounPlural: 'calls',
        trace: 'session', request: 'session', response: 'completion', tableTitle: 'Tool calls', tableSpan: 'call',
        delay: 'DELAY', serviceAlert: 'SERVICE ALERT', legendDelay: 'delay: self time ≥',
        legendAlert: 'service alert: error starts here', infrastructure: 'agent infrastructure',
        hop: 'CALL', span: 'TOOL', error: 'error', slow: 'slow call', contractFooter: false,
      },
    };
    
    export const VOCAB_PACKS = vocab;
    export const DEFAULT_VOCAB = 'request';
    
    export function resolveVocab(value = DEFAULT_VOCAB) {
      const pack = typeof value === 'string' ? vocab[value] : value;
      if (typeof value === 'string' && !pack) throw new Error(`unknown vocab pack: ${value}`);
      return { ...vocab.request, ...(pack || {}) };
    }
    
  • skills/agent-transit/LICENSE1.0 KB0cedbc256085upstream ↗
    MIT License
    
    Copyright (c) 2026 Israel Vicars
    
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
    
    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.
    
  • skills/agent-transit/package.json252 B28926b5b37e7upstream ↗
    {
      "name": "agent-transit",
      "private": true,
      "type": "module",
      "engines": {
        "node": ">=20"
      },
      "dependencies": {
        "ffmpeg-static": "5.3.0",
        "playwright-core": "1.64.0"
      },
      "allowScripts": {
        "[email protected]": true
      }
    }
    
  • skills/agent-transit/src/agent-transit/adapters/normalize-agent.mjs15 KBf467bc74ae28upstream ↗
    // Tolerant, privacy-first adapters for local coding-agent session JSONL.
    // Deliberately extracts timing and tool identity only; payloads are never copied.
    import { basename } from 'node:path';
    import { readFileSync } from 'node:fs';
    import { fileURLToPath } from 'node:url';
    import { validate, parseJsonSafe } from '../../request-transit/adapters/normalize.mjs';
    
    const FAMILIES = new Map([
      ['read', 'read'], ['readfile', 'read'], ['glob', 'read'], ['grep', 'read'], ['ls', 'read'], ['view', 'read'], ['view_image', 'read'],
      ['edit', 'edit'], ['write', 'edit'], ['multiedit', 'edit'], ['apply_patch', 'edit'],
      ['bash', 'shell'], ['shell', 'shell'], ['exec_command', 'shell'], ['local_shell', 'shell'], ['write_stdin', 'shell'], ['wait', 'shell'],
      ['webfetch', 'web'], ['websearch', 'web'], ['web_fetch', 'web'], ['web_search', 'web'], ['web__run', 'web'], ['web.run', 'web'],
      ['task', 'subagent'], ['agent', 'subagent'],
      ['skill', 'skill'], ['todowrite', 'plan'], ['update_plan', 'plan'], ['notebookedit', 'edit'],
      ['strreplace', 'edit'], ['delete', 'edit'], ['editnotebook', 'edit'],
      ['semanticsearch', 'read'], ['readlints', 'read'],
      // Cursor IDE composer tool names (toolFormerData.name in state.vscdb)
      ['read_file', 'read'], ['list_dir', 'read'], ['grep_search', 'read'], ['ripgrep', 'read'], ['file_search', 'read'], ['glob_file_search', 'read'],
      ['codebase_search', 'read'], ['read_lints', 'read'], ['search_replace', 'edit'], ['edit_file', 'edit'], ['edit_file_v2_apply_patch', 'edit'],
      ['delete_file', 'edit'], ['run_terminal_cmd', 'shell'], ['todo_write', 'plan'], ['update_memory', 'plan'], ['fetch_pull_request', 'web'],
    ]);
    const isoMs = (x) => {
      if (x == null) return null;
      if (typeof x === 'number') return x < 1e12 ? x * 1000 : x;
      const n = Number(x); if (Number.isFinite(n)) return n < 1e12 ? n * 1000 : n;
      const t = Date.parse(String(x)); return Number.isFinite(t) ? t : null;
    };
    const key = (name) => String(name || '').toLowerCase();
    const family = (name) => {
      const n = key(name);
      if (FAMILIES.has(n)) return FAMILIES.get(n);
      return n.startsWith('mcp__') || n.startsWith('mcp_') || n.includes('.') ? 'mcp' : 'other';
    };
    const contentText = (x) => Array.isArray(x) ? x.map((b) => typeof b === 'string' ? b : b && (b.text || b.content || '')).join(' ') : String(x ?? '');
    const denied = (x) => /\b(denied|permission denied|not allowed|rejected|approval required)\b/i.test(contentText(x));
    const errorText = (x) => /\b(error|failed|failure|exception)\b/i.test(contentText(x));
    const basenameTarget = (name, args) => {
      const a = args && typeof args === 'object' ? args : {};
      const path = a.file_path || a.filePath || a.path || a.filename || a.target_file || a.relativeWorkspacePath || a.relative_workspace_path || a.targetFile
        || a.directoryPath || a.targetDirectory || a.target_directory || a.globPattern || a.glob_pattern;
      if (path) return basename(String(path));
      const command = typeof args === 'string' ? args : a.command || a.cmd || a.script;
      if (command) return String(command).trim().split(/\s+/)[0].slice(0, 40);
      const query = a.pattern || a.query || a.url || a.searchTerm || a.search_term;
      return query ? String(query).trim().split(/\s+/)[0].slice(0, 40) : '';
    };
    const shortName = (name, args) => `${String(name || 'tool')}${basenameTarget(name, args) ? ` · ${basenameTarget(name, args)}` : ''}`.slice(0, 40);
    const safeArgs = (args) => {
      let s = typeof args === 'string' ? args : JSON.stringify(args ?? '', (k, v) => /^(content|output|result|prompt|text)$/i.test(k) ? '[REDACTED]' : v);
      s = s.replace(/("?(?:password|passwd|token|secret|authorization|cookie)"?\s*[:=]\s*")([^"\\]{0,200})(")/ig, '$1[REDACTED]$3');
      return s.slice(0, 120);
    };
    const isMarker = (x) => x && typeof x === 'object' && (x._agentFormat || x.meta && x.meta.agentFormat);
    const linesOf = (doc) => Array.isArray(doc) ? doc : String(doc).split(/\r?\n/).filter(Boolean).map((x) => JSON.parse(x));
    
    export function isAgentLog(doc) {
      const lines = Array.isArray(doc) ? doc : [];
      return !!lines.length && (isMarker(lines[0]) || lines.some((x) => x && (x.type === 'session_meta' || x.type === 'response_item' || x.role === 'assistant' || x.role === 'user')) || lines.slice(0, 5).some(isBubble));
    }
    
    function claudeEvents(lines, format, warnings) {
      const calls = new Map(), results = new Map(), times = [];
      for (const line of lines) {
        if (isMarker(line)) continue;
        if (['queue-operation', 'attachment', 'ai-title', 'last-prompt', 'summary', 'system', 'file-history-snapshot'].includes(line.type)) continue;
        const t = isoMs(line.timestamp); if (t != null) times.push(t);
        const blocks = line.message && Array.isArray(line.message.content) ? line.message.content : [];
        for (const b of blocks) {
          if (line.type === 'assistant' && b && b.type === 'tool_use' && b.id) calls.set(String(b.id), { id: String(b.id), name: b.name, args: b.input, start: t, sidechain: !!line.isSidechain });
          if (line.type === 'user' && b && b.type === 'tool_result' && b.tool_use_id) results.set(String(b.tool_use_id), { end: t, error: !!b.is_error, denied: !!b.is_error && denied(contentText(b.content).slice(0, 200)) });
        }
      }
      return [...calls.values()].map((c) => ({ ...c, result: results.get(c.id) })).filter((c) => c.start != null);
    }
    
    // Codex outputs: judge only the head of the output (first line / exit metadata). Scanning the whole body for
    // words like "error" or "denied" flags any call whose output merely mentions them (seen on real sessions).
    function codexStatus(p) {
      const text = contentText(p.output);
      const head = text.trimStart().split(/\r?\n/)[0].slice(0, 120);
      let exit = null;
      if (typeof p.output === 'string' && p.output.trim().startsWith('{')) { try { const o = JSON.parse(p.output); exit = o && o.metadata && o.metadata.exit_code; } catch {} }
      const isDenied = /^(permission denied|denied|rejected|not allowed|aborted by user)/i.test(head);
      return { error: !!p.is_error || isDenied || /^script failed\b/i.test(head) || (exit != null && Number(exit) !== 0), denied: isDenied };
    }
    
    function codexEvents(lines, warnings) {
      const calls = new Map(), results = new Map(), times = [], unknown = new Map();
      const warnUnknown = (type) => unknown.set(type, (unknown.get(type) || 0) + 1);
      for (const line of lines) {
        if (isMarker(line)) continue;
        const t = isoMs(line.timestamp); if (t != null) times.push(t);
        if (['session_meta', 'turn_context', 'event_msg', 'world_state', 'token_usage_record', 'compacted'].includes(line.type)) continue;
        if (line.type !== 'response_item') { warnUnknown(line.type || 'missing type'); continue; }
        const p = line.payload || {};
        const kind = p.type || line.type;
        if (kind === 'message' || kind === 'reasoning' || kind === 'agent_message' || String(kind).startsWith('compaction') || kind === 'ghost_snapshot') continue;
        if (kind === 'web_search_call') {
          const id = String(p.call_id || p.id || `web-${calls.size + 1}`);
          calls.set(id, { id, name: 'web_search', args: {}, start: t });
        } else if (kind === 'function_call' || kind === 'custom_tool_call' || kind === 'local_shell_call') {
          const id = String(p.call_id || p.id || `${kind}-${calls.size + 1}`);
          let args = p.arguments ?? p.input ?? p.command ?? p;
          let name = p.namespace && p.name ? `${p.namespace}.${p.name}` : p.name || (kind === 'local_shell_call' ? 'local_shell' : kind);
          let wrapper;
          if (kind === 'custom_tool_call' && name === 'exec') {
            const match = typeof p.input === 'string' && [...p.input.matchAll(/\btools\.(\w+)\s*\(/g)].find((m) => !/^(forEach|map|filter|find|some|every|reduce|includes|keys|values|entries)$/.test(m[1]));
            name = match ? match[1] : 'exec';
            args = {};
            wrapper = 'exec';
          }
          if (typeof args === 'string' && p.arguments && (args.trim().startsWith('{') || args.trim().startsWith('['))) { try { args = JSON.parse(args); } catch {} }
          calls.set(id, { id, name, args, start: t, wrapper });
        } else if (kind === 'function_call_output' || kind === 'custom_tool_call_output' || kind === 'local_shell_call_output') {
          const id = String(p.call_id || p.id || ''); if (id) { const st = codexStatus(p); results.set(id, { end: t, error: st.error, denied: st.denied }); }
        } else warnUnknown(kind || 'missing payload type');
      }
      for (const [type, count] of unknown) warnings.push(`ignored ${count} Codex line(s) of type ${type}`);
      return [...calls.values()].map((c) => ({ ...c, result: results.get(c.id) })).filter((c) => c.start != null);
    }
    
    // Cursor IDE composer bubbles (exported by scripts/export-cursor.mjs, or any dump of state.vscdb bubble records):
    // {bubbleId, type: 1 user | 2 assistant, createdAt ISO, timingInfo {clientRpcSendTime, clientEndTime}, toolFormerData {name, status, params}}.
    // A tool call starts at its bubble's createdAt. Cursor stores no tool end time, so it ends at the next bubble's
    // createdAt, capped by the turn's clientEndTime: an upper bound that includes the model's next step.
    // Bubbles from Cursor builds before ~Sep 2025 have no createdAt: order only, uniform durations.
    const isBubble = (l) => l && typeof l === 'object' && typeof l.bubbleId === 'string' && typeof l.type === 'number';
    const parseObj = (x) => { if (x && typeof x === 'object') return x; try { const o = JSON.parse(x); return o && typeof o === 'object' ? o : {}; } catch { return {}; } };
    function cursorBubbleEvents(lines, warnings) {
      const bubbles = lines.filter(isBubble);
      const t = bubbles.map((b) => isoMs(b.createdAt));
      const tools = bubbles.map((b, i) => [b, i]).filter(([b]) => b.toolFormerData && b.toolFormerData.name);
      const timed = tools.length > 0 && tools.every(([, i]) => t[i] != null);
      const events = []; let turnEnd = null;
      // Bubbles created within a few ms of each other belong to one model step: parallel tool calls, and the empty
      // assistant placeholder Cursor creates right after each call (seen on every call in real sessions). A call ends at
      // the first bubble after that burst.
      const BURST_MS = 300;
      const endOf = (i) => {   // next later timestamp after the burst, capped by the turn end
        let j = i + 1;
        while (j < bubbles.length && bubbles[j].type !== 1 && t[j] != null && t[j - 1] != null && t[j] - t[j - 1] < BURST_MS) j++;
        for (; j < bubbles.length; j++) if (t[j] != null && t[j] >= t[i]) return turnEnd != null && turnEnd >= t[i] ? Math.min(t[j], turnEnd) : t[j];
        return turnEnd != null && turnEnd >= t[i] ? turnEnd : t[i];
      };
      bubbles.forEach((b, i) => {
        if (b.type === 1) turnEnd = null;
        const te = b.timingInfo && isoMs(b.timingInfo.clientEndTime);
        if (te != null && te > 1e12) turnEnd = te;
        const tf = b.toolFormerData;
        if (!tf || !tf.name) return;
        const n = events.length, start = timed ? t[i] : n * 1250, end = timed ? endOf(i) : start + 1000;
        const st = String(tf.status || '').toLowerCase();
        events.push({ id: `cursor-${n + 1}`, name: tf.name, args: { ...parseObj(tf.rawArgs), ...parseObj(tf.params) }, start, cursor: true,
          result: { end, error: st === 'error', denied: st === 'cancelled' || st === 'rejected', cancelled: st === 'cancelled' } });
      });
      warnings.push(timed ? 'Cursor session: tool calls start at their bubble createdAt; Cursor stores no tool end time, so each ends at the next message (an upper bound)'
        : 'Cursor session without bubble timestamps (Cursor builds before ~Sep 2025): order only, durations are uniform');
      return events;
    }
    
    function cursorEvents(lines, warnings) {
      if (lines.some(isBubble)) return cursorBubbleEvents(lines, warnings);
      const calls = [], turnCalls = [];
      for (const line of lines) {
        if (line.type === 'turn_ended') {
          if (line.status === 'error' && turnCalls.length) turnCalls[turnCalls.length - 1].turnError = true;
          turnCalls.length = 0;
          continue;
        }
        const blocks = line.message && Array.isArray(line.message.content) ? line.message.content : [];
        for (const b of blocks) if (line.role === 'assistant' && b && b.type === 'tool_use') {
          const e = { id: `cursor-${calls.length + 1}`, name: b.name, args: b.input, start: calls.length * 1250, cursor: true };
          calls.push(e); turnCalls.push(e);
        }
      }
      warnings.push('Cursor transcripts have no timestamps or results: order only, durations are uniform');
      return calls.map((e) => ({ ...e, result: e.turnError ? { end: e.start + 1000, error: true, denied: false } : { end: e.start + 1000, error: false, denied: false } }));
    }
    
    export function normalizeAgent(doc, options = {}) {
      const lines = linesOf(doc), format = options.format || (lines[0] && lines[0]._agentFormat) || 'claude-code';
      const warnings = [], events = format === 'codex' ? codexEvents(lines, warnings) : format === 'cursor' ? cursorEvents(lines, warnings) : claudeEvents(lines, format, warnings);
      const allTimes = format === 'cursor' ? events.flatMap((e) => [e.start, e.result.end]) : lines.map((l) => isoMs(l.timestamp)).filter((x) => x != null);
      for (const e of events) if (e.result && e.result.end != null) allTimes.push(e.result.end);
      const startMs = allTimes.length ? Math.min(...allTimes) : 0, endMs = Math.max(...allTimes, startMs);
      const rootId = `session-${format}`;
      const raw = [{ spanId: rootId, parentId: null, service: 'agent', name: `${format} session`, start: 0, duration: endMs - startMs, status: 'ok', kind: 'server', attrs: { 'agent.source': format } }];
      for (const e of events) {
        const end = e.result && e.result.end != null ? e.result.end : e.start;
        const result = e.result;
        const status = result && (result.error || result.denied) ? 'error' : 'ok';
        const attrs = { 'agent.source': format };
        if (e.wrapper) attrs['agent.wrapper'] = e.wrapper;
        if (e.sidechain) attrs['agent.sidechain'] = true;
        const s = { spanId: `tool-${e.id}`, parentId: rootId, service: e.wrapper === 'exec' ? family(e.name) : family(e.name), name: shortName(e.name, e.args), start: Math.max(0, e.start - startMs), duration: Math.max(0, end - e.start), status, kind: 'internal', attrs };
        if (status === 'error') s.statusMessage = result.cancelled ? 'cancelled' : result.denied ? 'denied' : 'tool error';
        if (options.showArgs) s.attrs['agent.args'] = safeArgs(e.args);
        raw.push(s);
      }
      const synthetic = options.synthetic === true || lines.some((l) => /synthetic/i.test(String(l._comment || l.meta?.synthetic || '')));
      return validate({ traceId: `agent-${format}`, meta: { format, source: `agent.${format}`, synthetic, startUnixNs: String(Math.round(startMs * 1e6)), warnings }, spans: raw });
    }
    
    export function normalize(doc, options = {}) { return normalizeAgent(doc, options); }
    
    if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
      const file = process.argv[2], format = process.argv.find((x) => ['claude-code', 'codex', 'cursor'].includes(x));
      if (!file) throw new Error('usage: node normalize-agent.mjs <session.jsonl> [claude-code|codex|cursor]');
      const t = normalizeAgent(readFileSync(file, 'utf8'), { format }); console.log(JSON.stringify(t, null, 2));
    }
    
  • skills/agent-transit/src/agent-transit/samples/claude-code-synthetic.jsonl8.8 KB413b4b6bbfe8upstream ↗
    {"_agentFormat":"claude-code","_comment":"SYNTHETIC hand-built Claude Code JSONL; prompts, outputs, and file contents contain SECRET_CANARY_PROMPT_7731 / SECRET_CANARY_OUTPUT_7731 / SECRET_CANARY_COMMAND_7731."}
    {"type":"assistant","timestamp":"2026-10-16T12:00:01.000Z","uuid":"u-0","parentUuid":null,"message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-1","name":"Read","input":{"file_path":"src/module-0.mjs"}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:02.000Z","uuid":"r-0","parentUuid":"u-0","message":{"content":[{"type":"tool_result","tool_use_id":"cc-1","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:00:02.000Z","uuid":"u-1","parentUuid":"u-0","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-2","name":"Glob","input":{"pattern":"src/**/*.mjs"}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:03.000Z","uuid":"r-1","parentUuid":"u-1","message":{"content":[{"type":"tool_result","tool_use_id":"cc-2","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:00:03.000Z","uuid":"u-2","parentUuid":"u-1","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-3","name":"Grep","input":{"pattern":"normalize","path":"src"}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:04.000Z","uuid":"r-2","parentUuid":"u-2","message":{"content":[{"type":"tool_result","tool_use_id":"cc-3","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:00:04.000Z","uuid":"u-3","parentUuid":"u-2","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-4","name":"Edit","input":{"file_path":"src/module-3.mjs","content":"SECRET_CANARY_FILE_CONTENT_7731"}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:05.000Z","uuid":"r-3","parentUuid":"u-3","message":{"content":[{"type":"tool_result","tool_use_id":"cc-4","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:00:05.000Z","uuid":"u-4","parentUuid":"u-3","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-5","name":"Write","input":{"file_path":"src/module-4.mjs","content":"SECRET_CANARY_FILE_CONTENT_7731"}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:06.000Z","uuid":"r-4","parentUuid":"u-4","message":{"content":[{"type":"tool_result","tool_use_id":"cc-5","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:00:06.000Z","uuid":"u-5","parentUuid":"u-4","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-6","name":"Bash","input":{"command":"git diff --stat SECRET_CANARY_COMMAND_7731"}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:07.000Z","uuid":"r-5","parentUuid":"u-5","message":{"content":[{"type":"tool_result","tool_use_id":"cc-6","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:00:07.000Z","uuid":"u-6","parentUuid":"u-5","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-7","name":"Read","input":{"file_path":"src/module-6.mjs"}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:08.000Z","uuid":"r-6","parentUuid":"u-6","message":{"content":[{"type":"tool_result","tool_use_id":"cc-7","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:00:08.000Z","uuid":"u-7","parentUuid":"u-6","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-8","name":"Task","input":{"description":"inspect tests"}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:09.000Z","uuid":"r-7","parentUuid":"u-7","message":{"content":[{"type":"tool_result","tool_use_id":"cc-8","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:00:09.000Z","uuid":"u-8","parentUuid":"u-7","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-9","name":"WebSearch","input":{"query":"public API docs"}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:10.000Z","uuid":"r-8","parentUuid":"u-8","message":{"content":[{"type":"tool_result","tool_use_id":"cc-9","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:00:10.000Z","uuid":"u-9","parentUuid":"u-8","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-10","name":"WebFetch","input":{}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:11.000Z","uuid":"r-9","parentUuid":"u-9","message":{"content":[{"type":"tool_result","tool_use_id":"cc-10","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:00:11.000Z","uuid":"u-10","parentUuid":"u-9","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-11","name":"mcp__docs__lookup","input":{}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:12.000Z","uuid":"r-10","parentUuid":"u-10","message":{"content":[{"type":"tool_result","tool_use_id":"cc-11","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:00:12.000Z","uuid":"u-11","parentUuid":"u-10","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-12","name":"MultiEdit","input":{"file_path":"src/module-11.mjs","content":"SECRET_CANARY_FILE_CONTENT_7731"}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:13.000Z","uuid":"r-11","parentUuid":"u-11","message":{"content":[{"type":"tool_result","tool_use_id":"cc-12","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:00:13.000Z","uuid":"u-12","parentUuid":"u-11","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-13","name":"shell","input":{"command":"git diff --stat SECRET_CANARY_COMMAND_7731"}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:14.000Z","uuid":"r-12","parentUuid":"u-12","message":{"content":[{"type":"tool_result","tool_use_id":"cc-13","is_error":true,"content":"permission denied SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:00:14.000Z","uuid":"u-13","parentUuid":"u-12","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-14","name":"Read","input":{"file_path":"src/module-13.mjs"}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:15.000Z","uuid":"r-13","parentUuid":"u-13","message":{"content":[{"type":"tool_result","tool_use_id":"cc-14","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:00:15.000Z","uuid":"u-14","parentUuid":"u-13","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-15","name":"apply_patch","input":{"file_path":"src/module-14.mjs","content":"SECRET_CANARY_FILE_CONTENT_7731"}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:16.000Z","uuid":"r-14","parentUuid":"u-14","message":{"content":[{"type":"tool_result","tool_use_id":"cc-15","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:01:01.000Z","uuid":"u-15","parentUuid":"u-14","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-16","name":"Bash","input":{"command":"npm run build SECRET_CANARY_COMMAND_7731"}}]}}
    {"type":"user","timestamp":"2026-10-16T12:01:05.000Z","uuid":"r-15","parentUuid":"u-15","message":{"content":[{"type":"tool_result","tool_use_id":"cc-16","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:00:17.000Z","uuid":"u-16","parentUuid":"u-15","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-17","name":"Agent","input":{"description":"inspect tests"}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:18.000Z","uuid":"r-16","parentUuid":"u-16","message":{"content":[{"type":"tool_result","tool_use_id":"cc-17","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    {"type":"assistant","timestamp":"2026-10-16T12:00:18.000Z","uuid":"u-17","parentUuid":"u-16","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","id":"cc-18","name":"Write","input":{"file_path":"src/module-17.mjs","content":"SECRET_CANARY_FILE_CONTENT_7731"}}]}}
    {"type":"user","timestamp":"2026-10-16T12:00:19.000Z","uuid":"r-17","parentUuid":"u-17","message":{"content":[{"type":"tool_result","tool_use_id":"cc-18","is_error":false,"content":"result SECRET_CANARY_OUTPUT_7731"}]}}
    
  • skills/agent-transit/src/agent-transit/samples/codex-synthetic.jsonl7.1 KBdfb99279a439upstream ↗
    {"_agentFormat":"codex","_comment":"SYNTHETIC hand-built Codex rollout JSONL; SECRET_CANARY_PROMPT_7731 SECRET_CANARY_OUTPUT_7731 SECRET_CANARY_COMMAND_7731"}
    {"timestamp":"2026-10-16T12:00:00.000Z","type":"session_meta","payload":{"id":"synthetic-session"}}
    {"timestamp":"2026-10-16T12:00:01.000Z","type":"response_item","payload":{"type":"custom_tool_call","call_id":"cx-1","name":"exec","input":"const x = tools.exec_command({ command: 'SECRET_CANARY_COMMAND_7731' });"}}
    {"timestamp":"2026-10-16T12:00:02.000Z","type":"response_item","payload":{"type":"custom_tool_call_output","call_id":"cx-1","output":[{"type":"input_text","text":"SECRET_CANARY_OUTPUT_7731"}]}}
    {"timestamp":"2026-10-16T12:00:02.000Z","type":"response_item","payload":{"type":"custom_tool_call","call_id":"cx-2","name":"exec","input":"tools.apply_patch({ patch: 'SECRET_CANARY_FILE_CONTENT_7731' });"}}
    {"timestamp":"2026-10-16T12:00:03.000Z","type":"response_item","payload":{"type":"custom_tool_call_output","call_id":"cx-2","output":[{"type":"input_text","text":"SECRET_CANARY_OUTPUT_7731"}]}}
    {"timestamp":"2026-10-16T12:00:03.000Z","type":"response_item","payload":{"type":"custom_tool_call","call_id":"cx-3","name":"exec","input":"tools.update_plan({ plan: 'SECRET_CANARY_PROMPT_7731' });"}}
    {"timestamp":"2026-10-16T12:00:04.000Z","type":"response_item","payload":{"type":"custom_tool_call_output","call_id":"cx-3","output":[{"type":"input_text","text":"SECRET_CANARY_OUTPUT_7731"}]}}
    {"timestamp":"2026-10-16T12:00:04.000Z","type":"response_item","payload":{"type":"function_call","call_id":"cx-4","name":"Edit","arguments":"{\"file_path\":\"src/module-3.mjs\",\"content\":\"SECRET_CANARY_FILE_CONTENT_7731\"}"}}
    {"timestamp":"2026-10-16T12:00:05.000Z","type":"response_item","payload":{"type":"function_call_output","call_id":"cx-4","output":"output SECRET_CANARY_OUTPUT_7731","is_error":false}}
    {"timestamp":"2026-10-16T12:00:05.000Z","type":"response_item","payload":{"type":"function_call","call_id":"cx-5","name":"Write","arguments":"{\"file_path\":\"src/module-4.mjs\",\"content\":\"SECRET_CANARY_FILE_CONTENT_7731\"}"}}
    {"timestamp":"2026-10-16T12:00:06.000Z","type":"response_item","payload":{"type":"function_call_output","call_id":"cx-5","output":"output SECRET_CANARY_OUTPUT_7731","is_error":false}}
    {"timestamp":"2026-10-16T12:00:06.000Z","type":"response_item","payload":{"type":"local_shell_call","call_id":"cx-6","command":"git diff SECRET_CANARY_COMMAND_7731"}}
    {"timestamp":"2026-10-16T12:00:07.000Z","type":"response_item","payload":{"type":"local_shell_call_output","call_id":"cx-6","output":"output SECRET_CANARY_OUTPUT_7731","is_error":false}}
    {"timestamp":"2026-10-16T12:00:07.000Z","type":"response_item","payload":{"type":"function_call","call_id":"cx-7","name":"Read","arguments":"{\"file_path\":\"src/module-6.mjs\"}"}}
    {"timestamp":"2026-10-16T12:00:08.000Z","type":"response_item","payload":{"type":"function_call_output","call_id":"cx-7","output":"output SECRET_CANARY_OUTPUT_7731","is_error":false}}
    {"timestamp":"2026-10-16T12:00:08.000Z","type":"response_item","payload":{"type":"function_call","call_id":"cx-8","name":"Task","arguments":"{\"description\":\"inspect tests\"}"}}
    {"timestamp":"2026-10-16T12:00:09.000Z","type":"response_item","payload":{"type":"function_call_output","call_id":"cx-8","output":"output SECRET_CANARY_OUTPUT_7731","is_error":false}}
    {"timestamp":"2026-10-16T12:00:09.000Z","type":"response_item","payload":{"type":"function_call","call_id":"cx-9","name":"WebSearch","arguments":"{\"query\":\"public API docs\"}"}}
    {"timestamp":"2026-10-16T12:00:10.000Z","type":"response_item","payload":{"type":"function_call_output","call_id":"cx-9","output":"output SECRET_CANARY_OUTPUT_7731","is_error":false}}
    {"timestamp":"2026-10-16T12:00:10.000Z","type":"response_item","payload":{"type":"function_call","call_id":"cx-10","name":"WebFetch","arguments":"{}"}}
    {"timestamp":"2026-10-16T12:00:11.000Z","type":"response_item","payload":{"type":"function_call_output","call_id":"cx-10","output":"output SECRET_CANARY_OUTPUT_7731","is_error":false}}
    {"timestamp":"2026-10-16T12:00:11.000Z","type":"response_item","payload":{"type":"function_call","call_id":"cx-11","name":"mcp__docs__lookup","arguments":"{}"}}
    {"timestamp":"2026-10-16T12:00:12.000Z","type":"response_item","payload":{"type":"function_call_output","call_id":"cx-11","output":"output SECRET_CANARY_OUTPUT_7731","is_error":false}}
    {"timestamp":"2026-10-16T12:00:12.000Z","type":"response_item","payload":{"type":"function_call","call_id":"cx-12","name":"MultiEdit","arguments":"{\"file_path\":\"src/module-11.mjs\",\"content\":\"SECRET_CANARY_FILE_CONTENT_7731\"}"}}
    {"timestamp":"2026-10-16T12:00:13.000Z","type":"response_item","payload":{"type":"function_call_output","call_id":"cx-12","output":"output SECRET_CANARY_OUTPUT_7731","is_error":false}}
    {"timestamp":"2026-10-16T12:00:13.000Z","type":"response_item","payload":{"type":"local_shell_call","call_id":"cx-13","command":"git diff SECRET_CANARY_COMMAND_7731"}}
    {"timestamp":"2026-10-16T12:00:14.000Z","type":"response_item","payload":{"type":"local_shell_call_output","call_id":"cx-13","output":"Permission denied SECRET_CANARY_OUTPUT_7731","is_error":true}}
    {"timestamp":"2026-10-16T12:00:14.000Z","type":"response_item","payload":{"type":"function_call","call_id":"cx-14","name":"Read","arguments":"{\"file_path\":\"src/module-13.mjs\"}"}}
    {"timestamp":"2026-10-16T12:00:15.000Z","type":"response_item","payload":{"type":"function_call_output","call_id":"cx-14","output":"output SECRET_CANARY_OUTPUT_7731","is_error":false}}
    {"timestamp":"2026-10-16T12:00:15.000Z","type":"response_item","payload":{"type":"function_call","call_id":"cx-15","name":"apply_patch","arguments":"{\"file_path\":\"src/module-14.mjs\",\"content\":\"SECRET_CANARY_FILE_CONTENT_7731\"}"}}
    {"timestamp":"2026-10-16T12:00:16.000Z","type":"response_item","payload":{"type":"function_call_output","call_id":"cx-15","output":"output SECRET_CANARY_OUTPUT_7731","is_error":false}}
    {"timestamp":"2026-10-16T12:01:01.000Z","type":"response_item","payload":{"type":"local_shell_call","call_id":"cx-16","command":"npm run build SECRET_CANARY_COMMAND_7731"}}
    {"timestamp":"2026-10-16T12:01:05.000Z","type":"response_item","payload":{"type":"local_shell_call_output","call_id":"cx-16","output":"output SECRET_CANARY_OUTPUT_7731","is_error":false}}
    {"timestamp":"2026-10-16T12:00:17.000Z","type":"response_item","payload":{"type":"function_call","call_id":"cx-17","name":"Agent","arguments":"{\"description\":\"inspect tests\"}"}}
    {"timestamp":"2026-10-16T12:00:18.000Z","type":"response_item","payload":{"type":"function_call_output","call_id":"cx-17","output":"output SECRET_CANARY_OUTPUT_7731","is_error":false}}
    {"timestamp":"2026-10-16T12:00:18.000Z","type":"response_item","payload":{"type":"function_call","call_id":"cx-18","name":"Write","arguments":"{\"file_path\":\"src/module-17.mjs\",\"content\":\"SECRET_CANARY_FILE_CONTENT_7731\"}"}}
    {"timestamp":"2026-10-16T12:00:19.000Z","type":"response_item","payload":{"type":"function_call_output","call_id":"cx-18","output":"output SECRET_CANARY_OUTPUT_7731","is_error":false}}
    {"timestamp":"2026-10-16T12:00:25.000Z","type":"mystery_future_line","payload":{}}
    {"timestamp":"2026-10-16T12:00:26.000Z","type":"mystery_future_line","payload":{}}
    
  • skills/agent-transit/src/agent-transit/samples/cursor-bubbles-synthetic.jsonl14 KBfc7e0bf77106upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/agent-transit/samples/cursor-synthetic.jsonl4.1 KB3d1ec9817daeupstream ↗
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"Read","input":{"file_path":"src/module-0.mjs","content":"SECRET_CANARY_FILE_CONTENT_7731"}}]}}
    {"type":"turn_ended","status":"success"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"StrReplace","input":{"file_path":"src/module-1.mjs","content":"SECRET_CANARY_FILE_CONTENT_7731"}}]}}
    {"type":"turn_ended","status":"success"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"Shell","input":{"command":"git diff SECRET_CANARY_COMMAND_7731"}}]}}
    {"type":"turn_ended","status":"success"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"TodoWrite","input":{"content":"SECRET_CANARY_FILE_CONTENT_7731"}}]}}
    {"type":"turn_ended","status":"success"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"Task","input":{"description":"inspect tests"}}]}}
    {"type":"turn_ended","status":"success"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"WebSearch","input":{"query":"public API docs"}}]}}
    {"type":"turn_ended","status":"success"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"Glob","input":{"pattern":"src/**/*.mjs"}}]}}
    {"type":"turn_ended","status":"success"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"Grep","input":{"pattern":"normalize","path":"src"}}]}}
    {"type":"turn_ended","status":"success"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"Write","input":{"file_path":"src/module-8.mjs","content":"SECRET_CANARY_FILE_CONTENT_7731"}}]}}
    {"type":"turn_ended","status":"success"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"Delete","input":{"file_path":"src/module-9.mjs"}}]}}
    {"type":"turn_ended","status":"success"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"SemanticSearch","input":{"query":"normalize"}}]}}
    {"type":"turn_ended","status":"success"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"ReadLints","input":{"file_path":"src/module-11.mjs"}}]}}
    {"type":"turn_ended","status":"success"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"EditNotebook","input":{"file_path":"src/notebook.ipynb","content":"SECRET_CANARY_FILE_CONTENT_7731"}}]}}
    {"type":"turn_ended","status":"error","error":"SECRET_CANARY_OUTPUT_7731"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"WebFetch","input":{"url":"https://example.test"}}]}}
    {"type":"turn_ended","status":"success"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"Shell","input":{"command":"npm run build SECRET_CANARY_COMMAND_7731"}}]}}
    {"type":"turn_ended","status":"success"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"Read","input":{"file_path":"src/module-15.mjs"}}]}}
    {"type":"turn_ended","status":"success"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"StrReplace","input":{"file_path":"src/module-16.mjs","content":"SECRET_CANARY_FILE_CONTENT_7731"}}]}}
    {"type":"turn_ended","status":"success"}
    {"role":"assistant","message":{"content":[{"type":"text","text":"SECRET_CANARY_PROMPT_7731"},{"type":"tool_use","name":"Task","input":{"description":"inspect tests"}}]}}
    {"type":"turn_ended","status":"success"}
    
  • skills/agent-transit/src/agent-transit/samples/make-agent-samples.mjs4.3 KBa4896d579b53upstream ↗
    import { mkdirSync, writeFileSync } from 'node:fs';
    import { dirname, join } from 'node:path';
    import { fileURLToPath } from 'node:url';
    
    const here = dirname(fileURLToPath(import.meta.url));
    const iso = (n) => new Date(Date.UTC(2026, 9, 16, 12, 0, n)).toISOString();
    const tools = ['Read', 'Glob', 'Grep', 'Edit', 'Write', 'Bash', 'Read', 'Task', 'WebSearch', 'WebFetch', 'mcp__docs__lookup', 'MultiEdit', 'shell', 'Read', 'apply_patch', 'Bash', 'Agent', 'Write'];
    const args = (name, i) => name === 'Bash' || name === 'shell' ? { command: `${i === 15 ? 'npm run build' : 'git diff --stat'} SECRET_CANARY_COMMAND_7731` } : name === 'Task' || name === 'Agent' ? { description: 'inspect tests' } : name === 'WebSearch' ? { query: 'public API docs' } : name === 'Glob' ? { pattern: 'src/**/*.mjs' } : name === 'Grep' ? { pattern: 'normalize', path: 'src' } : name === 'Read' ? { file_path: `src/module-${i}.mjs` } : name === 'Write' || name === 'Edit' || name === 'MultiEdit' || name === 'apply_patch' ? { file_path: `src/module-${i}.mjs`, content: 'SECRET_CANARY_FILE_CONTENT_7731' } : {};
    function claude() {
      const out = [{ _agentFormat: 'claude-code', _comment: 'SYNTHETIC hand-built Claude Code JSONL; prompts, outputs, and file contents contain SECRET_CANARY_PROMPT_7731 / SECRET_CANARY_OUTPUT_7731 / SECRET_CANARY_COMMAND_7731.' }];
      tools.forEach((name, i) => { const id = `cc-${i + 1}`; const t = 1 + i * (i === 15 ? 4 : 1); out.push({ type: 'assistant', timestamp: iso(t), uuid: `u-${i}`, parentUuid: i ? `u-${i - 1}` : null, message: { content: [{ type: 'text', text: 'SECRET_CANARY_PROMPT_7731' }, { type: 'tool_use', id, name, input: args(name, i) }] } }); out.push({ type: 'user', timestamp: iso(t + (i === 15 ? 4 : 1)), uuid: `r-${i}`, parentUuid: `u-${i}`, message: { content: [{ type: 'tool_result', tool_use_id: id, is_error: i === 12, content: i === 12 ? 'permission denied SECRET_CANARY_OUTPUT_7731' : 'result SECRET_CANARY_OUTPUT_7731' }] } }); });
      return out;
    }
    function cursor() {
      const names = ['Read', 'StrReplace', 'Shell', 'TodoWrite', 'Task', 'WebSearch', 'Glob', 'Grep', 'Write', 'Delete', 'SemanticSearch', 'ReadLints', 'EditNotebook', 'WebFetch', 'Shell', 'Read', 'StrReplace', 'Task'];
      const out = [];
      names.forEach((name, i) => {
        out.push({ role: 'assistant', message: { content: [{ type: 'text', text: 'SECRET_CANARY_PROMPT_7731' }, { type: 'tool_use', name, input: { file_path: `src/module-${i}.mjs`, command: `${i === 14 ? 'npm run build' : 'git diff'} SECRET_CANARY_COMMAND_7731`, content: 'SECRET_CANARY_FILE_CONTENT_7731' } }] } });
        out.push({ type: 'turn_ended', status: i === 12 ? 'error' : 'success', ...(i === 12 ? { error: 'SECRET_CANARY_OUTPUT_7731' } : {}) });
      });
      return out;
    }
    function codex() {
      const out = [{ _agentFormat: 'codex', _comment: 'SYNTHETIC hand-built Codex rollout JSONL; SECRET_CANARY_PROMPT_7731 SECRET_CANARY_OUTPUT_7731 SECRET_CANARY_COMMAND_7731' }, { timestamp: iso(0), type: 'session_meta', payload: { id: 'synthetic-session' } }];
      tools.forEach((name, i) => { const id = `cx-${i + 1}`; const t = 1 + i * (i === 15 ? 4 : 1); const wrapped = i < 3; const inner = ['exec_command', 'apply_patch', 'update_plan'][i]; const payload = wrapped ? { type: 'custom_tool_call', call_id: id, name: 'exec', input: `tools.${inner}({ value: 'SECRET_CANARY_COMMAND_7731' });` } : name === 'Bash' || name === 'shell' ? { type: 'local_shell_call', call_id: id, command: `${i === 15 ? 'npm run build' : 'git diff'} SECRET_CANARY_COMMAND_7731` } : { type: 'function_call', call_id: id, name, arguments: JSON.stringify(args(name, i)) }; out.push({ timestamp: iso(t), type: 'response_item', payload }); out.push({ timestamp: iso(t + (i === 15 ? 4 : 1)), type: 'response_item', payload: { type: wrapped ? 'custom_tool_call_output' : name === 'Bash' || name === 'shell' ? 'local_shell_call_output' : 'function_call_output', call_id: id, output: wrapped ? [{ type: 'input_text', text: 'SECRET_CANARY_OUTPUT_7731' }] : i === 12 ? 'Permission denied SECRET_CANARY_OUTPUT_7731' : 'output SECRET_CANARY_OUTPUT_7731', is_error: i === 12 } }); });
      out.push({ timestamp: iso(25), type: 'mystery_future_line', payload: {} });
      return out;
    }
    mkdirSync(here, { recursive: true });
    for (const [name, data] of [['claude-code', claude()], ['codex', codex()], ['cursor', cursor()]]) writeFileSync(join(here, `${name}-synthetic.jsonl`), data.map((x) => JSON.stringify(x)).join('\n') + '\n');
    
  • skills/agent-transit/src/agent-transit/scripts/export-cursor.mjs9.1 KBbbffe73bc6b4upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/agent-transit/scripts/find-session.mjs2.6 KBe1d3fac2435eupstream ↗
    import { readdirSync, readFileSync, statSync } from 'node:fs';
    import { homedir } from 'node:os';
    import { join, resolve } from 'node:path';
    
    const source = process.argv[process.argv.indexOf('--source') + 1];
    if (!['claude-code', 'codex', 'cursor'].includes(source)) throw new Error('usage: node find-session.mjs --source claude-code|codex|cursor');
    const cwd = resolve(process.cwd()), home = homedir();
    const newest = (items) => items.sort((a, b) => b.mtime - a.mtime)[0];
    const mtime = (path) => { try { return statSync(path).mtimeMs; } catch { return 0; } };
    const entries = (dir) => { try { return readdirSync(dir, { withFileTypes: true }); } catch { return []; } };
    
    if (source === 'cursor') {
      const root = join(home, '.cursor', 'projects'), matches = [];
      for (const project of entries(root)) {
        if (!project.isDirectory()) continue;
        const transcripts = join(root, project.name, 'agent-transcripts');
        for (const session of entries(transcripts)) {
          if (!session.isDirectory()) continue;
          const path = join(transcripts, session.name, `${session.name}.jsonl`);
          try { if (statSync(path).isFile()) matches.push({ path, mtime: mtime(path) }); } catch {}
        }
      }
      const match = newest(matches);
      if (match) console.log(match.path);
      else console.error('no Cursor agent-transcripts found; Cursor keeps chats in its state.vscdb instead. Export one (metadata only): node <skill>/scripts/export-cursor.mjs --list, then --composer ID');
    } else if (source === 'codex') {
      const root = join(home, '.codex', 'sessions'), matches = [];
      const walk = (dir) => {
        for (const entry of entries(dir)) {
          const path = join(dir, entry.name);
          if (entry.isDirectory()) walk(path);
          else if (entry.isFile() && entry.name.endsWith('.jsonl')) {
            try {
              const first = readFileSync(path, 'utf8').split(/\r?\n/, 1)[0];
              const line = JSON.parse(first);
              if (line.type === 'session_meta' && (line.payload?.cwd || line.cwd) === cwd) matches.push({ path, mtime: mtime(path) });
            } catch {}
          }
        }
      };
      walk(root); const match = newest(matches); if (match) console.log(match.path);
    } else {
      const root = join(home, '.claude', 'projects'), encoded = cwd.replaceAll('/', '-'), matches = [];
      const walk = (dir) => {
        for (const entry of entries(dir)) {
          const path = join(dir, entry.name);
          if (entry.isDirectory()) walk(path);
          else if (entry.isFile() && entry.name.endsWith('.jsonl') && path.includes(encoded)) matches.push({ path, mtime: mtime(path) });
        }
      };
      walk(root); const match = newest(matches); if (match) console.log(match.path);
    }
    
  • skills/agent-transit/src/ci-transit/adapters/normalize-gh.mjs6.9 KB4c87b1a441ffupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/adapters/fetch-datadog-api.mjs2.6 KBea5b1f6533bfupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/adapters/fetch-posthog-api.mjs3.9 KB76c8ca090c4eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/adapters/fetch-tempo.mjs4.7 KB7a0bef04c864upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/adapters/fetch-xray.mjs3.2 KBdfcac55469d9upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/adapters/normalize.mjs30 KBb78a5ad116e1upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/contracts/extractors/class-validator.mjs2.7 KBce2e7e4df98fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/contracts/extractors/express.mjs2.3 KB02bdefe5cab5upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/contracts/extractors/index.mjs729 B9e72323e29d5upstream ↗
    // Extractor registry. Add one by exporting {name, kind, test(src, file), symbols?(src), routes?(src)} and
    // listing it here. symbols() returns named shapes; routes() returns http/rpc/topic entries that point at them.
    import zod from './zod.mjs';
    import joi from './joi.mjs';
    import classValidator from './class-validator.mjs';
    import tsTypes from './ts-types.mjs';
    import express from './express.mjs';
    import nest from './nest.mjs';
    import topicProducer from './topic-producer.mjs';
    import openapi from './openapi.mjs';
    import protobuf from './protobuf.mjs';
    export const EXTRACTORS = [zod, joi, classValidator, tsTypes, express, nest, topicProducer, openapi, protobuf];
    export const EXTRACTOR_VERSION = 'transit-contracts-2';
    
  • skills/agent-transit/src/request-transit/contracts/extractors/joi.mjs2.5 KB837b52093cf8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/contracts/extractors/nest.mjs3.5 KBc261bf4d5f63upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/contracts/extractors/openapi.mjs2.7 KB31b21363b8faupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/contracts/extractors/protobuf.mjs4.4 KB6bf35f6ea60cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/contracts/extractors/topic-producer.mjs998 Bc8c7b3acfe39upstream ↗
    // Message producers: <client>.emit('topic', payload satisfies T) or publish<T>('topic', ...).
    // Gives the topic's shape so consumers without their own schema can fall back to it.
    import { closeOf, lineOf } from '../text.mjs';
    export default {
      name: 'topic-producer', kind: 'route',
      test: (src) => /\.(emit|publish|send)\s*(<[^>]*>)?\(\s*['"`]/.test(src),
      routes(src) {
        const out = [];
        for (const m of src.matchAll(/\.(?:emit|publish|send)\s*(?:<\s*([\w$]+)\s*>)?\(\s*['"`]([\w.-]+)['"`]/g)) {
          const open = m.index + m[0].indexOf('('), args = src.slice(open, closeOf(src, open) + 1);
          const sat = args.match(/\bsatisfies\s+([\w$]+)/) || args.match(/\bas\s+([\w$]+)\s*\)/);
          const symbol = m[1] || (sat && sat[1]);
          if (!symbol) continue;
          out.push({ kind: 'topic', topic: m[2], role: 'producer', line: lineOf(src, m.index), request: { symbol, via: m[1] ? 'publish<T>' : 'satisfies' }, response: null, framework: 'producer' });
        }
        return out;
      },
    };
    
  • skills/agent-transit/src/request-transit/contracts/extractors/ts-types.mjs1.2 KBe71fe1fe6c18upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/contracts/extractors/zod.mjs3.2 KBc7d0ba285d09upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/contracts/indexer.mjs13 KB11e06885f806upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/contracts/match.mjs4.8 KB5af244c670fdupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/contracts/schema.mjs3.3 KB3a93c6e1fc80upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/agent-transit/src/request-transit/contracts/text.mjs3.5 KB2934e7f69dafupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

Plain-text copy, no login: /skill-bundles/israelvicars-transit-skills-agent-transit.md

// REPO STATS

2 stars