# Jonggrang Configuration Reference

## .jonggrang/jonggrang.json

Root configuration file generated by `jonggrang init`.

---

### Top-level Fields

| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Project name |
| `version` | string | Jonggrang config version |
| `tool` | string | Default AI tool: `opencode` \| `claude` \| `jonggrang` \| `codex` |
| `model` | string | Default model for the active tool (see backend mapping below) |
| `effort` | string | Default reasoning effort level (see backend mapping below) |

#### `tools` — Per-tool model/effort overrides

Optionally override `model` and `effort` for a specific backend. These take precedence over the top-level fields.

```json
{
  "tools": {
    "claude": {
      "model": "sonnet",
      "effort": "high"
    },
    "opencode": {
      "model": "anthropic/claude-opus-4-7",
      "effort": "max"
    },
    "jonggrang": {
      "model": "anthropic/claude-sonnet-4-5",
      "effort": "medium"
    }
  }
}
```

#### Model/effort resolution order

1. `--model` / `--effort` CLI flags
2. `JONGGRANG_MODEL` / `JONGGRANG_EFFORT` environment variables
3. `tools.<tool>.model` / `tools.<tool>.effort` in jonggrang.json
4. `model` / `effort` top-level fields in jonggrang.json
5. Backend default (flag omitted entirely)

#### Backend mapping table

| jonggrang flag | Claude Code (`--tool claude`) | OpenCode (`--tool opencode`) | Jonggrang SDK (`--tool jonggrang`) | Codex CLI (`--tool codex`) |
|---|---|---|---|---|
| `--model` | `--model <alias\|id>` | `--model <provider/model>` | `--model <provider/model>` | `--model <name>` |
| `--effort` | `--effort <level>` | `--variant <level>` | SDK `thinkingLevel` | `--config reasoning_effort=<level>` |

**Claude Code aliases**: `default`, `best`, `sonnet`, `opus`, `haiku`, `opusplan`, `sonnet[1m]`, `opus[1m]`; or full IDs like `claude-opus-4-7`, `claude-sonnet-4-6`, `claude-haiku-4-5`.

**OpenCode**: model must be in `provider/model` format (e.g. `anthropic/claude-sonnet-4-5-20250929`, `openai/gpt-5`). Bare model names are rejected with a friendly error.

**Jonggrang SDK**: accepts `provider/model` (e.g. `anthropic/claude-sonnet-4-5`) or bare model ID. Falls back to model saved via `jonggrang model` command when `--model` is not set. `--effort` maps directly to the SDK `thinkingLevel` (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`).

**Codex CLI**: bare model name (e.g. `codex-1`, `gpt-5.4`). Requires `OPENAI_API_KEY` env var. `--effort` maps to `reasoning_effort` via `--config`. Install: `npm install -g @openai/codex`.

---

### `project` — Project Settings

| Field | Type | Values | Description |
|-------|------|--------|-------------|
| `type` | string | `web-app`, `api`, `library`, `cli`, `tui` | Project type |
| `stack` | string | e.g. `nextjs-typescript` | Detected or selected stack |
| `template` | string | Template name used | Only for new projects |

---

### `mode` — Work Mode

| Field | Type | Values | Description |
|-------|------|--------|-------------|
| `work` | string | `solo`, `team` | Solo or team mode |
| `autonomy` | string | `supervised`, `balanced`, `autonomous` | Default autonomy level |
| `max_team_size` | number | 2-5 | Max team members (team mode) |

---

### `work` — Work Loop Settings

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `max_iterations` | number | 10 | Max loop iterations per session |
| `retry_limit` | number | 2 | Retries before skipping failed task |
| `kill_after_fails` | number | 3 | Consecutive fails before BLOCKED exit |
| `branch_prefix` | string | `feat/` | Prefix for auto-created branches |
| `commit_prefix` | string | `feat\|fix\|...` | Allowed conventional commit types |

---

### `orchestration` — Orchestrate Mode Settings

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `autonomy` | string | `balanced` | Phase-level autonomy: `supervised`, `balanced`, `autonomous` |
| `pause_for_brainstorm` | boolean | `true` | Pause at phase 6 (Brainstorm) for human input |
| `max_phase_retries` | number | 2 | Max retries per phase before marking failed |
| `heavy_phases` | array | `[3, 8, 13]` | Phases that trigger compaction gate check |
| `compaction_block_threshold` | number | 0.85 | Block at this fraction of context window |
| `compaction_warn_threshold` | number | 0.75 | Warn at this fraction of context window |
| `output_dir` | string | `.jonggrang/.output` | Where feature manifests and agent outputs are saved |
| `ephemeral_dir` | string | `.jonggrang/.ephemeral` | Where feedback-loop and compaction state are saved |

Example:

```json
{
  "orchestration": {
    "autonomy": "balanced",
    "pause_for_brainstorm": true,
    "max_phase_retries": 2,
    "heavy_phases": [3, 8, 13],
    "compaction_block_threshold": 0.85,
    "compaction_warn_threshold": 0.75,
    "output_dir": ".jonggrang/.output",
    "ephemeral_dir": ".jonggrang/.ephemeral"
  }
}
```

---

### `hooks` — Quality Gate Hooks

Shell commands executed at lifecycle events during the **work loop**. In orchestrate mode, hooks are managed by the universal hook system (`.claude/settings.json` or `.opencode/plugins/jonggrang.js`).

| Hook | When | Example |
|------|------|---------|
| `pre_implement` | Before implementation starts | Custom checks |
| `post_implement` | After implementation, before tests | `npm run typecheck`, `npm run lint` |
| `pre_commit` | Before git commit | `npm run test` |
| `post_commit` | After successful commit | Notifications |
| `task_complete` | After task marked done | `npm run test -- --coverage` |
| `session_end` | After work loop exits | Cleanup, reporting |

Example:

```json
{
  "hooks": {
    "pre_implement": [],
    "post_implement": ["npm run typecheck", "npm run lint"],
    "pre_commit": ["npm run test"],
    "post_commit": [],
    "task_complete": ["npm run test -- --coverage"],
    "session_end": []
  }
}
```

---

### `testing` — Test Configuration

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `framework` | string | auto-detect | `vitest`, `jest`, `go-test`, `pytest` |
| `command` | string | depends | Full test command |
| `coverage_threshold` | number | 80 | Minimum coverage % (enforced at phases 14 and task_complete) |

---

### `ci` — CI/CD Configuration

| Field | Type | Values | Description |
|-------|------|--------|-------------|
| `provider` | string | `github-actions`, `gitlab-ci`, `none`, `custom` | CI provider |
| `auto_setup` | boolean | true | Auto-generate CI config |

---

### `skills` — Skill System

| Field | Type | Description |
|-------|------|-------------|
| `directory` | string | Root path of the skills folder used by the selected tool (e.g. `.claude/skills` or `.opencode/skills`) |
| `custom` | array | List of custom skill names |

---

### `review` — Review Settings

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `security` | boolean | true | Include security scan |
| `performance` | boolean | true | Include performance scan |
| `coverage` | boolean | true | Include coverage analysis |

---

### Full Example

```json
{
  "name": "my-api",
  "version": "1.0.0",
  "tool": "claude",
  "project": {
    "type": "api",
    "stack": "express-typescript"
  },
  "mode": {
    "work": "solo",
    "autonomy": "balanced"
  },
  "work": {
    "max_iterations": 10,
    "retry_limit": 2,
    "kill_after_fails": 3,
    "branch_prefix": "feat/",
    "commit_prefix": "feat|fix|chore|refactor|test|docs"
  },
  "orchestration": {
    "autonomy": "balanced",
    "pause_for_brainstorm": true,
    "max_phase_retries": 2,
    "heavy_phases": [3, 8, 13],
    "compaction_block_threshold": 0.85,
    "compaction_warn_threshold": 0.75
  },
  "hooks": {
    "post_implement": ["npm run typecheck", "npm run lint"],
    "pre_commit": ["npm run test"]
  },
  "testing": {
    "framework": "vitest",
    "command": "npm run test",
    "coverage_threshold": 80
  },
  "ci": {
    "provider": "github-actions",
    "auto_setup": true
  },
  "skills": {
    "directory": "./.claude/skills",
    "custom": []
  },
  "review": {
    "security": true,
    "performance": true,
    "coverage": true
  }
}
```

---

## .jonggrang/jonggrang-tasks.json

Task board state file used by the **work loop**.

```json
{
  "feature": "feature-name",
  "branch": "feat/feature-name",
  "created_at": "2026-04-11T10:00:00Z",
  "tasks": [
    {
      "id": "task-001",
      "title": "Task description",
      "description": "Detailed description",
      "priority": 1,
      "status": "pending | in_progress | completed | blocked | skipped",
      "role": "developer | tester | reviewer | lead | null",
      "owner": "member-name | null",
      "skill": "skill-name | null",
      "skill_inputs": {},
      "files": ["list", "of", "owned", "files"],
      "blocked_by": ["task-id"],
      "passes": false,
      "retry_count": 0,
      "started_at": null,
      "completed_at": null,
      "error_log": []
    }
  ]
}
```

### Task Status Flow

```
pending --> in_progress --> completed
                |
                +--> blocked (by dependency or repeated failure)
                |
                +--> skipped (after kill_after_fails reached)
```

### `role` Field

The optional `role` field restricts which agent role can pick up a task:

| Value | Meaning |
|-------|---------|
| `"developer"` | Only the Developer role processes this task |
| `"tester"` | Only the Tester role processes this task |
| `"reviewer"` | Only the Reviewer role processes this task |
| `"lead"` | Only the Lead role processes this task |
| `null` | Any agent can pick this task (role inferred from description) |

This is used in orchestrate mode to route tasks to the right phase agent. In work loop mode with `--role`, only tasks matching the specified role are eligible.

---

## MANIFEST.yaml

Persistent orchestration state for a feature run. Located at:

```
.jonggrang/.output/features/<feature-id>/MANIFEST.yaml
```

| Field | Type | Description |
|-------|------|-------------|
| `feature_id` | string | Unique feature identifier |
| `description` | string | Original orchestrate description |
| `work_type` | string | `BUGFIX`, `SMALL`, `MEDIUM`, `LARGE` |
| `status` | string | `running`, `completed`, `failed`, `paused` |
| `current_phase` | number | Currently executing phase (1-16) |
| `active_phases` | array | Phases that will run (after skipping) |
| `phases` | object | Per-phase status, started/completed timestamps, output, `output_files` |
| `agents` | object | Per-role metadata (model, timestamps) |
| `phases[N].output_files` | array | Files produced by phase N. Each entry: `path` (relative), `type` (`code`\|`log`\|`report`\|`output`), `size` (bytes, null if missing), `created_at` (ISO, null if missing). Tracked for phases 8, 12, 14. |
| `validation` | object | typecheck, lint, tests pass/fail booleans |
| `context_usage` | object | current_tokens, percentage |

---

## Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `JONGGRANG_CONFIG` | Path to jonggrang.json | `./.jonggrang/jonggrang.json` |
| `JONGGRANG_MODE` | Override autonomy mode | from config |
| `JONGGRANG_MAX_ITERATIONS` | Override max iterations | from config |
| `JONGGRANG_VERBOSE` | Verbose logging | false |
| `JONGGRANG_DRY_RUN` | Plan only, no execution | false |
| `JONGGRANG_TOOL` | Override default AI tool (`claude` \| `opencode` \| `jonggrang` \| `codex`) | from config |
| `JONGGRANG_MODEL` | Override default model (backend-specific format) | from config |
| `JONGGRANG_EFFORT` | Override default effort/thinking level | from config |
| `JONGGRANG_PROJECT_ROOT` | Explicit project root for hook resolution | `process.cwd()` |
| `JONGGRANG_HOOKS_DIR` | Path to hooks/ directory | auto-resolved |
| `JONGGRANG_COMPACTION_THRESHOLD` | Override block threshold (0-1) | 0.85 |
| `JONGGRANG_WEB_PORT` | `jonggrang web` dashboard port (also `--port`) | `7777` |
| `JONGGRANG_WEB_HOST` | `jonggrang web` dashboard bind host (also `--host`) | `127.0.0.1` |
| `PORT` / `HOST` | Port/host read directly by `server.js` | `7777` / `127.0.0.1` |
| `NODE_ENV` | `development` → Vite middleware (client HMR); otherwise serve static `client/dist` | unset (production) |
| `OPENCODE_PROJECT_ROOT` | Project root passed by OpenCode to plugin | set by OpenCode |
| `GH_TOKEN` | GitHub CLI (`gh`) auth token | from global Git Host Tokens setting |
| `GITLAB_TOKEN` | GitLab CLI (`glab`) auth token | from global Git Host Tokens setting |

> **Git Host Tokens (web dashboard):** `GH_TOKEN` / `GITLAB_TOKEN` are set globally in
> Settings → Git Host Tokens and stored in `~/.jonggrang/web/index.json` (`git_tokens`).
> They are injected as default env into every sandbox container/agent so the `gh` and
> `glab` CLIs are pre-authenticated. The dashboard never returns the stored values — only
> whether each is set. A per-project secret with the same name overrides the global token.
