senclaw/claude-code

app A coding agent as a Space App: point it at a folder, describe the work, and it plans, reads, edits and runs commands across many turns until the job is done. Powered by Claude. Four trust levels — read-only planning, approve every tool call, auto-accept file edits, or fully unattended. An approval card shows each command before it runs and lets you edit it first

Install

senclaw app install senclaw/claude-code

Verifies sha512 and SenClaw’s signature before writing to disk.

Documentation

Claude Code (Space App)

A coding agent as a Space App. Point it at a folder, describe the work, and it plans, reads, edits and runs commands over many turns — with an approval gate, a recorded timeline, and a git marker taken before it starts.

  • Port 4790 · MCP **claude-code-mcp** at /api/mcp/sse
  • DB: ~/.senclaw/space-apps/claude-code/coder.db (override SENCLAW_DATA_DIR)
  • Drives the **user's own claude binary** — nothing is bundled, no provider key of the app's own. Token spend is reported back with usage.report.
apps/claude-code/
├── src/
│   ├── main.rs     serve web/dist + /api, or dispatch a CLI subcommand
│   ├── agent.rs    spawn `claude -p`, duplex NDJSON, sessions, SIGTERM, usage
│   ├── api.rs      REST + SSE, app state
│   ├── audit.rs    timeline from the event stream, git markers, import
│   ├── mcp.rs      two MCP servers: per-run approval + the control surface
│   ├── pty.rs      integrated terminal: WebSocket ↔ PTY (shell or `claude`)
│   ├── workspace.rs  path-safe file read/write/search for the editor
│   ├── cli.rs      run / history / timeline / sessions / replay / import / health
│   └── db.rs       SQLite (WAL): meta + agent_runs + agent_events
├── web/            React + Vite, plain CSS
├── skills/ personas/
└── scripts/pack.sh

Why it does not use the LLM bridge

Every other app talks to the daemon through llm.request: one system prompt plus one user prompt in, one block of text out, 32k output cap, no tools, no streaming. Editing a repository takes many turns of read/edit/bash over minutes. So this app runs claude as a child process and speaks its duplex NDJSON protocol instead. Background and rationale: [docs/claude-code-integration.md](../../docs/claude-code-integration.md).

Trust levels

ModePermission modeFor
Hỏi (ask)planexplore and propose; edits never auto-approve
Duyệt (review)defaultevery tool call shows an approval card
Cho sửa (edit)acceptEditsfile edits auto-approve, everything else asks
Tự chạy (auto)bypassPermissionsthe UI default — never asks; hard deny rules still hold
Khoá (locked)dontAskdenies anything not pre-approved; what MCP and the CLI use

auto and locked are both unattended and they are not the same thing. dontAsk denies rather than prompting, so a run with no allow list can barely do anything — correct for a caller with nobody to ask, wrong for a button labelled "just get on with it". So the UI defaults to auto, while the MCP control surface and claude-code run default to locked: a caller should not be handed bypassPermissions by default.

Approval works through --permission-prompt-tool: Claude Code calls an MCP tool this app serves, the call parks until the UI answers, and the answer comes back as a PermissionResult. You can edit a command before allowing it.

Three things hold regardless of mode:

  • Deny rules (sudo, rm -rf /, curl … | sh, ~/.ssh) are evaluated before the permission mode, so edit and auto cannot loosen them.
  • **--bare** keeps this machine's hooks, plugins, MCP servers and CLAUDE.md out of a run. It needs ANTHROPIC_API_KEY; without one the app falls back and reports degradedReason rather than failing or staying quiet.
  • Workflows are off (disableWorkflows). A workflow launched under claude -p starts with no prompt at all, and its subagents run acceptEdits whatever the session mode — it walks straight past the approval UI.

Audit trail

The timeline is built from the NDJSON stream, not from Claude Code hooks.

Hooks were the obvious design and they do not work here: Claude Code skips hooks under --bare, including hooks handed to it inline through --settings (verified against 2.1.227 — the session reaches system/init and runs to a result, and the hook never fires). Dropping --bare to regain hooks would pull the user's own hooks and MCP servers into every run. Reading the stream gives the same rows, costs no subprocess per tool call, and behaves identically in every permission mode.

Each run also gets a git marker before the agent touches anything: HEAD plus a git stash create commit capturing the dirty tree. git stash create writes the commit object without modifying the working tree or pushing onto the stash list, so the marker cannot surprise someone who is mid-edit. GET /diff compares against it — which is the honest answer to "what changed", because Claude Code's own file checkpointing misses anything done through Bash.

Transcripts written outside the app can be folded in with import; re-importing the same session replaces it rather than doubling it.

REST

EndpointDoes
GET /status · GET /healthopen folder; is claude installed and keyed?
POST /openchoose the folder runs default to
POST /run → GET /events?runId=start a run; SSE of every message, backlog first
POST /permitallow / deny / allow-with-changes
POST /send · POST /stopanother turn into the same process; SIGTERM
GET /history · GET /timeline · GET /diffthe audit trail
GET /sessions · GET /transcript · POST /importClaude Code transcripts
GET /terminal (WS)integrated terminal; ?cmd=claude launches the interactive CLI
GET/POST /settingsauth mode and agent teams
GET /commandsslash commands, skills and agents this machine's claude offers
POST /execrun one shell command and record it in the timeline
GET /teamteammates and the shared task list for a run
GET /tree · GET/POST /file · GET /searchworkspace files for the editor

Workspaces

Several folders can be open at once and each runs its own agent independently: a run is keyed by its own cwd, so nothing below the UI had to change to support it. Each workspace's panels stay mounted and only the active one is shown, so a run in one folder keeps streaming while you work in another. Terminals are the exception — they cost a real process, so they wait for first use.

Pick a folder with the + button. The picker marks git repos, because a run outside one has no marker to diff against afterwards.

Authentication

Two modes, and the choice decides isolation as much as billing:

ModeWhat it doesCost
API key (default)--bare + ANTHROPIC_API_KEYIsolated: none of this machine's hooks, plugins, MCP servers or CLAUDE.md load
Claude Pro / Teamuses the login claude already hasNot isolated: --bare must be dropped, so all of the above become active in the run

Picking API key with no key present falls back to the subscription path rather than failing, and says so in degradedReason. Deny rules and the approval gate are CLI flags, so they hold in both modes.

Slash commands and skills

/name works in the prompt box: Claude Code expands user-invoked skills and custom commands in -p mode before running. ⚙ → Kiểm tra lists what this machine offers, read from the system/init event — which Claude Code emits before the first API call, so the probe costs no tokens.

Agent teams

Off by default. When enabled, the child gets CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 and can spawn teammates: separate Claude Code instances with their own context that coordinate through a shared task list. GET /team reads the team config and task list Claude Code writes under ~/.claude/teams/session-<id8>/ and ~/.claude/tasks/session-<id8>/.

Teammate permission prompts surface in the lead session, so they still reach the approval gate. Teams are experimental in Claude Code itself and cost several times the tokens of one session, which is why they stay opt-in here.

Files

A file tree and a CodeMirror editor over the open workspace, so you can read what the agent is about to change and fix it by hand. Cmd/Ctrl+S saves. Paths are resolved through the same guard the agent's tools use: anything resolving outside the workspace is refused, on read and on write.

Staying alive

A Space App defaults to on-demand: the daemon starts it and its reaper stops it about 60s after the last request through the daemon's proxy. This app's UI is an iframe that talks straight to its own port, so from the daemon's side it looks untouched from the second it boots — it was being stopped mid-session, and the symptom was the terminal going [terminal đã đóng] and REST calls failing.

src/keepalive.rs reports the app's own use: any request that is not the keepalive ping marks activity, and while activity is recent the app pings its own proxy path on the daemon, which is exactly what a real user request would have done. The web UI heartbeats every 30s while its tab is visible, so an open but idle tab counts. Close the tab and the pings stop, and the reaper takes the app down as intended. SENCLAW_KEEPALIVE=0 disables it.

Terminal

A real PTY in the open folder, bridged to xterm.js. Two buttons:

  • Shell — the ordinary reasons: run the tests yourself, read git log, check what the agent just did.
  • claude (tương tác) — the user's own interactive CLI, the way the other wrappers offer it: their login, their hooks, their MCP servers, their prompts. A plain claude does not read this app's --settings, so the agent-teams toggle is passed to it as the env var it does read.

The second one deliberately runs outside this app. The approval gate, the deny rules and the audit trail govern runs (/api/run), not whatever someone types at a shell. The UI says so on the panel rather than letting the two blur together — a terminal that looked governed but was not would be worse than no terminal at all.

MCP — how another app drives this one

/api/mcp/control exposes coder_run, coder_status, coder_timeline and coder_history. This is the seam for BA Studio: read a user story out of ba-mcp, hand it to coder_run, read back what the agent actually did.

claude --mcp-config '{"mcpServers":{
  "ba":   {"type":"sse","url":"http://127.0.0.1:4740/api/mcp/sse"},
  "coder":{"type":"sse","url":"http://127.0.0.1:4790/api/mcp/sse"}}}'

Runs started over MCP are unattended by construction: there is no UI on the other end to answer an approval card, so they deny anything outside allow rather than parking forever. That is also why autoRegister is on despite the tool being able to edit a repo: a run with no allow list in dontAsk mode can barely do anything, and an MCP server that silently fails to appear is a worse failure than one the user can see and disable.

Two transports, on purpose

PathShapeWho uses it
GET/POST /api/mcp/sse + POST /api/mcp/messagelegacy MCP HTTP+SSEthe SenClaw daemon, like the other 40 apps in this repo
POST /api/mcp/controlstreamable HTTPcallers configured that way; same handler, alias only
POST /api/mcp/<runId>streamable HTTPClaude Code's --permission-prompt-tool, per run

The daemon's MCP client opens an SSE stream and reads replies off it. A control server that only answered POST returned 405 on the GET and never connected — verified against a running daemon, which now reports status: "connected" and all four tools. Claude Code is happy with streamable HTTP either way, which is what the per-run permission server uses.

What the daemon picks up

Registering with register-local gives the daemon the manifest, and it copies the skill to ~/.senclaw/managed/skills/<name>/ tagged source: app:claude-code. It does not copy personas — no app's persona appears under managed/, including the 33 others that declare one — so the persona here is declared for correctness and for whenever the daemon starts reading them.

CLI

The same binary, so CI and cron need no iframe and no daemon:

claude-code health                       # exit 1 when claude is missing or unkeyed
claude-code run "fix the failing test" --mode auto --max-turns 20
claude-code run "audit auth.rs" --json   # result object incl. total_cost_usd
claude-code history                      # recorded runs
claude-code timeline <run-id>            # what one run actually did
claude-code import <session-id>          # fold a transcript into the timeline

run exits non-zero when the agent failed. --mode review is refused there: it parks every tool call waiting on a UI a shell does not have. CLI runs write to the same audit db as the server, so a CI run shows up in the history too.

Naming

Anthropic's Agent SDK terms list the permitted forms as "Claude Agent", "Claude", and "<YourName> Powered by Claude", and name "Claude Code" among the forms that are not permitted for third-party products. This app is currently named claude-code by explicit choice; if it is ever published to a public hub, that is the thing to revisit first.

Develop

cargo run -p claude-code                      # API on :4790
cd apps/claude-code/web && npm install && npm run dev
cargo test -p claude-code
apps/claude-code/scripts/pack.sh              # -> claude-code-app.zip

Register with a running daemon:

curl -X POST http://127.0.0.1:18788/api/space/apps/register-local \
  -H 'Content-Type: application/json' \
  -d '{"path":"'"$PWD"'/apps/claude-code"}'

Declared permissions

{
  "network": [
    "127.0.0.1"
  ],
  "exec": [
    "./claude-code",
    "claude"
  ]
}

Declared by the publisher, not enforced by SenClaw. The CLI compares this against what you already have installed and asks before anything widens.

Published by

@senclawtrusted
benji
51 packages · 21 downloads · member since 2026-07 · GitHub since 2015-06

The checkmark means SenClaw confirmed who this publisher is. It says nothing about whether their packages are safe.

About

Owner@senclaw
Latest1.0.0
Downloads0
Repositorygithub.com/NortonBen/senclaw-app ↗

Versions

VersionTagsArtifactsPublished
1.0.0latestdarwin-arm64, linux-x64, win32-x642026-08-15

Artifacts · 1.0.0

PlatformFormatSignatureFileSize
darwin-arm64zipunsignedclaude-code-app.zip3947 KB
linux-x64zipunsignedclaude-code-app.zip4395 KB
win32-x64zipunsignedclaude-code-app.zip4396 KB

Unsigned builds require sign-in to download and are not distributed to the auto-updater, because unsigned executables from a new domain get flagged as unsafe. Sign and notarize (macOS) or Authenticode-sign (Windows) to distribute publicly.

Auto-update endpoints

For a Tauri app — add to tauri.conf.json:

"endpoints": ["https://senclaw.bacnd.com/api/v1/updates/tauri/senclaw/claude-code/{{target}}/{{arch}}/{{current_version}}"]

For an electron-updater app — generic provider URL:

https://senclaw.bacnd.com/updates/senclaw/claude-code/stable

Only signed/notarized artifacts are offered as updates — a client fetches the update without credentials, and unsigned binaries are not publicly downloadable.

Something wrong with this package?

Report this package

Report this package

Tell us what is wrong and a moderator will look at it. Be specific — a report that names a version and a behaviour can be checked in minutes; one that says “looks sketchy” cannot be acted on at all.

Not about this package? Contact us instead. What happens to a report is described in reporting and moderation.

API: GET https://senclaw.bacnd.com/api/v1/packages/senclaw/claude-code