SLOPSHOPPER

regin-bridge

Submits the prompts regin's agent bridge delivers to this session, in place of tmux keystrokes, and aborts the running turn on regin's send-now

newprocess
A shopper browsing a rack in a slop shop
README

regin

A workbench for building software with AI coding agents. Plan the feature, run the agents, see what they did, count what they got wrong.

🌐 regin.ccday.top — the official project site.

⚠️ Early beta. regin is under active development and breaking changes are expected at any time. Database schemas, settings keys, hook contracts, skill bundles, and CLI flags may all change without backward-compatible shims. Pin a commit if you need stability.

regin runs one loop around the coding agent you already use:

Spec  →  Run  →  Watch  →  Count
 │        │        │         └─ defects declared against the done-claim; rounds-to-done per feature
 │        │        └─ every session traced: tools, edits, tokens, time, grades
 │        └─ task runs launched on Claude or Kimi, steered and stopped from /live
 └─ a feature row carries the spec, its acceptance tests, and the slices cut from it

It is organized in four layers. The first two are where you spend the day; the last two make the runs inspectable and keep the agent on-spec.

LayerWhat it coversDashboard
Workbenchfeature → spec → acceptance tests → tasks → done-claim; the defect ledger/overview (Pipeline), /inbox
Runslaunching task runs, steering live sessions, the agent's messages back to you/live, /inbox
Observabilitysession traces, time and cost analytics, post-hoc grades, audit/trace, /audit
Guidancepatterns & skills, rule engines in hooks, cross-session memory, topic wikis/patterns, /rules, /memory, /repos/:name/topics

Workbench — features, tasks, defects

A feature is a row. Its spec lives on that row (or on its tracker issue when the feature is tracked remotely), and its acceptance items are named tests in the repo — red before the build, green at ship. The spec is cut into slices; each slice becomes a task with its dependencies, and tasks run in dependency order. When the work is called done, that is a done-claim, and anything found wrong afterwards is a defect declared against the feature.

The number the pipeline is held to is rounds-to-done: one build plus each time a done-claim was walked back. Only declarations feed it — nothing is inferred from prose, branch names or timing.

  • Dashboard: /overview is the Pipeline — Overview, Features and Tasks behind one tab strip; defects are reached from the Overview and the Features triage rail. /features/:id is a feature's ledger.
  • CLI: regin feature (where a feature stands and what it cost), regin task (capture, inspect, check a slice table); runs are armed and launched from the dashboard, regin defects (what a feature got wrong, and what escaped).
  • Internals: Feature pipeline, and docs/defect-task-feature.md for how the three join.

Runs — launch, steer, hear back

A task is armed with a provider, a model and a launch mode, then started from the dashboard. Claude runs go through the Python Claude Agent SDK, so regin owns the process and keeps a typed channel to it — an AskUserQuestion is answered from the browser rather than by typing into a terminal. Kimi Code runs over ACP or in a tmux pane on regin's own tmux server.

  • /live follows a running session: its current state, recent steps, any question it is blocked on, with steer and Stop.
  • Agent bridge — a guarded channel that relays a prompt or steering message into a live session's terminal. Off by default; see docs/setup.md.
  • Inbox — agents report progress, results and lessons through the send_to_user tool; messages land in /inbox and can fan out to a webhook, Telegram or Lark. CLI: regin messages, regin bridge.
  • **Mobile card — experimental.** /live renders a phone-sized card that can answer permission prompts while you are away from the desk. Rough edges and breaking changes are expected; see docs/agent-bridge-design.md.

Internals: Agent SDK tier, Provider launch capabilities, Agent Messages.

Observability — what the agents actually did

Hooks and transcripts produce a stream of events: tool calls, model thoughts, file edits, hook decisions, token usage. regin ingests that stream into a session trace you can filter by tool, agent and phase, replay, and roll up (tokens by tool, skill reads, MCP calls).

  • /trace/sessions lists sessions; each opens into a timeline and a conversation view.
  • regin stats time shows where build time goes (attended vs. agent vs. human); regin stats session <trace-id> ranks why one session was slow; regin stats sessions exports per-session rows.
  • regin grade scores finished sessions against a rubric. /audit is the ops history of regin itself.

Internals: Turn & Token Tracing, Session Grader.

Guidance — what steers the agent

The supporting layer: what the agent is told before it acts, and what pushes back when it drifts.

  • Patterns & skills — local procedure guides for recurring implementation shapes, deployed into the active provider's skills directory; regin pattern promote packages a proven pattern as a versioned skill. Dashboard: /patterns, /skills.
  • Rule engines — GritQL, Radon and bundle checkers wired into PostToolUse hooks, so a violation is reported to the agent in the same turn as the edit. Dashboard: /rules; CLI: regin rules, regin hooks.
  • Agent memory — lessons captured from past sessions, ranked, consolidated and superseded as they go stale, then recalled into later sessions when they match the task. Dashboard: /memory; CLI: regin memory.
  • Topic wikis — per-repo knowledge as a reviewed graph of topics; an agent proposes drafts, you approve them, and drift detection flags pages whose files have moved on. Dashboard: /repos/:name/topics; CLI: regin topics, regin wiki.

Internals: Patterns, Rule engines, Agent Memory, docs/topics/proposals.md.

Quick start

git clone https://github.com/caiuswang/regin && cd regin
./scripts/setup.sh
.venv/bin/python cli/regin.py serve      # dashboard on http://localhost:8321

Manual setup, settings, server mode and the CLI reference: docs/setup.md.

Supported agents

AgentStatus
Claude CodeFull: hooks, rule engines, trace, memory, and task runs through the Agent SDK.
Kimi CodeTask runs only (ACP or tmux), with known limits — runs are unattended and a steer is cancel-then-re-prompt. See Run a task with Kimi.
Codex, genericProvider adapters exist; cannot be armed for task runs. See Agent provider architecture.

What regin is not

Not a chat UI, not a model, not a hosted service. It assumes you already have a coding agent and a codebase, and runs locally beside them.

Acknowledgements

  • Qoder Repo Wiki — inspiration for the per-repo topic wiki design.
  • SkillRouter — inspiration for embedding-based skill/pattern routing.
  • GritQL — the query language powering regin's grit rule engine.

Further reading

Source 2 files
hooks/register.ts 88 lines
1import type { EngineInterface, Register } from 'claude-code'
2
3const REGIN = '/Users/taowang/regin'
4
5let listening = false
6
7const TURN = { plugin: 'regin-bridge', key: 'turn' } as const
8
9export type BridgePrompt = { text: string; id: string; session_id: string }
10export type BridgeControl = { op: 'abort'; id: string; session_id: string }
11
12// A spawned child's pieces end wherever its writes did, so a line can span
13// two pieces: the unterminated tail is carried into the next call.
14export function splitLines(carry: string, text: string): [string[], string] {
15  const parts = (carry + text).split('\n')
16  const tail = parts.pop() ?? ''
17  return [parts.filter(line => line.trim() !== ''), tail]
18}
19
20async function listen($: EngineInterface) {
21  const session = await $.session.id()
22  const child = $.process.spawn({
23    argv: [`${REGIN}/.venv/bin/python`, `${REGIN}/cli/regin.py`, 'bridge', 'listen', '--session', session],
24    cwd: REGIN,
25  })
26  let carry = ''
27  for await (const { stream, text } of child) {
28    if (stream !== 'stdout') continue
29    const [lines, tail] = splitLines(carry, text)
30    carry = tail
31    for (const line of lines) {
32      const frame = JSON.parse(line) as BridgePrompt | BridgeControl
33      if ('op' in frame) await abortRunning($)
34      else await $.prompt.submit({ text: frame.text })
35    }
36  }
37}
38
39// regin's "send now": end the running turn and Claude Code runs the prompts
40// queued behind it. Submitting one instead would only queue it behind them.
41async function abortRunning($: EngineInterface) {
42  const { value: turnId } = await $.state.get(TURN)
43  if (!turnId) return
44  try {
45    await $.turn.abort({ turnId })
46  } catch (err) {
47    // The turn ended between the frame and the call; the queue runs anyway.
48    $.ui.log(`regin-bridge: abort skipped: ${String(err)}`, { to: 'debug' })
49  }
50}
51
52// Without the child the bridge falls back to tmux on its own, so a listener
53// that fails is a debug line, never an error in the person's session.
54function ended($: EngineInterface, err: unknown) {
55  try {
56    $.ui.log(`regin-bridge: listener ended: ${String(err)}`, { to: 'debug' })
57  } catch {
58    // the module is unloading; there is nowhere left to log to
59  }
60}
61
62// One listener per loaded module: the bridge registers the session for as long
63// as this child's stream is open, and a second child would submit every prompt
64// twice. A reload ends the old loop (and its child) along with the module.
65export const register: Register = on => {
66  on('session.start', async ($, e, next) => {
67    const started = await next(e)
68    if (!listening) {
69      listening = true
70      void listen($)
71        .catch(err => ended($, err))
72        .finally(() => { listening = false })
73    }
74    return started
75  })
76  on('turn.start', async ($, e, next) => {
77    await $.state.set(TURN, e.turnId)
78    return next(e)
79  })
80  on('turn.complete', async ($, e, next) => {
81    if (!e.agentId) {
82      const { value } = await $.state.get(TURN)
83      if (value === e.turnId) await $.state.set(TURN, null)
84    }
85    return next(e)
86  })
87}
88
types/index.d.ts 8 lines
1export type RunningTurn = string | null
2
3declare module 'claude-code' {
4  interface PluginState {
5    'regin-bridge': { turn: RunningTurn }
6  }
7}
8