New:Socket for Asana Is Now Available.Learn more
Get Started

scofield

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install
Package was removed
Sorry, it seems this package was removed from the registry

scofield

Plan-first terminal coding agent — it shows you the plan before it touches your code, with an arcade to play while it works

latest
npmnpm
Version
0.1.0
Version published
Weekly downloads
0
Maintainers
1
Weekly downloads
 
Created
Source

scofield

A plan-first terminal coding agent, with an arcade to play while it works.

sco is the agent: it plans before it touches anything, shows you a diff before every change, and applies nothing you have not approved. Beside it is the daemon and arcade this project started as — real development activity (tool calls, tests, commits, whether it comes from sco or from a Claude Code session via hooks) feeds a deterministic XP and quest system, and there is a game to play while the work runs. All game content, narration, and low-value prompt offload run against a local LM Studio model, so the play layer costs zero cloud tokens.

Install

Requires Node 22 or newer. better-sqlite3 declares >=22, and on Node 20 npm only warns before the daemon segfaults inside new Database(). The floor is declared in package.json with engine-strict, so an unsupported Node fails at install with a clear message instead.

npm install -g scofield     # puts `sco` on your PATH

Or from a clone of this repository:

npm install && npm link

sco, the agent

vibevault run --only ANTHROPIC_API_KEY -- sco     # a session in this repository
vibevault run --only ANTHROPIC_API_KEY -- sco "add rate limiting to /login"

Without linking, run it in place instead: npx sco "..." from inside this directory, or node bin/sco.mjs "..." from anywhere. More on sco itself in sco — the agent core below.

The arcade — daemon, web UI, TUI, VS Code

As a Claude Code plugin — the daemon starts itself on the next session:

/plugin marketplace add lunaos-ai/luna-code
/plugin install scofield@sco

Or run the daemon directly, from the same clone:

npm start          # daemon on 127.0.0.1:7777
npm run dev        # same, with auto-reload

Then open http://127.0.0.1:7777/web/, or run node tui/index.mjs for the terminal surface.

In VS Code

extensions/vscode/ is a fourth surface: a sco icon in the editor title bar, and level, streak and phase in the status bar.

cd extensions/vscode && npx @vscode/vsce package
code --install-extension scofield-1.0.0.vsix

It is a viewer, not a second daemon — it starts nothing and stores nothing, and the Claude Code plugin still owns the daemon's lifecycle. It is also the quietest surface deliberately: an editor is the most intrusive place this product appears, so a level-up changes a number in the status bar and does nothing else. No notification, no focus steal, no panel you did not open.

LM Studio is optional. Without it the daemon serves static content packs and reports offline for LM-backed endpoints; games, XP, and quests are unaffected.

Sharing

Everything above is local. Nothing about you leaves the machine unless you ask for it, and the daemon never makes an outbound request.

GET /api/progression/card.svg renders your card locally, which is all you need to look at it. To embed it somewhere public — a GitHub README — an image proxy has to fetch it, and it cannot reach 127.0.0.1. So GET /api/share/url encodes your stats into the URL itself and points at a stateless renderer:

curl 127.0.0.1:7777/api/share/url

The renderer at luna-share.*.workers.dev has no database, no KV, no accounts, and no request logging — wrangler deploy reports "No bindings found". It is a pure function from URL to image. Nothing is stored because there is nowhere to store it.

Two things worth being straight about:

  • A card is self-asserted, exactly like any other README badge you write yourself. There is no account to verify it against, and signing would not help: a shared secret shipped inside a local-first tool is not a secret.
  • Publishing a URL publishes the stats inside it. That is the whole trade, and it only happens when you paste the link.

Self-host it in one step — workers/share/ is a single Worker:

npx wrangler deploy --config workers/share/wrangler.jsonc
# then point the daemon at it
SCO_SHARE_BASE=https://your-worker.workers.dev

sco — the agent core

sco is its own agent loop and its own identity, living beside the daemon above: it plans before it touches anything, shows you a diff before any file change, and treats the daemon and arcade as an optional play layer rather than something it depends on.

Run it two ways. sco on its own, in a repository, opens a session — a prompt you type at, where each request is asked of something that remembers the last one, and the whole sitting lands in one transcript:

$ sco
sco · plan-first coding agent

in ~/projects/api  · 3 skills

Type what you want done. /help for commands, Ctrl-C to stop a turn.

● raise the rate limit

Inside a session: /help, /skills, /context, /clear, /exit. Ctrl-C cancels the turn in flight and hands the prompt back — it does not kill the session. Ctrl-D leaves.

sco "one request" is the same agent for a single request, which is the form a script wants. See Install above; flags are --yes (approve the plan and every diff without asking), --dry-run (show everything, change nothing), --sessions (list past runs), --resume (continue the last run, or --resume=<id> a named one — it replays that conversation and then plans again, since an old approval is not consent for a new run), and --no-events (skip notifying the daemon).

Skills

sco reads the same skills Claude Code does: a directory with a SKILL.md in .claude/skills/, either in the repository or in your home directory. The frontmatter's name and description are shown to the agent — one line each — and it loads a body only when it decides one applies, so twenty skills cost twenty lines of context until one is needed. A repository skill wins a name clash with a personal one.

Loading a skill is a read, not an execution. It puts instructions in the window; it widens nothing, and every change they lead to still arrives at the diff gate.

Its terminal surface is a design system in ui/, derived from Apple's HIG: three type tiers, five semantic colour roles, a left rail instead of boxes, and an ASCII fallback for terminals that cannot draw the glyphs. Colour is never the only signal, and nothing you are asked to approve is ever shortened to fit — read ui/README.md for the rules.

It is a separate binary (bin/sco.mjs) over a separate library (agent/) — read agent/README.md for the module map, the invariants it enforces, and the limitations it documents honestly rather than hides. The design rationale — why a library and not a service, why two approval gates instead of one — is in docs/superpowers/specs/2026-08-06-scofield-agent-core-design.md.

Development

npm test           # ~1200 tests; boots its own throwaway daemon
npm run lint
npm run format
npm run check      # lint + format + manifest + tests
pushci run         # the full pipeline: quality, Node 22/24, Linux, worker build

CI is pushci (pushci.yml), not GitHub Actions. It runs the matrix explicitly — nvm for the Node axis, docker for Linux — because pushci migrate silently drops matrices, and that matrix is what caught the Node 20 segfault above.

Architecture

Claude Code ──hooks──▶ sco daemon (127.0.0.1:7777) ──▶ LM Studio (127.0.0.1:1234)
                             │  SSE event bus
                 ┌───────┬───┴───┬────────────┐
            web arcade  VS Code  TUI   plugin commands

The daemon is the only stateful component. The plugin, web arcade, and TUI are thin clients over its HTTP and SSE API. Everything binds to 127.0.0.1 only.

Features

  • ~15 arcade games — 2048, Snake, Tetris, Pong, Wordle, Breakout, and more
  • Local LM intelligence — game content, narration, retrieval all served by LM Studio
  • Deterministic XP — levels and quests derived from real Claude Code activity
  • Four surfaces — web arcade (primary), VS Code, terminal TUI, slash commands
  • Local by default — all progression is local, no accounts, no telemetry. Sharing is opt-in and stateless (see Sharing)

API Endpoints

MethodPathPurpose
GET/api/healthHealth check
POST/events/sessionSession opened
POST/events/prompt-startClaude began working
POST/events/tool-useTool call observed — classified and awarded XP
POST/events/prompt-endClaude finished — awards XP
POST/events/agent/:typeA sco run reports itself — awards XP for real work
GET/events/streamSSE feed for web/TUI clients
GET/api/gamesGame registry listing
POST/api/lm/contentGenerated game content by kind
POST/api/lm/askCheap-task offload completion
POST/api/rag/queryResource retrieval + answer
POST/api/rag/reindexRebuild corpus index
POST/api/progression/scoreGame reports a score/run result
GET/api/progression/profileXP, level, active quests
GET/api/progression/summaryLevel, streak, momentum, combo, quests, badges
GET/api/progression/streakStreak summary + 60-day contribution strip
GET/api/progression/achievementsThe 58-badge catalog with progress
GET/api/progression/questsToday's 3 daily and this week's 2 weekly quests
GET/api/progression/rulesThe complete reward table (see below)
GET/api/progression/sharePlain-text share card, rendered locally
GET/api/progression/card.svgThe same card as an SVG (?theme=dark|light|auto)
GET/api/progression/badge.jsonShields-compatible badge payload (?kind=level)
GET/api/share/urlPublic card/badge links, built locally; nothing is sent
GET/api/attentionWhether Claude is working, and whether arcade XP is open
GET/api/games/:slug/historyRecent runs for one game
GET/api/statsAggregate totals — games played, XP, avg score, play time
POST/api/lm/quizMultiple-choice quiz generated from the retrieved corpus

XP Rules

XP comes from real development activity, not only from games. The rules are pure functions in daemon/progression/rules.mjs, so the reward table is one file.

EventXP
Prompt completed (Stop hook)10
Test suite passes25
Test suite fails0 — never penalised
Commit created15
File edited (Edit/Write)3
Any other successful tool call1
Failed tool call0

Commands are matched per shell segment, so echo "npm test" is not a test run and git log --format=commit is not a commit.

A tight loop of real work builds a combo (max 3.0x) that multiplies dev XP only. Levels follow the curve in daemon/progression/levels.mjs: 80 XP to level 2, rising to a permanent cap of 2000 XP per level from level 19 on.

The arcade is deliberately worth less than your work

Arcade outcomeXP
Won4
Lost / abandoned1
Run under 5s, or within 20s of the last one0

Four rules bound arcade XP, all enforced by the daemon rather than any client:

  • The yield. When Claude finishes, arcade XP stops. The game keeps playing, the score and your personal best still record — only the reward yields. The next prompt reopens it. Nothing counts down and nothing nags.
  • Per-window budget. At most 3 scoring runs or 10 XP per working window.
  • Dev headroom. Cumulative arcade XP can never exceed 25% of cumulative dev XP, so the arcade alone never levels anyone up and play can never be the majority of anyone's progress.
  • No time rewards. The same run pays the same whether it lasted six seconds or an hour, and there is no first-game-of-the-day bonus.

GET /api/progression/rules returns this whole table at runtime. No award in this product depends on chance or on the clock.

The 25% came from measurement rather than taste. tests/simulation/personas.mjs runs six developers the tuning was never written against — a reader who rarely commits, a three-days-on contractor, someone who works past midnight, a TDD practitioner, a returner after ninety days, and one built to grind the cabinets. Across every shape people actually work in, arcade XP lands at 2–19% of dev. The grinder rode the old bound to 99%, which meant playing roughly doubled his level. A quarter costs the other five nothing and binds only him.

Configuration

Edit sco.config.json:

{
  "port": 7777,
  "lmStudio": {
    "baseUrl": "http://127.0.0.1:1234/v1",
    "chatModel": "qwen/qwen3.6-35b-a3b",
    "embedModel": "text-embedding-nomic-embed-text-v1.5",
    "timeoutMs": 3000
  },
  "autoOpenArcade": false,
  "surface": "web",
  "offload": { "enabled": true, "maxInputChars": 4000 }
}

Environment variables override the config, mainly so tests and multiple checkouts do not collide:

VariableEffect
SCO_PORTPort the daemon binds (overrides port)
SCO_DBPath to the SQLite file (default sco.db at the repo root)
SCO_LM_BASE_URLOverrides the LM endpoint; empty string disables the LM
SCO_ARCADEoff turns the arcade off entirely
SCO_SHARE_BASEOverrides where /api/share/url points (self-host the renderer)

Upgrading from an older install: on first boot, if sco.db does not exist yet, the daemon looks next to it for luna.db (or the older finsavvy.db) and renames whichever it finds into place — every level, badge and streak carries over automatically. This happens once; if sco.db already exists, the old file is left untouched.

Turning the arcade off

SCO_ARCADE=off

or { "arcade": { "enabled": false } } in sco.config.json.

The game list empties, nothing opens, and arcade events stop. XP, quests, streaks and achievements keep accruing from your actual work exactly as before, and turning it back on restores every value untouched — nothing is deleted and nothing is forfeited. It is one variable, and it costs you nothing.

Repository Layout

bin/sco.mjs                     # sco CLI — args, approval gates, diff rendering
agent/                          # sco's agent loop (see agent/README.md)
.claude-plugin/plugin.json      # plugin manifest (hooks declared inline)
hooks/                          # session-start, prompt-submit, post-tool-use, stop
commands/                       # play.md, quest.md, profile.md
daemon/
  server.mjs                    # HTTP + SSE, static game hosting
  lm/client.mjs                 # OpenAI-compatible LM Studio client
  lm/router.mjs                 # game content prompt routing
  lm/fallback.mjs               # static content packs when LM Studio is down
  lm/offload.mjs                # cheap-task offload classification
  rag/index.mjs                 # embedding index build + query
  rag/chunker.mjs               # corpus row -> chunk
  progression/rules.mjs         # deterministic XP rules (pure)
  state/store.mjs               # SQLite ledger, profile, quests, scores
scripts/run.sh                  # start/stop/status/tui/open — daemon process manager
scripts/validate-plugin.mjs     # manifest check, run in CI
games/
  _sdk/sco-game.js               # postMessage host API for games
  registry.json                 # slug, title, tags, LM content kinds
  <slug>/index.html             # vendored games
web/                            # arcade shell, catalog, quest HUD
tui/                            # terminal fallback renderer
workers/share/                  # stateless share-card renderer (self-host target)
data/resources.csv              # corpus (from THE_RESOURCES_TABLE.csv)
sco.config.json                 # models, port, toggles

Failure Modes

FailureBehavior
Daemon not runningHooks no-op. Claude Code unaffected. /play starts it.
Port 7777 takenDaemon probes /api/health; foreign occupant → error in /play only.
LM Studio downStatic content packs; no narration; /ask-local reports offline.
Model returns invalid JSONOne retry with stricter prompt, then static fallback.
Game crashesFrame isolated; arcade shell shows reload control; no XP awarded.

License

MIT — see LICENSE

Keywords

ai

FAQs

Package last updated on 08 Aug 2026

Related posts