Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
403b62a
C0: import slack-coding-agent scaffold
renxiawang May 31, 2026
c594c9c
C1: wire up Vitest + fold tests into the check gate
renxiawang May 31, 2026
5607890
C2: deterministic, reversible sandbox naming
renxiawang May 31, 2026
c480e9c
C3: pure per-thread state machine + coalescing
renxiawang May 31, 2026
8f3393a
C4: TTL dedupe set for idempotent dispatch
renxiawang May 31, 2026
b900d9d
C5: agent-state pure functions (transcript ledger + delta)
renxiawang May 31, 2026
fe5461f
C6: boundary logging — builders + best-effort sinks
renxiawang May 31, 2026
6979567
C7: pure sbx argv builders + shell quoting
renxiawang May 31, 2026
20b1c95
C8: Codex backend — the sole quarantine of agent format
renxiawang May 31, 2026
733d4f8
C9: AgentStateStore — per-thread durable state I/O
renxiawang May 31, 2026
c83fa88
C10: SbxProvider — the sbx I/O shell
renxiawang May 31, 2026
aaaa00b
C11: dispatcher — the one-turn lifecycle (keystone)
renxiawang May 31, 2026
cb9a554
C12: router glue — dedupe, FSM, per-thread mutex, coalescing
renxiawang May 31, 2026
ee30a60
C13: Slack adapter — pure mapping + Socket Mode shell
renxiawang May 31, 2026
6c30782
C14: CLI composition root + onboarding gate
renxiawang May 31, 2026
b1e53ee
C15: AGENTS.md (+ CLAUDE.md) + refreshed README
renxiawang May 31, 2026
663b4cf
C16: idle-sweep eviction + wire into the CLI
renxiawang May 31, 2026
bd3c559
Live: verify the sbx I/O seam against a real microVM
renxiawang May 31, 2026
4a10c86
Live (codex): fix headless stdin hang + relay real errors
renxiawang May 31, 2026
ad8f3f6
Live (E2E): tolerate sbx info lines in `sbx ls --json`
renxiawang May 31, 2026
a56d1f0
Live (E2E): hostname-safe sandbox names (encode '.' as '-')
renxiawang May 31, 2026
4cadf99
Live (E2E): provision the repo + run codex in it (§4.3)
renxiawang May 31, 2026
398d1c2
docs: rewrite README to lead with user-facing usage
renxiawang Jun 1, 2026
eaf66be
docs: address README review — drop unimplemented "grab a file", expan…
renxiawang Jun 1, 2026
c9b3b73
docs: sbx does not require Docker Desktop (verified live)
renxiawang Jun 1, 2026
bcc0912
refactor(backend): share SandboxShellExecutor via the backend barrel
renxiawang Jun 1, 2026
72e0a0b
test(backend): add Claude Code stream-json fixtures (placeholders)
renxiawang Jun 1, 2026
0fc2440
feat(backend): add the Claude Code backend (turnArgs/parseResult/sess…
renxiawang Jun 1, 2026
efd4011
feat(cli): select the Claude backend when SCA_AGENT=claude
renxiawang Jun 1, 2026
68feb87
test(backend): live-verify Claude backend; real error fixture
renxiawang Jun 1, 2026
e5e1c4e
feat(onboarding): simplify Claude login to one command (sbx run claude)
renxiawang Jun 1, 2026
5ea077a
test(backend): real success fixture; live-verify Claude resume end-to…
renxiawang Jun 1, 2026
31aa9bb
refactor: rename the project to Mishu
renxiawang Jun 1, 2026
ba2f14b
docs: drop the design-spec attachment path references
renxiawang Jun 1, 2026
ebcc870
feat(cli): add `npm run setup` guided onboarding
renxiawang Jun 1, 2026
edc0ac5
feat(cli): setup prompts for the agent + guards headless runs
renxiawang Jun 1, 2026
b4ad949
feat(cli): setup removes the Claude login sandbox afterward
renxiawang Jun 1, 2026
9e16d7c
refactor(backend): share agent-agnostic parsing helpers across backends
renxiawang Jun 1, 2026
43a9c47
fix(router): drop a stray NUL byte from the sandbox-name hash input
renxiawang Jun 1, 2026
f0c155b
perf(sandbox): cache $HOME per sandbox to cut ~5 sbx execs per turn
renxiawang Jun 1, 2026
a121679
chore(githooks): run the gate on pre-commit
renxiawang Jun 2, 2026
82f875e
docs(agents): trim spec refs and stale sections, document the hook
renxiawang Jun 2, 2026
df550ae
docs: remove design-spec (§) references from code comments
renxiawang Jun 2, 2026
83db081
docs(readme): add "work in the open" philosophy, trim Status section
renxiawang Jun 2, 2026
9e72d19
chore: prepare branch for open source
renxiawang Jun 3, 2026
556d4d9
feat: support optional sandbox template
renxiawang Jun 3, 2026
10fae52
docs: add README workflow image
renxiawang Jun 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
APP_SLACK_APP_TOKEN=xapp-...
APP_SLACK_BOT_TOKEN=xoxb-...
MISHU_REPO=/path/to/target/repo

# Optional:
# MISHU_AGENT=codex
# MISHU_SANDBOX_TEMPLATE=myregistry/image:tag
# MISHU_LOG_LEVEL=summary
# MISHU_BOT_USER=U...
5 changes: 5 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh
# Enforce the single gate — keep the repo green at every commit.
# Runs Biome (lint+format+organize-imports) + tsc --noEmit + Vitest — all offline.
# In a genuine pinch, bypass with `git commit --no-verify`.
exec npm run check
16 changes: 16 additions & 0 deletions .github/workflows/audit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Audit

on:
schedule:
- cron: "0 6 * * 1" # weekly, Mondays 06:00 UTC
workflow_dispatch:

jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm audit --omit=dev
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci --ignore-scripts
- run: npm run check
- run: npm run build
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules/
dist/
*.log
.env
.env.*
!.env.example
.DS_Store
coverage/
76 changes: 76 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# AGENTS.md

Rules for contributors and coding agents working in this repo.

Mishu is a Slack-to-coding-agent router. A Slack thread maps to one `sbx` sandbox and one coding-agent
session. The router is plumbing, not an LLM.

## Build And Test

- Run `npm install` once.
- Run `npm run check` before every commit. It runs Biome, `tsc --noEmit`, and Vitest.
- Use `npm run check:fix` for formatter/import fixes.
- Use `npm test`, `npm run test:watch`, or `npm run test:cov` for Vitest.
- Use `npm run build` to emit `dist/`.
- Use `npm run test:live` only for live `sbx` checks; it is not part of CI.

The pre-commit hook in `.githooks/pre-commit` runs `npm run check`.

## Module Rules

- ESM only. Relative imports include `.js`.
- Use `import type` for type-only imports.
- TypeScript is strict, with `noUncheckedIndexedAccess` and unused checks enabled.
- New pure logic should have colocated `*.test.ts` coverage.

## Architecture Invariants

```
Slack <-> PlatformAdapter <-> Router <-> SandboxProvider <-> sandbox
\-> CodingBackend
```

- The router does not call models, inspect intent, or decide whether to open PRs.
- Agent output is an opaque byte stream until it reaches `CodingBackend`.
- Codex-specific parsing lives in `src/backend/codex.ts`.
- Claude-specific parsing lives in `src/backend/claude.ts`.
- The router has no durable database. Per-thread state lives inside the sandbox under
`~/.agent-state/`.
- Router memory may hold only transient locks, dedupe state, and pending/running state.
- Keep pure code separate from I/O shells. Pure modules should not import `node:child_process`,
`node:fs`, or Slack SDK packages.

## Router Lifecycle

- ACK Socket Mode envelopes immediately and handle work asynchronously.
- Drop Slack retries with `retry_num > 0`.
- Use reactions for acknowledgements: `eyes` while running, then `white_check_mark` or `x`.
- Do not reply just to acknowledge a mention; replies affect thread high-water marks.
- Coalesce mentions that arrive during a running turn into one follow-up turn.
- Keep stream readers attached until the agent process exits.
- Persist a new session id before posting the agent reply.
- Append transcript messages only after the reply posts successfully.

## Sandbox Rules

- Sandbox names come from `src/router/sandbox-name.ts`.
- Names must contain only `[A-Za-z0-9-]`; encode Slack `thread_ts` dots as `-`.
- Long names may hash, but `~/.agent-state/thread` must preserve the full thread id.
- Use `sbx create --clone` so agents work in an isolated VM, not the host tree.
- `sbx exec` commands are wrapped in `bash -c`.
- Do not use `--ephemeral`; resume depends on sandbox state.
- Agent stdin is redirected from `/dev/null` inside the VM to avoid headless hangs.
- Provisioning must create a writable repo clone, set `origin`, and start from a non-base branch.

## Logging

- Boundary logs go through `src/router/log.ts`.
- Summary logging is always on; verbose logging may include raw stdout/stderr and full prompts.
- Logging is best-effort and must not block or fail a turn.
- Do not log credentials or credential-shaped fields.

## Commits

- Keep commits small and logical.
- Keep `npm run check` green at every commit.
- Include the project `Co-Authored-By` trailer when making commits.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
See [AGENTS.md](./AGENTS.md) for the rules and architecture invariants to follow when working in this repo.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Mishu contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
148 changes: 147 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,147 @@
# mishu
# Mishu

![Mishu workflow: summon a coding agent in Slack, run it in an sbx sandbox, and continue in the same thread.](./assets/mishu.png)

Mishu is a Slack bot that lets a team run coding-agent work from a shared thread.
Mention the bot, and it starts a coding-agent CLI inside an isolated `sbx` sandbox, then replies in
the same thread. Follow-up mentions in that thread resume the same sandbox and agent session.

The router is deliberately small: it does not call an LLM, classify intent, or decide whether to
delegate. It moves Slack thread text to a coding agent, captures the result, and keeps each Slack
thread mapped to one sandbox.

## Features

- One Slack thread maps to one sandbox and one agent session.
- Follow-up mentions resume the same coding-agent context.
- Different threads can run in parallel.
- Sandboxes keep per-thread state under `~/.agent-state/`.
- The coding agent can push or open a PR when the sandbox has GitHub credentials.
- Boundary logs are written as JSONL for debugging and audits.

## Requirements

- Node.js 22+
- The Docker Sandboxes `sbx` CLI
- A Slack app with Socket Mode enabled
- A target git repo
- A Codex or Claude Code credential configured in `sbx`

For PR creation, the target repo should have a pushable `origin`, and the sandbox needs a repo-scoped
GitHub credential:

```bash
sbx secret set -g github
```

## Setup

### 1. Install dependencies

```bash
npm install
```

### 2. Configure the coding-agent credential

Codex is the default agent:

```bash
npm run setup
```

To set up Claude Code instead:

```bash
MISHU_AGENT=claude npm run setup
```

The setup command checks `sbx secret ls` and runs the needed interactive login flow.

### 3. Create the Slack app

Create an app at <https://api.slack.com/apps>, enable Socket Mode, and add these scopes:

| Token | Scopes |
| --- | --- |
| App-level token | `connections:write` |
| Bot token | `app_mentions:read`, `chat:write`, `reactions:write`, `channels:history`, `groups:history` |

Subscribe the app to the `app_mention` bot event, install it to your workspace, and invite it to the
channels where it should work.

Create `.env` from [.env.example](./.env.example):

```bash
APP_SLACK_APP_TOKEN=xapp-...
APP_SLACK_BOT_TOKEN=xoxb-...
```

### 4. Run Mishu

```bash
npm run build
MISHU_REPO=/path/to/your/repo \
node --env-file=.env dist/cli/index.js --sandbox=sbx ./data
```

When Mishu prints `listening`, mention the bot in Slack.

## Configuration

| Variable | Required | Description |
| --- | --- | --- |
| `APP_SLACK_APP_TOKEN` | Yes | Slack app-level Socket Mode token (`xapp-...`). |
| `APP_SLACK_BOT_TOKEN` | Yes | Slack bot user token (`xoxb-...`). |
| `MISHU_REPO` | Yes | Repo path or ref passed to `sbx create --clone`. |
| `MISHU_AGENT` | No | `codex` or `claude`; default is `codex`. |
| `MISHU_SANDBOX_TEMPLATE` | No | Optional `sbx create --template` image. The image must include `bash`, `git`, and the selected agent CLI. |
| `MISHU_LOG_LEVEL` | No | `summary` or `verbose`; default is `summary`. |
| `MISHU_BOT_USER` | No | Slack bot user id; resolved with `auth.test` when omitted. |

## Sandbox Governance & Security

Mishu does not manage sandbox egress or network policy in code. When using `sbx` as the sandbox
provider, configure Docker Sandboxes governance and local policy outside Mishu:
<https://docs.docker.com/ai/sandboxes/governance/local/>.

If `MISHU_SANDBOX_TEMPLATE` is set, Mishu passes it to `sbx create --template` for future sandbox
creation. Custom templates must include `bash`, `git`, and the selected agent CLI.

## Logs

Each turn is logged to `./data/router.log` as JSONL. Summary logs include argv, prompt size/hash, exit
code, duration, and final-message snippet. Verbose logs also include raw stdout/stderr chunks.

```bash
tail -f ./data/router.log | jq 'select(.threadId=="t-<channel>-<thread-ts>")'
```

Credentials are stored and injected by `sbx`; Mishu does not put Slack, OpenAI, Anthropic, or GitHub
secrets in prompts or argv.

## Development

Read [AGENTS.md](./AGENTS.md) before changing the router, sandbox, platform, or backend seams.

| Command | Description |
| --- | --- |
| `npm run check` | Biome check, TypeScript, and Vitest. This is the main gate. |
| `npm run check:fix` | Apply Biome fixes, then run the gate. |
| `npm test` | Run offline unit tests. |
| `npm run test:live` | Run live `sbx` tests. Requires the `sbx` daemon and is not part of CI. |
| `npm run build` | Compile `dist/`. |
| `npm run dev` | Run the CLI under `tsx watch`. |

## Architecture

```
Slack <-> PlatformAdapter <-> Router <-> SandboxProvider <-> sandbox
\-> CodingBackend
```

- `src/platform/`: Slack event mapping and Slack SDK adapter.
- `src/router/`: thread state machine, dedupe, sandbox naming, state store, logging, dispatcher, idle sweep.
- `src/sandbox/`: `sbx` argv builders and provider.
- `src/backend/`: Codex and Claude Code argument builders and output parsers.
- `src/cli/`: argument parsing, setup flow, and composition root.
Binary file added assets/mishu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 39 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"ignoreUnknown": true,
"includes": ["**", "!**/dist"]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"assist": {
"enabled": true,
"actions": {
"source": {
"organizeImports": "on"
}
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"semicolons": "always",
"trailingCommas": "all"
}
}
}
Loading
Loading