No description
  • TypeScript 98.7%
  • CSS 0.7%
  • Shell 0.4%
  • JavaScript 0.2%
Find a file
chad 62c793bbdb
All checks were successful
deploy / checks (push) Successful in 45s
deploy / deploy (push) Successful in 1m49s
Merge pull request '0055 PR 1 — sticky ticket mode with phases' (#137) from glen/0055-chat-modes into main
Reviewed-on: chad/sweatshop#137
2026-10-21 11:18:09 -04:00
.agents/skills refactor!: 0010 phase 6 — delete eve 2026-10-01 19:34:43 -04:00
.forgejo/workflows fix(deploy): extract ci-deploy.sh from the target commit 2026-10-16 14:21:25 -04:00
app feat(skills): include mode on the composer picker's /api/skills data 2026-10-21 15:07:31 +00:00
components fix(ui): genericize the remaining hardcoded "ticket mode" strings 2026-10-21 15:03:05 +00:00
db feat(modes): expose the recorded phase on read models (0055 v7 part B) 2026-10-21 14:06:14 +00:00
design chore: repo scaffold — vendored skills, design references 2026-09-29 16:24:05 -04:00
docs docs(0055): clarify where the handoff is visible per worker kind 2026-10-21 15:03:52 +00:00
drizzle feat(db): add harness_calls.phase and conversations.work_phase (0060) 2026-10-21 14:05:58 +00:00
lib fix(modes): parser tolerance — whitespace before the colon, fenced code 2026-10-21 15:06:46 +00:00
scripts fix(deploy): extract ci-deploy.sh from the target commit 2026-10-16 14:21:25 -04:00
skills docs(ticket): rewrite the ticket skill in v7 phase format 2026-10-21 13:53:39 +00:00
tests feat(skills): include mode on the composer picker's /api/skills data 2026-10-21 15:07:31 +00:00
workflows feat(modes): wrap harness/start_thread prompts with the phase handoff 2026-10-21 14:06:10 +00:00
.env.example fix(review): address fresh-eyes findings 5-11 2026-10-20 21:50:27 +00:00
.gitignore docs: AGENTS.md path B + vendor workflow reference docs 2026-09-30 13:11:01 -04:00
AGENTS.md docs(0055): clarify where the handoff is visible per worker kind 2026-10-21 15:03:52 +00:00
components.json feat(web): next.js + tailwind v4 + shadcn + three-theme token system 2026-09-29 16:37:36 -04:00
Containerfile.dev feat(agent): gh — read-only GitHub CLI on Chad's stored credential 2026-10-07 17:59:10 -04:00
Containerfile.sandbox feat(cursor): cursor harness via browser-login auth.json (0038) 2026-10-10 22:52:49 -04:00
DESIGN.md docs(themes): 0044 — theme registry design + DESIGN.md theme table update 2026-10-16 16:14:54 +00:00
dev fix(dev): docker-portable checks, bash-3.2 arrays, DEV_PUBLISH for local mode 2026-10-20 21:46:16 +00:00
drizzle.config.ts feat(db): sqlite scaffold — drizzle schema, migrations, data/ 2026-09-29 17:16:04 -04:00
eslint.config.mjs chore(lint): enforce the component/db boundary — no @/db imports in components/ (0049) 2026-10-18 12:45:06 +00:00
instrumentation-node.ts fix(codex): review fixes — env guard, pinned-model validation, margin, unions (0050) 2026-10-19 01:55:27 +00:00
instrumentation.ts fix(previews): node-only instrumentation module; resolve runtime container id from /etc/hostname 2026-10-03 21:42:45 -04:00
next.config.ts refactor!: 0010 phase 6 — delete eve 2026-10-01 19:34:43 -04:00
package-lock.json feat(deps): pin @ai-sdk/harness-codex 1.0.143 (pairs with harness 1.0.141/provider-utils 5.0.56 — 0050) 2026-10-18 20:05:25 +00:00
package.json fix(codex): review fixes — env guard, pinned-model validation, margin, unions (0050) 2026-10-19 01:55:27 +00:00
postcss.config.mjs feat(web): next.js + tailwind v4 + shadcn + three-theme token system 2026-09-29 16:37:36 -04:00
README.md fix(dev): docker-portable checks, bash-3.2 arrays, DEV_PUBLISH for local mode 2026-10-20 21:46:16 +00:00
skills-lock.json refactor!: 0010 phase 6 — delete eve 2026-10-01 19:34:43 -04:00
tsconfig.json feat(web): next.js + tailwind v4 + shadcn + three-theme token system 2026-09-29 16:37:36 -04:00
vitest.config.ts feat(chat): 0010 turn workflow, scripted mock LLM, workflow executor for vitest 2026-10-01 18:28:49 -04:00

sweatshop

A personal software factory: a single Next.js app whose agent runtime is the Vercel AI SDK (WorkflowAgent) on the Workflow SDK. You talk to an agent; the agent works in sandboxed containers per conversation — cloning your repos, running harnesses (pi, claude, cursor, codex), serving live previews, and opening PRs you approve.

Single-user by design. AGENTS.md is the full architecture contract; docs/ holds the numbered design history. This file is how to run it when it isn't already yours.

Requirements

  • Linux or macOS host with a Docker-compatible container engine: podman, OrbStack, Docker Desktop, or colima. The app shells out to the engine's docker/podman CLI; sandboxes run as sibling containers.
  • Node 24 (for ./dev's helper commands and any host-side tooling).

Where it runs

One hard rule: the app container and the sandboxes must share one container engine. Sandboxes are reached over a private container network (sweatshop-harness); the harness bridges (claude / cursor / codex) and the preview proxy dial sandbox IPs there — membership in that network is the boundary, so split-engine setups cannot work.

Two supported topologies:

  1. All-local (a laptop): everything in your local engine via ./dev. Sandboxes and the app sit on the same sweatshop-harness network; previews land on pv-<slug>.previews.localhost (browsers resolve *.localhost to 127.0.0.1) with no wildcard certificate needed.
  2. All-on-one-box (a home-server / VM): the app runs as a systemd unit (or compose) on the machine, browser reaches it over LAN or Tailscale. Previews need PREVIEW_BASE_DOMAIN — a real domain with wildcard DNS pointing at the box (plus a cert) — or an SSH tunnel of the proxy port to your laptop.

Not supported: app on the laptop, sandboxes on a remote daemon. The app dials sandbox container IPs, which are only routable inside the same engine's network — DOCKER_HOST does not change that.

Quickstart

cp .env.example .env.local                      # set at least one key FIRST
./dev install                                   # deps, inside the engine
./dev exec -- npm run db:migrate
DEV_PUBLISH=3000:3000 ./dev exec -- npm run dev # or: build && start

./dev runs everything inside the dev container with the repo bind-mounted; .env.local is passed through. The wrapper publishes nothing by default — in the all-local mode set DEV_PUBLISH (a comma list of host:container pairs) so your browser can reach the app, e.g. DEV_PUBLISH=3000:3000,80:3001 also fronts the preview proxy on port 80 for the pv-<slug>.previews.localhost URLs. Point your browser at http://localhost:3000. Set SWEATSHOP_DB to move the sqlite database (default ./data/sweatshop.db).

Before your first sandbox: build the sandbox image —

./dev exec -- docker build -t localhost/sweatshop-sandbox -f Containerfile.sandbox .

(or any engine-pullable image per project via the project's image_ref, or SWEATSHOP_SANDBOX_IMAGE for the default).

Provider keys & harness logins

At least one provider key in .env.local — the default routes are derived from what's available (glm > openrouter > gloo):

Env What it unlocks
ZAI_API_KEY GLM provider (Z.AI coding plan) + web_search
GLOO_API_KEY Gloo provider
OPENROUTER_API_KEY OpenRouter provider

SWEATSHOP_DEFAULT_PROVIDER + SWEATSHOP_DEFAULT_MODEL override the derived default; per-surface pickers on /settings override those.

Harness logins (all optional, per harness):

  • pi — provider-routed (one of the keys above); no separate login.
  • claude — CLAUDE_CODE_OAUTH_TOKEN (from claude setup-token) in .env.local. Single-user personal use.
  • cursor — browser login; save auth.json under data/harness-auth/cursor/.
  • codex — Log in with ChatGPT on /settings (in-app OAuth).

Git-host credentials for the gh / forgejo agent tools and sandbox clones live as rows in the git_credentials sqlite table — no UI yet; see .env.example for the insert statement shape.

Previews

Preview URLs are pv-<slug>.<PREVIEW_BASE_DOMAIN> (scheme per PREVIEW_URL_SCHEME). Unset, the base falls back to previews.localhost — fine on a laptop (publish the proxy with DEV_PUBLISH=...,80:3001 so http://pv-<slug>.previews.localhost resolves to it), unreachable from other machines. On a home-server setup set PREVIEW_BASE_DOMAIN to your wildcard domain. The proxy listens on PREVIEW_PROXY_PORT (3001) and must be published where browsers can reach it.

Security posture

Single-user, LAN-only, no auth layer. Provider keys are env; git-host and harness credentials are stored plaintext in sqlite by design (reversible migration later). Secrets reach sandboxes read-scoped. Do not expose this to the internet — add a real verifier first.