npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

flashlite

v0.1.3

Published

flashlite — a personal agentic coding CLI

Readme

flashlite

A personal agentic coding CLI — Claude Code / pi style — for any OpenAI- or Anthropic-compatible chat endpoint. Streaming chat REPL plus an agent loop with filesystem tools for creating and organizing projects, reading/editing code, searching, copying assets, and removal, plus terminal commands for builds, tests, and other requested work. Opt-in guarded auto runs arbitrary terminal commands without prompts, but still asks before network access, installs, sudo, paths outside the project, and destructive Git operations — and refuses obviously catastrophic deletion/disk commands. A separate unguarded auto mode permits whole-drive file access and every command; only recognizable catastrophic actions ask.

TypeScript on Node ≥ 24, run straight from source via Node's native type stripping: zero build step, zero runtime dependencies (only typescript + @types/node as devDependencies for typechecking). macOS only.

Setup

npm install        # devDependencies only (typescript + @types/node)
npm run typecheck  # tsc --noEmit
npm test           # node --test — the full suite is offline

# put the CLI (command `flash`) on your PATH (no root needed):
mkdir -p ~/.local/bin
ln -s "$PWD/scripts/flashlite-wrapper.sh" ~/.local/bin/flash   # or copy it; chmod +x

On first interactive launch, flash asks for your endpoint URL and then your API key with masked input, saving both to ~/.config/flashlite/config.json (chmod 600). Each local user provides their own endpoint and key; nothing is bundled with the npm package or baked into the binary. Keys are never printed, logged, or committed — not even masked. Non-interactive runs require an existing config file (or --base-url). Configuration is validated before use: auto_approve must be a JSON boolean (the string "false" cannot enable it), max_output_tokens must be an integer from 256–65536, and tool_result_budget_bytes from 0–4,000,000 (0 disables tool-result elision). Invalid fields produce value-free warnings and fall back to defaults; permission fields fail closed. Base URLs must use HTTP(S) and cannot contain login credentials, query strings or fragments.

flash --check          # connectivity + model catalog diagnostic
flash                  # the REPL
flash -p "explain this code: …"  # text-only one-shot; no tools or workspace access
flash --agent -p "inspect this repo"  # headless tools, read-only by default
flash --agent --auto -p "fix the failing tests and verify"  # trusted project; guarded commands
flash --agent --auto-approve -p "update the README"  # file changes only
flash --absolute       # unguarded auto: whole drive + every command; no gate
flash --auto           # guarded auto; network/installs/sudo/outside/destructive still ask
flash --auto-approve   # file-only auto; every terminal command still asks
flash --model <id> --debug

Real-work workflows

The agent is instructed to inspect relevant files, make focused changes, run authorized validation, diagnose failures, retry, and report what was actually verified. Tool schemas explain scope, approval and supported follow-up options instead of treating bounded output as a dead end. Applicable AGENTS.md (or legacy CLAUDE.md) files are loaded from the nearest repository root down to the launch directory, attributed to their source/scope. Outside a repository, only the launch directory is considered. Sources are regular, non-symlink files, bounded to 16 KiB each / 32 KiB total; nested guidance must be inspected when working in a deeper subtree. Project instructions cannot override host permissions or credential protection.

For automation, --agent -p explicitly enables tools. It defaults to read-only, ignoring saved auto_approve permissions. Add --auto-approve for file changes or --auto for file changes plus guarded commands. Actions without approval are denied as tool results, never an unattended approval prompt; denied/failed tools or unfinished turns produce a nonzero exit code. Headless runs save private transcripts and retain completed-action evidence. They use bounded tool rounds but currently do not fetch model context windows or auto-compact; keep automated tasks scoped. Unguarded auto is interactive-only. Ordinary -p remains text-only, not a repo-inspection mode.

The REPL

  • Terminal UI: chat scrolls above a bottom-anchored panel, with the selected model on its top divider, the orange ❯ input between dividers, and a labeled status row below. Your input echoes with its orange ❯ chevron; streamed assistant replies start with a subtle ● marker. Tool calls are grouped by name only within a single model-proposed batch, not across later calls. The layout uses a flat light yellow accent (#ffe066) for branding, models, menus, and availability, with orange (#ff9f40) for keywords. The status row names every token (mode/effort); the effort value warms from light yellow to red as the level rises, and the drive-wide mode badge reads unguarded auto. A resting status word is omitted from the footer; success states stay green and warnings amber/red, and Shift+Tab cycles the permission mode. Command menus and views open immediately below the input, outside the scrolling conversation. While a command is open, the normal status row is hidden so only its controls remain. Reasoning text is hidden; answers and in-flow tool activity cards stay in chat. The compact header shows only the working directory. Pipes and very small terminals keep a simple layout; --no-color disables styling and animation. Set FLASHLITE_THEME=light for white/light terminals: the warm palette darkens and switches to explicit true-color (no bright 9x, no 2 faint) so every text role keeps ≥4.5:1 contrast; dark is the default and unchanged. Restart flash after source changes — existing sessions do not hot-reload.
  • Context meter: the bottom bar shows conversation context usage as a color-coded percent with token counts (e.g. ctx 42% 42k/100k) — accent up to 70%, yellow 70–85%, red above 85%; missing or disabled context shows nothing. The context amount is always kept visible, even on narrow terminals — the status truncates around it.
  • Dynamic slash commands: type / to open a live filtered palette below the input. ↑/↓ selects, Tab completes, Enter runs, and Esc dismisses. /model opens the model picker; /model <id> still sets directly and persists the default. /compact summarizes older complete turns while retaining the latest user turn; the append-only session log stores a checkpoint so resumed conversations use the compacted context. /settings offers token saver (off/low/high/custom), auto-compaction, and boost (on/off). Off uses the router's reported output and context limits (falling back to saved budgets); low/high reduce output, context and replay budgets and compact earlier. Max output and max context controls appear only while a token saver is on; auto-compaction is always available. Custom keeps saved budgets and lets you edit the replay budget. Boost keeps the same model, reasoning effort and token budgets. For substantial requests it starts independent read-only code/UI investigation or research helpers while the main agent works. Finished findings arrive at safe model-round boundaries so the main agent can avoid duplicate investigation; remaining helpers are awaited before concluding. Focused validation stays with the main agent. Small tasks skip delegation. Boost tints the model name, ϟ glyph and composer rules orange. While the model works, a highlight sweeps the full model bar and the lower composer rule; at rest the bar holds its static orange. Set FLASHLITE_REDUCED_MOTION=1 (or NO_COLOR) to disable motion, and animations never run in piped output. Flashlite already uses fast context checks, bounded replay and parallel tool calls in every mode; Boost cannot force faster provider inference. Presets do not overwrite advanced config values. Shift+Alt+Tab toggles Boost in the interactive editor. Settings also include the local API key, default model, reasoning effort, permission default, endpoint and Memory entry. Pick Memory to append a global prompt for the next turn. Context checks use exact counting near the limit where supported and estimates otherwise. Key entry is masked and never echoed; credentials come only from the local config. /mode picks ask, files, guarded auto, or unguarded auto; /mode auto and /mode unguarded switch directly, and Shift+Tab cycles the mode from the prompt. Modes are session-only. /clear resets the visible conversation and starts a fresh transcript; /exit quits. Command notices and controls stay below the input; agent answers and tool cards stay in chat. Pipes and tiny terminals use plain command output.
  • Anchored scrolling and selection: Page Up/Down scroll conversation history while the input stays fixed, even during a streaming answer. New output does not pull you away from the history you are reading. Page Down returns live; submitting a new turn also returns live. Open command panels/pickers receive these keys instead. Display history retains the latest 10,000 logical lines; full transcripts stay on disk. Mouse reporting stays disabled so normal click-and-drag text selection and copy work without a modifier key. The mouse wheel uses your terminal's native scrolling.
  • Effort: /effort opens a picker for auto, low, medium, high, xhigh, max; /effort high sets directly. The choice is shown in the bar and saved as reasoning_effort. flash --effort high overrides it for this run, including -p. auto omits an explicit API setting. Chat Completions uses reasoning_effort; Messages uses thinking budgets (2,048 / 4,096 / 8,192 / 16,384 / 32,000 tokens), raising total max_tokens when necessary to leave at least 4,096 tokens for the answer. Supported levels depend on the model/router; a rejection is reported, not silently downgraded. DeepSeek is the exception: its picker offers only the levels the model accepts (auto, low, high, max; auto, high, max for pro) and the wire value is coerced (medium/xhigh → high) so an unsupported level is never sent. Signed thinking blocks are retained for tool continuations, never shown in chat.
  • Live agent cards: the thinking indicator updates in place in the chat area, above the composer, not in the bottom status strip. It is transient (not transcript/history content), disappears when the answer starts, and stays hidden while viewing older chat. Hidden model reasoning remains hidden. Tool activity updates a card in the conversation, rather than pinning it to the composer; file writes/edits show pending code as arguments stream, then update that same card to success/denied/error. An edit over a readable file renders as a line-numbered unified diff — context rows, +/− rows and a +N −M chip — with the code token-colored by the file's language; when the target can't be read (a new file, a partial stream) it falls back to a plain − old/ + new block. The finished card keeps the +N −M count. Completed cards remain in chat history, and later replies follow them. Previews do not execute changes: writes follow the selected permission mode. Completed cards name what a tool acted on beside its timing — e.g. ✓ read_file 2x · src/a.ts · src/b.ts · 1.2s and ✗ run_command · npm test · 2m 05s — and denied runs say what was denied; live queued cards show the subject too (e.g. ● read_file src/a.ts).
  • Image attachments: Ctrl+V reads an image from the macOS clipboard and queues it for your next prompt; your typed draft stays intact. Copy an image, or capture a screenshot directly to the clipboard with Ctrl+Cmd+Shift+4, then Ctrl+V in flash. Cmd+V still handles ordinary terminal text paste. An empty/text-only clipboard produces a notice, not a message to the agent. Clipboard access occurs only on this explicit shortcut, never in the background or via agent tools. Pending filenames appear in the footer. Type your question and Enter, or Enter alone to send an image-only turn. Ctrl+X removes pending attachments; /clear removes both queued attachments and images already in conversation history. Images stay in history across tool rounds and follow-up questions. Failed requests retain the original user message and images in history once; send a continuation to retry without reattaching. Use an image-capable model: router/model support varies, and unsupported images are reported rather than silently dropped.
    • Up to 4 images per prompt, 10 MiB each, 20 MiB total. Clipboard PNG is preserved; screenshot TIFF is converted to PNG using AppKit. No resizing or HEIC conversion. TIFF input is bounded to 40 MiB / 40 million pixels before PNG conversion.
    • flash --image ./shot.png -p "Explain this screenshot" works in one-shot mode; repeat --image for multiple files. Without -p, images are queued in the REPL. Local files support PNG, JPEG, WebP, GIF, quoted paths, ~/, and absolute paths.
    • No image slash commands. Type / for the palette or /help for shortcuts and recovery. Clipboard images are local snapshots; nothing is uploaded until you submit a prompt. Remote image URLs and Finder file-copy clipboard references are not supported.
    • Clipboard access uses macOS's built-in /usr/bin/osascript with fixed AppKit/JXA code via execFile (no shell, packages, native compilation, or temporary image files). Reads time out after 10 seconds and Ctrl+C cancels; failed/cancelled reads keep the queue intact. Shortcuts are disabled during turns, permission prompts, model pickers and command panels.
    • UI and Markdown transcripts show filenames/metadata only. Private JSONL transcripts (mode 0600) retain the encoded image bytes, so they can be much larger and include private image content. No clipboard payload is printed to the terminal.
    • Context warnings use exact Messages token counting when available, otherwise a rough 2,048-token allowance per image; actual image tokens vary by model and resolution.
  • Live typing and steering: the composer stays editable while the AI streams or runs tools. Enter queues your text as steering; muted grey message rows above the composer show what's pending, with the count in the existing footer. The latest three messages are visible (earlier ones are summarized); long rows use an ellipsis. Smaller terminals reduce this preview to preserve chat and the anchored composer. Messages are delivered in order at the next safe model boundary, after all results from an in-flight tool batch. A queued message received during a final answer starts another response in the same turn. It does not interrupt a running request or undo tools already executing — Ctrl+C stops the turn. Pending previews update in place and disappear when sent. Applied messages use the ordinary user-message styling, with no “steer:” prefix, and are included in the same active workflow/transcript. Delivering them does not stop or restart the agent or a running terminal command. A provider request already in flight cannot accept new input retroactively; the next continuation receives the messages after tool results. Pending text is display-only. Unsubmitted drafts survive turn completion and temporarily yield to permission questions, then return afterward. Permission answers are not steering. Ctrl+C during approval cancels rather than approving the write. Unapplied steering on cancellation, error or the tool-round cap returns to the editor for an explicit retry; multiple restored messages are joined in order. Slash commands work during a turn: /model, /effort, and /mode open their usual controls while the AI continues. Model/effort changes apply to the next turn; current requests and tool continuations retain their original settings, and usage/transcripts remain attributed to the active turn's model. Cancelling a command does not cancel the agent. Permission questions take priority over open command controls. /clear cancels and awaits the active turn before resetting history/queued steering; /exit stops both. Clipboard shortcuts remain idle-only.
  • Agent loop: no fixed tool-round cap — the turn runs until the model stops requesting tools (bounded by the context budget, output limits and auto-compaction).
  • Tool-result budget: to keep replay tokens bounded, older tool results are elided from the request the model sees once their combined size exceeds tool_result_budget_bytes (default 8,192; advanced value in config.json, 0 disables). The newest result is always sent whole; older ones are replaced with a short [earlier tool result elided …] stub that records the original size. This trims only the replayed request — canonical history, the transcript and tool-call/result pairing stay intact, and the elision is idempotent across rounds.
  • Multi-turn context: each turn appends the user message and the assistant answer to the conversation, so follow-ups continue the same context. The router only concatenates a prior turn's chain of thought when the request carries tools; this CLI follows that rule — OpenAI reasoning_content is retained on the assistant message but replayed only while tools are present (so tool continuations keep continuity), and tool-less requests omit it. Model reasoning is never shown in chat or written to the Markdown log; it stays in the private JSONL transcript. "Steering" is separate: it queues a message delivered at the next safe boundary within an active turn. File and terminal approvals appear in a bordered panel above the composer with a separate choice row, not as text in the normal input bar. File changes accept y = once, a = all file changes (this session), n = deny, Esc = deny. A blank Enter re-prompts. --auto-approve or "auto_approve": true skips file-write prompts only. /mode auto or --auto runs arbitrary terminal commands, still asking before the five guarded-auto risk categories. In ask and files modes, all terminal commands show the full command/cwd/timeout and require their own explicit y/yes; only y/yes authorizes, a blank Enter re-prompts, and Esc/Ctrl+C/EOF deny. Unguarded auto skips file and command prompts entirely, except recognizable catastrophic commands. /mode ask revokes automatic grants; queued auto/unguarded commands and unguarded file mutations cannot start after revocation. Running commands need cancellation to stop; file operations also recheck cancellation/authorization at bounded checkpoints. Tool batches have four workers; approvals are asked just-in-time (one prompt at a time, in call order) so an early tool can run while later calls still await approval, and results keep their call order. After the last permitted batch, one tools-disabled request lets the model evaluate its results. Truncated/incomplete proposals never execute; unmatched/duplicate batch IDs stop with an explicit interruption rather than corrupting the next request. Round exhaustion, context exhaustion, invalid calls and model truncation have distinct recoverable notices—not invented success. Completed tool evidence survives failures. Interactive context checks reserve actual output/thinking capacity. Auto-compaction can summarize older exchanges within a long active task, retaining current instructions and the latest two complete exchanges (including required signed thinking). Attempts are bounded and recounted; if retained context still cannot fit, the turn stops instead of knowingly sending an overfull request. Fallback token counts remain estimates.
  • Keys: Enter submits, arrows walk history, Ctrl+C interrupts (aborts a streaming turn), Ctrl+D exits. Esc backs out or cancels — one-shot prompts, menus and approvals (a denied command or file change is never authorized). Ctrl+O expands/collapses the latest completed tool batch's output in place; the expanded view shares the chat viewport across calls, with omitted lines indicated (full results remain in the private transcript). Full output also remains in the private session transcript. The agent can start read-only background helpers (roles: ui-ux, code, minor, oracle, reviewer, coder and researcher) while it keeps working; their finished findings are folded back into the conversation at safe model-round boundaries. Boost starts the same helpers automatically for substantial requests. All helpers are read-only: they can inspect project files but never edit them or run shell commands. Researchers can read public HTTPS text/HTML/JSON pages without a network prompt; local/private endpoints and redirects are blocked. Running helpers stay listed above the input; finished ones linger for a couple of seconds, then disappear. Ctrl+R opens a bash-style reverse history search: typing narrows, Ctrl+R or ↑ walk older matches, ↓ newer, Enter accepts the match back into your draft (Enter again submits), and Esc restores your exact pre-search draft. Multi-line bracketed pastes submit as one turn. The model picker navigates with ↑/↓ (or j/k), confirms with Enter, cancels with Esc or Ctrl+C.
  • Markdown: answers render as markdown — **bold**, `code`, fenced code blocks (light yellow), # headings, list markers, and [links](url) (URL dropped). Italic is intentionally unsupported (*/_ collide with identifiers). Fenced code with a recognized language (ts, js, jsx, json, bash, sh, zsh, python, go, rust, yaml, toml) is token-colored live and in one-shot — keywords orange, strings green, numbers yellow, comments dim — while prose stays default. Unknown or absent languages keep the flat light-yellow accent, and fence openers show their language tag in orange. Transcripts keep the raw markdown; styling is display-only. Partial prose, bold/code spans, and code-block content appear as chunks arrive, without waiting for a newline or Ctrl+C. Only ambiguous Markdown prefixes/links use bounded lookahead. Streamed replies discard oversized leading/trailing blank gaps and use one blank line between prose paragraphs; code-block whitespace is preserved.
  • Sessions: every turn appends to ~/.config/flashlite/sessions/ — a markdown log plus a JSONL transcript. Resume the newest saved conversation with flash --resume latest, a specific one with flash --resume <session-id>, or use /sessions from the REPL to select one. New sessions record their canonical workspace; resuming from a different folder is blocked rather than silently retargeting paths. Legacy sessions require explicit workspace acknowledgement (CLI: --acknowledge-legacy-workspace). Permissions are never restored from a transcript. Validated context and recoverable checkpoints retain completed actions even after a later provider failure. Transcript errors do not undo execution; a later successful JSONL write checkpoints unsaved context. If logging keeps failing, evidence is retained only in the running process, not guaranteed on disk. Future turns append to the same private transcript; /clear starts fresh.
  • Model IDs auto-route to the right wire dialect: anthropic/* speaks the Messages API; everything else speaks Chat Completions. Bare claude-* and gpt-*/grok-*/deepseek-* also work; other bare IDs ask for the full provider-prefixed ID from /model.

Custom commands, memory, hooks, todos, and undo

Five extension points build on the same tool/approval engine:

  • Custom slash commands. Drop markdown files in ~/.config/flashlite/commands/ (global) or <project>/.flashlite/commands/ (project; shadows global by name). The filename is the command (review.md → /review); the body is a prompt template. $1..$9 substitute positional arguments and $ARGS substitutes them all. Optional frontmatter sets description (shown in the / palette) and argument-hint. No shell interpolation runs — the body is prompt text, not a script.
  • Path completion. Tab completes @file mentions and bare paths relative to the working directory (directories get a trailing /); an unambiguous match resolves fully, an ambiguous one extends to the longest common prefix.
  • Memory. ~/.config/flashlite/memory.md (global) and <project>/.flashlite/memory.md (project) are loaded into the system prompt (bounded, treated as data). The /settings Memory entry appends a global memory prompt; project memory can be edited directly.
  • Todos. The agent can call todo_write to publish a task list (pending/in_progress/completed/cancelled); it renders live in a dock strip with ☐/◐/✓/✗ and clears on /clear.
  • Undo. Before a mutating file tool runs, its target's original bytes are captured; a turn's captures form a checkpoint. /undo reverts the most recent turn's file changes (created files are removed, changed files restored). Shell side effects are never captured — this only reverses the file tools.
  • Hooks + notifications. Executable scripts named PreToolUse, PostToolUse, Notification, or Stop in ~/.config/flashlite/hooks/ or <project>/.flashlite/hooks/ receive one JSON object on stdin. PreToolUse may veto a call with {"decision":"block","reason":"…"}; hook errors fail open. A ~15s+ turn also fires a macOS notification.

Tool policy

Tools are available in the interactive agent REPL. Launch flash from the folder you want it to work in. In ask, files, and guarded auto, filesystem-tool paths are relative to that folder and confined there: absolute paths, ~, parent-directory escapes, and symlink escapes are refused. In unguarded auto, tools can use absolute paths, ~/, parent paths and external symlink parents across the drive, wherever your current macOS account has permission. Terminal commands are separately authorized (explicitly, by guarded auto, or by unguarded auto) and are not sandboxed. Their starting directory is validated, but their effects can extend to other files on your PC and the network. Ordinary -p is chat-only; explicit --agent -p uses the same tools with the noninteractive approval policy above.

| Tool | What it does | Approval | | --- | --- | --- | | read_file | Read text, with UTF-8-safe byte pages up to 256 KiB and continuation metadata | No | | read_files | Read 1–20 text files with per-file errors and a shared 256 KiB content budget | No | | file_info | Inspect type, size, permissions and modification time; no file content | No | | list_dir | Sorted directory pages using offset and limit (up to 500 per page) | No | | find_files | Find paths by literal substring, optionally files/directories only | No | | grep | Stream literal text search in a file or directory with bounded snippets and honest partial-result metadata | No | | write_file | Create/overwrite text, including missing parents, up to 1 MiB | Yes | | edit_file | Replace a nonempty, unique exact match; resulting file up to 1 MiB | Yes | | make_dir | Create folders and missing parents | Yes | | copy_path | Copy a file (including binary assets) or directory tree | Yes | | move_path | Move/rename a file or directory | Yes | | remove_path | Permanently delete a file/directory (no recovery store; use git or a copy) | Yes | | run_command | Noninteractive terminal command with bounded output/time | Explicit yes; guarded auto for arbitrary commands except five risk categories; unguarded auto for everything |

File mutations marked Yes ask in ask mode and run automatically in files/auto/unguarded. Neither file approve-all nor --auto-approve grants terminal authorization.

copy_path and move_path never overwrite an existing destination. Copy/move/remove preflight trees with a 5,000-entry / 100 MiB limit. Tree operations reject symlinks and special files rather than silently omitting entries. Outside unguarded auto they also reject .git internals; mutations reject symlink paths (even links staying inside the project), the working directory itself, and .git internals. Unguarded auto lifts those project/protected-name path guards, but write/edit still refuse final symlinks and non-regular files, and tree operations still refuse symlink entries. Text tools reject NUL-containing binary files. Write/edit may overwrite according to the selected permission mode; read before changing an existing file. Aborted/revoked copies roll back their newly-created destination contents; an individual OS copy syscall cannot be interrupted, and newly-created parent folders may remain. File paging/search is not snapshot-isolated from concurrent rewrites.

find_files caps at 500 results / 5,000 inspected entries; grep caps at 200 results / 5,000 inspected entries (including directories). Both skip symlinks. Outside unguarded auto they also skip .git, dependencies, common build directories and .DS_Store; unguarded searches include those names, without dropping traversal/output caps. Find patterns are literal, not globs or regular expressions.

Large text files can be inspected without increasing context arbitrarily: read_file(path, offset_bytes?, max_bytes?) returns ordinary text for a complete small file, or a JSON page with byte positions, total bytes and next_offset_bytes for a large/ranged read. Page content is UTF-8-safe; continue using the returned offset. list_dir(path?, offset?, limit?) sorts entries and reports the next offset when more remain. Pages are not filesystem snapshots: restart after directory changes. A listing that reaches its 100,000-entry scan bound is explicitly partial, without a misleading continuation offset for an unscanned remainder.

grep can search a single regular file as well as a directory. Its default scan budget is 16 MiB (configurable with max_scan_bytes up to 100 MiB), and results use bounded snippets with byte positions. max_output_bytes controls the aggregate result budget up to 64 KiB, including partial-result notices; max_results is capped at 200. For a single file, pass the reported next_start_byte as start_byte; inspect matching ranges with read_file. Directory traversal caps/skipped files are explicitly reported: partial search is not an exhaustive “no matches.” Narrow the directory scope when traversal is capped. Search may read up to 16.5 KiB of preceding context and 16 KiB of unsearched suffix context for snippets/credential redaction; matches and continuation stay within the forward scan budget. Read-page limits bound source bytes, not JSON escaping/redaction overhead. Binary/special files remain unsupported by text tools.

Mutations from concurrent tool batches are serialized per project in this process. Confinement is a path guard, not an OS sandbox against another process changing the filesystem concurrently; avoid moving project folders or changing symlinks mid-operation.

Removal is permanent. remove_path deletes with fs.rm; there is no recovery store, list_removed/restore_file, or automatic purge — git (or a copy made first) is the only recovery. A shell rm is likewise a real deletion, and redirects/tee can clobber a file. These file-tool protections are not an OS sandbox: an approved terminal command can delete files the tools guard.

Terminal commands

run_command(command, cwd?, timeout_ms?) uses macOS /bin/zsh -f -c, with ignored stdin and piped stdout/stderr, not an interactive terminal. cwd must be an existing project directory (default project root), except unguarded auto also permits existing directories elsewhere on the drive. The default timeout is 60 seconds, maximum 5 minutes; commands are limited to 16 KiB and captured output to a combined 1 MiB. Overflow terminates the command and reports truncation, not unlimited collection. Results include status, exit code/signal, stdout/stderr and truncation; failure is reported faithfully. The live card shows lifecycle only, not raw command output.

In ask/file-only mode, every command gets a separate approval. Guarded auto runs arbitrary commands without a prompt except for five risk categories (below); unguarded auto runs every command except recognizable catastrophic deletion/disk destruction. When a command needs approval, its full text, canonical cwd and effective timeout appear in chat before the anchored yes/no question; control/invisible characters and backslashes are escaped visibly rather than executed by the terminal. Known router-key-bearing commands are rejected before display. Commands use a minimal environment allowlist (standard path/home/user/temp/locale variables, TERM=dumb), not ambient credential variables. Known router credentials are redacted from captured output before model/history/transcript use. This is not comprehensive secret isolation: commands can still read local configuration or make network requests. Review what you approve. /bin/zsh -f disables ordinary user rc loading; it does not isolate system startup behavior.

Guarded auto

Enable with flash --auto, /mode auto, or the /mode picker. It is opt-in for this session, never saved as a default. files mode and legacy auto_approve remain file-only; --auto, --auto-approve, and --absolute are mutually exclusive.

Guarded auto is trust-by-default: arbitrary commands and pipelines run without a prompt, including unknown executables, interpreters, builds, tests and file mutations. Exactly five categories still ask, and none can be hidden behind a wrapper, pipeline, env assignment or literal sh -c body:

  • Network access — curl, wget, nc, ssh, scp, rsync, git fetch|pull|push|clone|remote, and similar.
  • Installs / package managers — npm install|ci|add|…, pnpm/yarn add, brew, pip, apt, cargo install, go get, npx, and similar.
  • Privilege escalation — sudo, doas, su, pkexec.
  • Paths outside the project — absolute paths, ~, parent (..) traversal, and symlink escapes.
  • Destructive Git — git reset --hard, git clean -f…, git push --force, git branch -D.

Obviously catastrophic deletion or disk commands (recursive deletion of a filesystem root, home or volume; diskutil erase; mkfs; dd/redirection to a raw disk) are refused outright, not offered. Syntax the classifier cannot reduce to inspectable words — command substitution, backticks, globs, here-docs, eval/source, subshells and background jobs — fails closed and asks. Paths must resolve inside the project, plus the CLI's own ~/.config/flashlite tree for reads. cp/mv never overwrite.

Guarded auto trusts arbitrary commands and project code. A classifier is not proof of safety, this is not an OS sandbox, and buildings/tests can execute arbitrary code with side effects. There is an external filesystem race between inspection and execution; command text cannot provide isolation. Do not enable auto in an untrusted repository. Turning auto off revokes queued grants, including after turning it on again, but does not interrupt work already running.

Unguarded auto

Enable with flash --absolute, /mode unguarded, or the /mode picker. It takes effect immediately with a one-line notice — there is no typed acknowledgement or disclaimer gate. A /mode switch applies to the session; the /config default-mode picker can save it as the launch default (written to config as unguarded, never absolute). An interactive terminal is required, and --absolute is rejected with -p, --check, or piped input/output.

Unguarded auto has no approval prompts and no guarded-auto ask list. Ordinary rm, builds, installs, Git changes, elevated, unknown and opaque commands all run. File tools can read/change paths across the drive, including absolute paths and ~/, and commands may start outside the project. The mode badge reads unguarded auto. Use only when you trust the task and the files/code the agent will encounter.

The only exceptional action-confirmation gate recognizes very large irreversible operations: recursive deletion of a filesystem root, whole home/user tree, named system root or whole mounted volume (including whole-tree glob variants); destructive diskutil erase/partition/APFS-delete operations; formatting a raw disk with mkfs/newfs; dd writing a raw disk; or shell output redirected to a raw disk. Recognized literal sudo/env wrappers, compound commands and sh/zsh/bash -c bodies are checked too. These show the exact command/canonical cwd/timeout and require an explicit yes for that command only, without disabling unguarded auto. Denial, cancellation or revocation prevents that command from starting.

This is a best-effort recognizer, not a safety guarantee: it does not inspect script files, aliases, arbitrary expansions, remote code or general interpreter programs, and it does not make opaque commands safe. Unrecognized actions still run without a prompt in unguarded auto; do not rely on this gate to protect your machine.

This is current-account access, not automatic root/sudo or a bypass of macOS privacy permissions. No interactive password entry is provided. Tool byte/tree limits, regular-file and symlink-tree rules, no-overwrite copy/move and permanent-deletion guards, bounded command output/time and cancellation remain. Commands can still perform permanent deletion. Known router credentials are rejected in commands and redacted from command output and unguarded file-tool results/errors before history; this is not comprehensive secret isolation or a network sandbox.

Unguarded auto is session-only, never saved or enabled by auto_approve. /mode ask disables it; queued unguarded commands/mutations recheck their mode generation before executing. Turning it back on cannot revive old grants. Running commands are not interrupted by a mode change; use Ctrl+C to stop the active turn. File tools recheck live authorization at bounded checkpoints and before commits; revoked copies roll back new destination data.

Command cancellation

Ctrl+C, /clear, /exit, timeout and output overflow terminate the process group, with TERM→KILL escalation. Normal shell completion also cleans up ordinary background descendants. There is no persistent-background-job or interactive-input API; programs requiring a TTY or password entry may fail. Descendants that detach into another process group/session can escape cleanup; no sandbox guarantee is made. Commands serialize with project mutations in this process and recheck cancellation before queued execution starts. Tool results remain paired with calls after cancellation, so continuing the conversation does not send broken tool history.

For example: “Create a small website in site/, copy my assets into it, and run its tests.” In ask mode, the agent creates the structure with approval and asks before running tests. In guarded auto, the project work runs without per-call prompts, but a network install or sudo still asks. Package installs and downloads are performed through authorized commands, not silently through filesystem tools.

Development

src/index.ts       entry, flags, bootstrap      test/            node:test suites
src/client.ts      RouterClient (HTTP, retry)   scripts/         SSE harness (live)
src/adapters/      openai.ts, anthropic.ts
src/tools.ts       file tools + confinement     src/loop.ts      agent round driver
src/repl.ts        REPL shell                   src/lineinput.ts raw-mode editor
src/sessions.ts    transcripts
src/markdown.ts    terminal markdown renderer

Live wire-shape verification:

node scripts/sse-harness.ts --model <id> --dialect openai|anthropic|auto [--tools] [--save test/fixtures/x.sse]

Manual E2E checklist

  • [ ] flash --check green; npm test green; npm run typecheck clean
  • [ ] /model → arrow-key picker → streamed chat answers (markdown-rendered)
  • [ ] multi-tool task (create → edit → grep) exercising y / a / n approvals
  • [ ] Page Up/Down scroll chat with input anchored, including mid-stream; mouse drag selects text
  • [ ] Ctrl+V queues a clipboard screenshot without submitting; Ctrl+X clears pending images
  • [ ] text Cmd+V still works; / palette has no image commands; /help opens shortcuts
  • [ ] /clear starts a fresh transcript; session files exist
  • [ ] type during streaming; Enter queues steering and leaves tool/result ordering valid
  • [ ] an unfinished draft survives a permission question and turn completion
  • [ ] Ctrl+C mid-stream prints *[stopped]* and restores any unapplied steering
  • [ ] flash -p "hi" one-shot; echo "…" | flash piped mode works
  • [ ] bad key exits with the 401 hint; FLASHLITE_BASE_URL pointing at a dead port reports a network error, not a crash
  • [ ] flash --debug shows frame sizes and retry metadata without message content or credentials

Notes

  • Zero runtime deps — src/ and scripts/ use Node built-ins only.
  • Never print an API key — not even masked. Real keys live only in the local config.
  • Diagnostic logging never includes message content. Private transcripts live under ~/.config/…/sessions/; normal UI/one-shot output shows the requested answer. --debug reports metadata only, never raw SSE payloads.