From e9fd7de55657d64db16730f96fab813b19b31da8 Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Wed, 14 Oct 2026 09:40:12 -0700 Subject: [PATCH] Add concurrency caps, the two-pane web UI, AGENTS.md, and plan status --- .gitignore | 1 + AGENTS.md | 28 + api/openapi.yaml | 35 +- docs/operations.md | 6 +- docs/plan.md | 44 +- internal/agents/inbox.go | 13 +- internal/agents/manager.go | 268 ++++- internal/agents/manager_test.go | 113 +- internal/agents/ratelimit_internal_test.go | 78 ++ internal/api/api.gen.go | 186 +++- .../TestCommitIdentityHooksAndReport.jsonl | 78 +- .../testdata/transcripts/TestInterrupt.jsonl | 39 +- .../transcripts/TestResume-first.jsonl | 16 +- .../transcripts/TestResume-resumed.jsonl | 18 +- .../transcripts/TestStopHookBlocks.jsonl | 30 +- internal/server/server.go | 35 +- internal/server/server_test.go | 42 + internal/store/queue.go | 99 ++ internal/store/store.go | 24 + web/dist/api.gen.js | 6 + web/dist/api.gen.js.map | 1 + web/dist/api.js | 48 + web/dist/api.js.map | 1 + web/dist/app.js | 60 ++ web/dist/app.js.map | 1 + web/dist/dom.js | 45 + web/dist/dom.js.map | 1 + web/dist/inbox.js | 250 +++++ web/dist/inbox.js.map | 1 + web/dist/navbar.js | 92 ++ web/dist/navbar.js.map | 1 + web/dist/panes.js | 44 + web/dist/panes.js.map | 1 + web/dist/state.js | 133 +++ web/dist/state.js.map | 1 + web/dist/task.js | 230 ++++ web/dist/task.js.map | 1 + web/dist/tasks.js | 49 + web/dist/tasks.js.map | 1 + web/embed.go | 5 +- web/fonts/LICENSE | 93 ++ ...erlegible-mono-latin-ext-wght-italic.woff2 | Bin 0 -> 11440 bytes ...erlegible-mono-latin-ext-wght-normal.woff2 | Bin 0 -> 10740 bytes ...-hyperlegible-mono-latin-wght-italic.woff2 | Bin 0 -> 19084 bytes ...-hyperlegible-mono-latin-wght-normal.woff2 | Bin 0 -> 17752 bytes web/index.html | 37 +- web/package-lock.json | 392 +++++++ web/package.json | 12 + web/src/api.gen.ts | 978 ++++++++++++++++++ web/src/api.ts | 89 ++ web/src/app.ts | 66 ++ web/src/dom.ts | 58 ++ web/src/inbox.ts | 315 ++++++ web/src/navbar.ts | 107 ++ web/src/panes.ts | 55 + web/src/state.ts | 153 +++ web/src/task.ts | 271 +++++ web/src/tasks.ts | 73 ++ web/style.css | 833 +++++++++++++++ web/tsconfig.json | 16 + 60 files changed, 5419 insertions(+), 254 deletions(-) create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 internal/agents/ratelimit_internal_test.go create mode 100644 internal/store/queue.go create mode 100644 web/dist/api.gen.js create mode 100644 web/dist/api.gen.js.map create mode 100644 web/dist/api.js create mode 100644 web/dist/api.js.map create mode 100644 web/dist/app.js create mode 100644 web/dist/app.js.map create mode 100644 web/dist/dom.js create mode 100644 web/dist/dom.js.map create mode 100644 web/dist/inbox.js create mode 100644 web/dist/inbox.js.map create mode 100644 web/dist/navbar.js create mode 100644 web/dist/navbar.js.map create mode 100644 web/dist/panes.js create mode 100644 web/dist/panes.js.map create mode 100644 web/dist/state.js create mode 100644 web/dist/state.js.map create mode 100644 web/dist/task.js create mode 100644 web/dist/task.js.map create mode 100644 web/dist/tasks.js create mode 100644 web/dist/tasks.js.map create mode 100644 web/fonts/LICENSE create mode 100644 web/fonts/atkinson-hyperlegible-mono-latin-ext-wght-italic.woff2 create mode 100644 web/fonts/atkinson-hyperlegible-mono-latin-ext-wght-normal.woff2 create mode 100644 web/fonts/atkinson-hyperlegible-mono-latin-wght-italic.woff2 create mode 100644 web/fonts/atkinson-hyperlegible-mono-latin-wght-normal.woff2 create mode 100644 web/package-lock.json create mode 100644 web/package.json create mode 100644 web/src/api.gen.ts create mode 100644 web/src/api.ts create mode 100644 web/src/app.ts create mode 100644 web/src/dom.ts create mode 100644 web/src/inbox.ts create mode 100644 web/src/navbar.ts create mode 100644 web/src/panes.ts create mode 100644 web/src/state.ts create mode 100644 web/src/task.ts create mode 100644 web/src/tasks.ts create mode 100644 web/style.css create mode 100644 web/tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..764912b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/web/node_modules/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..1f6c05a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,28 @@ +# Rules for agents working on byre + +These are requirements, not preferences. Changing one needs the owner's agreement first. + +## Workflow + +- Deploy before handing off. Any change that affects the running server or the web UI is deployed to the service before you stop and report, so the owner can look at it immediately: `go generate ./web` if web sources changed, then `go install ./cmd/byre && launchctl kickstart -k gui/$(id -u)/run.fc.byre`. Then confirm it came up: the tail of `~/Library/Logs/byre.log`, and `GET /api/admin/status` through the `tailscale serve` URL (`tailscale serve status` shows it). Report what you deployed and what you checked. +- `docs/operations.md` describes the layout, data, service and APIs; keep it current with every change. + +## Web UI layout + +- Exactly two panes, side by side, each exactly half the width, at every window size. Never three or more columns, never a sidebar, never a resizable or collapsible split. +- One navbar, across the top. +- Navigation happens inside a pane: each pane has tabs, and any view can be shown in either pane. A new view is a new tab, never a new column or region. +- Overlays (the task switcher) are allowed; they sit above the panes and don't change the layout. + +## Web UI look + +- One font everywhere: Atkinson Hyperlegible Mono, self-hosted from `web/fonts/`. No font CDNs. +- High contrast means readable text, not harshness. Body text is at least 7:1 against its background and secondary text at least 4.5:1, on soft backgrounds (off-white, dark slate; never pure black or white). Keep the frame quiet: 1px borders in a subtle tone, no dashed or heavy borders, no inverted blocks for selection, colour used sparingly for state and accent. The palette lives in the custom properties at the top of `web/style.css`; change it there. +- Never show agent cost, turn duration, or turn counts anywhere in the UI. They stay in the event log for the API, not on screen. + +## Web UI code + +- Frameworkless. TypeScript in `web/src/`, compiled file-by-file to ES modules in `web/dist/` with `tsc`. No bundler, no framework, no runtime dependencies. +- API types come from `api/openapi.yaml` via `openapi-typescript` (`web/src/api.gen.ts`); call the API through the typed client in `web/src/api.ts`, never raw `fetch`. +- `web/dist/` is committed so `go install` needs no Node. Regenerate it with `go generate ./web` whenever `web/src/` or the spec changes. +- Every view mounted into a pane returns a dispose function that removes its state subscriptions. diff --git a/api/openapi.yaml b/api/openapi.yaml index 6f33001..9655cce 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -125,6 +125,25 @@ paths: "404": { $ref: "#/components/responses/Error" } "409": { $ref: "#/components/responses/Error" } + /api/tasks/{taskId}/events: + parameters: + - { name: taskId, in: path, required: true, schema: { type: string } } + get: + operationId: listTaskEvents + description: The task's events after a seq, oldest first. Page by passing the last seq back as after. + parameters: + - { name: after, in: query, required: false, schema: { type: integer, format: int64 } } + - { name: limit, in: query, required: false, schema: { type: integer, minimum: 1, maximum: 1000 } } + responses: + "200": + description: Up to limit events (default 500). + content: + application/json: + schema: + type: array + items: { $ref: "#/components/schemas/Event" } + "404": { $ref: "#/components/responses/Error" } + /api/tasks/{taskId}/sessions: parameters: - { name: taskId, in: path, required: true, schema: { type: string } } @@ -368,7 +387,10 @@ components: vendor: { type: string, enum: [claude] } model: { type: string, description: Requested model; empty means the vendor's default. } vendor_session_id: { type: string } - state: { type: string, enum: [idle, running, completed] } + state: + type: string + enum: [idle, queued, running, completed] + description: queued means a turn is waiting for a free slot under the concurrency caps. role: { type: string, enum: [implementer] } created_by: { type: string } created_at: { type: string, format: date-time } @@ -415,7 +437,7 @@ components: task_id: { type: string } agent_session_id: { type: string } thread_id: { type: string } - kind: { type: string, enum: [question, report, session_error] } + kind: { type: string, enum: [question, report, session_error, rate_limited] } priority: { type: string, enum: [blocked, gate, review, fyi] } blocking: { type: boolean } payload: @@ -424,6 +446,7 @@ components: question: question, why_blocked, options, recommended_option, attachments. report: done, open_questions, next_steps, risks, deviations. session_error: error. + rate_limited: vendor, rate_limit (the vendor's rate-limit status), error. state: { $ref: "#/components/schemas/InboxState" } snoozed_until: { type: string, format: date-time } created_at: { type: string, format: date-time } @@ -491,9 +514,15 @@ components: Status: type: object - required: [started_at, db_bytes, last_event_seq, projects, active_tasks, sse_clients, live_agents] + required: [started_at, db_bytes, last_event_seq, projects, active_tasks, sse_clients, live_agents, running_turns, queued_turns, rate_limits] properties: live_agents: { type: integer, description: Agent processes currently running. } + running_turns: { type: integer } + queued_turns: { type: integer } + rate_limits: + type: object + description: The latest rate-limit status each vendor reported, keyed by vendor. + additionalProperties: { type: object } started_at: { type: string, format: date-time } db_bytes: { type: integer, format: int64 } last_event_seq: { type: integer, format: int64 } diff --git a/docs/operations.md b/docs/operations.md index 344d753..4350766 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -10,7 +10,7 @@ - `internal/agents`: the agent session manager. It owns the running Claude processes and records their activity as events. - `internal/claude`: the Claude Code stream-json client (see `docs/claude-protocol.md`). - `internal/report`: the `report` MCP tool and the Stop hook that requires it each turn. -- `web/`: the SPA, embedded into the binary. `byre serve --dev` serves `./web` from disk instead (run from the repo root). +- `web/`: the SPA, frameworkless TypeScript compiled file-by-file to ES modules (no bundler). `web/src/*.ts` is the source; `web/src/api.gen.ts` holds types generated from `api/openapi.yaml`, and `web/src/api.ts` is the typed client over them. `go generate ./web` runs `npm ci`, regenerates the types and compiles `web/dist/`, which is committed so `go install` needs no Node. The binary embeds `index.html`, `style.css`, `dist/` and `src/` (for source maps). `byre serve --dev` serves `./web` from disk instead (run from the repo root); pair it with `npm --prefix web exec tsc -- --watch`. ## Data @@ -49,6 +49,10 @@ curl -H 'Tailscale-User-Login: you@example.com' -H 'Tailscale-User-Name: You' ht byre chooses the Claude session ID (`vendor_session_id`) up front, so every session can be resumed. Processes start on demand: the first message after a restart resumes the session with `--resume`. Sessions are `running` from the moment a turn is sent until its `result` arrives; on startup, every session still marked `running` was cut off mid-turn, and byre resumes it and sends a message telling the agent to re-check the working tree. Stopping the server kills all agent processes. Completing a task kills its sessions and marks them `completed`. +Running turns are capped globally and per vendor (`DefaultLimits` in `internal/agents/manager.go`). A turn that would exceed a cap leaves its session `queued`, with the message held in the `turn_queue` table; whenever a turn ends, a process exits, or a task completes, the oldest queued turns whose vendor has room start. Interrupting a `queued` session cancels its queued turn. Idle sessions with a live process don't count against the caps. Whether a turn resumes the Claude conversation is decided by whether the session has ever sent one. + +When Claude Code reports a rate-limit status of `rejected` and the turn then fails, the inbox gets a `rate_limited` item carrying the vendor's status (including when the limit resets) instead of a generic error. `GET /api/admin/status` reports running and queued turns and the latest rate-limit status each vendor sent. + Agent activity becomes events with `agent_session_id` set: `user.message`, `agent.message`, `agent.thinking`, `agent.tool_call`, `agent.tool_result`, `agent.report`, `turn.ended`, and `session.state`. Anything else Claude Code emits (rate-limit status, task progress, state changes) is recorded unchanged as `agent.vendor`. ## Inbox diff --git a/docs/plan.md b/docs/plan.md index 0096ece..cf25be8 100644 --- a/docs/plan.md +++ b/docs/plan.md @@ -1,6 +1,4 @@ -# Agent Manager — Plan - -Working name: `agentmgr` (placeholder; final name TBD). +# byre — Plan A self-hosted Go server on a home Mac Studio that manages third-party coding agents (Claude Code, Codex) across projects and tasks, exposed to a SPA over Tailscale. Built for its author first; multi-user and multi-device from the start. @@ -8,6 +6,8 @@ A self-hosted Go server on a home Mac Studio that manages third-party coding age This plan reflects decisions already agreed. Do not deviate from it without asking first. If an approach turns out not to work, a decision needs revisiting, or something requires reverting work or a destructive action, stop and ask rather than choosing an alternative. Items under "Open items" are not yet decided; raise them when they become blocking. +`AGENTS.md` holds the standing rules for working on byre (deploying before handoff, UI layout and look, web code). `docs/operations.md` describes how the built system works and runs. "Build sequence" below says what is built. + ## Goals - Manage many concurrent agent sessions across projects (repos) and tasks (worktrees). @@ -39,7 +39,7 @@ This plan reflects decisions already agreed. Do not deviate from it without aski ### Server - Go, single binary, run under launchd on the Mac Studio. -- Binds to `127.0.0.1` only. Fronted by `tailscale serve`, which provides HTTPS with a valid `*.ts.net` certificate. +- Binds to `127.0.0.1` only. Fronted by `tailscale serve` on its own HTTPS port of the machine's `*.ts.net` name (not a path prefix, which breaks the SPA's absolute paths), with a valid certificate. Clients get HTTP/2 and SSE passes through unbuffered. - SPA served from `embed.FS` in release builds and `os.DirFS` behind a dev flag. Same handler either way. - Storage: SQLite via `modernc.org/sqlite` (pure Go, no CGO), WAL mode. @@ -47,11 +47,11 @@ This plan reflects decisions already agreed. Do not deviate from it without aski - Identity comes from Tailscale's injected headers (`Tailscale-User-Login`, `Tailscale-User-Name`). Trust them only on requests arriving from loopback via the serve proxy. - Multi-user is a real requirement. Every mutating action records the acting user. -- Config maps each Tailscale login to a git name and email, used for commit authorship and co-author trailers. +- Users are created on first request. Each sets the git name and email used for commit authorship and co-author trailers through the API (`PUT /api/me`); agents can't start until it is set. ### API -- REST, OpenAPI spec-first. Generate the TypeScript client for the SPA from the spec; a Swift client can be generated later for the macOS app. +- REST, OpenAPI spec-first (`api/openapi.yaml`). Go server types and routing are generated with `oapi-codegen`; the SPA's TypeScript types are generated with `openapi-typescript` and used through a small typed client. A Swift client can be generated later for the macOS app. - Real-time state: one multiplexed SSE stream per client. Every event carries a monotonic sequence number; clients resume with `Last-Event-ID`. - Terminals are the only WebSocket use: one WebSocket per open terminal. @@ -71,8 +71,8 @@ This plan reflects decisions already agreed. Do not deviate from it without aski ### Concurrency and usage -- Global and per-vendor caps on concurrently running agent sessions; excess work queues. -- Usage visibility per task, workflow stage, and vendor. +- Global and per-vendor caps on concurrently running agent turns (idle sessions don't count); excess turns queue. +- Usage (cost, duration, turns) is recorded per turn in the event log and available through the API per task, workflow stage, and vendor. The UI never shows it. - Vendor rate-limit errors surface as their own inbox state rather than as generic failures. --- @@ -133,7 +133,7 @@ Validation: reject malformed calls with a corrective message (for example, an `a 1. Claude: a Stop hook blocks stopping until `report` has been called this turn, injecting an instruction to call it. 2. Codex (or any vendor without a stop gate): on turn completion with no report, the server automatically sends a follow-up turn asking for one. -3. After N failed nudges, the review agent synthesizes a report from the transcript and diff. The UI marks it as synthesized, not self-reported. +3. After the nudge limit (`maxNudges` in `internal/report/gate.go`), the review agent synthesizes a report from the transcript and diff. The UI marks it as synthesized, not self-reported. The same mechanism bounces questions asked in plain prose (a final message that asks something without an `ask_human` call). Expect to tune strictness; overly strict validation produces nudge loops, overly lenient produces filler reports. @@ -320,7 +320,7 @@ Diff and stats are computed between the resulting tree and the merge-base with t ### Snapshots - Built with the same temp-index technique, then `git commit-tree` on the resulting tree. -- Stored under hidden refs: `refs/agentmgr/tasks//snap/`. Git deduplicates blobs, so frequent snapshots are cheap. +- Stored under hidden refs: `refs/byre/tasks//snap/`. Git deduplicates blobs, so frequent snapshots are cheap. - A snapshot pairs the code commit with the conversation position (`event_seq`). - Triggers: after every tool call (PostToolUse hook where available; shell commands can change files, so file-editing tools alone are not enough), at turn end, on `ask_human`, and on a timer as a backstop. Skip if the tree sha is unchanged. - Retention: keep everything until task completion. @@ -356,7 +356,7 @@ An explicit human action. Removes the worktree, the task branch, and the task's ## History -- On each squash merge, attach a git note under `refs/agentmgr/notes` (or `refs/notes/agentmgr`) containing: the plan, the handoff bundle, review summaries, eval results, and resolved inbox decisions. Full transcripts are referenced by task ID. +- On each squash merge, attach a git note under `refs/notes/byre` containing: the plan, the handoff bundle, review summaries, eval results, and resolved inbox decisions. Full transcripts are referenced by task ID. - Notes don't push or fetch by default; the merge cycle pushes the notes ref explicitly, and setup configures fetching it. - Archived tasks are searchable across transcripts, artifacts, and decisions. @@ -439,9 +439,10 @@ Read-only, scoped to project and worktree roots. ### UI -- Split-screen layout. -- Fast project and task switching (command palette). -- SPA framework not yet chosen. +- Two panes, always side by side at exactly half the width each, under one top navbar. Each pane has tabs and can show any view; new views (diff, files, terminal, preview) become tabs, never columns. `AGENTS.md` holds the binding layout and look rules. +- Fast task switching: a ⌘K switcher in the navbar, plus a Tasks view. +- Frameworkless: TypeScript compiled file-by-file to ES modules, no bundler. +- Atkinson Hyperlegible Mono throughout, self-hosted; high-contrast text on a quiet palette. --- @@ -462,6 +463,8 @@ Issues identified in review that implementation must account for: ### Spike (first) +**Status: done.** `internal/claude` is the client; `docs/claude-protocol.md` records the protocol; `internal/claude/session_test.go` exercises each capability against the real CLI and records transcripts into `internal/claude/testdata/transcripts/`, with the account email scrubbed. Fork is wired in but untested. + A Go client for the Claude Code CLI's stream-json control protocol that can: - Start a session, stream events, send a message. @@ -475,8 +478,11 @@ Record transcripts from the spike as the seed of the contract test suite. ### Milestones - **M0 — Skeleton.** launchd service, tailscale serve auth, SQLite schema, OpenAPI spec, projects, tasks, worktree create/delete, event log, SSE stream with resume, embedded SPA shell, basic server observability. + - **Status: done**, and running as a service on the Mac Studio. Observability is exposed through `GET /api/admin/status` only; the SPA has no admin view yet. - **M1 — One agent end to end (Claude).** Sessions, streaming, messaging, full-interface rendering for Claude, MCP tools (`ask_human`, `report`), report enforcement, inbox with all views and response types, web push notifications, restart-and-resume, concurrency caps. -- **M2 — Seeing the work.** Temp-index diffs and stats, snapshots, timeline scrubbing, terminals, file browser, split screen, previews. + - **Done:** sessions, streaming, messaging, interrupt; `ask_human` and `report` with validation; Stop-hook report enforcement; inbox with global, project, and task views and every response type, answered as one batch and delivered to the agent as one message; restart-and-resume; global and per-vendor caps with queueing; rate limits as their own inbox state; the web UI (two panes, task switcher, transcript, inbox). + - **Not done:** full-interface rendering beyond messages, tool calls, and interrupt (slash commands, plan mode, model switching, compaction, todo lists, subagent views, image input, `@`-file mentions, Markdown and Mermaid rendering); web push notifications; bouncing questions asked in plain prose; the synthesized-report fallback (needs the M3 review agent); the session instructions from "Session configuration" (no pushing, no other branches, no git maintenance, no self-attribution; only the attribution setting is applied); pinning the CLI version (auto-update is disabled, the version isn't pinned). +- **M2 — Seeing the work.** Temp-index diffs and stats, snapshots, timeline scrubbing, terminals, file browser, previews. - **M3 — Second vendor and review.** Codex adapter with full-interface rendering, review profiles, review triggers, visual review output, cross-vendor review. - **M4 — Merging and history.** Full merge cycle with squash, authorship and co-author trailers, drafted commit messages, merge-resolution stage, git notes, task completion and archive, archive search, GitHub Issues integration. - **M5 — Workflows.** Workflow definitions and storage, stages, gates, bounded loops, plan artifact, eval stages, task intake, mid-task swapping. @@ -487,15 +493,11 @@ Record transcripts from the spike as the seed of the contract test suite. ## Open items -- Raised in critique, not decided: build the MCP server and inbox first against manually launched agents, to validate the structured-question loop before investing in orchestration. - Verify Codex app-server support for forking threads. -- Confirm the exact Claude Code setting for disabling co-author attribution in the CLI version we pin. -- Confirm `tailscale serve` behavior for HTTP/2 and for exposing per-task preview ports. +- Confirm `tailscale serve` behavior for exposing per-task preview ports. - Terminal persistence across server restarts: accept loss, or back terminals with tmux? - Format of per-project setup config (setup script, port allocation, run/capture config for reviews). - Simulator allocation strategy for parallel tasks and review agents. - Periodic review thresholds (defaults for time and diff size). -- Report-enforcement nudge limit N. - Default limits for just-in-time fan-out (depth, concurrency, budget). -- SPA framework. -- Final app name. +- Task navigation: keep both the ⌘K switcher and the Tasks view, or only one. diff --git a/internal/agents/inbox.go b/internal/agents/inbox.go index 7a45a5e..36f6bee 100644 --- a/internal/agents/inbox.go +++ b/internal/agents/inbox.go @@ -178,6 +178,15 @@ func (m *Manager) fileError(l *live, msg string) { } } +func (m *Manager) fileRateLimited(l *live, info map[string]any, failure string) { + id := store.NewID() + payload := map[string]any{"vendor": string(l.vendor), "rate_limit": info, "error": failure} + err := m.store.CreateInboxItem(context.Background(), m.newItem(l, id, id, api.InboxItemKindRateLimited, api.Blocked, true, payload)) + if err != nil { + slog.Error("file rate limit", "session", l.id, "err", err) + } +} + func (m *Manager) newItem(l *live, id, thread string, kind api.InboxItemKind, priority api.InboxItemPriority, blocking bool, payload map[string]any) api.InboxItem { return api.InboxItem{ Id: id, @@ -228,7 +237,7 @@ func (m *Manager) Deliver(ctx context.Context, sessionID string) error { return err } - _, err = m.turn(ctx, sess, task, pending[0].Response.User, deliveryMessage(pending), true) + _, err = m.turn(ctx, sess, task, pending[0].Response.User, deliveryMessage(pending)) if errors.Is(err, ErrConflict) { return nil } @@ -258,6 +267,8 @@ func deliveryMessage(pending []store.Pending) string { fmt.Fprintf(b, "Your report %s\n", p.Item.Id) case api.InboxItemKindSessionError: fmt.Fprintf(b, "Your error %s: %s\n", p.Item.Id, p.Item.Payload["error"]) + case api.InboxItemKindRateLimited: + fmt.Fprintf(b, "Your rate-limited turn %s: %s\n", p.Item.Id, p.Item.Payload["error"]) } switch r.Type { diff --git a/internal/agents/manager.go b/internal/agents/manager.go index d157457..24072bb 100644 --- a/internal/agents/manager.go +++ b/internal/agents/manager.go @@ -26,28 +26,48 @@ const agentActor = "agent" const restartNotice = "byre restarted while you were mid-turn, so your last turn was cut off. Re-check the working tree (git status and the files you were editing) before continuing where you left off." -type Manager struct { - store *store.Store +type Limits struct { + Global int + PerVendor map[api.AgentSessionVendor]int +} - mu sync.Mutex - live map[string]*live - closing bool +var DefaultLimits = Limits{ + Global: 8, + PerVendor: map[api.AgentSessionVendor]int{api.Claude: 6}, +} + +type Manager struct { + store *store.Store + limits Limits + + mu sync.Mutex + live map[string]*live + closing bool + rateLimits map[api.AgentSessionVendor]map[string]any deliverMu sync.Mutex + capMu sync.Mutex } type live struct { sess *claude.Session gate *report.Gate id string + vendor api.AgentSessionVendor taskID string projectID string stopping bool interrupted bool + rateLimited map[string]any } -func New(st *store.Store) *Manager { - return &Manager{store: st, live: map[string]*live{}} +func New(st *store.Store, limits Limits) *Manager { + return &Manager{ + store: st, + limits: limits, + live: map[string]*live{}, + rateLimits: map[api.AgentSessionVendor]map[string]any{}, + } } func (m *Manager) Create(ctx context.Context, task api.Task, creator api.User, prompt, model string) (api.AgentSession, error) { @@ -87,7 +107,7 @@ func (m *Manager) Create(ctx context.Context, task api.Task, creator api.User, p return api.AgentSession{}, err } - return m.turn(ctx, sess, task, creator.Login, prompt, false) + return m.turn(ctx, sess, task, creator.Login, prompt) } func (m *Manager) Send(ctx context.Context, sessionID, actor, text string) (api.AgentSession, error) { @@ -105,7 +125,7 @@ func (m *Manager) Send(ctx context.Context, sessionID, actor, text string) (api. return api.AgentSession{}, err } - return m.turn(ctx, sess, task, actor, text, true) + return m.turn(ctx, sess, task, actor, text) } func (m *Manager) Interrupt(ctx context.Context, sessionID string) (api.AgentSession, error) { @@ -114,6 +134,10 @@ func (m *Manager) Interrupt(ctx context.Context, sessionID string) (api.AgentSes return api.AgentSession{}, err } + if sess.State == api.AgentSessionStateQueued { + return m.cancelQueued(ctx, sess) + } + m.mu.Lock() l := m.live[sessionID] m.mu.Unlock() @@ -159,13 +183,20 @@ func (m *Manager) StopTask(ctx context.Context, taskID, actor string) error { l.sess.Kill() } + if sess.State == api.AgentSessionStateQueued { + err = m.store.DeleteQueuedTurn(ctx, sess.Id) + if err != nil { + return err + } + } + err = m.store.SetSessionState(ctx, sess.Id, api.AgentSessionStateCompleted, actor, nil) if err != nil { return err } } - return nil + return m.drain(ctx) } func (m *Manager) Recover(ctx context.Context) error { @@ -180,7 +211,7 @@ func (m *Manager) Recover(ctx context.Context) error { return err } - l, err := m.ensureLive(ctx, sess, task, true) + l, err := m.ensureLive(ctx, sess, task) if err != nil { slog.Error("resume session", "session", sess.Id, "err", err) err = m.store.SetSessionState(ctx, sess.Id, api.AgentSessionStateIdle, "byre", map[string]any{"error": err.Error()}) @@ -197,7 +228,7 @@ func (m *Manager) Recover(ctx context.Context) error { slog.Info("resumed session", "session", sess.Id, "task", task.Id) } - return nil + return m.drain(ctx) } func (m *Manager) Close() { @@ -220,20 +251,84 @@ func (m *Manager) LiveCount() int { return len(m.live) } -func (m *Manager) turn(ctx context.Context, sess api.AgentSession, task api.Task, actor, text string, resume bool) (api.AgentSession, error) { - l, err := m.ensureLive(ctx, sess, task, resume) +type Usage struct { + Running int + Queued int + RateLimits map[string]map[string]any +} + +func (m *Manager) Usage(ctx context.Context) (Usage, error) { + u := Usage{RateLimits: map[string]map[string]any{}} + + counts, err := m.store.CountRunning(ctx) + if err != nil { + return u, err + } + for _, n := range counts { + u.Running += n + } + + queued, err := m.store.QueuedTurns(ctx) + if err != nil { + return u, err + } + u.Queued = len(queued) + + m.mu.Lock() + defer m.mu.Unlock() + for vendor, info := range m.rateLimits { + u.RateLimits[string(vendor)] = info + } + return u, nil +} + +func (m *Manager) turn(ctx context.Context, sess api.AgentSession, task api.Task, actor, text string) (api.AgentSession, error) { + m.capMu.Lock() + defer m.capMu.Unlock() + + room, err := m.hasRoom(ctx, sess.Vendor) if err != nil { return api.AgentSession{}, err } - err = m.store.TransitionSession(ctx, sess.Id, api.AgentSessionStateIdle, api.AgentSessionStateRunning, actor) - if errors.Is(err, store.ErrStateChanged) { - return api.AgentSession{}, fmt.Errorf("%w: %w", ErrConflict, err) + if room { + return m.start(ctx, sess, task, actor, text, api.AgentSessionStateIdle) } + + err = transition(ctx, m.store, sess.Id, api.AgentSessionStateIdle, api.AgentSessionStateQueued, actor) if err != nil { return api.AgentSession{}, err } + err = m.store.EnqueueTurn(ctx, store.QueuedTurn{ + SessionID: sess.Id, + Vendor: sess.Vendor, + Actor: actor, + Text: text, + CreatedAt: time.Now().UTC(), + }) + if err != nil { + return api.AgentSession{}, err + } + + return m.store.GetSession(ctx, sess.Id) +} + +func (m *Manager) start(ctx context.Context, sess api.AgentSession, task api.Task, actor, text string, from api.AgentSessionState) (api.AgentSession, error) { + err := transition(ctx, m.store, sess.Id, from, api.AgentSessionStateRunning, actor) + if err != nil { + return api.AgentSession{}, err + } + + l, err := m.ensureLive(ctx, sess, task) + if err != nil { + serr := m.store.SetSessionState(ctx, sess.Id, api.AgentSessionStateIdle, actor, map[string]any{"error": err.Error()}) + if serr != nil { + slog.Error("record failed start", "session", sess.Id, "err", serr) + } + return api.AgentSession{}, err + } + err = m.message(ctx, l, actor, text) if err != nil { return api.AgentSession{}, err @@ -242,6 +337,89 @@ func (m *Manager) turn(ctx context.Context, sess api.AgentSession, task api.Task return m.store.GetSession(ctx, sess.Id) } +func (m *Manager) hasRoom(ctx context.Context, vendor api.AgentSessionVendor) (bool, error) { + counts, err := m.store.CountRunning(ctx) + if err != nil { + return false, err + } + + total := 0 + for _, n := range counts { + total += n + } + + return total < m.limits.Global && counts[vendor] < m.limits.PerVendor[vendor], nil +} + +func (m *Manager) drain(ctx context.Context) error { + m.capMu.Lock() + defer m.capMu.Unlock() + + queued, err := m.store.QueuedTurns(ctx) + if err != nil { + return err + } + + for _, q := range queued { + room, err := m.hasRoom(ctx, q.Vendor) + if err != nil { + return err + } + if !room { + continue + } + + sess, err := m.store.GetSession(ctx, q.SessionID) + if err != nil { + return err + } + task, err := m.store.GetTask(ctx, sess.TaskId) + if err != nil { + return err + } + + err = m.store.DeleteQueuedTurn(ctx, q.SessionID) + if err != nil { + return err + } + + _, err = m.start(ctx, sess, task, q.Actor, q.Text, api.AgentSessionStateQueued) + if err != nil { + slog.Error("start queued turn", "session", q.SessionID, "err", err) + } + } + + return nil +} + +func (m *Manager) cancelQueued(ctx context.Context, sess api.AgentSession) (api.AgentSession, error) { + m.capMu.Lock() + defer m.capMu.Unlock() + + err := m.store.DeleteQueuedTurn(ctx, sess.Id) + if errors.Is(err, store.ErrNotFound) { + return api.AgentSession{}, fmt.Errorf("%w: session %s has no queued turn", ErrConflict, sess.Id) + } + if err != nil { + return api.AgentSession{}, err + } + + err = transition(ctx, m.store, sess.Id, api.AgentSessionStateQueued, api.AgentSessionStateIdle, "byre") + if err != nil { + return api.AgentSession{}, err + } + + return m.store.GetSession(ctx, sess.Id) +} + +func transition(ctx context.Context, st *store.Store, id string, from, to api.AgentSessionState, actor string) error { + err := st.TransitionSession(ctx, id, from, to, actor) + if errors.Is(err, store.ErrStateChanged) { + return fmt.Errorf("%w: %w", ErrConflict, err) + } + return err +} + func (m *Manager) message(ctx context.Context, l *live, actor, text string) error { err := m.emit(ctx, l, actor, "user.message", map[string]any{"text": text}) if err != nil { @@ -250,13 +428,14 @@ func (m *Manager) message(ctx context.Context, l *live, actor, text string) erro m.mu.Lock() l.interrupted = false + l.rateLimited = nil m.mu.Unlock() l.gate.Reset() return l.sess.Send(text) } -func (m *Manager) ensureLive(ctx context.Context, sess api.AgentSession, task api.Task, resume bool) (*live, error) { +func (m *Manager) ensureLive(ctx context.Context, sess api.AgentSession, task api.Task) (*live, error) { m.mu.Lock() defer m.mu.Unlock() @@ -274,7 +453,7 @@ func (m *Manager) ensureLive(ctx context.Context, sess api.AgentSession, task ap return nil, err } - l = &live{id: sess.Id, taskID: task.Id, projectID: task.ProjectId} + l = &live{id: sess.Id, vendor: sess.Vendor, taskID: task.Id, projectID: task.ProjectId} l.gate = &report.Gate{OnReport: func(r report.Report) { err := m.emit(context.Background(), l, agentActor, "agent.report", store.ToPayload(r)) if err != nil { @@ -293,7 +472,11 @@ func (m *Manager) ensureLive(ctx context.Context, sess api.AgentSession, task ap "Stop": {{Func: l.gate.StopHook}}, }, } - if resume { + started, err := m.store.SessionStarted(ctx, sess.Id) + if err != nil { + return nil, err + } + if started { opts.Resume = sess.VendorSessionId } else { opts.SessionID = sess.VendorSessionId @@ -346,6 +529,11 @@ func (m *Manager) pump(l *live) { slog.Error("record session exit", "session", l.id, "err", err) } m.fileError(l, "The agent process exited mid-turn. Send a message to resume it.") + + err = m.drain(ctx) + if err != nil { + slog.Error("start queued turns", "err", err) + } } func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error { @@ -399,10 +587,16 @@ func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error { m.mu.Lock() interrupted := l.interrupted + rateLimited := l.rateLimited m.mu.Unlock() - if res.IsError && !interrupted { - m.fileError(l, fmt.Sprintf("The turn ended with an error (%s): %s", res.Subtype, res.Result)) + failure := fmt.Sprintf("The turn ended with an error (%s): %s", res.Subtype, res.Result) + switch { + case !res.IsError || interrupted: + case rateLimited != nil: + m.fileRateLimited(l, rateLimited, failure) + default: + m.fileError(l, failure) } err = m.store.SetSessionState(ctx, l.id, api.AgentSessionStateIdle, agentActor, nil) @@ -410,14 +604,32 @@ func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error { return err } - go func() { - err := m.Deliver(context.Background(), l.id) - if err != nil { - slog.Error("deliver inbox responses", "session", l.id, "err", err) - } - }() + err = m.drain(ctx) + if err != nil { + slog.Error("start queued turns", "err", err) + } + err = m.Deliver(ctx, l.id) + if err != nil { + slog.Error("deliver inbox responses", "session", l.id, "err", err) + } return nil + case "rate_limit_event": + info := struct { + RateLimitInfo map[string]any `json:"rate_limit_info"` + }{} + err := json.Unmarshal(ev.Raw, &info) + if err != nil { + return err + } + + m.mu.Lock() + m.rateLimits[l.vendor] = info.RateLimitInfo + if info.RateLimitInfo["status"] == "rejected" { + l.rateLimited = info.RateLimitInfo + } + m.mu.Unlock() + case "system": if ev.Subtype == "init" { return nil diff --git a/internal/agents/manager_test.go b/internal/agents/manager_test.go index 3a842b5..73ebd0d 100644 --- a/internal/agents/manager_test.go +++ b/internal/agents/manager_test.go @@ -26,12 +26,14 @@ const ( ) type env struct { - t *testing.T - ctx context.Context - store *store.Store - events chan api.Event - user api.User - task api.Task + t *testing.T + ctx context.Context + store *store.Store + events chan api.Event + user api.User + repo string + project api.Project + task api.Task } func newEnv(t *testing.T) *env { @@ -56,38 +58,45 @@ func newEnv(t *testing.T) *env { t.Fatalf("update user: %v", err) } - repo := scratchRepo(t) - p := api.Project{Id: store.NewID(), Name: "demo", RepoPath: repo, DefaultBranch: "main", Remote: "none", CreatedBy: login, CreatedAt: time.Now().UTC()} - err = e.store.CreateProject(ctx, p) + e.repo = scratchRepo(t) + e.project = api.Project{Id: store.NewID(), Name: "demo", RepoPath: e.repo, DefaultBranch: "main", Remote: "none", CreatedBy: login, CreatedAt: time.Now().UTC()} + err = e.store.CreateProject(ctx, e.project) if err != nil { t.Fatalf("create project: %v", err) } + e.task = e.addTask() + return e +} + +func (e *env) addTask() api.Task { + e.t.Helper() id := store.NewID() - e.task = api.Task{ + task := api.Task{ Id: id, - ProjectId: p.Id, + ProjectId: e.project.Id, Title: "test", Branch: "byre/" + id, - WorktreePath: filepath.Join(t.TempDir(), id), + WorktreePath: filepath.Join(e.t.TempDir(), id), Status: api.TaskStatusActive, CreatedBy: login, CreatedAt: time.Now().UTC(), } - e.task.BaseRef, err = git.AddWorktree(repo, e.task.WorktreePath, e.task.Branch, "main") - if err != nil { - t.Fatalf("add worktree: %v", err) - } - err = e.store.CreateTask(ctx, e.task) - if err != nil { - t.Fatalf("create task: %v", err) - } - return e + var err error + task.BaseRef, err = git.AddWorktree(e.repo, task.WorktreePath, task.Branch, "main") + if err != nil { + e.t.Fatalf("add worktree: %v", err) + } + err = e.store.CreateTask(e.ctx, task) + if err != nil { + e.t.Fatalf("create task: %v", err) + } + return task } func (e *env) manager() *agents.Manager { - m := agents.New(e.store) + m := agents.New(e.store, agents.DefaultLimits) e.t.Cleanup(m.Close) return m } @@ -221,7 +230,7 @@ func TestInterrupt(t *testing.T) { func TestRecoverMidTurn(t *testing.T) { t.Parallel() e := newEnv(t) - first := agents.New(e.store) + first := agents.New(e.store, agents.DefaultLimits) sess, err := first.Create(e.ctx, e.task, e.user, "Run the bash command `sleep 15` in the foreground (not in the background). After it finishes, create a file done.txt containing exactly the word done.", testModel) if err != nil { @@ -446,6 +455,64 @@ func TestFollowUpQuestionThread(t *testing.T) { } } +func TestConcurrencyQueue(t *testing.T) { + t.Parallel() + e := newEnv(t) + m := agents.New(e.store, agents.Limits{Global: 1, PerVendor: map[api.AgentSessionVendor]int{api.Claude: 1}}) + t.Cleanup(m.Close) + + first, err := m.Create(e.ctx, e.task, e.user, longCommandPrompt, testModel) + if err != nil { + t.Fatalf("create first: %v", err) + } + e.waitFor("first tool call", func(ev api.Event) bool { return ev.Kind == "agent.tool_call" }) + + second, err := m.Create(e.ctx, e.addTask(), e.user, "Reply with the single word ok.", testModel) + if err != nil { + t.Fatalf("create second: %v", err) + } + if second.State != api.AgentSessionStateQueued { + t.Fatalf("second session is %s with one slot taken, want queued", second.State) + } + + usage, err := m.Usage(e.ctx) + if err != nil || usage.Running != 1 || usage.Queued != 1 { + t.Fatalf("usage %+v %v", usage, err) + } + + cancelled, err := m.Interrupt(e.ctx, second.Id) + if err != nil || cancelled.State != api.AgentSessionStateIdle { + t.Fatalf("cancel queued turn: %+v %v", cancelled, err) + } + + second, err = m.Send(e.ctx, second.Id, login, "Reply with the single word ok.") + if err != nil || second.State != api.AgentSessionStateQueued { + t.Fatalf("requeue: %+v %v", second, err) + } + + _, err = m.Interrupt(e.ctx, first.Id) + if err != nil { + t.Fatalf("interrupt first: %v", err) + } + + e.waitFor("first idle", func(ev api.Event) bool { + return ev.Kind == "session.state" && *ev.AgentSessionId == first.Id && ev.Payload["state"] == string(api.AgentSessionStateIdle) + }) + events := e.waitFor("second idle", func(ev api.Event) bool { + return ev.Kind == "session.state" && *ev.AgentSessionId == second.Id && ev.Payload["state"] == string(api.AgentSessionStateIdle) + }) + + replied := false + for _, ev := range events { + if ev.Kind == "agent.message" && *ev.AgentSessionId == second.Id && strings.Contains(strings.ToLower(ev.Payload["text"].(string)), "ok") { + replied = true + } + } + if !replied { + t.Fatalf("queued turn never ran: %v", kinds(events)) + } +} + func scratchRepo(t *testing.T) string { dir := t.TempDir() gitOut(t, dir, "init", "--initial-branch=main") diff --git a/internal/agents/ratelimit_internal_test.go b/internal/agents/ratelimit_internal_test.go new file mode 100644 index 0000000..cd1335f --- /dev/null +++ b/internal/agents/ratelimit_internal_test.go @@ -0,0 +1,78 @@ +package agents + +import ( + "context" + "path/filepath" + "testing" + "time" + + "g.fc.run/misc/byre/internal/api" + "g.fc.run/misc/byre/internal/claude" + "g.fc.run/misc/byre/internal/report" + "g.fc.run/misc/byre/internal/store" +) + +func TestRateLimitedTurn(t *testing.T) { + ctx := context.Background() + st, err := store.Open(filepath.Join(t.TempDir(), "byre.db"), func(api.Event) {}) + if err != nil { + t.Fatalf("open store: %v", err) + } + t.Cleanup(func() { st.Close() }) + + now := time.Now().UTC() + _, err = st.EnsureUser(ctx, "alice", "Alice") + if err != nil { + t.Fatalf("ensure user: %v", err) + } + p := api.Project{Id: "p1", Name: "demo", RepoPath: "/nonexistent", DefaultBranch: "main", Remote: "none", CreatedBy: "alice", CreatedAt: now} + err = st.CreateProject(ctx, p) + if err != nil { + t.Fatalf("create project: %v", err) + } + task := api.Task{Id: "t1", ProjectId: "p1", Title: "t", Branch: "b", WorktreePath: "/nonexistent", BaseRef: "x", Status: api.TaskStatusActive, CreatedBy: "alice", CreatedAt: now} + err = st.CreateTask(ctx, task) + if err != nil { + t.Fatalf("create task: %v", err) + } + sess := api.AgentSession{Id: "s1", TaskId: "t1", Vendor: api.Claude, VendorSessionId: "s1", State: api.AgentSessionStateRunning, Role: api.Implementer, CreatedBy: "alice", CreatedAt: now} + err = st.CreateSession(ctx, sess, "p1") + if err != nil { + t.Fatalf("create session: %v", err) + } + + m := New(st, DefaultLimits) + l := &live{id: "s1", vendor: api.Claude, taskID: "t1", projectID: "p1", gate: &report.Gate{}} + + feed := func(typ, raw string) { + t.Helper() + err := m.handle(ctx, l, claude.Event{Type: typ, Raw: []byte(raw)}) + if err != nil { + t.Fatalf("handle %s: %v", raw, err) + } + } + + feed("rate_limit_event", `{"type":"rate_limit_event","rate_limit_info":{"status":"allowed","rateLimitType":"five_hour","resetsAt":1791972000}}`) + usage, err := m.Usage(ctx) + if err != nil { + t.Fatalf("usage: %v", err) + } + if usage.RateLimits["claude"]["status"] != "allowed" { + t.Fatalf("rate limit status not recorded: %+v", usage.RateLimits) + } + + feed("rate_limit_event", `{"type":"rate_limit_event","rate_limit_info":{"status":"rejected","rateLimitType":"five_hour","resetsAt":1791972000}}`) + feed("result", `{"type":"result","subtype":"success","is_error":true,"result":"API Error: rate limited","num_turns":1}`) + + items, err := st.ListInbox(ctx, store.InboxFilter{SessionID: "s1", State: api.Open}) + if err != nil { + t.Fatalf("list inbox: %v", err) + } + if len(items) != 1 || items[0].Kind != api.InboxItemKindRateLimited || items[0].Priority != api.Blocked { + t.Fatalf("want one blocked rate_limited item, got %+v", items) + } + limit, ok := items[0].Payload["rate_limit"].(map[string]any) + if !ok || limit["status"] != "rejected" || items[0].Payload["vendor"] != "claude" { + t.Fatalf("unexpected payload %+v", items[0].Payload) + } +} diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 1c292c4..e26e724 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -40,6 +40,7 @@ func (e AgentSessionRole) Valid() bool { const ( AgentSessionStateCompleted AgentSessionState = "completed" AgentSessionStateIdle AgentSessionState = "idle" + AgentSessionStateQueued AgentSessionState = "queued" AgentSessionStateRunning AgentSessionState = "running" ) @@ -50,6 +51,8 @@ func (e AgentSessionState) Valid() bool { return true case AgentSessionStateIdle: return true + case AgentSessionStateQueued: + return true case AgentSessionStateRunning: return true default: @@ -108,6 +111,7 @@ func (e AttachmentKind) Valid() bool { // Defines values for InboxItemKind. const ( InboxItemKindQuestion InboxItemKind = "question" + InboxItemKindRateLimited InboxItemKind = "rate_limited" InboxItemKindReport InboxItemKind = "report" InboxItemKindSessionError InboxItemKind = "session_error" ) @@ -117,6 +121,8 @@ func (e InboxItemKind) Valid() bool { switch e { case InboxItemKindQuestion: return true + case InboxItemKindRateLimited: + return true case InboxItemKindReport: return true case InboxItemKindSessionError: @@ -232,8 +238,10 @@ type AgentSession struct { Id string `json:"id"` // Model Requested model; empty means the vendor's default. - Model string `json:"model"` - Role AgentSessionRole `json:"role"` + Model string `json:"model"` + Role AgentSessionRole `json:"role"` + + // State queued means a turn is waiting for a free slot under the concurrency caps. State AgentSessionState `json:"state"` TaskId string `json:"task_id"` Vendor AgentSessionVendor `json:"vendor"` @@ -243,7 +251,7 @@ type AgentSession struct { // AgentSessionRole defines model for AgentSession.Role. type AgentSessionRole string -// AgentSessionState defines model for AgentSession.State. +// AgentSessionState queued means a turn is waiting for a free slot under the concurrency caps. type AgentSessionState string // AgentSessionVendor defines model for AgentSession.Vendor. @@ -300,7 +308,7 @@ type InboxItem struct { Id string `json:"id"` Kind InboxItemKind `json:"kind"` - // Payload question: question, why_blocked, options, recommended_option, attachments. report: done, open_questions, next_steps, risks, deviations. session_error: error. + // Payload question: question, why_blocked, options, recommended_option, attachments. report: done, open_questions, next_steps, risks, deviations. session_error: error. rate_limited: vendor, rate_limit (the vendor's rate-limit status), error. Payload map[string]interface{} `json:"payload"` Priority InboxItemPriority `json:"priority"` ProjectId string `json:"project_id"` @@ -395,10 +403,15 @@ type Status struct { LastEventSeq int64 `json:"last_event_seq"` // LiveAgents Agent processes currently running. - LiveAgents int `json:"live_agents"` - Projects int `json:"projects"` - SseClients int `json:"sse_clients"` - StartedAt time.Time `json:"started_at"` + LiveAgents int `json:"live_agents"` + Projects int `json:"projects"` + QueuedTurns int `json:"queued_turns"` + + // RateLimits The latest rate-limit status each vendor reported, keyed by vendor. + RateLimits map[string]map[string]interface{} `json:"rate_limits"` + RunningTurns int `json:"running_turns"` + SseClients int `json:"sse_clients"` + StartedAt time.Time `json:"started_at"` } // Task defines model for Task. @@ -451,6 +464,12 @@ type ListInboxParams struct { IncludeSnoozed *bool `form:"include_snoozed,omitempty" json:"include_snoozed,omitempty"` } +// ListTaskEventsParams defines parameters for ListTaskEvents. +type ListTaskEventsParams struct { + After *int64 `form:"after,omitempty" json:"after,omitempty"` + Limit *int `form:"limit,omitempty" json:"limit,omitempty"` +} + // RespondInboxJSONRequestBody defines body for RespondInbox for application/json ContentType. type RespondInboxJSONRequestBody = InboxRespond @@ -523,6 +542,9 @@ type ServerInterface interface { // (POST /api/tasks/{taskId}/complete) CompleteTask(w http.ResponseWriter, r *http.Request, taskId string) + // (GET /api/tasks/{taskId}/events) + ListTaskEvents(w http.ResponseWriter, r *http.Request, taskId string, params ListTaskEventsParams) + // (GET /api/tasks/{taskId}/sessions) ListSessions(w http.ResponseWriter, r *http.Request, taskId string) @@ -983,6 +1005,61 @@ func (siw *ServerInterfaceWrapper) CompleteTask(w http.ResponseWriter, r *http.R handler.ServeHTTP(w, r) } +// ListTaskEvents operation middleware +func (siw *ServerInterfaceWrapper) ListTaskEvents(w http.ResponseWriter, r *http.Request) { + + var err error + _ = err + + // ------------- Path parameter "taskId" ------------- + var taskId string + + err = runtime.BindStyledParameterWithOptions("simple", "taskId", r.PathValue("taskId"), &taskId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "taskId", Err: err}) + return + } + + // Parameter object where we will unmarshal all parameters from the context + var params ListTaskEventsParams + + // ------------- Optional query parameter "after" ------------- + + err = runtime.BindQueryParameterWithOptions("form", true, false, "after", r.URL.Query(), ¶ms.After, runtime.BindQueryParameterOptions{Type: "integer", Format: "int64"}) + if err != nil { + var requiredError *runtime.RequiredParameterError + if errors.As(err, &requiredError) { + siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "after"}) + } else { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "after", Err: err}) + } + return + } + + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameterWithOptions("form", true, false, "limit", r.URL.Query(), ¶ms.Limit, runtime.BindQueryParameterOptions{Type: "integer", Format: ""}) + if err != nil { + var requiredError *runtime.RequiredParameterError + if errors.As(err, &requiredError) { + siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "limit"}) + } else { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "limit", Err: err}) + } + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.ListTaskEvents(w, r, taskId, params) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // ListSessions operation middleware func (siw *ServerInterfaceWrapper) ListSessions(w http.ResponseWriter, r *http.Request) { @@ -1164,6 +1241,7 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/api/projects/{projectId}/tasks", wrapper.CreateTask) m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/tasks/{taskId}", wrapper.GetTask) m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/api/tasks/{taskId}/complete", wrapper.CompleteTask) + m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/tasks/{taskId}/events", wrapper.ListTaskEvents) m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/tasks/{taskId}/sessions", wrapper.ListSessions) m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/api/tasks/{taskId}/sessions", wrapper.CreateSession) m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/sessions/{sessionId}", wrapper.GetSession) @@ -1183,50 +1261,54 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H // const string: with thousands of chunks the chained `+` fold is several // times slower for the Go compiler than parsing a slice literal. var swaggerSpec = []string{ - "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", - "ncOYdGbqgm51/M1kst3ufwEAAP//", + "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", } // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, diff --git a/internal/claude/testdata/transcripts/TestCommitIdentityHooksAndReport.jsonl b/internal/claude/testdata/transcripts/TestCommitIdentityHooksAndReport.jsonl index d3b0171..d7b3b36 100644 --- a/internal/claude/testdata/transcripts/TestCommitIdentityHooksAndReport.jsonl +++ b/internal/claude/testdata/transcripts/TestCommitIdentityHooksAndReport.jsonl @@ -1,40 +1,40 @@ -{"dir":"send","msg":{"request":{"hooks":{"PostToolUse":[{"hookCallbackIds":["hook_1"],"matcher":""}],"Stop":[{"hookCallbackIds":["hook_0"],"matcher":""}]},"subtype":"initialize"},"request_id":"req_65f013ea3d59b4e9","type":"control_request"}} -{"dir":"recv","msg":{"type":"control_request","request_id":"dc5b6365-0751-4e49-8842-aa11dc5b8c26","request":{"subtype":"mcp_message","server_name":"byre","message":{"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"claude-code","title":"Claude Code","description":"Anthropic's agentic coding tool","websiteUrl":"https://claude.com/claude-code","version":"2.1.296"}},"jsonrpc":"2.0","id":0}}}} -{"dir":"send","msg":{"response":{"request_id":"dc5b6365-0751-4e49-8842-aa11dc5b8c26","response":{"mcp_response":{"jsonrpc":"2.0","id":0,"result":{"capabilities":{"tools":{}},"protocolVersion":"2025-06-18","serverInfo":{"name":"byre","version":"0.0.0"}}}},"subtype":"success"},"type":"control_response"}} -{"dir":"recv","msg":{"type":"control_response","response":{"subtype":"success","request_id":"req_65f013ea3d59b4e9","response":{"commands":[{"name":"deep-research","description":"Deep research harness — fan-out web searches, fetch sources, adversarially verify claims, synthesize a cited report. (dynamic workflow)","argumentHint":"","builtin":true},{"name":"design","description":"Make a new Design artifact from a brief","argumentHint":"[what to design]","builtin":true},{"name":"slides","description":"Make a new Slides deck artifact from a brief","argumentHint":"[what the deck is about]","builtin":true},{"name":"design-sync","description":"Push a React design system to claude.ai/design. This runs a converter that bundles the real component code (from Storybook or a bare package) and uploads it. Use when the user runs /design-sync or says \"sync my design system to Claude Design\".","argumentHint":"[\u003cproject hint, e.g. \"Acme DS\"\u003e]","builtin":true},{"name":"dataviz","description":"Use this skill whenever you are about to create ANY chart, graph, plot, dashboard, or data visualization, in ANY output medium — an HTML or React artifact, inline SVG, plotting code in any library (matplotlib, plotly, d3, Recharts, …), an image/PNG you will render and upload, or a chart shared into Slack. Read it BEFORE writing the first line of chart code, choosing chart colors, building a stat tile / meter / KPI row, or laying out a dashboard. When the destination is a first-party document connector (host-designated, never self-described) that renders live charts, hand it the rows (inline, or as an uploaded data file the chart cites) rather than a rendered PNG/SVG — a picture of a chart loses hover, data inspection and per-value comments. Produces visualizations that read as one system — elegant, accessible, consistent in light and dark — using a brand-neutral placeholder palette you swap for your own. Teaches a design-system-agnostic method: a form heuristic, a color formula with a runnable validator, mark specs, and interaction rules. A validated default palette is documented in `references/palette.md` — swap that file's values for your brand's. Triggers on: \"chart\", \"graph\", \"plot\", \"data viz\", \"visualization\", \"dashboard\", \"analytics\", \"visualize data\", \"categorical colors\", \"sequential / diverging palette\", \"stat tile\", \"sparkline\", \"heatmap\", \"legend\", \"axis\", \"tooltip\", \"chart colors\", \"color by series\".","argumentHint":"","builtin":true},{"name":"artifact-diagramming","description":"Diagramming know-how for Artifacts - when a picture earns its place, how to draw one that shows the real mechanism, and the inline-SVG mechanics that keep it legible in both themes.","argumentHint":"","builtin":true},{"name":"artifact-capabilities","description":"Runtime capabilities a published Artifact page can be granted — behavior static HTML cannot provide on its own, such as the page reading live or connected data, remembering what people do on it (a poll, a sign-up sheet, a checklist, a document edited in place — it saves new versions of itself), keeping state shared across viewers, knowing who is viewing, asking Claude a question of its own, storing files people add, handing the viewer a file to save, or using the viewer's camera, microphone, location, screen or device motion. Serves this user's live capability roster and the typed call definitions. Load it whenever any such runtime behavior would make an artifact more useful, before writing the page.","argumentHint":"","builtin":true},{"name":"update-config","description":"Use this skill to configure the Claude Code harness via settings.json. Automated behaviors (\"from now on when X\", \"each time X\", \"whenever X\", \"before/after X\") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions (\"allow X\", \"add permission\", \"move permission to\"), env vars (\"set X=Y\"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: \"allow npm commands\", \"add bq permission to global settings\", \"move permission to user settings\", \"set DEBUG=true\", \"when claude stops show X\". For simple settings like theme/model, suggest the /config command.","argumentHint":"","builtin":true},{"name":"verify","description":"Verify that a code change actually does what it's supposed to by exercising it end-to-end and observing behavior — drive the affected flow, not just tests or typecheck. Run before committing nontrivial changes; bootstraps this repo's project verify skill if none exists yet. Don't invoke it on a diff that only touches tests, docs, or other code with no runtime surface to drive (a change to product source always has one) — there's nothing to observe.","argumentHint":"","builtin":true},{"name":"debug","description":"Enable debug logging for this session and help diagnose issues","argumentHint":"[issue description]","builtin":true},{"name":"code-review","description":"Review the current diff, or a PR number/branch/path target, for correctness bugs (plus reuse/simplification/efficiency cleanups where the model's review recipe covers them) at the given effort level (low|medium|high|xhigh|max: from few high-confidence findings up to many, some of them uncertain; ultra: deep multi-agent review in the cloud); with no level given, it reuses the level you typed last. Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review. Pass --max-findings \u003cn\u003e to report up to n findings, or --max-findings all for every finding. The choice stays until you pass --max-findings default. For ultra on a GitHub.com PR target, --post asks to post the finished review’s findings to the PR as a single comment from the user’s GitHub account (not a review; the launch dialog still confirms in interactive sessions, while non-interactive mode posts on the flag alone) and --no-post hides that option.","argumentHint":"[low|medium|high|xhigh|max|ultra] [--fix] [--comment] [\u003cpr#\u003e|\u003cbranch\u003e|\u003cpath\u003e] [--max-findings \u003cn\u003e|all]","aliases":["review"],"builtin":true},{"name":"simplify","description":"Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.","argumentHint":"[\u003ctarget\u003e]","builtin":true},{"name":"batch","description":"Research and plan a large-scale change, then execute it in parallel across 5–30 isolated worktree agents that each open a PR.","argumentHint":"\u003cinstruction\u003e","builtin":true},{"name":"fewer-permission-prompts","description":"Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.","argumentHint":"","builtin":true},{"name":"doctor","description":"Health-check the user's Claude Code setup and fix issues: diagnose installation health — what the `claude doctor` terminal diagnostics cover — from local data (duplicate or leftover installs, PATH, unparseable settings files, broken or colliding agent definitions, skills whose frontmatter fails to parse); find unused skills, MCP servers, and plugins versus their context cost and disable dead weight; deduplicate local CLAUDE.md files against checked-in ones; trim checked-in CLAUDE.md files by cutting content a session could derive from the codebase (directory layouts, tech-stack lists, architecture overviews) while keeping gotchas, rationale, and non-standard conventions; migrate always-loaded CLAUDE.md guidance into lazy skills and nested CLAUDE.md files; flag slow hooks and context-heavy extensions; check the installed version is current; make auto mode the default permission mode; and pre-approve frequently denied read-only commands. Use when the user asks for a doctor run, checkup, audit, tune-up, or cleanup of their Claude Code setup or configuration.","argumentHint":"[prompt-audit [\u003cpath\u003e]]","aliases":["checkup"],"builtin":true},{"name":"loop","description":"Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace.","argumentHint":"[interval] [prompt]","aliases":["proactive"],"builtin":true},{"name":"schedule","description":"Create, update, list, or run scheduled cloud agents (routines) that execute on a cron schedule.","argumentHint":"","aliases":["routines"],"builtin":true},{"name":"claude-api","description":"Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.\nTRIGGER — read BEFORE opening the target file; don't skip because it \"looks like a one-liner\" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).\nSKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).","argumentHint":"","builtin":true},{"name":"workflow-authoring","description":"Reference for writing a Workflow tool script (script API and gotchas, resume, quality patterns, worked examples). Load before authoring a script for a workflow the user already opted into; it does not itself authorize running one.","argumentHint":"","builtin":true},{"name":"run","description":"Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).","argumentHint":"","builtin":true},{"name":"run-skill-generator","description":"Author or improve the run-\u003cunit\u003e skill - a per-project skill that tells agents how to build, launch, and drive this project's app. Use when the user asks to set up the project, get it running, write run instructions, or verify build/run steps work from a clean environment.","argumentHint":"","builtin":true},{"name":"plugin-authoring","description":"Make a mod: a change to Claude's own interface or behaviour, such as a live pane or panel, a band above the prompt, a status line, a toast, a slash command, or a hook on tool calls or prompts. Use to make, change or debug a mod or its hooks module.","argumentHint":"","builtin":true},{"name":"advisor","description":"Let Claude consult a stronger model at key moments","argumentHint":"[fable|opus|sonnet|off]","builtin":true},{"name":"agents","description":"(removed) Ask Claude to create/manage subagents, or edit .claude/agents/","argumentHint":"","builtin":true},{"name":"auto-mode-setup","description":"Teach auto mode about your environment, plus optional rule tweaks","argumentHint":"[--request-id \u003cuuid\u003e] (--wizard posture=… scope=… depth=… --propose | --expect-sha256 \u003c64-hex\u003e --apply-file \u003cpath\u003e)","builtin":true},{"name":"autocompact","description":"Configure the auto-compact window size","argumentHint":"[auto|\u003ctokens\u003e]","builtin":true},{"name":"clear","description":"Start a new session with empty context; previous session stays on disk (resumable with /resume)","argumentHint":"[name]","aliases":["reset","new"],"builtin":true},{"name":"color","description":"Set the prompt bar color for this session","argumentHint":"[red|blue|green|yellow|purple|orange|pink|cyan|default]","builtin":true},{"name":"compact","description":"Free up context by summarizing the conversation so far","argumentHint":"\u003coptional custom summarization instructions\u003e","builtin":true},{"name":"config","description":"Set a setting by key","argumentHint":"key=value","aliases":["settings"],"builtin":true},{"name":"output-style","description":"List output styles or switch to one","argumentHint":"[style]","builtin":true},{"name":"context","description":"Show current context usage","argumentHint":"","builtin":true},{"name":"effort","description":"Set effort level for model usage","argumentHint":"\u003clow|medium|high|xhigh|max|auto|ultracode [on|off]\u003e","builtin":true},{"name":"fast","description":"Toggle fast mode (Opus 5.5)","argumentHint":"[on|off]","builtin":true},{"name":"focus","description":"Toggle focus view: just your prompt, summary, and response","argumentHint":"[on|off]","builtin":true},{"name":"heapdump","description":"Dump the JS heap to the Desktop; on Linux with no Desktop folder, the home directory","argumentHint":"","builtin":true},{"name":"init","description":"Initialize a new CLAUDE.md file with codebase documentation","argumentHint":"","builtin":true},{"name":"mcp","description":"Manage MCP servers","argumentHint":"[reconnect|enable|disable [\u003cserver\u003e|all]]","builtin":true},{"name":"import","description":"Import config from another AI coding agent","argumentHint":"","builtin":true},{"name":"model","description":"Set the AI model for Claude Code","argumentHint":"\u003cmodel\u003e","builtin":true},{"name":"__remote-workflow","description":"Run the workflow script delivered in this session environment (server-launched sessions only)","argumentHint":"","builtin":true},{"name":"workflow-launch-exec","description":"Execute a server-launched workflow handoff (workflow_launch event sessions only)","argumentHint":"","builtin":true},{"name":"reload-plugins","description":"Activate pending plugin changes in the current session","argumentHint":"[--force]","builtin":true},{"name":"reload-skills","description":"Pick up skills added or changed on disk during this session","argumentHint":"","builtin":true},{"name":"rename","description":"Rename the current conversation","argumentHint":"[name]","aliases":["name"],"builtin":true},{"name":"ultrareview","description":"Start a cloud agent that finds and verifies bugs in your branch (~15-25 min, $5-$25 USD) · Runs in a cloud session. See https://code.claude.com/docs/en/claude-code-on-the-web","argumentHint":"","builtin":true},{"name":"security-review","description":"Complete a security review of the pending changes on the current branch","argumentHint":"","builtin":true},{"name":"usage-credits","description":"Configure usage credits or request them from your admin when you hit a limit","argumentHint":"","builtin":true},{"name":"extra-usage","description":"Renamed to /usage-credits","argumentHint":"","builtin":true},{"name":"usage","description":"Show session cost, plan usage, and what's contributing to your limits","argumentHint":"","aliases":["cost","stats"],"builtin":true},{"name":"insights","description":"Generate a report analyzing your Claude Code sessions","argumentHint":"","builtin":true},{"name":"recap","description":"Generate a one-line session recap now","argumentHint":"","builtin":true},{"name":"skill-doctor","description":"Show which loaded skills are unused and costing context","argumentHint":"","builtin":true},{"name":"goal","description":"Set a goal — keep working until the condition is met","argumentHint":"","builtin":true},{"name":"design-consent","description":"Grant Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"design-revoke","description":"Revoke Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"list-agents","description":"List subagents, teammates, and other Claude sessions you can message","argumentHint":"","aliases":["peers"],"builtin":true},{"name":"team-onboarding","description":"Help teammates ramp on Claude Code with a guide from your usage","argumentHint":"","builtin":true},{"name":"docs","description":"docs (editable docs people share and comment on; the default for any document, named as a doc or not: a document, report, proposal, resume, cover letter, letter, contract, policy, form, template, worksheet, essay, handbook, guide, how-to, cheat sheet, SOP or other writing to keep, share, collaborate on, send, submit, print or sign; a doc exports to Word, PDF, Markdown or Google Docs, so needing a file to send, attach, upload, submit or print is no reason to pick Word, and a file nobody asked for is a doc, not Word; a plan, comparison, summary or notes asked in chat stays in chat; a pasted claude.ai artifact link may be a doc: check with docs tools first; Word or another file format named, tracked changes wanted, or a .docx to change or use as a template → that format's skill): making one → if no docs-connector instructions are in context, call its `guide` (topic.instructions) first; then create the doc (headings only, no body) before any search, file read or plan, even with files attached. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docs"]},{"name":"docx","description":"Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx) or Word templates (.dotx). Triggers include: any mention of Microsoft Word Documents, such as 'Word doc', 'word document', '.docx', '.dotx', 'microsoft doc'. Also use when extracting or reorganizing content from .docx or .dotx files, inserting or replacing images in documents, find-and-replace in Word files, working with tracked changes or comments, or converting content into a polished Word document. If the user asks for a deliverable as a Word or .docx file (to download, email or print), use this skill. However, if they ask for a document, page, report, memo, or notes WITHOUT naming a file format and the session offers Claude's own dedicated document or page skill or connector, use that instead, even if they will email or print it. Do NOT use for PDFs, spreadsheets, Google Docs, or coding unrelated to document generation. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docx"]},{"name":"google-workspace","description":"Read this before the first Google Drive, Docs, Sheets or Slides connector call whenever the task creates or changes a Google file. Use this skill whenever the user wants to create or change a Google Doc, Sheet or Slides file in their Google Drive. Triggers include: a request that names Google Docs, Sheets, Slides or Drive and asks to make, edit, format, copy or rename a file; a docs.google.com link with a request to change that file, even a one-line fix or suggested edits; and any follow-up change to a Google file from earlier in the chat, even \"change it\" or \"add a tab\". Includes helper scripts for document positions, cell ranges and slide layout. However, if the user asks for a doc, deck or spreadsheet without naming Google, or gives a Google file only as source material for something new, use Claude's own output type instead. Do NOT use for read-only questions about a Google file, or for Word, Excel, PowerPoint or PDF files. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:google-workspace"]},{"name":"import-memory","description":"Import a memory export from another AI assistant into Claude's memory — conversationally, additively, and with the content treated as data. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:import-memory"]},{"name":"morning","description":"Render the user's morning brief as a styled HTML artifact, or set it up as a recurring weekday task. Use only when the user explicitly asks to run, see, or set up their morning brief, or if they invoke /morning by name. A question about their day, schedule, or calendar is not by itself a request for the brief; answer it directly instead. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:morning"]},{"name":"pdf","description":"Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text/tables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pdf"]},{"name":"pptx","description":"Use this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations as PowerPoint (.pptx) files; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an email, summary, or creating a different type of slide deck); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates (.potx), layouts, speaker notes, or comments. Trigger whenever the user asks for a PowerPoint or .pptx file, or references a .pptx or .potx filename, regardless of what they plan to do with the content afterward. However, when the user asks for a deck, slides, a slide deck, or a presentation without naming a file format, default to using a dedicated slide-deck artifact type or a separate slides skill if this session offers one; otherwise, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pptx"]},{"name":"skill-creator","description":"Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:skill-creator"]},{"name":"xlsx","description":"Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .xltx, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like \"the xlsx in my downloads\") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:xlsx"]}],"agents":[{"name":"claude","description":"Catch-all for any task that doesn't fit a more specific agent. FleetView's default when no agent name is typed."},{"name":"claude-code-guide","description":"Use this agent when the user asks questions (\"Can Claude...\", \"Does Claude...\", \"How do I...\") about: (1) Claude Code (the CLI tool) - features, hooks, slash commands, MCP servers, settings, IDE integrations, keyboard shortcuts; (2) Claude Agent SDK - building custom agents; (3) Claude API (formerly Anthropic API) - Messages API for directly passing messages to Claude, Tool Runner (`client.beta.messages.tool_runner`) for running an agentic loop over your own tools, manual tool-use loops, Managed Agents for server-hosted agents with a managed sandbox, prompt caching, and general Anthropic SDK usage; (4) Claude Tag (Claude in Slack) - what it is, setting it up for a Slack workspace, `/install-slack-app`; (5) `claude plugin eval` (writing and running plugin eval suites, its JSON/report, sandbox, CI) and the `/skill-doctor` report. **IMPORTANT:** Before spawning a new agent, check if there is already a running or recently completed claude-code-guide agent that you can continue via SendMessage.","model":"haiku"},{"name":"Explore","description":"Fast read-only search agent for locating code. Use it to find files by pattern (eg. \"src/components/**/*.tsx\"), grep for symbols or keywords (eg. \"API endpoints\"), or answer \"where is X defined / which files reference Y.\" Do NOT use it for code review, design-doc auditing, cross-file consistency checks, or open-ended analysis — it reads excerpts rather than whole files and will miss content past its read window. When calling, specify search breadth: \"quick\" for a single targeted lookup, \"medium\" for moderate exploration, or \"very thorough\" to search across multiple locations and naming conventions.","model":"inherit"},{"name":"general-purpose","description":"General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you."},{"name":"Plan","description":"Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.","model":"inherit"},{"name":"statusline-setup","description":"Use this agent to configure the user's Claude Code status line setting.","model":"sonnet"}],"output_style":"default","available_output_styles":["default","Proactive","Concise","Explanatory","Learning"],"user_output_styles_dir":"/Users/flamingcow/.claude/output-styles","models":[{"value":"default","resolvedModel":"claude-opus-5-5","displayName":"Default (recommended)","description":"Opus 5.5 · Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"opus","resolvedModel":"claude-opus-5-5","displayName":"Opus 5.5","description":"For complex work and everyday tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"fable","resolvedModel":"claude-fable-5-1","displayName":"Fable 5.1","description":"For your toughest challenges","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"sonnet","resolvedModel":"claude-sonnet-5-5","displayName":"Sonnet 5.5","description":"Most efficient for simpler tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"haiku","resolvedModel":"claude-haiku-5-5","displayName":"Haiku 5.5","description":"Fastest for quick answers","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-haiku-4-5-20251001","resolvedModel":"claude-haiku-4-5-20251001","displayName":"Haiku 4.5","description":"Fastest for quick answers"},{"value":"claude-sonnet-5","resolvedModel":"claude-sonnet-5","displayName":"Sonnet 5","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-5","resolvedModel":"claude-opus-5","displayName":"Opus 5","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-fable-5","resolvedModel":"claude-fable-5","displayName":"Fable 5","description":"Most capable for your hardest and longest-running tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-8","resolvedModel":"claude-opus-4-8","displayName":"Opus 4.8","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-opus-4-7","resolvedModel":"claude-opus-4-7","displayName":"Opus 4.7","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-6","resolvedModel":"claude-opus-4-6","displayName":"Opus 4.6","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-sonnet-4-6","resolvedModel":"claude-sonnet-4-6","displayName":"Sonnet 4.6","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true}],"account":{"email":"[redacted]","organization":"[redacted]'s Organization","subscriptionType":"Claude Max","apiProvider":"firstParty"},"pid":27098,"claude_code_version":"2.1.296","current_permission_mode":"bypassPermissions","hooks_applied":true,"feedback_mode":{"kind":"post"},"analytics_disabled":false,"remote_control_auto_enable":false,"remote_control_auto_connect_default":false,"remote_control_available":true,"remote_control_auto_on_by_default":false,"ide_rc_auto_enable_gate":true,"fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","session_state":"idle","capabilities":["ui_surface_v1"]},"pending_permission_requests":[],"pending_user_dialog_requests":[]}}} +{"dir":"send","msg":{"request":{"hooks":{"PostToolUse":[{"hookCallbackIds":["hook_1"],"matcher":""}],"Stop":[{"hookCallbackIds":["hook_0"],"matcher":""}]},"subtype":"initialize"},"request_id":"req_650e894e81ecb6c9","type":"control_request"}} +{"dir":"recv","msg":{"type":"control_request","request_id":"4efc4ddc-9ca2-4284-9f57-23e48a03ebfc","request":{"subtype":"mcp_message","server_name":"byre","message":{"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"claude-code","title":"Claude Code","description":"Anthropic's agentic coding tool","websiteUrl":"https://claude.com/claude-code","version":"2.1.296"}},"jsonrpc":"2.0","id":0}}}} +{"dir":"send","msg":{"response":{"request_id":"4efc4ddc-9ca2-4284-9f57-23e48a03ebfc","response":{"mcp_response":{"jsonrpc":"2.0","id":0,"result":{"capabilities":{"tools":{}},"protocolVersion":"2025-06-18","serverInfo":{"name":"byre","version":"0.0.0"}}}},"subtype":"success"},"type":"control_response"}} +{"dir":"recv","msg":{"type":"control_response","response":{"subtype":"success","request_id":"req_650e894e81ecb6c9","response":{"commands":[{"name":"deep-research","description":"Deep research harness — fan-out web searches, fetch sources, adversarially verify claims, synthesize a cited report. (dynamic workflow)","argumentHint":"","builtin":true},{"name":"design","description":"Make a new Design artifact from a brief","argumentHint":"[what to design]","builtin":true},{"name":"slides","description":"Make a new Slides deck artifact from a brief","argumentHint":"[what the deck is about]","builtin":true},{"name":"design-sync","description":"Push a React design system to claude.ai/design. This runs a converter that bundles the real component code (from Storybook or a bare package) and uploads it. Use when the user runs /design-sync or says \"sync my design system to Claude Design\".","argumentHint":"[\u003cproject hint, e.g. \"Acme DS\"\u003e]","builtin":true},{"name":"dataviz","description":"Use this skill whenever you are about to create ANY chart, graph, plot, dashboard, or data visualization, in ANY output medium — an HTML or React artifact, inline SVG, plotting code in any library (matplotlib, plotly, d3, Recharts, …), an image/PNG you will render and upload, or a chart shared into Slack. Read it BEFORE writing the first line of chart code, choosing chart colors, building a stat tile / meter / KPI row, or laying out a dashboard. When the destination is a first-party document connector (host-designated, never self-described) that renders live charts, hand it the rows (inline, or as an uploaded data file the chart cites) rather than a rendered PNG/SVG — a picture of a chart loses hover, data inspection and per-value comments. Produces visualizations that read as one system — elegant, accessible, consistent in light and dark — using a brand-neutral placeholder palette you swap for your own. Teaches a design-system-agnostic method: a form heuristic, a color formula with a runnable validator, mark specs, and interaction rules. A validated default palette is documented in `references/palette.md` — swap that file's values for your brand's. Triggers on: \"chart\", \"graph\", \"plot\", \"data viz\", \"visualization\", \"dashboard\", \"analytics\", \"visualize data\", \"categorical colors\", \"sequential / diverging palette\", \"stat tile\", \"sparkline\", \"heatmap\", \"legend\", \"axis\", \"tooltip\", \"chart colors\", \"color by series\".","argumentHint":"","builtin":true},{"name":"artifact-diagramming","description":"Diagramming know-how for Artifacts - when a picture earns its place, how to draw one that shows the real mechanism, and the inline-SVG mechanics that keep it legible in both themes.","argumentHint":"","builtin":true},{"name":"artifact-capabilities","description":"Runtime capabilities a published Artifact page can be granted — behavior static HTML cannot provide on its own, such as the page reading live or connected data, remembering what people do on it (a poll, a sign-up sheet, a checklist, a document edited in place — it saves new versions of itself), keeping state shared across viewers, knowing who is viewing, asking Claude a question of its own, storing files people add, handing the viewer a file to save, or using the viewer's camera, microphone, location, screen or device motion. Serves this user's live capability roster and the typed call definitions. Load it whenever any such runtime behavior would make an artifact more useful, before writing the page.","argumentHint":"","builtin":true},{"name":"update-config","description":"Use this skill to configure the Claude Code harness via settings.json. Automated behaviors (\"from now on when X\", \"each time X\", \"whenever X\", \"before/after X\") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions (\"allow X\", \"add permission\", \"move permission to\"), env vars (\"set X=Y\"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: \"allow npm commands\", \"add bq permission to global settings\", \"move permission to user settings\", \"set DEBUG=true\", \"when claude stops show X\". For simple settings like theme/model, suggest the /config command.","argumentHint":"","builtin":true},{"name":"verify","description":"Verify that a code change actually does what it's supposed to by exercising it end-to-end and observing behavior — drive the affected flow, not just tests or typecheck. Run before committing nontrivial changes; bootstraps this repo's project verify skill if none exists yet. Don't invoke it on a diff that only touches tests, docs, or other code with no runtime surface to drive (a change to product source always has one) — there's nothing to observe.","argumentHint":"","builtin":true},{"name":"debug","description":"Enable debug logging for this session and help diagnose issues","argumentHint":"[issue description]","builtin":true},{"name":"code-review","description":"Review the current diff, or a PR number/branch/path target, for correctness bugs (plus reuse/simplification/efficiency cleanups where the model's review recipe covers them) at the given effort level (low|medium|high|xhigh|max: from few high-confidence findings up to many, some of them uncertain; ultra: deep multi-agent review in the cloud); with no level given, it reuses the level you typed last. Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review. Pass --max-findings \u003cn\u003e to report up to n findings, or --max-findings all for every finding. The choice stays until you pass --max-findings default. For ultra on a GitHub.com PR target, --post asks to post the finished review’s findings to the PR as a single comment from the user’s GitHub account (not a review; the launch dialog still confirms in interactive sessions, while non-interactive mode posts on the flag alone) and --no-post hides that option.","argumentHint":"[low|medium|high|xhigh|max|ultra] [--fix] [--comment] [\u003cpr#\u003e|\u003cbranch\u003e|\u003cpath\u003e] [--max-findings \u003cn\u003e|all]","aliases":["review"],"builtin":true},{"name":"simplify","description":"Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.","argumentHint":"[\u003ctarget\u003e]","builtin":true},{"name":"batch","description":"Research and plan a large-scale change, then execute it in parallel across 5–30 isolated worktree agents that each open a PR.","argumentHint":"\u003cinstruction\u003e","builtin":true},{"name":"fewer-permission-prompts","description":"Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.","argumentHint":"","builtin":true},{"name":"doctor","description":"Health-check the user's Claude Code setup and fix issues: diagnose installation health — what the `claude doctor` terminal diagnostics cover — from local data (duplicate or leftover installs, PATH, unparseable settings files, broken or colliding agent definitions, skills whose frontmatter fails to parse); find unused skills, MCP servers, and plugins versus their context cost and disable dead weight; deduplicate local CLAUDE.md files against checked-in ones; trim checked-in CLAUDE.md files by cutting content a session could derive from the codebase (directory layouts, tech-stack lists, architecture overviews) while keeping gotchas, rationale, and non-standard conventions; migrate always-loaded CLAUDE.md guidance into lazy skills and nested CLAUDE.md files; flag slow hooks and context-heavy extensions; check the installed version is current; make auto mode the default permission mode; and pre-approve frequently denied read-only commands. Use when the user asks for a doctor run, checkup, audit, tune-up, or cleanup of their Claude Code setup or configuration.","argumentHint":"[prompt-audit [\u003cpath\u003e]]","aliases":["checkup"],"builtin":true},{"name":"loop","description":"Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace.","argumentHint":"[interval] [prompt]","aliases":["proactive"],"builtin":true},{"name":"schedule","description":"Create, update, list, or run scheduled cloud agents (routines) that execute on a cron schedule.","argumentHint":"","aliases":["routines"],"builtin":true},{"name":"claude-api","description":"Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.\nTRIGGER — read BEFORE opening the target file; don't skip because it \"looks like a one-liner\" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).\nSKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).","argumentHint":"","builtin":true},{"name":"workflow-authoring","description":"Reference for writing a Workflow tool script (script API and gotchas, resume, quality patterns, worked examples). Load before authoring a script for a workflow the user already opted into; it does not itself authorize running one.","argumentHint":"","builtin":true},{"name":"run","description":"Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).","argumentHint":"","builtin":true},{"name":"run-skill-generator","description":"Author or improve the run-\u003cunit\u003e skill - a per-project skill that tells agents how to build, launch, and drive this project's app. Use when the user asks to set up the project, get it running, write run instructions, or verify build/run steps work from a clean environment.","argumentHint":"","builtin":true},{"name":"plugin-authoring","description":"Make a mod: a change to Claude's own interface or behaviour, such as a live pane or panel, a band above the prompt, a status line, a toast, a slash command, or a hook on tool calls or prompts. Use to make, change or debug a mod or its hooks module.","argumentHint":"","builtin":true},{"name":"advisor","description":"Let Claude consult a stronger model at key moments","argumentHint":"[fable|opus|sonnet|off]","builtin":true},{"name":"agents","description":"(removed) Ask Claude to create/manage subagents, or edit .claude/agents/","argumentHint":"","builtin":true},{"name":"auto-mode-setup","description":"Teach auto mode about your environment, plus optional rule tweaks","argumentHint":"[--request-id \u003cuuid\u003e] (--wizard posture=… scope=… depth=… --propose | --expect-sha256 \u003c64-hex\u003e --apply-file \u003cpath\u003e)","builtin":true},{"name":"autocompact","description":"Configure the auto-compact window size","argumentHint":"[auto|\u003ctokens\u003e]","builtin":true},{"name":"clear","description":"Start a new session with empty context; previous session stays on disk (resumable with /resume)","argumentHint":"[name]","aliases":["reset","new"],"builtin":true},{"name":"color","description":"Set the prompt bar color for this session","argumentHint":"[red|blue|green|yellow|purple|orange|pink|cyan|default]","builtin":true},{"name":"compact","description":"Free up context by summarizing the conversation so far","argumentHint":"\u003coptional custom summarization instructions\u003e","builtin":true},{"name":"config","description":"Set a setting by key","argumentHint":"key=value","aliases":["settings"],"builtin":true},{"name":"output-style","description":"List output styles or switch to one","argumentHint":"[style]","builtin":true},{"name":"context","description":"Show current context usage","argumentHint":"","builtin":true},{"name":"effort","description":"Set effort level for model usage","argumentHint":"\u003clow|medium|high|xhigh|max|auto|ultracode [on|off]\u003e","builtin":true},{"name":"fast","description":"Toggle fast mode (Opus 5.5)","argumentHint":"[on|off]","builtin":true},{"name":"focus","description":"Toggle focus view: just your prompt, summary, and response","argumentHint":"[on|off]","builtin":true},{"name":"heapdump","description":"Dump the JS heap to the Desktop; on Linux with no Desktop folder, the home directory","argumentHint":"","builtin":true},{"name":"init","description":"Initialize a new CLAUDE.md file with codebase documentation","argumentHint":"","builtin":true},{"name":"mcp","description":"Manage MCP servers","argumentHint":"[reconnect|enable|disable [\u003cserver\u003e|all]]","builtin":true},{"name":"import","description":"Import config from another AI coding agent","argumentHint":"","builtin":true},{"name":"model","description":"Set the AI model for Claude Code","argumentHint":"\u003cmodel\u003e","builtin":true},{"name":"__remote-workflow","description":"Run the workflow script delivered in this session environment (server-launched sessions only)","argumentHint":"","builtin":true},{"name":"workflow-launch-exec","description":"Execute a server-launched workflow handoff (workflow_launch event sessions only)","argumentHint":"","builtin":true},{"name":"reload-plugins","description":"Activate pending plugin changes in the current session","argumentHint":"[--force]","builtin":true},{"name":"reload-skills","description":"Pick up skills added or changed on disk during this session","argumentHint":"","builtin":true},{"name":"rename","description":"Rename the current conversation","argumentHint":"[name]","aliases":["name"],"builtin":true},{"name":"ultrareview","description":"Start a cloud agent that finds and verifies bugs in your branch (~15-25 min, $5-$25 USD) · Runs in a cloud session. See https://code.claude.com/docs/en/claude-code-on-the-web","argumentHint":"","builtin":true},{"name":"security-review","description":"Complete a security review of the pending changes on the current branch","argumentHint":"","builtin":true},{"name":"usage-credits","description":"Configure usage credits or request them from your admin when you hit a limit","argumentHint":"","builtin":true},{"name":"extra-usage","description":"Renamed to /usage-credits","argumentHint":"","builtin":true},{"name":"usage","description":"Show session cost, plan usage, and what's contributing to your limits","argumentHint":"","aliases":["cost","stats"],"builtin":true},{"name":"insights","description":"Generate a report analyzing your Claude Code sessions","argumentHint":"","builtin":true},{"name":"recap","description":"Generate a one-line session recap now","argumentHint":"","builtin":true},{"name":"skill-doctor","description":"Show which loaded skills are unused and costing context","argumentHint":"","builtin":true},{"name":"goal","description":"Set a goal — keep working until the condition is met","argumentHint":"","builtin":true},{"name":"design-consent","description":"Grant Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"design-revoke","description":"Revoke Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"list-agents","description":"List subagents, teammates, and other Claude sessions you can message","argumentHint":"","aliases":["peers"],"builtin":true},{"name":"team-onboarding","description":"Help teammates ramp on Claude Code with a guide from your usage","argumentHint":"","builtin":true},{"name":"docs","description":"docs (editable docs people share and comment on; the default for any document, named as a doc or not: a document, report, proposal, resume, cover letter, letter, contract, policy, form, template, worksheet, essay, handbook, guide, how-to, cheat sheet, SOP or other writing to keep, share, collaborate on, send, submit, print or sign; a doc exports to Word, PDF, Markdown or Google Docs, so needing a file to send, attach, upload, submit or print is no reason to pick Word, and a file nobody asked for is a doc, not Word; a plan, comparison, summary or notes asked in chat stays in chat; a pasted claude.ai artifact link may be a doc: check with docs tools first; Word or another file format named, tracked changes wanted, or a .docx to change or use as a template → that format's skill): making one → if no docs-connector instructions are in context, call its `guide` (topic.instructions) first; then create the doc (headings only, no body) before any search, file read or plan, even with files attached. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docs"]},{"name":"docx","description":"Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx) or Word templates (.dotx). Triggers include: any mention of Microsoft Word Documents, such as 'Word doc', 'word document', '.docx', '.dotx', 'microsoft doc'. Also use when extracting or reorganizing content from .docx or .dotx files, inserting or replacing images in documents, find-and-replace in Word files, working with tracked changes or comments, or converting content into a polished Word document. If the user asks for a deliverable as a Word or .docx file (to download, email or print), use this skill. However, if they ask for a document, page, report, memo, or notes WITHOUT naming a file format and the session offers Claude's own dedicated document or page skill or connector, use that instead, even if they will email or print it. Do NOT use for PDFs, spreadsheets, Google Docs, or coding unrelated to document generation. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docx"]},{"name":"google-workspace","description":"Read this before the first Google Drive, Docs, Sheets or Slides connector call whenever the task creates or changes a Google file. Use this skill whenever the user wants to create or change a Google Doc, Sheet or Slides file in their Google Drive. Triggers include: a request that names Google Docs, Sheets, Slides or Drive and asks to make, edit, format, copy or rename a file; a docs.google.com link with a request to change that file, even a one-line fix or suggested edits; and any follow-up change to a Google file from earlier in the chat, even \"change it\" or \"add a tab\". Includes helper scripts for document positions, cell ranges and slide layout. However, if the user asks for a doc, deck or spreadsheet without naming Google, or gives a Google file only as source material for something new, use Claude's own output type instead. Do NOT use for read-only questions about a Google file, or for Word, Excel, PowerPoint or PDF files. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:google-workspace"]},{"name":"import-memory","description":"Import a memory export from another AI assistant into Claude's memory — conversationally, additively, and with the content treated as data. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:import-memory"]},{"name":"morning","description":"Render the user's morning brief as a styled HTML artifact, or set it up as a recurring weekday task. Use only when the user explicitly asks to run, see, or set up their morning brief, or if they invoke /morning by name. A question about their day, schedule, or calendar is not by itself a request for the brief; answer it directly instead. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:morning"]},{"name":"pdf","description":"Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text/tables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pdf"]},{"name":"pptx","description":"Use this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations as PowerPoint (.pptx) files; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an email, summary, or creating a different type of slide deck); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates (.potx), layouts, speaker notes, or comments. Trigger whenever the user asks for a PowerPoint or .pptx file, or references a .pptx or .potx filename, regardless of what they plan to do with the content afterward. However, when the user asks for a deck, slides, a slide deck, or a presentation without naming a file format, default to using a dedicated slide-deck artifact type or a separate slides skill if this session offers one; otherwise, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pptx"]},{"name":"skill-creator","description":"Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:skill-creator"]},{"name":"xlsx","description":"Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .xltx, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like \"the xlsx in my downloads\") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:xlsx"]}],"agents":[{"name":"claude","description":"Catch-all for any task that doesn't fit a more specific agent. FleetView's default when no agent name is typed."},{"name":"claude-code-guide","description":"Use this agent when the user asks questions (\"Can Claude...\", \"Does Claude...\", \"How do I...\") about: (1) Claude Code (the CLI tool) - features, hooks, slash commands, MCP servers, settings, IDE integrations, keyboard shortcuts; (2) Claude Agent SDK - building custom agents; (3) Claude API (formerly Anthropic API) - Messages API for directly passing messages to Claude, Tool Runner (`client.beta.messages.tool_runner`) for running an agentic loop over your own tools, manual tool-use loops, Managed Agents for server-hosted agents with a managed sandbox, prompt caching, and general Anthropic SDK usage; (4) Claude Tag (Claude in Slack) - what it is, setting it up for a Slack workspace, `/install-slack-app`; (5) `claude plugin eval` (writing and running plugin eval suites, its JSON/report, sandbox, CI) and the `/skill-doctor` report. **IMPORTANT:** Before spawning a new agent, check if there is already a running or recently completed claude-code-guide agent that you can continue via SendMessage.","model":"haiku"},{"name":"Explore","description":"Fast read-only search agent for locating code. Use it to find files by pattern (eg. \"src/components/**/*.tsx\"), grep for symbols or keywords (eg. \"API endpoints\"), or answer \"where is X defined / which files reference Y.\" Do NOT use it for code review, design-doc auditing, cross-file consistency checks, or open-ended analysis — it reads excerpts rather than whole files and will miss content past its read window. When calling, specify search breadth: \"quick\" for a single targeted lookup, \"medium\" for moderate exploration, or \"very thorough\" to search across multiple locations and naming conventions.","model":"inherit"},{"name":"general-purpose","description":"General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you."},{"name":"Plan","description":"Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.","model":"inherit"},{"name":"statusline-setup","description":"Use this agent to configure the user's Claude Code status line setting.","model":"sonnet"}],"output_style":"default","available_output_styles":["default","Proactive","Concise","Explanatory","Learning"],"user_output_styles_dir":"/Users/flamingcow/.claude/output-styles","models":[{"value":"default","resolvedModel":"claude-opus-5-5","displayName":"Default (recommended)","description":"Opus 5.5 · Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"opus","resolvedModel":"claude-opus-5-5","displayName":"Opus 5.5","description":"For complex work and everyday tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"fable","resolvedModel":"claude-fable-5-1","displayName":"Fable 5.1","description":"For your toughest challenges","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"sonnet","resolvedModel":"claude-sonnet-5-5","displayName":"Sonnet 5.5","description":"Most efficient for simpler tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"haiku","resolvedModel":"claude-haiku-5-5","displayName":"Haiku 5.5","description":"Fastest for quick answers","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-haiku-4-5-20251001","resolvedModel":"claude-haiku-4-5-20251001","displayName":"Haiku 4.5","description":"Fastest for quick answers"},{"value":"claude-sonnet-5","resolvedModel":"claude-sonnet-5","displayName":"Sonnet 5","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-5","resolvedModel":"claude-opus-5","displayName":"Opus 5","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-fable-5","resolvedModel":"claude-fable-5","displayName":"Fable 5","description":"Most capable for your hardest and longest-running tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-8","resolvedModel":"claude-opus-4-8","displayName":"Opus 4.8","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-opus-4-7","resolvedModel":"claude-opus-4-7","displayName":"Opus 4.7","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-6","resolvedModel":"claude-opus-4-6","displayName":"Opus 4.6","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-sonnet-4-6","resolvedModel":"claude-sonnet-4-6","displayName":"Sonnet 4.6","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true}],"account":{"email":"[redacted]","organization":"[redacted]'s Organization","subscriptionType":"Claude Max","apiProvider":"firstParty"},"pid":31924,"claude_code_version":"2.1.296","current_permission_mode":"bypassPermissions","hooks_applied":true,"feedback_mode":{"kind":"post"},"analytics_disabled":false,"remote_control_auto_enable":false,"remote_control_auto_connect_default":false,"remote_control_available":true,"remote_control_auto_on_by_default":false,"ide_rc_auto_enable_gate":true,"fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","session_state":"idle","capabilities":["ui_surface_v1"]},"pending_permission_requests":[],"pending_user_dialog_requests":[]}}} {"dir":"send","msg":{"message":{"content":"Create a file hello.txt containing exactly the word hi, then git add and git commit it with the message \"add hello\".","role":"user"},"parent_tool_use_id":null,"session_id":"","type":"user"}} -{"dir":"recv","msg":{"type":"control_request","request_id":"baf0fdad-9c90-4035-b888-23f9a3b30eb7","request":{"subtype":"mcp_message","server_name":"byre","message":{"jsonrpc":"2.0","method":"notifications/initialized"}}}} -{"dir":"send","msg":{"response":{"request_id":"baf0fdad-9c90-4035-b888-23f9a3b30eb7","response":{"mcp_response":{"jsonrpc":"2.0","id":null,"result":{}}},"subtype":"success"},"type":"control_response"}} -{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"running","uuid":"b94f3315-fb3d-4f04-b65a-4859cccf714d","session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca"}} -{"dir":"recv","msg":{"type":"control_request","request_id":"34917706-82a7-42c2-9cb5-91fbf968ee23","request":{"subtype":"mcp_message","server_name":"byre","message":{"method":"tools/list","jsonrpc":"2.0","id":1}}}} -{"dir":"send","msg":{"response":{"request_id":"34917706-82a7-42c2-9cb5-91fbf968ee23","response":{"mcp_response":{"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"report","description":"Report what you did this turn. You must call this before ending every turn. List every departure from the plan in deviations.","inputSchema":{"type":"object","properties":{"done":{"type":"array","items":{"type":"string"}},"open_questions":{"type":"array","items":{"type":"string"}},"next_steps":{"type":"array","items":{"type":"string"}},"risks":{"type":"array","items":{"type":"string"}},"deviations":{"type":"array","items":{"type":"string"}}},"required":["done"]}}]}}},"subtype":"success"},"type":"control_response"}} -{"dir":"recv","msg":{"type":"system","subtype":"init","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport2814973677/001","session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","tools":["Task","Artifact","ArtifactComments","ArtifactData","Bash","CronCreate","CronDelete","CronList","DesignSync","Edit","EnterWorktree","ExitWorktree","ListAgents","LSP","Monitor","NotebookEdit","PushNotification","Read","RemoteTrigger","ReportFindings","ScheduleWakeup","SendMessage","Skill","TaskStop","ToolSearch","WebFetch","WebSearch","Workflow","Write","mcp__byre__report"],"mcp_servers":[{"name":"claude.ai Claude Docs","status":"pending","source":"claudeai"},{"name":"claude.ai Heliosian","status":"pending","source":"claudeai"},{"name":"claude.ai Fastmail","status":"pending","source":"claudeai"},{"name":"claude.ai Google Calendar","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Gmail","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Google Drive","status":"needs-auth","source":"claudeai"},{"name":"byre","status":"connected","source":"sdk"}],"model":"claude-sonnet-5-5","permissionMode":"bypassPermissions","slash_commands":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","advisor","agents","auto-mode-setup","autocompact","clear","color","compact","config","output-style","context","effort","fast","focus","heapdump","init","mcp","import","model","__remote-workflow","workflow-launch-exec","reload-plugins","reload-skills","rename","ultrareview","security-review","usage-credits","extra-usage","usage","insights","recap","skill-doctor","goal","design-consent","design-revoke","list-agents","team-onboarding","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"terminal_slash_commands":["doctor","color","focus","reload-plugins"],"apiKeySource":"none","claude_code_version":"2.1.296","output_style":"default","agents":["claude","claude-code-guide","Explore","general-purpose","Plan","statusline-setup"],"skills":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"plugins":[{"name":"clangd-lsp","path":"/Users/flamingcow/.claude/plugins/cache/claude-plugins-official/clangd-lsp/1.0.0","source":"clangd-lsp@claude-plugins-official","version":"1.0.0"},{"name":"cc-plugin-agents-md","path":"builtin","source":"cc-plugin-agents-md@builtin"},{"name":"cc-plugin-telemetry","path":"builtin","source":"cc-plugin-telemetry@builtin"},{"name":"cc-plugin-plugin-authoring","path":"builtin","source":"cc-plugin-plugin-authoring@builtin"}],"capabilities":["interrupt_receipt_v1","interrupt_cancel_queued_v1","interrupt_send_now_v1","msg_lifecycle_v1","request_marker_lists_v1","sdk_mcp_tools_list_changed","sdk_mcp_manifests","mcp_read_resource_v1","mcp_tool_ui_meta_v1","ui_surface_v1"],"analytics_disabled":false,"product_feedback_disabled":false,"uuid":"78bbe182-3ab4-45fe-9cfe-4c6eaa7fafe2","memory_paths":{"auto":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport2814973677-001/memory/"},"scratchpad_path":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport2814973677-001/8c0d4bbf-f6d3-4111-8261-19ab3cc05bca/scratchpad","messaging_socket_path":"/tmp/cc-socks/27098.sock","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","per_turn_effort_active":true,"view_mode":"default"}} -{"dir":"recv","msg":{"type":"rate_limit_event","rate_limit_info":{"status":"allowed","resetsAt":1791990000,"rateLimitType":"five_hour","overageStatus":"rejected","overageDisabledReason":"org_level_disabled","isUsingOverage":false,"unifiedWindows":{"five_hour":{"utilization":0.04,"resetsAt":1791990000},"seven_day":{"utilization":0.27,"resetsAt":1792270800}}},"uuid":"7e4980db-f4bf-4ad8-b7b9-3670cbf2362a","session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca"}} -{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2FDR1h4ziZRKDHoRY2x","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01BPLc8EPVKw7n36xHkpFxNN","name":"Write","input":{"file_path":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport2814973677/001/hello.txt","content":"hi"},"caller":{"type":"direct"}}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":37866,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":37866},"output_tokens":16,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","uuid":"c14e54ff-b7ae-43de-b6ce-566cce8b2559","timestamp":"2026-10-14T13:56:54.766Z","request_id":"req_011Cg2FDQiqPPCZznQGbZRSk","wire_tool_inputs":{"toolu_01BPLc8EPVKw7n36xHkpFxNN":{"file_path":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport2814973677/001/hello.txt","content":"hi"}}}} -{"dir":"recv","msg":{"type":"control_request","request_id":"f4a0b76d-36c8-400f-9a7b-99cf0699a269","request":{"subtype":"hook_callback","callback_id":"hook_1","input":{"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","transcript_path":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport2814973677-001/8c0d4bbf-f6d3-4111-8261-19ab3cc05bca.jsonl","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport2814973677/001","scratchpad_dir":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport2814973677-001/8c0d4bbf-f6d3-4111-8261-19ab3cc05bca/scratchpad","prompt_id":"d249b7ba-071d-450e-9ab9-dd6e9a3f48f1","permission_mode":"bypassPermissions","effort":{"level":"medium"},"hook_event_name":"PostToolUse","tool_name":"Write","tool_input":{"file_path":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport2814973677/001/hello.txt","content":"hi"},"tool_response":{"type":"create","filePath":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport2814973677/001/hello.txt","content":"hi","structuredPatch":[],"originalFile":null,"userModified":false},"tool_use_id":"toolu_01BPLc8EPVKw7n36xHkpFxNN","duration_ms":2},"tool_use_id":"toolu_01BPLc8EPVKw7n36xHkpFxNN"}}} -{"dir":"send","msg":{"response":{"request_id":"f4a0b76d-36c8-400f-9a7b-99cf0699a269","response":{},"subtype":"success"},"type":"control_response"}} -{"dir":"recv","msg":{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01BPLc8EPVKw7n36xHkpFxNN","type":"tool_result","content":"File created successfully at: /private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport2814973677/001/hello.txt (file state is current in your context — no need to Read it back)"}]},"parent_tool_use_id":null,"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","uuid":"12abf763-d2c4-44a2-87c6-e4a00e8cd600","timestamp":"2026-10-14T13:56:54.777Z","tool_use_result":{"type":"create","filePath":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport2814973677/001/hello.txt","content":"hi","structuredPatch":[],"originalFile":null,"userModified":false},"tool_result_meta":[{"id":"toolu_01BPLc8EPVKw7n36xHkpFxNN","permission_decision":{"decision":"accept","source":"config","reason_type":"mode"}}]}} -{"dir":"recv","msg":{"type":"system","subtype":"thinking_tokens","estimated_tokens":50,"estimated_tokens_delta":50,"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","uuid":"7ba54341-2216-424c-8ae4-2cc39ff30b5e"}} -{"dir":"recv","msg":{"type":"system","subtype":"thinking_tokens","estimated_tokens":240,"estimated_tokens_delta":190,"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","uuid":"00bf3c0e-2430-4af2-a4b5-74b82c20de95"}} -{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2FDaziN5JhzKHq33Bxq","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"","signature":"CAQSuAcKEAgSGAI4AUIIdGhpbmtpbmcSDJYby/04pwKWg2I60xoMqruumhTaSpLMr0znIjCqk1kZMK1evR/U/tOTmTl+mlAK1lcGGwuc2mEF9dejLh0VAGRFaparBbpmIOOjWAUq1Qb/hbycbTmK2S/Pjfr/JInxSbKE+A7k6iHV43tWWfz0E6bSGEd+pxj/y/icJXLbnAXs372B6uCi0/d9IyfnRyW8x8dSaNuff5sCh61FjAPKLj0Cqi0N/c4GK/1COeNrmEAS1rMG+5QVLYozzhQGvIsMBPTl+NsoKJTFPw9rNXeCha0EpmGxsgheUkzxMTzI8e02QtEwMPNsFNwNfxTlcXc1UD2r89uETWPrRHMRL5aYm+bD3Y4umJlnAot8rp9kTgherOH6v/Y73GpENFwIi66dR0CSTPsvjr18FrRuTwb/3e00k2QdJoGPb34Jfyoze6LN3+ZDSnqKi3LBdl9M0cKxe3+2mea0ETJE350V+DygRxhy6ZpXzI1BC2sePU0vCyOEmSYwFreAZaR526nN6Hfba4NZ6P78tJGrNbLyZLCQoFYOb8M+c1DlK2fks2tOkqq/6F/md+psMT5NWwSpl12664lXWJEGQ8Gpi1YrhNc7NVh6f/h3J0bHKeGw90uEKV0a/HcBPqyqPPMXYwS91iaN3myybdKBZ9PjXPU6KtBl1TzV2gtHD3lVYJcvnAyeyEiRi0tt6FAe+9gv3aoL7Ncrs2JpYQ7S+jMlTLEaFitClxKH36ag7/+/fTFmTEqXu/77ICFxyaQ9TsB4OgQG9JYFw4EMZ2ef4Bx1Ri6jTLNtyN2eMBZ1S9ar4AFbJHqL/6pHemovlgPxsFEzJS5q5mhYUO2MsNk8QZl/0nzfeCRohR7gyGUs24SC9sKsyqA0k9jg4HnerroP1lQE1HEsT/deNxTf9cw4RGJkYf+gUwr9BG9GU99tmLd49g9TohFQ1F5FORtaXdMTYUc44M/4ShlIC4NB6AIvXDjWf0EwrTI9k+8qVYhHGfp0JxlqoDI1V146q81KXLW2BDSQAAOQmwjNK5wDsglvdRWNa9F/3BDnIZyvf2zixy9vzdaT44BLKqtMpy5+T238xF1DxZWLP8qXb78C/UTMxr3MlSHcWV6TCzgXOZPKz9H10Ava407E/xjYVILeiIw2EwWunpzGNYaRyU23i/OUyFNl8wc/gdjkzrasJ0F0WWm2sMmXcqN4iIt2pXY5YAyFZjQfe9klYERDDLx4+TuPun3JYYAqib6OX8T4uVq+GAE="}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":4712,"cache_read_input_tokens":37866,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":4712},"output_tokens":2,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","uuid":"798e0986-8b80-48e4-82c4-3fe1dd28a66c","timestamp":"2026-10-14T13:56:56.997Z","request_id":"req_011Cg2FDaiMMMLvE3KCnNxsn","thinking_duration_ms":1547,"thinking_display":"updates"}} -{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2FDaziN5JhzKHq33Bxq","type":"message","role":"assistant","content":[{"type":"text","text":"Authorization for the add and the commit: the user's words in this turn, \"then git add and git commit it with the message \"add hello\"\"."}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":4712,"cache_read_input_tokens":37866,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":4712},"output_tokens":2,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","uuid":"a9838d8d-c423-46fc-bdea-96e246bc8046","timestamp":"2026-10-14T13:56:56.998Z","request_id":"req_011Cg2FDaiMMMLvE3KCnNxsn"}} -{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2FDaziN5JhzKHq33Bxq","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01A6iZvcrnDU3tbD9QRMr1RY","name":"Bash","input":{"command":"timeout 20 git add hello.txt","description":"Stage hello.txt","timeout":30000},"caller":{"type":"direct"}}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":4712,"cache_read_input_tokens":37866,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":4712},"output_tokens":2,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","uuid":"2f2bda0b-d81a-4e62-ac8c-586d2917dc67","timestamp":"2026-10-14T13:56:57.382Z","request_id":"req_011Cg2FDaiMMMLvE3KCnNxsn","wire_tool_inputs":{"toolu_01A6iZvcrnDU3tbD9QRMr1RY":{"command":"timeout 20 git add hello.txt","description":"Stage hello.txt","timeout":30000}}}} -{"dir":"recv","msg":{"type":"control_request","request_id":"f9e1ac64-5d43-4968-8c54-5239f8313d53","request":{"subtype":"hook_callback","callback_id":"hook_1","input":{"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","transcript_path":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport2814973677-001/8c0d4bbf-f6d3-4111-8261-19ab3cc05bca.jsonl","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport2814973677/001","scratchpad_dir":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport2814973677-001/8c0d4bbf-f6d3-4111-8261-19ab3cc05bca/scratchpad","prompt_id":"d249b7ba-071d-450e-9ab9-dd6e9a3f48f1","permission_mode":"bypassPermissions","effort":{"level":"medium"},"hook_event_name":"PostToolUse","tool_name":"Bash","tool_input":{"command":"timeout 20 git add hello.txt","timeout":30000,"description":"Stage hello.txt"},"tool_response":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false},"tool_use_id":"toolu_01A6iZvcrnDU3tbD9QRMr1RY","duration_ms":66},"tool_use_id":"toolu_01A6iZvcrnDU3tbD9QRMr1RY"}}} -{"dir":"send","msg":{"response":{"request_id":"f9e1ac64-5d43-4968-8c54-5239f8313d53","response":{},"subtype":"success"},"type":"control_response"}} -{"dir":"recv","msg":{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01A6iZvcrnDU3tbD9QRMr1RY","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","uuid":"8c6384ee-a31f-41c3-918b-0e6da311ebf5","timestamp":"2026-10-14T13:56:57.466Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false},"tool_result_meta":[{"id":"toolu_01A6iZvcrnDU3tbD9QRMr1RY","permission_decision":{"decision":"accept","source":"config","reason_type":"mode"}}]}} -{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2FDnaiTrzTpzjKLqsub","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Srh8GQJQFnSGEU9xmLKvEM","name":"Bash","input":{"command":"timeout 20 git commit -m \"add hello\" -- hello.txt # USER EXPLICITLY AUTHORIZED THIS COMMIT IN THE IMMEDIATELY PRECEDING REQUEST","description":"Commit hello.txt","timeout":30000},"caller":{"type":"direct"}}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":332,"cache_read_input_tokens":42578,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":332},"output_tokens":16,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","uuid":"56a57bf0-2b9b-48a8-a223-65a5f9e5ad5f","timestamp":"2026-10-14T13:56:58.842Z","request_id":"req_011Cg2FDnHN4jpaw8fK5i7xY","wire_tool_inputs":{"toolu_01Srh8GQJQFnSGEU9xmLKvEM":{"command":"timeout 20 git commit -m \"add hello\" -- hello.txt # USER EXPLICITLY AUTHORIZED THIS COMMIT IN THE IMMEDIATELY PRECEDING REQUEST","description":"Commit hello.txt","timeout":30000}}}} -{"dir":"recv","msg":{"type":"system","subtype":"vcs_state_changed","kind":"commit","branch":"main","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport2814973677/001","uuid":"81f9a993-fcd9-4026-86c0-f92e4b79213e","session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca"}} -{"dir":"recv","msg":{"type":"control_request","request_id":"ce6fb82a-8c8b-4c10-bcef-385bb4b78095","request":{"subtype":"hook_callback","callback_id":"hook_1","input":{"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","transcript_path":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport2814973677-001/8c0d4bbf-f6d3-4111-8261-19ab3cc05bca.jsonl","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport2814973677/001","scratchpad_dir":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport2814973677-001/8c0d4bbf-f6d3-4111-8261-19ab3cc05bca/scratchpad","prompt_id":"d249b7ba-071d-450e-9ab9-dd6e9a3f48f1","permission_mode":"bypassPermissions","effort":{"level":"medium"},"hook_event_name":"PostToolUse","tool_name":"Bash","tool_input":{"command":"timeout 20 git commit -m \"add hello\" -- hello.txt # USER EXPLICITLY AUTHORIZED THIS COMMIT IN THE IMMEDIATELY PRECEDING REQUEST","timeout":30000,"description":"Commit hello.txt"},"tool_response":{"stdout":"[main 8d8ef52] add hello\n 1 file changed, 1 insertion(+)\n create mode 100644 hello.txt","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false,"gitOperation":{"commit":{"sha":"8d8ef52","kind":"committed","branch":"main"}}},"tool_use_id":"toolu_01Srh8GQJQFnSGEU9xmLKvEM","duration_ms":29},"tool_use_id":"toolu_01Srh8GQJQFnSGEU9xmLKvEM"}}} -{"dir":"send","msg":{"response":{"request_id":"ce6fb82a-8c8b-4c10-bcef-385bb4b78095","response":{},"subtype":"success"},"type":"control_response"}} -{"dir":"recv","msg":{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Srh8GQJQFnSGEU9xmLKvEM","type":"tool_result","content":"[main 8d8ef52] add hello\n 1 file changed, 1 insertion(+)\n create mode 100644 hello.txt","is_error":false}]},"parent_tool_use_id":null,"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","uuid":"81f6aefb-b6ce-4db7-b42c-a2c09f0fbbc7","timestamp":"2026-10-14T13:56:58.889Z","tool_use_result":{"stdout":"[main 8d8ef52] add hello\n 1 file changed, 1 insertion(+)\n create mode 100644 hello.txt","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false,"gitOperation":{"commit":{"sha":"8d8ef52","kind":"committed","branch":"main"}}},"tool_result_meta":[{"id":"toolu_01Srh8GQJQFnSGEU9xmLKvEM","permission_decision":{"decision":"accept","source":"config","reason_type":"mode"}}]}} -{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2FDteMhvSCmgykXQ6eW","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_011w3Bz78nNUBo1J4iPdCHz3","name":"mcp__byre__report","input":{"done":["Created hello.txt containing exactly 'hi' (no trailing newline)","git add hello.txt","git commit -m 'add hello' (8d8ef52 on main)"]},"caller":{"type":"direct"}}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":245,"cache_read_input_tokens":42910,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":245},"output_tokens":24,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","uuid":"953dce50-8fe8-4210-8a5f-7c1bef7d031a","timestamp":"2026-10-14T13:57:00.231Z","request_id":"req_011Cg2FDtNj9RRtgoRCGydtd","wire_tool_inputs":{"toolu_011w3Bz78nNUBo1J4iPdCHz3":{"done":["Created hello.txt containing exactly 'hi' (no trailing newline)","git add hello.txt","git commit -m 'add hello' (8d8ef52 on main)"]}},"tool_use_meta":[{"id":"toolu_011w3Bz78nNUBo1J4iPdCHz3","display_name":"Report","server_display_name":"byre"}]}} -{"dir":"recv","msg":{"type":"control_request","request_id":"665b8de4-0cde-4e9b-b6e7-205ca3174874","request":{"subtype":"mcp_message","server_name":"byre","message":{"method":"tools/call","params":{"name":"report","arguments":{"done":["Created hello.txt containing exactly 'hi' (no trailing newline)","git add hello.txt","git commit -m 'add hello' (8d8ef52 on main)"]},"_meta":{"claudecode/toolUseId":"toolu_011w3Bz78nNUBo1J4iPdCHz3","progressToken":2}},"jsonrpc":"2.0","id":2}}}} -{"dir":"send","msg":{"response":{"request_id":"665b8de4-0cde-4e9b-b6e7-205ca3174874","response":{"mcp_response":{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"report filed"}]}}},"subtype":"success"},"type":"control_response"}} -{"dir":"recv","msg":{"type":"control_request","request_id":"dc3fe015-a478-4f85-8bbb-9ad3bc8f7c37","request":{"subtype":"hook_callback","callback_id":"hook_1","input":{"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","transcript_path":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport2814973677-001/8c0d4bbf-f6d3-4111-8261-19ab3cc05bca.jsonl","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport2814973677/001","scratchpad_dir":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport2814973677-001/8c0d4bbf-f6d3-4111-8261-19ab3cc05bca/scratchpad","prompt_id":"d249b7ba-071d-450e-9ab9-dd6e9a3f48f1","permission_mode":"bypassPermissions","effort":{"level":"medium"},"hook_event_name":"PostToolUse","tool_name":"mcp__byre__report","tool_input":{"done":["Created hello.txt containing exactly 'hi' (no trailing newline)","git add hello.txt","git commit -m 'add hello' (8d8ef52 on main)"]},"tool_response":[{"type":"text","text":"report filed"}],"tool_use_id":"toolu_011w3Bz78nNUBo1J4iPdCHz3","duration_ms":2,"mcp_server":{"name":"byre","source":"sdk"}},"tool_use_id":"toolu_011w3Bz78nNUBo1J4iPdCHz3"}}} -{"dir":"send","msg":{"response":{"request_id":"dc3fe015-a478-4f85-8bbb-9ad3bc8f7c37","response":{},"subtype":"success"},"type":"control_response"}} -{"dir":"recv","msg":{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_011w3Bz78nNUBo1J4iPdCHz3","type":"tool_result","content":[{"type":"text","text":"report filed"}]}]},"parent_tool_use_id":null,"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","uuid":"7df4f2cf-9b34-45b4-90ee-9db3b64b1434","timestamp":"2026-10-14T13:57:00.236Z","tool_use_result":[{"type":"text","text":"report filed"}],"tool_result_meta":[{"id":"toolu_011w3Bz78nNUBo1J4iPdCHz3","permission_decision":{"decision":"accept","source":"config","reason_type":"mode"}}]}} -{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2FDzMttqJBTtsca3MAf","type":"message","role":"assistant","content":[{"type":"text","text":"I created `hello.txt` containing exactly `hi`, with no trailing newline. I staged it with `git add` and committed it on `main` as `8d8ef52` with the message \"add hello\".\n\nI didn't push."}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":144,"cache_read_input_tokens":43155,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":144},"output_tokens":70,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","uuid":"8ce469cc-1c81-4083-8e2b-d8c5fda37fcc","timestamp":"2026-10-14T13:57:01.375Z","request_id":"req_011Cg2FDz52cbZzPvQB55e6Q"}} -{"dir":"recv","msg":{"type":"control_request","request_id":"87613379-85ba-4619-b3e8-b6fdb7b0b661","request":{"subtype":"hook_callback","callback_id":"hook_0","input":{"session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","transcript_path":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport2814973677-001/8c0d4bbf-f6d3-4111-8261-19ab3cc05bca.jsonl","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport2814973677/001","scratchpad_dir":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport2814973677-001/8c0d4bbf-f6d3-4111-8261-19ab3cc05bca/scratchpad","prompt_id":"d249b7ba-071d-450e-9ab9-dd6e9a3f48f1","permission_mode":"bypassPermissions","effort":{"level":"medium"},"hook_event_name":"Stop","stop_hook_active":false,"last_assistant_message":"I created `hello.txt` containing exactly `hi`, with no trailing newline. I staged it with `git add` and committed it on `main` as `8d8ef52` with the message \"add hello\".\n\nI didn't push.","background_tasks":[],"session_crons":[]},"tool_use_id":"bb61afb8-88a2-471c-abf0-c3f21b925c87"}}} -{"dir":"send","msg":{"response":{"request_id":"87613379-85ba-4619-b3e8-b6fdb7b0b661","response":{},"subtype":"success"},"type":"control_response"}} -{"dir":"recv","msg":{"duration_api_ms":9692,"stop_reason":"end_turn","session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca","total_cost_usd":0.1978058,"usage":{"input_tokens":10,"cache_creation_input_tokens":43299,"cache_read_input_tokens":166509,"output_tokens":781,"output_tokens_details":{"thinking_tokens":142},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":43299,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":70,"cache_read_input_tokens":43155,"cache_creation_input_tokens":144,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":144},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":1199,"outputTokens":18,"cacheReadInputTokens":0,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.0001289,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":0,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"},"claude-sonnet-5-5":{"inputTokens":10,"outputTokens":781,"cacheReadInputTokens":166509,"cacheCreationInputTokens":43299,"webSearchRequests":0,"costUSD":0.19767690000000002,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":142,"canonicalModel":"claude-sonnet-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":5,"subtype":"success","api_error_status":null,"result":"I created `hello.txt` containing exactly `hi`, with no trailing newline. I staged it with `git add` and committed it on `main` as `8d8ef52` with the message \"add hello\".\n\nI didn't push.","ttft_ms":2409,"type":"result","duration_ms":9036,"uuid":"4a32b62b-7d95-43a6-bd9d-8ef310248cde","ttft_stream_ms":1428,"time_to_request_ms":26,"first_content_frame_ms":1428,"queued_turn_count":0,"result_index":0}} -{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"idle","uuid":"2254b41b-3fa8-41a6-84d0-5f7fbc9d147f","session_id":"8c0d4bbf-f6d3-4111-8261-19ab3cc05bca"}} +{"dir":"recv","msg":{"type":"control_request","request_id":"14399810-1594-49a4-8472-8d46dbee6ce0","request":{"subtype":"mcp_message","server_name":"byre","message":{"jsonrpc":"2.0","method":"notifications/initialized"}}}} +{"dir":"send","msg":{"response":{"request_id":"14399810-1594-49a4-8472-8d46dbee6ce0","response":{"mcp_response":{"jsonrpc":"2.0","id":null,"result":{}}},"subtype":"success"},"type":"control_response"}} +{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"running","uuid":"b376031c-8f55-4491-971f-25894fe4f915","session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8"}} +{"dir":"recv","msg":{"type":"control_request","request_id":"71d0e8d6-64d8-4010-95bf-6431af4986b9","request":{"subtype":"mcp_message","server_name":"byre","message":{"method":"tools/list","jsonrpc":"2.0","id":1}}}} +{"dir":"send","msg":{"response":{"request_id":"71d0e8d6-64d8-4010-95bf-6431af4986b9","response":{"mcp_response":{"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"report","description":"Report what you did this turn. You must call this before ending every turn. List every departure from the plan in deviations.","inputSchema":{"type":"object","properties":{"done":{"type":"array","items":{"type":"string"}},"open_questions":{"type":"array","items":{"type":"string"}},"next_steps":{"type":"array","items":{"type":"string"}},"risks":{"type":"array","items":{"type":"string"}},"deviations":{"type":"array","items":{"type":"string"}}},"required":["done"]}}]}}},"subtype":"success"},"type":"control_response"}} +{"dir":"recv","msg":{"type":"system","subtype":"init","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport1389302870/001","session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","tools":["Task","Artifact","ArtifactComments","ArtifactData","Bash","CronCreate","CronDelete","CronList","DesignSync","Edit","EnterWorktree","ExitWorktree","ListAgents","LSP","Monitor","NotebookEdit","PushNotification","Read","RemoteTrigger","ReportFindings","ScheduleWakeup","SendMessage","Skill","TaskStop","ToolSearch","WebFetch","WebSearch","Workflow","Write","mcp__byre__report"],"mcp_servers":[{"name":"byre","status":"connected","source":"sdk"}],"model":"claude-sonnet-5-5","permissionMode":"bypassPermissions","slash_commands":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","advisor","agents","auto-mode-setup","autocompact","clear","color","compact","config","output-style","context","effort","fast","focus","heapdump","init","mcp","import","model","__remote-workflow","workflow-launch-exec","reload-plugins","reload-skills","rename","ultrareview","security-review","usage-credits","extra-usage","usage","insights","recap","skill-doctor","goal","design-consent","design-revoke","list-agents","team-onboarding","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"terminal_slash_commands":["doctor","color","focus","reload-plugins"],"apiKeySource":"none","claude_code_version":"2.1.296","output_style":"default","agents":["claude","claude-code-guide","Explore","general-purpose","Plan","statusline-setup"],"skills":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"plugins":[{"name":"clangd-lsp","path":"/Users/flamingcow/.claude/plugins/cache/claude-plugins-official/clangd-lsp/1.0.0","source":"clangd-lsp@claude-plugins-official","version":"1.0.0"},{"name":"cc-plugin-agents-md","path":"builtin","source":"cc-plugin-agents-md@builtin"},{"name":"cc-plugin-telemetry","path":"builtin","source":"cc-plugin-telemetry@builtin"},{"name":"cc-plugin-plugin-authoring","path":"builtin","source":"cc-plugin-plugin-authoring@builtin"}],"capabilities":["interrupt_receipt_v1","interrupt_cancel_queued_v1","interrupt_send_now_v1","msg_lifecycle_v1","request_marker_lists_v1","sdk_mcp_tools_list_changed","sdk_mcp_manifests","mcp_read_resource_v1","mcp_tool_ui_meta_v1","ui_surface_v1"],"analytics_disabled":false,"product_feedback_disabled":false,"uuid":"7c7a46c2-2fd4-4e1d-953f-6a8ae6ad9ab5","memory_paths":{"auto":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport1389302870-001/memory/"},"scratchpad_path":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport1389302870-001/10728ddc-efa3-413f-b22f-6ee82e180af8/scratchpad","messaging_socket_path":"/tmp/cc-socks/31924.sock","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","per_turn_effort_active":true,"view_mode":"default"}} +{"dir":"recv","msg":{"type":"rate_limit_event","rate_limit_info":{"status":"allowed","resetsAt":1791990000,"rateLimitType":"five_hour","overageStatus":"rejected","overageDisabledReason":"org_level_disabled","isUsingOverage":false,"unifiedWindows":{"five_hour":{"utilization":0.1,"resetsAt":1791990000},"seven_day":{"utilization":0.27,"resetsAt":1792270800}}},"uuid":"207f520a-d85b-4e51-915a-256b297f1f57","session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8"}} +{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2Gx9HTbxditWoiEnQFP","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_017ads21R5ekigv94EkHb3dj","name":"Write","input":{"file_path":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport1389302870/001/hello.txt","content":"hi"},"caller":{"type":"direct"}}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":14881,"cache_read_input_tokens":22583,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":14881},"output_tokens":16,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","uuid":"24602a16-de55-42a8-9260-8fa410174bd6","timestamp":"2026-10-14T14:19:40.828Z","request_id":"req_011Cg2Gx92afYWd5EYieDex2","wire_tool_inputs":{"toolu_017ads21R5ekigv94EkHb3dj":{"file_path":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport1389302870/001/hello.txt","content":"hi"}}}} +{"dir":"recv","msg":{"type":"control_request","request_id":"3461ff4a-5cca-4486-b357-1382b28726fc","request":{"subtype":"hook_callback","callback_id":"hook_1","input":{"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","transcript_path":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport1389302870-001/10728ddc-efa3-413f-b22f-6ee82e180af8.jsonl","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport1389302870/001","scratchpad_dir":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport1389302870-001/10728ddc-efa3-413f-b22f-6ee82e180af8/scratchpad","prompt_id":"3ee39807-b326-40d9-9000-0221c4f70a94","permission_mode":"bypassPermissions","effort":{"level":"medium"},"hook_event_name":"PostToolUse","tool_name":"Write","tool_input":{"file_path":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport1389302870/001/hello.txt","content":"hi"},"tool_response":{"type":"create","filePath":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport1389302870/001/hello.txt","content":"hi","structuredPatch":[],"originalFile":null,"userModified":false},"tool_use_id":"toolu_017ads21R5ekigv94EkHb3dj","duration_ms":2},"tool_use_id":"toolu_017ads21R5ekigv94EkHb3dj"}}} +{"dir":"send","msg":{"response":{"request_id":"3461ff4a-5cca-4486-b357-1382b28726fc","response":{},"subtype":"success"},"type":"control_response"}} +{"dir":"recv","msg":{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_017ads21R5ekigv94EkHb3dj","type":"tool_result","content":"File created successfully at: /private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport1389302870/001/hello.txt (file state is current in your context — no need to Read it back)"}]},"parent_tool_use_id":null,"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","uuid":"413179fb-1b90-490a-a149-b33063620693","timestamp":"2026-10-14T14:19:40.840Z","tool_use_result":{"type":"create","filePath":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport1389302870/001/hello.txt","content":"hi","structuredPatch":[],"originalFile":null,"userModified":false},"tool_result_meta":[{"id":"toolu_017ads21R5ekigv94EkHb3dj","permission_decision":{"decision":"accept","source":"config","reason_type":"mode"}}]}} +{"dir":"recv","msg":{"type":"system","subtype":"thinking_tokens","estimated_tokens":50,"estimated_tokens_delta":50,"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","uuid":"aade7409-b39d-47ee-8b66-ad5fd553bd54"}} +{"dir":"recv","msg":{"type":"system","subtype":"thinking_tokens","estimated_tokens":241,"estimated_tokens_delta":191,"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","uuid":"93ed9a70-7a04-43c0-ab6a-0b833a0e6bc6"}} +{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2GxHbWZKsroXw32uwkH","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"","signature":"CAQSuwcKEAgSGAI4AUIIdGhpbmtpbmcSDCPJV4+dSfmbQPI66xoMuidTjRQc5OTn+QUVIjCgq+xQiLRvIBVGEqkVx86GT5QtauYmClva0gSefGIogAIc7wsxcVrHbpzEVYrcTBoq2Ab9+/yFsoh/tRweqgEBLtJZf0+Uu+LFRFvNXsB2Tdb8vfhCU/V5Onk1o40E6lD3W+8FXHagzs92WjkcRTKSOmbv7rF17c2Ox0SVv3Cp7bHkyClhsjeM3M52qzeyjCx+Qs+udHMGgR2lvJ+b44U2ilhcKtolsBLNzGaMFTUzcttSTP21wqDdUsTHS72HXt526AERSauwsH9KyqrBiH9UN+AdeVigjBv7SZSXt0iy5FLzVwEta1+OzEzOlBq18YxQQgnVvAg9PwWoA2rgqqhjGbu9corxF/HaG5hs7AeCVgYKu38Y4nfh/232aRi+1ybm7H2gs5rx6VZzRnne7uOWNP4u9uwe2uxsZwwD18gF/gXK6iViOFyYRcxMlQeMsJLOH4xjjc3cjXYzTT3iLy4E4JFSQQFRvIhnI1Wfa9mnC8S7REJssGN/H15lyfXSC7wdCWRpwf13vnwes4UPHF3y42yByY+SKf0CKVnx/PDlQlXj5p7hDDlkHQlSgZK2QP1Y5NIZYdxfFBOembGnc8nUY/jmRgsiFmymM8GgELqOgw2aLRpEgMPWYkY2Z6da4HPIFbAOzUrSu9+Cvw+SmH8YWWA0a6zO9fKo6wWNBQZQAWt2GSRixvLAHuk/8h/qj86d0LO/EJ0fQ79lA+u/IT5Saz31TmtRyHbj43s+WlXWLjB1HCsRa0mOterClN+vXK3gXhnbrIGfKo1CobXYrdKVy4VpEYSCm8ujMpBP9Q+hJw7ZAIHkmexdA1ILqd9HaC1V10Ilf2/EkSBZwgxWOLBKS9JsfEZjPSjvUDN3vlYTNljGv72JE+0wETt7ifWQVaA8EGd6fp3CahhWpULpTVm5z4tTgqcYvCXt5XLoIX59A3+RkxqD3BtosU7IGVZlACO8tSiDCWbe58FtduBIX3FsCKrm8CPt9Cz9is4bNw6Wyo9pfJFZlOsmUMjby+MEAVZz9kxVQHzK5B3Qm1ElmNZHFmVUpmtWNCD7CM5f7WHN8kMnFX1EeiOHwVWmkYhq9s+lj1LT4KvpJxMIjcirqyyDW9Uv7dk5bsuRW427uyhDLdXdefHNew8bCdLfXqdgZFYKwvW7MXGf0zgndBwg1X6pn0ihJDS4nCgLOZ/WFUAfKqfcxOOktXpHqjSqGAE="}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":4712,"cache_read_input_tokens":37464,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":4712},"output_tokens":3,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","uuid":"61abae30-e176-4fed-a9f2-2572e5ee873f","timestamp":"2026-10-14T14:19:43.132Z","request_id":"req_011Cg2GxHHfbocsnu5einYsF","thinking_duration_ms":1610,"thinking_display":"updates"}} +{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2GxHbWZKsroXw32uwkH","type":"message","role":"assistant","content":[{"type":"text","text":"Authorization for both commands, quoted from the current turn: \"git add and git commit it with the message \\\"add hello\\\"\"."}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":4712,"cache_read_input_tokens":37464,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":4712},"output_tokens":3,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","uuid":"56e648f5-b652-44de-aac8-7dcafb5f5873","timestamp":"2026-10-14T14:19:43.133Z","request_id":"req_011Cg2GxHHfbocsnu5einYsF"}} +{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2GxHbWZKsroXw32uwkH","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01PW43YmeqKAVFYwACdRzhqh","name":"Bash","input":{"command":"timeout 20 git add hello.txt","description":"Stage hello.txt","timeout":30000},"caller":{"type":"direct"}}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":4712,"cache_read_input_tokens":37464,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":4712},"output_tokens":3,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","uuid":"e8af26cd-035c-4035-a358-8ad6e97e50a4","timestamp":"2026-10-14T14:19:43.670Z","request_id":"req_011Cg2GxHHfbocsnu5einYsF","wire_tool_inputs":{"toolu_01PW43YmeqKAVFYwACdRzhqh":{"command":"timeout 20 git add hello.txt","description":"Stage hello.txt","timeout":30000}}}} +{"dir":"recv","msg":{"type":"control_request","request_id":"943d275f-2be4-4154-a2dd-20e4ba09ba52","request":{"subtype":"hook_callback","callback_id":"hook_1","input":{"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","transcript_path":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport1389302870-001/10728ddc-efa3-413f-b22f-6ee82e180af8.jsonl","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport1389302870/001","scratchpad_dir":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport1389302870-001/10728ddc-efa3-413f-b22f-6ee82e180af8/scratchpad","prompt_id":"3ee39807-b326-40d9-9000-0221c4f70a94","permission_mode":"bypassPermissions","effort":{"level":"medium"},"hook_event_name":"PostToolUse","tool_name":"Bash","tool_input":{"command":"timeout 20 git add hello.txt","timeout":30000,"description":"Stage hello.txt"},"tool_response":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false},"tool_use_id":"toolu_01PW43YmeqKAVFYwACdRzhqh","duration_ms":64},"tool_use_id":"toolu_01PW43YmeqKAVFYwACdRzhqh"}}} +{"dir":"send","msg":{"response":{"request_id":"943d275f-2be4-4154-a2dd-20e4ba09ba52","response":{},"subtype":"success"},"type":"control_response"}} +{"dir":"recv","msg":{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01PW43YmeqKAVFYwACdRzhqh","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","uuid":"85ee663a-6c43-4cbc-bf9c-932a41dcd227","timestamp":"2026-10-14T14:19:43.755Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false},"tool_result_meta":[{"id":"toolu_01PW43YmeqKAVFYwACdRzhqh","permission_decision":{"decision":"accept","source":"config","reason_type":"mode"}}]}} +{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2GxHbWZKsroXw32uwkH","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_015ZhVbzsLecfsUySb1c2n1e","name":"Bash","input":{"command":"timeout 20 git commit -m \"add hello\" -- hello.txt # USER EXPLICITLY AUTHORIZED THIS COMMIT IN THE IMMEDIATELY PRECEDING REQUEST","description":"Commit hello.txt","timeout":30000},"caller":{"type":"direct"}}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":4712,"cache_read_input_tokens":37464,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":4712},"output_tokens":3,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","uuid":"cf76b52c-9f06-443f-8bcd-dede8bf1cb08","timestamp":"2026-10-14T14:19:44.356Z","request_id":"req_011Cg2GxHHfbocsnu5einYsF","wire_tool_inputs":{"toolu_015ZhVbzsLecfsUySb1c2n1e":{"command":"timeout 20 git commit -m \"add hello\" -- hello.txt # USER EXPLICITLY AUTHORIZED THIS COMMIT IN THE IMMEDIATELY PRECEDING REQUEST","description":"Commit hello.txt","timeout":30000}}}} +{"dir":"recv","msg":{"type":"system","subtype":"vcs_state_changed","kind":"commit","branch":"main","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport1389302870/001","uuid":"7151e52a-8556-4119-89f1-9b258135aa72","session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8"}} +{"dir":"recv","msg":{"type":"control_request","request_id":"74d6223f-5503-4cff-aae2-79b567329a14","request":{"subtype":"hook_callback","callback_id":"hook_1","input":{"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","transcript_path":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport1389302870-001/10728ddc-efa3-413f-b22f-6ee82e180af8.jsonl","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport1389302870/001","scratchpad_dir":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport1389302870-001/10728ddc-efa3-413f-b22f-6ee82e180af8/scratchpad","prompt_id":"3ee39807-b326-40d9-9000-0221c4f70a94","permission_mode":"bypassPermissions","effort":{"level":"medium"},"hook_event_name":"PostToolUse","tool_name":"Bash","tool_input":{"command":"timeout 20 git commit -m \"add hello\" -- hello.txt # USER EXPLICITLY AUTHORIZED THIS COMMIT IN THE IMMEDIATELY PRECEDING REQUEST","timeout":30000,"description":"Commit hello.txt"},"tool_response":{"stdout":"[main 61bab5a] add hello\n 1 file changed, 1 insertion(+)\n create mode 100644 hello.txt","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false,"gitOperation":{"commit":{"sha":"61bab5a","kind":"committed","branch":"main"}}},"tool_use_id":"toolu_015ZhVbzsLecfsUySb1c2n1e","duration_ms":32},"tool_use_id":"toolu_015ZhVbzsLecfsUySb1c2n1e"}}} +{"dir":"send","msg":{"response":{"request_id":"74d6223f-5503-4cff-aae2-79b567329a14","response":{},"subtype":"success"},"type":"control_response"}} +{"dir":"recv","msg":{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_015ZhVbzsLecfsUySb1c2n1e","type":"tool_result","content":"[main 61bab5a] add hello\n 1 file changed, 1 insertion(+)\n create mode 100644 hello.txt","is_error":false}]},"parent_tool_use_id":null,"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","uuid":"88621452-fb77-4591-8ff1-e21e0ae6a365","timestamp":"2026-10-14T14:19:44.408Z","tool_use_result":{"stdout":"[main 61bab5a] add hello\n 1 file changed, 1 insertion(+)\n create mode 100644 hello.txt","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false,"gitOperation":{"commit":{"sha":"61bab5a","kind":"committed","branch":"main"}}},"tool_result_meta":[{"id":"toolu_015ZhVbzsLecfsUySb1c2n1e","permission_decision":{"decision":"accept","source":"config","reason_type":"mode"}}]}} +{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2GxYp4WTw3YhGtsEVmu","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01EnKB92LRqCjmeExrcVZAse","name":"mcp__byre__report","input":{"done":["Created hello.txt containing exactly 'hi' (no trailing newline)","git add hello.txt","git commit -m 'add hello' -- hello.txt (commit 61bab5a on main)"]},"caller":{"type":"direct"}}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":594,"cache_read_input_tokens":42176,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":594},"output_tokens":24,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","uuid":"20337a9a-98bc-48a8-9b53-57358b6b81af","timestamp":"2026-10-14T14:19:45.600Z","request_id":"req_011Cg2GxYXwxRabd3per1kFU","wire_tool_inputs":{"toolu_01EnKB92LRqCjmeExrcVZAse":{"done":["Created hello.txt containing exactly 'hi' (no trailing newline)","git add hello.txt","git commit -m 'add hello' -- hello.txt (commit 61bab5a on main)"]}},"tool_use_meta":[{"id":"toolu_01EnKB92LRqCjmeExrcVZAse","display_name":"Report","server_display_name":"byre"}]}} +{"dir":"recv","msg":{"type":"control_request","request_id":"7ddf014b-96ea-4d52-ba81-b9d29bd66023","request":{"subtype":"mcp_message","server_name":"byre","message":{"method":"tools/call","params":{"name":"report","arguments":{"done":["Created hello.txt containing exactly 'hi' (no trailing newline)","git add hello.txt","git commit -m 'add hello' -- hello.txt (commit 61bab5a on main)"]},"_meta":{"claudecode/toolUseId":"toolu_01EnKB92LRqCjmeExrcVZAse","progressToken":2}},"jsonrpc":"2.0","id":2}}}} +{"dir":"send","msg":{"response":{"request_id":"7ddf014b-96ea-4d52-ba81-b9d29bd66023","response":{"mcp_response":{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"report filed"}]}}},"subtype":"success"},"type":"control_response"}} +{"dir":"recv","msg":{"type":"control_request","request_id":"4aea9b83-599a-4a71-99f1-083b221a1223","request":{"subtype":"hook_callback","callback_id":"hook_1","input":{"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","transcript_path":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport1389302870-001/10728ddc-efa3-413f-b22f-6ee82e180af8.jsonl","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport1389302870/001","scratchpad_dir":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport1389302870-001/10728ddc-efa3-413f-b22f-6ee82e180af8/scratchpad","prompt_id":"3ee39807-b326-40d9-9000-0221c4f70a94","permission_mode":"bypassPermissions","effort":{"level":"medium"},"hook_event_name":"PostToolUse","tool_name":"mcp__byre__report","tool_input":{"done":["Created hello.txt containing exactly 'hi' (no trailing newline)","git add hello.txt","git commit -m 'add hello' -- hello.txt (commit 61bab5a on main)"]},"tool_response":[{"type":"text","text":"report filed"}],"tool_use_id":"toolu_01EnKB92LRqCjmeExrcVZAse","duration_ms":2,"mcp_server":{"name":"byre","source":"sdk"}},"tool_use_id":"toolu_01EnKB92LRqCjmeExrcVZAse"}}} +{"dir":"send","msg":{"response":{"request_id":"4aea9b83-599a-4a71-99f1-083b221a1223","response":{},"subtype":"success"},"type":"control_response"}} +{"dir":"recv","msg":{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01EnKB92LRqCjmeExrcVZAse","type":"tool_result","content":[{"type":"text","text":"report filed"}]}]},"parent_tool_use_id":null,"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","uuid":"7e50398e-cf92-4a85-ab3f-92f836c27b3a","timestamp":"2026-10-14T14:19:45.604Z","tool_use_result":[{"type":"text","text":"report filed"}],"tool_result_meta":[{"id":"toolu_01EnKB92LRqCjmeExrcVZAse","permission_decision":{"decision":"accept","source":"config","reason_type":"mode"}}]}} +{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2Gxdvt3ffUHcBsmoAfJ","type":"message","role":"assistant","content":[{"type":"text","text":"I created `hello.txt` containing exactly `hi`, with no trailing newline. I staged it with `git add` and committed it on `main` with the message \"add hello\" (commit `61bab5a`). I didn't push."}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":152,"cache_read_input_tokens":42770,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":152},"output_tokens":71,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","uuid":"719cae62-647f-43a5-995a-c6b2768d41b3","timestamp":"2026-10-14T14:19:46.627Z","request_id":"req_011Cg2GxdeGxF83B7miYHNH2"}} +{"dir":"recv","msg":{"type":"control_request","request_id":"c6960462-64e6-4c45-8a63-5e740eee1e3a","request":{"subtype":"hook_callback","callback_id":"hook_0","input":{"session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","transcript_path":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport1389302870-001/10728ddc-efa3-413f-b22f-6ee82e180af8.jsonl","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestCommitIdentityHooksAndReport1389302870/001","scratchpad_dir":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestCommitIdentityHooksAndReport1389302870-001/10728ddc-efa3-413f-b22f-6ee82e180af8/scratchpad","prompt_id":"3ee39807-b326-40d9-9000-0221c4f70a94","permission_mode":"bypassPermissions","effort":{"level":"medium"},"hook_event_name":"Stop","stop_hook_active":false,"last_assistant_message":"I created `hello.txt` containing exactly `hi`, with no trailing newline. I staged it with `git add` and committed it on `main` with the message \"add hello\" (commit `61bab5a`). I didn't push.","background_tasks":[],"session_crons":[]},"tool_use_id":"71e120d9-ceb6-4359-8f67-22573620af2d"}}} +{"dir":"send","msg":{"response":{"request_id":"c6960462-64e6-4c45-8a63-5e740eee1e3a","response":{},"subtype":"success"},"type":"control_response"}} +{"dir":"recv","msg":{"duration_api_ms":8323,"stop_reason":"end_turn","session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8","total_cost_usd":0.10384020000000001,"usage":{"input_tokens":8,"cache_creation_input_tokens":20339,"cache_read_input_tokens":144993,"output_tokens":784,"output_tokens_details":{"thinking_tokens":143},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":20339,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":71,"cache_read_input_tokens":42770,"cache_creation_input_tokens":152,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":152},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":1199,"outputTokens":18,"cacheReadInputTokens":0,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.0001289,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":0,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"},"claude-sonnet-5-5":{"inputTokens":8,"outputTokens":784,"cacheReadInputTokens":144993,"cacheCreationInputTokens":20339,"webSearchRequests":0,"costUSD":0.10371129999999999,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":143,"canonicalModel":"claude-sonnet-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":5,"subtype":"success","api_error_status":null,"result":"I created `hello.txt` containing exactly `hi`, with no trailing newline. I staged it with `git add` and committed it on `main` with the message \"add hello\" (commit `61bab5a`). I didn't push.","ttft_ms":1983,"type":"result","duration_ms":7802,"uuid":"99109ecc-0557-4324-9dfa-8b8b9d64805c","ttft_stream_ms":1136,"time_to_request_ms":20,"first_content_frame_ms":1136,"queued_turn_count":0,"result_index":0}} +{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"idle","uuid":"af7e6f10-963d-48cd-a121-1397f95cd11a","session_id":"10728ddc-efa3-413f-b22f-6ee82e180af8"}} diff --git a/internal/claude/testdata/transcripts/TestInterrupt.jsonl b/internal/claude/testdata/transcripts/TestInterrupt.jsonl index 1ae2ae9..20626d7 100644 --- a/internal/claude/testdata/transcripts/TestInterrupt.jsonl +++ b/internal/claude/testdata/transcripts/TestInterrupt.jsonl @@ -1,19 +1,22 @@ -{"dir":"send","msg":{"request":{"hooks":null,"subtype":"initialize"},"request_id":"req_b5bebca766f082d4","type":"control_request"}} -{"dir":"recv","msg":{"type":"control_response","response":{"subtype":"success","request_id":"req_b5bebca766f082d4","response":{"commands":[{"name":"deep-research","description":"Deep research harness — fan-out web searches, fetch sources, adversarially verify claims, synthesize a cited report. (dynamic workflow)","argumentHint":"","builtin":true},{"name":"design","description":"Make a new Design artifact from a brief","argumentHint":"[what to design]","builtin":true},{"name":"slides","description":"Make a new Slides deck artifact from a brief","argumentHint":"[what the deck is about]","builtin":true},{"name":"design-sync","description":"Push a React design system to claude.ai/design. This runs a converter that bundles the real component code (from Storybook or a bare package) and uploads it. Use when the user runs /design-sync or says \"sync my design system to Claude Design\".","argumentHint":"[\u003cproject hint, e.g. \"Acme DS\"\u003e]","builtin":true},{"name":"dataviz","description":"Use this skill whenever you are about to create ANY chart, graph, plot, dashboard, or data visualization, in ANY output medium — an HTML or React artifact, inline SVG, plotting code in any library (matplotlib, plotly, d3, Recharts, …), an image/PNG you will render and upload, or a chart shared into Slack. Read it BEFORE writing the first line of chart code, choosing chart colors, building a stat tile / meter / KPI row, or laying out a dashboard. When the destination is a first-party document connector (host-designated, never self-described) that renders live charts, hand it the rows (inline, or as an uploaded data file the chart cites) rather than a rendered PNG/SVG — a picture of a chart loses hover, data inspection and per-value comments. Produces visualizations that read as one system — elegant, accessible, consistent in light and dark — using a brand-neutral placeholder palette you swap for your own. Teaches a design-system-agnostic method: a form heuristic, a color formula with a runnable validator, mark specs, and interaction rules. A validated default palette is documented in `references/palette.md` — swap that file's values for your brand's. Triggers on: \"chart\", \"graph\", \"plot\", \"data viz\", \"visualization\", \"dashboard\", \"analytics\", \"visualize data\", \"categorical colors\", \"sequential / diverging palette\", \"stat tile\", \"sparkline\", \"heatmap\", \"legend\", \"axis\", \"tooltip\", \"chart colors\", \"color by series\".","argumentHint":"","builtin":true},{"name":"artifact-diagramming","description":"Diagramming know-how for Artifacts - when a picture earns its place, how to draw one that shows the real mechanism, and the inline-SVG mechanics that keep it legible in both themes.","argumentHint":"","builtin":true},{"name":"artifact-capabilities","description":"Runtime capabilities a published Artifact page can be granted — behavior static HTML cannot provide on its own, such as the page reading live or connected data, remembering what people do on it (a poll, a sign-up sheet, a checklist, a document edited in place — it saves new versions of itself), keeping state shared across viewers, knowing who is viewing, asking Claude a question of its own, storing files people add, handing the viewer a file to save, or using the viewer's camera, microphone, location, screen or device motion. Serves this user's live capability roster and the typed call definitions. Load it whenever any such runtime behavior would make an artifact more useful, before writing the page.","argumentHint":"","builtin":true},{"name":"update-config","description":"Use this skill to configure the Claude Code harness via settings.json. Automated behaviors (\"from now on when X\", \"each time X\", \"whenever X\", \"before/after X\") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions (\"allow X\", \"add permission\", \"move permission to\"), env vars (\"set X=Y\"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: \"allow npm commands\", \"add bq permission to global settings\", \"move permission to user settings\", \"set DEBUG=true\", \"when claude stops show X\". For simple settings like theme/model, suggest the /config command.","argumentHint":"","builtin":true},{"name":"verify","description":"Verify that a code change actually does what it's supposed to by exercising it end-to-end and observing behavior — drive the affected flow, not just tests or typecheck. Run before committing nontrivial changes; bootstraps this repo's project verify skill if none exists yet. Don't invoke it on a diff that only touches tests, docs, or other code with no runtime surface to drive (a change to product source always has one) — there's nothing to observe.","argumentHint":"","builtin":true},{"name":"debug","description":"Enable debug logging for this session and help diagnose issues","argumentHint":"[issue description]","builtin":true},{"name":"code-review","description":"Review the current diff, or a PR number/branch/path target, for correctness bugs (plus reuse/simplification/efficiency cleanups where the model's review recipe covers them) at the given effort level (low|medium|high|xhigh|max: from few high-confidence findings up to many, some of them uncertain; ultra: deep multi-agent review in the cloud); with no level given, it reuses the level you typed last. Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review. Pass --max-findings \u003cn\u003e to report up to n findings, or --max-findings all for every finding. The choice stays until you pass --max-findings default. For ultra on a GitHub.com PR target, --post asks to post the finished review’s findings to the PR as a single comment from the user’s GitHub account (not a review; the launch dialog still confirms in interactive sessions, while non-interactive mode posts on the flag alone) and --no-post hides that option.","argumentHint":"[low|medium|high|xhigh|max|ultra] [--fix] [--comment] [\u003cpr#\u003e|\u003cbranch\u003e|\u003cpath\u003e] [--max-findings \u003cn\u003e|all]","aliases":["review"],"builtin":true},{"name":"simplify","description":"Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.","argumentHint":"[\u003ctarget\u003e]","builtin":true},{"name":"batch","description":"Research and plan a large-scale change, then execute it in parallel across 5–30 isolated worktree agents that each open a PR.","argumentHint":"\u003cinstruction\u003e","builtin":true},{"name":"fewer-permission-prompts","description":"Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.","argumentHint":"","builtin":true},{"name":"doctor","description":"Health-check the user's Claude Code setup and fix issues: diagnose installation health — what the `claude doctor` terminal diagnostics cover — from local data (duplicate or leftover installs, PATH, unparseable settings files, broken or colliding agent definitions, skills whose frontmatter fails to parse); find unused skills, MCP servers, and plugins versus their context cost and disable dead weight; deduplicate local CLAUDE.md files against checked-in ones; trim checked-in CLAUDE.md files by cutting content a session could derive from the codebase (directory layouts, tech-stack lists, architecture overviews) while keeping gotchas, rationale, and non-standard conventions; migrate always-loaded CLAUDE.md guidance into lazy skills and nested CLAUDE.md files; flag slow hooks and context-heavy extensions; check the installed version is current; make auto mode the default permission mode; and pre-approve frequently denied read-only commands. Use when the user asks for a doctor run, checkup, audit, tune-up, or cleanup of their Claude Code setup or configuration.","argumentHint":"[prompt-audit [\u003cpath\u003e]]","aliases":["checkup"],"builtin":true},{"name":"loop","description":"Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace.","argumentHint":"[interval] [prompt]","aliases":["proactive"],"builtin":true},{"name":"schedule","description":"Create, update, list, or run scheduled cloud agents (routines) that execute on a cron schedule.","argumentHint":"","aliases":["routines"],"builtin":true},{"name":"claude-api","description":"Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.\nTRIGGER — read BEFORE opening the target file; don't skip because it \"looks like a one-liner\" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).\nSKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).","argumentHint":"","builtin":true},{"name":"workflow-authoring","description":"Reference for writing a Workflow tool script (script API and gotchas, resume, quality patterns, worked examples). Load before authoring a script for a workflow the user already opted into; it does not itself authorize running one.","argumentHint":"","builtin":true},{"name":"run","description":"Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).","argumentHint":"","builtin":true},{"name":"run-skill-generator","description":"Author or improve the run-\u003cunit\u003e skill - a per-project skill that tells agents how to build, launch, and drive this project's app. Use when the user asks to set up the project, get it running, write run instructions, or verify build/run steps work from a clean environment.","argumentHint":"","builtin":true},{"name":"plugin-authoring","description":"Make a mod: a change to Claude's own interface or behaviour, such as a live pane or panel, a band above the prompt, a status line, a toast, a slash command, or a hook on tool calls or prompts. Use to make, change or debug a mod or its hooks module.","argumentHint":"","builtin":true},{"name":"advisor","description":"Let Claude consult a stronger model at key moments","argumentHint":"[fable|opus|sonnet|off]","builtin":true},{"name":"agents","description":"(removed) Ask Claude to create/manage subagents, or edit .claude/agents/","argumentHint":"","builtin":true},{"name":"auto-mode-setup","description":"Teach auto mode about your environment, plus optional rule tweaks","argumentHint":"[--request-id \u003cuuid\u003e] (--wizard posture=… scope=… depth=… --propose | --expect-sha256 \u003c64-hex\u003e --apply-file \u003cpath\u003e)","builtin":true},{"name":"autocompact","description":"Configure the auto-compact window size","argumentHint":"[auto|\u003ctokens\u003e]","builtin":true},{"name":"clear","description":"Start a new session with empty context; previous session stays on disk (resumable with /resume)","argumentHint":"[name]","aliases":["reset","new"],"builtin":true},{"name":"color","description":"Set the prompt bar color for this session","argumentHint":"[red|blue|green|yellow|purple|orange|pink|cyan|default]","builtin":true},{"name":"compact","description":"Free up context by summarizing the conversation so far","argumentHint":"\u003coptional custom summarization instructions\u003e","builtin":true},{"name":"config","description":"Set a setting by key","argumentHint":"key=value","aliases":["settings"],"builtin":true},{"name":"output-style","description":"List output styles or switch to one","argumentHint":"[style]","builtin":true},{"name":"context","description":"Show current context usage","argumentHint":"","builtin":true},{"name":"effort","description":"Set effort level for model usage","argumentHint":"\u003clow|medium|high|xhigh|max|auto|ultracode [on|off]\u003e","builtin":true},{"name":"fast","description":"Toggle fast mode (Opus 5.5)","argumentHint":"[on|off]","builtin":true},{"name":"focus","description":"Toggle focus view: just your prompt, summary, and response","argumentHint":"[on|off]","builtin":true},{"name":"heapdump","description":"Dump the JS heap to the Desktop; on Linux with no Desktop folder, the home directory","argumentHint":"","builtin":true},{"name":"init","description":"Initialize a new CLAUDE.md file with codebase documentation","argumentHint":"","builtin":true},{"name":"mcp","description":"Manage MCP servers","argumentHint":"[reconnect|enable|disable [\u003cserver\u003e|all]]","builtin":true},{"name":"import","description":"Import config from another AI coding agent","argumentHint":"","builtin":true},{"name":"model","description":"Set the AI model for Claude Code","argumentHint":"\u003cmodel\u003e","builtin":true},{"name":"__remote-workflow","description":"Run the workflow script delivered in this session environment (server-launched sessions only)","argumentHint":"","builtin":true},{"name":"workflow-launch-exec","description":"Execute a server-launched workflow handoff (workflow_launch event sessions only)","argumentHint":"","builtin":true},{"name":"reload-plugins","description":"Activate pending plugin changes in the current session","argumentHint":"[--force]","builtin":true},{"name":"reload-skills","description":"Pick up skills added or changed on disk during this session","argumentHint":"","builtin":true},{"name":"rename","description":"Rename the current conversation","argumentHint":"[name]","aliases":["name"],"builtin":true},{"name":"ultrareview","description":"Start a cloud agent that finds and verifies bugs in your branch (~15-25 min, $5-$25 USD) · Runs in a cloud session. See https://code.claude.com/docs/en/claude-code-on-the-web","argumentHint":"","builtin":true},{"name":"security-review","description":"Complete a security review of the pending changes on the current branch","argumentHint":"","builtin":true},{"name":"usage-credits","description":"Configure usage credits or request them from your admin when you hit a limit","argumentHint":"","builtin":true},{"name":"extra-usage","description":"Renamed to /usage-credits","argumentHint":"","builtin":true},{"name":"usage","description":"Show session cost, plan usage, and what's contributing to your limits","argumentHint":"","aliases":["cost","stats"],"builtin":true},{"name":"insights","description":"Generate a report analyzing your Claude Code sessions","argumentHint":"","builtin":true},{"name":"recap","description":"Generate a one-line session recap now","argumentHint":"","builtin":true},{"name":"skill-doctor","description":"Show which loaded skills are unused and costing context","argumentHint":"","builtin":true},{"name":"goal","description":"Set a goal — keep working until the condition is met","argumentHint":"","builtin":true},{"name":"design-consent","description":"Grant Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"design-revoke","description":"Revoke Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"list-agents","description":"List subagents, teammates, and other Claude sessions you can message","argumentHint":"","aliases":["peers"],"builtin":true},{"name":"team-onboarding","description":"Help teammates ramp on Claude Code with a guide from your usage","argumentHint":"","builtin":true},{"name":"docs","description":"docs (editable docs people share and comment on; the default for any document, named as a doc or not: a document, report, proposal, resume, cover letter, letter, contract, policy, form, template, worksheet, essay, handbook, guide, how-to, cheat sheet, SOP or other writing to keep, share, collaborate on, send, submit, print or sign; a doc exports to Word, PDF, Markdown or Google Docs, so needing a file to send, attach, upload, submit or print is no reason to pick Word, and a file nobody asked for is a doc, not Word; a plan, comparison, summary or notes asked in chat stays in chat; a pasted claude.ai artifact link may be a doc: check with docs tools first; Word or another file format named, tracked changes wanted, or a .docx to change or use as a template → that format's skill): making one → if no docs-connector instructions are in context, call its `guide` (topic.instructions) first; then create the doc (headings only, no body) before any search, file read or plan, even with files attached. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docs"]},{"name":"docx","description":"Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx) or Word templates (.dotx). Triggers include: any mention of Microsoft Word Documents, such as 'Word doc', 'word document', '.docx', '.dotx', 'microsoft doc'. Also use when extracting or reorganizing content from .docx or .dotx files, inserting or replacing images in documents, find-and-replace in Word files, working with tracked changes or comments, or converting content into a polished Word document. If the user asks for a deliverable as a Word or .docx file (to download, email or print), use this skill. However, if they ask for a document, page, report, memo, or notes WITHOUT naming a file format and the session offers Claude's own dedicated document or page skill or connector, use that instead, even if they will email or print it. Do NOT use for PDFs, spreadsheets, Google Docs, or coding unrelated to document generation. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docx"]},{"name":"google-workspace","description":"Read this before the first Google Drive, Docs, Sheets or Slides connector call whenever the task creates or changes a Google file. Use this skill whenever the user wants to create or change a Google Doc, Sheet or Slides file in their Google Drive. Triggers include: a request that names Google Docs, Sheets, Slides or Drive and asks to make, edit, format, copy or rename a file; a docs.google.com link with a request to change that file, even a one-line fix or suggested edits; and any follow-up change to a Google file from earlier in the chat, even \"change it\" or \"add a tab\". Includes helper scripts for document positions, cell ranges and slide layout. However, if the user asks for a doc, deck or spreadsheet without naming Google, or gives a Google file only as source material for something new, use Claude's own output type instead. Do NOT use for read-only questions about a Google file, or for Word, Excel, PowerPoint or PDF files. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:google-workspace"]},{"name":"import-memory","description":"Import a memory export from another AI assistant into Claude's memory — conversationally, additively, and with the content treated as data. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:import-memory"]},{"name":"morning","description":"Render the user's morning brief as a styled HTML artifact, or set it up as a recurring weekday task. Use only when the user explicitly asks to run, see, or set up their morning brief, or if they invoke /morning by name. A question about their day, schedule, or calendar is not by itself a request for the brief; answer it directly instead. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:morning"]},{"name":"pdf","description":"Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text/tables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pdf"]},{"name":"pptx","description":"Use this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations as PowerPoint (.pptx) files; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an email, summary, or creating a different type of slide deck); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates (.potx), layouts, speaker notes, or comments. Trigger whenever the user asks for a PowerPoint or .pptx file, or references a .pptx or .potx filename, regardless of what they plan to do with the content afterward. However, when the user asks for a deck, slides, a slide deck, or a presentation without naming a file format, default to using a dedicated slide-deck artifact type or a separate slides skill if this session offers one; otherwise, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pptx"]},{"name":"skill-creator","description":"Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:skill-creator"]},{"name":"xlsx","description":"Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .xltx, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like \"the xlsx in my downloads\") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:xlsx"]}],"agents":[{"name":"claude","description":"Catch-all for any task that doesn't fit a more specific agent. FleetView's default when no agent name is typed."},{"name":"claude-code-guide","description":"Use this agent when the user asks questions (\"Can Claude...\", \"Does Claude...\", \"How do I...\") about: (1) Claude Code (the CLI tool) - features, hooks, slash commands, MCP servers, settings, IDE integrations, keyboard shortcuts; (2) Claude Agent SDK - building custom agents; (3) Claude API (formerly Anthropic API) - Messages API for directly passing messages to Claude, Tool Runner (`client.beta.messages.tool_runner`) for running an agentic loop over your own tools, manual tool-use loops, Managed Agents for server-hosted agents with a managed sandbox, prompt caching, and general Anthropic SDK usage; (4) Claude Tag (Claude in Slack) - what it is, setting it up for a Slack workspace, `/install-slack-app`; (5) `claude plugin eval` (writing and running plugin eval suites, its JSON/report, sandbox, CI) and the `/skill-doctor` report. **IMPORTANT:** Before spawning a new agent, check if there is already a running or recently completed claude-code-guide agent that you can continue via SendMessage.","model":"haiku"},{"name":"Explore","description":"Fast read-only search agent for locating code. Use it to find files by pattern (eg. \"src/components/**/*.tsx\"), grep for symbols or keywords (eg. \"API endpoints\"), or answer \"where is X defined / which files reference Y.\" Do NOT use it for code review, design-doc auditing, cross-file consistency checks, or open-ended analysis — it reads excerpts rather than whole files and will miss content past its read window. When calling, specify search breadth: \"quick\" for a single targeted lookup, \"medium\" for moderate exploration, or \"very thorough\" to search across multiple locations and naming conventions.","model":"inherit"},{"name":"general-purpose","description":"General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you."},{"name":"Plan","description":"Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.","model":"inherit"},{"name":"statusline-setup","description":"Use this agent to configure the user's Claude Code status line setting.","model":"sonnet"}],"output_style":"default","available_output_styles":["default","Proactive","Concise","Explanatory","Learning"],"user_output_styles_dir":"/Users/flamingcow/.claude/output-styles","models":[{"value":"default","resolvedModel":"claude-opus-5-5","displayName":"Default (recommended)","description":"Opus 5.5 · Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"opus","resolvedModel":"claude-opus-5-5","displayName":"Opus 5.5","description":"For complex work and everyday tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"fable","resolvedModel":"claude-fable-5-1","displayName":"Fable 5.1","description":"For your toughest challenges","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"sonnet","resolvedModel":"claude-sonnet-5-5","displayName":"Sonnet 5.5","description":"Most efficient for simpler tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"haiku","resolvedModel":"claude-haiku-5-5","displayName":"Haiku 5.5","description":"Fastest for quick answers","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-haiku-4-5-20251001","resolvedModel":"claude-haiku-4-5-20251001","displayName":"Haiku 4.5","description":"Fastest for quick answers"},{"value":"claude-sonnet-5","resolvedModel":"claude-sonnet-5","displayName":"Sonnet 5","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-5","resolvedModel":"claude-opus-5","displayName":"Opus 5","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-fable-5","resolvedModel":"claude-fable-5","displayName":"Fable 5","description":"Most capable for your hardest and longest-running tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-8","resolvedModel":"claude-opus-4-8","displayName":"Opus 4.8","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-opus-4-7","resolvedModel":"claude-opus-4-7","displayName":"Opus 4.7","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-6","resolvedModel":"claude-opus-4-6","displayName":"Opus 4.6","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-sonnet-4-6","resolvedModel":"claude-sonnet-4-6","displayName":"Sonnet 4.6","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true}],"account":{"email":"[redacted]","organization":"[redacted]'s Organization","subscriptionType":"Claude Max","apiProvider":"firstParty"},"pid":27097,"claude_code_version":"2.1.296","current_permission_mode":"bypassPermissions","feedback_mode":{"kind":"post"},"analytics_disabled":false,"remote_control_auto_enable":false,"remote_control_auto_connect_default":false,"remote_control_available":true,"remote_control_auto_on_by_default":false,"ide_rc_auto_enable_gate":true,"fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","session_state":"idle","capabilities":["ui_surface_v1"]},"pending_permission_requests":[],"pending_user_dialog_requests":[]}}} +{"dir":"send","msg":{"request":{"hooks":null,"subtype":"initialize"},"request_id":"req_42a6b24e70afd977","type":"control_request"}} +{"dir":"recv","msg":{"type":"control_response","response":{"subtype":"success","request_id":"req_42a6b24e70afd977","response":{"commands":[{"name":"deep-research","description":"Deep research harness — fan-out web searches, fetch sources, adversarially verify claims, synthesize a cited report. (dynamic workflow)","argumentHint":"","builtin":true},{"name":"design","description":"Make a new Design artifact from a brief","argumentHint":"[what to design]","builtin":true},{"name":"slides","description":"Make a new Slides deck artifact from a brief","argumentHint":"[what the deck is about]","builtin":true},{"name":"design-sync","description":"Push a React design system to claude.ai/design. This runs a converter that bundles the real component code (from Storybook or a bare package) and uploads it. Use when the user runs /design-sync or says \"sync my design system to Claude Design\".","argumentHint":"[\u003cproject hint, e.g. \"Acme DS\"\u003e]","builtin":true},{"name":"dataviz","description":"Use this skill whenever you are about to create ANY chart, graph, plot, dashboard, or data visualization, in ANY output medium — an HTML or React artifact, inline SVG, plotting code in any library (matplotlib, plotly, d3, Recharts, …), an image/PNG you will render and upload, or a chart shared into Slack. Read it BEFORE writing the first line of chart code, choosing chart colors, building a stat tile / meter / KPI row, or laying out a dashboard. When the destination is a first-party document connector (host-designated, never self-described) that renders live charts, hand it the rows (inline, or as an uploaded data file the chart cites) rather than a rendered PNG/SVG — a picture of a chart loses hover, data inspection and per-value comments. Produces visualizations that read as one system — elegant, accessible, consistent in light and dark — using a brand-neutral placeholder palette you swap for your own. Teaches a design-system-agnostic method: a form heuristic, a color formula with a runnable validator, mark specs, and interaction rules. A validated default palette is documented in `references/palette.md` — swap that file's values for your brand's. Triggers on: \"chart\", \"graph\", \"plot\", \"data viz\", \"visualization\", \"dashboard\", \"analytics\", \"visualize data\", \"categorical colors\", \"sequential / diverging palette\", \"stat tile\", \"sparkline\", \"heatmap\", \"legend\", \"axis\", \"tooltip\", \"chart colors\", \"color by series\".","argumentHint":"","builtin":true},{"name":"artifact-diagramming","description":"Diagramming know-how for Artifacts - when a picture earns its place, how to draw one that shows the real mechanism, and the inline-SVG mechanics that keep it legible in both themes.","argumentHint":"","builtin":true},{"name":"artifact-capabilities","description":"Runtime capabilities a published Artifact page can be granted — behavior static HTML cannot provide on its own, such as the page reading live or connected data, remembering what people do on it (a poll, a sign-up sheet, a checklist, a document edited in place — it saves new versions of itself), keeping state shared across viewers, knowing who is viewing, asking Claude a question of its own, storing files people add, handing the viewer a file to save, or using the viewer's camera, microphone, location, screen or device motion. Serves this user's live capability roster and the typed call definitions. Load it whenever any such runtime behavior would make an artifact more useful, before writing the page.","argumentHint":"","builtin":true},{"name":"update-config","description":"Use this skill to configure the Claude Code harness via settings.json. Automated behaviors (\"from now on when X\", \"each time X\", \"whenever X\", \"before/after X\") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions (\"allow X\", \"add permission\", \"move permission to\"), env vars (\"set X=Y\"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: \"allow npm commands\", \"add bq permission to global settings\", \"move permission to user settings\", \"set DEBUG=true\", \"when claude stops show X\". For simple settings like theme/model, suggest the /config command.","argumentHint":"","builtin":true},{"name":"verify","description":"Verify that a code change actually does what it's supposed to by exercising it end-to-end and observing behavior — drive the affected flow, not just tests or typecheck. Run before committing nontrivial changes; bootstraps this repo's project verify skill if none exists yet. Don't invoke it on a diff that only touches tests, docs, or other code with no runtime surface to drive (a change to product source always has one) — there's nothing to observe.","argumentHint":"","builtin":true},{"name":"debug","description":"Enable debug logging for this session and help diagnose issues","argumentHint":"[issue description]","builtin":true},{"name":"code-review","description":"Review the current diff, or a PR number/branch/path target, for correctness bugs (plus reuse/simplification/efficiency cleanups where the model's review recipe covers them) at the given effort level (low|medium|high|xhigh|max: from few high-confidence findings up to many, some of them uncertain; ultra: deep multi-agent review in the cloud); with no level given, it reuses the level you typed last. Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review. Pass --max-findings \u003cn\u003e to report up to n findings, or --max-findings all for every finding. The choice stays until you pass --max-findings default. For ultra on a GitHub.com PR target, --post asks to post the finished review’s findings to the PR as a single comment from the user’s GitHub account (not a review; the launch dialog still confirms in interactive sessions, while non-interactive mode posts on the flag alone) and --no-post hides that option.","argumentHint":"[low|medium|high|xhigh|max|ultra] [--fix] [--comment] [\u003cpr#\u003e|\u003cbranch\u003e|\u003cpath\u003e] [--max-findings \u003cn\u003e|all]","aliases":["review"],"builtin":true},{"name":"simplify","description":"Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.","argumentHint":"[\u003ctarget\u003e]","builtin":true},{"name":"batch","description":"Research and plan a large-scale change, then execute it in parallel across 5–30 isolated worktree agents that each open a PR.","argumentHint":"\u003cinstruction\u003e","builtin":true},{"name":"fewer-permission-prompts","description":"Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.","argumentHint":"","builtin":true},{"name":"doctor","description":"Health-check the user's Claude Code setup and fix issues: diagnose installation health — what the `claude doctor` terminal diagnostics cover — from local data (duplicate or leftover installs, PATH, unparseable settings files, broken or colliding agent definitions, skills whose frontmatter fails to parse); find unused skills, MCP servers, and plugins versus their context cost and disable dead weight; deduplicate local CLAUDE.md files against checked-in ones; trim checked-in CLAUDE.md files by cutting content a session could derive from the codebase (directory layouts, tech-stack lists, architecture overviews) while keeping gotchas, rationale, and non-standard conventions; migrate always-loaded CLAUDE.md guidance into lazy skills and nested CLAUDE.md files; flag slow hooks and context-heavy extensions; check the installed version is current; make auto mode the default permission mode; and pre-approve frequently denied read-only commands. Use when the user asks for a doctor run, checkup, audit, tune-up, or cleanup of their Claude Code setup or configuration.","argumentHint":"[prompt-audit [\u003cpath\u003e]]","aliases":["checkup"],"builtin":true},{"name":"loop","description":"Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace.","argumentHint":"[interval] [prompt]","aliases":["proactive"],"builtin":true},{"name":"schedule","description":"Create, update, list, or run scheduled cloud agents (routines) that execute on a cron schedule.","argumentHint":"","aliases":["routines"],"builtin":true},{"name":"claude-api","description":"Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.\nTRIGGER — read BEFORE opening the target file; don't skip because it \"looks like a one-liner\" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).\nSKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).","argumentHint":"","builtin":true},{"name":"workflow-authoring","description":"Reference for writing a Workflow tool script (script API and gotchas, resume, quality patterns, worked examples). Load before authoring a script for a workflow the user already opted into; it does not itself authorize running one.","argumentHint":"","builtin":true},{"name":"run","description":"Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).","argumentHint":"","builtin":true},{"name":"run-skill-generator","description":"Author or improve the run-\u003cunit\u003e skill - a per-project skill that tells agents how to build, launch, and drive this project's app. Use when the user asks to set up the project, get it running, write run instructions, or verify build/run steps work from a clean environment.","argumentHint":"","builtin":true},{"name":"plugin-authoring","description":"Make a mod: a change to Claude's own interface or behaviour, such as a live pane or panel, a band above the prompt, a status line, a toast, a slash command, or a hook on tool calls or prompts. Use to make, change or debug a mod or its hooks module.","argumentHint":"","builtin":true},{"name":"advisor","description":"Let Claude consult a stronger model at key moments","argumentHint":"[fable|opus|sonnet|off]","builtin":true},{"name":"agents","description":"(removed) Ask Claude to create/manage subagents, or edit .claude/agents/","argumentHint":"","builtin":true},{"name":"auto-mode-setup","description":"Teach auto mode about your environment, plus optional rule tweaks","argumentHint":"[--request-id \u003cuuid\u003e] (--wizard posture=… scope=… depth=… --propose | --expect-sha256 \u003c64-hex\u003e --apply-file \u003cpath\u003e)","builtin":true},{"name":"autocompact","description":"Configure the auto-compact window size","argumentHint":"[auto|\u003ctokens\u003e]","builtin":true},{"name":"clear","description":"Start a new session with empty context; previous session stays on disk (resumable with /resume)","argumentHint":"[name]","aliases":["reset","new"],"builtin":true},{"name":"color","description":"Set the prompt bar color for this session","argumentHint":"[red|blue|green|yellow|purple|orange|pink|cyan|default]","builtin":true},{"name":"compact","description":"Free up context by summarizing the conversation so far","argumentHint":"\u003coptional custom summarization instructions\u003e","builtin":true},{"name":"config","description":"Set a setting by key","argumentHint":"key=value","aliases":["settings"],"builtin":true},{"name":"output-style","description":"List output styles or switch to one","argumentHint":"[style]","builtin":true},{"name":"context","description":"Show current context usage","argumentHint":"","builtin":true},{"name":"effort","description":"Set effort level for model usage","argumentHint":"\u003clow|medium|high|xhigh|max|auto|ultracode [on|off]\u003e","builtin":true},{"name":"fast","description":"Toggle fast mode (Opus 5.5)","argumentHint":"[on|off]","builtin":true},{"name":"focus","description":"Toggle focus view: just your prompt, summary, and response","argumentHint":"[on|off]","builtin":true},{"name":"heapdump","description":"Dump the JS heap to the Desktop; on Linux with no Desktop folder, the home directory","argumentHint":"","builtin":true},{"name":"init","description":"Initialize a new CLAUDE.md file with codebase documentation","argumentHint":"","builtin":true},{"name":"mcp","description":"Manage MCP servers","argumentHint":"[reconnect|enable|disable [\u003cserver\u003e|all]]","builtin":true},{"name":"import","description":"Import config from another AI coding agent","argumentHint":"","builtin":true},{"name":"model","description":"Set the AI model for Claude Code","argumentHint":"\u003cmodel\u003e","builtin":true},{"name":"__remote-workflow","description":"Run the workflow script delivered in this session environment (server-launched sessions only)","argumentHint":"","builtin":true},{"name":"workflow-launch-exec","description":"Execute a server-launched workflow handoff (workflow_launch event sessions only)","argumentHint":"","builtin":true},{"name":"reload-plugins","description":"Activate pending plugin changes in the current session","argumentHint":"[--force]","builtin":true},{"name":"reload-skills","description":"Pick up skills added or changed on disk during this session","argumentHint":"","builtin":true},{"name":"rename","description":"Rename the current conversation","argumentHint":"[name]","aliases":["name"],"builtin":true},{"name":"ultrareview","description":"Start a cloud agent that finds and verifies bugs in your branch (~15-25 min, $5-$25 USD) · Runs in a cloud session. See https://code.claude.com/docs/en/claude-code-on-the-web","argumentHint":"","builtin":true},{"name":"security-review","description":"Complete a security review of the pending changes on the current branch","argumentHint":"","builtin":true},{"name":"usage-credits","description":"Configure usage credits or request them from your admin when you hit a limit","argumentHint":"","builtin":true},{"name":"extra-usage","description":"Renamed to /usage-credits","argumentHint":"","builtin":true},{"name":"usage","description":"Show session cost, plan usage, and what's contributing to your limits","argumentHint":"","aliases":["cost","stats"],"builtin":true},{"name":"insights","description":"Generate a report analyzing your Claude Code sessions","argumentHint":"","builtin":true},{"name":"recap","description":"Generate a one-line session recap now","argumentHint":"","builtin":true},{"name":"skill-doctor","description":"Show which loaded skills are unused and costing context","argumentHint":"","builtin":true},{"name":"goal","description":"Set a goal — keep working until the condition is met","argumentHint":"","builtin":true},{"name":"design-consent","description":"Grant Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"design-revoke","description":"Revoke Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"list-agents","description":"List subagents, teammates, and other Claude sessions you can message","argumentHint":"","aliases":["peers"],"builtin":true},{"name":"team-onboarding","description":"Help teammates ramp on Claude Code with a guide from your usage","argumentHint":"","builtin":true},{"name":"docs","description":"docs (editable docs people share and comment on; the default for any document, named as a doc or not: a document, report, proposal, resume, cover letter, letter, contract, policy, form, template, worksheet, essay, handbook, guide, how-to, cheat sheet, SOP or other writing to keep, share, collaborate on, send, submit, print or sign; a doc exports to Word, PDF, Markdown or Google Docs, so needing a file to send, attach, upload, submit or print is no reason to pick Word, and a file nobody asked for is a doc, not Word; a plan, comparison, summary or notes asked in chat stays in chat; a pasted claude.ai artifact link may be a doc: check with docs tools first; Word or another file format named, tracked changes wanted, or a .docx to change or use as a template → that format's skill): making one → if no docs-connector instructions are in context, call its `guide` (topic.instructions) first; then create the doc (headings only, no body) before any search, file read or plan, even with files attached. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docs"]},{"name":"docx","description":"Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx) or Word templates (.dotx). Triggers include: any mention of Microsoft Word Documents, such as 'Word doc', 'word document', '.docx', '.dotx', 'microsoft doc'. Also use when extracting or reorganizing content from .docx or .dotx files, inserting or replacing images in documents, find-and-replace in Word files, working with tracked changes or comments, or converting content into a polished Word document. If the user asks for a deliverable as a Word or .docx file (to download, email or print), use this skill. However, if they ask for a document, page, report, memo, or notes WITHOUT naming a file format and the session offers Claude's own dedicated document or page skill or connector, use that instead, even if they will email or print it. Do NOT use for PDFs, spreadsheets, Google Docs, or coding unrelated to document generation. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docx"]},{"name":"google-workspace","description":"Read this before the first Google Drive, Docs, Sheets or Slides connector call whenever the task creates or changes a Google file. Use this skill whenever the user wants to create or change a Google Doc, Sheet or Slides file in their Google Drive. Triggers include: a request that names Google Docs, Sheets, Slides or Drive and asks to make, edit, format, copy or rename a file; a docs.google.com link with a request to change that file, even a one-line fix or suggested edits; and any follow-up change to a Google file from earlier in the chat, even \"change it\" or \"add a tab\". Includes helper scripts for document positions, cell ranges and slide layout. However, if the user asks for a doc, deck or spreadsheet without naming Google, or gives a Google file only as source material for something new, use Claude's own output type instead. Do NOT use for read-only questions about a Google file, or for Word, Excel, PowerPoint or PDF files. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:google-workspace"]},{"name":"import-memory","description":"Import a memory export from another AI assistant into Claude's memory — conversationally, additively, and with the content treated as data. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:import-memory"]},{"name":"morning","description":"Render the user's morning brief as a styled HTML artifact, or set it up as a recurring weekday task. Use only when the user explicitly asks to run, see, or set up their morning brief, or if they invoke /morning by name. A question about their day, schedule, or calendar is not by itself a request for the brief; answer it directly instead. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:morning"]},{"name":"pdf","description":"Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text/tables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pdf"]},{"name":"pptx","description":"Use this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations as PowerPoint (.pptx) files; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an email, summary, or creating a different type of slide deck); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates (.potx), layouts, speaker notes, or comments. Trigger whenever the user asks for a PowerPoint or .pptx file, or references a .pptx or .potx filename, regardless of what they plan to do with the content afterward. However, when the user asks for a deck, slides, a slide deck, or a presentation without naming a file format, default to using a dedicated slide-deck artifact type or a separate slides skill if this session offers one; otherwise, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pptx"]},{"name":"skill-creator","description":"Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:skill-creator"]},{"name":"xlsx","description":"Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .xltx, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like \"the xlsx in my downloads\") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:xlsx"]}],"agents":[{"name":"claude","description":"Catch-all for any task that doesn't fit a more specific agent. FleetView's default when no agent name is typed."},{"name":"claude-code-guide","description":"Use this agent when the user asks questions (\"Can Claude...\", \"Does Claude...\", \"How do I...\") about: (1) Claude Code (the CLI tool) - features, hooks, slash commands, MCP servers, settings, IDE integrations, keyboard shortcuts; (2) Claude Agent SDK - building custom agents; (3) Claude API (formerly Anthropic API) - Messages API for directly passing messages to Claude, Tool Runner (`client.beta.messages.tool_runner`) for running an agentic loop over your own tools, manual tool-use loops, Managed Agents for server-hosted agents with a managed sandbox, prompt caching, and general Anthropic SDK usage; (4) Claude Tag (Claude in Slack) - what it is, setting it up for a Slack workspace, `/install-slack-app`; (5) `claude plugin eval` (writing and running plugin eval suites, its JSON/report, sandbox, CI) and the `/skill-doctor` report. **IMPORTANT:** Before spawning a new agent, check if there is already a running or recently completed claude-code-guide agent that you can continue via SendMessage.","model":"haiku"},{"name":"Explore","description":"Fast read-only search agent for locating code. Use it to find files by pattern (eg. \"src/components/**/*.tsx\"), grep for symbols or keywords (eg. \"API endpoints\"), or answer \"where is X defined / which files reference Y.\" Do NOT use it for code review, design-doc auditing, cross-file consistency checks, or open-ended analysis — it reads excerpts rather than whole files and will miss content past its read window. When calling, specify search breadth: \"quick\" for a single targeted lookup, \"medium\" for moderate exploration, or \"very thorough\" to search across multiple locations and naming conventions.","model":"inherit"},{"name":"general-purpose","description":"General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you."},{"name":"Plan","description":"Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.","model":"inherit"},{"name":"statusline-setup","description":"Use this agent to configure the user's Claude Code status line setting.","model":"sonnet"}],"output_style":"default","available_output_styles":["default","Proactive","Concise","Explanatory","Learning"],"user_output_styles_dir":"/Users/flamingcow/.claude/output-styles","models":[{"value":"default","resolvedModel":"claude-opus-5-5","displayName":"Default (recommended)","description":"Opus 5.5 · Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"opus","resolvedModel":"claude-opus-5-5","displayName":"Opus 5.5","description":"For complex work and everyday tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"fable","resolvedModel":"claude-fable-5-1","displayName":"Fable 5.1","description":"For your toughest challenges","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"sonnet","resolvedModel":"claude-sonnet-5-5","displayName":"Sonnet 5.5","description":"Most efficient for simpler tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"haiku","resolvedModel":"claude-haiku-5-5","displayName":"Haiku 5.5","description":"Fastest for quick answers","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-haiku-4-5-20251001","resolvedModel":"claude-haiku-4-5-20251001","displayName":"Haiku 4.5","description":"Fastest for quick answers"},{"value":"claude-sonnet-5","resolvedModel":"claude-sonnet-5","displayName":"Sonnet 5","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-5","resolvedModel":"claude-opus-5","displayName":"Opus 5","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-fable-5","resolvedModel":"claude-fable-5","displayName":"Fable 5","description":"Most capable for your hardest and longest-running tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-8","resolvedModel":"claude-opus-4-8","displayName":"Opus 4.8","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-opus-4-7","resolvedModel":"claude-opus-4-7","displayName":"Opus 4.7","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-6","resolvedModel":"claude-opus-4-6","displayName":"Opus 4.6","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-sonnet-4-6","resolvedModel":"claude-sonnet-4-6","displayName":"Sonnet 4.6","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true}],"account":{"email":"[redacted]","organization":"[redacted]'s Organization","subscriptionType":"Claude Max","apiProvider":"firstParty"},"pid":31925,"claude_code_version":"2.1.296","current_permission_mode":"bypassPermissions","feedback_mode":{"kind":"post"},"analytics_disabled":false,"remote_control_auto_enable":false,"remote_control_auto_connect_default":false,"remote_control_available":true,"remote_control_auto_on_by_default":false,"ide_rc_auto_enable_gate":true,"fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","session_state":"idle","capabilities":["ui_surface_v1"]},"pending_permission_requests":[],"pending_user_dialog_requests":[]}}} {"dir":"send","msg":{"message":{"content":"Run the bash command `ping -c 120 127.0.0.1` in the foreground (not in the background) and wait for it to finish, then say done.","role":"user"},"parent_tool_use_id":null,"session_id":"","type":"user"}} -{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"running","uuid":"034cb167-21b3-40d6-b609-3cb65d7696b0","session_id":"a0e4f7f0-1255-4b91-9ce6-ab9f7adcae94"}} -{"dir":"recv","msg":{"type":"system","subtype":"init","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestInterrupt1854807854/001","session_id":"a0e4f7f0-1255-4b91-9ce6-ab9f7adcae94","tools":["Task","Artifact","ArtifactComments","ArtifactData","Bash","CronCreate","CronDelete","CronList","DesignSync","Edit","EnterWorktree","ExitWorktree","ListAgents","LSP","Monitor","NotebookEdit","PushNotification","Read","RemoteTrigger","ReportFindings","ScheduleWakeup","SendMessage","Skill","TaskStop","ToolSearch","WebFetch","WebSearch","Workflow","Write"],"mcp_servers":[],"model":"claude-sonnet-5-5","permissionMode":"bypassPermissions","slash_commands":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","advisor","agents","auto-mode-setup","autocompact","clear","color","compact","config","output-style","context","effort","fast","focus","heapdump","init","mcp","import","model","__remote-workflow","workflow-launch-exec","reload-plugins","reload-skills","rename","ultrareview","security-review","usage-credits","extra-usage","usage","insights","recap","skill-doctor","goal","design-consent","design-revoke","list-agents","team-onboarding","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"terminal_slash_commands":["doctor","color","focus","reload-plugins"],"apiKeySource":"none","claude_code_version":"2.1.296","output_style":"default","agents":["claude","claude-code-guide","Explore","general-purpose","Plan","statusline-setup"],"skills":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"plugins":[{"name":"clangd-lsp","path":"/Users/flamingcow/.claude/plugins/cache/claude-plugins-official/clangd-lsp/1.0.0","source":"clangd-lsp@claude-plugins-official","version":"1.0.0"},{"name":"cc-plugin-agents-md","path":"builtin","source":"cc-plugin-agents-md@builtin"},{"name":"cc-plugin-telemetry","path":"builtin","source":"cc-plugin-telemetry@builtin"},{"name":"cc-plugin-plugin-authoring","path":"builtin","source":"cc-plugin-plugin-authoring@builtin"}],"capabilities":["interrupt_receipt_v1","interrupt_cancel_queued_v1","interrupt_send_now_v1","msg_lifecycle_v1","request_marker_lists_v1","sdk_mcp_tools_list_changed","sdk_mcp_manifests","mcp_read_resource_v1","mcp_tool_ui_meta_v1","ui_surface_v1"],"analytics_disabled":false,"product_feedback_disabled":false,"uuid":"ba5b6e6a-6298-42e6-88b8-83221bdbf7b6","memory_paths":{"auto":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestInterrupt1854807854-001/memory/"},"scratchpad_path":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestInterrupt1854807854-001/a0e4f7f0-1255-4b91-9ce6-ab9f7adcae94/scratchpad","messaging_socket_path":"/tmp/cc-socks/27097.sock","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","per_turn_effort_active":true,"view_mode":"default"}} -{"dir":"recv","msg":{"type":"rate_limit_event","rate_limit_info":{"status":"allowed","resetsAt":1791990000,"rateLimitType":"five_hour","overageStatus":"rejected","overageDisabledReason":"org_level_disabled","isUsingOverage":false,"unifiedWindows":{"five_hour":{"utilization":0.04,"resetsAt":1791990000},"seven_day":{"utilization":0.27,"resetsAt":1792270800}}},"uuid":"7829df78-0d44-4fe3-b796-cf930b2ce9f4","session_id":"a0e4f7f0-1255-4b91-9ce6-ab9f7adcae94"}} -{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2FDQjpCAd3f61D9hUCH","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01RoyrUfXNZ54u95Zs1XTkfn","name":"Bash","input":{"command":"ping -c 120 127.0.0.1","description":"Ping localhost 120 times","timeout":180000},"caller":{"type":"direct"}}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":37239,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":37239},"output_tokens":16,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"a0e4f7f0-1255-4b91-9ce6-ab9f7adcae94","uuid":"7dc41e89-a923-4ca1-929b-eea08f2fcab1","timestamp":"2026-10-14T13:56:54.870Z","request_id":"req_011Cg2FDQTTVx8V1nURiWxaX","wire_tool_inputs":{"toolu_01RoyrUfXNZ54u95Zs1XTkfn":{"command":"ping -c 120 127.0.0.1","description":"Ping localhost 120 times","timeout":180000}}}} -{"dir":"send","msg":{"request":{"subtype":"interrupt"},"request_id":"req_85a418e576f9d23b","type":"control_request"}} -{"dir":"recv","msg":{"type":"control_response","response":{"subtype":"success","request_id":"req_85a418e576f9d23b","response":{"still_queued":[]}}}} -{"dir":"recv","msg":{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"The user doesn't want to proceed with this tool use. The tool use was rejected (eg. if it was a file edit, the new_string was NOT written to the file). STOP what you are doing and wait for the user to tell you how to proceed.\n\nNote: The user's next message may contain a correction or preference. Pay close attention — if they explain what went wrong or how they'd prefer you to work, consider saving that to memory for future sessions.","is_error":true,"tool_use_id":"toolu_01RoyrUfXNZ54u95Zs1XTkfn"}]},"parent_tool_use_id":null,"session_id":"a0e4f7f0-1255-4b91-9ce6-ab9f7adcae94","uuid":"7f23a63a-6070-431f-946b-19932d317467","timestamp":"2026-10-14T13:56:56.886Z","tool_use_result":"User rejected tool use","tool_result_meta":[{"id":"toolu_01RoyrUfXNZ54u95Zs1XTkfn","non_execution_kind":"user-rejected"}]}} -{"dir":"recv","msg":{"type":"user","message":{"role":"user","content":[{"type":"text","text":"[Request interrupted by user for tool use]"}]},"parent_tool_use_id":null,"session_id":"a0e4f7f0-1255-4b91-9ce6-ab9f7adcae94","uuid":"76aa6f09-81c0-4149-a71a-072ddb6c8412","timestamp":"2026-10-14T13:56:56.887Z"}} -{"dir":"recv","msg":{"duration_api_ms":3409,"stop_reason":"tool_use","session_id":"a0e4f7f0-1255-4b91-9ce6-ab9f7adcae94","total_cost_usd":0.15023350000000002,"usage":{"input_tokens":2,"cache_creation_input_tokens":37239,"cache_read_input_tokens":0,"output_tokens":114,"output_tokens_details":{"thinking_tokens":0},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":37239,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":114,"cache_read_input_tokens":0,"cache_creation_input_tokens":37239,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":37239},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":1210,"outputTokens":25,"cacheReadInputTokens":0,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.0001335,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":0,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"},"claude-sonnet-5-5":{"inputTokens":2,"outputTokens":114,"cacheReadInputTokens":0,"cacheCreationInputTokens":37239,"webSearchRequests":0,"costUSD":0.1501,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":0,"canonicalModel":"claude-sonnet-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"aborted_tools","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":true,"num_turns":3,"subtype":"error_during_execution","errors":["[ede_diagnostic] result_type=user last_content_type=n/a stop_reason=tool_use"],"type":"result","duration_ms":4583,"uuid":"bcf363fd-4317-409e-b216-1a20df3231ec","queued_turn_count":0,"result_index":0}} -{"dir":"send","msg":{"message":{"content":"Reply with the single word ok.","role":"user"},"parent_tool_use_id":null,"session_id":"a0e4f7f0-1255-4b91-9ce6-ab9f7adcae94","type":"user"}} -{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"idle","uuid":"f5ff5e04-190c-47e7-b9df-15fdb368cf70","session_id":"a0e4f7f0-1255-4b91-9ce6-ab9f7adcae94"}} -{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"running","uuid":"5a5f12d7-4d9e-4ea2-9c1e-a44d32d7321d","session_id":"a0e4f7f0-1255-4b91-9ce6-ab9f7adcae94"}} -{"dir":"recv","msg":{"type":"system","subtype":"init","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestInterrupt1854807854/001","session_id":"a0e4f7f0-1255-4b91-9ce6-ab9f7adcae94","tools":["Task","Artifact","ArtifactComments","ArtifactData","Bash","CronCreate","CronDelete","CronList","DesignSync","Edit","EnterWorktree","ExitWorktree","ListAgents","ListMcpResourcesTool","LSP","Monitor","NotebookEdit","PushNotification","Read","ReadMcpResourceDirTool","ReadMcpResourceTool","RemoteTrigger","ReportFindings","ScheduleWakeup","SendMessage","Skill","TaskStop","ToolSearch","WebFetch","WebSearch","Workflow","Write","mcp__claude_ai_Claude_Docs__batch","mcp__claude_ai_Claude_Docs__create","mcp__claude_ai_Claude_Docs__delete","mcp__claude_ai_Claude_Docs__export","mcp__claude_ai_Claude_Docs__guide","mcp__claude_ai_Claude_Docs__query","mcp__claude_ai_Claude_Docs__read","mcp__claude_ai_Claude_Docs__update","mcp__claude_ai_Fastmail__list_calendars","mcp__claude_ai_Fastmail__list_identities","mcp__claude_ai_Fastmail__list_labels","mcp__claude_ai_Fastmail__read_attachment","mcp__claude_ai_Fastmail__read_email","mcp__claude_ai_Fastmail__read_note","mcp__claude_ai_Fastmail__read_thread","mcp__claude_ai_Fastmail__search_contacts","mcp__claude_ai_Fastmail__search_email","mcp__claude_ai_Fastmail__search_events","mcp__claude_ai_Fastmail__search_notes","mcp__claude_ai_Fastmail__search_org_contacts","mcp__claude_ai_Heliosian__helios_activities","mcp__claude_ai_Heliosian__helios_classroom","mcp__claude_ai_Heliosian__helios_days","mcp__claude_ai_Heliosian__helios_describe_schema","mcp__claude_ai_Heliosian__helios_describe_table","mcp__claude_ai_Heliosian__helios_events","mcp__claude_ai_Heliosian__helios_find_people","mcp__claude_ai_Heliosian__helios_get","mcp__claude_ai_Heliosian__helios_group","mcp__claude_ai_Heliosian__helios_links","mcp__claude_ai_Heliosian__helios_lists","mcp__claude_ai_Heliosian__helios_nearby_families","mcp__claude_ai_Heliosian__helios_parties","mcp__claude_ai_Heliosian__helios_policies","mcp__claude_ai_Heliosian__helios_query","mcp__claude_ai_Heliosian__helios_query_json","mcp__claude_ai_Heliosian__helios_read_document","mcp__claude_ai_Heliosian__helios_search","mcp__claude_ai_Heliosian__helios_similar","mcp__claude_ai_Heliosian__helios_whoami"],"mcp_servers":[{"name":"claude.ai Claude Docs","status":"connected","source":"claudeai"},{"name":"claude.ai Heliosian","status":"connected","source":"claudeai"},{"name":"claude.ai Fastmail","status":"connected","source":"claudeai"},{"name":"claude.ai Google Calendar","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Gmail","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Google Drive","status":"needs-auth","source":"claudeai"}],"model":"claude-sonnet-5-5","permissionMode":"bypassPermissions","slash_commands":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","advisor","agents","auto-mode-setup","autocompact","clear","color","compact","config","output-style","context","effort","fast","focus","heapdump","init","mcp","import","model","__remote-workflow","workflow-launch-exec","reload-plugins","reload-skills","rename","ultrareview","security-review","usage-credits","extra-usage","usage","insights","recap","skill-doctor","goal","design-consent","design-revoke","list-agents","team-onboarding","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"terminal_slash_commands":["doctor","color","focus","reload-plugins"],"apiKeySource":"none","claude_code_version":"2.1.296","output_style":"default","agents":["claude","claude-code-guide","Explore","general-purpose","Plan","statusline-setup"],"skills":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"plugins":[{"name":"clangd-lsp","path":"/Users/flamingcow/.claude/plugins/cache/claude-plugins-official/clangd-lsp/1.0.0","source":"clangd-lsp@claude-plugins-official","version":"1.0.0"},{"name":"cc-plugin-agents-md","path":"builtin","source":"cc-plugin-agents-md@builtin"},{"name":"cc-plugin-telemetry","path":"builtin","source":"cc-plugin-telemetry@builtin"},{"name":"cc-plugin-plugin-authoring","path":"builtin","source":"cc-plugin-plugin-authoring@builtin"}],"capabilities":["interrupt_receipt_v1","interrupt_cancel_queued_v1","interrupt_send_now_v1","msg_lifecycle_v1","request_marker_lists_v1","sdk_mcp_tools_list_changed","sdk_mcp_manifests","mcp_read_resource_v1","mcp_tool_ui_meta_v1","ui_surface_v1"],"analytics_disabled":false,"product_feedback_disabled":false,"uuid":"36d8478c-d4f3-4277-bdbb-ba774aae1f88","memory_paths":{"auto":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestInterrupt1854807854-001/memory/"},"scratchpad_path":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestInterrupt1854807854-001/a0e4f7f0-1255-4b91-9ce6-ab9f7adcae94/scratchpad","messaging_socket_path":"/tmp/cc-socks/27097.sock","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","per_turn_effort_active":true,"view_mode":"default"}} -{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2FDk4Qr6D1AEHkNSKad","type":"message","role":"assistant","content":[{"type":"text","text":"ok"}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":4772,"cache_read_input_tokens":37239,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":4772},"output_tokens":4,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"a0e4f7f0-1255-4b91-9ce6-ab9f7adcae94","uuid":"8f54b5f2-5a63-40db-a37b-e629f8c7481e","timestamp":"2026-10-14T13:56:58.119Z","request_id":"req_011Cg2FDjkZN3b1MS2paL9QC"}} -{"dir":"recv","msg":{"duration_api_ms":4642,"stop_reason":"end_turn","session_id":"a0e4f7f0-1255-4b91-9ce6-ab9f7adcae94","total_cost_usd":0.17308940000000003,"usage":{"input_tokens":2,"cache_creation_input_tokens":4772,"cache_read_input_tokens":37239,"output_tokens":4,"output_tokens_details":{"thinking_tokens":0},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":4772,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":4,"cache_read_input_tokens":37239,"cache_creation_input_tokens":4772,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":4772},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":1210,"outputTokens":25,"cacheReadInputTokens":0,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.0001335,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":0,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"},"claude-sonnet-5-5":{"inputTokens":4,"outputTokens":118,"cacheReadInputTokens":37239,"cacheCreationInputTokens":42011,"webSearchRequests":0,"costUSD":0.17295590000000002,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":0,"canonicalModel":"claude-sonnet-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"ok","ttft_ms":1228,"type":"result","duration_ms":1244,"uuid":"eec469c5-4e4c-41b5-93be-b7acbb5ae0a5","ttft_stream_ms":1227,"time_to_request_ms":9,"first_content_frame_ms":1228,"queued_turn_count":0,"result_index":1}} -{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"idle","uuid":"f1db8ac7-84d3-4356-9331-c9eb42fb1abe","session_id":"a0e4f7f0-1255-4b91-9ce6-ab9f7adcae94"}} +{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"running","uuid":"c98ed9b4-9c3a-48cd-b010-aac10f78e3aa","session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad"}} +{"dir":"recv","msg":{"type":"system","subtype":"init","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestInterrupt3822280781/001","session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad","tools":["Task","Artifact","ArtifactComments","ArtifactData","Bash","CronCreate","CronDelete","CronList","DesignSync","Edit","EnterWorktree","ExitWorktree","ListAgents","LSP","Monitor","NotebookEdit","PushNotification","Read","RemoteTrigger","ReportFindings","ScheduleWakeup","SendMessage","Skill","TaskStop","ToolSearch","WebFetch","WebSearch","Workflow","Write"],"mcp_servers":[{"name":"claude.ai Claude Docs","status":"pending","source":"claudeai"},{"name":"claude.ai Heliosian","status":"pending","source":"claudeai"},{"name":"claude.ai Fastmail","status":"pending","source":"claudeai"},{"name":"claude.ai Google Calendar","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Gmail","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Google Drive","status":"needs-auth","source":"claudeai"}],"model":"claude-sonnet-5-5","permissionMode":"bypassPermissions","slash_commands":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","advisor","agents","auto-mode-setup","autocompact","clear","color","compact","config","output-style","context","effort","fast","focus","heapdump","init","mcp","import","model","__remote-workflow","workflow-launch-exec","reload-plugins","reload-skills","rename","ultrareview","security-review","usage-credits","extra-usage","usage","insights","recap","skill-doctor","goal","design-consent","design-revoke","list-agents","team-onboarding","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"terminal_slash_commands":["doctor","color","focus","reload-plugins"],"apiKeySource":"none","claude_code_version":"2.1.296","output_style":"default","agents":["claude","claude-code-guide","Explore","general-purpose","Plan","statusline-setup"],"skills":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"plugins":[{"name":"clangd-lsp","path":"/Users/flamingcow/.claude/plugins/cache/claude-plugins-official/clangd-lsp/1.0.0","source":"clangd-lsp@claude-plugins-official","version":"1.0.0"},{"name":"cc-plugin-agents-md","path":"builtin","source":"cc-plugin-agents-md@builtin"},{"name":"cc-plugin-telemetry","path":"builtin","source":"cc-plugin-telemetry@builtin"},{"name":"cc-plugin-plugin-authoring","path":"builtin","source":"cc-plugin-plugin-authoring@builtin"}],"capabilities":["interrupt_receipt_v1","interrupt_cancel_queued_v1","interrupt_send_now_v1","msg_lifecycle_v1","request_marker_lists_v1","sdk_mcp_tools_list_changed","sdk_mcp_manifests","mcp_read_resource_v1","mcp_tool_ui_meta_v1","ui_surface_v1"],"analytics_disabled":false,"product_feedback_disabled":false,"uuid":"a7fc50fd-621d-441c-8f68-be7dfa9c7fe8","memory_paths":{"auto":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestInterrupt3822280781-001/memory/"},"scratchpad_path":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestInterrupt3822280781-001/7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad/scratchpad","messaging_socket_path":"/tmp/cc-socks/31925.sock","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","per_turn_effort_active":true,"view_mode":"default"}} +{"dir":"recv","msg":{"type":"rate_limit_event","rate_limit_info":{"status":"allowed","resetsAt":1791990000,"rateLimitType":"five_hour","overageStatus":"rejected","overageDisabledReason":"org_level_disabled","isUsingOverage":false,"unifiedWindows":{"five_hour":{"utilization":0.1,"resetsAt":1791990000},"seven_day":{"utilization":0.27,"resetsAt":1792270800}}},"uuid":"785cabba-d550-4744-9a5d-bdfa75191cc8","session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad"}} +{"dir":"recv","msg":{"type":"system","subtype":"thinking_tokens","estimated_tokens":50,"estimated_tokens_delta":50,"session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad","uuid":"42ff0f46-d080-4414-b64a-1e75412a5e1e"}} +{"dir":"recv","msg":{"type":"system","subtype":"thinking_tokens","estimated_tokens":189,"estimated_tokens_delta":139,"session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad","uuid":"0c1e849f-c5fb-418b-b8db-88a4bb633dd6"}} +{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2Gx9YKyCp3FYqs1TLZP","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"","signature":"CAQS6QUKEAgSGAI4AUIIdGhpbmtpbmcSDAna+Fa85r4CykuWyRoMTHOBsDKndwbXzaJpIjCJQb0Fi5kc6jAvT3FQ4TFU3PUZxsPN5mYUuBkftu3DsVUssO9ntwv7Zg0leJScKOIqhgUgu3De0cpSCn0lBc+n140Bpe+YsuNpg3o6p3bbA42QhhGG48JocVj/pggD22mJ+KVG/FnzT04ryFIgXLWiq3qp27eHlKhO5304py7+BsAzkPDn55V4ywuHnc+5v8ynEz4GnLiEUqfeoUngStg6fpQuo1JvOEVVEFjrJon1E6UbqJmseVjVXateZfCvESMuRIAM1wzmK48MJXjBZD+P1F7Z3dcDoCR1CveoSSZbXk+jXgTIONQL2mx+xOvl3ooCzi3IOQ0UE/ZGYK+/l6GulJgnhHkYJ0e7xPfXEcF3CiYONLXwJ6Rd/0llTV44OkFY97h/3LkCLDAdkgPlpmrSdv1PoMX9sl/T8Um9qNj7e1yD/Gd4d1xhXYdhs9mkhFa0GQYjA0AFPbzjDq1n9PHYg/9YAQ28b92iOlEUznur/1VItuJn1W9l097w4OF86qIhHbjPotr6wHjU1oh7u0a7ikCJTI/J8I20/NV155/N+NNfRFVO04rQ9gvgf07YWJGdj7nfd5MUtkxngx0V/L9LeWS612b8vgXKcHolINXNlpxtPnYUbXU5M0tc4IazgQ/KLqVJCWYjV6fEOtjImvzHurmTyaWQd4mobxRD/iNv3IF/73JuFPbiKvLIrqzE+H41xK51gxqBPK1uzkvbSd0t3tuJyLBcjh6y/5Lp5nIyEvU69PIb9fZvzH/uct4EXIDUoSvRabaat5iEoRpkYlgK6vmLTrPWGdDvx7qoD33R0EnJKeBO34nczcmLZSVccJABu/T6439voIWKJoN3UhK9kWOWE6L8L9tw6n7Vj303Gc3wXdxZ+QeqUPUQiM9etM/USwjNfZC5rZMEe9iJ9i26DUlehgGVFgqjGAE="}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":15266,"cache_read_input_tokens":22367,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":15266},"output_tokens":3,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad","uuid":"b3118a6e-41f7-4bde-9b63-03dee92d0bd0","timestamp":"2026-10-14T14:19:40.133Z","request_id":"req_011Cg2Gx9FxeZNhWDmHm34aG","thinking_duration_ms":589,"thinking_display":"updates"}} +{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2Gx9YKyCp3FYqs1TLZP","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_015cHLpSS5y4nhmJATEewSBy","name":"Bash","input":{"command":"timeout 150 ping -c 120 127.0.0.1","description":"Ping localhost 120 times","timeout":160000},"caller":{"type":"direct"}}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":15266,"cache_read_input_tokens":22367,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":15266},"output_tokens":3,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad","uuid":"0b76100e-137f-4c41-b399-55e94a64c1a1","timestamp":"2026-10-14T14:19:40.689Z","request_id":"req_011Cg2Gx9FxeZNhWDmHm34aG","wire_tool_inputs":{"toolu_015cHLpSS5y4nhmJATEewSBy":{"command":"timeout 150 ping -c 120 127.0.0.1","description":"Ping localhost 120 times","timeout":160000}}}} +{"dir":"send","msg":{"request":{"subtype":"interrupt"},"request_id":"req_401df9466efcd6dd","type":"control_request"}} +{"dir":"recv","msg":{"type":"control_response","response":{"subtype":"success","request_id":"req_401df9466efcd6dd","response":{"still_queued":[]}}}} +{"dir":"recv","msg":{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"The user doesn't want to proceed with this tool use. The tool use was rejected (eg. if it was a file edit, the new_string was NOT written to the file). STOP what you are doing and wait for the user to tell you how to proceed.\n\nNote: The user's next message may contain a correction or preference. Pay close attention — if they explain what went wrong or how they'd prefer you to work, consider saving that to memory for future sessions.","is_error":true,"tool_use_id":"toolu_015cHLpSS5y4nhmJATEewSBy"}]},"parent_tool_use_id":null,"session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad","uuid":"9cf7bff4-9675-4aa9-bb15-2979cc072b8e","timestamp":"2026-10-14T14:19:42.699Z","tool_use_result":"User rejected tool use","tool_result_meta":[{"id":"toolu_015cHLpSS5y4nhmJATEewSBy","non_execution_kind":"user-rejected"}]}} +{"dir":"recv","msg":{"type":"user","message":{"role":"user","content":[{"type":"text","text":"[Request interrupted by user for tool use]"}]},"parent_tool_use_id":null,"session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad","uuid":"4b09d68b-83f2-49d1-9337-52a7e4d0ea2d","timestamp":"2026-10-14T14:19:42.700Z"}} +{"dir":"recv","msg":{"duration_api_ms":2330,"stop_reason":"tool_use","session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad","total_cost_usd":0.0652452,"usage":{"input_tokens":2,"cache_creation_input_tokens":15266,"cache_read_input_tokens":22367,"output_tokens":181,"output_tokens_details":{"thinking_tokens":60},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":15266,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":181,"cache_read_input_tokens":22367,"cache_creation_input_tokens":15266,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":15266},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":1210,"outputTokens":19,"cacheReadInputTokens":0,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.0001305,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":0,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"},"claude-sonnet-5-5":{"inputTokens":2,"outputTokens":181,"cacheReadInputTokens":22367,"cacheCreationInputTokens":15266,"webSearchRequests":0,"costUSD":0.0651147,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":60,"canonicalModel":"claude-sonnet-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"aborted_tools","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":true,"num_turns":3,"subtype":"error_during_execution","errors":["[ede_diagnostic] result_type=user last_content_type=n/a stop_reason=tool_use"],"type":"result","duration_ms":3792,"uuid":"e47fab59-7b14-4362-acf4-d7ccaf1881ed","queued_turn_count":0,"result_index":0}} +{"dir":"send","msg":{"message":{"content":"Reply with the single word ok.","role":"user"},"parent_tool_use_id":null,"session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad","type":"user"}} +{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"idle","uuid":"16611d87-df90-464f-8937-aeaa4f86acfc","session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad"}} +{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"running","uuid":"ed539e59-b7a9-40f7-ace2-6dd288624dba","session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad"}} +{"dir":"recv","msg":{"type":"system","subtype":"init","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestInterrupt3822280781/001","session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad","tools":["Task","Artifact","ArtifactComments","ArtifactData","Bash","CronCreate","CronDelete","CronList","DesignSync","Edit","EnterWorktree","ExitWorktree","ListAgents","ListMcpResourcesTool","LSP","Monitor","NotebookEdit","PushNotification","Read","ReadMcpResourceDirTool","ReadMcpResourceTool","RemoteTrigger","ReportFindings","ScheduleWakeup","SendMessage","Skill","TaskStop","ToolSearch","WebFetch","WebSearch","Workflow","Write","mcp__claude_ai_Claude_Docs__batch","mcp__claude_ai_Claude_Docs__create","mcp__claude_ai_Claude_Docs__delete","mcp__claude_ai_Claude_Docs__export","mcp__claude_ai_Claude_Docs__guide","mcp__claude_ai_Claude_Docs__query","mcp__claude_ai_Claude_Docs__read","mcp__claude_ai_Claude_Docs__update","mcp__claude_ai_Fastmail__list_calendars","mcp__claude_ai_Fastmail__list_identities","mcp__claude_ai_Fastmail__list_labels","mcp__claude_ai_Fastmail__read_attachment","mcp__claude_ai_Fastmail__read_email","mcp__claude_ai_Fastmail__read_note","mcp__claude_ai_Fastmail__read_thread","mcp__claude_ai_Fastmail__search_contacts","mcp__claude_ai_Fastmail__search_email","mcp__claude_ai_Fastmail__search_events","mcp__claude_ai_Fastmail__search_notes","mcp__claude_ai_Fastmail__search_org_contacts","mcp__claude_ai_Heliosian__helios_activities","mcp__claude_ai_Heliosian__helios_classroom","mcp__claude_ai_Heliosian__helios_days","mcp__claude_ai_Heliosian__helios_describe_schema","mcp__claude_ai_Heliosian__helios_describe_table","mcp__claude_ai_Heliosian__helios_events","mcp__claude_ai_Heliosian__helios_find_people","mcp__claude_ai_Heliosian__helios_get","mcp__claude_ai_Heliosian__helios_group","mcp__claude_ai_Heliosian__helios_links","mcp__claude_ai_Heliosian__helios_lists","mcp__claude_ai_Heliosian__helios_nearby_families","mcp__claude_ai_Heliosian__helios_parties","mcp__claude_ai_Heliosian__helios_policies","mcp__claude_ai_Heliosian__helios_query","mcp__claude_ai_Heliosian__helios_query_json","mcp__claude_ai_Heliosian__helios_read_document","mcp__claude_ai_Heliosian__helios_search","mcp__claude_ai_Heliosian__helios_similar","mcp__claude_ai_Heliosian__helios_whoami"],"mcp_servers":[{"name":"claude.ai Claude Docs","status":"connected","source":"claudeai"},{"name":"claude.ai Heliosian","status":"connected","source":"claudeai"},{"name":"claude.ai Fastmail","status":"connected","source":"claudeai"},{"name":"claude.ai Google Calendar","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Gmail","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Google Drive","status":"needs-auth","source":"claudeai"}],"model":"claude-sonnet-5-5","permissionMode":"bypassPermissions","slash_commands":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","advisor","agents","auto-mode-setup","autocompact","clear","color","compact","config","output-style","context","effort","fast","focus","heapdump","init","mcp","import","model","__remote-workflow","workflow-launch-exec","reload-plugins","reload-skills","rename","ultrareview","security-review","usage-credits","extra-usage","usage","insights","recap","skill-doctor","goal","design-consent","design-revoke","list-agents","team-onboarding","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"terminal_slash_commands":["doctor","color","focus","reload-plugins"],"apiKeySource":"none","claude_code_version":"2.1.296","output_style":"default","agents":["claude","claude-code-guide","Explore","general-purpose","Plan","statusline-setup"],"skills":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"plugins":[{"name":"clangd-lsp","path":"/Users/flamingcow/.claude/plugins/cache/claude-plugins-official/clangd-lsp/1.0.0","source":"clangd-lsp@claude-plugins-official","version":"1.0.0"},{"name":"cc-plugin-agents-md","path":"builtin","source":"cc-plugin-agents-md@builtin"},{"name":"cc-plugin-telemetry","path":"builtin","source":"cc-plugin-telemetry@builtin"},{"name":"cc-plugin-plugin-authoring","path":"builtin","source":"cc-plugin-plugin-authoring@builtin"}],"capabilities":["interrupt_receipt_v1","interrupt_cancel_queued_v1","interrupt_send_now_v1","msg_lifecycle_v1","request_marker_lists_v1","sdk_mcp_tools_list_changed","sdk_mcp_manifests","mcp_read_resource_v1","mcp_tool_ui_meta_v1","ui_surface_v1"],"analytics_disabled":false,"product_feedback_disabled":false,"uuid":"0391f72d-f451-4646-a451-638e255566f9","memory_paths":{"auto":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestInterrupt3822280781-001/memory/"},"scratchpad_path":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestInterrupt3822280781-001/7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad/scratchpad","messaging_socket_path":"/tmp/cc-socks/31925.sock","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","per_turn_effort_active":true,"view_mode":"default"}} +{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2GxRVXVBZQ2vwzjT2cX","type":"message","role":"assistant","content":[{"type":"text","text":"ok"}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":4839,"cache_read_input_tokens":37633,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":4839},"output_tokens":4,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad","uuid":"7bb9232f-af8e-4b16-aae2-cb3ec92db068","timestamp":"2026-10-14T14:19:43.261Z","request_id":"req_011Cg2GxRBfca5BbcfpVfJV1"}} +{"dir":"recv","msg":{"duration_api_ms":2893,"stop_reason":"end_turn","session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad","total_cost_usd":0.0884085,"usage":{"input_tokens":2,"cache_creation_input_tokens":4839,"cache_read_input_tokens":37633,"output_tokens":4,"output_tokens_details":{"thinking_tokens":0},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":4839,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":4,"cache_read_input_tokens":37633,"cache_creation_input_tokens":4839,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":4839},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":1210,"outputTokens":19,"cacheReadInputTokens":0,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.0001305,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":0,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"},"claude-sonnet-5-5":{"inputTokens":4,"outputTokens":185,"cacheReadInputTokens":60000,"cacheCreationInputTokens":20105,"webSearchRequests":0,"costUSD":0.088278,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":60,"canonicalModel":"claude-sonnet-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"ok","ttft_ms":557,"type":"result","duration_ms":574,"uuid":"6a949de5-9de5-4e29-adfd-f56f4f817e08","ttft_stream_ms":557,"time_to_request_ms":9,"first_content_frame_ms":557,"queued_turn_count":0,"result_index":1}} +{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"idle","uuid":"087c0803-25b6-42ca-a289-f62f4c8278ea","session_id":"7e0e80ae-59b0-4fb8-ba22-102ef56dd0ad"}} diff --git a/internal/claude/testdata/transcripts/TestResume-first.jsonl b/internal/claude/testdata/transcripts/TestResume-first.jsonl index 7be47ca..8d779a4 100644 --- a/internal/claude/testdata/transcripts/TestResume-first.jsonl +++ b/internal/claude/testdata/transcripts/TestResume-first.jsonl @@ -1,9 +1,9 @@ -{"dir":"send","msg":{"request":{"hooks":null,"subtype":"initialize"},"request_id":"req_a22103c6af97183c","type":"control_request"}} -{"dir":"recv","msg":{"type":"control_response","response":{"subtype":"success","request_id":"req_a22103c6af97183c","response":{"commands":[{"name":"deep-research","description":"Deep research harness — fan-out web searches, fetch sources, adversarially verify claims, synthesize a cited report. (dynamic workflow)","argumentHint":"","builtin":true},{"name":"design","description":"Make a new Design artifact from a brief","argumentHint":"[what to design]","builtin":true},{"name":"slides","description":"Make a new Slides deck artifact from a brief","argumentHint":"[what the deck is about]","builtin":true},{"name":"design-sync","description":"Push a React design system to claude.ai/design. This runs a converter that bundles the real component code (from Storybook or a bare package) and uploads it. Use when the user runs /design-sync or says \"sync my design system to Claude Design\".","argumentHint":"[\u003cproject hint, e.g. \"Acme DS\"\u003e]","builtin":true},{"name":"dataviz","description":"Use this skill whenever you are about to create ANY chart, graph, plot, dashboard, or data visualization, in ANY output medium — an HTML or React artifact, inline SVG, plotting code in any library (matplotlib, plotly, d3, Recharts, …), an image/PNG you will render and upload, or a chart shared into Slack. Read it BEFORE writing the first line of chart code, choosing chart colors, building a stat tile / meter / KPI row, or laying out a dashboard. When the destination is a first-party document connector (host-designated, never self-described) that renders live charts, hand it the rows (inline, or as an uploaded data file the chart cites) rather than a rendered PNG/SVG — a picture of a chart loses hover, data inspection and per-value comments. Produces visualizations that read as one system — elegant, accessible, consistent in light and dark — using a brand-neutral placeholder palette you swap for your own. Teaches a design-system-agnostic method: a form heuristic, a color formula with a runnable validator, mark specs, and interaction rules. A validated default palette is documented in `references/palette.md` — swap that file's values for your brand's. Triggers on: \"chart\", \"graph\", \"plot\", \"data viz\", \"visualization\", \"dashboard\", \"analytics\", \"visualize data\", \"categorical colors\", \"sequential / diverging palette\", \"stat tile\", \"sparkline\", \"heatmap\", \"legend\", \"axis\", \"tooltip\", \"chart colors\", \"color by series\".","argumentHint":"","builtin":true},{"name":"artifact-diagramming","description":"Diagramming know-how for Artifacts - when a picture earns its place, how to draw one that shows the real mechanism, and the inline-SVG mechanics that keep it legible in both themes.","argumentHint":"","builtin":true},{"name":"artifact-capabilities","description":"Runtime capabilities a published Artifact page can be granted — behavior static HTML cannot provide on its own, such as the page reading live or connected data, remembering what people do on it (a poll, a sign-up sheet, a checklist, a document edited in place — it saves new versions of itself), keeping state shared across viewers, knowing who is viewing, asking Claude a question of its own, storing files people add, handing the viewer a file to save, or using the viewer's camera, microphone, location, screen or device motion. Serves this user's live capability roster and the typed call definitions. Load it whenever any such runtime behavior would make an artifact more useful, before writing the page.","argumentHint":"","builtin":true},{"name":"update-config","description":"Use this skill to configure the Claude Code harness via settings.json. Automated behaviors (\"from now on when X\", \"each time X\", \"whenever X\", \"before/after X\") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions (\"allow X\", \"add permission\", \"move permission to\"), env vars (\"set X=Y\"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: \"allow npm commands\", \"add bq permission to global settings\", \"move permission to user settings\", \"set DEBUG=true\", \"when claude stops show X\". For simple settings like theme/model, suggest the /config command.","argumentHint":"","builtin":true},{"name":"verify","description":"Verify that a code change actually does what it's supposed to by exercising it end-to-end and observing behavior — drive the affected flow, not just tests or typecheck. Run before committing nontrivial changes; bootstraps this repo's project verify skill if none exists yet. Don't invoke it on a diff that only touches tests, docs, or other code with no runtime surface to drive (a change to product source always has one) — there's nothing to observe.","argumentHint":"","builtin":true},{"name":"debug","description":"Enable debug logging for this session and help diagnose issues","argumentHint":"[issue description]","builtin":true},{"name":"code-review","description":"Review the current diff, or a PR number/branch/path target, for correctness bugs (plus reuse/simplification/efficiency cleanups where the model's review recipe covers them) at the given effort level (low|medium|high|xhigh|max: from few high-confidence findings up to many, some of them uncertain; ultra: deep multi-agent review in the cloud); with no level given, it reuses the level you typed last. Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review. Pass --max-findings \u003cn\u003e to report up to n findings, or --max-findings all for every finding. The choice stays until you pass --max-findings default. For ultra on a GitHub.com PR target, --post asks to post the finished review’s findings to the PR as a single comment from the user’s GitHub account (not a review; the launch dialog still confirms in interactive sessions, while non-interactive mode posts on the flag alone) and --no-post hides that option.","argumentHint":"[low|medium|high|xhigh|max|ultra] [--fix] [--comment] [\u003cpr#\u003e|\u003cbranch\u003e|\u003cpath\u003e] [--max-findings \u003cn\u003e|all]","aliases":["review"],"builtin":true},{"name":"simplify","description":"Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.","argumentHint":"[\u003ctarget\u003e]","builtin":true},{"name":"batch","description":"Research and plan a large-scale change, then execute it in parallel across 5–30 isolated worktree agents that each open a PR.","argumentHint":"\u003cinstruction\u003e","builtin":true},{"name":"fewer-permission-prompts","description":"Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.","argumentHint":"","builtin":true},{"name":"doctor","description":"Health-check the user's Claude Code setup and fix issues: diagnose installation health — what the `claude doctor` terminal diagnostics cover — from local data (duplicate or leftover installs, PATH, unparseable settings files, broken or colliding agent definitions, skills whose frontmatter fails to parse); find unused skills, MCP servers, and plugins versus their context cost and disable dead weight; deduplicate local CLAUDE.md files against checked-in ones; trim checked-in CLAUDE.md files by cutting content a session could derive from the codebase (directory layouts, tech-stack lists, architecture overviews) while keeping gotchas, rationale, and non-standard conventions; migrate always-loaded CLAUDE.md guidance into lazy skills and nested CLAUDE.md files; flag slow hooks and context-heavy extensions; check the installed version is current; make auto mode the default permission mode; and pre-approve frequently denied read-only commands. Use when the user asks for a doctor run, checkup, audit, tune-up, or cleanup of their Claude Code setup or configuration.","argumentHint":"[prompt-audit [\u003cpath\u003e]]","aliases":["checkup"],"builtin":true},{"name":"loop","description":"Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace.","argumentHint":"[interval] [prompt]","aliases":["proactive"],"builtin":true},{"name":"schedule","description":"Create, update, list, or run scheduled cloud agents (routines) that execute on a cron schedule.","argumentHint":"","aliases":["routines"],"builtin":true},{"name":"claude-api","description":"Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.\nTRIGGER — read BEFORE opening the target file; don't skip because it \"looks like a one-liner\" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).\nSKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).","argumentHint":"","builtin":true},{"name":"workflow-authoring","description":"Reference for writing a Workflow tool script (script API and gotchas, resume, quality patterns, worked examples). Load before authoring a script for a workflow the user already opted into; it does not itself authorize running one.","argumentHint":"","builtin":true},{"name":"run","description":"Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).","argumentHint":"","builtin":true},{"name":"run-skill-generator","description":"Author or improve the run-\u003cunit\u003e skill - a per-project skill that tells agents how to build, launch, and drive this project's app. Use when the user asks to set up the project, get it running, write run instructions, or verify build/run steps work from a clean environment.","argumentHint":"","builtin":true},{"name":"plugin-authoring","description":"Make a mod: a change to Claude's own interface or behaviour, such as a live pane or panel, a band above the prompt, a status line, a toast, a slash command, or a hook on tool calls or prompts. Use to make, change or debug a mod or its hooks module.","argumentHint":"","builtin":true},{"name":"advisor","description":"Let Claude consult a stronger model at key moments","argumentHint":"[fable|opus|sonnet|off]","builtin":true},{"name":"agents","description":"(removed) Ask Claude to create/manage subagents, or edit .claude/agents/","argumentHint":"","builtin":true},{"name":"auto-mode-setup","description":"Teach auto mode about your environment, plus optional rule tweaks","argumentHint":"[--request-id \u003cuuid\u003e] (--wizard posture=… scope=… depth=… --propose | --expect-sha256 \u003c64-hex\u003e --apply-file \u003cpath\u003e)","builtin":true},{"name":"autocompact","description":"Configure the auto-compact window size","argumentHint":"[auto|\u003ctokens\u003e]","builtin":true},{"name":"clear","description":"Start a new session with empty context; previous session stays on disk (resumable with /resume)","argumentHint":"[name]","aliases":["reset","new"],"builtin":true},{"name":"color","description":"Set the prompt bar color for this session","argumentHint":"[red|blue|green|yellow|purple|orange|pink|cyan|default]","builtin":true},{"name":"compact","description":"Free up context by summarizing the conversation so far","argumentHint":"\u003coptional custom summarization instructions\u003e","builtin":true},{"name":"config","description":"Set a setting by key","argumentHint":"key=value","aliases":["settings"],"builtin":true},{"name":"output-style","description":"List output styles or switch to one","argumentHint":"[style]","builtin":true},{"name":"context","description":"Show current context usage","argumentHint":"","builtin":true},{"name":"effort","description":"Set effort level for model usage","argumentHint":"\u003clow|medium|high|xhigh|max|auto|ultracode [on|off]\u003e","builtin":true},{"name":"fast","description":"Toggle fast mode (Opus 5.5)","argumentHint":"[on|off]","builtin":true},{"name":"focus","description":"Toggle focus view: just your prompt, summary, and response","argumentHint":"[on|off]","builtin":true},{"name":"heapdump","description":"Dump the JS heap to the Desktop; on Linux with no Desktop folder, the home directory","argumentHint":"","builtin":true},{"name":"init","description":"Initialize a new CLAUDE.md file with codebase documentation","argumentHint":"","builtin":true},{"name":"mcp","description":"Manage MCP servers","argumentHint":"[reconnect|enable|disable [\u003cserver\u003e|all]]","builtin":true},{"name":"import","description":"Import config from another AI coding agent","argumentHint":"","builtin":true},{"name":"model","description":"Set the AI model for Claude Code","argumentHint":"\u003cmodel\u003e","builtin":true},{"name":"__remote-workflow","description":"Run the workflow script delivered in this session environment (server-launched sessions only)","argumentHint":"","builtin":true},{"name":"workflow-launch-exec","description":"Execute a server-launched workflow handoff (workflow_launch event sessions only)","argumentHint":"","builtin":true},{"name":"reload-plugins","description":"Activate pending plugin changes in the current session","argumentHint":"[--force]","builtin":true},{"name":"reload-skills","description":"Pick up skills added or changed on disk during this session","argumentHint":"","builtin":true},{"name":"rename","description":"Rename the current conversation","argumentHint":"[name]","aliases":["name"],"builtin":true},{"name":"ultrareview","description":"Start a cloud agent that finds and verifies bugs in your branch (~15-25 min, $5-$25 USD) · Runs in a cloud session. See https://code.claude.com/docs/en/claude-code-on-the-web","argumentHint":"","builtin":true},{"name":"security-review","description":"Complete a security review of the pending changes on the current branch","argumentHint":"","builtin":true},{"name":"usage-credits","description":"Configure usage credits or request them from your admin when you hit a limit","argumentHint":"","builtin":true},{"name":"extra-usage","description":"Renamed to /usage-credits","argumentHint":"","builtin":true},{"name":"usage","description":"Show session cost, plan usage, and what's contributing to your limits","argumentHint":"","aliases":["cost","stats"],"builtin":true},{"name":"insights","description":"Generate a report analyzing your Claude Code sessions","argumentHint":"","builtin":true},{"name":"recap","description":"Generate a one-line session recap now","argumentHint":"","builtin":true},{"name":"skill-doctor","description":"Show which loaded skills are unused and costing context","argumentHint":"","builtin":true},{"name":"goal","description":"Set a goal — keep working until the condition is met","argumentHint":"","builtin":true},{"name":"design-consent","description":"Grant Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"design-revoke","description":"Revoke Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"list-agents","description":"List subagents, teammates, and other Claude sessions you can message","argumentHint":"","aliases":["peers"],"builtin":true},{"name":"team-onboarding","description":"Help teammates ramp on Claude Code with a guide from your usage","argumentHint":"","builtin":true},{"name":"docs","description":"docs (editable docs people share and comment on; the default for any document, named as a doc or not: a document, report, proposal, resume, cover letter, letter, contract, policy, form, template, worksheet, essay, handbook, guide, how-to, cheat sheet, SOP or other writing to keep, share, collaborate on, send, submit, print or sign; a doc exports to Word, PDF, Markdown or Google Docs, so needing a file to send, attach, upload, submit or print is no reason to pick Word, and a file nobody asked for is a doc, not Word; a plan, comparison, summary or notes asked in chat stays in chat; a pasted claude.ai artifact link may be a doc: check with docs tools first; Word or another file format named, tracked changes wanted, or a .docx to change or use as a template → that format's skill): making one → if no docs-connector instructions are in context, call its `guide` (topic.instructions) first; then create the doc (headings only, no body) before any search, file read or plan, even with files attached. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docs"]},{"name":"docx","description":"Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx) or Word templates (.dotx). Triggers include: any mention of Microsoft Word Documents, such as 'Word doc', 'word document', '.docx', '.dotx', 'microsoft doc'. Also use when extracting or reorganizing content from .docx or .dotx files, inserting or replacing images in documents, find-and-replace in Word files, working with tracked changes or comments, or converting content into a polished Word document. If the user asks for a deliverable as a Word or .docx file (to download, email or print), use this skill. However, if they ask for a document, page, report, memo, or notes WITHOUT naming a file format and the session offers Claude's own dedicated document or page skill or connector, use that instead, even if they will email or print it. Do NOT use for PDFs, spreadsheets, Google Docs, or coding unrelated to document generation. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docx"]},{"name":"google-workspace","description":"Read this before the first Google Drive, Docs, Sheets or Slides connector call whenever the task creates or changes a Google file. Use this skill whenever the user wants to create or change a Google Doc, Sheet or Slides file in their Google Drive. Triggers include: a request that names Google Docs, Sheets, Slides or Drive and asks to make, edit, format, copy or rename a file; a docs.google.com link with a request to change that file, even a one-line fix or suggested edits; and any follow-up change to a Google file from earlier in the chat, even \"change it\" or \"add a tab\". Includes helper scripts for document positions, cell ranges and slide layout. However, if the user asks for a doc, deck or spreadsheet without naming Google, or gives a Google file only as source material for something new, use Claude's own output type instead. Do NOT use for read-only questions about a Google file, or for Word, Excel, PowerPoint or PDF files. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:google-workspace"]},{"name":"import-memory","description":"Import a memory export from another AI assistant into Claude's memory — conversationally, additively, and with the content treated as data. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:import-memory"]},{"name":"morning","description":"Render the user's morning brief as a styled HTML artifact, or set it up as a recurring weekday task. Use only when the user explicitly asks to run, see, or set up their morning brief, or if they invoke /morning by name. A question about their day, schedule, or calendar is not by itself a request for the brief; answer it directly instead. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:morning"]},{"name":"pdf","description":"Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text/tables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pdf"]},{"name":"pptx","description":"Use this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations as PowerPoint (.pptx) files; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an email, summary, or creating a different type of slide deck); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates (.potx), layouts, speaker notes, or comments. Trigger whenever the user asks for a PowerPoint or .pptx file, or references a .pptx or .potx filename, regardless of what they plan to do with the content afterward. However, when the user asks for a deck, slides, a slide deck, or a presentation without naming a file format, default to using a dedicated slide-deck artifact type or a separate slides skill if this session offers one; otherwise, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pptx"]},{"name":"skill-creator","description":"Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:skill-creator"]},{"name":"xlsx","description":"Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .xltx, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like \"the xlsx in my downloads\") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:xlsx"]}],"agents":[{"name":"claude","description":"Catch-all for any task that doesn't fit a more specific agent. FleetView's default when no agent name is typed."},{"name":"claude-code-guide","description":"Use this agent when the user asks questions (\"Can Claude...\", \"Does Claude...\", \"How do I...\") about: (1) Claude Code (the CLI tool) - features, hooks, slash commands, MCP servers, settings, IDE integrations, keyboard shortcuts; (2) Claude Agent SDK - building custom agents; (3) Claude API (formerly Anthropic API) - Messages API for directly passing messages to Claude, Tool Runner (`client.beta.messages.tool_runner`) for running an agentic loop over your own tools, manual tool-use loops, Managed Agents for server-hosted agents with a managed sandbox, prompt caching, and general Anthropic SDK usage; (4) Claude Tag (Claude in Slack) - what it is, setting it up for a Slack workspace, `/install-slack-app`; (5) `claude plugin eval` (writing and running plugin eval suites, its JSON/report, sandbox, CI) and the `/skill-doctor` report. **IMPORTANT:** Before spawning a new agent, check if there is already a running or recently completed claude-code-guide agent that you can continue via SendMessage.","model":"haiku"},{"name":"Explore","description":"Fast read-only search agent for locating code. Use it to find files by pattern (eg. \"src/components/**/*.tsx\"), grep for symbols or keywords (eg. \"API endpoints\"), or answer \"where is X defined / which files reference Y.\" Do NOT use it for code review, design-doc auditing, cross-file consistency checks, or open-ended analysis — it reads excerpts rather than whole files and will miss content past its read window. When calling, specify search breadth: \"quick\" for a single targeted lookup, \"medium\" for moderate exploration, or \"very thorough\" to search across multiple locations and naming conventions.","model":"inherit"},{"name":"general-purpose","description":"General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you."},{"name":"Plan","description":"Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.","model":"inherit"},{"name":"statusline-setup","description":"Use this agent to configure the user's Claude Code status line setting.","model":"sonnet"}],"output_style":"default","available_output_styles":["default","Proactive","Concise","Explanatory","Learning"],"user_output_styles_dir":"/Users/flamingcow/.claude/output-styles","models":[{"value":"default","resolvedModel":"claude-opus-5-5","displayName":"Default (recommended)","description":"Opus 5.5 · Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"opus","resolvedModel":"claude-opus-5-5","displayName":"Opus 5.5","description":"For complex work and everyday tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"fable","resolvedModel":"claude-fable-5-1","displayName":"Fable 5.1","description":"For your toughest challenges","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"sonnet","resolvedModel":"claude-sonnet-5-5","displayName":"Sonnet 5.5","description":"Most efficient for simpler tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"haiku","resolvedModel":"claude-haiku-5-5","displayName":"Haiku 5.5","description":"Fastest for quick answers","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-haiku-4-5-20251001","resolvedModel":"claude-haiku-4-5-20251001","displayName":"Haiku 4.5","description":"Fastest for quick answers"},{"value":"claude-sonnet-5","resolvedModel":"claude-sonnet-5","displayName":"Sonnet 5","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-5","resolvedModel":"claude-opus-5","displayName":"Opus 5","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-fable-5","resolvedModel":"claude-fable-5","displayName":"Fable 5","description":"Most capable for your hardest and longest-running tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-8","resolvedModel":"claude-opus-4-8","displayName":"Opus 4.8","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-opus-4-7","resolvedModel":"claude-opus-4-7","displayName":"Opus 4.7","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-6","resolvedModel":"claude-opus-4-6","displayName":"Opus 4.6","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-sonnet-4-6","resolvedModel":"claude-sonnet-4-6","displayName":"Sonnet 4.6","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true}],"account":{"email":"[redacted]","organization":"[redacted]'s Organization","subscriptionType":"Claude Max","apiProvider":"firstParty"},"pid":27100,"claude_code_version":"2.1.296","current_permission_mode":"bypassPermissions","feedback_mode":{"kind":"post"},"analytics_disabled":false,"remote_control_auto_enable":false,"remote_control_auto_connect_default":false,"remote_control_available":true,"remote_control_auto_on_by_default":false,"ide_rc_auto_enable_gate":true,"fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","session_state":"idle","capabilities":["ui_surface_v1"]},"pending_permission_requests":[],"pending_user_dialog_requests":[]}}} +{"dir":"send","msg":{"request":{"hooks":null,"subtype":"initialize"},"request_id":"req_ba9b962a1674fe05","type":"control_request"}} +{"dir":"recv","msg":{"type":"control_response","response":{"subtype":"success","request_id":"req_ba9b962a1674fe05","response":{"commands":[{"name":"deep-research","description":"Deep research harness — fan-out web searches, fetch sources, adversarially verify claims, synthesize a cited report. (dynamic workflow)","argumentHint":"","builtin":true},{"name":"design","description":"Make a new Design artifact from a brief","argumentHint":"[what to design]","builtin":true},{"name":"slides","description":"Make a new Slides deck artifact from a brief","argumentHint":"[what the deck is about]","builtin":true},{"name":"design-sync","description":"Push a React design system to claude.ai/design. This runs a converter that bundles the real component code (from Storybook or a bare package) and uploads it. Use when the user runs /design-sync or says \"sync my design system to Claude Design\".","argumentHint":"[\u003cproject hint, e.g. \"Acme DS\"\u003e]","builtin":true},{"name":"dataviz","description":"Use this skill whenever you are about to create ANY chart, graph, plot, dashboard, or data visualization, in ANY output medium — an HTML or React artifact, inline SVG, plotting code in any library (matplotlib, plotly, d3, Recharts, …), an image/PNG you will render and upload, or a chart shared into Slack. Read it BEFORE writing the first line of chart code, choosing chart colors, building a stat tile / meter / KPI row, or laying out a dashboard. When the destination is a first-party document connector (host-designated, never self-described) that renders live charts, hand it the rows (inline, or as an uploaded data file the chart cites) rather than a rendered PNG/SVG — a picture of a chart loses hover, data inspection and per-value comments. Produces visualizations that read as one system — elegant, accessible, consistent in light and dark — using a brand-neutral placeholder palette you swap for your own. Teaches a design-system-agnostic method: a form heuristic, a color formula with a runnable validator, mark specs, and interaction rules. A validated default palette is documented in `references/palette.md` — swap that file's values for your brand's. Triggers on: \"chart\", \"graph\", \"plot\", \"data viz\", \"visualization\", \"dashboard\", \"analytics\", \"visualize data\", \"categorical colors\", \"sequential / diverging palette\", \"stat tile\", \"sparkline\", \"heatmap\", \"legend\", \"axis\", \"tooltip\", \"chart colors\", \"color by series\".","argumentHint":"","builtin":true},{"name":"artifact-diagramming","description":"Diagramming know-how for Artifacts - when a picture earns its place, how to draw one that shows the real mechanism, and the inline-SVG mechanics that keep it legible in both themes.","argumentHint":"","builtin":true},{"name":"artifact-capabilities","description":"Runtime capabilities a published Artifact page can be granted — behavior static HTML cannot provide on its own, such as the page reading live or connected data, remembering what people do on it (a poll, a sign-up sheet, a checklist, a document edited in place — it saves new versions of itself), keeping state shared across viewers, knowing who is viewing, asking Claude a question of its own, storing files people add, handing the viewer a file to save, or using the viewer's camera, microphone, location, screen or device motion. Serves this user's live capability roster and the typed call definitions. Load it whenever any such runtime behavior would make an artifact more useful, before writing the page.","argumentHint":"","builtin":true},{"name":"update-config","description":"Use this skill to configure the Claude Code harness via settings.json. Automated behaviors (\"from now on when X\", \"each time X\", \"whenever X\", \"before/after X\") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions (\"allow X\", \"add permission\", \"move permission to\"), env vars (\"set X=Y\"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: \"allow npm commands\", \"add bq permission to global settings\", \"move permission to user settings\", \"set DEBUG=true\", \"when claude stops show X\". For simple settings like theme/model, suggest the /config command.","argumentHint":"","builtin":true},{"name":"verify","description":"Verify that a code change actually does what it's supposed to by exercising it end-to-end and observing behavior — drive the affected flow, not just tests or typecheck. Run before committing nontrivial changes; bootstraps this repo's project verify skill if none exists yet. Don't invoke it on a diff that only touches tests, docs, or other code with no runtime surface to drive (a change to product source always has one) — there's nothing to observe.","argumentHint":"","builtin":true},{"name":"debug","description":"Enable debug logging for this session and help diagnose issues","argumentHint":"[issue description]","builtin":true},{"name":"code-review","description":"Review the current diff, or a PR number/branch/path target, for correctness bugs (plus reuse/simplification/efficiency cleanups where the model's review recipe covers them) at the given effort level (low|medium|high|xhigh|max: from few high-confidence findings up to many, some of them uncertain; ultra: deep multi-agent review in the cloud); with no level given, it reuses the level you typed last. Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review. Pass --max-findings \u003cn\u003e to report up to n findings, or --max-findings all for every finding. The choice stays until you pass --max-findings default. For ultra on a GitHub.com PR target, --post asks to post the finished review’s findings to the PR as a single comment from the user’s GitHub account (not a review; the launch dialog still confirms in interactive sessions, while non-interactive mode posts on the flag alone) and --no-post hides that option.","argumentHint":"[low|medium|high|xhigh|max|ultra] [--fix] [--comment] [\u003cpr#\u003e|\u003cbranch\u003e|\u003cpath\u003e] [--max-findings \u003cn\u003e|all]","aliases":["review"],"builtin":true},{"name":"simplify","description":"Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.","argumentHint":"[\u003ctarget\u003e]","builtin":true},{"name":"batch","description":"Research and plan a large-scale change, then execute it in parallel across 5–30 isolated worktree agents that each open a PR.","argumentHint":"\u003cinstruction\u003e","builtin":true},{"name":"fewer-permission-prompts","description":"Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.","argumentHint":"","builtin":true},{"name":"doctor","description":"Health-check the user's Claude Code setup and fix issues: diagnose installation health — what the `claude doctor` terminal diagnostics cover — from local data (duplicate or leftover installs, PATH, unparseable settings files, broken or colliding agent definitions, skills whose frontmatter fails to parse); find unused skills, MCP servers, and plugins versus their context cost and disable dead weight; deduplicate local CLAUDE.md files against checked-in ones; trim checked-in CLAUDE.md files by cutting content a session could derive from the codebase (directory layouts, tech-stack lists, architecture overviews) while keeping gotchas, rationale, and non-standard conventions; migrate always-loaded CLAUDE.md guidance into lazy skills and nested CLAUDE.md files; flag slow hooks and context-heavy extensions; check the installed version is current; make auto mode the default permission mode; and pre-approve frequently denied read-only commands. Use when the user asks for a doctor run, checkup, audit, tune-up, or cleanup of their Claude Code setup or configuration.","argumentHint":"[prompt-audit [\u003cpath\u003e]]","aliases":["checkup"],"builtin":true},{"name":"loop","description":"Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace.","argumentHint":"[interval] [prompt]","aliases":["proactive"],"builtin":true},{"name":"schedule","description":"Create, update, list, or run scheduled cloud agents (routines) that execute on a cron schedule.","argumentHint":"","aliases":["routines"],"builtin":true},{"name":"claude-api","description":"Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.\nTRIGGER — read BEFORE opening the target file; don't skip because it \"looks like a one-liner\" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).\nSKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).","argumentHint":"","builtin":true},{"name":"workflow-authoring","description":"Reference for writing a Workflow tool script (script API and gotchas, resume, quality patterns, worked examples). Load before authoring a script for a workflow the user already opted into; it does not itself authorize running one.","argumentHint":"","builtin":true},{"name":"run","description":"Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).","argumentHint":"","builtin":true},{"name":"run-skill-generator","description":"Author or improve the run-\u003cunit\u003e skill - a per-project skill that tells agents how to build, launch, and drive this project's app. Use when the user asks to set up the project, get it running, write run instructions, or verify build/run steps work from a clean environment.","argumentHint":"","builtin":true},{"name":"plugin-authoring","description":"Make a mod: a change to Claude's own interface or behaviour, such as a live pane or panel, a band above the prompt, a status line, a toast, a slash command, or a hook on tool calls or prompts. Use to make, change or debug a mod or its hooks module.","argumentHint":"","builtin":true},{"name":"advisor","description":"Let Claude consult a stronger model at key moments","argumentHint":"[fable|opus|sonnet|off]","builtin":true},{"name":"agents","description":"(removed) Ask Claude to create/manage subagents, or edit .claude/agents/","argumentHint":"","builtin":true},{"name":"auto-mode-setup","description":"Teach auto mode about your environment, plus optional rule tweaks","argumentHint":"[--request-id \u003cuuid\u003e] (--wizard posture=… scope=… depth=… --propose | --expect-sha256 \u003c64-hex\u003e --apply-file \u003cpath\u003e)","builtin":true},{"name":"autocompact","description":"Configure the auto-compact window size","argumentHint":"[auto|\u003ctokens\u003e]","builtin":true},{"name":"clear","description":"Start a new session with empty context; previous session stays on disk (resumable with /resume)","argumentHint":"[name]","aliases":["reset","new"],"builtin":true},{"name":"color","description":"Set the prompt bar color for this session","argumentHint":"[red|blue|green|yellow|purple|orange|pink|cyan|default]","builtin":true},{"name":"compact","description":"Free up context by summarizing the conversation so far","argumentHint":"\u003coptional custom summarization instructions\u003e","builtin":true},{"name":"config","description":"Set a setting by key","argumentHint":"key=value","aliases":["settings"],"builtin":true},{"name":"output-style","description":"List output styles or switch to one","argumentHint":"[style]","builtin":true},{"name":"context","description":"Show current context usage","argumentHint":"","builtin":true},{"name":"effort","description":"Set effort level for model usage","argumentHint":"\u003clow|medium|high|xhigh|max|auto|ultracode [on|off]\u003e","builtin":true},{"name":"fast","description":"Toggle fast mode (Opus 5.5)","argumentHint":"[on|off]","builtin":true},{"name":"focus","description":"Toggle focus view: just your prompt, summary, and response","argumentHint":"[on|off]","builtin":true},{"name":"heapdump","description":"Dump the JS heap to the Desktop; on Linux with no Desktop folder, the home directory","argumentHint":"","builtin":true},{"name":"init","description":"Initialize a new CLAUDE.md file with codebase documentation","argumentHint":"","builtin":true},{"name":"mcp","description":"Manage MCP servers","argumentHint":"[reconnect|enable|disable [\u003cserver\u003e|all]]","builtin":true},{"name":"import","description":"Import config from another AI coding agent","argumentHint":"","builtin":true},{"name":"model","description":"Set the AI model for Claude Code","argumentHint":"\u003cmodel\u003e","builtin":true},{"name":"__remote-workflow","description":"Run the workflow script delivered in this session environment (server-launched sessions only)","argumentHint":"","builtin":true},{"name":"workflow-launch-exec","description":"Execute a server-launched workflow handoff (workflow_launch event sessions only)","argumentHint":"","builtin":true},{"name":"reload-plugins","description":"Activate pending plugin changes in the current session","argumentHint":"[--force]","builtin":true},{"name":"reload-skills","description":"Pick up skills added or changed on disk during this session","argumentHint":"","builtin":true},{"name":"rename","description":"Rename the current conversation","argumentHint":"[name]","aliases":["name"],"builtin":true},{"name":"ultrareview","description":"Start a cloud agent that finds and verifies bugs in your branch (~15-25 min, $5-$25 USD) · Runs in a cloud session. See https://code.claude.com/docs/en/claude-code-on-the-web","argumentHint":"","builtin":true},{"name":"security-review","description":"Complete a security review of the pending changes on the current branch","argumentHint":"","builtin":true},{"name":"usage-credits","description":"Configure usage credits or request them from your admin when you hit a limit","argumentHint":"","builtin":true},{"name":"extra-usage","description":"Renamed to /usage-credits","argumentHint":"","builtin":true},{"name":"usage","description":"Show session cost, plan usage, and what's contributing to your limits","argumentHint":"","aliases":["cost","stats"],"builtin":true},{"name":"insights","description":"Generate a report analyzing your Claude Code sessions","argumentHint":"","builtin":true},{"name":"recap","description":"Generate a one-line session recap now","argumentHint":"","builtin":true},{"name":"skill-doctor","description":"Show which loaded skills are unused and costing context","argumentHint":"","builtin":true},{"name":"goal","description":"Set a goal — keep working until the condition is met","argumentHint":"","builtin":true},{"name":"design-consent","description":"Grant Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"design-revoke","description":"Revoke Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"list-agents","description":"List subagents, teammates, and other Claude sessions you can message","argumentHint":"","aliases":["peers"],"builtin":true},{"name":"team-onboarding","description":"Help teammates ramp on Claude Code with a guide from your usage","argumentHint":"","builtin":true},{"name":"docs","description":"docs (editable docs people share and comment on; the default for any document, named as a doc or not: a document, report, proposal, resume, cover letter, letter, contract, policy, form, template, worksheet, essay, handbook, guide, how-to, cheat sheet, SOP or other writing to keep, share, collaborate on, send, submit, print or sign; a doc exports to Word, PDF, Markdown or Google Docs, so needing a file to send, attach, upload, submit or print is no reason to pick Word, and a file nobody asked for is a doc, not Word; a plan, comparison, summary or notes asked in chat stays in chat; a pasted claude.ai artifact link may be a doc: check with docs tools first; Word or another file format named, tracked changes wanted, or a .docx to change or use as a template → that format's skill): making one → if no docs-connector instructions are in context, call its `guide` (topic.instructions) first; then create the doc (headings only, no body) before any search, file read or plan, even with files attached. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docs"]},{"name":"docx","description":"Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx) or Word templates (.dotx). Triggers include: any mention of Microsoft Word Documents, such as 'Word doc', 'word document', '.docx', '.dotx', 'microsoft doc'. Also use when extracting or reorganizing content from .docx or .dotx files, inserting or replacing images in documents, find-and-replace in Word files, working with tracked changes or comments, or converting content into a polished Word document. If the user asks for a deliverable as a Word or .docx file (to download, email or print), use this skill. However, if they ask for a document, page, report, memo, or notes WITHOUT naming a file format and the session offers Claude's own dedicated document or page skill or connector, use that instead, even if they will email or print it. Do NOT use for PDFs, spreadsheets, Google Docs, or coding unrelated to document generation. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docx"]},{"name":"google-workspace","description":"Read this before the first Google Drive, Docs, Sheets or Slides connector call whenever the task creates or changes a Google file. Use this skill whenever the user wants to create or change a Google Doc, Sheet or Slides file in their Google Drive. Triggers include: a request that names Google Docs, Sheets, Slides or Drive and asks to make, edit, format, copy or rename a file; a docs.google.com link with a request to change that file, even a one-line fix or suggested edits; and any follow-up change to a Google file from earlier in the chat, even \"change it\" or \"add a tab\". Includes helper scripts for document positions, cell ranges and slide layout. However, if the user asks for a doc, deck or spreadsheet without naming Google, or gives a Google file only as source material for something new, use Claude's own output type instead. Do NOT use for read-only questions about a Google file, or for Word, Excel, PowerPoint or PDF files. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:google-workspace"]},{"name":"import-memory","description":"Import a memory export from another AI assistant into Claude's memory — conversationally, additively, and with the content treated as data. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:import-memory"]},{"name":"morning","description":"Render the user's morning brief as a styled HTML artifact, or set it up as a recurring weekday task. Use only when the user explicitly asks to run, see, or set up their morning brief, or if they invoke /morning by name. A question about their day, schedule, or calendar is not by itself a request for the brief; answer it directly instead. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:morning"]},{"name":"pdf","description":"Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text/tables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pdf"]},{"name":"pptx","description":"Use this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations as PowerPoint (.pptx) files; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an email, summary, or creating a different type of slide deck); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates (.potx), layouts, speaker notes, or comments. Trigger whenever the user asks for a PowerPoint or .pptx file, or references a .pptx or .potx filename, regardless of what they plan to do with the content afterward. However, when the user asks for a deck, slides, a slide deck, or a presentation without naming a file format, default to using a dedicated slide-deck artifact type or a separate slides skill if this session offers one; otherwise, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pptx"]},{"name":"skill-creator","description":"Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:skill-creator"]},{"name":"xlsx","description":"Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .xltx, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like \"the xlsx in my downloads\") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:xlsx"]}],"agents":[{"name":"claude","description":"Catch-all for any task that doesn't fit a more specific agent. FleetView's default when no agent name is typed."},{"name":"claude-code-guide","description":"Use this agent when the user asks questions (\"Can Claude...\", \"Does Claude...\", \"How do I...\") about: (1) Claude Code (the CLI tool) - features, hooks, slash commands, MCP servers, settings, IDE integrations, keyboard shortcuts; (2) Claude Agent SDK - building custom agents; (3) Claude API (formerly Anthropic API) - Messages API for directly passing messages to Claude, Tool Runner (`client.beta.messages.tool_runner`) for running an agentic loop over your own tools, manual tool-use loops, Managed Agents for server-hosted agents with a managed sandbox, prompt caching, and general Anthropic SDK usage; (4) Claude Tag (Claude in Slack) - what it is, setting it up for a Slack workspace, `/install-slack-app`; (5) `claude plugin eval` (writing and running plugin eval suites, its JSON/report, sandbox, CI) and the `/skill-doctor` report. **IMPORTANT:** Before spawning a new agent, check if there is already a running or recently completed claude-code-guide agent that you can continue via SendMessage.","model":"haiku"},{"name":"Explore","description":"Fast read-only search agent for locating code. Use it to find files by pattern (eg. \"src/components/**/*.tsx\"), grep for symbols or keywords (eg. \"API endpoints\"), or answer \"where is X defined / which files reference Y.\" Do NOT use it for code review, design-doc auditing, cross-file consistency checks, or open-ended analysis — it reads excerpts rather than whole files and will miss content past its read window. When calling, specify search breadth: \"quick\" for a single targeted lookup, \"medium\" for moderate exploration, or \"very thorough\" to search across multiple locations and naming conventions.","model":"inherit"},{"name":"general-purpose","description":"General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you."},{"name":"Plan","description":"Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.","model":"inherit"},{"name":"statusline-setup","description":"Use this agent to configure the user's Claude Code status line setting.","model":"sonnet"}],"output_style":"default","available_output_styles":["default","Proactive","Concise","Explanatory","Learning"],"user_output_styles_dir":"/Users/flamingcow/.claude/output-styles","models":[{"value":"default","resolvedModel":"claude-opus-5-5","displayName":"Default (recommended)","description":"Opus 5.5 · Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"opus","resolvedModel":"claude-opus-5-5","displayName":"Opus 5.5","description":"For complex work and everyday tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"fable","resolvedModel":"claude-fable-5-1","displayName":"Fable 5.1","description":"For your toughest challenges","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"sonnet","resolvedModel":"claude-sonnet-5-5","displayName":"Sonnet 5.5","description":"Most efficient for simpler tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"haiku","resolvedModel":"claude-haiku-5-5","displayName":"Haiku 5.5","description":"Fastest for quick answers","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-haiku-4-5-20251001","resolvedModel":"claude-haiku-4-5-20251001","displayName":"Haiku 4.5","description":"Fastest for quick answers"},{"value":"claude-sonnet-5","resolvedModel":"claude-sonnet-5","displayName":"Sonnet 5","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-5","resolvedModel":"claude-opus-5","displayName":"Opus 5","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-fable-5","resolvedModel":"claude-fable-5","displayName":"Fable 5","description":"Most capable for your hardest and longest-running tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-8","resolvedModel":"claude-opus-4-8","displayName":"Opus 4.8","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-opus-4-7","resolvedModel":"claude-opus-4-7","displayName":"Opus 4.7","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-6","resolvedModel":"claude-opus-4-6","displayName":"Opus 4.6","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-sonnet-4-6","resolvedModel":"claude-sonnet-4-6","displayName":"Sonnet 4.6","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true}],"account":{"email":"[redacted]","organization":"[redacted]'s Organization","subscriptionType":"Claude Max","apiProvider":"firstParty"},"pid":31923,"claude_code_version":"2.1.296","current_permission_mode":"bypassPermissions","feedback_mode":{"kind":"post"},"analytics_disabled":false,"remote_control_auto_enable":false,"remote_control_auto_connect_default":false,"remote_control_available":true,"remote_control_auto_on_by_default":false,"ide_rc_auto_enable_gate":true,"fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","session_state":"idle","capabilities":["ui_surface_v1"]},"pending_permission_requests":[],"pending_user_dialog_requests":[]}}} {"dir":"send","msg":{"message":{"content":"Remember the codeword AZURE-17. Reply with the single word ok.","role":"user"},"parent_tool_use_id":null,"session_id":"","type":"user"}} -{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"running","uuid":"16f4f0b8-1b67-4102-9fe8-e8a98cf79b2f","session_id":"69d6b49d-423a-466e-89a2-9131c2a21cca"}} -{"dir":"recv","msg":{"type":"system","subtype":"init","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestResume1233782747/001","session_id":"69d6b49d-423a-466e-89a2-9131c2a21cca","tools":["Task","Artifact","ArtifactComments","ArtifactData","Bash","CronCreate","CronDelete","CronList","DesignSync","Edit","EnterWorktree","ExitWorktree","ListAgents","LSP","Monitor","NotebookEdit","PushNotification","Read","RemoteTrigger","ReportFindings","ScheduleWakeup","SendMessage","Skill","TaskStop","ToolSearch","WebFetch","WebSearch","Workflow","Write"],"mcp_servers":[{"name":"claude.ai Claude Docs","status":"pending","source":"claudeai"},{"name":"claude.ai Heliosian","status":"pending","source":"claudeai"},{"name":"claude.ai Fastmail","status":"pending","source":"claudeai"},{"name":"claude.ai Google Calendar","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Gmail","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Google Drive","status":"needs-auth","source":"claudeai"}],"model":"claude-sonnet-5-5","permissionMode":"bypassPermissions","slash_commands":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","advisor","agents","auto-mode-setup","autocompact","clear","color","compact","config","output-style","context","effort","fast","focus","heapdump","init","mcp","import","model","__remote-workflow","workflow-launch-exec","reload-plugins","reload-skills","rename","ultrareview","security-review","usage-credits","extra-usage","usage","insights","recap","skill-doctor","goal","design-consent","design-revoke","list-agents","team-onboarding","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"terminal_slash_commands":["doctor","color","focus","reload-plugins"],"apiKeySource":"none","claude_code_version":"2.1.296","output_style":"default","agents":["claude","claude-code-guide","Explore","general-purpose","Plan","statusline-setup"],"skills":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"plugins":[{"name":"clangd-lsp","path":"/Users/flamingcow/.claude/plugins/cache/claude-plugins-official/clangd-lsp/1.0.0","source":"clangd-lsp@claude-plugins-official","version":"1.0.0"},{"name":"cc-plugin-agents-md","path":"builtin","source":"cc-plugin-agents-md@builtin"},{"name":"cc-plugin-telemetry","path":"builtin","source":"cc-plugin-telemetry@builtin"},{"name":"cc-plugin-plugin-authoring","path":"builtin","source":"cc-plugin-plugin-authoring@builtin"}],"capabilities":["interrupt_receipt_v1","interrupt_cancel_queued_v1","interrupt_send_now_v1","msg_lifecycle_v1","request_marker_lists_v1","sdk_mcp_tools_list_changed","sdk_mcp_manifests","mcp_read_resource_v1","mcp_tool_ui_meta_v1","ui_surface_v1"],"analytics_disabled":false,"product_feedback_disabled":false,"uuid":"3e4446d8-7c17-416c-9ed8-c1c64fc4285a","memory_paths":{"auto":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestResume1233782747-001/memory/"},"scratchpad_path":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestResume1233782747-001/69d6b49d-423a-466e-89a2-9131c2a21cca/scratchpad","messaging_socket_path":"/tmp/cc-socks/27100.sock","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","per_turn_effort_active":true,"view_mode":"default"}} -{"dir":"recv","msg":{"type":"rate_limit_event","rate_limit_info":{"status":"allowed","resetsAt":1791990000,"rateLimitType":"five_hour","overageStatus":"rejected","overageDisabledReason":"org_level_disabled","isUsingOverage":false,"unifiedWindows":{"five_hour":{"utilization":0.04,"resetsAt":1791990000},"seven_day":{"utilization":0.27,"resetsAt":1792270800}}},"uuid":"0a8c7394-1501-409d-909e-921614f4d6cf","session_id":"69d6b49d-423a-466e-89a2-9131c2a21cca"}} -{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2FDR4At3YbEGWtNbcvK","type":"message","role":"assistant","content":[{"type":"text","text":"ok"}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":37599,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":37599},"output_tokens":4,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"69d6b49d-423a-466e-89a2-9131c2a21cca","uuid":"8bbc1f13-719a-45fa-bf7e-cae5d9a75f22","timestamp":"2026-10-14T13:56:56.410Z","request_id":"req_011Cg2FDQkpAnL8oWgV73Kxa"}} -{"dir":"recv","msg":{"duration_api_ms":4593,"stop_reason":"end_turn","session_id":"69d6b49d-423a-466e-89a2-9131c2a21cca","total_cost_usd":0.1505693,"usage":{"input_tokens":2,"cache_creation_input_tokens":37599,"cache_read_input_tokens":0,"output_tokens":4,"output_tokens_details":{"thinking_tokens":0},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":37599,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":4,"cache_read_input_tokens":0,"cache_creation_input_tokens":37599,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":37599},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":1188,"outputTokens":21,"cacheReadInputTokens":0,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.00012930000000000003,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":0,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"},"claude-sonnet-5-5":{"inputTokens":2,"outputTokens":4,"cacheReadInputTokens":0,"cacheCreationInputTokens":37599,"webSearchRequests":0,"costUSD":0.15044,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":0,"canonicalModel":"claude-sonnet-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"ok","ttft_ms":4040,"type":"result","duration_ms":4060,"uuid":"daa79ce8-7b9c-4b52-b50d-6992fe4208c1","ttft_stream_ms":4035,"time_to_request_ms":32,"first_content_frame_ms":4035,"queued_turn_count":0,"result_index":0}} -{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"idle","uuid":"ded7539f-ad76-4d44-8aa7-b3985648829c","session_id":"69d6b49d-423a-466e-89a2-9131c2a21cca"}} +{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"running","uuid":"0d0b683a-476d-4f74-8af2-a0abd10c7c09","session_id":"5ad73b02-ffc2-42d6-af14-f192fd1b1958"}} +{"dir":"recv","msg":{"type":"system","subtype":"init","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestResume2224935177/001","session_id":"5ad73b02-ffc2-42d6-af14-f192fd1b1958","tools":["Task","Artifact","ArtifactComments","ArtifactData","Bash","CronCreate","CronDelete","CronList","DesignSync","Edit","EnterWorktree","ExitWorktree","ListAgents","LSP","Monitor","NotebookEdit","PushNotification","Read","RemoteTrigger","ReportFindings","ScheduleWakeup","SendMessage","Skill","TaskStop","ToolSearch","WebFetch","WebSearch","Workflow","Write"],"mcp_servers":[{"name":"claude.ai Claude Docs","status":"pending","source":"claudeai"},{"name":"claude.ai Heliosian","status":"pending","source":"claudeai"},{"name":"claude.ai Fastmail","status":"pending","source":"claudeai"},{"name":"claude.ai Google Calendar","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Gmail","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Google Drive","status":"needs-auth","source":"claudeai"}],"model":"claude-sonnet-5-5","permissionMode":"bypassPermissions","slash_commands":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","advisor","agents","auto-mode-setup","autocompact","clear","color","compact","config","output-style","context","effort","fast","focus","heapdump","init","mcp","import","model","__remote-workflow","workflow-launch-exec","reload-plugins","reload-skills","rename","ultrareview","security-review","usage-credits","extra-usage","usage","insights","recap","skill-doctor","goal","design-consent","design-revoke","list-agents","team-onboarding","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"terminal_slash_commands":["doctor","color","focus","reload-plugins"],"apiKeySource":"none","claude_code_version":"2.1.296","output_style":"default","agents":["claude","claude-code-guide","Explore","general-purpose","Plan","statusline-setup"],"skills":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"plugins":[{"name":"clangd-lsp","path":"/Users/flamingcow/.claude/plugins/cache/claude-plugins-official/clangd-lsp/1.0.0","source":"clangd-lsp@claude-plugins-official","version":"1.0.0"},{"name":"cc-plugin-agents-md","path":"builtin","source":"cc-plugin-agents-md@builtin"},{"name":"cc-plugin-telemetry","path":"builtin","source":"cc-plugin-telemetry@builtin"},{"name":"cc-plugin-plugin-authoring","path":"builtin","source":"cc-plugin-plugin-authoring@builtin"}],"capabilities":["interrupt_receipt_v1","interrupt_cancel_queued_v1","interrupt_send_now_v1","msg_lifecycle_v1","request_marker_lists_v1","sdk_mcp_tools_list_changed","sdk_mcp_manifests","mcp_read_resource_v1","mcp_tool_ui_meta_v1","ui_surface_v1"],"analytics_disabled":false,"product_feedback_disabled":false,"uuid":"9e551aee-d428-46ed-84a6-6e5c17858680","memory_paths":{"auto":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestResume2224935177-001/memory/"},"scratchpad_path":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestResume2224935177-001/5ad73b02-ffc2-42d6-af14-f192fd1b1958/scratchpad","messaging_socket_path":"/tmp/cc-socks/31923.sock","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","per_turn_effort_active":true,"view_mode":"default"}} +{"dir":"recv","msg":{"type":"rate_limit_event","rate_limit_info":{"status":"allowed","resetsAt":1791990000,"rateLimitType":"five_hour","overageStatus":"rejected","overageDisabledReason":"org_level_disabled","isUsingOverage":false,"unifiedWindows":{"five_hour":{"utilization":0.1,"resetsAt":1791990000},"seven_day":{"utilization":0.27,"resetsAt":1792270800}}},"uuid":"c567eb49-bdb7-4768-9925-fde051c549f5","session_id":"5ad73b02-ffc2-42d6-af14-f192fd1b1958"}} +{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2Gx9UcAiKbtnR27nbrr","type":"message","role":"assistant","content":[{"type":"text","text":"ok"}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":15232,"cache_read_input_tokens":22367,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":15232},"output_tokens":4,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"5ad73b02-ffc2-42d6-af14-f192fd1b1958","uuid":"6e1365f9-3b06-45ef-81dd-1f8037e79db1","timestamp":"2026-10-14T14:19:40.778Z","request_id":"req_011Cg2Gx9CzgxwyWg8Ef8dZX"}} +{"dir":"recv","msg":{"duration_api_ms":2803,"stop_reason":"end_turn","session_id":"5ad73b02-ffc2-42d6-af14-f192fd1b1958","total_cost_usd":0.063338,"usage":{"input_tokens":2,"cache_creation_input_tokens":15232,"cache_read_input_tokens":22367,"output_tokens":4,"output_tokens_details":{"thinking_tokens":0},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":15232,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":4,"cache_read_input_tokens":22367,"cache_creation_input_tokens":15232,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":15232},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":1188,"outputTokens":21,"cacheReadInputTokens":0,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.00012930000000000003,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":0,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"},"claude-sonnet-5-5":{"inputTokens":2,"outputTokens":4,"cacheReadInputTokens":22367,"cacheCreationInputTokens":15232,"webSearchRequests":0,"costUSD":0.0632087,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":0,"canonicalModel":"claude-sonnet-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"ok","ttft_ms":1904,"type":"result","duration_ms":1923,"uuid":"064eaca1-cf8d-4316-8fea-3456afa0367e","ttft_stream_ms":1903,"time_to_request_ms":28,"first_content_frame_ms":1904,"queued_turn_count":0,"result_index":0}} +{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"idle","uuid":"b9f7430e-04ba-4a68-ad27-fe2b2b91b4ec","session_id":"5ad73b02-ffc2-42d6-af14-f192fd1b1958"}} diff --git a/internal/claude/testdata/transcripts/TestResume-resumed.jsonl b/internal/claude/testdata/transcripts/TestResume-resumed.jsonl index 21b8079..d8e0b6e 100644 --- a/internal/claude/testdata/transcripts/TestResume-resumed.jsonl +++ b/internal/claude/testdata/transcripts/TestResume-resumed.jsonl @@ -1,9 +1,9 @@ -{"dir":"send","msg":{"request":{"hooks":null,"subtype":"initialize"},"request_id":"req_3282a55a2785ae64","type":"control_request"}} -{"dir":"recv","msg":{"type":"control_response","response":{"subtype":"success","request_id":"req_3282a55a2785ae64","response":{"commands":[{"name":"deep-research","description":"Deep research harness — fan-out web searches, fetch sources, adversarially verify claims, synthesize a cited report. (dynamic workflow)","argumentHint":"","builtin":true},{"name":"design","description":"Make a new Design artifact from a brief","argumentHint":"[what to design]","builtin":true},{"name":"slides","description":"Make a new Slides deck artifact from a brief","argumentHint":"[what the deck is about]","builtin":true},{"name":"design-sync","description":"Push a React design system to claude.ai/design. This runs a converter that bundles the real component code (from Storybook or a bare package) and uploads it. Use when the user runs /design-sync or says \"sync my design system to Claude Design\".","argumentHint":"[\u003cproject hint, e.g. \"Acme DS\"\u003e]","builtin":true},{"name":"dataviz","description":"Use this skill whenever you are about to create ANY chart, graph, plot, dashboard, or data visualization, in ANY output medium — an HTML or React artifact, inline SVG, plotting code in any library (matplotlib, plotly, d3, Recharts, …), an image/PNG you will render and upload, or a chart shared into Slack. Read it BEFORE writing the first line of chart code, choosing chart colors, building a stat tile / meter / KPI row, or laying out a dashboard. When the destination is a first-party document connector (host-designated, never self-described) that renders live charts, hand it the rows (inline, or as an uploaded data file the chart cites) rather than a rendered PNG/SVG — a picture of a chart loses hover, data inspection and per-value comments. Produces visualizations that read as one system — elegant, accessible, consistent in light and dark — using a brand-neutral placeholder palette you swap for your own. Teaches a design-system-agnostic method: a form heuristic, a color formula with a runnable validator, mark specs, and interaction rules. A validated default palette is documented in `references/palette.md` — swap that file's values for your brand's. Triggers on: \"chart\", \"graph\", \"plot\", \"data viz\", \"visualization\", \"dashboard\", \"analytics\", \"visualize data\", \"categorical colors\", \"sequential / diverging palette\", \"stat tile\", \"sparkline\", \"heatmap\", \"legend\", \"axis\", \"tooltip\", \"chart colors\", \"color by series\".","argumentHint":"","builtin":true},{"name":"artifact-diagramming","description":"Diagramming know-how for Artifacts - when a picture earns its place, how to draw one that shows the real mechanism, and the inline-SVG mechanics that keep it legible in both themes.","argumentHint":"","builtin":true},{"name":"artifact-capabilities","description":"Runtime capabilities a published Artifact page can be granted — behavior static HTML cannot provide on its own, such as the page reading live or connected data, remembering what people do on it (a poll, a sign-up sheet, a checklist, a document edited in place — it saves new versions of itself), keeping state shared across viewers, knowing who is viewing, asking Claude a question of its own, storing files people add, handing the viewer a file to save, or using the viewer's camera, microphone, location, screen or device motion. Serves this user's live capability roster and the typed call definitions. Load it whenever any such runtime behavior would make an artifact more useful, before writing the page.","argumentHint":"","builtin":true},{"name":"update-config","description":"Use this skill to configure the Claude Code harness via settings.json. Automated behaviors (\"from now on when X\", \"each time X\", \"whenever X\", \"before/after X\") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions (\"allow X\", \"add permission\", \"move permission to\"), env vars (\"set X=Y\"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: \"allow npm commands\", \"add bq permission to global settings\", \"move permission to user settings\", \"set DEBUG=true\", \"when claude stops show X\". For simple settings like theme/model, suggest the /config command.","argumentHint":"","builtin":true},{"name":"verify","description":"Verify that a code change actually does what it's supposed to by exercising it end-to-end and observing behavior — drive the affected flow, not just tests or typecheck. Run before committing nontrivial changes; bootstraps this repo's project verify skill if none exists yet. Don't invoke it on a diff that only touches tests, docs, or other code with no runtime surface to drive (a change to product source always has one) — there's nothing to observe.","argumentHint":"","builtin":true},{"name":"debug","description":"Enable debug logging for this session and help diagnose issues","argumentHint":"[issue description]","builtin":true},{"name":"code-review","description":"Review the current diff, or a PR number/branch/path target, for correctness bugs (plus reuse/simplification/efficiency cleanups where the model's review recipe covers them) at the given effort level (low|medium|high|xhigh|max: from few high-confidence findings up to many, some of them uncertain; ultra: deep multi-agent review in the cloud); with no level given, it reuses the level you typed last. Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review. Pass --max-findings \u003cn\u003e to report up to n findings, or --max-findings all for every finding. The choice stays until you pass --max-findings default. For ultra on a GitHub.com PR target, --post asks to post the finished review’s findings to the PR as a single comment from the user’s GitHub account (not a review; the launch dialog still confirms in interactive sessions, while non-interactive mode posts on the flag alone) and --no-post hides that option.","argumentHint":"[low|medium|high|xhigh|max|ultra] [--fix] [--comment] [\u003cpr#\u003e|\u003cbranch\u003e|\u003cpath\u003e] [--max-findings \u003cn\u003e|all]","aliases":["review"],"builtin":true},{"name":"simplify","description":"Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.","argumentHint":"[\u003ctarget\u003e]","builtin":true},{"name":"batch","description":"Research and plan a large-scale change, then execute it in parallel across 5–30 isolated worktree agents that each open a PR.","argumentHint":"\u003cinstruction\u003e","builtin":true},{"name":"fewer-permission-prompts","description":"Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.","argumentHint":"","builtin":true},{"name":"doctor","description":"Health-check the user's Claude Code setup and fix issues: diagnose installation health — what the `claude doctor` terminal diagnostics cover — from local data (duplicate or leftover installs, PATH, unparseable settings files, broken or colliding agent definitions, skills whose frontmatter fails to parse); find unused skills, MCP servers, and plugins versus their context cost and disable dead weight; deduplicate local CLAUDE.md files against checked-in ones; trim checked-in CLAUDE.md files by cutting content a session could derive from the codebase (directory layouts, tech-stack lists, architecture overviews) while keeping gotchas, rationale, and non-standard conventions; migrate always-loaded CLAUDE.md guidance into lazy skills and nested CLAUDE.md files; flag slow hooks and context-heavy extensions; check the installed version is current; make auto mode the default permission mode; and pre-approve frequently denied read-only commands. Use when the user asks for a doctor run, checkup, audit, tune-up, or cleanup of their Claude Code setup or configuration.","argumentHint":"[prompt-audit [\u003cpath\u003e]]","aliases":["checkup"],"builtin":true},{"name":"loop","description":"Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace.","argumentHint":"[interval] [prompt]","aliases":["proactive"],"builtin":true},{"name":"schedule","description":"Create, update, list, or run scheduled cloud agents (routines) that execute on a cron schedule.","argumentHint":"","aliases":["routines"],"builtin":true},{"name":"claude-api","description":"Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.\nTRIGGER — read BEFORE opening the target file; don't skip because it \"looks like a one-liner\" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).\nSKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).","argumentHint":"","builtin":true},{"name":"workflow-authoring","description":"Reference for writing a Workflow tool script (script API and gotchas, resume, quality patterns, worked examples). Load before authoring a script for a workflow the user already opted into; it does not itself authorize running one.","argumentHint":"","builtin":true},{"name":"run","description":"Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).","argumentHint":"","builtin":true},{"name":"run-skill-generator","description":"Author or improve the run-\u003cunit\u003e skill - a per-project skill that tells agents how to build, launch, and drive this project's app. Use when the user asks to set up the project, get it running, write run instructions, or verify build/run steps work from a clean environment.","argumentHint":"","builtin":true},{"name":"plugin-authoring","description":"Make a mod: a change to Claude's own interface or behaviour, such as a live pane or panel, a band above the prompt, a status line, a toast, a slash command, or a hook on tool calls or prompts. Use to make, change or debug a mod or its hooks module.","argumentHint":"","builtin":true},{"name":"advisor","description":"Let Claude consult a stronger model at key moments","argumentHint":"[fable|opus|sonnet|off]","builtin":true},{"name":"agents","description":"(removed) Ask Claude to create/manage subagents, or edit .claude/agents/","argumentHint":"","builtin":true},{"name":"auto-mode-setup","description":"Teach auto mode about your environment, plus optional rule tweaks","argumentHint":"[--request-id \u003cuuid\u003e] (--wizard posture=… scope=… depth=… --propose | --expect-sha256 \u003c64-hex\u003e --apply-file \u003cpath\u003e)","builtin":true},{"name":"autocompact","description":"Configure the auto-compact window size","argumentHint":"[auto|\u003ctokens\u003e]","builtin":true},{"name":"clear","description":"Start a new session with empty context; previous session stays on disk (resumable with /resume)","argumentHint":"[name]","aliases":["reset","new"],"builtin":true},{"name":"color","description":"Set the prompt bar color for this session","argumentHint":"[red|blue|green|yellow|purple|orange|pink|cyan|default]","builtin":true},{"name":"compact","description":"Free up context by summarizing the conversation so far","argumentHint":"\u003coptional custom summarization instructions\u003e","builtin":true},{"name":"config","description":"Set a setting by key","argumentHint":"key=value","aliases":["settings"],"builtin":true},{"name":"output-style","description":"List output styles or switch to one","argumentHint":"[style]","builtin":true},{"name":"context","description":"Show current context usage","argumentHint":"","builtin":true},{"name":"effort","description":"Set effort level for model usage","argumentHint":"\u003clow|medium|high|xhigh|max|auto|ultracode [on|off]\u003e","builtin":true},{"name":"fast","description":"Toggle fast mode (Opus 5.5)","argumentHint":"[on|off]","builtin":true},{"name":"focus","description":"Toggle focus view: just your prompt, summary, and response","argumentHint":"[on|off]","builtin":true},{"name":"heapdump","description":"Dump the JS heap to the Desktop; on Linux with no Desktop folder, the home directory","argumentHint":"","builtin":true},{"name":"init","description":"Initialize a new CLAUDE.md file with codebase documentation","argumentHint":"","builtin":true},{"name":"mcp","description":"Manage MCP servers","argumentHint":"[reconnect|enable|disable [\u003cserver\u003e|all]]","builtin":true},{"name":"import","description":"Import config from another AI coding agent","argumentHint":"","builtin":true},{"name":"model","description":"Set the AI model for Claude Code","argumentHint":"\u003cmodel\u003e","builtin":true},{"name":"__remote-workflow","description":"Run the workflow script delivered in this session environment (server-launched sessions only)","argumentHint":"","builtin":true},{"name":"workflow-launch-exec","description":"Execute a server-launched workflow handoff (workflow_launch event sessions only)","argumentHint":"","builtin":true},{"name":"reload-plugins","description":"Activate pending plugin changes in the current session","argumentHint":"[--force]","builtin":true},{"name":"reload-skills","description":"Pick up skills added or changed on disk during this session","argumentHint":"","builtin":true},{"name":"rename","description":"Rename the current conversation","argumentHint":"[name]","aliases":["name"],"builtin":true},{"name":"ultrareview","description":"Start a cloud agent that finds and verifies bugs in your branch (~15-25 min, $5-$25 USD) · Runs in a cloud session. See https://code.claude.com/docs/en/claude-code-on-the-web","argumentHint":"","builtin":true},{"name":"security-review","description":"Complete a security review of the pending changes on the current branch","argumentHint":"","builtin":true},{"name":"usage-credits","description":"Configure usage credits or request them from your admin when you hit a limit","argumentHint":"","builtin":true},{"name":"extra-usage","description":"Renamed to /usage-credits","argumentHint":"","builtin":true},{"name":"usage","description":"Show session cost, plan usage, and what's contributing to your limits","argumentHint":"","aliases":["cost","stats"],"builtin":true},{"name":"insights","description":"Generate a report analyzing your Claude Code sessions","argumentHint":"","builtin":true},{"name":"recap","description":"Generate a one-line session recap now","argumentHint":"","builtin":true},{"name":"skill-doctor","description":"Show which loaded skills are unused and costing context","argumentHint":"","builtin":true},{"name":"goal","description":"Set a goal — keep working until the condition is met","argumentHint":"","builtin":true},{"name":"design-consent","description":"Grant Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"design-revoke","description":"Revoke Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"list-agents","description":"List subagents, teammates, and other Claude sessions you can message","argumentHint":"","aliases":["peers"],"builtin":true},{"name":"team-onboarding","description":"Help teammates ramp on Claude Code with a guide from your usage","argumentHint":"","builtin":true},{"name":"docs","description":"docs (editable docs people share and comment on; the default for any document, named as a doc or not: a document, report, proposal, resume, cover letter, letter, contract, policy, form, template, worksheet, essay, handbook, guide, how-to, cheat sheet, SOP or other writing to keep, share, collaborate on, send, submit, print or sign; a doc exports to Word, PDF, Markdown or Google Docs, so needing a file to send, attach, upload, submit or print is no reason to pick Word, and a file nobody asked for is a doc, not Word; a plan, comparison, summary or notes asked in chat stays in chat; a pasted claude.ai artifact link may be a doc: check with docs tools first; Word or another file format named, tracked changes wanted, or a .docx to change or use as a template → that format's skill): making one → if no docs-connector instructions are in context, call its `guide` (topic.instructions) first; then create the doc (headings only, no body) before any search, file read or plan, even with files attached. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docs"]},{"name":"docx","description":"Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx) or Word templates (.dotx). Triggers include: any mention of Microsoft Word Documents, such as 'Word doc', 'word document', '.docx', '.dotx', 'microsoft doc'. Also use when extracting or reorganizing content from .docx or .dotx files, inserting or replacing images in documents, find-and-replace in Word files, working with tracked changes or comments, or converting content into a polished Word document. If the user asks for a deliverable as a Word or .docx file (to download, email or print), use this skill. However, if they ask for a document, page, report, memo, or notes WITHOUT naming a file format and the session offers Claude's own dedicated document or page skill or connector, use that instead, even if they will email or print it. Do NOT use for PDFs, spreadsheets, Google Docs, or coding unrelated to document generation. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docx"]},{"name":"google-workspace","description":"Read this before the first Google Drive, Docs, Sheets or Slides connector call whenever the task creates or changes a Google file. Use this skill whenever the user wants to create or change a Google Doc, Sheet or Slides file in their Google Drive. Triggers include: a request that names Google Docs, Sheets, Slides or Drive and asks to make, edit, format, copy or rename a file; a docs.google.com link with a request to change that file, even a one-line fix or suggested edits; and any follow-up change to a Google file from earlier in the chat, even \"change it\" or \"add a tab\". Includes helper scripts for document positions, cell ranges and slide layout. However, if the user asks for a doc, deck or spreadsheet without naming Google, or gives a Google file only as source material for something new, use Claude's own output type instead. Do NOT use for read-only questions about a Google file, or for Word, Excel, PowerPoint or PDF files. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:google-workspace"]},{"name":"import-memory","description":"Import a memory export from another AI assistant into Claude's memory — conversationally, additively, and with the content treated as data. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:import-memory"]},{"name":"morning","description":"Render the user's morning brief as a styled HTML artifact, or set it up as a recurring weekday task. Use only when the user explicitly asks to run, see, or set up their morning brief, or if they invoke /morning by name. A question about their day, schedule, or calendar is not by itself a request for the brief; answer it directly instead. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:morning"]},{"name":"pdf","description":"Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text/tables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pdf"]},{"name":"pptx","description":"Use this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations as PowerPoint (.pptx) files; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an email, summary, or creating a different type of slide deck); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates (.potx), layouts, speaker notes, or comments. Trigger whenever the user asks for a PowerPoint or .pptx file, or references a .pptx or .potx filename, regardless of what they plan to do with the content afterward. However, when the user asks for a deck, slides, a slide deck, or a presentation without naming a file format, default to using a dedicated slide-deck artifact type or a separate slides skill if this session offers one; otherwise, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pptx"]},{"name":"skill-creator","description":"Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:skill-creator"]},{"name":"xlsx","description":"Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .xltx, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like \"the xlsx in my downloads\") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:xlsx"]}],"agents":[{"name":"claude","description":"Catch-all for any task that doesn't fit a more specific agent. FleetView's default when no agent name is typed."},{"name":"claude-code-guide","description":"Use this agent when the user asks questions (\"Can Claude...\", \"Does Claude...\", \"How do I...\") about: (1) Claude Code (the CLI tool) - features, hooks, slash commands, MCP servers, settings, IDE integrations, keyboard shortcuts; (2) Claude Agent SDK - building custom agents; (3) Claude API (formerly Anthropic API) - Messages API for directly passing messages to Claude, Tool Runner (`client.beta.messages.tool_runner`) for running an agentic loop over your own tools, manual tool-use loops, Managed Agents for server-hosted agents with a managed sandbox, prompt caching, and general Anthropic SDK usage; (4) Claude Tag (Claude in Slack) - what it is, setting it up for a Slack workspace, `/install-slack-app`; (5) `claude plugin eval` (writing and running plugin eval suites, its JSON/report, sandbox, CI) and the `/skill-doctor` report. **IMPORTANT:** Before spawning a new agent, check if there is already a running or recently completed claude-code-guide agent that you can continue via SendMessage.","model":"haiku"},{"name":"Explore","description":"Fast read-only search agent for locating code. Use it to find files by pattern (eg. \"src/components/**/*.tsx\"), grep for symbols or keywords (eg. \"API endpoints\"), or answer \"where is X defined / which files reference Y.\" Do NOT use it for code review, design-doc auditing, cross-file consistency checks, or open-ended analysis — it reads excerpts rather than whole files and will miss content past its read window. When calling, specify search breadth: \"quick\" for a single targeted lookup, \"medium\" for moderate exploration, or \"very thorough\" to search across multiple locations and naming conventions.","model":"inherit"},{"name":"general-purpose","description":"General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you."},{"name":"Plan","description":"Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.","model":"inherit"},{"name":"statusline-setup","description":"Use this agent to configure the user's Claude Code status line setting.","model":"sonnet"}],"output_style":"default","available_output_styles":["default","Proactive","Concise","Explanatory","Learning"],"user_output_styles_dir":"/Users/flamingcow/.claude/output-styles","models":[{"value":"default","resolvedModel":"claude-opus-5-5","displayName":"Default (recommended)","description":"Opus 5.5 · Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"opus","resolvedModel":"claude-opus-5-5","displayName":"Opus 5.5","description":"For complex work and everyday tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"fable","resolvedModel":"claude-fable-5-1","displayName":"Fable 5.1","description":"For your toughest challenges","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"sonnet","resolvedModel":"claude-sonnet-5-5","displayName":"Sonnet 5.5","description":"Most efficient for simpler tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"haiku","resolvedModel":"claude-haiku-5-5","displayName":"Haiku 5.5","description":"Fastest for quick answers","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-haiku-4-5-20251001","resolvedModel":"claude-haiku-4-5-20251001","displayName":"Haiku 4.5","description":"Fastest for quick answers"},{"value":"claude-sonnet-5","resolvedModel":"claude-sonnet-5","displayName":"Sonnet 5","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-5","resolvedModel":"claude-opus-5","displayName":"Opus 5","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-fable-5","resolvedModel":"claude-fable-5","displayName":"Fable 5","description":"Most capable for your hardest and longest-running tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-8","resolvedModel":"claude-opus-4-8","displayName":"Opus 4.8","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-opus-4-7","resolvedModel":"claude-opus-4-7","displayName":"Opus 4.7","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-6","resolvedModel":"claude-opus-4-6","displayName":"Opus 4.6","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-sonnet-4-6","resolvedModel":"claude-sonnet-4-6","displayName":"Sonnet 4.6","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true}],"account":{"email":"[redacted]","organization":"[redacted]'s Organization","subscriptionType":"Claude Max","apiProvider":"firstParty"},"pid":27551,"claude_code_version":"2.1.296","current_permission_mode":"bypassPermissions","feedback_mode":{"kind":"post"},"analytics_disabled":false,"remote_control_auto_enable":false,"remote_control_auto_connect_default":false,"remote_control_available":true,"remote_control_auto_on_by_default":false,"ide_rc_auto_enable_gate":true,"fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","session_state":"idle","capabilities":["ui_surface_v1"]},"pending_permission_requests":[],"pending_user_dialog_requests":[]}}} -{"dir":"send","msg":{"message":{"content":"What was the codeword? Reply with just the codeword.","role":"user"},"parent_tool_use_id":null,"session_id":"69d6b49d-423a-466e-89a2-9131c2a21cca","type":"user"}} -{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"running","uuid":"5c7e633c-217a-4243-8b53-ab947bfcdb3e","session_id":"69d6b49d-423a-466e-89a2-9131c2a21cca"}} -{"dir":"recv","msg":{"type":"system","subtype":"init","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestResume1233782747/001","session_id":"69d6b49d-423a-466e-89a2-9131c2a21cca","tools":["Task","Artifact","ArtifactComments","ArtifactData","Bash","CronCreate","CronDelete","CronList","DesignSync","Edit","EnterWorktree","ExitWorktree","ListAgents","LSP","Monitor","NotebookEdit","PushNotification","Read","RemoteTrigger","ReportFindings","ScheduleWakeup","SendMessage","Skill","TaskStop","ToolSearch","WebFetch","WebSearch","Workflow","Write"],"mcp_servers":[{"name":"claude.ai Claude Docs","status":"pending","source":"claudeai"},{"name":"claude.ai Heliosian","status":"pending","source":"claudeai"},{"name":"claude.ai Fastmail","status":"pending","source":"claudeai"},{"name":"claude.ai Google Calendar","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Gmail","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Google Drive","status":"needs-auth","source":"claudeai"}],"model":"claude-sonnet-5-5","permissionMode":"bypassPermissions","slash_commands":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","advisor","agents","auto-mode-setup","autocompact","clear","color","compact","config","output-style","context","effort","fast","focus","heapdump","init","mcp","import","model","__remote-workflow","workflow-launch-exec","reload-plugins","reload-skills","rename","ultrareview","security-review","usage-credits","extra-usage","usage","insights","recap","skill-doctor","goal","design-consent","design-revoke","list-agents","team-onboarding","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"terminal_slash_commands":["doctor","color","focus","reload-plugins"],"apiKeySource":"none","claude_code_version":"2.1.296","output_style":"default","agents":["claude","claude-code-guide","Explore","general-purpose","Plan","statusline-setup"],"skills":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"plugins":[{"name":"clangd-lsp","path":"/Users/flamingcow/.claude/plugins/cache/claude-plugins-official/clangd-lsp/1.0.0","source":"clangd-lsp@claude-plugins-official","version":"1.0.0"},{"name":"cc-plugin-agents-md","path":"builtin","source":"cc-plugin-agents-md@builtin"},{"name":"cc-plugin-telemetry","path":"builtin","source":"cc-plugin-telemetry@builtin"},{"name":"cc-plugin-plugin-authoring","path":"builtin","source":"cc-plugin-plugin-authoring@builtin"}],"capabilities":["interrupt_receipt_v1","interrupt_cancel_queued_v1","interrupt_send_now_v1","msg_lifecycle_v1","request_marker_lists_v1","sdk_mcp_tools_list_changed","sdk_mcp_manifests","mcp_read_resource_v1","mcp_tool_ui_meta_v1","ui_surface_v1"],"analytics_disabled":false,"product_feedback_disabled":false,"uuid":"d742f18a-1c94-41f8-98d9-3790f851e492","memory_paths":{"auto":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestResume1233782747-001/memory/"},"scratchpad_path":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestResume1233782747-001/69d6b49d-423a-466e-89a2-9131c2a21cca/scratchpad","messaging_socket_path":"/tmp/cc-socks/27551.sock","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","per_turn_effort_active":true,"view_mode":"default"}} -{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2FDqKvVAHSgNFnFjmeL","type":"message","role":"assistant","content":[{"type":"text","text":"AZURE-17"}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":7016,"cache_read_input_tokens":30630,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":7016},"output_tokens":8,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":{"cache_miss_reason":{"type":"messages_changed","cache_missed_input_tokens":13150}},"context_management":null},"parent_tool_use_id":null,"session_id":"69d6b49d-423a-466e-89a2-9131c2a21cca","uuid":"ec69e787-f631-4270-b255-691d89b25e61","timestamp":"2026-10-14T13:56:59.941Z","request_id":"req_011Cg2FDq2a8U5TvPR5HPYbC"}} -{"dir":"recv","msg":{"type":"rate_limit_event","rate_limit_info":{"status":"allowed","resetsAt":1791990000,"rateLimitType":"five_hour","overageStatus":"rejected","overageDisabledReason":"org_level_disabled","isUsingOverage":false,"unifiedWindows":{"five_hour":{"utilization":0.04,"resetsAt":1791990000},"seven_day":{"utilization":0.27,"resetsAt":1792270800}}},"uuid":"e9d56400-cdc7-460d-aad7-2d63552445aa","session_id":"69d6b49d-423a-466e-89a2-9131c2a21cca"}} -{"dir":"recv","msg":{"duration_api_ms":6429,"stop_reason":"end_turn","session_id":"69d6b49d-423a-466e-89a2-9131c2a21cca","total_cost_usd":0.18178029999999998,"usage":{"input_tokens":2,"cache_creation_input_tokens":7016,"cache_read_input_tokens":30630,"output_tokens":8,"output_tokens_details":{"thinking_tokens":0},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":7016,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":8,"cache_read_input_tokens":30630,"cache_creation_input_tokens":7016,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":7016},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":1188,"outputTokens":21,"thinkingTokens":0,"cacheReadInputTokens":0,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.00012930000000000003,"contextWindow":1000000,"maxOutputTokens":128000},"claude-sonnet-5-5":{"inputTokens":4,"outputTokens":12,"thinkingTokens":0,"cacheReadInputTokens":30630,"cacheCreationInputTokens":44615,"webSearchRequests":0,"costUSD":0.18165099999999998,"contextWindow":1000000,"maxOutputTokens":128000,"canonicalModel":"claude-sonnet-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"AZURE-17","ttft_ms":1859,"type":"result","duration_ms":1875,"uuid":"5ca30bc5-1f4e-4dea-bd26-a07a9677741b","ttft_stream_ms":1858,"time_to_request_ms":39,"first_content_frame_ms":1858,"queued_turn_count":0,"result_index":0}} -{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"idle","uuid":"e93021bd-4e9d-41a8-9784-aba6fde32bf2","session_id":"69d6b49d-423a-466e-89a2-9131c2a21cca"}} +{"dir":"send","msg":{"request":{"hooks":null,"subtype":"initialize"},"request_id":"req_0df7ddde22d0d8a2","type":"control_request"}} +{"dir":"recv","msg":{"type":"control_response","response":{"subtype":"success","request_id":"req_0df7ddde22d0d8a2","response":{"commands":[{"name":"deep-research","description":"Deep research harness — fan-out web searches, fetch sources, adversarially verify claims, synthesize a cited report. (dynamic workflow)","argumentHint":"","builtin":true},{"name":"design","description":"Make a new Design artifact from a brief","argumentHint":"[what to design]","builtin":true},{"name":"slides","description":"Make a new Slides deck artifact from a brief","argumentHint":"[what the deck is about]","builtin":true},{"name":"design-sync","description":"Push a React design system to claude.ai/design. This runs a converter that bundles the real component code (from Storybook or a bare package) and uploads it. Use when the user runs /design-sync or says \"sync my design system to Claude Design\".","argumentHint":"[\u003cproject hint, e.g. \"Acme DS\"\u003e]","builtin":true},{"name":"dataviz","description":"Use this skill whenever you are about to create ANY chart, graph, plot, dashboard, or data visualization, in ANY output medium — an HTML or React artifact, inline SVG, plotting code in any library (matplotlib, plotly, d3, Recharts, …), an image/PNG you will render and upload, or a chart shared into Slack. Read it BEFORE writing the first line of chart code, choosing chart colors, building a stat tile / meter / KPI row, or laying out a dashboard. When the destination is a first-party document connector (host-designated, never self-described) that renders live charts, hand it the rows (inline, or as an uploaded data file the chart cites) rather than a rendered PNG/SVG — a picture of a chart loses hover, data inspection and per-value comments. Produces visualizations that read as one system — elegant, accessible, consistent in light and dark — using a brand-neutral placeholder palette you swap for your own. Teaches a design-system-agnostic method: a form heuristic, a color formula with a runnable validator, mark specs, and interaction rules. A validated default palette is documented in `references/palette.md` — swap that file's values for your brand's. Triggers on: \"chart\", \"graph\", \"plot\", \"data viz\", \"visualization\", \"dashboard\", \"analytics\", \"visualize data\", \"categorical colors\", \"sequential / diverging palette\", \"stat tile\", \"sparkline\", \"heatmap\", \"legend\", \"axis\", \"tooltip\", \"chart colors\", \"color by series\".","argumentHint":"","builtin":true},{"name":"artifact-diagramming","description":"Diagramming know-how for Artifacts - when a picture earns its place, how to draw one that shows the real mechanism, and the inline-SVG mechanics that keep it legible in both themes.","argumentHint":"","builtin":true},{"name":"artifact-capabilities","description":"Runtime capabilities a published Artifact page can be granted — behavior static HTML cannot provide on its own, such as the page reading live or connected data, remembering what people do on it (a poll, a sign-up sheet, a checklist, a document edited in place — it saves new versions of itself), keeping state shared across viewers, knowing who is viewing, asking Claude a question of its own, storing files people add, handing the viewer a file to save, or using the viewer's camera, microphone, location, screen or device motion. Serves this user's live capability roster and the typed call definitions. Load it whenever any such runtime behavior would make an artifact more useful, before writing the page.","argumentHint":"","builtin":true},{"name":"update-config","description":"Use this skill to configure the Claude Code harness via settings.json. Automated behaviors (\"from now on when X\", \"each time X\", \"whenever X\", \"before/after X\") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions (\"allow X\", \"add permission\", \"move permission to\"), env vars (\"set X=Y\"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: \"allow npm commands\", \"add bq permission to global settings\", \"move permission to user settings\", \"set DEBUG=true\", \"when claude stops show X\". For simple settings like theme/model, suggest the /config command.","argumentHint":"","builtin":true},{"name":"verify","description":"Verify that a code change actually does what it's supposed to by exercising it end-to-end and observing behavior — drive the affected flow, not just tests or typecheck. Run before committing nontrivial changes; bootstraps this repo's project verify skill if none exists yet. Don't invoke it on a diff that only touches tests, docs, or other code with no runtime surface to drive (a change to product source always has one) — there's nothing to observe.","argumentHint":"","builtin":true},{"name":"debug","description":"Enable debug logging for this session and help diagnose issues","argumentHint":"[issue description]","builtin":true},{"name":"code-review","description":"Review the current diff, or a PR number/branch/path target, for correctness bugs (plus reuse/simplification/efficiency cleanups where the model's review recipe covers them) at the given effort level (low|medium|high|xhigh|max: from few high-confidence findings up to many, some of them uncertain; ultra: deep multi-agent review in the cloud); with no level given, it reuses the level you typed last. Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review. Pass --max-findings \u003cn\u003e to report up to n findings, or --max-findings all for every finding. The choice stays until you pass --max-findings default. For ultra on a GitHub.com PR target, --post asks to post the finished review’s findings to the PR as a single comment from the user’s GitHub account (not a review; the launch dialog still confirms in interactive sessions, while non-interactive mode posts on the flag alone) and --no-post hides that option.","argumentHint":"[low|medium|high|xhigh|max|ultra] [--fix] [--comment] [\u003cpr#\u003e|\u003cbranch\u003e|\u003cpath\u003e] [--max-findings \u003cn\u003e|all]","aliases":["review"],"builtin":true},{"name":"simplify","description":"Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.","argumentHint":"[\u003ctarget\u003e]","builtin":true},{"name":"batch","description":"Research and plan a large-scale change, then execute it in parallel across 5–30 isolated worktree agents that each open a PR.","argumentHint":"\u003cinstruction\u003e","builtin":true},{"name":"fewer-permission-prompts","description":"Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.","argumentHint":"","builtin":true},{"name":"doctor","description":"Health-check the user's Claude Code setup and fix issues: diagnose installation health — what the `claude doctor` terminal diagnostics cover — from local data (duplicate or leftover installs, PATH, unparseable settings files, broken or colliding agent definitions, skills whose frontmatter fails to parse); find unused skills, MCP servers, and plugins versus their context cost and disable dead weight; deduplicate local CLAUDE.md files against checked-in ones; trim checked-in CLAUDE.md files by cutting content a session could derive from the codebase (directory layouts, tech-stack lists, architecture overviews) while keeping gotchas, rationale, and non-standard conventions; migrate always-loaded CLAUDE.md guidance into lazy skills and nested CLAUDE.md files; flag slow hooks and context-heavy extensions; check the installed version is current; make auto mode the default permission mode; and pre-approve frequently denied read-only commands. Use when the user asks for a doctor run, checkup, audit, tune-up, or cleanup of their Claude Code setup or configuration.","argumentHint":"[prompt-audit [\u003cpath\u003e]]","aliases":["checkup"],"builtin":true},{"name":"loop","description":"Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace.","argumentHint":"[interval] [prompt]","aliases":["proactive"],"builtin":true},{"name":"schedule","description":"Create, update, list, or run scheduled cloud agents (routines) that execute on a cron schedule.","argumentHint":"","aliases":["routines"],"builtin":true},{"name":"claude-api","description":"Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.\nTRIGGER — read BEFORE opening the target file; don't skip because it \"looks like a one-liner\" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).\nSKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).","argumentHint":"","builtin":true},{"name":"workflow-authoring","description":"Reference for writing a Workflow tool script (script API and gotchas, resume, quality patterns, worked examples). Load before authoring a script for a workflow the user already opted into; it does not itself authorize running one.","argumentHint":"","builtin":true},{"name":"run","description":"Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).","argumentHint":"","builtin":true},{"name":"run-skill-generator","description":"Author or improve the run-\u003cunit\u003e skill - a per-project skill that tells agents how to build, launch, and drive this project's app. Use when the user asks to set up the project, get it running, write run instructions, or verify build/run steps work from a clean environment.","argumentHint":"","builtin":true},{"name":"plugin-authoring","description":"Make a mod: a change to Claude's own interface or behaviour, such as a live pane or panel, a band above the prompt, a status line, a toast, a slash command, or a hook on tool calls or prompts. Use to make, change or debug a mod or its hooks module.","argumentHint":"","builtin":true},{"name":"advisor","description":"Let Claude consult a stronger model at key moments","argumentHint":"[fable|opus|sonnet|off]","builtin":true},{"name":"agents","description":"(removed) Ask Claude to create/manage subagents, or edit .claude/agents/","argumentHint":"","builtin":true},{"name":"auto-mode-setup","description":"Teach auto mode about your environment, plus optional rule tweaks","argumentHint":"[--request-id \u003cuuid\u003e] (--wizard posture=… scope=… depth=… --propose | --expect-sha256 \u003c64-hex\u003e --apply-file \u003cpath\u003e)","builtin":true},{"name":"autocompact","description":"Configure the auto-compact window size","argumentHint":"[auto|\u003ctokens\u003e]","builtin":true},{"name":"clear","description":"Start a new session with empty context; previous session stays on disk (resumable with /resume)","argumentHint":"[name]","aliases":["reset","new"],"builtin":true},{"name":"color","description":"Set the prompt bar color for this session","argumentHint":"[red|blue|green|yellow|purple|orange|pink|cyan|default]","builtin":true},{"name":"compact","description":"Free up context by summarizing the conversation so far","argumentHint":"\u003coptional custom summarization instructions\u003e","builtin":true},{"name":"config","description":"Set a setting by key","argumentHint":"key=value","aliases":["settings"],"builtin":true},{"name":"output-style","description":"List output styles or switch to one","argumentHint":"[style]","builtin":true},{"name":"context","description":"Show current context usage","argumentHint":"","builtin":true},{"name":"effort","description":"Set effort level for model usage","argumentHint":"\u003clow|medium|high|xhigh|max|auto|ultracode [on|off]\u003e","builtin":true},{"name":"fast","description":"Toggle fast mode (Opus 5.5)","argumentHint":"[on|off]","builtin":true},{"name":"focus","description":"Toggle focus view: just your prompt, summary, and response","argumentHint":"[on|off]","builtin":true},{"name":"heapdump","description":"Dump the JS heap to the Desktop; on Linux with no Desktop folder, the home directory","argumentHint":"","builtin":true},{"name":"init","description":"Initialize a new CLAUDE.md file with codebase documentation","argumentHint":"","builtin":true},{"name":"mcp","description":"Manage MCP servers","argumentHint":"[reconnect|enable|disable [\u003cserver\u003e|all]]","builtin":true},{"name":"import","description":"Import config from another AI coding agent","argumentHint":"","builtin":true},{"name":"model","description":"Set the AI model for Claude Code","argumentHint":"\u003cmodel\u003e","builtin":true},{"name":"__remote-workflow","description":"Run the workflow script delivered in this session environment (server-launched sessions only)","argumentHint":"","builtin":true},{"name":"workflow-launch-exec","description":"Execute a server-launched workflow handoff (workflow_launch event sessions only)","argumentHint":"","builtin":true},{"name":"reload-plugins","description":"Activate pending plugin changes in the current session","argumentHint":"[--force]","builtin":true},{"name":"reload-skills","description":"Pick up skills added or changed on disk during this session","argumentHint":"","builtin":true},{"name":"rename","description":"Rename the current conversation","argumentHint":"[name]","aliases":["name"],"builtin":true},{"name":"ultrareview","description":"Start a cloud agent that finds and verifies bugs in your branch (~15-25 min, $5-$25 USD) · Runs in a cloud session. See https://code.claude.com/docs/en/claude-code-on-the-web","argumentHint":"","builtin":true},{"name":"security-review","description":"Complete a security review of the pending changes on the current branch","argumentHint":"","builtin":true},{"name":"usage-credits","description":"Configure usage credits or request them from your admin when you hit a limit","argumentHint":"","builtin":true},{"name":"extra-usage","description":"Renamed to /usage-credits","argumentHint":"","builtin":true},{"name":"usage","description":"Show session cost, plan usage, and what's contributing to your limits","argumentHint":"","aliases":["cost","stats"],"builtin":true},{"name":"insights","description":"Generate a report analyzing your Claude Code sessions","argumentHint":"","builtin":true},{"name":"recap","description":"Generate a one-line session recap now","argumentHint":"","builtin":true},{"name":"skill-doctor","description":"Show which loaded skills are unused and costing context","argumentHint":"","builtin":true},{"name":"goal","description":"Set a goal — keep working until the condition is met","argumentHint":"","builtin":true},{"name":"design-consent","description":"Grant Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"design-revoke","description":"Revoke Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"list-agents","description":"List subagents, teammates, and other Claude sessions you can message","argumentHint":"","aliases":["peers"],"builtin":true},{"name":"team-onboarding","description":"Help teammates ramp on Claude Code with a guide from your usage","argumentHint":"","builtin":true},{"name":"docs","description":"docs (editable docs people share and comment on; the default for any document, named as a doc or not: a document, report, proposal, resume, cover letter, letter, contract, policy, form, template, worksheet, essay, handbook, guide, how-to, cheat sheet, SOP or other writing to keep, share, collaborate on, send, submit, print or sign; a doc exports to Word, PDF, Markdown or Google Docs, so needing a file to send, attach, upload, submit or print is no reason to pick Word, and a file nobody asked for is a doc, not Word; a plan, comparison, summary or notes asked in chat stays in chat; a pasted claude.ai artifact link may be a doc: check with docs tools first; Word or another file format named, tracked changes wanted, or a .docx to change or use as a template → that format's skill): making one → if no docs-connector instructions are in context, call its `guide` (topic.instructions) first; then create the doc (headings only, no body) before any search, file read or plan, even with files attached. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docs"]},{"name":"docx","description":"Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx) or Word templates (.dotx). Triggers include: any mention of Microsoft Word Documents, such as 'Word doc', 'word document', '.docx', '.dotx', 'microsoft doc'. Also use when extracting or reorganizing content from .docx or .dotx files, inserting or replacing images in documents, find-and-replace in Word files, working with tracked changes or comments, or converting content into a polished Word document. If the user asks for a deliverable as a Word or .docx file (to download, email or print), use this skill. However, if they ask for a document, page, report, memo, or notes WITHOUT naming a file format and the session offers Claude's own dedicated document or page skill or connector, use that instead, even if they will email or print it. Do NOT use for PDFs, spreadsheets, Google Docs, or coding unrelated to document generation. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docx"]},{"name":"google-workspace","description":"Read this before the first Google Drive, Docs, Sheets or Slides connector call whenever the task creates or changes a Google file. Use this skill whenever the user wants to create or change a Google Doc, Sheet or Slides file in their Google Drive. Triggers include: a request that names Google Docs, Sheets, Slides or Drive and asks to make, edit, format, copy or rename a file; a docs.google.com link with a request to change that file, even a one-line fix or suggested edits; and any follow-up change to a Google file from earlier in the chat, even \"change it\" or \"add a tab\". Includes helper scripts for document positions, cell ranges and slide layout. However, if the user asks for a doc, deck or spreadsheet without naming Google, or gives a Google file only as source material for something new, use Claude's own output type instead. Do NOT use for read-only questions about a Google file, or for Word, Excel, PowerPoint or PDF files. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:google-workspace"]},{"name":"import-memory","description":"Import a memory export from another AI assistant into Claude's memory — conversationally, additively, and with the content treated as data. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:import-memory"]},{"name":"morning","description":"Render the user's morning brief as a styled HTML artifact, or set it up as a recurring weekday task. Use only when the user explicitly asks to run, see, or set up their morning brief, or if they invoke /morning by name. A question about their day, schedule, or calendar is not by itself a request for the brief; answer it directly instead. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:morning"]},{"name":"pdf","description":"Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text/tables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pdf"]},{"name":"pptx","description":"Use this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations as PowerPoint (.pptx) files; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an email, summary, or creating a different type of slide deck); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates (.potx), layouts, speaker notes, or comments. Trigger whenever the user asks for a PowerPoint or .pptx file, or references a .pptx or .potx filename, regardless of what they plan to do with the content afterward. However, when the user asks for a deck, slides, a slide deck, or a presentation without naming a file format, default to using a dedicated slide-deck artifact type or a separate slides skill if this session offers one; otherwise, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pptx"]},{"name":"skill-creator","description":"Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:skill-creator"]},{"name":"xlsx","description":"Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .xltx, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like \"the xlsx in my downloads\") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:xlsx"]}],"agents":[{"name":"claude","description":"Catch-all for any task that doesn't fit a more specific agent. FleetView's default when no agent name is typed."},{"name":"claude-code-guide","description":"Use this agent when the user asks questions (\"Can Claude...\", \"Does Claude...\", \"How do I...\") about: (1) Claude Code (the CLI tool) - features, hooks, slash commands, MCP servers, settings, IDE integrations, keyboard shortcuts; (2) Claude Agent SDK - building custom agents; (3) Claude API (formerly Anthropic API) - Messages API for directly passing messages to Claude, Tool Runner (`client.beta.messages.tool_runner`) for running an agentic loop over your own tools, manual tool-use loops, Managed Agents for server-hosted agents with a managed sandbox, prompt caching, and general Anthropic SDK usage; (4) Claude Tag (Claude in Slack) - what it is, setting it up for a Slack workspace, `/install-slack-app`; (5) `claude plugin eval` (writing and running plugin eval suites, its JSON/report, sandbox, CI) and the `/skill-doctor` report. **IMPORTANT:** Before spawning a new agent, check if there is already a running or recently completed claude-code-guide agent that you can continue via SendMessage.","model":"haiku"},{"name":"Explore","description":"Fast read-only search agent for locating code. Use it to find files by pattern (eg. \"src/components/**/*.tsx\"), grep for symbols or keywords (eg. \"API endpoints\"), or answer \"where is X defined / which files reference Y.\" Do NOT use it for code review, design-doc auditing, cross-file consistency checks, or open-ended analysis — it reads excerpts rather than whole files and will miss content past its read window. When calling, specify search breadth: \"quick\" for a single targeted lookup, \"medium\" for moderate exploration, or \"very thorough\" to search across multiple locations and naming conventions.","model":"inherit"},{"name":"general-purpose","description":"General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you."},{"name":"Plan","description":"Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.","model":"inherit"},{"name":"statusline-setup","description":"Use this agent to configure the user's Claude Code status line setting.","model":"sonnet"}],"output_style":"default","available_output_styles":["default","Proactive","Concise","Explanatory","Learning"],"user_output_styles_dir":"/Users/flamingcow/.claude/output-styles","models":[{"value":"default","resolvedModel":"claude-opus-5-5","displayName":"Default (recommended)","description":"Opus 5.5 · Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"opus","resolvedModel":"claude-opus-5-5","displayName":"Opus 5.5","description":"For complex work and everyday tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"fable","resolvedModel":"claude-fable-5-1","displayName":"Fable 5.1","description":"For your toughest challenges","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"sonnet","resolvedModel":"claude-sonnet-5-5","displayName":"Sonnet 5.5","description":"Most efficient for simpler tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"haiku","resolvedModel":"claude-haiku-5-5","displayName":"Haiku 5.5","description":"Fastest for quick answers","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-haiku-4-5-20251001","resolvedModel":"claude-haiku-4-5-20251001","displayName":"Haiku 4.5","description":"Fastest for quick answers"},{"value":"claude-sonnet-5","resolvedModel":"claude-sonnet-5","displayName":"Sonnet 5","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-5","resolvedModel":"claude-opus-5","displayName":"Opus 5","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-fable-5","resolvedModel":"claude-fable-5","displayName":"Fable 5","description":"Most capable for your hardest and longest-running tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-8","resolvedModel":"claude-opus-4-8","displayName":"Opus 4.8","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-opus-4-7","resolvedModel":"claude-opus-4-7","displayName":"Opus 4.7","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-6","resolvedModel":"claude-opus-4-6","displayName":"Opus 4.6","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-sonnet-4-6","resolvedModel":"claude-sonnet-4-6","displayName":"Sonnet 4.6","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true}],"account":{"email":"[redacted]","organization":"[redacted]'s Organization","subscriptionType":"Claude Max","apiProvider":"firstParty"},"pid":32361,"claude_code_version":"2.1.296","current_permission_mode":"bypassPermissions","feedback_mode":{"kind":"post"},"analytics_disabled":false,"remote_control_auto_enable":false,"remote_control_auto_connect_default":false,"remote_control_available":true,"remote_control_auto_on_by_default":false,"ide_rc_auto_enable_gate":true,"fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","session_state":"idle","capabilities":["ui_surface_v1"]},"pending_permission_requests":[],"pending_user_dialog_requests":[]}}} +{"dir":"send","msg":{"message":{"content":"What was the codeword? Reply with just the codeword.","role":"user"},"parent_tool_use_id":null,"session_id":"5ad73b02-ffc2-42d6-af14-f192fd1b1958","type":"user"}} +{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"running","uuid":"556d8b80-ede8-4923-8aa1-370eda654b5f","session_id":"5ad73b02-ffc2-42d6-af14-f192fd1b1958"}} +{"dir":"recv","msg":{"type":"system","subtype":"init","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestResume2224935177/001","session_id":"5ad73b02-ffc2-42d6-af14-f192fd1b1958","tools":["Task","Artifact","ArtifactComments","ArtifactData","Bash","CronCreate","CronDelete","CronList","DesignSync","Edit","EnterWorktree","ExitWorktree","ListAgents","LSP","Monitor","NotebookEdit","PushNotification","Read","RemoteTrigger","ReportFindings","ScheduleWakeup","SendMessage","Skill","TaskStop","ToolSearch","WebFetch","WebSearch","Workflow","Write"],"mcp_servers":[{"name":"claude.ai Claude Docs","status":"pending","source":"claudeai"},{"name":"claude.ai Heliosian","status":"pending","source":"claudeai"},{"name":"claude.ai Fastmail","status":"pending","source":"claudeai"},{"name":"claude.ai Google Calendar","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Gmail","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Google Drive","status":"needs-auth","source":"claudeai"}],"model":"claude-sonnet-5-5","permissionMode":"bypassPermissions","slash_commands":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","advisor","agents","auto-mode-setup","autocompact","clear","color","compact","config","output-style","context","effort","fast","focus","heapdump","init","mcp","import","model","__remote-workflow","workflow-launch-exec","reload-plugins","reload-skills","rename","ultrareview","security-review","usage-credits","extra-usage","usage","insights","recap","skill-doctor","goal","design-consent","design-revoke","list-agents","team-onboarding","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"terminal_slash_commands":["doctor","color","focus","reload-plugins"],"apiKeySource":"none","claude_code_version":"2.1.296","output_style":"default","agents":["claude","claude-code-guide","Explore","general-purpose","Plan","statusline-setup"],"skills":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"plugins":[{"name":"clangd-lsp","path":"/Users/flamingcow/.claude/plugins/cache/claude-plugins-official/clangd-lsp/1.0.0","source":"clangd-lsp@claude-plugins-official","version":"1.0.0"},{"name":"cc-plugin-agents-md","path":"builtin","source":"cc-plugin-agents-md@builtin"},{"name":"cc-plugin-telemetry","path":"builtin","source":"cc-plugin-telemetry@builtin"},{"name":"cc-plugin-plugin-authoring","path":"builtin","source":"cc-plugin-plugin-authoring@builtin"}],"capabilities":["interrupt_receipt_v1","interrupt_cancel_queued_v1","interrupt_send_now_v1","msg_lifecycle_v1","request_marker_lists_v1","sdk_mcp_tools_list_changed","sdk_mcp_manifests","mcp_read_resource_v1","mcp_tool_ui_meta_v1","ui_surface_v1"],"analytics_disabled":false,"product_feedback_disabled":false,"uuid":"194cade6-2350-4332-89f8-071c0dcb4557","memory_paths":{"auto":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestResume2224935177-001/memory/"},"scratchpad_path":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestResume2224935177-001/5ad73b02-ffc2-42d6-af14-f192fd1b1958/scratchpad","messaging_socket_path":"/tmp/cc-socks/32361.sock","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","per_turn_effort_active":true,"view_mode":"default"}} +{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2GxQZxU499yYy1r7g98","type":"message","role":"assistant","content":[{"type":"text","text":"AZURE-17"}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":7018,"cache_read_input_tokens":30628,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":7018},"output_tokens":8,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":{"cache_miss_reason":{"type":"messages_changed","cache_missed_input_tokens":13150}},"context_management":null},"parent_tool_use_id":null,"session_id":"5ad73b02-ffc2-42d6-af14-f192fd1b1958","uuid":"5830b8b4-71bb-4b56-bcaa-da02ce3ecbfd","timestamp":"2026-10-14T14:19:43.919Z","request_id":"req_011Cg2GxQK5hcvHdPfZq9KH3"}} +{"dir":"recv","msg":{"type":"rate_limit_event","rate_limit_info":{"status":"allowed","resetsAt":1791990000,"rateLimitType":"five_hour","overageStatus":"rejected","overageDisabledReason":"org_level_disabled","isUsingOverage":false,"unifiedWindows":{"five_hour":{"utilization":0.1,"resetsAt":1791990000},"seven_day":{"utilization":0.27,"resetsAt":1792270800}}},"uuid":"fe9ae427-4ea7-4e26-88ab-e641fd93f706","session_id":"5ad73b02-ffc2-42d6-af14-f192fd1b1958"}} +{"dir":"recv","msg":{"duration_api_ms":4248,"stop_reason":"end_turn","session_id":"5ad73b02-ffc2-42d6-af14-f192fd1b1958","total_cost_usd":0.0945568,"usage":{"input_tokens":2,"cache_creation_input_tokens":7018,"cache_read_input_tokens":30628,"output_tokens":8,"output_tokens_details":{"thinking_tokens":0},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":7018,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":8,"cache_read_input_tokens":30628,"cache_creation_input_tokens":7018,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":7018},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":1188,"outputTokens":21,"thinkingTokens":0,"cacheReadInputTokens":0,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.00012930000000000003,"contextWindow":1000000,"maxOutputTokens":128000},"claude-sonnet-5-5":{"inputTokens":4,"outputTokens":12,"thinkingTokens":0,"cacheReadInputTokens":52995,"cacheCreationInputTokens":22250,"webSearchRequests":0,"costUSD":0.0944275,"contextWindow":1000000,"maxOutputTokens":128000,"canonicalModel":"claude-sonnet-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"AZURE-17","ttft_ms":1464,"type":"result","duration_ms":1476,"uuid":"67b749f1-cc30-4763-a2a3-24242f84f697","ttft_stream_ms":1464,"time_to_request_ms":31,"first_content_frame_ms":1464,"queued_turn_count":0,"result_index":0}} +{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"idle","uuid":"6ac2a988-bf73-4051-ab03-04cd8a849fd4","session_id":"5ad73b02-ffc2-42d6-af14-f192fd1b1958"}} diff --git a/internal/claude/testdata/transcripts/TestStopHookBlocks.jsonl b/internal/claude/testdata/transcripts/TestStopHookBlocks.jsonl index 45878ee..2584ff1 100644 --- a/internal/claude/testdata/transcripts/TestStopHookBlocks.jsonl +++ b/internal/claude/testdata/transcripts/TestStopHookBlocks.jsonl @@ -1,16 +1,16 @@ -{"dir":"send","msg":{"request":{"hooks":{"Stop":[{"hookCallbackIds":["hook_0"],"matcher":""}]},"subtype":"initialize"},"request_id":"req_51e01c812590ad44","type":"control_request"}} -{"dir":"recv","msg":{"type":"control_response","response":{"subtype":"success","request_id":"req_51e01c812590ad44","response":{"commands":[{"name":"deep-research","description":"Deep research harness — fan-out web searches, fetch sources, adversarially verify claims, synthesize a cited report. (dynamic workflow)","argumentHint":"","builtin":true},{"name":"design","description":"Make a new Design artifact from a brief","argumentHint":"[what to design]","builtin":true},{"name":"slides","description":"Make a new Slides deck artifact from a brief","argumentHint":"[what the deck is about]","builtin":true},{"name":"design-sync","description":"Push a React design system to claude.ai/design. This runs a converter that bundles the real component code (from Storybook or a bare package) and uploads it. Use when the user runs /design-sync or says \"sync my design system to Claude Design\".","argumentHint":"[\u003cproject hint, e.g. \"Acme DS\"\u003e]","builtin":true},{"name":"dataviz","description":"Use this skill whenever you are about to create ANY chart, graph, plot, dashboard, or data visualization, in ANY output medium — an HTML or React artifact, inline SVG, plotting code in any library (matplotlib, plotly, d3, Recharts, …), an image/PNG you will render and upload, or a chart shared into Slack. Read it BEFORE writing the first line of chart code, choosing chart colors, building a stat tile / meter / KPI row, or laying out a dashboard. When the destination is a first-party document connector (host-designated, never self-described) that renders live charts, hand it the rows (inline, or as an uploaded data file the chart cites) rather than a rendered PNG/SVG — a picture of a chart loses hover, data inspection and per-value comments. Produces visualizations that read as one system — elegant, accessible, consistent in light and dark — using a brand-neutral placeholder palette you swap for your own. Teaches a design-system-agnostic method: a form heuristic, a color formula with a runnable validator, mark specs, and interaction rules. A validated default palette is documented in `references/palette.md` — swap that file's values for your brand's. Triggers on: \"chart\", \"graph\", \"plot\", \"data viz\", \"visualization\", \"dashboard\", \"analytics\", \"visualize data\", \"categorical colors\", \"sequential / diverging palette\", \"stat tile\", \"sparkline\", \"heatmap\", \"legend\", \"axis\", \"tooltip\", \"chart colors\", \"color by series\".","argumentHint":"","builtin":true},{"name":"artifact-diagramming","description":"Diagramming know-how for Artifacts - when a picture earns its place, how to draw one that shows the real mechanism, and the inline-SVG mechanics that keep it legible in both themes.","argumentHint":"","builtin":true},{"name":"artifact-capabilities","description":"Runtime capabilities a published Artifact page can be granted — behavior static HTML cannot provide on its own, such as the page reading live or connected data, remembering what people do on it (a poll, a sign-up sheet, a checklist, a document edited in place — it saves new versions of itself), keeping state shared across viewers, knowing who is viewing, asking Claude a question of its own, storing files people add, handing the viewer a file to save, or using the viewer's camera, microphone, location, screen or device motion. Serves this user's live capability roster and the typed call definitions. Load it whenever any such runtime behavior would make an artifact more useful, before writing the page.","argumentHint":"","builtin":true},{"name":"update-config","description":"Use this skill to configure the Claude Code harness via settings.json. Automated behaviors (\"from now on when X\", \"each time X\", \"whenever X\", \"before/after X\") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions (\"allow X\", \"add permission\", \"move permission to\"), env vars (\"set X=Y\"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: \"allow npm commands\", \"add bq permission to global settings\", \"move permission to user settings\", \"set DEBUG=true\", \"when claude stops show X\". For simple settings like theme/model, suggest the /config command.","argumentHint":"","builtin":true},{"name":"verify","description":"Verify that a code change actually does what it's supposed to by exercising it end-to-end and observing behavior — drive the affected flow, not just tests or typecheck. Run before committing nontrivial changes; bootstraps this repo's project verify skill if none exists yet. Don't invoke it on a diff that only touches tests, docs, or other code with no runtime surface to drive (a change to product source always has one) — there's nothing to observe.","argumentHint":"","builtin":true},{"name":"debug","description":"Enable debug logging for this session and help diagnose issues","argumentHint":"[issue description]","builtin":true},{"name":"code-review","description":"Review the current diff, or a PR number/branch/path target, for correctness bugs (plus reuse/simplification/efficiency cleanups where the model's review recipe covers them) at the given effort level (low|medium|high|xhigh|max: from few high-confidence findings up to many, some of them uncertain; ultra: deep multi-agent review in the cloud); with no level given, it reuses the level you typed last. Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review. Pass --max-findings \u003cn\u003e to report up to n findings, or --max-findings all for every finding. The choice stays until you pass --max-findings default. For ultra on a GitHub.com PR target, --post asks to post the finished review’s findings to the PR as a single comment from the user’s GitHub account (not a review; the launch dialog still confirms in interactive sessions, while non-interactive mode posts on the flag alone) and --no-post hides that option.","argumentHint":"[low|medium|high|xhigh|max|ultra] [--fix] [--comment] [\u003cpr#\u003e|\u003cbranch\u003e|\u003cpath\u003e] [--max-findings \u003cn\u003e|all]","aliases":["review"],"builtin":true},{"name":"simplify","description":"Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.","argumentHint":"[\u003ctarget\u003e]","builtin":true},{"name":"batch","description":"Research and plan a large-scale change, then execute it in parallel across 5–30 isolated worktree agents that each open a PR.","argumentHint":"\u003cinstruction\u003e","builtin":true},{"name":"fewer-permission-prompts","description":"Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.","argumentHint":"","builtin":true},{"name":"doctor","description":"Health-check the user's Claude Code setup and fix issues: diagnose installation health — what the `claude doctor` terminal diagnostics cover — from local data (duplicate or leftover installs, PATH, unparseable settings files, broken or colliding agent definitions, skills whose frontmatter fails to parse); find unused skills, MCP servers, and plugins versus their context cost and disable dead weight; deduplicate local CLAUDE.md files against checked-in ones; trim checked-in CLAUDE.md files by cutting content a session could derive from the codebase (directory layouts, tech-stack lists, architecture overviews) while keeping gotchas, rationale, and non-standard conventions; migrate always-loaded CLAUDE.md guidance into lazy skills and nested CLAUDE.md files; flag slow hooks and context-heavy extensions; check the installed version is current; make auto mode the default permission mode; and pre-approve frequently denied read-only commands. Use when the user asks for a doctor run, checkup, audit, tune-up, or cleanup of their Claude Code setup or configuration.","argumentHint":"[prompt-audit [\u003cpath\u003e]]","aliases":["checkup"],"builtin":true},{"name":"loop","description":"Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace.","argumentHint":"[interval] [prompt]","aliases":["proactive"],"builtin":true},{"name":"schedule","description":"Create, update, list, or run scheduled cloud agents (routines) that execute on a cron schedule.","argumentHint":"","aliases":["routines"],"builtin":true},{"name":"claude-api","description":"Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.\nTRIGGER — read BEFORE opening the target file; don't skip because it \"looks like a one-liner\" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).\nSKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).","argumentHint":"","builtin":true},{"name":"workflow-authoring","description":"Reference for writing a Workflow tool script (script API and gotchas, resume, quality patterns, worked examples). Load before authoring a script for a workflow the user already opted into; it does not itself authorize running one.","argumentHint":"","builtin":true},{"name":"run","description":"Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).","argumentHint":"","builtin":true},{"name":"run-skill-generator","description":"Author or improve the run-\u003cunit\u003e skill - a per-project skill that tells agents how to build, launch, and drive this project's app. Use when the user asks to set up the project, get it running, write run instructions, or verify build/run steps work from a clean environment.","argumentHint":"","builtin":true},{"name":"plugin-authoring","description":"Make a mod: a change to Claude's own interface or behaviour, such as a live pane or panel, a band above the prompt, a status line, a toast, a slash command, or a hook on tool calls or prompts. Use to make, change or debug a mod or its hooks module.","argumentHint":"","builtin":true},{"name":"advisor","description":"Let Claude consult a stronger model at key moments","argumentHint":"[fable|opus|sonnet|off]","builtin":true},{"name":"agents","description":"(removed) Ask Claude to create/manage subagents, or edit .claude/agents/","argumentHint":"","builtin":true},{"name":"auto-mode-setup","description":"Teach auto mode about your environment, plus optional rule tweaks","argumentHint":"[--request-id \u003cuuid\u003e] (--wizard posture=… scope=… depth=… --propose | --expect-sha256 \u003c64-hex\u003e --apply-file \u003cpath\u003e)","builtin":true},{"name":"autocompact","description":"Configure the auto-compact window size","argumentHint":"[auto|\u003ctokens\u003e]","builtin":true},{"name":"clear","description":"Start a new session with empty context; previous session stays on disk (resumable with /resume)","argumentHint":"[name]","aliases":["reset","new"],"builtin":true},{"name":"color","description":"Set the prompt bar color for this session","argumentHint":"[red|blue|green|yellow|purple|orange|pink|cyan|default]","builtin":true},{"name":"compact","description":"Free up context by summarizing the conversation so far","argumentHint":"\u003coptional custom summarization instructions\u003e","builtin":true},{"name":"config","description":"Set a setting by key","argumentHint":"key=value","aliases":["settings"],"builtin":true},{"name":"output-style","description":"List output styles or switch to one","argumentHint":"[style]","builtin":true},{"name":"context","description":"Show current context usage","argumentHint":"","builtin":true},{"name":"effort","description":"Set effort level for model usage","argumentHint":"\u003clow|medium|high|xhigh|max|auto|ultracode [on|off]\u003e","builtin":true},{"name":"fast","description":"Toggle fast mode (Opus 5.5)","argumentHint":"[on|off]","builtin":true},{"name":"focus","description":"Toggle focus view: just your prompt, summary, and response","argumentHint":"[on|off]","builtin":true},{"name":"heapdump","description":"Dump the JS heap to the Desktop; on Linux with no Desktop folder, the home directory","argumentHint":"","builtin":true},{"name":"init","description":"Initialize a new CLAUDE.md file with codebase documentation","argumentHint":"","builtin":true},{"name":"mcp","description":"Manage MCP servers","argumentHint":"[reconnect|enable|disable [\u003cserver\u003e|all]]","builtin":true},{"name":"import","description":"Import config from another AI coding agent","argumentHint":"","builtin":true},{"name":"model","description":"Set the AI model for Claude Code","argumentHint":"\u003cmodel\u003e","builtin":true},{"name":"__remote-workflow","description":"Run the workflow script delivered in this session environment (server-launched sessions only)","argumentHint":"","builtin":true},{"name":"workflow-launch-exec","description":"Execute a server-launched workflow handoff (workflow_launch event sessions only)","argumentHint":"","builtin":true},{"name":"reload-plugins","description":"Activate pending plugin changes in the current session","argumentHint":"[--force]","builtin":true},{"name":"reload-skills","description":"Pick up skills added or changed on disk during this session","argumentHint":"","builtin":true},{"name":"rename","description":"Rename the current conversation","argumentHint":"[name]","aliases":["name"],"builtin":true},{"name":"ultrareview","description":"Start a cloud agent that finds and verifies bugs in your branch (~15-25 min, $5-$25 USD) · Runs in a cloud session. See https://code.claude.com/docs/en/claude-code-on-the-web","argumentHint":"","builtin":true},{"name":"security-review","description":"Complete a security review of the pending changes on the current branch","argumentHint":"","builtin":true},{"name":"usage-credits","description":"Configure usage credits or request them from your admin when you hit a limit","argumentHint":"","builtin":true},{"name":"extra-usage","description":"Renamed to /usage-credits","argumentHint":"","builtin":true},{"name":"usage","description":"Show session cost, plan usage, and what's contributing to your limits","argumentHint":"","aliases":["cost","stats"],"builtin":true},{"name":"insights","description":"Generate a report analyzing your Claude Code sessions","argumentHint":"","builtin":true},{"name":"recap","description":"Generate a one-line session recap now","argumentHint":"","builtin":true},{"name":"skill-doctor","description":"Show which loaded skills are unused and costing context","argumentHint":"","builtin":true},{"name":"goal","description":"Set a goal — keep working until the condition is met","argumentHint":"","builtin":true},{"name":"design-consent","description":"Grant Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"design-revoke","description":"Revoke Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"list-agents","description":"List subagents, teammates, and other Claude sessions you can message","argumentHint":"","aliases":["peers"],"builtin":true},{"name":"team-onboarding","description":"Help teammates ramp on Claude Code with a guide from your usage","argumentHint":"","builtin":true},{"name":"docs","description":"docs (editable docs people share and comment on; the default for any document, named as a doc or not: a document, report, proposal, resume, cover letter, letter, contract, policy, form, template, worksheet, essay, handbook, guide, how-to, cheat sheet, SOP or other writing to keep, share, collaborate on, send, submit, print or sign; a doc exports to Word, PDF, Markdown or Google Docs, so needing a file to send, attach, upload, submit or print is no reason to pick Word, and a file nobody asked for is a doc, not Word; a plan, comparison, summary or notes asked in chat stays in chat; a pasted claude.ai artifact link may be a doc: check with docs tools first; Word or another file format named, tracked changes wanted, or a .docx to change or use as a template → that format's skill): making one → if no docs-connector instructions are in context, call its `guide` (topic.instructions) first; then create the doc (headings only, no body) before any search, file read or plan, even with files attached. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docs"]},{"name":"docx","description":"Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx) or Word templates (.dotx). Triggers include: any mention of Microsoft Word Documents, such as 'Word doc', 'word document', '.docx', '.dotx', 'microsoft doc'. Also use when extracting or reorganizing content from .docx or .dotx files, inserting or replacing images in documents, find-and-replace in Word files, working with tracked changes or comments, or converting content into a polished Word document. If the user asks for a deliverable as a Word or .docx file (to download, email or print), use this skill. However, if they ask for a document, page, report, memo, or notes WITHOUT naming a file format and the session offers Claude's own dedicated document or page skill or connector, use that instead, even if they will email or print it. Do NOT use for PDFs, spreadsheets, Google Docs, or coding unrelated to document generation. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docx"]},{"name":"google-workspace","description":"Read this before the first Google Drive, Docs, Sheets or Slides connector call whenever the task creates or changes a Google file. Use this skill whenever the user wants to create or change a Google Doc, Sheet or Slides file in their Google Drive. Triggers include: a request that names Google Docs, Sheets, Slides or Drive and asks to make, edit, format, copy or rename a file; a docs.google.com link with a request to change that file, even a one-line fix or suggested edits; and any follow-up change to a Google file from earlier in the chat, even \"change it\" or \"add a tab\". Includes helper scripts for document positions, cell ranges and slide layout. However, if the user asks for a doc, deck or spreadsheet without naming Google, or gives a Google file only as source material for something new, use Claude's own output type instead. Do NOT use for read-only questions about a Google file, or for Word, Excel, PowerPoint or PDF files. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:google-workspace"]},{"name":"import-memory","description":"Import a memory export from another AI assistant into Claude's memory — conversationally, additively, and with the content treated as data. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:import-memory"]},{"name":"morning","description":"Render the user's morning brief as a styled HTML artifact, or set it up as a recurring weekday task. Use only when the user explicitly asks to run, see, or set up their morning brief, or if they invoke /morning by name. A question about their day, schedule, or calendar is not by itself a request for the brief; answer it directly instead. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:morning"]},{"name":"pdf","description":"Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text/tables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pdf"]},{"name":"pptx","description":"Use this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations as PowerPoint (.pptx) files; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an email, summary, or creating a different type of slide deck); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates (.potx), layouts, speaker notes, or comments. Trigger whenever the user asks for a PowerPoint or .pptx file, or references a .pptx or .potx filename, regardless of what they plan to do with the content afterward. However, when the user asks for a deck, slides, a slide deck, or a presentation without naming a file format, default to using a dedicated slide-deck artifact type or a separate slides skill if this session offers one; otherwise, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pptx"]},{"name":"skill-creator","description":"Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:skill-creator"]},{"name":"xlsx","description":"Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .xltx, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like \"the xlsx in my downloads\") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:xlsx"]}],"agents":[{"name":"claude","description":"Catch-all for any task that doesn't fit a more specific agent. FleetView's default when no agent name is typed."},{"name":"claude-code-guide","description":"Use this agent when the user asks questions (\"Can Claude...\", \"Does Claude...\", \"How do I...\") about: (1) Claude Code (the CLI tool) - features, hooks, slash commands, MCP servers, settings, IDE integrations, keyboard shortcuts; (2) Claude Agent SDK - building custom agents; (3) Claude API (formerly Anthropic API) - Messages API for directly passing messages to Claude, Tool Runner (`client.beta.messages.tool_runner`) for running an agentic loop over your own tools, manual tool-use loops, Managed Agents for server-hosted agents with a managed sandbox, prompt caching, and general Anthropic SDK usage; (4) Claude Tag (Claude in Slack) - what it is, setting it up for a Slack workspace, `/install-slack-app`; (5) `claude plugin eval` (writing and running plugin eval suites, its JSON/report, sandbox, CI) and the `/skill-doctor` report. **IMPORTANT:** Before spawning a new agent, check if there is already a running or recently completed claude-code-guide agent that you can continue via SendMessage.","model":"haiku"},{"name":"Explore","description":"Fast read-only search agent for locating code. Use it to find files by pattern (eg. \"src/components/**/*.tsx\"), grep for symbols or keywords (eg. \"API endpoints\"), or answer \"where is X defined / which files reference Y.\" Do NOT use it for code review, design-doc auditing, cross-file consistency checks, or open-ended analysis — it reads excerpts rather than whole files and will miss content past its read window. When calling, specify search breadth: \"quick\" for a single targeted lookup, \"medium\" for moderate exploration, or \"very thorough\" to search across multiple locations and naming conventions.","model":"inherit"},{"name":"general-purpose","description":"General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you."},{"name":"Plan","description":"Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.","model":"inherit"},{"name":"statusline-setup","description":"Use this agent to configure the user's Claude Code status line setting.","model":"sonnet"}],"output_style":"default","available_output_styles":["default","Proactive","Concise","Explanatory","Learning"],"user_output_styles_dir":"/Users/flamingcow/.claude/output-styles","models":[{"value":"default","resolvedModel":"claude-opus-5-5","displayName":"Default (recommended)","description":"Opus 5.5 · Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"opus","resolvedModel":"claude-opus-5-5","displayName":"Opus 5.5","description":"For complex work and everyday tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"fable","resolvedModel":"claude-fable-5-1","displayName":"Fable 5.1","description":"For your toughest challenges","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"sonnet","resolvedModel":"claude-sonnet-5-5","displayName":"Sonnet 5.5","description":"Most efficient for simpler tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"haiku","resolvedModel":"claude-haiku-5-5","displayName":"Haiku 5.5","description":"Fastest for quick answers","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-haiku-4-5-20251001","resolvedModel":"claude-haiku-4-5-20251001","displayName":"Haiku 4.5","description":"Fastest for quick answers"},{"value":"claude-sonnet-5","resolvedModel":"claude-sonnet-5","displayName":"Sonnet 5","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-5","resolvedModel":"claude-opus-5","displayName":"Opus 5","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-fable-5","resolvedModel":"claude-fable-5","displayName":"Fable 5","description":"Most capable for your hardest and longest-running tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-8","resolvedModel":"claude-opus-4-8","displayName":"Opus 4.8","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-opus-4-7","resolvedModel":"claude-opus-4-7","displayName":"Opus 4.7","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-6","resolvedModel":"claude-opus-4-6","displayName":"Opus 4.6","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-sonnet-4-6","resolvedModel":"claude-sonnet-4-6","displayName":"Sonnet 4.6","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true}],"account":{"email":"[redacted]","organization":"[redacted]'s Organization","subscriptionType":"Claude Max","apiProvider":"firstParty"},"pid":27101,"claude_code_version":"2.1.296","current_permission_mode":"bypassPermissions","hooks_applied":true,"feedback_mode":{"kind":"post"},"analytics_disabled":false,"remote_control_auto_enable":false,"remote_control_auto_connect_default":false,"remote_control_available":true,"remote_control_auto_on_by_default":false,"ide_rc_auto_enable_gate":true,"fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","session_state":"idle","capabilities":["ui_surface_v1"]},"pending_permission_requests":[],"pending_user_dialog_requests":[]}}} +{"dir":"send","msg":{"request":{"hooks":{"Stop":[{"hookCallbackIds":["hook_0"],"matcher":""}]},"subtype":"initialize"},"request_id":"req_feb244893da35d3d","type":"control_request"}} +{"dir":"recv","msg":{"type":"control_response","response":{"subtype":"success","request_id":"req_feb244893da35d3d","response":{"commands":[{"name":"deep-research","description":"Deep research harness — fan-out web searches, fetch sources, adversarially verify claims, synthesize a cited report. (dynamic workflow)","argumentHint":"","builtin":true},{"name":"design","description":"Make a new Design artifact from a brief","argumentHint":"[what to design]","builtin":true},{"name":"slides","description":"Make a new Slides deck artifact from a brief","argumentHint":"[what the deck is about]","builtin":true},{"name":"design-sync","description":"Push a React design system to claude.ai/design. This runs a converter that bundles the real component code (from Storybook or a bare package) and uploads it. Use when the user runs /design-sync or says \"sync my design system to Claude Design\".","argumentHint":"[\u003cproject hint, e.g. \"Acme DS\"\u003e]","builtin":true},{"name":"dataviz","description":"Use this skill whenever you are about to create ANY chart, graph, plot, dashboard, or data visualization, in ANY output medium — an HTML or React artifact, inline SVG, plotting code in any library (matplotlib, plotly, d3, Recharts, …), an image/PNG you will render and upload, or a chart shared into Slack. Read it BEFORE writing the first line of chart code, choosing chart colors, building a stat tile / meter / KPI row, or laying out a dashboard. When the destination is a first-party document connector (host-designated, never self-described) that renders live charts, hand it the rows (inline, or as an uploaded data file the chart cites) rather than a rendered PNG/SVG — a picture of a chart loses hover, data inspection and per-value comments. Produces visualizations that read as one system — elegant, accessible, consistent in light and dark — using a brand-neutral placeholder palette you swap for your own. Teaches a design-system-agnostic method: a form heuristic, a color formula with a runnable validator, mark specs, and interaction rules. A validated default palette is documented in `references/palette.md` — swap that file's values for your brand's. Triggers on: \"chart\", \"graph\", \"plot\", \"data viz\", \"visualization\", \"dashboard\", \"analytics\", \"visualize data\", \"categorical colors\", \"sequential / diverging palette\", \"stat tile\", \"sparkline\", \"heatmap\", \"legend\", \"axis\", \"tooltip\", \"chart colors\", \"color by series\".","argumentHint":"","builtin":true},{"name":"artifact-diagramming","description":"Diagramming know-how for Artifacts - when a picture earns its place, how to draw one that shows the real mechanism, and the inline-SVG mechanics that keep it legible in both themes.","argumentHint":"","builtin":true},{"name":"artifact-capabilities","description":"Runtime capabilities a published Artifact page can be granted — behavior static HTML cannot provide on its own, such as the page reading live or connected data, remembering what people do on it (a poll, a sign-up sheet, a checklist, a document edited in place — it saves new versions of itself), keeping state shared across viewers, knowing who is viewing, asking Claude a question of its own, storing files people add, handing the viewer a file to save, or using the viewer's camera, microphone, location, screen or device motion. Serves this user's live capability roster and the typed call definitions. Load it whenever any such runtime behavior would make an artifact more useful, before writing the page.","argumentHint":"","builtin":true},{"name":"update-config","description":"Use this skill to configure the Claude Code harness via settings.json. Automated behaviors (\"from now on when X\", \"each time X\", \"whenever X\", \"before/after X\") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions (\"allow X\", \"add permission\", \"move permission to\"), env vars (\"set X=Y\"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: \"allow npm commands\", \"add bq permission to global settings\", \"move permission to user settings\", \"set DEBUG=true\", \"when claude stops show X\". For simple settings like theme/model, suggest the /config command.","argumentHint":"","builtin":true},{"name":"verify","description":"Verify that a code change actually does what it's supposed to by exercising it end-to-end and observing behavior — drive the affected flow, not just tests or typecheck. Run before committing nontrivial changes; bootstraps this repo's project verify skill if none exists yet. Don't invoke it on a diff that only touches tests, docs, or other code with no runtime surface to drive (a change to product source always has one) — there's nothing to observe.","argumentHint":"","builtin":true},{"name":"debug","description":"Enable debug logging for this session and help diagnose issues","argumentHint":"[issue description]","builtin":true},{"name":"code-review","description":"Review the current diff, or a PR number/branch/path target, for correctness bugs (plus reuse/simplification/efficiency cleanups where the model's review recipe covers them) at the given effort level (low|medium|high|xhigh|max: from few high-confidence findings up to many, some of them uncertain; ultra: deep multi-agent review in the cloud); with no level given, it reuses the level you typed last. Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review. Pass --max-findings \u003cn\u003e to report up to n findings, or --max-findings all for every finding. The choice stays until you pass --max-findings default. For ultra on a GitHub.com PR target, --post asks to post the finished review’s findings to the PR as a single comment from the user’s GitHub account (not a review; the launch dialog still confirms in interactive sessions, while non-interactive mode posts on the flag alone) and --no-post hides that option.","argumentHint":"[low|medium|high|xhigh|max|ultra] [--fix] [--comment] [\u003cpr#\u003e|\u003cbranch\u003e|\u003cpath\u003e] [--max-findings \u003cn\u003e|all]","aliases":["review"],"builtin":true},{"name":"simplify","description":"Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.","argumentHint":"[\u003ctarget\u003e]","builtin":true},{"name":"batch","description":"Research and plan a large-scale change, then execute it in parallel across 5–30 isolated worktree agents that each open a PR.","argumentHint":"\u003cinstruction\u003e","builtin":true},{"name":"fewer-permission-prompts","description":"Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.","argumentHint":"","builtin":true},{"name":"doctor","description":"Health-check the user's Claude Code setup and fix issues: diagnose installation health — what the `claude doctor` terminal diagnostics cover — from local data (duplicate or leftover installs, PATH, unparseable settings files, broken or colliding agent definitions, skills whose frontmatter fails to parse); find unused skills, MCP servers, and plugins versus their context cost and disable dead weight; deduplicate local CLAUDE.md files against checked-in ones; trim checked-in CLAUDE.md files by cutting content a session could derive from the codebase (directory layouts, tech-stack lists, architecture overviews) while keeping gotchas, rationale, and non-standard conventions; migrate always-loaded CLAUDE.md guidance into lazy skills and nested CLAUDE.md files; flag slow hooks and context-heavy extensions; check the installed version is current; make auto mode the default permission mode; and pre-approve frequently denied read-only commands. Use when the user asks for a doctor run, checkup, audit, tune-up, or cleanup of their Claude Code setup or configuration.","argumentHint":"[prompt-audit [\u003cpath\u003e]]","aliases":["checkup"],"builtin":true},{"name":"loop","description":"Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace.","argumentHint":"[interval] [prompt]","aliases":["proactive"],"builtin":true},{"name":"schedule","description":"Create, update, list, or run scheduled cloud agents (routines) that execute on a cron schedule.","argumentHint":"","aliases":["routines"],"builtin":true},{"name":"claude-api","description":"Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.\nTRIGGER — read BEFORE opening the target file; don't skip because it \"looks like a one-liner\" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).\nSKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).","argumentHint":"","builtin":true},{"name":"workflow-authoring","description":"Reference for writing a Workflow tool script (script API and gotchas, resume, quality patterns, worked examples). Load before authoring a script for a workflow the user already opted into; it does not itself authorize running one.","argumentHint":"","builtin":true},{"name":"run","description":"Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).","argumentHint":"","builtin":true},{"name":"run-skill-generator","description":"Author or improve the run-\u003cunit\u003e skill - a per-project skill that tells agents how to build, launch, and drive this project's app. Use when the user asks to set up the project, get it running, write run instructions, or verify build/run steps work from a clean environment.","argumentHint":"","builtin":true},{"name":"plugin-authoring","description":"Make a mod: a change to Claude's own interface or behaviour, such as a live pane or panel, a band above the prompt, a status line, a toast, a slash command, or a hook on tool calls or prompts. Use to make, change or debug a mod or its hooks module.","argumentHint":"","builtin":true},{"name":"advisor","description":"Let Claude consult a stronger model at key moments","argumentHint":"[fable|opus|sonnet|off]","builtin":true},{"name":"agents","description":"(removed) Ask Claude to create/manage subagents, or edit .claude/agents/","argumentHint":"","builtin":true},{"name":"auto-mode-setup","description":"Teach auto mode about your environment, plus optional rule tweaks","argumentHint":"[--request-id \u003cuuid\u003e] (--wizard posture=… scope=… depth=… --propose | --expect-sha256 \u003c64-hex\u003e --apply-file \u003cpath\u003e)","builtin":true},{"name":"autocompact","description":"Configure the auto-compact window size","argumentHint":"[auto|\u003ctokens\u003e]","builtin":true},{"name":"clear","description":"Start a new session with empty context; previous session stays on disk (resumable with /resume)","argumentHint":"[name]","aliases":["reset","new"],"builtin":true},{"name":"color","description":"Set the prompt bar color for this session","argumentHint":"[red|blue|green|yellow|purple|orange|pink|cyan|default]","builtin":true},{"name":"compact","description":"Free up context by summarizing the conversation so far","argumentHint":"\u003coptional custom summarization instructions\u003e","builtin":true},{"name":"config","description":"Set a setting by key","argumentHint":"key=value","aliases":["settings"],"builtin":true},{"name":"output-style","description":"List output styles or switch to one","argumentHint":"[style]","builtin":true},{"name":"context","description":"Show current context usage","argumentHint":"","builtin":true},{"name":"effort","description":"Set effort level for model usage","argumentHint":"\u003clow|medium|high|xhigh|max|auto|ultracode [on|off]\u003e","builtin":true},{"name":"fast","description":"Toggle fast mode (Opus 5.5)","argumentHint":"[on|off]","builtin":true},{"name":"focus","description":"Toggle focus view: just your prompt, summary, and response","argumentHint":"[on|off]","builtin":true},{"name":"heapdump","description":"Dump the JS heap to the Desktop; on Linux with no Desktop folder, the home directory","argumentHint":"","builtin":true},{"name":"init","description":"Initialize a new CLAUDE.md file with codebase documentation","argumentHint":"","builtin":true},{"name":"mcp","description":"Manage MCP servers","argumentHint":"[reconnect|enable|disable [\u003cserver\u003e|all]]","builtin":true},{"name":"import","description":"Import config from another AI coding agent","argumentHint":"","builtin":true},{"name":"model","description":"Set the AI model for Claude Code","argumentHint":"\u003cmodel\u003e","builtin":true},{"name":"__remote-workflow","description":"Run the workflow script delivered in this session environment (server-launched sessions only)","argumentHint":"","builtin":true},{"name":"workflow-launch-exec","description":"Execute a server-launched workflow handoff (workflow_launch event sessions only)","argumentHint":"","builtin":true},{"name":"reload-plugins","description":"Activate pending plugin changes in the current session","argumentHint":"[--force]","builtin":true},{"name":"reload-skills","description":"Pick up skills added or changed on disk during this session","argumentHint":"","builtin":true},{"name":"rename","description":"Rename the current conversation","argumentHint":"[name]","aliases":["name"],"builtin":true},{"name":"ultrareview","description":"Start a cloud agent that finds and verifies bugs in your branch (~15-25 min, $5-$25 USD) · Runs in a cloud session. See https://code.claude.com/docs/en/claude-code-on-the-web","argumentHint":"","builtin":true},{"name":"security-review","description":"Complete a security review of the pending changes on the current branch","argumentHint":"","builtin":true},{"name":"usage-credits","description":"Configure usage credits or request them from your admin when you hit a limit","argumentHint":"","builtin":true},{"name":"extra-usage","description":"Renamed to /usage-credits","argumentHint":"","builtin":true},{"name":"usage","description":"Show session cost, plan usage, and what's contributing to your limits","argumentHint":"","aliases":["cost","stats"],"builtin":true},{"name":"insights","description":"Generate a report analyzing your Claude Code sessions","argumentHint":"","builtin":true},{"name":"recap","description":"Generate a one-line session recap now","argumentHint":"","builtin":true},{"name":"skill-doctor","description":"Show which loaded skills are unused and costing context","argumentHint":"","builtin":true},{"name":"goal","description":"Set a goal — keep working until the condition is met","argumentHint":"","builtin":true},{"name":"design-consent","description":"Grant Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"design-revoke","description":"Revoke Claude agent access to your Design projects","argumentHint":"","builtin":true},{"name":"list-agents","description":"List subagents, teammates, and other Claude sessions you can message","argumentHint":"","aliases":["peers"],"builtin":true},{"name":"team-onboarding","description":"Help teammates ramp on Claude Code with a guide from your usage","argumentHint":"","builtin":true},{"name":"docs","description":"docs (editable docs people share and comment on; the default for any document, named as a doc or not: a document, report, proposal, resume, cover letter, letter, contract, policy, form, template, worksheet, essay, handbook, guide, how-to, cheat sheet, SOP or other writing to keep, share, collaborate on, send, submit, print or sign; a doc exports to Word, PDF, Markdown or Google Docs, so needing a file to send, attach, upload, submit or print is no reason to pick Word, and a file nobody asked for is a doc, not Word; a plan, comparison, summary or notes asked in chat stays in chat; a pasted claude.ai artifact link may be a doc: check with docs tools first; Word or another file format named, tracked changes wanted, or a .docx to change or use as a template → that format's skill): making one → if no docs-connector instructions are in context, call its `guide` (topic.instructions) first; then create the doc (headings only, no body) before any search, file read or plan, even with files attached. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docs"]},{"name":"docx","description":"Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx) or Word templates (.dotx). Triggers include: any mention of Microsoft Word Documents, such as 'Word doc', 'word document', '.docx', '.dotx', 'microsoft doc'. Also use when extracting or reorganizing content from .docx or .dotx files, inserting or replacing images in documents, find-and-replace in Word files, working with tracked changes or comments, or converting content into a polished Word document. If the user asks for a deliverable as a Word or .docx file (to download, email or print), use this skill. However, if they ask for a document, page, report, memo, or notes WITHOUT naming a file format and the session offers Claude's own dedicated document or page skill or connector, use that instead, even if they will email or print it. Do NOT use for PDFs, spreadsheets, Google Docs, or coding unrelated to document generation. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:docx"]},{"name":"google-workspace","description":"Read this before the first Google Drive, Docs, Sheets or Slides connector call whenever the task creates or changes a Google file. Use this skill whenever the user wants to create or change a Google Doc, Sheet or Slides file in their Google Drive. Triggers include: a request that names Google Docs, Sheets, Slides or Drive and asks to make, edit, format, copy or rename a file; a docs.google.com link with a request to change that file, even a one-line fix or suggested edits; and any follow-up change to a Google file from earlier in the chat, even \"change it\" or \"add a tab\". Includes helper scripts for document positions, cell ranges and slide layout. However, if the user asks for a doc, deck or spreadsheet without naming Google, or gives a Google file only as source material for something new, use Claude's own output type instead. Do NOT use for read-only questions about a Google file, or for Word, Excel, PowerPoint or PDF files. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:google-workspace"]},{"name":"import-memory","description":"Import a memory export from another AI assistant into Claude's memory — conversationally, additively, and with the content treated as data. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:import-memory"]},{"name":"morning","description":"Render the user's morning brief as a styled HTML artifact, or set it up as a recurring weekday task. Use only when the user explicitly asks to run, see, or set up their morning brief, or if they invoke /morning by name. A question about their day, schedule, or calendar is not by itself a request for the brief; answer it directly instead. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:morning"]},{"name":"pdf","description":"Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text/tables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pdf"]},{"name":"pptx","description":"Use this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations as PowerPoint (.pptx) files; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an email, summary, or creating a different type of slide deck); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates (.potx), layouts, speaker notes, or comments. Trigger whenever the user asks for a PowerPoint or .pptx file, or references a .pptx or .potx filename, regardless of what they plan to do with the content afterward. However, when the user asks for a deck, slides, a slide deck, or a presentation without naming a file format, default to using a dedicated slide-deck artifact type or a separate slides skill if this session offers one; otherwise, use this skill. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:pptx"]},{"name":"skill-creator","description":"Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:skill-creator"]},{"name":"xlsx","description":"Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .xltx, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like \"the xlsx in my downloads\") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved. (claude.ai sync)","argumentHint":"","aliases":["anthropic-skills:xlsx"]}],"agents":[{"name":"claude","description":"Catch-all for any task that doesn't fit a more specific agent. FleetView's default when no agent name is typed."},{"name":"claude-code-guide","description":"Use this agent when the user asks questions (\"Can Claude...\", \"Does Claude...\", \"How do I...\") about: (1) Claude Code (the CLI tool) - features, hooks, slash commands, MCP servers, settings, IDE integrations, keyboard shortcuts; (2) Claude Agent SDK - building custom agents; (3) Claude API (formerly Anthropic API) - Messages API for directly passing messages to Claude, Tool Runner (`client.beta.messages.tool_runner`) for running an agentic loop over your own tools, manual tool-use loops, Managed Agents for server-hosted agents with a managed sandbox, prompt caching, and general Anthropic SDK usage; (4) Claude Tag (Claude in Slack) - what it is, setting it up for a Slack workspace, `/install-slack-app`; (5) `claude plugin eval` (writing and running plugin eval suites, its JSON/report, sandbox, CI) and the `/skill-doctor` report. **IMPORTANT:** Before spawning a new agent, check if there is already a running or recently completed claude-code-guide agent that you can continue via SendMessage.","model":"haiku"},{"name":"Explore","description":"Fast read-only search agent for locating code. Use it to find files by pattern (eg. \"src/components/**/*.tsx\"), grep for symbols or keywords (eg. \"API endpoints\"), or answer \"where is X defined / which files reference Y.\" Do NOT use it for code review, design-doc auditing, cross-file consistency checks, or open-ended analysis — it reads excerpts rather than whole files and will miss content past its read window. When calling, specify search breadth: \"quick\" for a single targeted lookup, \"medium\" for moderate exploration, or \"very thorough\" to search across multiple locations and naming conventions.","model":"inherit"},{"name":"general-purpose","description":"General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you."},{"name":"Plan","description":"Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.","model":"inherit"},{"name":"statusline-setup","description":"Use this agent to configure the user's Claude Code status line setting.","model":"sonnet"}],"output_style":"default","available_output_styles":["default","Proactive","Concise","Explanatory","Learning"],"user_output_styles_dir":"/Users/flamingcow/.claude/output-styles","models":[{"value":"default","resolvedModel":"claude-opus-5-5","displayName":"Default (recommended)","description":"Opus 5.5 · Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"opus","resolvedModel":"claude-opus-5-5","displayName":"Opus 5.5","description":"For complex work and everyday tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"fable","resolvedModel":"claude-fable-5-1","displayName":"Fable 5.1","description":"For your toughest challenges","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"sonnet","resolvedModel":"claude-sonnet-5-5","displayName":"Sonnet 5.5","description":"Most efficient for simpler tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"haiku","resolvedModel":"claude-haiku-5-5","displayName":"Haiku 5.5","description":"Fastest for quick answers","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-haiku-4-5-20251001","resolvedModel":"claude-haiku-4-5-20251001","displayName":"Haiku 4.5","description":"Fastest for quick answers"},{"value":"claude-sonnet-5","resolvedModel":"claude-sonnet-5","displayName":"Sonnet 5","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-5","resolvedModel":"claude-opus-5","displayName":"Opus 5","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-fable-5","resolvedModel":"claude-fable-5","displayName":"Fable 5","description":"Most capable for your hardest and longest-running tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-8","resolvedModel":"claude-opus-4-8","displayName":"Opus 4.8","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsFastMode":true,"supportsAutoMode":true},{"value":"claude-opus-4-7","resolvedModel":"claude-opus-4-7","displayName":"Opus 4.7","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","xhigh","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-opus-4-6","resolvedModel":"claude-opus-4-6","displayName":"Opus 4.6","description":"Best for everyday, complex tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true},{"value":"claude-sonnet-4-6","resolvedModel":"claude-sonnet-4-6","displayName":"Sonnet 4.6","description":"Efficient for routine tasks","supportsEffort":true,"supportedEffortLevels":["low","medium","high","max"],"supportsAdaptiveThinking":true,"supportsAutoMode":true}],"account":{"email":"[redacted]","organization":"[redacted]'s Organization","subscriptionType":"Claude Max","apiProvider":"firstParty"},"pid":31926,"claude_code_version":"2.1.296","current_permission_mode":"bypassPermissions","hooks_applied":true,"feedback_mode":{"kind":"post"},"analytics_disabled":false,"remote_control_auto_enable":false,"remote_control_auto_connect_default":false,"remote_control_available":true,"remote_control_auto_on_by_default":false,"ide_rc_auto_enable_gate":true,"fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","session_state":"idle","capabilities":["ui_surface_v1"]},"pending_permission_requests":[],"pending_user_dialog_requests":[]}}} {"dir":"send","msg":{"message":{"content":"Reply with the single word ok.","role":"user"},"parent_tool_use_id":null,"session_id":"","type":"user"}} -{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"running","uuid":"c9e33583-f4fe-45b5-afe1-3e375d3c5821","session_id":"cdf5f946-25fd-407e-b9c1-1508f0d49770"}} -{"dir":"recv","msg":{"type":"system","subtype":"init","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestStopHookBlocks4069958610/001","session_id":"cdf5f946-25fd-407e-b9c1-1508f0d49770","tools":["Task","Artifact","ArtifactComments","ArtifactData","Bash","CronCreate","CronDelete","CronList","DesignSync","Edit","EnterWorktree","ExitWorktree","ListAgents","LSP","Monitor","NotebookEdit","PushNotification","Read","RemoteTrigger","ReportFindings","ScheduleWakeup","SendMessage","Skill","TaskStop","ToolSearch","WebFetch","WebSearch","Workflow","Write"],"mcp_servers":[{"name":"claude.ai Claude Docs","status":"pending","source":"claudeai"},{"name":"claude.ai Heliosian","status":"pending","source":"claudeai"},{"name":"claude.ai Fastmail","status":"pending","source":"claudeai"},{"name":"claude.ai Google Calendar","status":"pending","source":"claudeai"},{"name":"claude.ai Gmail","status":"pending","source":"claudeai"},{"name":"claude.ai Google Drive","status":"pending","source":"claudeai"}],"model":"claude-sonnet-5-5","permissionMode":"bypassPermissions","slash_commands":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","advisor","agents","auto-mode-setup","autocompact","clear","color","compact","config","output-style","context","effort","fast","focus","heapdump","init","mcp","import","model","__remote-workflow","workflow-launch-exec","reload-plugins","reload-skills","rename","ultrareview","security-review","usage-credits","extra-usage","usage","insights","recap","skill-doctor","goal","design-consent","design-revoke","list-agents","team-onboarding","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"terminal_slash_commands":["doctor","color","focus","reload-plugins"],"apiKeySource":"none","claude_code_version":"2.1.296","output_style":"default","agents":["claude","claude-code-guide","Explore","general-purpose","Plan","statusline-setup"],"skills":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"plugins":[{"name":"clangd-lsp","path":"/Users/flamingcow/.claude/plugins/cache/claude-plugins-official/clangd-lsp/1.0.0","source":"clangd-lsp@claude-plugins-official","version":"1.0.0"},{"name":"cc-plugin-agents-md","path":"builtin","source":"cc-plugin-agents-md@builtin"},{"name":"cc-plugin-telemetry","path":"builtin","source":"cc-plugin-telemetry@builtin"},{"name":"cc-plugin-plugin-authoring","path":"builtin","source":"cc-plugin-plugin-authoring@builtin"}],"capabilities":["interrupt_receipt_v1","interrupt_cancel_queued_v1","interrupt_send_now_v1","msg_lifecycle_v1","request_marker_lists_v1","sdk_mcp_tools_list_changed","sdk_mcp_manifests","mcp_read_resource_v1","mcp_tool_ui_meta_v1","ui_surface_v1"],"analytics_disabled":false,"product_feedback_disabled":false,"uuid":"b14b9147-333e-466a-b9c9-97dc8b307923","memory_paths":{"auto":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestStopHookBlocks4069958610-001/memory/"},"scratchpad_path":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestStopHookBlocks4069958610-001/cdf5f946-25fd-407e-b9c1-1508f0d49770/scratchpad","messaging_socket_path":"/tmp/cc-socks/27101.sock","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","per_turn_effort_active":true,"view_mode":"default"}} -{"dir":"recv","msg":{"type":"rate_limit_event","rate_limit_info":{"status":"allowed","resetsAt":1791990000,"rateLimitType":"five_hour","overageStatus":"rejected","overageDisabledReason":"org_level_disabled","isUsingOverage":false,"unifiedWindows":{"five_hour":{"utilization":0.04,"resetsAt":1791990000},"seven_day":{"utilization":0.27,"resetsAt":1792270800}}},"uuid":"01ac2a2f-3ed8-4ead-afd1-aa5dc1996242","session_id":"cdf5f946-25fd-407e-b9c1-1508f0d49770"}} -{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2FDQkohWEGHydtjSkQv","type":"message","role":"assistant","content":[{"type":"text","text":"ok"}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":37208,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":37208},"output_tokens":4,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"cdf5f946-25fd-407e-b9c1-1508f0d49770","uuid":"16aafb54-d4cd-417e-9cf8-f152a5517e89","timestamp":"2026-10-14T13:56:53.980Z","request_id":"req_011Cg2FDQVvgBwfhVsNJ96BS"}} -{"dir":"recv","msg":{"type":"control_request","request_id":"b9e960c9-26bc-4f69-86c2-b19db58762f9","request":{"subtype":"hook_callback","callback_id":"hook_0","input":{"session_id":"cdf5f946-25fd-407e-b9c1-1508f0d49770","transcript_path":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestStopHookBlocks4069958610-001/cdf5f946-25fd-407e-b9c1-1508f0d49770.jsonl","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestStopHookBlocks4069958610/001","scratchpad_dir":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestStopHookBlocks4069958610-001/cdf5f946-25fd-407e-b9c1-1508f0d49770/scratchpad","prompt_id":"65443393-987b-41cd-a954-1f4358da5cf2","permission_mode":"bypassPermissions","effort":{"level":"medium"},"hook_event_name":"Stop","stop_hook_active":false,"last_assistant_message":"ok","background_tasks":[],"session_crons":[]},"tool_use_id":"97794fce-b8d3-4249-b586-bd8b61a88f3a"}}} -{"dir":"send","msg":{"response":{"request_id":"b9e960c9-26bc-4f69-86c2-b19db58762f9","response":{"decision":"block","reason":"Before stopping, reply with the single word PINEAPPLE."},"subtype":"success"},"type":"control_response"}} -{"dir":"recv","msg":{"type":"user","message":{"role":"user","content":[{"type":"text","text":"Stop hook feedback:\nBefore stopping, reply with the single word PINEAPPLE."}]},"parent_tool_use_id":null,"session_id":"cdf5f946-25fd-407e-b9c1-1508f0d49770","uuid":"8fe4efc0-56c4-47fa-8a9d-8d1aaf94ba7b","timestamp":"2026-10-14T13:56:53.999Z","isSynthetic":true}} -{"dir":"recv","msg":{"type":"system","subtype":"notification","key":"stop-hook-error","text":"Stop hook error occurred · ctrl+o to see","priority":"immediate","session_id":"cdf5f946-25fd-407e-b9c1-1508f0d49770","uuid":"ec2b8e01-76ad-4139-b785-76638bb013da"}} -{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2FDXjG2gpERGPdwuZS9","type":"message","role":"assistant","content":[{"type":"text","text":"PINEAPPLE"}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":6666,"cache_read_input_tokens":30622,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":6666},"output_tokens":8,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":{"cache_miss_reason":{"type":"messages_changed","cache_missed_input_tokens":12736}},"context_management":null},"parent_tool_use_id":null,"session_id":"cdf5f946-25fd-407e-b9c1-1508f0d49770","uuid":"9b5b2968-1089-46ba-9ff9-f06f97fabe29","timestamp":"2026-10-14T13:56:55.174Z","request_id":"req_011Cg2FDXNB3Ucc4J1dzRTnt"}} -{"dir":"recv","msg":{"type":"control_request","request_id":"873aafb7-50cd-4008-a59b-0952971fcc34","request":{"subtype":"hook_callback","callback_id":"hook_0","input":{"session_id":"cdf5f946-25fd-407e-b9c1-1508f0d49770","transcript_path":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestStopHookBlocks4069958610-001/cdf5f946-25fd-407e-b9c1-1508f0d49770.jsonl","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestStopHookBlocks4069958610/001","scratchpad_dir":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestStopHookBlocks4069958610-001/cdf5f946-25fd-407e-b9c1-1508f0d49770/scratchpad","prompt_id":"65443393-987b-41cd-a954-1f4358da5cf2","permission_mode":"bypassPermissions","effort":{"level":"medium"},"hook_event_name":"Stop","stop_hook_active":true,"last_assistant_message":"PINEAPPLE","background_tasks":[],"session_crons":[]},"tool_use_id":"914565c6-5396-4b28-8d02-8117d557be8d"}}} -{"dir":"send","msg":{"response":{"request_id":"873aafb7-50cd-4008-a59b-0952971fcc34","response":{},"subtype":"success"},"type":"control_response"}} -{"dir":"recv","msg":{"duration_api_ms":3475,"stop_reason":"end_turn","session_id":"cdf5f946-25fd-407e-b9c1-1508f0d49770","total_cost_usd":0.17883059999999998,"usage":{"input_tokens":4,"cache_creation_input_tokens":43874,"cache_read_input_tokens":30622,"output_tokens":14,"output_tokens_details":{"thinking_tokens":0},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":43874,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":10,"cache_read_input_tokens":30622,"cache_creation_input_tokens":6666,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":6666},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":1174,"outputTokens":14,"cacheReadInputTokens":0,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.0001244,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":0,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"},"claude-sonnet-5-5":{"inputTokens":4,"outputTokens":14,"cacheReadInputTokens":30622,"cacheCreationInputTokens":43874,"webSearchRequests":0,"costUSD":0.17870619999999998,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":0,"canonicalModel":"claude-sonnet-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":2,"subtype":"success","api_error_status":null,"result":"PINEAPPLE","ttft_ms":1659,"type":"result","duration_ms":2875,"uuid":"46b61811-e9c6-4e94-b22b-8c8d83f06e4d","ttft_stream_ms":1654,"time_to_request_ms":30,"first_content_frame_ms":1654,"queued_turn_count":0,"result_index":0}} -{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"idle","uuid":"89efc1c0-2550-4aaf-b6e8-b9eb3ed41f10","session_id":"cdf5f946-25fd-407e-b9c1-1508f0d49770"}} +{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"running","uuid":"7466e689-4aa9-46ad-8a99-39b30828fd81","session_id":"3e4f2d36-4639-4ae0-a7b4-f02ca6d1ad1c"}} +{"dir":"recv","msg":{"type":"system","subtype":"init","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestStopHookBlocks1326260825/001","session_id":"3e4f2d36-4639-4ae0-a7b4-f02ca6d1ad1c","tools":["Task","Artifact","ArtifactComments","ArtifactData","Bash","CronCreate","CronDelete","CronList","DesignSync","Edit","EnterWorktree","ExitWorktree","ListAgents","LSP","Monitor","NotebookEdit","PushNotification","Read","RemoteTrigger","ReportFindings","ScheduleWakeup","SendMessage","Skill","TaskStop","ToolSearch","WebFetch","WebSearch","Workflow","Write"],"mcp_servers":[{"name":"claude.ai Claude Docs","status":"pending","source":"claudeai"},{"name":"claude.ai Heliosian","status":"pending","source":"claudeai"},{"name":"claude.ai Fastmail","status":"pending","source":"claudeai"},{"name":"claude.ai Google Calendar","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Gmail","status":"needs-auth","source":"claudeai"},{"name":"claude.ai Google Drive","status":"needs-auth","source":"claudeai"}],"model":"claude-sonnet-5-5","permissionMode":"bypassPermissions","slash_commands":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","advisor","agents","auto-mode-setup","autocompact","clear","color","compact","config","output-style","context","effort","fast","focus","heapdump","init","mcp","import","model","__remote-workflow","workflow-launch-exec","reload-plugins","reload-skills","rename","ultrareview","security-review","usage-credits","extra-usage","usage","insights","recap","skill-doctor","goal","design-consent","design-revoke","list-agents","team-onboarding","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"terminal_slash_commands":["doctor","color","focus","reload-plugins"],"apiKeySource":"none","claude_code_version":"2.1.296","output_style":"default","agents":["claude","claude-code-guide","Explore","general-purpose","Plan","statusline-setup"],"skills":["deep-research","design","slides","design-sync","dataviz","artifact-diagramming","artifact-capabilities","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","schedule","claude-api","workflow-authoring","run","run-skill-generator","plugin-authoring","anthropic-skills:docs","anthropic-skills:docx","anthropic-skills:google-workspace","anthropic-skills:import-memory","anthropic-skills:morning","anthropic-skills:pdf","anthropic-skills:pptx","anthropic-skills:skill-creator","anthropic-skills:xlsx"],"plugins":[{"name":"clangd-lsp","path":"/Users/flamingcow/.claude/plugins/cache/claude-plugins-official/clangd-lsp/1.0.0","source":"clangd-lsp@claude-plugins-official","version":"1.0.0"},{"name":"cc-plugin-agents-md","path":"builtin","source":"cc-plugin-agents-md@builtin"},{"name":"cc-plugin-telemetry","path":"builtin","source":"cc-plugin-telemetry@builtin"},{"name":"cc-plugin-plugin-authoring","path":"builtin","source":"cc-plugin-plugin-authoring@builtin"}],"capabilities":["interrupt_receipt_v1","interrupt_cancel_queued_v1","interrupt_send_now_v1","msg_lifecycle_v1","request_marker_lists_v1","sdk_mcp_tools_list_changed","sdk_mcp_manifests","mcp_read_resource_v1","mcp_tool_ui_meta_v1","ui_surface_v1"],"analytics_disabled":false,"product_feedback_disabled":false,"uuid":"d290ca4d-bec4-431b-9d88-32f6254bef16","memory_paths":{"auto":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestStopHookBlocks1326260825-001/memory/"},"scratchpad_path":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestStopHookBlocks1326260825-001/3e4f2d36-4639-4ae0-a7b4-f02ca6d1ad1c/scratchpad","messaging_socket_path":"/tmp/cc-socks/31926.sock","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","per_turn_effort_active":true,"view_mode":"default"}} +{"dir":"recv","msg":{"type":"rate_limit_event","rate_limit_info":{"status":"allowed","resetsAt":1791990000,"rateLimitType":"five_hour","overageStatus":"rejected","overageDisabledReason":"org_level_disabled","isUsingOverage":false,"unifiedWindows":{"five_hour":{"utilization":0.1,"resetsAt":1791990000},"seven_day":{"utilization":0.27,"resetsAt":1792270800}}},"uuid":"298c97fd-18ae-4fd3-a1c0-97e0652f967e","session_id":"3e4f2d36-4639-4ae0-a7b4-f02ca6d1ad1c"}} +{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2Gx9GTtRmVucdJz23Dr","type":"message","role":"assistant","content":[{"type":"text","text":"ok"}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":15241,"cache_read_input_tokens":22367,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":15241},"output_tokens":4,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":null,"context_management":null},"parent_tool_use_id":null,"session_id":"3e4f2d36-4639-4ae0-a7b4-f02ca6d1ad1c","uuid":"fa778c18-4947-4714-9a34-33f77e80155f","timestamp":"2026-10-14T14:19:40.561Z","request_id":"req_011Cg2Gx8yMakaQsD5VcD2DT"}} +{"dir":"recv","msg":{"type":"control_request","request_id":"2bac3cff-f7a1-4ae9-805a-12f82c932e4f","request":{"subtype":"hook_callback","callback_id":"hook_0","input":{"session_id":"3e4f2d36-4639-4ae0-a7b4-f02ca6d1ad1c","transcript_path":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestStopHookBlocks1326260825-001/3e4f2d36-4639-4ae0-a7b4-f02ca6d1ad1c.jsonl","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestStopHookBlocks1326260825/001","scratchpad_dir":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestStopHookBlocks1326260825-001/3e4f2d36-4639-4ae0-a7b4-f02ca6d1ad1c/scratchpad","prompt_id":"2e64780e-10e7-42e7-8eff-5f43af1ca501","permission_mode":"bypassPermissions","effort":{"level":"medium"},"hook_event_name":"Stop","stop_hook_active":false,"last_assistant_message":"ok","background_tasks":[],"session_crons":[]},"tool_use_id":"fe0f7ba8-53b5-4d3b-9043-d54d699737f9"}}} +{"dir":"send","msg":{"response":{"request_id":"2bac3cff-f7a1-4ae9-805a-12f82c932e4f","response":{"decision":"block","reason":"Before stopping, reply with the single word PINEAPPLE."},"subtype":"success"},"type":"control_response"}} +{"dir":"recv","msg":{"type":"user","message":{"role":"user","content":[{"type":"text","text":"Stop hook feedback:\nBefore stopping, reply with the single word PINEAPPLE."}]},"parent_tool_use_id":null,"session_id":"3e4f2d36-4639-4ae0-a7b4-f02ca6d1ad1c","uuid":"0ff31df4-3585-4f6f-9ab3-0bd33d6377e7","timestamp":"2026-10-14T14:19:40.584Z","isSynthetic":true}} +{"dir":"recv","msg":{"type":"system","subtype":"notification","key":"stop-hook-error","text":"Stop hook error occurred · ctrl+o to see","priority":"immediate","session_id":"3e4f2d36-4639-4ae0-a7b4-f02ca6d1ad1c","uuid":"c624126b-84dc-429e-93f2-271320dddd18"}} +{"dir":"recv","msg":{"type":"assistant","message":{"model":"claude-sonnet-5-5","id":"msg_011Cg2GxGUHzR8gii2cwrHrF","type":"message","role":"assistant","content":[{"type":"text","text":"PINEAPPLE"}],"container":null,"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":7068,"cache_read_input_tokens":30620,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":7068},"output_tokens":8,"service_tier":"standard","inference_geo":"global"},"input_transformations":[],"diagnostics":{"cache_miss_reason":{"type":"messages_changed","cache_missed_input_tokens":13145}},"context_management":null},"parent_tool_use_id":null,"session_id":"3e4f2d36-4639-4ae0-a7b4-f02ca6d1ad1c","uuid":"5f09e57e-9607-4df6-91d6-a54cc86e4049","timestamp":"2026-10-14T14:19:42.164Z","request_id":"req_011Cg2GxG8xRHjaHDa66Mve8"}} +{"dir":"recv","msg":{"type":"control_request","request_id":"00b870c0-62fc-4801-b95b-44f7b57d70ff","request":{"subtype":"hook_callback","callback_id":"hook_0","input":{"session_id":"3e4f2d36-4639-4ae0-a7b4-f02ca6d1ad1c","transcript_path":"/Users/flamingcow/.claude/projects/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestStopHookBlocks1326260825-001/3e4f2d36-4639-4ae0-a7b4-f02ca6d1ad1c.jsonl","cwd":"/private/var/folders/r4/m529kfnd537_x0xfbgcgqfs40000gn/T/TestStopHookBlocks1326260825/001","scratchpad_dir":"/private/tmp/claude-501/-private-var-folders-r4-m529kfnd537-x0xfbgcgqfs40000gn-T-TestStopHookBlocks1326260825-001/3e4f2d36-4639-4ae0-a7b4-f02ca6d1ad1c/scratchpad","prompt_id":"2e64780e-10e7-42e7-8eff-5f43af1ca501","permission_mode":"bypassPermissions","effort":{"level":"medium"},"hook_event_name":"Stop","stop_hook_active":true,"last_assistant_message":"PINEAPPLE","background_tasks":[],"session_crons":[]},"tool_use_id":"75f2118f-2a97-4223-82be-f5e22125eba2"}}} +{"dir":"send","msg":{"response":{"request_id":"00b870c0-62fc-4801-b95b-44f7b57d70ff","response":{},"subtype":"success"},"type":"control_response"}} +{"dir":"recv","msg":{"duration_api_ms":3835,"stop_reason":"end_turn","session_id":"3e4f2d36-4639-4ae0-a7b4-f02ca6d1ad1c","total_cost_usd":0.0948071,"usage":{"input_tokens":4,"cache_creation_input_tokens":22309,"cache_read_input_tokens":52987,"output_tokens":14,"output_tokens_details":{"thinking_tokens":0},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":22309,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":10,"cache_read_input_tokens":30620,"cache_creation_input_tokens":7068,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":7068},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":1174,"outputTokens":14,"cacheReadInputTokens":0,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.0001244,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":0,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"},"claude-sonnet-5-5":{"inputTokens":4,"outputTokens":14,"cacheReadInputTokens":52987,"cacheCreationInputTokens":22309,"webSearchRequests":0,"costUSD":0.09468270000000001,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":0,"canonicalModel":"claude-sonnet-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":2,"subtype":"success","api_error_status":null,"result":"PINEAPPLE","ttft_ms":1716,"type":"result","duration_ms":3339,"uuid":"bdd6d87e-8a22-4936-9e61-9f3347fbd26c","ttft_stream_ms":1715,"time_to_request_ms":24,"first_content_frame_ms":1715,"queued_turn_count":0,"result_index":0}} +{"dir":"recv","msg":{"type":"system","subtype":"session_state_changed","state":"idle","uuid":"7c6218c1-6073-4534-95bf-8cd35f22fbc6","session_id":"3e4f2d36-4639-4ae0-a7b4-f02ca6d1ad1c"}} diff --git a/internal/server/server.go b/internal/server/server.go index 4a91ecd..2373862 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -54,7 +54,7 @@ func New(dataDir string, spa fs.FS) (*Server, error) { return nil, err } - s.agents = agents.New(s.store) + s.agents = agents.New(s.store, agents.DefaultLimits) err = s.agents.Recover(context.Background()) if err != nil { s.store.Close() @@ -325,6 +325,12 @@ func (s *Server) GetStatus(w http.ResponseWriter, r *http.Request) { return } + usage, err := s.agents.Usage(r.Context()) + if err != nil { + writeStoreError(w, err) + return + } + dbBytes := int64(0) for _, suffix := range []string{"", "-wal", "-shm"} { fi, err := os.Stat(s.store.Path() + suffix) @@ -341,9 +347,36 @@ func (s *Server) GetStatus(w http.ResponseWriter, r *http.Request) { ActiveTasks: c.ActiveTasks, SseClients: s.hub.count(), LiveAgents: s.agents.LiveCount(), + RunningTurns: usage.Running, + QueuedTurns: usage.Queued, + RateLimits: usage.RateLimits, }) } +func (s *Server) ListTaskEvents(w http.ResponseWriter, r *http.Request, taskID string, params api.ListTaskEventsParams) { + _, err := s.store.GetTask(r.Context(), taskID) + if err != nil { + writeStoreError(w, err) + return + } + + after := int64(0) + if params.After != nil { + after = *params.After + } + limit := 500 + if params.Limit != nil { + limit = *params.Limit + } + + events, err := s.store.TaskEvents(r.Context(), taskID, after, limit) + if err != nil { + writeStoreError(w, err) + return + } + writeJSON(w, http.StatusOK, events) +} + func (s *Server) ListSessions(w http.ResponseWriter, r *http.Request, taskID string) { _, err := s.store.GetTask(r.Context(), taskID) if err != nil { diff --git a/internal/server/server_test.go b/internal/server/server_test.go index 04aee16..46b60d6 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -18,6 +18,7 @@ import ( "g.fc.run/misc/byre/internal/api" "g.fc.run/misc/byre/internal/server" + "g.fc.run/misc/byre/web" ) const login = "alice@example.com" @@ -105,6 +106,47 @@ func TestIdentityRequired(t *testing.T) { } } +func TestServesEmbeddedSPA(t *testing.T) { + srv, err := server.New(t.TempDir(), web.FS) + if err != nil { + t.Fatalf("new server: %v", err) + } + t.Cleanup(func() { srv.Close() }) + hs := httptest.NewServer(srv.Handler()) + t.Cleanup(hs.Close) + + for path, contentType := range map[string]string{ + "/": "text/html", + "/style.css": "text/css", + "/dist/app.js": "text/javascript", + "/dist/app.js.map": "", + "/src/app.ts": "", + "/dist/inbox.js": "text/javascript", + "/dist/api.gen.js": "text/javascript", + "/dist/tasks.js": "text/javascript", + "/dist/navbar.js": "text/javascript", + "/dist/panes.js": "text/javascript", + "/dist/task.js": "text/javascript", + "/dist/state.js": "text/javascript", + "/dist/dom.js": "text/javascript", + "/dist/api.js": "text/javascript", + "/fonts/atkinson-hyperlegible-mono-latin-wght-normal.woff2": "font/woff2", + "/fonts/LICENSE": "", + } { + resp, err := http.Get(hs.URL + path) + if err != nil { + t.Fatalf("get %s: %v", path, err) + } + resp.Body.Close() + if resp.StatusCode != http.StatusOK { + t.Fatalf("get %s: %d", path, resp.StatusCode) + } + if contentType != "" && !strings.HasPrefix(resp.Header.Get("Content-Type"), contentType) { + t.Fatalf("get %s: content type %q, want %s", path, resp.Header.Get("Content-Type"), contentType) + } + } +} + func TestMe(t *testing.T) { h := newHarness(t) diff --git a/internal/store/queue.go b/internal/store/queue.go new file mode 100644 index 0000000..f29625a --- /dev/null +++ b/internal/store/queue.go @@ -0,0 +1,99 @@ +package store + +import ( + "context" + "database/sql" + "errors" + "time" + + "g.fc.run/misc/byre/internal/api" +) + +type QueuedTurn struct { + SessionID string + Vendor api.AgentSessionVendor + Actor string + Text string + CreatedAt time.Time +} + +func (s *Store) SessionStarted(ctx context.Context, sessionID string) (bool, error) { + started := false + err := s.db.QueryRowContext(ctx, `SELECT EXISTS (SELECT 1 FROM events WHERE agent_session_id = ? AND kind = 'user.message')`, sessionID).Scan(&started) + return started, err +} + +func (s *Store) CountRunning(ctx context.Context) (map[api.AgentSessionVendor]int, error) { + rows, err := s.db.QueryContext(ctx, `SELECT vendor, COUNT(*) FROM agent_sessions WHERE state = ? GROUP BY vendor`, string(api.AgentSessionStateRunning)) + if err != nil { + return nil, err + } + defer rows.Close() + + counts := map[api.AgentSessionVendor]int{} + for rows.Next() { + vendor := "" + n := 0 + err = rows.Scan(&vendor, &n) + if err != nil { + return nil, err + } + counts[api.AgentSessionVendor(vendor)] = n + } + return counts, rows.Err() +} + +func (s *Store) EnqueueTurn(ctx context.Context, q QueuedTurn) error { + return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error { + _, err := tx.ExecContext(ctx, `INSERT INTO turn_queue (session_id, actor, text, created_at) VALUES (?, ?, ?, ?)`, + q.SessionID, q.Actor, q.Text, formatTime(q.CreatedAt)) + return err + }) +} + +func (s *Store) QueuedTurns(ctx context.Context) ([]QueuedTurn, error) { + rows, err := s.db.QueryContext(ctx, ` + SELECT q.session_id, s.vendor, q.actor, q.text, q.created_at + FROM turn_queue q JOIN agent_sessions s ON s.id = q.session_id + ORDER BY q.created_at + `) + if err != nil { + return nil, err + } + defer rows.Close() + + turns := []QueuedTurn{} + for rows.Next() { + q := QueuedTurn{} + vendor := "" + createdAt := "" + err = rows.Scan(&q.SessionID, &vendor, &q.Actor, &q.Text, &createdAt) + if err != nil { + return nil, err + } + q.Vendor = api.AgentSessionVendor(vendor) + q.CreatedAt, err = parseTime(createdAt) + if err != nil { + return nil, err + } + turns = append(turns, q) + } + return turns, rows.Err() +} + +func (s *Store) DeleteQueuedTurn(ctx context.Context, sessionID string) error { + return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error { + res, err := tx.ExecContext(ctx, `DELETE FROM turn_queue WHERE session_id = ?`, sessionID) + if err != nil { + return err + } + n, err := res.RowsAffected() + if err != nil { + return err + } + if n == 0 { + return errors.Join(ErrNotFound, errors.New("no queued turn for session "+sessionID)) + } + return nil + }) +} diff --git a/internal/store/store.go b/internal/store/store.go index bae7758..42c7015 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -113,6 +113,15 @@ CREATE TABLE inbox_responses ( ); CREATE INDEX inbox_responses_item ON inbox_responses(item_id, created_at); +`, ` +CREATE INDEX events_task ON events(task_id, seq); +`, ` +CREATE TABLE turn_queue ( + session_id TEXT PRIMARY KEY REFERENCES agent_sessions(id), + actor TEXT NOT NULL, + text TEXT NOT NULL, + created_at TEXT NOT NULL +); `} type Store struct { @@ -335,6 +344,21 @@ func (s *Store) EventsAfter(ctx context.Context, after int64, limit int) ([]api. if err != nil { return nil, err } + return scanEvents(rows) +} + +func (s *Store) TaskEvents(ctx context.Context, taskID string, after int64, limit int) ([]api.Event, error) { + rows, err := s.db.QueryContext(ctx, ` + SELECT seq, kind, project_id, task_id, agent_session_id, actor, payload, created_at + FROM events WHERE task_id = ? AND seq > ? ORDER BY seq LIMIT ? + `, taskID, after, limit) + if err != nil { + return nil, err + } + return scanEvents(rows) +} + +func scanEvents(rows *sql.Rows) ([]api.Event, error) { defer rows.Close() events := []api.Event{} diff --git a/web/dist/api.gen.js b/web/dist/api.gen.js new file mode 100644 index 0000000..ca5314f --- /dev/null +++ b/web/dist/api.gen.js @@ -0,0 +1,6 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ +export {}; +//# sourceMappingURL=api.gen.js.map \ No newline at end of file diff --git a/web/dist/api.gen.js.map b/web/dist/api.gen.js.map new file mode 100644 index 0000000..53fb788 --- /dev/null +++ b/web/dist/api.gen.js.map @@ -0,0 +1 @@ +{"version":3,"file":"api.gen.js","sourceRoot":"","sources":["../src/api.gen.ts"],"names":[],"mappings":"AAAA;;;GAGG"} \ No newline at end of file diff --git a/web/dist/api.js b/web/dist/api.js new file mode 100644 index 0000000..b750d5d --- /dev/null +++ b/web/dist/api.js @@ -0,0 +1,48 @@ +export class ApiError extends Error { + status; + constructor(status, message) { + super(message); + this.status = status; + } +} +export async function request(method, path, opts = {}) { + const pathParams = (opts.path ?? {}); + let url = path.replace(/\{(\w+)\}/g, (_, key) => { + const value = pathParams[key]; + if (value === undefined) { + throw new Error(`missing path parameter ${key} for ${path}`); + } + return encodeURIComponent(String(value)); + }); + const query = new URLSearchParams(); + for (const [key, value] of Object.entries((opts.query ?? {}))) { + if (value !== undefined) { + query.set(key, String(value)); + } + } + if (query.size > 0) { + url += `?${query}`; + } + const init = { method: method.toUpperCase() }; + if (opts.body !== undefined) { + init.headers = { "Content-Type": "application/json" }; + init.body = JSON.stringify(opts.body); + } + const res = await fetch(url, init); + const data = await res.json(); + if (!res.ok) { + throw new ApiError(res.status, data.message); + } + return data; +} +export const get = (path, opts) => request("get", path, opts); +export const post = (path, opts) => request("post", path, opts); +export const put = (path, opts) => request("put", path, opts); +export function subscribe(after, onEvent, onState) { + const source = new EventSource(`/api/events?after=${after}`); + source.onopen = () => onState(true); + source.onerror = () => onState(false); + source.onmessage = (msg) => onEvent(JSON.parse(msg.data)); + return source; +} +//# sourceMappingURL=api.js.map \ No newline at end of file diff --git a/web/dist/api.js.map b/web/dist/api.js.map new file mode 100644 index 0000000..03cc9f1 --- /dev/null +++ b/web/dist/api.js.map @@ -0,0 +1 @@ +{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAkCA,MAAM,OAAO,QAAS,SAAQ,KAAK;IACZ;IAArB,YAAqB,MAAc,EAAE,OAAe;QAClD,KAAK,CAAC,OAAO,CAAC,CAAC;QADI,WAAM,GAAN,MAAM,CAAQ;IAEnC,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,MAAS,EACT,IAAO,EACP,OAA0B,EAAE;IAE5B,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAA4B,CAAC;IAChE,IAAI,GAAG,GAAW,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,GAAW,EAAE,EAAE;QAC9D,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,QAAQ,IAAI,EAAE,CAAC,CAAC;QAC/D,CAAC;QACD,OAAO,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;IACpC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAA4B,CAAC,EAAE,CAAC;QACzF,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QACnB,GAAG,IAAI,IAAI,KAAK,EAAE,CAAC;IACrB,CAAC;IAED,MAAM,IAAI,GAAgB,EAAE,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;IAC3D,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO,GAAG,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;QACtD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAG,IAAyB,CAAC,OAAO,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,IAAwB,CAAC;AAClC,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAG,CAA6B,IAAO,EAAE,IAA4B,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AACrH,MAAM,CAAC,MAAM,IAAI,GAAG,CAA8B,IAAO,EAAE,IAA6B,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AACzH,MAAM,CAAC,MAAM,GAAG,GAAG,CAA6B,IAAO,EAAE,IAA4B,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAErH,MAAM,UAAU,SAAS,CAAC,KAAa,EAAE,OAAgC,EAAE,OAAqC;IAC9G,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,qBAAqB,KAAK,EAAE,CAAC,CAAC;IAC7D,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,CAAC,SAAS,GAAG,CAAC,GAAyB,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAc,CAAC,CAAC;IAC7F,OAAO,MAAM,CAAC;AAChB,CAAC"} \ No newline at end of file diff --git a/web/dist/app.js b/web/dist/app.js new file mode 100644 index 0000000..9ee05cf --- /dev/null +++ b/web/dist/app.js @@ -0,0 +1,60 @@ +import { get, put, subscribe } from "./api.js"; +import { errorText, h, replace } from "./dom.js"; +import { mountNavbar } from "./navbar.js"; +import { Pane } from "./panes.js"; +import { handleEvent, loadInbox, loadMe, loadProjects, notify, on, selectTask, state } from "./state.js"; +function identityBanner(root) { + const render = () => { + const me = state.me; + if (!me || (me.git_name && me.git_email)) { + replace(root); + root.hidden = true; + return; + } + const name = h("input", { placeholder: "Git name", value: me.display_name, required: true }); + const email = h("input", { type: "email", placeholder: "Git email", required: true }); + const form = h("form", { class: "inline-form" }, "Set the identity agents commit as: ", name, email, h("button", { type: "submit" }, "Save")); + form.onsubmit = async (e) => { + e.preventDefault(); + try { + state.me = await put("/api/me", { body: { git_name: name.value, git_email: email.value } }); + render(); + } + catch (err) { + alert(errorText(err)); + } + }; + root.hidden = false; + replace(root, form); + }; + on("me", render); + render(); +} +function route() { + const match = /^#\/task\/(\w+)$/.exec(location.hash); + const taskId = match?.[1]; + if (taskId !== state.selectedTaskId) { + void selectTask(taskId); + } +} +async function main() { + const navbar = h("header", { class: "navbar" }); + const banner = h("div", { class: "banner", hidden: true }); + const left = new Pane("left", "transcript"); + const right = new Pane("right", "inbox"); + replace(document.body, navbar, banner, h("div", { class: "panes" }, left.el, right.el)); + mountNavbar(navbar, () => right.show("inbox")); + identityBanner(banner); + const status = await get("/api/admin/status"); + await Promise.all([loadMe(), loadProjects(), loadInbox()]); + subscribe(status.last_event_seq, handleEvent, (connected) => { + state.connected = connected; + notify("connection"); + }); + addEventListener("hashchange", route); + route(); +} +main().catch((err) => { + replace(document.body, h("pre", { class: "fatal" }, `byre failed to start: ${errorText(err)}`)); +}); +//# sourceMappingURL=app.js.map \ No newline at end of file diff --git a/web/dist/app.js.map b/web/dist/app.js.map new file mode 100644 index 0000000..725bf15 --- /dev/null +++ b/web/dist/app.js.map @@ -0,0 +1 @@ +{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/web/dist/dom.js b/web/dist/dom.js new file mode 100644 index 0000000..c317379 --- /dev/null +++ b/web/dist/dom.js @@ -0,0 +1,45 @@ +export function h(tag, props = {}, ...children) { + const el = document.createElement(tag); + const { class: className, style, data, ...rest } = props; + if (className) { + el.className = className; + } + if (style) { + el.setAttribute("style", style); + } + for (const [key, value] of Object.entries(data ?? {})) { + el.dataset[key] = value; + } + Object.assign(el, rest); + append(el, ...children); + return el; +} +export function append(el, ...children) { + for (const child of children) { + if (child === null || child === undefined || child === false) { + continue; + } + el.appendChild(typeof child === "object" ? child : document.createTextNode(String(child))); + } +} +export function replace(el, ...children) { + el.replaceChildren(); + append(el, ...children); +} +export function ago(iso) { + const seconds = Math.round((Date.now() - Date.parse(iso)) / 1000); + if (seconds < 60) { + return "just now"; + } + if (seconds < 3600) { + return `${Math.floor(seconds / 60)}m ago`; + } + if (seconds < 86400) { + return `${Math.floor(seconds / 3600)}h ago`; + } + return `${Math.floor(seconds / 86400)}d ago`; +} +export function errorText(err) { + return err instanceof Error ? err.message : String(err); +} +//# sourceMappingURL=dom.js.map \ No newline at end of file diff --git a/web/dist/dom.js.map b/web/dist/dom.js.map new file mode 100644 index 0000000..9880b59 --- /dev/null +++ b/web/dist/dom.js.map @@ -0,0 +1 @@ +{"version":3,"file":"dom.js","sourceRoot":"","sources":["../src/dom.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,CAAC,CAAwC,GAAM,EAAE,QAAkB,EAAE,EAAE,GAAG,QAAiB;IACzG,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;IAEzD,IAAI,SAAS,EAAE,CAAC;QACd,EAAE,CAAC,SAAS,GAAG,SAAS,CAAC;IAC3B,CAAC;IACD,IAAI,KAAK,EAAE,CAAC;QACV,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;QACtD,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC1B,CAAC;IACD,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAExB,MAAM,CAAC,EAAE,EAAE,GAAG,QAAQ,CAAC,CAAC;IACxB,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,EAAQ,EAAE,GAAG,QAAiB;IACnD,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;YAC7D,SAAS;QACX,CAAC;QACD,EAAE,CAAC,WAAW,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7F,CAAC;AACH,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,EAAW,EAAE,GAAG,QAAiB;IACvD,EAAE,CAAC,eAAe,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,EAAE,GAAG,QAAQ,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,GAAW;IAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAClE,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;QACjB,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,IAAI,OAAO,GAAG,IAAI,EAAE,CAAC;QACnB,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC;IAC5C,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,EAAE,CAAC;QACpB,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAC9C,CAAC;IACD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAY;IACpC,OAAO,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC1D,CAAC"} \ No newline at end of file diff --git a/web/dist/inbox.js b/web/dist/inbox.js new file mode 100644 index 0000000..40c6b5b --- /dev/null +++ b/web/dist/inbox.js @@ -0,0 +1,250 @@ +import { post } from "./api.js"; +import { ago, errorText, h, replace } from "./dom.js"; +import { findTask, onEach, state } from "./state.js"; +import { reportCard } from "./task.js"; +const drafts = new Map(); +const modes = new Map(); +let scope = "all"; +export function mountInbox(root) { + const tabs = h("nav", { class: "scope-tabs" }); + const list = h("div", { class: "inbox-list" }); + const submitBar = h("div", { class: "submit-bar" }); + const cards = new Map(); + const visible = () => { + const task = state.selectedTaskId ? findTask(state.selectedTaskId) : undefined; + if (scope === "task" && task) { + return state.inbox.filter((i) => i.task_id === task.id); + } + if (scope === "project" && task) { + return state.inbox.filter((i) => i.project_id === task.project_id); + } + return state.inbox; + }; + const renderTabs = () => { + const hasTask = state.selectedTaskId !== undefined; + const tab = (s, label) => { + const b = h("button", { class: s === scope ? "active" : "", disabled: s !== "all" && !hasTask }, label); + b.onclick = () => { + scope = s; + render(); + }; + return b; + }; + replace(tabs, tab("all", "All"), tab("project", "This project"), tab("task", "This task")); + }; + const renderSubmit = () => { + const ready = [...drafts.values()].filter((d) => state.inbox.some((i) => i.id === d.item_id)); + if (ready.length === 0) { + replace(submitBar); + return; + } + const button = h("button", { class: "primary" }, `Submit ${ready.length} ${ready.length === 1 ? "response" : "responses"}`); + button.onclick = async () => { + button.disabled = true; + try { + await post("/api/inbox/responses", { body: { responses: ready } }); + for (const d of ready) { + drafts.delete(d.item_id); + modes.delete(d.item_id); + } + } + catch (err) { + alert(errorText(err)); + } + button.disabled = false; + render(); + }; + replace(submitBar, button); + }; + const render = () => { + if (scope !== "all" && !state.selectedTaskId) { + scope = "all"; + } + renderTabs(); + const items = visible(); + const keep = new Set(items.map((i) => i.id)); + for (const id of cards.keys()) { + if (!keep.has(id)) { + cards.delete(id); + } + } + for (const id of drafts.keys()) { + if (!state.inbox.some((i) => i.id === id)) { + drafts.delete(id); + modes.delete(id); + } + } + const els = items.map((item) => { + const sig = JSON.stringify(item); + const cached = cards.get(item.id); + if (cached && cached.sig === sig) { + return cached.el; + } + const el = card(item, () => { + renderSubmit(); + }); + cards.set(item.id, { sig, el }); + return el; + }); + replace(list, ...(els.length > 0 ? els : [h("div", { class: "empty" }, "Nothing waiting on you.")])); + renderSubmit(); + }; + const off = onEach(["inbox", "selection", "tasks"], render); + replace(root, h("div", { class: "inbox-view" }, tabs, submitBar, list)); + render(); + return off; +} +function card(item, changed) { + const task = findTask(item.task_id); + const p = item.payload; + const body = h("div", { class: "card-body" }); + const actions = h("div", { class: "card-actions" }); + const el = h("article", { class: `card ${item.kind} ${item.priority}` }, h("header", {}, h("span", { class: `priority ${item.priority}` }, item.priority), h("span", { class: "kind" }, item.kind.replace("_", " ")), h("a", { href: `#/task/${item.task_id}`, class: "card-task" }, task?.title ?? item.task_id), h("time", { dateTime: item.created_at, title: item.created_at }, ago(item.created_at))), body, thread(item), actions); + const allowed = item.kind === "question" ? ["option", "free_form", "question", "redirect", "snooze"] : ["dismiss", "redirect", "snooze"]; + const labels = { + option: "Pick", + free_form: "Other answer", + question: "Ask back", + redirect: "Redirect", + snooze: "Snooze", + dismiss: "Dismiss", + }; + const renderActions = () => { + const mode = modes.get(item.id) ?? allowed[0]; + const tabs = h("div", { class: "modes" }, ...allowed.map((m) => { + const b = h("button", { type: "button", class: m === mode ? "active" : "" }, labels[m]); + b.onclick = () => { + modes.set(item.id, m); + drafts.delete(item.id); + if (m === "dismiss") { + drafts.set(item.id, { item_id: item.id, type: "dismiss" }); + } + renderActions(); + changed(); + }; + return b; + })); + replace(actions, tabs, modeInput(item, mode, changed)); + }; + switch (item.kind) { + case "question": + replace(body, h("p", { class: "question" }, String(p["question"])), h("p", { class: "why" }, h("strong", {}, "Blocked on: "), String(p["why_blocked"])), attachments(p["attachments"] ?? [])); + break; + case "report": + replace(body, reportCard(p)); + break; + case "session_error": + replace(body, h("p", { class: "error-text" }, String(p["error"]))); + break; + case "rate_limited": { + const limit = (p["rate_limit"] ?? {}); + const resets = typeof limit["resetsAt"] === "number" ? new Date(limit["resetsAt"] * 1000).toLocaleString() : "unknown"; + replace(body, h("p", { class: "error-text" }, String(p["error"])), h("p", {}, `${p["vendor"]} ${limit["rateLimitType"] ?? ""} limit resets ${resets}`)); + break; + } + } + renderActions(); + return el; +} +function modeInput(item, mode, changed) { + const draft = drafts.get(item.id); + const set = (d) => { + if (d) { + drafts.set(item.id, d); + } + else { + drafts.delete(item.id); + } + changed(); + }; + switch (mode) { + case "option": { + const p = item.payload; + const options = p["options"] ?? []; + const note = h("input", { placeholder: "Optional note", value: draft?.text ?? "" }); + const choose = (option) => set({ item_id: item.id, type: "option", option, text: note.value || undefined }); + note.oninput = () => { + const current = drafts.get(item.id); + if (current?.type === "option" && current.option) { + choose(current.option); + } + }; + return h("div", { class: "options" }, ...options.map((option) => { + const radio = h("input", { type: "radio", name: `opt-${item.id}`, checked: drafts.get(item.id)?.option === option }); + radio.onchange = () => choose(option); + return h("label", { class: "option" }, radio, " ", option, option === p["recommended_option"] && h("span", { class: "recommended" }, "recommended")); + }), note); + } + case "free_form": + case "question": + case "redirect": { + const placeholder = { + free_form: "Your answer", + question: "Your question back to the agent", + redirect: "New instructions for the agent", + }[mode]; + const text = h("textarea", { rows: 3, placeholder, value: draft?.type === mode ? (draft.text ?? "") : "" }); + text.oninput = () => set(text.value.trim() ? { item_id: item.id, type: mode, text: text.value } : undefined); + return text; + } + case "snooze": { + const presets = [ + ["1 hour", () => new Date(Date.now() + 3600_000)], + ["4 hours", () => new Date(Date.now() + 4 * 3600_000)], + [ + "Tomorrow 9am", + () => { + const d = new Date(); + d.setDate(d.getDate() + 1); + d.setHours(9, 0, 0, 0); + return d; + }, + ], + ]; + return h("div", { class: "snooze" }, ...presets.map(([label, when]) => { + const radio = h("input", { type: "radio", name: `snooze-${item.id}` }); + radio.onchange = () => set({ item_id: item.id, type: "snooze", snooze_until: when().toISOString() }); + return h("label", {}, radio, " ", label); + })); + } + case "dismiss": + return h("p", { class: "hint" }, "Will be dismissed when you submit."); + } +} +function thread(item) { + if (item.responses.length === 0) { + return null; + } + return h("ul", { class: "thread" }, ...item.responses.map((r) => h("li", {}, h("strong", {}, r.user), ` ${r.type.replace("_", " ")}`, r.option ? `: ${r.option}` : "", r.text ? ` — ${r.text}` : ""))); +} +function attachments(list) { + if (list.length === 0) { + return null; + } + return h("ul", { class: "attachments" }, ...list.map((a) => { + const caption = a.caption ? h("div", { class: "caption" }, a.caption) : null; + switch (a.kind) { + case "file": { + const lines = a.start_line ? `:${a.start_line}${a.end_line ? `-${a.end_line}` : ""}` : ""; + return h("li", {}, h("code", {}, `${a.path ?? ""}${lines}`), caption); + } + case "diff": + return h("li", {}, "diff ", h("code", {}, a.ref ?? ""), caption); + case "image": + return h("li", {}, a.image?.startsWith("data:image/") ? h("img", { src: a.image, alt: a.caption ?? "" }) : null, caption); + case "diagram": + return h("li", {}, h("pre", { class: "mermaid-source" }, a.mermaid ?? ""), caption); + case "task": + return h("li", {}, h("a", { href: `#/task/${a.task_id}` }, "task ", a.task_id ?? ""), caption); + case "snapshot": + return h("li", {}, "snapshot ", h("code", {}, a.snapshot_id ?? ""), caption); + case "url": + return h("li", {}, safeLink(a.url ?? ""), caption); + } + })); +} +function safeLink(url) { + const ok = /^https?:\/\//i.test(url); + return ok ? h("a", { href: url, target: "_blank", rel: "noopener noreferrer" }, url) : h("code", {}, url); +} +//# sourceMappingURL=inbox.js.map \ No newline at end of file diff --git a/web/dist/inbox.js.map b/web/dist/inbox.js.map new file mode 100644 index 0000000..0cbb347 --- /dev/null +++ b/web/dist/inbox.js.map @@ -0,0 +1 @@ +{"version":3,"file":"inbox.js","sourceRoot":"","sources":["../src/inbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAA6D,MAAM,UAAU,CAAC;AAC3F,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAKvC,MAAM,MAAM,GAAG,IAAI,GAAG,EAA+B,CAAC;AACtD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAgB,CAAC;AACtC,IAAI,KAAK,GAAU,KAAK,CAAC;AAEzB,MAAM,UAAU,UAAU,CAAC,IAAiB;IAC1C,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA4C,CAAC;IAElE,MAAM,OAAO,GAAG,GAAgB,EAAE;QAChC,MAAM,IAAI,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC/E,IAAI,KAAK,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC;YAC7B,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,KAAK,KAAK,SAAS,IAAI,IAAI,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,IAAI,CAAC,UAAU,CAAC,CAAC;QACrE,CAAC;QACD,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,GAAG,EAAE;QACtB,MAAM,OAAO,GAAG,KAAK,CAAC,cAAc,KAAK,SAAS,CAAC;QACnD,MAAM,GAAG,GAAG,CAAC,CAAQ,EAAE,KAAa,EAAE,EAAE;YACtC,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;YACxG,CAAC,CAAC,OAAO,GAAG,GAAG,EAAE;gBACf,KAAK,GAAG,CAAC,CAAC;gBACV,MAAM,EAAE,CAAC;YACX,CAAC,CAAC;YACF,OAAO,CAAC,CAAC;QACX,CAAC,CAAC;QACF,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,GAAG,CAAC,SAAS,EAAE,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAC7F,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,GAAG,EAAE;QACxB,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAC9F,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,SAAS,CAAC,CAAC;YACnB,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,UAAU,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAC5H,MAAM,CAAC,OAAO,GAAG,KAAK,IAAI,EAAE;YAC1B,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC;YACvB,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,sBAAsB,EAAE,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;gBACnE,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;oBACtB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;oBACzB,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;YACxB,CAAC;YACD,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC;YACxB,MAAM,EAAE,CAAC;QACX,CAAC,CAAC;QACF,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC7B,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,GAAG,EAAE;QAClB,IAAI,KAAK,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;YAC7C,KAAK,GAAG,KAAK,CAAC;QAChB,CAAC;QACD,UAAU,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7C,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBAClB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;QACD,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;YAC/B,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;gBAC1C,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAClB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;QAED,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACjC,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAClC,IAAI,MAAM,IAAI,MAAM,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;gBACjC,OAAO,MAAM,CAAC,EAAE,CAAC;YACnB,CAAC;YACD,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE;gBACzB,YAAY,EAAE,CAAC;YACjB,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;YAChC,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,yBAAyB,CAAC,CAAC,CAAC,CAAC,CAAC;QACrG,YAAY,EAAE,CAAC;IACjB,CAAC,CAAC;IAEF,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;IAC5D,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;IACxE,MAAM,EAAE,CAAC;IACT,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,IAAI,CAAC,IAAe,EAAE,OAAmB;IAChD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpC,MAAM,CAAC,GAAG,IAAI,CAAC,OAAkC,CAAC;IAClD,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;IACpD,MAAM,EAAE,GAAG,CAAC,CACV,SAAS,EACT,EAAE,KAAK,EAAE,QAAQ,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE,EAC/C,CAAC,CACC,QAAQ,EACR,EAAE,EACF,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,YAAY,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,EAChE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,EACzD,CAAC,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,UAAU,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,EAC3F,CAAC,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CACvF,EACD,IAAI,EACJ,MAAM,CAAC,IAAI,CAAC,EACZ,OAAO,CACR,CAAC;IAEF,MAAM,OAAO,GACX,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC3H,MAAM,MAAM,GAAyB;QACnC,MAAM,EAAE,MAAM;QACd,SAAS,EAAE,cAAc;QACzB,QAAQ,EAAE,UAAU;QACpB,QAAQ,EAAE,UAAU;QACpB,MAAM,EAAE,QAAQ;QAChB,OAAO,EAAE,SAAS;KACnB,CAAC;IAEF,MAAM,aAAa,GAAG,GAAG,EAAE;QACzB,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAE,CAAC;QAC/C,MAAM,IAAI,GAAG,CAAC,CACZ,KAAK,EACL,EAAE,KAAK,EAAE,OAAO,EAAE,EAClB,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACnB,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YACxF,CAAC,CAAC,OAAO,GAAG,GAAG,EAAE;gBACf,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;gBACtB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACvB,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;oBACpB,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;gBAC7D,CAAC;gBACD,aAAa,EAAE,CAAC;gBAChB,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC;YACF,OAAO,CAAC,CAAC;QACX,CAAC,CAAC,CACH,CAAC;QACF,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACzD,CAAC,CAAC;IAEF,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,UAAU;YACb,OAAO,CACL,IAAI,EACJ,CAAC,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EACpD,CAAC,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EACnF,WAAW,CAAE,CAAC,CAAC,aAAa,CAA8B,IAAI,EAAE,CAAC,CAClE,CAAC;YACF,MAAM;QACR,KAAK,QAAQ;YACX,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7B,MAAM;QACR,KAAK,eAAe;YAClB,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YACnE,MAAM;QACR,KAAK,cAAc,CAAC,CAAC,CAAC;YACpB,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,CAA4B,CAAC;YACjE,MAAM,MAAM,GAAG,OAAO,KAAK,CAAC,UAAU,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;YACvH,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,eAAe,CAAC,IAAI,EAAE,iBAAiB,MAAM,EAAE,CAAC,CAAC,CAAC;YACxJ,MAAM;QACR,CAAC;IACH,CAAC;IAED,aAAa,EAAE,CAAC;IAChB,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,SAAS,CAAC,IAAe,EAAE,IAAU,EAAE,OAAmB;IACjE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,GAAG,GAAG,CAAC,CAAkC,EAAE,EAAE;QACjD,IAAI,CAAC,EAAE,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACzB,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC;IAEF,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,CAAC,GAAG,IAAI,CAAC,OAAkC,CAAC;YAClD,MAAM,OAAO,GAAI,CAAC,CAAC,SAAS,CAAc,IAAI,EAAE,CAAC;YACjD,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;YACpF,MAAM,MAAM,GAAG,CAAC,MAAc,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,IAAI,SAAS,EAAE,CAAC,CAAC;YACpH,IAAI,CAAC,OAAO,GAAG,GAAG,EAAE;gBAClB,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACpC,IAAI,OAAO,EAAE,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;oBACjD,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;gBACzB,CAAC;YACH,CAAC,CAAC;YACF,OAAO,CAAC,CACN,KAAK,EACL,EAAE,KAAK,EAAE,SAAS,EAAE,EACpB,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;gBACxB,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC,CAAC;gBACrH,KAAK,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACtC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC;YACvJ,CAAC,CAAC,EACF,IAAI,CACL,CAAC;QACJ,CAAC;QAED,KAAK,WAAW,CAAC;QACjB,KAAK,UAAU,CAAC;QAChB,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,WAAW,GAAG;gBAClB,SAAS,EAAE,aAAa;gBACxB,QAAQ,EAAE,iCAAiC;gBAC3C,QAAQ,EAAE,gCAAgC;aAC3C,CAAC,IAAI,CAAC,CAAC;YACR,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC5G,IAAI,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YAC7G,OAAO,IAAI,CAAC;QACd,CAAC;QAED,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,OAAO,GAA2B;gBACtC,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC;gBACjD,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC;gBACtD;oBACE,cAAc;oBACd,GAAG,EAAE;wBACH,MAAM,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;wBACrB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;wBAC3B,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;wBACvB,OAAO,CAAC,CAAC;oBACX,CAAC;iBACF;aACF,CAAC;YACF,OAAO,CAAC,CACN,KAAK,EACL,EAAE,KAAK,EAAE,QAAQ,EAAE,EACnB,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE;gBAC/B,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;gBACvE,KAAK,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;gBACrG,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;YAC3C,CAAC,CAAC,CACH,CAAC;QACJ,CAAC;QAED,KAAK,SAAS;YACZ,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,oCAAoC,CAAC,CAAC;IAC3E,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,IAAe;IAC7B,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,CAAC,CACN,IAAI,EACJ,EAAE,KAAK,EAAE,QAAQ,EAAE,EACnB,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC1B,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CACpI,CACF,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,IAAkB;IACrC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,CAAC,CACN,IAAI,EACJ,EAAE,KAAK,EAAE,aAAa,EAAE,EACxB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAChB,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7E,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;YACf,KAAK,MAAM,CAAC,CAAC,CAAC;gBACZ,MAAM,KAAK,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC1F,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,IAAI,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;YACxE,CAAC;YACD,KAAK,MAAM;gBACT,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;YACnE,KAAK,OAAO;gBACV,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC5H,KAAK,SAAS;gBACZ,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;YACtF,KAAK,MAAM;gBACT,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;YACjG,KAAK,UAAU;gBACb,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;YAC/E,KAAK,KAAK;gBACR,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;QACvD,CAAC;IACH,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,GAAW;IAC3B,MAAM,EAAE,GAAG,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,qBAAqB,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;AAC5G,CAAC"} \ No newline at end of file diff --git a/web/dist/navbar.js b/web/dist/navbar.js new file mode 100644 index 0000000..fe6789b --- /dev/null +++ b/web/dist/navbar.js @@ -0,0 +1,92 @@ +import { h, replace } from "./dom.js"; +import { activeTasks, findTask, onEach, state } from "./state.js"; +export function mountNavbar(root, showInbox) { + const crumb = h("button", { class: "crumb", title: "Switch task (⌘K)" }); + crumb.onclick = openSwitcher; + const inbox = h("button", { class: "inbox-count" }); + inbox.onclick = showInbox; + const conn = h("span", { class: "connection down", title: "connecting" }); + const me = h("span", { class: "me" }); + const render = () => { + const task = state.selectedTaskId ? findTask(state.selectedTaskId) : undefined; + const project = task ? state.projects.find((p) => p.id === task.project_id) : undefined; + replace(crumb, task ? `${project?.name ?? ""} ▸ ${task.title}` : "No task", " ▾"); + const blocked = state.inbox.filter((i) => i.priority === "blocked").length; + inbox.className = blocked > 0 ? "inbox-count urgent" : "inbox-count"; + replace(inbox, `Inbox ${state.inbox.length}`, blocked > 0 && h("span", { class: "blocked-count" }, ` · ${blocked} blocked`)); + conn.className = state.connected ? "connection up" : "connection down"; + conn.title = state.connected ? "live" : "reconnecting"; + replace(me, state.me?.display_name ?? ""); + }; + onEach(["me", "projects", "tasks", "inbox", "selection", "connection"], render); + replace(root, h("a", { href: "#/", class: "brand" }, "byre"), crumb, h("span", { class: "spacer" }), inbox, conn, me); + render(); + addEventListener("keydown", (e) => { + if (e.key === "k" && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + openSwitcher(); + } + }); +} +function openSwitcher() { + if (document.querySelector(".switcher")) { + return; + } + const input = h("input", { placeholder: "Go to task…", autocomplete: "off" }); + const list = h("ul", { class: "switcher-list" }); + const backdrop = h("div", { class: "switcher-backdrop" }); + const box = h("div", { class: "switcher" }, input, list); + let selected = 0; + let matches = []; + const close = () => { + backdrop.remove(); + box.remove(); + }; + const go = (taskId) => { + close(); + location.hash = `#/task/${taskId}`; + }; + const render = () => { + const q = input.value.trim().toLowerCase(); + matches = activeTasks().filter(({ project, task }) => `${project.name} ${task.title}`.toLowerCase().includes(q)); + selected = Math.min(selected, Math.max(matches.length - 1, 0)); + replace(list, ...matches.map(({ project, task }, i) => { + const li = h("li", { class: i === selected ? "selected" : "" }, h("span", { class: "project-name" }, project.name), " ", task.title); + li.onmousedown = (e) => { + e.preventDefault(); + go(task.id); + }; + return li; + }), matches.length === 0 && h("li", { class: "empty-row" }, "No matching tasks.")); + }; + input.oninput = () => { + selected = 0; + render(); + }; + input.onkeydown = (e) => { + if (e.key === "Escape") { + close(); + } + else if (e.key === "ArrowDown") { + e.preventDefault(); + selected = Math.min(selected + 1, matches.length - 1); + render(); + } + else if (e.key === "ArrowUp") { + e.preventDefault(); + selected = Math.max(selected - 1, 0); + render(); + } + else if (e.key === "Enter") { + const match = matches[selected]; + if (match) { + go(match.task.id); + } + } + }; + backdrop.onclick = close; + document.body.append(backdrop, box); + render(); + input.focus(); +} +//# sourceMappingURL=navbar.js.map \ No newline at end of file diff --git a/web/dist/navbar.js.map b/web/dist/navbar.js.map new file mode 100644 index 0000000..573038b --- /dev/null +++ b/web/dist/navbar.js.map @@ -0,0 +1 @@ +{"version":3,"file":"navbar.js","sourceRoot":"","sources":["../src/navbar.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/web/dist/panes.js b/web/dist/panes.js new file mode 100644 index 0000000..25f85a2 --- /dev/null +++ b/web/dist/panes.js @@ -0,0 +1,44 @@ +import { h, replace } from "./dom.js"; +import { mountInbox } from "./inbox.js"; +import { mountTranscript } from "./task.js"; +import { mountTasks } from "./tasks.js"; +const views = { + transcript: { label: "Transcript", mount: mountTranscript }, + tasks: { label: "Tasks", mount: mountTasks }, + inbox: { label: "Inbox", mount: mountInbox }, +}; +const order = ["transcript", "tasks", "inbox"]; +export class Pane { + key; + el; + tabs; + body; + view; + dispose = () => { }; + constructor(key, fallback) { + this.key = key; + this.tabs = h("nav", { class: "pane-tabs" }); + this.body = h("div", { class: "pane-body" }); + this.el = h("section", { class: "pane" }, this.tabs, this.body); + const saved = localStorage.getItem(this.storageKey()); + this.view = saved && saved in views ? saved : fallback; + this.show(this.view); + } + show(view) { + this.dispose(); + this.view = view; + localStorage.setItem(this.storageKey(), view); + replace(this.tabs, ...order.map((v) => { + const b = h("button", { class: v === view ? "active" : "" }, views[v].label); + b.onclick = () => this.show(v); + return b; + })); + const root = h("div", { class: `view ${view}` }); + replace(this.body, root); + this.dispose = views[view].mount(root); + } + storageKey() { + return `byre.pane.${this.key}`; + } +} +//# sourceMappingURL=panes.js.map \ No newline at end of file diff --git a/web/dist/panes.js.map b/web/dist/panes.js.map new file mode 100644 index 0000000..46a6cee --- /dev/null +++ b/web/dist/panes.js.map @@ -0,0 +1 @@ +{"version":3,"file":"panes.js","sourceRoot":"","sources":["../src/panes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIxC,MAAM,KAAK,GAA8E;IACvF,UAAU,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE;IAC3D,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE;IAC5C,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE;CAC7C,CAAC;AAEF,MAAM,KAAK,GAAW,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAEvD,MAAM,OAAO,IAAI;IAOc;IANpB,EAAE,CAAc;IACR,IAAI,CAAc;IAClB,IAAI,CAAc;IAC3B,IAAI,CAAO;IACX,OAAO,GAAe,GAAG,EAAE,GAAE,CAAC,CAAC;IAEvC,YAA6B,GAAW,EAAE,QAAc;QAA3B,QAAG,GAAH,GAAG,CAAQ;QACtC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAEhE,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,CAAgB,CAAC;QACrE,IAAI,CAAC,IAAI,GAAG,KAAK,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;QACvD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,IAAI,CAAC,IAAU;QACb,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,CAAC;QAE9C,OAAO,CACL,IAAI,CAAC,IAAI,EACT,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACjB,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YAC7E,CAAC,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC/B,OAAO,CAAC,CAAC;QACX,CAAC,CAAC,CACH,CAAC;QAEF,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,QAAQ,IAAI,EAAE,EAAE,CAAC,CAAC;QACjD,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAEO,UAAU;QAChB,OAAO,aAAa,IAAI,CAAC,GAAG,EAAE,CAAC;IACjC,CAAC;CACF"} \ No newline at end of file diff --git a/web/dist/state.js b/web/dist/state.js new file mode 100644 index 0000000..7b82822 --- /dev/null +++ b/web/dist/state.js @@ -0,0 +1,133 @@ +import { get } from "./api.js"; +export const state = { + me: undefined, + projects: [], + tasks: new Map(), + sessions: new Map(), + inbox: [], + selectedTaskId: undefined, + transcript: [], + lastTranscriptSeq: 0, + connected: false, +}; +const listeners = new Map(); +export function on(topic, fn) { + let set = listeners.get(topic); + if (!set) { + set = new Set(); + listeners.set(topic, set); + } + set.add(fn); + return () => set.delete(fn); +} +export function onEach(topics, fn) { + const offs = topics.map((t) => on(t, fn)); + return () => { + for (const off of offs) { + off(); + } + }; +} +export function activeTasks() { + return state.projects.flatMap((project) => (state.tasks.get(project.id) ?? []).filter((t) => t.status === "active").map((task) => ({ project, task }))); +} +export function notify(topic, detail) { + for (const fn of listeners.get(topic) ?? []) { + fn(detail); + } +} +export function findTask(id) { + for (const tasks of state.tasks.values()) { + const task = tasks.find((t) => t.id === id); + if (task) { + return task; + } + } + return undefined; +} +export function currentSession(taskId) { + return state.sessions.get(taskId)?.findLast((s) => s.state !== "completed") ?? state.sessions.get(taskId)?.at(-1); +} +export async function loadMe() { + state.me = await get("/api/me"); + notify("me"); +} +export async function loadProjects() { + state.projects = await get("/api/projects"); + await Promise.all(state.projects.map((p) => loadTasks(p.id))); + notify("projects"); +} +export async function loadTasks(projectId) { + const tasks = await get("/api/projects/{projectId}/tasks", { path: { projectId } }); + state.tasks.set(projectId, tasks); + await Promise.all(tasks.filter((t) => t.status === "active").map((t) => loadSessions(t.id))); + notify("tasks"); +} +export async function loadSessions(taskId) { + state.sessions.set(taskId, await get("/api/tasks/{taskId}/sessions", { path: { taskId } })); + notify("sessions", taskId); +} +export async function loadInbox() { + state.inbox = await get("/api/inbox"); + notify("inbox"); +} +export async function selectTask(taskId) { + state.selectedTaskId = taskId; + state.transcript = []; + state.lastTranscriptSeq = 0; + notify("selection"); + if (!taskId) { + return; + } + for (;;) { + const page = await get("/api/tasks/{taskId}/events", { path: { taskId }, query: { after: state.lastTranscriptSeq, limit: 1000 } }); + if (state.selectedTaskId !== taskId) { + return; + } + appendTranscript(page); + if (page.length < 1000) { + return; + } + } +} +function appendTranscript(events) { + const fresh = events.filter((ev) => ev.seq > state.lastTranscriptSeq); + if (fresh.length === 0) { + return; + } + state.transcript.push(...fresh); + state.lastTranscriptSeq = fresh.at(-1).seq; + notify("transcript", fresh); +} +export function handleEvent(ev) { + if (ev.task_id && ev.task_id === state.selectedTaskId) { + appendTranscript([ev]); + } + switch (ev.kind) { + case "user.updated": + if (ev.payload["login"] === state.me?.login) { + void loadMe(); + } + break; + case "project.created": + void loadProjects(); + break; + case "task.created": + case "task.completed": + if (ev.project_id) { + void loadTasks(ev.project_id); + } + break; + case "session.created": + case "session.state": + if (ev.task_id) { + void loadSessions(ev.task_id); + } + break; + case "inbox.created": + case "inbox.updated": + void loadInbox(); + break; + } +} +//# sourceMappingURL=state.js.map \ No newline at end of file diff --git a/web/dist/state.js.map b/web/dist/state.js.map new file mode 100644 index 0000000..730112c --- /dev/null +++ b/web/dist/state.js.map @@ -0,0 +1 @@ +{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAyF,MAAM,UAAU,CAAC;AAItH,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,EAAE,EAAE,SAA6B;IACjC,QAAQ,EAAE,EAAe;IACzB,KAAK,EAAE,IAAI,GAAG,EAAkB;IAChC,QAAQ,EAAE,IAAI,GAAG,EAA0B;IAC3C,KAAK,EAAE,EAAiB;IACxB,cAAc,EAAE,SAA+B;IAC/C,UAAU,EAAE,EAAiB;IAC7B,iBAAiB,EAAE,CAAC;IACpB,SAAS,EAAE,KAAK;CACjB,CAAC;AAEF,MAAM,SAAS,GAAG,IAAI,GAAG,EAA0C,CAAC;AAEpE,MAAM,UAAU,EAAE,CAAC,KAAY,EAAE,EAA8B;IAC7D,IAAI,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;QAChB,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC5B,CAAC;IACD,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACZ,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,MAAe,EAAE,EAA8B;IACpE,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC1C,OAAO,GAAG,EAAE;QACV,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,GAAG,EAAE,CAAC;QACR,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC1J,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,KAAY,EAAE,MAAgB;IACnD,KAAK,MAAM,EAAE,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAC5C,EAAE,CAAC,MAAM,CAAC,CAAC;IACb,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,EAAU;IACjC,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5C,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,WAAW,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACpH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM;IAC1B,KAAK,CAAC,EAAE,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,CAAC;IAChC,MAAM,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,KAAK,CAAC,QAAQ,GAAG,MAAM,GAAG,CAAC,eAAe,CAAC,CAAC;IAC5C,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9D,MAAM,CAAC,UAAU,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,SAAiB;IAC/C,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,iCAAiC,EAAE,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;IACpF,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAClC,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7F,MAAM,CAAC,OAAO,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAc;IAC/C,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,8BAA8B,EAAE,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;IAC5F,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,KAAK,CAAC,KAAK,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,CAAC;IACtC,MAAM,CAAC,OAAO,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAA0B;IACzD,KAAK,CAAC,cAAc,GAAG,MAAM,CAAC;IAC9B,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;IACtB,KAAK,CAAC,iBAAiB,GAAG,CAAC,CAAC;IAC5B,MAAM,CAAC,WAAW,CAAC,CAAC;IAEpB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO;IACT,CAAC;IAED,SAAS,CAAC;QACR,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,4BAA4B,EAAE,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,iBAAiB,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QACnI,IAAI,KAAK,CAAC,cAAc,KAAK,MAAM,EAAE,CAAC;YACpC,OAAO;QACT,CAAC;QACD,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACvB,IAAI,IAAI,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAmB;IAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACtE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO;IACT,CAAC;IACD,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;IAChC,KAAK,CAAC,iBAAiB,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,GAAG,CAAC;IAC5C,MAAM,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAAa;IACvC,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,CAAC,OAAO,KAAK,KAAK,CAAC,cAAc,EAAE,CAAC;QACtD,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACzB,CAAC;IAED,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;QAChB,KAAK,cAAc;YACjB,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC;gBAC5C,KAAK,MAAM,EAAE,CAAC;YAChB,CAAC;YACD,MAAM;QACR,KAAK,iBAAiB;YACpB,KAAK,YAAY,EAAE,CAAC;YACpB,MAAM;QACR,KAAK,cAAc,CAAC;QACpB,KAAK,gBAAgB;YACnB,IAAI,EAAE,CAAC,UAAU,EAAE,CAAC;gBAClB,KAAK,SAAS,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC;YAChC,CAAC;YACD,MAAM;QACR,KAAK,iBAAiB,CAAC;QACvB,KAAK,eAAe;YAClB,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;gBACf,KAAK,YAAY,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;YAChC,CAAC;YACD,MAAM;QACR,KAAK,eAAe,CAAC;QACrB,KAAK,eAAe;YAClB,KAAK,SAAS,EAAE,CAAC;YACjB,MAAM;IACV,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/web/dist/task.js b/web/dist/task.js new file mode 100644 index 0000000..f9bb93d --- /dev/null +++ b/web/dist/task.js @@ -0,0 +1,230 @@ +import { post } from "./api.js"; +import { ago, errorText, h, replace } from "./dom.js"; +import { currentSession, findTask, onEach, state } from "./state.js"; +const models = ["", "opus", "sonnet", "haiku"]; +export function mountTranscript(root) { + const head = h("header", { class: "task-header" }); + const transcript = h("div", { class: "transcript" }); + const composer = h("div", { class: "composer" }); + let tools = new Map(); + const renderHead = () => { + const task = state.selectedTaskId ? findTask(state.selectedTaskId) : undefined; + if (!task) { + replace(head); + return; + } + const sess = currentSession(task.id); + const complete = h("button", { class: "danger", title: "Remove the worktree and branch; history stays" }, "Complete task"); + complete.onclick = async () => { + if (!confirm(`Complete "${task.title}"? Its worktree and branch are removed; its history stays.`)) { + return; + } + try { + await post("/api/tasks/{taskId}/complete", { path: { taskId: task.id } }); + location.hash = "#/"; + } + catch (err) { + alert(errorText(err)); + } + }; + replace(head, h("div", { class: "meta" }, h("code", {}, task.branch), h("code", { class: "path" }, task.worktree_path)), h("div", { class: "head-actions" }, sess && h("span", { class: `state ${sess.state}` }, sess.state), complete)); + }; + const renderComposer = () => { + const task = state.selectedTaskId ? findTask(state.selectedTaskId) : undefined; + if (!task) { + replace(composer); + return; + } + const sess = currentSession(task.id); + if (!sess || sess.state === "completed") { + replace(composer, startForm(task.id)); + return; + } + replace(composer, messageForm(sess.id, sess.state)); + }; + const appendEvents = (events) => { + const pinned = transcript.scrollHeight - transcript.scrollTop - transcript.clientHeight < 80; + for (const ev of events) { + const el = renderEvent(ev, tools); + if (el) { + transcript.append(el); + } + } + if (pinned) { + transcript.scrollTop = transcript.scrollHeight; + } + }; + const renderAll = () => { + tools = new Map(); + renderHead(); + renderComposer(); + if (!state.selectedTaskId) { + replace(transcript, h("div", { class: "empty" }, "No task selected. Pick one with ⌘K or from the Tasks tab.")); + return; + } + replace(transcript); + appendEvents(state.transcript); + }; + const offs = [ + onEach(["selection"], renderAll), + onEach(["tasks"], () => { + renderHead(); + renderComposer(); + }), + onEach(["sessions"], (taskId) => { + if (taskId === state.selectedTaskId) { + renderHead(); + renderComposer(); + } + }), + onEach(["transcript"], (fresh) => appendEvents(fresh)), + ]; + replace(root, h("div", { class: "transcript-view" }, head, transcript, composer)); + renderAll(); + return () => { + for (const off of offs) { + off(); + } + }; +} +function startForm(taskId) { + const prompt = h("textarea", { placeholder: "What should the agent do?", rows: 4, required: true }); + const model = h("select", {}, ...models.map((m) => h("option", { value: m }, m || "default model"))); + const form = h("form", { class: "compose" }, prompt, h("div", { class: "compose-actions" }, model, h("button", { type: "submit" }, "Start agent"))); + form.onsubmit = async (e) => { + e.preventDefault(); + try { + await post("/api/tasks/{taskId}/sessions", { path: { taskId }, body: { prompt: prompt.value, model: model.value || undefined } }); + } + catch (err) { + alert(errorText(err)); + } + }; + submitOnModEnter(prompt, form); + return form; +} +function messageForm(sessionId, sessionState) { + const text = h("textarea", { placeholder: sessionState === "idle" ? "Message the agent" : "Wait for the turn to end, or interrupt it", rows: 3 }); + const send = h("button", { type: "submit", disabled: sessionState !== "idle" }, "Send"); + const interrupt = h("button", { type: "button", class: "danger", disabled: sessionState !== "running" && sessionState !== "queued" }, sessionState === "queued" ? "Cancel queued turn" : "Interrupt"); + interrupt.onclick = async () => { + try { + await post("/api/sessions/{sessionId}/interrupt", { path: { sessionId } }); + } + catch (err) { + alert(errorText(err)); + } + }; + const form = h("form", { class: "compose" }, text, h("div", { class: "compose-actions" }, interrupt, send)); + form.onsubmit = async (e) => { + e.preventDefault(); + if (!text.value.trim()) { + return; + } + try { + await post("/api/sessions/{sessionId}/messages", { path: { sessionId }, body: { text: text.value } }); + text.value = ""; + } + catch (err) { + alert(errorText(err)); + } + }; + submitOnModEnter(text, form); + return form; +} +function submitOnModEnter(input, form) { + input.onkeydown = (e) => { + if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + form.requestSubmit(); + } + }; +} +function renderEvent(ev, tools) { + const p = ev.payload; + const when = h("time", { dateTime: ev.created_at, title: ev.created_at }, ago(ev.created_at)); + switch (ev.kind) { + case "user.message": + return h("div", { class: "msg user" }, h("div", { class: "who" }, ev.actor, " ", when), h("div", { class: "text" }, String(p["text"]))); + case "agent.message": + return h("div", { class: "msg agent" }, h("div", { class: "text" }, String(p["text"]))); + case "agent.thinking": + return h("details", { class: "thinking" }, h("summary", {}, "thinking"), h("div", { class: "text" }, String(p["text"]))); + case "agent.tool_call": { + const result = h("div", { class: "tool-result pending" }, "running…"); + tools.set(String(p["id"]), result); + return h("details", { class: "tool" }, h("summary", {}, h("span", { class: "tool-name" }, String(p["name"])), " ", h("span", { class: "tool-brief" }, toolBrief(p["input"]))), h("pre", { class: "tool-input" }, JSON.stringify(p["input"], null, 2)), result); + } + case "agent.tool_result": { + const target = tools.get(String(p["tool_use_id"])); + const body = h("pre", {}, resultText(p["content"])); + if (!target) { + return h("div", { class: "tool-result orphan" }, body); + } + target.className = p["is_error"] ? "tool-result error" : "tool-result"; + replace(target, body); + if (p["is_error"]) { + target.closest("details")?.classList.add("errored"); + } + return null; + } + case "agent.report": + return reportCard(p); + case "turn.ended": + return h("div", { class: `turn-end ${p["is_error"] ? "error" : ""}` }, p["is_error"] ? `turn ended: ${String(p["subtype"]).replaceAll("_", " ")}` : ""); + case "session.created": + return h("div", { class: "note" }, `${ev.actor} started a ${p["vendor"]} session`, p["model"] ? ` (${p["model"]})` : "", " ", when); + case "session.state": + return p["error"] ? h("div", { class: "note error" }, `session ${p["state"]}: ${p["error"]}`) : null; + case "inbox.created": { + const kind = p["kind"]; + if (kind === "question") { + const q = p["payload"]; + return h("div", { class: "note ask" }, `asked${p["blocking"] ? " (blocking)" : ""}: `, String(q["question"])); + } + if (kind === "session_error" || kind === "rate_limited") { + const q = p["payload"]; + return h("div", { class: "note error" }, String(q["error"])); + } + return null; + } + case "task.completed": + return h("div", { class: "note" }, `${ev.actor} completed the task `, when); + } + return null; +} +export function reportCard(p) { + const sections = [ + ["done", "Done"], + ["deviations", "Deviations from plan"], + ["open_questions", "Open questions"], + ["risks", "Risks"], + ["next_steps", "Next steps"], + ]; + return h("div", { class: "report" }, ...sections.map(([key, label]) => { + const items = p[key] ?? []; + if (items.length === 0) { + return null; + } + return h("div", { class: `report-section ${key}` }, h("h4", {}, label), h("ul", {}, ...items.map((i) => h("li", {}, i)))); + })); +} +function toolBrief(input) { + if (typeof input !== "object" || input === null) { + return ""; + } + const i = input; + const brief = i["command"] ?? i["file_path"] ?? i["pattern"] ?? i["url"] ?? i["question"] ?? i["description"] ?? ""; + const text = String(brief); + return text.length > 120 ? `${text.slice(0, 120)}…` : text; +} +function resultText(content) { + if (typeof content === "string") { + return content; + } + if (Array.isArray(content)) { + return content.map((c) => (typeof c === "object" && c !== null && "text" in c ? String(c.text) : JSON.stringify(c))).join("\n"); + } + return JSON.stringify(content, null, 2); +} +//# sourceMappingURL=task.js.map \ No newline at end of file diff --git a/web/dist/task.js.map b/web/dist/task.js.map new file mode 100644 index 0000000..9da97b8 --- /dev/null +++ b/web/dist/task.js.map @@ -0,0 +1 @@ +{"version":3,"file":"task.js","sourceRoot":"","sources":["../src/task.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/web/dist/tasks.js b/web/dist/tasks.js new file mode 100644 index 0000000..bafd9d9 --- /dev/null +++ b/web/dist/tasks.js @@ -0,0 +1,49 @@ +import { post } from "./api.js"; +import { errorText, h, replace } from "./dom.js"; +import { currentSession, onEach, state } from "./state.js"; +export function mountTasks(root) { + const render = () => replace(root, h("div", { class: "tasks-view" }, ...state.projects.map(projectBlock), newProjectForm())); + const off = onEach(["projects", "tasks", "sessions", "inbox", "selection"], render); + render(); + return off; +} +function projectBlock(project) { + const tasks = (state.tasks.get(project.id) ?? []).filter((t) => t.status === "active"); + return h("section", { class: "project" }, h("h2", {}, project.name, " ", h("span", { class: "repo" }, project.repo_path)), h("ul", { class: "task-list" }, ...tasks.map((task) => { + const sess = currentSession(task.id); + const count = state.inbox.filter((item) => item.task_id === task.id).length; + return h("li", { class: task.id === state.selectedTaskId ? "selected" : "" }, h("a", { href: `#/task/${task.id}` }, h("span", { class: `dot ${sess?.state ?? "none"}`, title: sess?.state ?? "no session" }), h("span", { class: "task-title" }, task.title), h("span", { class: "task-state" }, sess?.state ?? "no agent"), count > 0 && h("span", { class: "badge" }, count))); + }), tasks.length === 0 && h("li", { class: "empty-row" }, "No active tasks.")), newTaskForm(project.id)); +} +function newTaskForm(projectId) { + const title = h("input", { placeholder: "New task title", required: true }); + const form = h("form", { class: "inline-form" }, title, h("button", { type: "submit" }, "Create")); + form.onsubmit = async (e) => { + e.preventDefault(); + try { + const task = await post("/api/projects/{projectId}/tasks", { path: { projectId }, body: { title: title.value } }); + location.hash = `#/task/${task.id}`; + } + catch (err) { + alert(errorText(err)); + } + }; + return form; +} +function newProjectForm() { + const name = h("input", { placeholder: "Name", required: true }); + const path = h("input", { placeholder: "Repo path on the server", required: true }); + const form = h("form", { class: "stack-form" }, name, path, h("button", { type: "submit" }, "Add project")); + form.onsubmit = async (e) => { + e.preventDefault(); + try { + await post("/api/projects", { body: { name: name.value, repo_path: path.value } }); + form.reset(); + } + catch (err) { + alert(errorText(err)); + } + }; + return h("details", { class: "add-project", open: state.projects.length === 0 }, h("summary", {}, "Add project"), form); +} +//# sourceMappingURL=tasks.js.map \ No newline at end of file diff --git a/web/dist/tasks.js.map b/web/dist/tasks.js.map new file mode 100644 index 0000000..4e1e88a --- /dev/null +++ b/web/dist/tasks.js.map @@ -0,0 +1 @@ +{"version":3,"file":"tasks.js","sourceRoot":"","sources":["../src/tasks.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/web/embed.go b/web/embed.go index 38ca34f..9b6fae0 100644 --- a/web/embed.go +++ b/web/embed.go @@ -2,5 +2,8 @@ package web import "embed" -//go:embed index.html +//go:generate npm ci --silent +//go:generate npm run --silent generate + +//go:embed index.html style.css dist src fonts var FS embed.FS diff --git a/web/fonts/LICENSE b/web/fonts/LICENSE new file mode 100644 index 0000000..8d2ce88 --- /dev/null +++ b/web/fonts/LICENSE @@ -0,0 +1,93 @@ +Copyright 2020-2024 The Atkinson Hyperlegible Mono Project Authors (https://github.com/googlefonts/atkinson-hyperlegible-next-mono) AtkinsonHyperlegibleMono-Italic[wght].ttf: Copyright 2020-2024 The Atkinson Hyperlegible Mono Project Authors (https://github.com/googlefonts/atkinson-hyperlegible-next-mono) + +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: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +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. diff --git a/web/fonts/atkinson-hyperlegible-mono-latin-ext-wght-italic.woff2 b/web/fonts/atkinson-hyperlegible-mono-latin-ext-wght-italic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..5985c61007eb57ec6494817df343d137dfcaceda GIT binary patch literal 11440 zcmV;hEKk#SPew8T0RR9104%To6#xJL0A3&f04zHI0RR9100000000000000000000 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 K{2kc)E*k&~BNJQz literal 0 HcmV?d00001 diff --git a/web/fonts/atkinson-hyperlegible-mono-latin-ext-wght-normal.woff2 b/web/fonts/atkinson-hyperlegible-mono-latin-ext-wght-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..a60e131b602daf2e8c7e52c6c1318156b8cb6c5f GIT binary patch literal 10740 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 literal 0 HcmV?d00001 diff --git a/web/fonts/atkinson-hyperlegible-mono-latin-wght-italic.woff2 b/web/fonts/atkinson-hyperlegible-mono-latin-wght-italic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e6525cc0d4d79d43cb2a4ac07d873bbc4d8decf7 GIT binary patch literal 19084 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 P(j{w2AHghd9bxwZ)ocQh literal 0 HcmV?d00001 diff --git a/web/fonts/atkinson-hyperlegible-mono-latin-wght-normal.woff2 b/web/fonts/atkinson-hyperlegible-mono-latin-wght-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..f5c108140795b5fd70aa7ac1221a00c3d9815f65 GIT binary patch literal 17752 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 literal 0 HcmV?d00001 diff --git a/web/index.html b/web/index.html index d9cbc6b..57120fb 100644 --- a/web/index.html +++ b/web/index.html @@ -4,39 +4,8 @@ byre - + + - -

byre

-

Me

-

Projects

-

Events

- - + diff --git a/web/package-lock.json b/web/package-lock.json new file mode 100644 index 0000000..f6a983c --- /dev/null +++ b/web/package-lock.json @@ -0,0 +1,392 @@ +{ + "name": "byre-web", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "byre-web", + "devDependencies": { + "openapi-typescript": "^7.13.0", + "typescript": "^5.9.3" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@redocly/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js-replace": "^1.0.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@redocly/config": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.0.tgz", + "integrity": "sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@redocly/openapi-core": { + "version": "1.34.21", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.21.tgz", + "integrity": "sha512-yXMiBFYJbP5JOaxPzx2tJhK+v8h2Wu2uhad1Ro6GzVT8E3F4GcQ7sbLPJ9w4zvGjfxroD0bIh5B0pDtnLV/qUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@redocly/ajv": "8.11.2", + "@redocly/config": "0.22.0", + "colorette": "1.4.0", + "https-proxy-agent": "7.0.6", + "js-levenshtein": "1.1.6", + "js-yaml": "4.3.2", + "minimatch": "5.1.9", + "pluralize": "8.0.0", + "yaml-ast-parser": "0.0.43" + }, + "engines": { + "node": ">=18.17.0", + "npm": ">=9.5.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/openapi-typescript": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.13.0.tgz", + "integrity": "sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@redocly/openapi-core": "^1.34.6", + "ansi-colors": "^4.1.3", + "change-case": "^5.4.4", + "parse-json": "^8.3.0", + "supports-color": "^10.2.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "openapi-typescript": "bin/cli.js" + }, + "peerDependencies": { + "typescript": "^5.x" + } + }, + "node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uri-js-replace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uri-js-replace/-/uri-js-replace-1.0.1.tgz", + "integrity": "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/yaml-ast-parser": { + "version": "0.0.43", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..649a9a4 --- /dev/null +++ b/web/package.json @@ -0,0 +1,12 @@ +{ + "name": "byre-web", + "private": true, + "type": "module", + "scripts": { + "generate": "openapi-typescript ../api/openapi.yaml --output src/api.gen.ts && rm -rf dist && tsc" + }, + "devDependencies": { + "openapi-typescript": "^7.13.0", + "typescript": "^5.9.3" + } +} diff --git a/web/src/api.gen.ts b/web/src/api.gen.ts new file mode 100644 index 0000000..550111e --- /dev/null +++ b/web/src/api.gen.ts @@ -0,0 +1,978 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/api/me": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getMe"]; + put: operations["updateMe"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/projects": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["listProjects"]; + put?: never; + post: operations["createProject"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/projects/{projectId}": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + get: operations["getProject"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/projects/{projectId}/tasks": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + get: operations["listTasks"]; + put?: never; + /** @description Creates the task's branch and worktree from the project's default branch. */ + post: operations["createTask"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tasks/{taskId}": { + parameters: { + query?: never; + header?: never; + path: { + taskId: string; + }; + cookie?: never; + }; + get: operations["getTask"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tasks/{taskId}/complete": { + parameters: { + query?: never; + header?: never; + path: { + taskId: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** @description Removes the task's worktree and branch. The task's record and events remain. */ + post: operations["completeTask"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tasks/{taskId}/events": { + parameters: { + query?: never; + header?: never; + path: { + taskId: string; + }; + cookie?: never; + }; + /** @description The task's events after a seq, oldest first. Page by passing the last seq back as after. */ + get: operations["listTaskEvents"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tasks/{taskId}/sessions": { + parameters: { + query?: never; + header?: never; + path: { + taskId: string; + }; + cookie?: never; + }; + get: operations["listSessions"]; + put?: never; + /** @description Starts an agent session in the task's worktree and sends it the prompt as its first turn. */ + post: operations["createSession"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/sessions/{sessionId}": { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + get: operations["getSession"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/sessions/{sessionId}/messages": { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** @description Starts a new turn with this message. The session must be idle. */ + post: operations["sendMessage"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/sessions/{sessionId}/interrupt": { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** @description Ends the current turn. */ + post: operations["interruptSession"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/inbox": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Inbox items, highest priority first (blocked, gate, review, fyi), then oldest first. Filters narrow the one query to a project's or a task's view. */ + get: operations["listInbox"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/inbox/{itemId}": { + parameters: { + query?: never; + header?: never; + path: { + itemId: string; + }; + cookie?: never; + }; + get: operations["getInboxItem"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/inbox/responses": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** @description Answers one or more items in one submission. Answers reach the agent as one message once it is idle and none of its blocking questions are unanswered. */ + post: operations["respondInbox"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/events": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Server-sent events. Each event's id is its seq; data is the Event as JSON. Events after Last-Event-ID (or the after query parameter) are replayed before live events. */ + get: operations["streamEvents"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/status": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getStatus"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + Error: { + message: string; + }; + User: { + login: string; + display_name: string; + git_name: string; + git_email: string; + }; + UserUpdate: { + git_name: string; + git_email: string; + }; + Project: { + id: string; + name: string; + repo_path: string; + default_branch: string; + remote: string; + created_by: string; + /** Format: date-time */ + created_at: string; + }; + ProjectCreate: { + name: string; + repo_path: string; + }; + Task: { + id: string; + project_id: string; + title: string; + branch: string; + worktree_path: string; + base_ref: string; + /** @enum {string} */ + status: "active" | "completed"; + created_by: string; + /** Format: date-time */ + created_at: string; + /** Format: date-time */ + completed_at?: string; + }; + TaskCreate: { + title: string; + }; + AgentSession: { + id: string; + task_id: string; + /** @enum {string} */ + vendor: "claude"; + /** @description Requested model; empty means the vendor's default. */ + model: string; + vendor_session_id: string; + /** + * @description queued means a turn is waiting for a free slot under the concurrency caps. + * @enum {string} + */ + state: "idle" | "queued" | "running" | "completed"; + /** @enum {string} */ + role: "implementer"; + created_by: string; + /** Format: date-time */ + created_at: string; + }; + SessionCreate: { + prompt: string; + model?: string; + }; + MessageCreate: { + text: string; + }; + Attachment: { + /** @enum {string} */ + kind: "file" | "diff" | "image" | "diagram" | "task" | "snapshot" | "url"; + /** @description file: repo-relative path. */ + path?: string; + start_line?: number; + end_line?: number; + /** @description diff: git revision range. */ + ref?: string; + url?: string; + /** @description diagram: Mermaid source. */ + mermaid?: string; + /** @description image: data URL. */ + image?: string; + task_id?: string; + snapshot_id?: string; + caption?: string; + }; + /** @enum {string} */ + InboxState: "open" | "answered" | "redirected" | "dismissed" | "superseded"; + InboxItem: { + id: string; + project_id: string; + task_id: string; + agent_session_id: string; + thread_id: string; + /** @enum {string} */ + kind: "question" | "report" | "session_error" | "rate_limited"; + /** @enum {string} */ + priority: "blocked" | "gate" | "review" | "fyi"; + blocking: boolean; + /** @description question: question, why_blocked, options, recommended_option, attachments. report: done, open_questions, next_steps, risks, deviations. session_error: error. rate_limited: vendor, rate_limit (the vendor's rate-limit status), error. */ + payload: Record; + state: components["schemas"]["InboxState"]; + /** Format: date-time */ + snoozed_until?: string; + /** Format: date-time */ + created_at: string; + resolved_by?: string; + /** Format: date-time */ + resolved_at?: string; + responses: components["schemas"]["InboxResponse"][]; + }; + /** @enum {string} */ + InboxResponseType: "option" | "free_form" | "redirect" | "question" | "snooze" | "dismiss"; + InboxResponse: { + id: string; + item_id: string; + user: string; + type: components["schemas"]["InboxResponseType"]; + option?: string; + text?: string; + attachments: components["schemas"]["Attachment"][]; + /** Format: date-time */ + snooze_until?: string; + /** Format: date-time */ + created_at: string; + /** Format: date-time */ + delivered_at?: string; + }; + InboxResponseCreate: { + item_id: string; + type: components["schemas"]["InboxResponseType"]; + /** @description option: one of the question's options. */ + option?: string; + /** @description Required for free_form, redirect, question; an optional note on option. */ + text?: string; + attachments?: components["schemas"]["Attachment"][]; + /** + * Format: date-time + * @description snooze: when the item reappears. + */ + snooze_until?: string; + }; + InboxRespond: { + responses: components["schemas"]["InboxResponseCreate"][]; + }; + Event: { + /** Format: int64 */ + seq: number; + kind: string; + project_id?: string; + task_id?: string; + agent_session_id?: string; + actor: string; + payload: Record; + /** Format: date-time */ + created_at: string; + }; + Status: { + /** @description Agent processes currently running. */ + live_agents: number; + running_turns: number; + queued_turns: number; + /** @description The latest rate-limit status each vendor reported, keyed by vendor. */ + rate_limits: { + [key: string]: Record; + }; + /** Format: date-time */ + started_at: string; + /** Format: int64 */ + db_bytes: number; + /** Format: int64 */ + last_event_seq: number; + projects: number; + active_tasks: number; + sse_clients: number; + }; + }; + responses: { + /** @description Error. */ + Error: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + getMe: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The calling user. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["User"]; + }; + }; + }; + }; + updateMe: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["UserUpdate"]; + }; + }; + responses: { + /** @description The updated user. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["User"]; + }; + }; + 400: components["responses"]["Error"]; + }; + }; + listProjects: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description All projects. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Project"][]; + }; + }; + }; + }; + createProject: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ProjectCreate"]; + }; + }; + responses: { + /** @description The new project. */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Project"]; + }; + }; + 400: components["responses"]["Error"]; + }; + }; + getProject: { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The project. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Project"]; + }; + }; + 404: components["responses"]["Error"]; + }; + }; + listTasks: { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The project's tasks, newest first. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Task"][]; + }; + }; + 404: components["responses"]["Error"]; + }; + }; + createTask: { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["TaskCreate"]; + }; + }; + responses: { + /** @description The new task. */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Task"]; + }; + }; + 400: components["responses"]["Error"]; + 404: components["responses"]["Error"]; + }; + }; + getTask: { + parameters: { + query?: never; + header?: never; + path: { + taskId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The task. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Task"]; + }; + }; + 404: components["responses"]["Error"]; + }; + }; + completeTask: { + parameters: { + query?: never; + header?: never; + path: { + taskId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The completed task. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Task"]; + }; + }; + 404: components["responses"]["Error"]; + 409: components["responses"]["Error"]; + }; + }; + listTaskEvents: { + parameters: { + query?: { + after?: number; + limit?: number; + }; + header?: never; + path: { + taskId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Up to limit events (default 500). */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Event"][]; + }; + }; + 404: components["responses"]["Error"]; + }; + }; + listSessions: { + parameters: { + query?: never; + header?: never; + path: { + taskId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The task's agent sessions, oldest first. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AgentSession"][]; + }; + }; + 404: components["responses"]["Error"]; + }; + }; + createSession: { + parameters: { + query?: never; + header?: never; + path: { + taskId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SessionCreate"]; + }; + }; + responses: { + /** @description The new session. */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AgentSession"]; + }; + }; + 400: components["responses"]["Error"]; + 404: components["responses"]["Error"]; + 409: components["responses"]["Error"]; + }; + }; + getSession: { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The session. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AgentSession"]; + }; + }; + 404: components["responses"]["Error"]; + }; + }; + sendMessage: { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["MessageCreate"]; + }; + }; + responses: { + /** @description The turn has started. */ + 202: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AgentSession"]; + }; + }; + 400: components["responses"]["Error"]; + 404: components["responses"]["Error"]; + 409: components["responses"]["Error"]; + }; + }; + interruptSession: { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The interrupt was delivered. */ + 202: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AgentSession"]; + }; + }; + 404: components["responses"]["Error"]; + 409: components["responses"]["Error"]; + }; + }; + listInbox: { + parameters: { + query?: { + project_id?: string; + task_id?: string; + state?: components["schemas"]["InboxState"]; + include_snoozed?: boolean; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Matching items with their responses. Defaults to open, unsnoozed items. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InboxItem"][]; + }; + }; + }; + }; + getInboxItem: { + parameters: { + query?: never; + header?: never; + path: { + itemId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The item with its responses. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InboxItem"]; + }; + }; + 404: components["responses"]["Error"]; + }; + }; + respondInbox: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["InboxRespond"]; + }; + }; + responses: { + /** @description The updated items. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InboxItem"][]; + }; + }; + 400: components["responses"]["Error"]; + 404: components["responses"]["Error"]; + 409: components["responses"]["Error"]; + }; + }; + streamEvents: { + parameters: { + query?: { + after?: number; + }; + header?: { + "Last-Event-ID"?: number; + }; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Event stream. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "text/event-stream": string; + }; + }; + }; + }; + getStatus: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Server health and usage. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Status"]; + }; + }; + }; + }; +} diff --git a/web/src/api.ts b/web/src/api.ts new file mode 100644 index 0000000..5046970 --- /dev/null +++ b/web/src/api.ts @@ -0,0 +1,89 @@ +import type { components, paths } from "./api.gen.js"; + +export type Schemas = components["schemas"]; +export type User = Schemas["User"]; +export type Project = Schemas["Project"]; +export type Task = Schemas["Task"]; +export type AgentSession = Schemas["AgentSession"]; +export type InboxItem = Schemas["InboxItem"]; +export type InboxResponse = Schemas["InboxResponse"]; +export type InboxResponseCreate = Schemas["InboxResponseCreate"]; +export type Attachment = Schemas["Attachment"]; +export type ByreEvent = Schemas["Event"]; +export type Status = Schemas["Status"]; + +type Method = "get" | "post" | "put"; + +type PathsWith = { + [P in keyof paths]: paths[P][M] extends { responses: unknown } ? P : never; +}[keyof paths]; + +type Op

= NonNullable; +type Params = O extends { parameters: infer X } ? X : never; +type PathParams = Params extends { path: infer X } ? X : never; +type QueryParams = Params extends { query?: infer X } ? X : never; +type Body = O extends { requestBody: { content: { "application/json": infer B } } } ? B : never; +type Success = O extends { responses: infer R } ? R[Extract] : never; +type Result = Success extends { content: { "application/json": infer J } } ? J : never; + +type Options = { + path?: PathParams; + query?: QueryParams; + body?: Body; +}; + +export class ApiError extends Error { + constructor(readonly status: number, message: string) { + super(message); + } +} + +export async function request>( + method: M, + path: P, + opts: Options> = {}, +): Promise>> { + const pathParams = (opts.path ?? {}) as Record; + let url: string = path.replace(/\{(\w+)\}/g, (_, key: string) => { + const value = pathParams[key]; + if (value === undefined) { + throw new Error(`missing path parameter ${key} for ${path}`); + } + return encodeURIComponent(String(value)); + }); + + const query = new URLSearchParams(); + for (const [key, value] of Object.entries((opts.query ?? {}) as Record)) { + if (value !== undefined) { + query.set(key, String(value)); + } + } + if (query.size > 0) { + url += `?${query}`; + } + + const init: RequestInit = { method: method.toUpperCase() }; + if (opts.body !== undefined) { + init.headers = { "Content-Type": "application/json" }; + init.body = JSON.stringify(opts.body); + } + + const res = await fetch(url, init); + const data = await res.json(); + if (!res.ok) { + throw new ApiError(res.status, (data as Schemas["Error"]).message); + } + return data as Result>; +} + +export const get =

>(path: P, opts?: Options>) => request("get", path, opts); +export const post =

>(path: P, opts?: Options>) => request("post", path, opts); +export const put =

>(path: P, opts?: Options>) => request("put", path, opts); + +export function subscribe(after: number, onEvent: (ev: ByreEvent) => void, onState: (connected: boolean) => void): EventSource { + const source = new EventSource(`/api/events?after=${after}`); + source.onopen = () => onState(true); + source.onerror = () => onState(false); + source.onmessage = (msg: MessageEvent) => onEvent(JSON.parse(msg.data) as ByreEvent); + return source; +} diff --git a/web/src/app.ts b/web/src/app.ts new file mode 100644 index 0000000..41f3e87 --- /dev/null +++ b/web/src/app.ts @@ -0,0 +1,66 @@ +import { get, put, subscribe } from "./api.js"; +import { errorText, h, replace } from "./dom.js"; +import { mountNavbar } from "./navbar.js"; +import { Pane } from "./panes.js"; +import { handleEvent, loadInbox, loadMe, loadProjects, notify, on, selectTask, state } from "./state.js"; + +function identityBanner(root: HTMLElement): void { + const render = () => { + const me = state.me; + if (!me || (me.git_name && me.git_email)) { + replace(root); + root.hidden = true; + return; + } + const name = h("input", { placeholder: "Git name", value: me.display_name, required: true }); + const email = h("input", { type: "email", placeholder: "Git email", required: true }); + const form = h("form", { class: "inline-form" }, "Set the identity agents commit as: ", name, email, h("button", { type: "submit" }, "Save")); + form.onsubmit = async (e) => { + e.preventDefault(); + try { + state.me = await put("/api/me", { body: { git_name: name.value, git_email: email.value } }); + render(); + } catch (err) { + alert(errorText(err)); + } + }; + root.hidden = false; + replace(root, form); + }; + on("me", render); + render(); +} + +function route(): void { + const match = /^#\/task\/(\w+)$/.exec(location.hash); + const taskId = match?.[1]; + if (taskId !== state.selectedTaskId) { + void selectTask(taskId); + } +} + +async function main(): Promise { + const navbar = h("header", { class: "navbar" }); + const banner = h("div", { class: "banner", hidden: true }); + const left = new Pane("left", "transcript"); + const right = new Pane("right", "inbox"); + replace(document.body, navbar, banner, h("div", { class: "panes" }, left.el, right.el)); + + mountNavbar(navbar, () => right.show("inbox")); + identityBanner(banner); + + const status = await get("/api/admin/status"); + await Promise.all([loadMe(), loadProjects(), loadInbox()]); + + subscribe(status.last_event_seq, handleEvent, (connected) => { + state.connected = connected; + notify("connection"); + }); + + addEventListener("hashchange", route); + route(); +} + +main().catch((err: unknown) => { + replace(document.body, h("pre", { class: "fatal" }, `byre failed to start: ${errorText(err)}`)); +}); diff --git a/web/src/dom.ts b/web/src/dom.ts new file mode 100644 index 0000000..726e411 --- /dev/null +++ b/web/src/dom.ts @@ -0,0 +1,58 @@ +type Child = Node | string | number | null | undefined | false; + +type Props = Partial> & { + class?: string; + style?: string; + data?: Record; +}; + +export function h(tag: K, props: Props = {}, ...children: Child[]): HTMLElementTagNameMap[K] { + const el = document.createElement(tag); + const { class: className, style, data, ...rest } = props; + + if (className) { + el.className = className; + } + if (style) { + el.setAttribute("style", style); + } + for (const [key, value] of Object.entries(data ?? {})) { + el.dataset[key] = value; + } + Object.assign(el, rest); + + append(el, ...children); + return el; +} + +export function append(el: Node, ...children: Child[]): void { + for (const child of children) { + if (child === null || child === undefined || child === false) { + continue; + } + el.appendChild(typeof child === "object" ? child : document.createTextNode(String(child))); + } +} + +export function replace(el: Element, ...children: Child[]): void { + el.replaceChildren(); + append(el, ...children); +} + +export function ago(iso: string): string { + const seconds = Math.round((Date.now() - Date.parse(iso)) / 1000); + if (seconds < 60) { + return "just now"; + } + if (seconds < 3600) { + return `${Math.floor(seconds / 60)}m ago`; + } + if (seconds < 86400) { + return `${Math.floor(seconds / 3600)}h ago`; + } + return `${Math.floor(seconds / 86400)}d ago`; +} + +export function errorText(err: unknown): string { + return err instanceof Error ? err.message : String(err); +} diff --git a/web/src/inbox.ts b/web/src/inbox.ts new file mode 100644 index 0000000..3ab7dd0 --- /dev/null +++ b/web/src/inbox.ts @@ -0,0 +1,315 @@ +import { post, type Attachment, type InboxItem, type InboxResponseCreate } from "./api.js"; +import { ago, errorText, h, replace } from "./dom.js"; +import { findTask, onEach, state } from "./state.js"; +import { reportCard } from "./task.js"; + +type Scope = "all" | "project" | "task"; +type Mode = InboxResponseCreate["type"]; + +const drafts = new Map(); +const modes = new Map(); +let scope: Scope = "all"; + +export function mountInbox(root: HTMLElement): () => void { + const tabs = h("nav", { class: "scope-tabs" }); + const list = h("div", { class: "inbox-list" }); + const submitBar = h("div", { class: "submit-bar" }); + const cards = new Map(); + + const visible = (): InboxItem[] => { + const task = state.selectedTaskId ? findTask(state.selectedTaskId) : undefined; + if (scope === "task" && task) { + return state.inbox.filter((i) => i.task_id === task.id); + } + if (scope === "project" && task) { + return state.inbox.filter((i) => i.project_id === task.project_id); + } + return state.inbox; + }; + + const renderTabs = () => { + const hasTask = state.selectedTaskId !== undefined; + const tab = (s: Scope, label: string) => { + const b = h("button", { class: s === scope ? "active" : "", disabled: s !== "all" && !hasTask }, label); + b.onclick = () => { + scope = s; + render(); + }; + return b; + }; + replace(tabs, tab("all", "All"), tab("project", "This project"), tab("task", "This task")); + }; + + const renderSubmit = () => { + const ready = [...drafts.values()].filter((d) => state.inbox.some((i) => i.id === d.item_id)); + if (ready.length === 0) { + replace(submitBar); + return; + } + const button = h("button", { class: "primary" }, `Submit ${ready.length} ${ready.length === 1 ? "response" : "responses"}`); + button.onclick = async () => { + button.disabled = true; + try { + await post("/api/inbox/responses", { body: { responses: ready } }); + for (const d of ready) { + drafts.delete(d.item_id); + modes.delete(d.item_id); + } + } catch (err) { + alert(errorText(err)); + } + button.disabled = false; + render(); + }; + replace(submitBar, button); + }; + + const render = () => { + if (scope !== "all" && !state.selectedTaskId) { + scope = "all"; + } + renderTabs(); + const items = visible(); + const keep = new Set(items.map((i) => i.id)); + for (const id of cards.keys()) { + if (!keep.has(id)) { + cards.delete(id); + } + } + for (const id of drafts.keys()) { + if (!state.inbox.some((i) => i.id === id)) { + drafts.delete(id); + modes.delete(id); + } + } + + const els = items.map((item) => { + const sig = JSON.stringify(item); + const cached = cards.get(item.id); + if (cached && cached.sig === sig) { + return cached.el; + } + const el = card(item, () => { + renderSubmit(); + }); + cards.set(item.id, { sig, el }); + return el; + }); + + replace(list, ...(els.length > 0 ? els : [h("div", { class: "empty" }, "Nothing waiting on you.")])); + renderSubmit(); + }; + + const off = onEach(["inbox", "selection", "tasks"], render); + replace(root, h("div", { class: "inbox-view" }, tabs, submitBar, list)); + render(); + return off; +} + +function card(item: InboxItem, changed: () => void): HTMLElement { + const task = findTask(item.task_id); + const p = item.payload as Record; + const body = h("div", { class: "card-body" }); + const actions = h("div", { class: "card-actions" }); + const el = h( + "article", + { class: `card ${item.kind} ${item.priority}` }, + h( + "header", + {}, + h("span", { class: `priority ${item.priority}` }, item.priority), + h("span", { class: "kind" }, item.kind.replace("_", " ")), + h("a", { href: `#/task/${item.task_id}`, class: "card-task" }, task?.title ?? item.task_id), + h("time", { dateTime: item.created_at, title: item.created_at }, ago(item.created_at)), + ), + body, + thread(item), + actions, + ); + + const allowed: Mode[] = + item.kind === "question" ? ["option", "free_form", "question", "redirect", "snooze"] : ["dismiss", "redirect", "snooze"]; + const labels: Record = { + option: "Pick", + free_form: "Other answer", + question: "Ask back", + redirect: "Redirect", + snooze: "Snooze", + dismiss: "Dismiss", + }; + + const renderActions = () => { + const mode = modes.get(item.id) ?? allowed[0]!; + const tabs = h( + "div", + { class: "modes" }, + ...allowed.map((m) => { + const b = h("button", { type: "button", class: m === mode ? "active" : "" }, labels[m]); + b.onclick = () => { + modes.set(item.id, m); + drafts.delete(item.id); + if (m === "dismiss") { + drafts.set(item.id, { item_id: item.id, type: "dismiss" }); + } + renderActions(); + changed(); + }; + return b; + }), + ); + replace(actions, tabs, modeInput(item, mode, changed)); + }; + + switch (item.kind) { + case "question": + replace( + body, + h("p", { class: "question" }, String(p["question"])), + h("p", { class: "why" }, h("strong", {}, "Blocked on: "), String(p["why_blocked"])), + attachments((p["attachments"] as Attachment[] | undefined) ?? []), + ); + break; + case "report": + replace(body, reportCard(p)); + break; + case "session_error": + replace(body, h("p", { class: "error-text" }, String(p["error"]))); + break; + case "rate_limited": { + const limit = (p["rate_limit"] ?? {}) as Record; + const resets = typeof limit["resetsAt"] === "number" ? new Date(limit["resetsAt"] * 1000).toLocaleString() : "unknown"; + replace(body, h("p", { class: "error-text" }, String(p["error"])), h("p", {}, `${p["vendor"]} ${limit["rateLimitType"] ?? ""} limit resets ${resets}`)); + break; + } + } + + renderActions(); + return el; +} + +function modeInput(item: InboxItem, mode: Mode, changed: () => void): HTMLElement | null { + const draft = drafts.get(item.id); + const set = (d: InboxResponseCreate | undefined) => { + if (d) { + drafts.set(item.id, d); + } else { + drafts.delete(item.id); + } + changed(); + }; + + switch (mode) { + case "option": { + const p = item.payload as Record; + const options = (p["options"] as string[]) ?? []; + const note = h("input", { placeholder: "Optional note", value: draft?.text ?? "" }); + const choose = (option: string) => set({ item_id: item.id, type: "option", option, text: note.value || undefined }); + note.oninput = () => { + const current = drafts.get(item.id); + if (current?.type === "option" && current.option) { + choose(current.option); + } + }; + return h( + "div", + { class: "options" }, + ...options.map((option) => { + const radio = h("input", { type: "radio", name: `opt-${item.id}`, checked: drafts.get(item.id)?.option === option }); + radio.onchange = () => choose(option); + return h("label", { class: "option" }, radio, " ", option, option === p["recommended_option"] && h("span", { class: "recommended" }, "recommended")); + }), + note, + ); + } + + case "free_form": + case "question": + case "redirect": { + const placeholder = { + free_form: "Your answer", + question: "Your question back to the agent", + redirect: "New instructions for the agent", + }[mode]; + const text = h("textarea", { rows: 3, placeholder, value: draft?.type === mode ? (draft.text ?? "") : "" }); + text.oninput = () => set(text.value.trim() ? { item_id: item.id, type: mode, text: text.value } : undefined); + return text; + } + + case "snooze": { + const presets: [string, () => Date][] = [ + ["1 hour", () => new Date(Date.now() + 3600_000)], + ["4 hours", () => new Date(Date.now() + 4 * 3600_000)], + [ + "Tomorrow 9am", + () => { + const d = new Date(); + d.setDate(d.getDate() + 1); + d.setHours(9, 0, 0, 0); + return d; + }, + ], + ]; + return h( + "div", + { class: "snooze" }, + ...presets.map(([label, when]) => { + const radio = h("input", { type: "radio", name: `snooze-${item.id}` }); + radio.onchange = () => set({ item_id: item.id, type: "snooze", snooze_until: when().toISOString() }); + return h("label", {}, radio, " ", label); + }), + ); + } + + case "dismiss": + return h("p", { class: "hint" }, "Will be dismissed when you submit."); + } +} + +function thread(item: InboxItem): HTMLElement | null { + if (item.responses.length === 0) { + return null; + } + return h( + "ul", + { class: "thread" }, + ...item.responses.map((r) => + h("li", {}, h("strong", {}, r.user), ` ${r.type.replace("_", " ")}`, r.option ? `: ${r.option}` : "", r.text ? ` — ${r.text}` : ""), + ), + ); +} + +function attachments(list: Attachment[]): HTMLElement | null { + if (list.length === 0) { + return null; + } + return h( + "ul", + { class: "attachments" }, + ...list.map((a) => { + const caption = a.caption ? h("div", { class: "caption" }, a.caption) : null; + switch (a.kind) { + case "file": { + const lines = a.start_line ? `:${a.start_line}${a.end_line ? `-${a.end_line}` : ""}` : ""; + return h("li", {}, h("code", {}, `${a.path ?? ""}${lines}`), caption); + } + case "diff": + return h("li", {}, "diff ", h("code", {}, a.ref ?? ""), caption); + case "image": + return h("li", {}, a.image?.startsWith("data:image/") ? h("img", { src: a.image, alt: a.caption ?? "" }) : null, caption); + case "diagram": + return h("li", {}, h("pre", { class: "mermaid-source" }, a.mermaid ?? ""), caption); + case "task": + return h("li", {}, h("a", { href: `#/task/${a.task_id}` }, "task ", a.task_id ?? ""), caption); + case "snapshot": + return h("li", {}, "snapshot ", h("code", {}, a.snapshot_id ?? ""), caption); + case "url": + return h("li", {}, safeLink(a.url ?? ""), caption); + } + }), + ); +} + +function safeLink(url: string): HTMLElement { + const ok = /^https?:\/\//i.test(url); + return ok ? h("a", { href: url, target: "_blank", rel: "noopener noreferrer" }, url) : h("code", {}, url); +} diff --git a/web/src/navbar.ts b/web/src/navbar.ts new file mode 100644 index 0000000..99e56ae --- /dev/null +++ b/web/src/navbar.ts @@ -0,0 +1,107 @@ +import { h, replace } from "./dom.js"; +import { activeTasks, findTask, onEach, state } from "./state.js"; + +export function mountNavbar(root: HTMLElement, showInbox: () => void): void { + const crumb = h("button", { class: "crumb", title: "Switch task (⌘K)" }); + crumb.onclick = openSwitcher; + + const inbox = h("button", { class: "inbox-count" }); + inbox.onclick = showInbox; + + const conn = h("span", { class: "connection down", title: "connecting" }); + const me = h("span", { class: "me" }); + + const render = () => { + const task = state.selectedTaskId ? findTask(state.selectedTaskId) : undefined; + const project = task ? state.projects.find((p) => p.id === task.project_id) : undefined; + replace(crumb, task ? `${project?.name ?? ""} ▸ ${task.title}` : "No task", " ▾"); + + const blocked = state.inbox.filter((i) => i.priority === "blocked").length; + inbox.className = blocked > 0 ? "inbox-count urgent" : "inbox-count"; + replace(inbox, `Inbox ${state.inbox.length}`, blocked > 0 && h("span", { class: "blocked-count" }, ` · ${blocked} blocked`)); + + conn.className = state.connected ? "connection up" : "connection down"; + conn.title = state.connected ? "live" : "reconnecting"; + replace(me, state.me?.display_name ?? ""); + }; + + onEach(["me", "projects", "tasks", "inbox", "selection", "connection"], render); + replace(root, h("a", { href: "#/", class: "brand" }, "byre"), crumb, h("span", { class: "spacer" }), inbox, conn, me); + render(); + + addEventListener("keydown", (e) => { + if (e.key === "k" && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + openSwitcher(); + } + }); +} + +function openSwitcher(): void { + if (document.querySelector(".switcher")) { + return; + } + + const input = h("input", { placeholder: "Go to task…", autocomplete: "off" }); + const list = h("ul", { class: "switcher-list" }); + const backdrop = h("div", { class: "switcher-backdrop" }); + const box = h("div", { class: "switcher" }, input, list); + let selected = 0; + let matches: ReturnType = []; + + const close = () => { + backdrop.remove(); + box.remove(); + }; + + const go = (taskId: string) => { + close(); + location.hash = `#/task/${taskId}`; + }; + + const render = () => { + const q = input.value.trim().toLowerCase(); + matches = activeTasks().filter(({ project, task }) => `${project.name} ${task.title}`.toLowerCase().includes(q)); + selected = Math.min(selected, Math.max(matches.length - 1, 0)); + replace( + list, + ...matches.map(({ project, task }, i) => { + const li = h("li", { class: i === selected ? "selected" : "" }, h("span", { class: "project-name" }, project.name), " ", task.title); + li.onmousedown = (e) => { + e.preventDefault(); + go(task.id); + }; + return li; + }), + matches.length === 0 && h("li", { class: "empty-row" }, "No matching tasks."), + ); + }; + + input.oninput = () => { + selected = 0; + render(); + }; + input.onkeydown = (e) => { + if (e.key === "Escape") { + close(); + } else if (e.key === "ArrowDown") { + e.preventDefault(); + selected = Math.min(selected + 1, matches.length - 1); + render(); + } else if (e.key === "ArrowUp") { + e.preventDefault(); + selected = Math.max(selected - 1, 0); + render(); + } else if (e.key === "Enter") { + const match = matches[selected]; + if (match) { + go(match.task.id); + } + } + }; + backdrop.onclick = close; + + document.body.append(backdrop, box); + render(); + input.focus(); +} diff --git a/web/src/panes.ts b/web/src/panes.ts new file mode 100644 index 0000000..2ec9183 --- /dev/null +++ b/web/src/panes.ts @@ -0,0 +1,55 @@ +import { h, replace } from "./dom.js"; +import { mountInbox } from "./inbox.js"; +import { mountTranscript } from "./task.js"; +import { mountTasks } from "./tasks.js"; + +export type View = "transcript" | "tasks" | "inbox"; + +const views: Record () => void }> = { + transcript: { label: "Transcript", mount: mountTranscript }, + tasks: { label: "Tasks", mount: mountTasks }, + inbox: { label: "Inbox", mount: mountInbox }, +}; + +const order: View[] = ["transcript", "tasks", "inbox"]; + +export class Pane { + readonly el: HTMLElement; + private readonly tabs: HTMLElement; + private readonly body: HTMLElement; + private view: View; + private dispose: () => void = () => {}; + + constructor(private readonly key: string, fallback: View) { + this.tabs = h("nav", { class: "pane-tabs" }); + this.body = h("div", { class: "pane-body" }); + this.el = h("section", { class: "pane" }, this.tabs, this.body); + + const saved = localStorage.getItem(this.storageKey()) as View | null; + this.view = saved && saved in views ? saved : fallback; + this.show(this.view); + } + + show(view: View): void { + this.dispose(); + this.view = view; + localStorage.setItem(this.storageKey(), view); + + replace( + this.tabs, + ...order.map((v) => { + const b = h("button", { class: v === view ? "active" : "" }, views[v].label); + b.onclick = () => this.show(v); + return b; + }), + ); + + const root = h("div", { class: `view ${view}` }); + replace(this.body, root); + this.dispose = views[view].mount(root); + } + + private storageKey(): string { + return `byre.pane.${this.key}`; + } +} diff --git a/web/src/state.ts b/web/src/state.ts new file mode 100644 index 0000000..3bdd9d9 --- /dev/null +++ b/web/src/state.ts @@ -0,0 +1,153 @@ +import { get, type AgentSession, type ByreEvent, type InboxItem, type Project, type Task, type User } from "./api.js"; + +export type Topic = "me" | "projects" | "tasks" | "sessions" | "inbox" | "selection" | "transcript" | "connection"; + +export const state = { + me: undefined as User | undefined, + projects: [] as Project[], + tasks: new Map(), + sessions: new Map(), + inbox: [] as InboxItem[], + selectedTaskId: undefined as string | undefined, + transcript: [] as ByreEvent[], + lastTranscriptSeq: 0, + connected: false, +}; + +const listeners = new Map void>>(); + +export function on(topic: Topic, fn: (detail?: unknown) => void): () => void { + let set = listeners.get(topic); + if (!set) { + set = new Set(); + listeners.set(topic, set); + } + set.add(fn); + return () => set.delete(fn); +} + +export function onEach(topics: Topic[], fn: (detail?: unknown) => void): () => void { + const offs = topics.map((t) => on(t, fn)); + return () => { + for (const off of offs) { + off(); + } + }; +} + +export function activeTasks(): { project: Project; task: Task }[] { + return state.projects.flatMap((project) => (state.tasks.get(project.id) ?? []).filter((t) => t.status === "active").map((task) => ({ project, task }))); +} + +export function notify(topic: Topic, detail?: unknown): void { + for (const fn of listeners.get(topic) ?? []) { + fn(detail); + } +} + +export function findTask(id: string): Task | undefined { + for (const tasks of state.tasks.values()) { + const task = tasks.find((t) => t.id === id); + if (task) { + return task; + } + } + return undefined; +} + +export function currentSession(taskId: string): AgentSession | undefined { + return state.sessions.get(taskId)?.findLast((s) => s.state !== "completed") ?? state.sessions.get(taskId)?.at(-1); +} + +export async function loadMe(): Promise { + state.me = await get("/api/me"); + notify("me"); +} + +export async function loadProjects(): Promise { + state.projects = await get("/api/projects"); + await Promise.all(state.projects.map((p) => loadTasks(p.id))); + notify("projects"); +} + +export async function loadTasks(projectId: string): Promise { + const tasks = await get("/api/projects/{projectId}/tasks", { path: { projectId } }); + state.tasks.set(projectId, tasks); + await Promise.all(tasks.filter((t) => t.status === "active").map((t) => loadSessions(t.id))); + notify("tasks"); +} + +export async function loadSessions(taskId: string): Promise { + state.sessions.set(taskId, await get("/api/tasks/{taskId}/sessions", { path: { taskId } })); + notify("sessions", taskId); +} + +export async function loadInbox(): Promise { + state.inbox = await get("/api/inbox"); + notify("inbox"); +} + +export async function selectTask(taskId: string | undefined): Promise { + state.selectedTaskId = taskId; + state.transcript = []; + state.lastTranscriptSeq = 0; + notify("selection"); + + if (!taskId) { + return; + } + + for (;;) { + const page = await get("/api/tasks/{taskId}/events", { path: { taskId }, query: { after: state.lastTranscriptSeq, limit: 1000 } }); + if (state.selectedTaskId !== taskId) { + return; + } + appendTranscript(page); + if (page.length < 1000) { + return; + } + } +} + +function appendTranscript(events: ByreEvent[]): void { + const fresh = events.filter((ev) => ev.seq > state.lastTranscriptSeq); + if (fresh.length === 0) { + return; + } + state.transcript.push(...fresh); + state.lastTranscriptSeq = fresh.at(-1)!.seq; + notify("transcript", fresh); +} + +export function handleEvent(ev: ByreEvent): void { + if (ev.task_id && ev.task_id === state.selectedTaskId) { + appendTranscript([ev]); + } + + switch (ev.kind) { + case "user.updated": + if (ev.payload["login"] === state.me?.login) { + void loadMe(); + } + break; + case "project.created": + void loadProjects(); + break; + case "task.created": + case "task.completed": + if (ev.project_id) { + void loadTasks(ev.project_id); + } + break; + case "session.created": + case "session.state": + if (ev.task_id) { + void loadSessions(ev.task_id); + } + break; + case "inbox.created": + case "inbox.updated": + void loadInbox(); + break; + } +} diff --git a/web/src/task.ts b/web/src/task.ts new file mode 100644 index 0000000..2c3210e --- /dev/null +++ b/web/src/task.ts @@ -0,0 +1,271 @@ +import { post, type ByreEvent } from "./api.js"; +import { ago, errorText, h, replace } from "./dom.js"; +import { currentSession, findTask, onEach, state } from "./state.js"; + +const models = ["", "opus", "sonnet", "haiku"]; + +export function mountTranscript(root: HTMLElement): () => void { + const head = h("header", { class: "task-header" }); + const transcript = h("div", { class: "transcript" }); + const composer = h("div", { class: "composer" }); + let tools = new Map(); + + const renderHead = () => { + const task = state.selectedTaskId ? findTask(state.selectedTaskId) : undefined; + if (!task) { + replace(head); + return; + } + const sess = currentSession(task.id); + const complete = h("button", { class: "danger", title: "Remove the worktree and branch; history stays" }, "Complete task"); + complete.onclick = async () => { + if (!confirm(`Complete "${task.title}"? Its worktree and branch are removed; its history stays.`)) { + return; + } + try { + await post("/api/tasks/{taskId}/complete", { path: { taskId: task.id } }); + location.hash = "#/"; + } catch (err) { + alert(errorText(err)); + } + }; + replace( + head, + h("div", { class: "meta" }, h("code", {}, task.branch), h("code", { class: "path" }, task.worktree_path)), + h("div", { class: "head-actions" }, sess && h("span", { class: `state ${sess.state}` }, sess.state), complete), + ); + }; + + const renderComposer = () => { + const task = state.selectedTaskId ? findTask(state.selectedTaskId) : undefined; + if (!task) { + replace(composer); + return; + } + const sess = currentSession(task.id); + if (!sess || sess.state === "completed") { + replace(composer, startForm(task.id)); + return; + } + replace(composer, messageForm(sess.id, sess.state)); + }; + + const appendEvents = (events: ByreEvent[]) => { + const pinned = transcript.scrollHeight - transcript.scrollTop - transcript.clientHeight < 80; + for (const ev of events) { + const el = renderEvent(ev, tools); + if (el) { + transcript.append(el); + } + } + if (pinned) { + transcript.scrollTop = transcript.scrollHeight; + } + }; + + const renderAll = () => { + tools = new Map(); + renderHead(); + renderComposer(); + if (!state.selectedTaskId) { + replace(transcript, h("div", { class: "empty" }, "No task selected. Pick one with ⌘K or from the Tasks tab.")); + return; + } + replace(transcript); + appendEvents(state.transcript); + }; + + const offs = [ + onEach(["selection"], renderAll), + onEach(["tasks"], () => { + renderHead(); + renderComposer(); + }), + onEach(["sessions"], (taskId) => { + if (taskId === state.selectedTaskId) { + renderHead(); + renderComposer(); + } + }), + onEach(["transcript"], (fresh) => appendEvents(fresh as ByreEvent[])), + ]; + + replace(root, h("div", { class: "transcript-view" }, head, transcript, composer)); + renderAll(); + return () => { + for (const off of offs) { + off(); + } + }; +} + +function startForm(taskId: string): HTMLElement { + const prompt = h("textarea", { placeholder: "What should the agent do?", rows: 4, required: true }); + const model = h("select", {}, ...models.map((m) => h("option", { value: m }, m || "default model"))); + const form = h("form", { class: "compose" }, prompt, h("div", { class: "compose-actions" }, model, h("button", { type: "submit" }, "Start agent"))); + form.onsubmit = async (e) => { + e.preventDefault(); + try { + await post("/api/tasks/{taskId}/sessions", { path: { taskId }, body: { prompt: prompt.value, model: model.value || undefined } }); + } catch (err) { + alert(errorText(err)); + } + }; + submitOnModEnter(prompt, form); + return form; +} + +function messageForm(sessionId: string, sessionState: string): HTMLElement { + const text = h("textarea", { placeholder: sessionState === "idle" ? "Message the agent" : "Wait for the turn to end, or interrupt it", rows: 3 }); + const send = h("button", { type: "submit", disabled: sessionState !== "idle" }, "Send"); + const interrupt = h( + "button", + { type: "button", class: "danger", disabled: sessionState !== "running" && sessionState !== "queued" }, + sessionState === "queued" ? "Cancel queued turn" : "Interrupt", + ); + interrupt.onclick = async () => { + try { + await post("/api/sessions/{sessionId}/interrupt", { path: { sessionId } }); + } catch (err) { + alert(errorText(err)); + } + }; + + const form = h("form", { class: "compose" }, text, h("div", { class: "compose-actions" }, interrupt, send)); + form.onsubmit = async (e) => { + e.preventDefault(); + if (!text.value.trim()) { + return; + } + try { + await post("/api/sessions/{sessionId}/messages", { path: { sessionId }, body: { text: text.value } }); + text.value = ""; + } catch (err) { + alert(errorText(err)); + } + }; + submitOnModEnter(text, form); + return form; +} + +function submitOnModEnter(input: HTMLTextAreaElement, form: HTMLFormElement): void { + input.onkeydown = (e) => { + if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + form.requestSubmit(); + } + }; +} + +function renderEvent(ev: ByreEvent, tools: Map): HTMLElement | null { + const p = ev.payload as Record; + const when = h("time", { dateTime: ev.created_at, title: ev.created_at }, ago(ev.created_at)); + + switch (ev.kind) { + case "user.message": + return h("div", { class: "msg user" }, h("div", { class: "who" }, ev.actor, " ", when), h("div", { class: "text" }, String(p["text"]))); + + case "agent.message": + return h("div", { class: "msg agent" }, h("div", { class: "text" }, String(p["text"]))); + + case "agent.thinking": + return h("details", { class: "thinking" }, h("summary", {}, "thinking"), h("div", { class: "text" }, String(p["text"]))); + + case "agent.tool_call": { + const result = h("div", { class: "tool-result pending" }, "running…"); + tools.set(String(p["id"]), result); + return h( + "details", + { class: "tool" }, + h("summary", {}, h("span", { class: "tool-name" }, String(p["name"])), " ", h("span", { class: "tool-brief" }, toolBrief(p["input"]))), + h("pre", { class: "tool-input" }, JSON.stringify(p["input"], null, 2)), + result, + ); + } + + case "agent.tool_result": { + const target = tools.get(String(p["tool_use_id"])); + const body = h("pre", {}, resultText(p["content"])); + if (!target) { + return h("div", { class: "tool-result orphan" }, body); + } + target.className = p["is_error"] ? "tool-result error" : "tool-result"; + replace(target, body); + if (p["is_error"]) { + target.closest("details")?.classList.add("errored"); + } + return null; + } + + case "agent.report": + return reportCard(p); + + case "turn.ended": + return h("div", { class: `turn-end ${p["is_error"] ? "error" : ""}` }, p["is_error"] ? `turn ended: ${String(p["subtype"]).replaceAll("_", " ")}` : ""); + + case "session.created": + return h("div", { class: "note" }, `${ev.actor} started a ${p["vendor"]} session`, p["model"] ? ` (${p["model"]})` : "", " ", when); + + case "session.state": + return p["error"] ? h("div", { class: "note error" }, `session ${p["state"]}: ${p["error"]}`) : null; + + case "inbox.created": { + const kind = p["kind"]; + if (kind === "question") { + const q = p["payload"] as Record; + return h("div", { class: "note ask" }, `asked${p["blocking"] ? " (blocking)" : ""}: `, String(q["question"])); + } + if (kind === "session_error" || kind === "rate_limited") { + const q = p["payload"] as Record; + return h("div", { class: "note error" }, String(q["error"])); + } + return null; + } + + case "task.completed": + return h("div", { class: "note" }, `${ev.actor} completed the task `, when); + } + + return null; +} + +export function reportCard(p: Record): HTMLElement { + const sections: [string, string][] = [ + ["done", "Done"], + ["deviations", "Deviations from plan"], + ["open_questions", "Open questions"], + ["risks", "Risks"], + ["next_steps", "Next steps"], + ]; + return h( + "div", + { class: "report" }, + ...sections.map(([key, label]) => { + const items = (p[key] as string[] | null | undefined) ?? []; + if (items.length === 0) { + return null; + } + return h("div", { class: `report-section ${key}` }, h("h4", {}, label), h("ul", {}, ...items.map((i) => h("li", {}, i)))); + }), + ); +} + +function toolBrief(input: unknown): string { + if (typeof input !== "object" || input === null) { + return ""; + } + const i = input as Record; + const brief = i["command"] ?? i["file_path"] ?? i["pattern"] ?? i["url"] ?? i["question"] ?? i["description"] ?? ""; + const text = String(brief); + return text.length > 120 ? `${text.slice(0, 120)}…` : text; +} + +function resultText(content: unknown): string { + if (typeof content === "string") { + return content; + } + if (Array.isArray(content)) { + return content.map((c) => (typeof c === "object" && c !== null && "text" in c ? String(c.text) : JSON.stringify(c))).join("\n"); + } + return JSON.stringify(content, null, 2); +} diff --git a/web/src/tasks.ts b/web/src/tasks.ts new file mode 100644 index 0000000..4ce7805 --- /dev/null +++ b/web/src/tasks.ts @@ -0,0 +1,73 @@ +import { post, type Project } from "./api.js"; +import { errorText, h, replace } from "./dom.js"; +import { currentSession, onEach, state } from "./state.js"; + +export function mountTasks(root: HTMLElement): () => void { + const render = () => replace(root, h("div", { class: "tasks-view" }, ...state.projects.map(projectBlock), newProjectForm())); + const off = onEach(["projects", "tasks", "sessions", "inbox", "selection"], render); + render(); + return off; +} + +function projectBlock(project: Project): HTMLElement { + const tasks = (state.tasks.get(project.id) ?? []).filter((t) => t.status === "active"); + + return h( + "section", + { class: "project" }, + h("h2", {}, project.name, " ", h("span", { class: "repo" }, project.repo_path)), + h( + "ul", + { class: "task-list" }, + ...tasks.map((task) => { + const sess = currentSession(task.id); + const count = state.inbox.filter((item) => item.task_id === task.id).length; + return h( + "li", + { class: task.id === state.selectedTaskId ? "selected" : "" }, + h( + "a", + { href: `#/task/${task.id}` }, + h("span", { class: `dot ${sess?.state ?? "none"}`, title: sess?.state ?? "no session" }), + h("span", { class: "task-title" }, task.title), + h("span", { class: "task-state" }, sess?.state ?? "no agent"), + count > 0 && h("span", { class: "badge" }, count), + ), + ); + }), + tasks.length === 0 && h("li", { class: "empty-row" }, "No active tasks."), + ), + newTaskForm(project.id), + ); +} + +function newTaskForm(projectId: string): HTMLElement { + const title = h("input", { placeholder: "New task title", required: true }); + const form = h("form", { class: "inline-form" }, title, h("button", { type: "submit" }, "Create")); + form.onsubmit = async (e) => { + e.preventDefault(); + try { + const task = await post("/api/projects/{projectId}/tasks", { path: { projectId }, body: { title: title.value } }); + location.hash = `#/task/${task.id}`; + } catch (err) { + alert(errorText(err)); + } + }; + return form; +} + +function newProjectForm(): HTMLElement { + const name = h("input", { placeholder: "Name", required: true }); + const path = h("input", { placeholder: "Repo path on the server", required: true }); + const form = h("form", { class: "stack-form" }, name, path, h("button", { type: "submit" }, "Add project")); + form.onsubmit = async (e) => { + e.preventDefault(); + try { + await post("/api/projects", { body: { name: name.value, repo_path: path.value } }); + form.reset(); + } catch (err) { + alert(errorText(err)); + } + }; + return h("details", { class: "add-project", open: state.projects.length === 0 }, h("summary", {}, "Add project"), form); +} diff --git a/web/style.css b/web/style.css new file mode 100644 index 0000000..b797e6b --- /dev/null +++ b/web/style.css @@ -0,0 +1,833 @@ +@font-face { + font-family: "Atkinson Hyperlegible Mono"; + font-style: normal; + font-display: swap; + font-weight: 200 800; + src: url(/fonts/atkinson-hyperlegible-mono-latin-wght-normal.woff2) format("woff2-variations"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: "Atkinson Hyperlegible Mono"; + font-style: normal; + font-display: swap; + font-weight: 200 800; + src: url(/fonts/atkinson-hyperlegible-mono-latin-ext-wght-normal.woff2) format("woff2-variations"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +@font-face { + font-family: "Atkinson Hyperlegible Mono"; + font-style: italic; + font-display: swap; + font-weight: 200 800; + src: url(/fonts/atkinson-hyperlegible-mono-latin-wght-italic.woff2) format("woff2-variations"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: "Atkinson Hyperlegible Mono"; + font-style: italic; + font-display: swap; + font-weight: 200 800; + src: url(/fonts/atkinson-hyperlegible-mono-latin-ext-wght-italic.woff2) format("woff2-variations"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +:root { + color-scheme: light dark; + --bg: #fbfbfa; + --surface: #f2f3f5; + --raised: #ffffff; + --border: #d6d9de; + --text: #16181b; + --muted: #4a5260; + --accent: #1a45bd; + --accent-tint: #e7edfb; + --danger: #b42318; + --danger-tint: #fdecea; + --warn: #855600; + --warn-tint: #fdf3dc; + --ok: #1a7f37; + --shadow: 0 8px 28px rgb(16 24 40 / 0.18); + --font: "Atkinson Hyperlegible Mono", ui-monospace, Menlo, monospace; + font: 14px/1.55 var(--font); +} + +@media (prefers-color-scheme: dark) { + :root { + --bg: #15171a; + --surface: #1c1f23; + --raised: #23272c; + --border: #353b43; + --text: #eceff3; + --muted: #a9b1bc; + --accent: #8ab4ff; + --accent-tint: #1e2a40; + --danger: #ff9488; + --danger-tint: #3a1f1d; + --warn: #f2c66d; + --warn-tint: #352a14; + --ok: #7fd88f; + --shadow: 0 8px 28px rgb(0 0 0 / 0.5); + } +} + +* { + box-sizing: border-box; +} + +html, +body { + height: 100%; + margin: 0; + background: var(--bg); + color: var(--text); +} + +body { + display: flex; + flex-direction: column; +} + +a { + color: var(--accent); + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +code, +pre, +input, +textarea, +select, +button { + font-family: var(--font); + font-size: inherit; +} + +pre { + margin: 0; + white-space: pre-wrap; + word-break: break-word; +} + +:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; +} + +button { + padding: 4px 12px; + border: 1px solid var(--border); + border-radius: 6px; + background: var(--raised); + color: var(--text); + cursor: pointer; +} + +button:hover:not(:disabled) { + border-color: var(--muted); +} + +button:disabled { + opacity: 0.45; + cursor: default; +} + +button.primary { + background: var(--accent); + border-color: var(--accent); + color: var(--bg); + font-weight: 700; +} + +button.danger { + color: var(--danger); +} + +input, +textarea, +select { + color: var(--text); + background: var(--raised); + border: 1px solid var(--border); + border-radius: 6px; + padding: 6px 10px; + width: 100%; +} + +input:focus, +textarea:focus, +select:focus { + border-color: var(--accent); + outline: none; +} + +textarea { + resize: vertical; +} + +.navbar { + display: flex; + align-items: center; + gap: 14px; + padding: 8px 16px; + background: var(--surface); + border-bottom: 1px solid var(--border); +} + +.brand { + font-weight: 800; + font-size: 15px; + color: var(--text); +} + +.brand:hover { + text-decoration: none; +} + +.crumb { + border-color: transparent; + background: transparent; + max-width: 50%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.crumb:hover:not(:disabled) { + background: var(--raised); + border-color: var(--border); +} + +.spacer { + flex: 1; +} + +.inbox-count { + background: transparent; +} + +.inbox-count.urgent { + background: var(--danger-tint); + border-color: transparent; + color: var(--danger); + font-weight: 700; +} + +.connection { + width: 8px; + height: 8px; + border-radius: 50%; +} + +.connection.up { + background: var(--ok); +} + +.connection.down { + background: var(--danger); +} + +.me { + color: var(--muted); +} + +.banner { + padding: 8px 16px; + background: var(--accent-tint); + border-bottom: 1px solid var(--border); +} + +.banner input { + width: auto; + margin: 0 4px; +} + +.panes { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + min-height: 0; +} + +.pane { + display: flex; + flex-direction: column; + min-width: 0; + min-height: 0; +} + +.pane + .pane { + border-left: 1px solid var(--border); +} + +.pane-tabs { + display: flex; + gap: 4px; + padding: 0 12px; + border-bottom: 1px solid var(--border); +} + +.pane-tabs button, +.scope-tabs button, +.modes button { + border: none; + border-radius: 0; + background: transparent; + color: var(--muted); + border-bottom: 2px solid transparent; +} + +.pane-tabs button { + padding: 8px 10px 6px; +} + +.pane-tabs button:hover:not(:disabled), +.scope-tabs button:hover:not(:disabled), +.modes button:hover:not(:disabled) { + color: var(--text); +} + +.pane-tabs button.active, +.scope-tabs button.active, +.modes button.active { + color: var(--text); + border-bottom-color: var(--accent); +} + +.pane-body, +.view, +.transcript-view, +.inbox-view { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; +} + +.tasks-view { + overflow-y: auto; + padding: 8px 16px 16px; +} + +.project h2 { + font-size: 14px; + margin: 16px 0 6px; +} + +.project h2 .repo { + font-weight: 400; + color: var(--muted); + font-size: 12px; + margin-left: 6px; +} + +.task-list { + list-style: none; + margin: 0 0 8px; + padding: 0; +} + +.task-list a { + display: flex; + align-items: center; + gap: 10px; + padding: 6px 10px; + border-radius: 6px; + color: var(--text); +} + +.task-list a:hover { + background: var(--surface); + text-decoration: none; +} + +.task-list li.selected a { + background: var(--accent-tint); +} + +.task-title { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.task-state { + color: var(--muted); + font-size: 12px; +} + +.empty-row { + color: var(--muted); + padding: 6px 10px; +} + +.dot { + width: 8px; + height: 8px; + border-radius: 50%; + flex-shrink: 0; + box-shadow: inset 0 0 0 1.5px var(--muted); +} + +.dot.running { + background: var(--ok); + box-shadow: none; +} + +.dot.queued { + background: var(--warn); + box-shadow: none; +} + +.badge { + font-size: 12px; + font-weight: 700; + background: var(--danger-tint); + color: var(--danger); + border-radius: 10px; + padding: 0 7px; +} + +.inline-form, +.stack-form { + display: flex; + gap: 6px; + margin-top: 6px; +} + +.stack-form { + flex-direction: column; +} + +.add-project { + margin-top: 24px; +} + +.add-project summary { + cursor: pointer; + color: var(--muted); +} + +.task-header { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + padding: 8px 16px; + border-bottom: 1px solid var(--border); +} + +.task-header:empty { + display: none; +} + +.meta { + display: flex; + flex-direction: column; + min-width: 0; + font-size: 12px; + color: var(--muted); +} + +.meta code { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.head-actions { + display: flex; + gap: 8px; + align-items: center; + flex-shrink: 0; +} + +.state { + font-size: 12px; + padding: 1px 8px; + border-radius: 10px; + background: var(--surface); + color: var(--muted); +} + +.state.running { + color: var(--ok); +} + +.state.queued { + background: var(--warn-tint); + color: var(--warn); +} + +.transcript { + flex: 1; + overflow-y: auto; + padding: 16px; + display: flex; + flex-direction: column; + gap: 12px; +} + +.empty { + color: var(--muted); + padding: 32px; + text-align: center; +} + +.msg .text { + white-space: pre-wrap; + word-break: break-word; +} + +.msg.user { + align-self: flex-end; + max-width: 85%; + background: var(--accent-tint); + padding: 8px 12px; + border-radius: 10px; +} + +.who { + font-size: 12px; + color: var(--muted); + margin-bottom: 2px; +} + +.thinking, +.tool { + border: 1px solid var(--border); + border-radius: 8px; + padding: 4px 10px; + background: var(--surface); +} + +.thinking summary, +.tool summary { + cursor: pointer; +} + +.thinking summary { + color: var(--muted); +} + +.thinking .text { + white-space: pre-wrap; + color: var(--muted); + padding: 6px 0; +} + +.tool-name { + font-weight: 700; +} + +.tool-brief { + color: var(--muted); +} + +.tool.errored { + border-color: var(--danger); +} + +.tool-input, +.tool-result pre { + background: var(--raised); + border: 1px solid var(--border); + padding: 6px 8px; + border-radius: 6px; + margin: 6px 0; + max-height: 320px; + overflow: auto; +} + +.tool-result.pending { + color: var(--muted); + font-style: italic; + padding: 4px 0; +} + +.tool-result.error pre { + color: var(--danger); +} + +.report { + border-left: 3px solid var(--accent); + padding: 2px 12px; +} + +.report h4 { + margin: 6px 0 2px; + font-size: 12px; + color: var(--muted); +} + +.report ul { + margin: 0; + padding-left: 18px; +} + +.report-section.deviations h4, +.report-section.risks h4 { + color: var(--warn); +} + +.turn-end, +.note { + font-size: 12px; + color: var(--muted); +} + +.turn-end:empty { + height: 1px; + background: var(--border); +} + +.note.error, +.turn-end.error { + color: var(--danger); +} + +.note.ask { + color: var(--accent); +} + +.composer { + border-top: 1px solid var(--border); + padding: 12px 16px; + background: var(--surface); +} + +.composer:empty { + display: none; +} + +.compose-actions { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 8px; +} + +.compose-actions select { + width: auto; + margin-right: auto; +} + +.scope-tabs { + display: flex; + gap: 4px; + padding: 4px 12px 0; +} + +.scope-tabs button { + padding: 6px 8px 4px; + font-size: 13px; +} + +.submit-bar { + padding: 8px 16px; +} + +.submit-bar:empty { + display: none; +} + +.submit-bar button { + width: 100%; +} + +.inbox-list { + flex: 1; + overflow-y: auto; + padding: 8px 16px 16px; + display: flex; + flex-direction: column; + gap: 12px; +} + +.card { + border: 1px solid var(--border); + border-radius: 10px; + padding: 12px 14px; + background: var(--raised); +} + +.card.blocked { + box-shadow: inset 3px 0 0 var(--danger); +} + +.card header { + display: flex; + gap: 10px; + align-items: baseline; + font-size: 12px; + color: var(--muted); +} + +.card header time { + margin-left: auto; +} + +.priority { + font-weight: 700; +} + +.priority.blocked { + color: var(--danger); +} + +.card-task { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.question { + font-weight: 700; + margin: 10px 0 4px; +} + +.why { + color: var(--muted); + margin: 0 0 8px; +} + +.why strong { + color: var(--text); + font-weight: 400; +} + +.error-text { + color: var(--danger); +} + +.attachments { + padding-left: 18px; + margin: 6px 0; +} + +.attachments img { + max-width: 100%; + border-radius: 6px; +} + +.mermaid-source { + background: var(--surface); + padding: 6px 8px; + border-radius: 6px; +} + +.caption { + color: var(--muted); + font-size: 12px; +} + +.thread { + list-style: none; + padding: 6px 10px; + margin: 8px 0; + background: var(--surface); + border-radius: 6px; + font-size: 12px; +} + +.card-actions { + margin-top: 10px; + padding-top: 8px; + border-top: 1px solid var(--border); + display: flex; + flex-direction: column; + gap: 8px; +} + +.modes { + display: flex; + flex-wrap: wrap; + gap: 2px; +} + +.modes button { + font-size: 12px; + padding: 2px 8px 3px; +} + +.options, +.snooze { + display: flex; + flex-direction: column; + gap: 4px; +} + +.option, +.snooze label { + cursor: pointer; +} + +.option input, +.snooze input { + width: auto; + accent-color: var(--accent); +} + +.recommended { + margin-left: 8px; + font-size: 12px; + color: var(--ok); +} + +.hint { + color: var(--muted); + margin: 0; +} + +.switcher-backdrop { + position: fixed; + inset: 0; + background: rgb(0 0 0 / 0.35); +} + +.switcher { + position: fixed; + top: 15vh; + left: 50%; + translate: -50% 0; + width: min(560px, 90vw); + background: var(--raised); + border: 1px solid var(--border); + border-radius: 10px; + padding: 10px; + box-shadow: var(--shadow); +} + +.switcher-list { + list-style: none; + margin: 8px 0 0; + padding: 0; + max-height: 50vh; + overflow-y: auto; +} + +.switcher-list li { + padding: 6px 10px; + border-radius: 6px; + cursor: pointer; +} + +.switcher-list li.selected { + background: var(--accent-tint); +} + +.project-name { + color: var(--muted); +} + +.fatal { + padding: 24px; + color: var(--danger); +} diff --git a/web/tsconfig.json b/web/tsconfig.json new file mode 100644 index 0000000..f76f82c --- /dev/null +++ b/web/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2023", + "module": "ES2022", + "moduleResolution": "bundler", + "lib": ["ES2023", "DOM", "DOM.Iterable"], + "strict": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "rootDir": "src", + "outDir": "dist", + "sourceMap": true, + "skipLibCheck": true + }, + "include": ["src"] +}