https://github.com/singlr-ai/sing
Isolated development environments for AI agents
https://github.com/singlr-ai/sing
ai-agents coding-agent development-environment
Last synced: about 1 month ago
JSON representation
Isolated development environments for AI agents
- Host: GitHub
- URL: https://github.com/singlr-ai/sing
- Owner: singlr-ai
- License: mit
- Created: 2026-02-07T22:49:44.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-04-05T18:57:03.000Z (about 1 month ago)
- Last Synced: 2026-04-05T20:31:22.841Z (about 1 month ago)
- Topics: ai-agents, coding-agent, development-environment
- Language: Java
- Homepage: https://sing.singlr.ai
- Size: 265 KB
- Stars: 6
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# sing
A single native binary that provisions bare-metal servers and manages isolated dev environments for AI-assisted engineering. One binary, zero dependencies, fully declarative.
Built with Java 25 + picocli + GraalVM native-image. <1ms startup.
## Why
You have a bare-metal server. You work on multiple projects simultaneously, each needing its own JDK, Postgres, Meilisearch, Redpanda, and AI coding agents — fully isolated so a runaway agent in one project can't affect another.
`sing` provisions the server, creates project environments as Incus system containers, manages their lifecycle, and orchestrates AI agents inside them with spec-driven workflows, guardrails, and rollback safety. Each project gets a complete Ubuntu 24.04 userspace with its own filesystem, network stack, and rootless Podman runtime.
## Day 0: Server + Project Setup
```bash
# Install sing (single binary, no dependencies)
curl -fsSL https://raw.githubusercontent.com/singlr-ai/sing/main/install.sh | bash
# Initialize server (one-time, needs sudo)
sudo sing host init
```
`host init` installs Incus, creates a storage pool (dir by default, ZFS with `--storage zfs --disk /dev/sdX`), configures networking, and caches the base Ubuntu 24.04 image.
```bash
# Pull project descriptor from shared repo (team projects)
export GITHUB_TOKEN=ghp_...
sing project pull acme-health
# Or generate a new sing.yaml interactively
sing project init
# Create the environment
sing project create acme-health
```
`project create` provisions an Incus container with everything declared in `sing.yaml` — installs runtimes, starts Podman services, clones repos, configures git identity, generates agent context files, and sets up the harness.
```bash
# Print SSH config for your editor
sing connect acme-health
```
Add the output to `~/.ssh/config`, then connect in Zed: `Cmd+Shift+P` → "Connect to SSH Host" → `acme-health`.
## Day 2: The Two Modes
Engineers work in two modes. `sing` supports both from the same project.
### Interactive Mode (Daytime)
Open Zed, connect via SSH remote dev, start the agent from Zed's Agent Panel. You're in the loop — brainstorming, exploring code, writing specs, reviewing output. `sing` is invisible here; the generated context files (CLAUDE.md, SECURITY.md, `.context/`) guide the agent.
```bash
sing switch acme-health # start container, show connection info
```
Developer processes (`java -jar`, `npm run dev`) run interactively in Zed terminal tabs. Infrastructure services (Postgres, Meilisearch, etc.) are managed by Podman with `--restart=always` and survive container reboots automatically.
### Autonomous Mode (Overnight)
Write specs during the day. Walk away. The agent works through them overnight.
```bash
sing dispatch acme-health # pick next ready spec, launch agent
```
`dispatch` reads `specs/index.yaml` from the container, finds the next pending spec (respecting dependencies and assignee), reads the detailed `spec.md`, and launches the agent with full context. Guardrails enforce time limits. Auto-snapshot provides rollback safety.
## Spec-Driven Workflow
Specs are the unit of work. Each spec lives in its own directory inside `specs/`, checked into a shared repo so the team can see and assign work.
### Structure
```
specs/
├── index.yaml # Ordered list: id, title, status, assignee, depends_on
├── oauth-flow/
│ ├── spec.md # What to build and why (brainstormed with agent in Zed)
│ └── plan.md # How to build it (optional, for complex specs)
├── payment-integration/
│ ├── spec.md
│ └── plan.md
└── fix-footer-typo/
└── spec.md # Simple spec, no plan needed
```
### index.yaml
```yaml
specs:
- id: oauth-flow
title: "Implement OAuth flow with Google"
status: pending
assignee: alice
depends_on: []
- id: payment-integration
title: "Stripe payment webhook"
status: pending
assignee: bob
depends_on:
- oauth-flow
- id: fix-footer-typo
title: "Fix typo in footer"
status: done
```
### Lifecycle
```
pending → in_progress → review → done → archive
```
**pending**: Ready for an agent to pick up. **in_progress**: Agent is working. **review**: Work complete, security review and code review hooks run automatically. **done**: All reviews pass. **archive**: Cleaned up (`sing task archive`).
### The Full Loop
```
Morning (Zed, interactive):
Engineer + agent brainstorm → write specs/oauth-flow/spec.md
Optionally plan → write specs/oauth-flow/plan.md
Push specs to shared repo
Evening:
sing dispatch acme-health
Overnight:
Agent reads spec.md, works, commits, pushes branch
Reviews run automatically at spec completion
Guardrails enforce time limits
Next morning:
sing agent status # all projects at a glance
sing agent report acme-health # detailed: commits, spec progress, review results
Review PRs, merge or address findings
```
### Team Coordination
Specs live in a shared private repo (e.g., `your-org/projects/acme-health/specs/`). Multiple engineers, each with their own container:
- Alice specs out OAuth, assigns to herself
- Bob specs out payments, depends on Alice's OAuth work
- `sing dispatch` respects `assignee` — each engineer's agent only picks up their specs (or unassigned ones)
- Dependencies prevent premature work — payments won't start until OAuth is done
No project board needed. The spec directory *is* the board. Git history is the audit trail.
## Context Generation
`sing run` (or `sing agent context regen`) generates a complete agent environment from `sing.yaml`. Context files are agent-agnostic — Claude Code gets `CLAUDE.md`, Codex gets `AGENTS.md`, Gemini gets `GEMINI.md`. Same content, different format.
| Generated | Purpose |
|-----------|---------|
| `CLAUDE.md` / `AGENTS.md` / `GEMINI.md` | Tech stack, conventions, runtimes, services, autonomous work protocol |
| `SECURITY.md` | Zero-trust principles, OWASP Top 10, input validation, secrets management |
| `.context/` repository | Persistent knowledge across sessions (system overview, patterns, failure log) |
| Methodology skills | `/spec` (write spec first), `/verify` (run tests and block on failures) |
| Post-task hooks | Auto-trigger security audit and code review at spec completion |
The autonomous work protocol in the context file tells the agent how to read specs, update status, and hand off — this works identically across Claude Code, Codex, and Gemini.
### `.context/` — Institutional Memory
```
.context/
system/ # always loaded — project overview, key decisions
patterns/ # discovered architectural patterns and conventions
failures/ # what went wrong and how it was fixed
```
Agents read `system/README.md` at session start and update these files as they learn. Committed alongside code so knowledge persists across sessions and engineers.
## Methodology
```yaml
agent:
methodology:
approach: spec-driven # spec-driven | tdd | free-form
verify: "mvn clean test"
lint: "mvn spotless:check"
```
- **`spec-driven`**: Agent writes a spec before coding. Generated as a `/spec` skill.
- **`tdd`**: Agent writes failing tests first, then implements until green.
- **`free-form`**: No methodology constraints.
- **`verify`** / **`lint`**: Commands injected as skills the agent runs after implementation.
## Guardrails
```yaml
agent:
guardrails:
max_duration: 4h
action: snapshot-and-stop
```
When the time limit triggers, the agent is stopped and rolled back to the pre-launch snapshot. The watcher starts automatically with `sing dispatch` and `sing run --background`.
Actions: `snapshot-and-stop` (rollback), `stop` (keep changes), `notify` (webhook, agent continues).
## Agent Commands
```bash
# Dispatch
sing dispatch acme-health # next ready spec, background launch
sing dispatch acme-health --spec auth # specific spec by ID
# Run (context regen + launch)
sing run acme-health # interactive mode (Zed)
sing run acme-health --task "..." # headless with explicit task
sing run acme-health --background # background, picks next spec
# Monitor
sing agent status # all projects at a glance
sing agent status acme-health # single project detail
sing agent log acme-health -f # stream output live
sing agent report acme-health # morning-after summary
# Control
sing agent stop acme-health # SIGTERM → SIGKILL after 3s
sing agent watch acme-health # enforce guardrails (auto-started by dispatch)
# Quality
sing agent audit acme-health # cross-agent security audit
sing agent review acme-health # cross-agent code review
sing agent sweep acme-health # codebase cleanup pass
```
## Multi-Agent Support
`sing` is agent-agnostic. Configure the primary agent and optionally install others for cross-agent review:
```yaml
agent:
type: claude-code
install:
- claude-code
- gemini
security_audit:
enabled: true
# auditor: gemini # defaults to a different agent than primary
code_review:
enabled: true
```
Claude codes. Gemini reviews. Or the same agent does both with fresh context. The hooks fire at spec completion, not session stop — so reviews always see complete, coherent work.
## Example `sing.yaml`
```yaml
name: acme-health
description: "Acme Health Platform"
resources:
cpu: 4
memory: 12GB
disk: 150GB
image: ubuntu/24.04
runtimes:
jdk: 25
node: 22
maven: "3.9.9"
git:
name: "Acme Engineering"
email: "eng@acme.com"
auth: token
repos:
- url: "https://github.com/acme/backend.git"
path: "backend"
branch: "main"
- url: "https://github.com/acme/webapp.git"
path: "webapp"
services:
postgres:
image: postgres:16
ports: [5432]
environment:
POSTGRES_DB: acme
POSTGRES_USER: dev
POSTGRES_PASSWORD: dev
volumes:
- pgdata:/var/lib/postgresql/data
meilisearch:
image: getmeili/meilisearch:latest
ports: [7700]
environment:
MEILI_ENV: development
agent:
type: claude-code
auto_branch: true
auto_snapshot: true
specs_dir: specs
methodology:
approach: spec-driven
verify: "mvn clean test"
lint: "mvn spotless:check"
guardrails:
max_duration: 4h
action: snapshot-and-stop
ssh:
user: dev
authorized_keys:
- "ssh-ed25519 AAAA... you@laptop"
```
## Project Lifecycle
```bash
sing project create acme-health # provision container from sing.yaml
sing up acme-health # start stopped container
sing down acme-health # stop container (preserves state)
sing switch acme-health # start + show connection info
sing ps # list all projects with status
sing snap acme-health # create snapshot
sing restore acme-health snap-01 # rollback to snapshot
# Modify running projects
sing project add service acme-health
sing project add repo acme-health
sing project destroy acme-health # delete container and state
```
## Building from Source
Requires JDK 25+ and Maven 3.9+.
```bash
mvn clean test # run tests (588 tests)
mvn clean package # build JAR
# Native image (requires GraalVM JDK 25)
JAVA_HOME=/usr/lib/jvm/graalvm-jdk-25 mvn clean package -Pnative -DskipTests
```
Every command supports `--help`. State-modifying commands support `--dry-run`. All commands support `--json` for machine-parseable output.
## License
[MIT](LICENSE)