Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
301 changes: 188 additions & 113 deletions README.md

Large diffs are not rendered by default.

883 changes: 883 additions & 0 deletions docs/api-reference.md

Large diffs are not rendered by default.

422 changes: 422 additions & 0 deletions docs/architecture.md

Large diffs are not rendered by default.

564 changes: 564 additions & 0 deletions docs/packages.md

Large diffs are not rendered by default.

187 changes: 187 additions & 0 deletions docs/session-isolation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# Session Isolation

Every Mitzo session gets deterministic isolation via git worktrees. Each session operates on its own branch in its own directory, preventing cross-session contamination.

## How It Works

When a new session starts with isolation enabled:

1. **Worktree creation** -- `createSessionWorktrees()` creates a git worktree for the primary repo and every repo listed in `.mitzo.json`
2. **Branch creation** -- each worktree gets a dedicated branch: `session/<session-id>`
3. **Path setup** -- worktree paths are injected into the system prompt so the agent knows where to work
4. **Env vars** -- `MITZO_SESSION_ID` and `MITZO_REPO_<NAME>` are set for every repo

### Paths and Branches

| Item | Pattern |
| ----------------- | ------------------------------------------------------- |
| Worktree path | `<repo>/.claude/worktrees/<session-id>/` |
| Branch name | `session/<session-id>` |
| Primary env var | `MITZO_REPO_PRIMARY` |
| Secondary env var | `MITZO_REPO_<NAME>` (uppercase, hyphens to underscores) |

### Multi-Repo

When `.mitzo.json` declares secondary repos, all of them get worktrees at session start:

```json
{
"repos": {
"mitzo": "/Users/you/tools/mitzo",
"team-home": "/Users/you/redhat/team_home",
"centaur": "/Users/you/projects/centaur"
}
}
```

This creates four worktrees per session: one for the primary repo (`REPO_PATH`) and one for each secondary repo. The agent can work across all of them in a single session.

## Write Enforcement

The worktree guard (`checkWorktreePolicy()` in `@mitzo/harness`) inspects every tool call that could modify files:

| Tool | Checked | What's Inspected |
| ------- | ------- | ------------------------------------------- |
| `Write` | Yes | `file_path` parameter |
| `Edit` | Yes | `file_path` parameter |
| `Bash` | Yes | Command string (path extraction heuristics) |
| `Read` | No | Read operations are unrestricted |
| `Glob` | No | Read operations are unrestricted |
| `Grep` | No | Read operations are unrestricted |

If a write target falls outside the session's worktree directories, the tool call is **denied** with a redirect message telling the agent the correct worktree path. The agent self-corrects. No user prompt, no approval flow.

### Enforcement by Client

| Client | Enforcement | Mechanism |
| ----------- | -------------------- | -------------------------------------------- |
| Mitzo | Programmatic deny | `checkWorktreePolicy()` in `canUseTool` |
| Claude Code | cwd + system prompt | `SessionStart` hook sets working directory |
| Cursor | Advisory + git guard | `alwaysApply` rule + pre-commit rejects main |

### System Prompt Injection

`buildWorktreeSystemPrompt()` generates a lookup table of all repo paths for the agent:

```
## Session Worktrees
Session ID: 2026-07-01-abc123

- **primary (cwd)**: /path/to/repo/.claude/worktrees/2026-07-01-abc123
- **mitzo**: /path/to/mitzo/.claude/worktrees/2026-07-01-abc123
- **team-home**: /path/to/team_home/.claude/worktrees/2026-07-01-abc123
```

This ensures the agent knows the exact paths without guessing.

## External Hooks

Mitzo creates worktrees for Claude Code and Cursor sessions too, not just its own.

### How It Works

1. On startup, Mitzo generates an internal token and persists it to `~/.mitzo/internal-token`
2. A `SessionStart` hook in your repo's `.claude/hooks/` or `.cursor/hooks/` reads this token
3. The hook calls `POST /api/sessions` with the internal token
4. The server creates worktrees for all configured repos and returns the paths
5. Claude Code/Cursor sessions get the same isolation as Mitzo sessions

### Claude Code Hook

```bash
#!/bin/bash
# .claude/hooks/session-isolate.sh
TOKEN=$(cat ~/.mitzo/internal-token 2>/dev/null)
if [ -z "$TOKEN" ]; then exit 0; fi

RESPONSE=$(curl -s -X POST http://localhost:3100/api/sessions \
-H "Content-Type: application/json" \
-H "X-Internal-Token: $TOKEN" \
-d '{"source": "claude-code"}')

if [ $? -eq 0 ]; then
CWD=$(echo "$RESPONSE" | jq -r '.worktrees.primary // empty')
if [ -n "$CWD" ]; then
echo "{\"cwd\": \"$CWD\"}"
fi
fi
```

### Cursor Hook

Similar pattern, but outputs `agent_message` with all worktree paths for the system prompt.

## Cleanup

### Automatic Cleanup

Stale worktrees (older than 96 hours) are cleaned up automatically on server startup. The cleanup scans both `.claude/worktrees/` and `.cursor/worktrees/` directories in all configured repos.

### Dirty Worktree Handling

Worktrees with uncommitted changes are **not** deleted during cleanup. Instead:

1. The cleanup process detects uncommitted work
2. Creates a commit with the uncommitted changes
3. Creates a draft PR from the session branch
4. Flags the worktree in the mgmt inbox for human review
5. Then removes the worktree directory

This prevents losing work from sessions that were interrupted before the agent could commit.

### Manual Cleanup

```bash
# Via the mgmt CLI (if using mgmt workspace)
./mgmt session cleanup [session-id]

# Or manually
git worktree list # see all worktrees
git worktree remove <path> # remove a specific worktree
git branch -d session/<session-id> # delete the session branch
```

## Session Index

Mitzo maintains a YAML session index at `<repo>/.claude/sessions/index.yaml`. This tracks:

- Active and closed sessions
- Worktree paths per repo
- Session metadata (title, creation time, last activity)

The index is useful for finding prior session work without grepping through worktree directories.

## Configuration

### Enabling/Disabling

Set `WORKTREE_ENABLED=false` in `.env` to disable isolation entirely. Sessions work directly on the main repo. This is the kill switch.

### Skipping Worktree Creation

Sessions with explicit `cwd` or `resume` parameters skip worktree creation. This allows:

- Resuming an existing session in its original worktree
- Starting a session in a specific directory (e.g., a quick action with a `cwd` override)

### Data Files

Worktrees are lightweight git checkouts. They don't include:

- `.venv/` (Python virtual environments)
- Parquet data files
- `node_modules/` (uses the main repo's copy via npm workspace resolution)
- Any gitignored files

This is expected. The agent works with code, not data artifacts.

## Troubleshooting

| Problem | Fix |
| -------------------------- | ---------------------------------------------------------------------------------------- |
| Worktree creation fails | Run `git worktree list` in the failing repo to check for conflicts |
| Stale worktrees piling up | Run `git worktree prune` then restart Mitzo (auto-cleanup runs on start) |
| Agent writes to wrong path | Check that the system prompt includes worktree paths (should be automatic) |
| Branch already exists | A prior session with the same ID left a branch. Delete with `git branch -d session/<id>` |
| npm/node_modules missing | Expected in worktrees. Symlink or use the main repo's packages |
| Data files missing | Expected. Worktrees only contain tracked files |
184 changes: 184 additions & 0 deletions docs/skills.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# Skills System

Skills are reusable prompt packages invoked via `/slash-command` in the chat input. They provide a way to package common workflows, enforce tool restrictions, and share prompt templates across repos.

## Using Skills

Type `/` in the chat input to see all available skills. The `SlashPicker` component shows skill names, descriptions, source badges, and collision notes.

```
/simplify -- reduce complexity and duplication
/risk-scan -- failure modes, missing tests, unsafe assumptions
/pr-review -- review a pull request
/person -- people profile lookup and update
/review-response -- triage and fix PR review comments
/land-pr -- shepherd a PR from open to merged
/pr-shepherd -- persistent PR lifecycle monitoring
```

Skills can accept arguments:

```
/pr-review 42 -- review PR #42
/person akram -- look up Akram's profile
/pr-shepherd mitzo#350 -- monitor PR #350 in the mitzo repo
```

## Bundled Skills

Mitzo ships with these skills in the `skills/` directory:

| Skill | Description | Arguments |
| ------------------ | ---------------------------------------------------------------------------------- | ---------------- |
| `/simplify` | Code review focused on reducing complexity, duplication, and cleanup opportunities | None |
| `/risk-scan` | Security-oriented audit -- failure modes, missing tests, unsafe assumptions | None |
| `/pr-review` | Review a pull request -- diff analysis, code quality, architecture alignment | PR number or URL |
| `/person` | People profile lookup and update | Person name |
| `/review-response` | Triage and fix PR review comments | PR number or URL |
| `/land-pr` | Land a PR -- rebase, squash, merge | PR number or URL |
| `/pr-shepherd` | Persistent PR lifecycle monitoring -- conflicts, CI, reviews, merge-readiness | repo#number |

## Custom Skills

Create your own skills by adding markdown files with YAML frontmatter.

### Skill file format

```markdown
---
name: deploy
description: Deploy to staging or production
allowed-tools: [Bash, Read]
arguments:
- name: environment
description: Target environment (staging or production)
required: true
- name: branch
description: Branch to deploy
required: false
---

Deploy the application to the {{environment}} environment.

{{#if branch}}
Deploy from the {{branch}} branch.
{{/if}}

Run the deployment script and report the result.
Include the deployment URL in your response.
```

### Frontmatter fields

| Field | Type | Required | Description |
| --------------- | ---------- | -------- | ---------------------------------------- |
| `name` | `string` | Yes | Skill name (used as `/name` command) |
| `description` | `string` | Yes | One-line description shown in the picker |
| `allowed-tools` | `string[]` | No | Tool restriction ceiling (see below) |
| `arguments` | `array` | No | Named arguments with descriptions |

### Arguments

Each argument has:

| Field | Type | Required | Description |
| ------------- | --------- | -------- | --------------------------------------------------- |
| `name` | `string` | Yes | Argument name |
| `description` | `string` | Yes | Description shown in help |
| `required` | `boolean` | No | Whether the argument is required (default: `false`) |

Arguments are injected into the skill body via `{{argument_name}}` template syntax. Positional arguments map to the declared order.

## Discovery Scopes

Skills are discovered from three locations, in precedence order:

```
1. Repo-local: <REPO_PATH>/.mitzo/skills/*.md
2. User: ~/.mitzo/skills/*.md
3. Bundled: <mitzo>/skills/*.md
```

### Precedence Rules

When skills from different scopes share the same name, the highest-precedence scope wins:

1. **Native commands** (TypeScript) -- highest precedence. Currently: `/skills`.
2. **Repo-local** -- skills in your project's `.mitzo/skills/` directory.
3. **User** -- skills in `~/.mitzo/skills/` (available in all repos).
4. **Bundled** -- skills shipped with Mitzo.

The `/` picker shows collision notes when a skill shadows another. For example, if you have a repo-local `/simplify` that shadows the bundled one, the picker will note "overrides bundled".

## Tool Restrictions

The `allowed-tools` frontmatter field enforces a ceiling on what tools Claude can use during the skill's execution:

```yaml
allowed-tools: [Read, Glob, Grep]
```

This means Claude can **only** use Read, Glob, and Grep during this skill -- even if the user is in Auto mode. The restriction never expands permissions beyond the current mode. It is enforced by `skill-policy.ts` via the `canUseTool` callback.

If `allowed-tools` is not specified, the skill uses the mode's default permissions.

## API

### GET /api/skills

Returns the merged skill registry with collision metadata.

**Query parameters:**

| Parameter | Type | Description |
| --------- | -------- | ------------------------------------------- |
| `cwd` | `string` | Working directory for repo-scoped discovery |

**Response:** Array of skill objects with `name`, `description`, `source` (scope), and collision information.

## Adding Skills to Your Repo

1. Create `.mitzo/skills/` in your repo root
2. Add markdown files with the frontmatter format above
3. Skills appear immediately in the `/` picker -- no restart needed

Example: a deployment skill for your project:

```markdown
---
name: deploy
description: Deploy to production
allowed-tools: [Bash, Read]
---

Deploy the application:

1. Run `npm run build`
2. Run `npm run deploy`
3. Verify the deployment succeeded
4. Report the deployment URL
```

Example: a code review skill with restricted tools:

```markdown
---
name: audit
description: Security audit of recent changes
allowed-tools: [Read, Glob, Grep]
---

Audit the most recent commit for security issues:

1. Read the diff with `git diff HEAD~1`
2. Check for hardcoded secrets, SQL injection, XSS
3. Report findings with severity ratings
```

## Native Commands

Native commands are TypeScript-implemented commands that bypass the prompt system entirely. Currently:

- `/skills` -- lists all available skills with their sources and collision info

Native commands always take precedence over prompt-based skills.
Loading
Loading