# AgenTeam Specification

## Distribution

```
npm: agenteam
install: npm i -g agenteam  OR  "plugin": ["agenteam"] in opencode.json
init: cd your-project && npx agenteam init
uninstall: cd your-project && npx agenteam uninstall
```

AgenTeam is project-scoped. Run `init` and `uninstall` inside your project directory. The wizard warns if no project markers are detected (`package.json`, `pyproject.toml`, `Cargo.toml`, `.git`).

## File Layout

```
project/
├── agenteam.config.json         # user config
├── .agenteam/
│   ├── state.json               # runtime state (gitignored, in-memory cache)
│   ├── log.jsonl                # event log (gitignored)
│   ├── debug.jsonl              # verbose debug trace (gitignored)
│   └── memory/                  # vectra index + content files (gitignored)
├── docs/
│   ├── ARCHITECTURE.md
│   └── SPEC.md
└── src/
    ├── index.ts                 # plugin entry
    ├── agents.ts                # agent prompts
    ├── types.ts                 # type definitions
    ├── state-io.ts              # state + debug log I/O (in-memory cache)
    ├── enforcer.ts              # FSM enforcement
    ├── memory/
    │   ├── index.ts             # MemoryManager (vectra wrapper)
    │   └── embeddings.ts        # @huggingface/transformers embedding provider
    └── tools/
        ├── state.ts
        ├── dag.ts
        ├── triage.ts
        ├── compress.ts
        ├── review-gate.ts
        └── memory.ts
```

## Config: `agenteam.config.json`

```typescript
interface AgenteamConfig {
  models: { cheap: string; smart: string }
  limits: {
    max_review_retries: number
    escalation: "smart" | "user" | "fail"
  }
  triage: { simple_max_files: number; risk_keywords: string[] }
  review: { lint_command: string | null; always_adversary: boolean }
  adversary_model: string                    // "smart" | "cheap" — model key for adversary agent
  memory: MemoryConfig
}

interface MemoryConfig {
  enabled: boolean
  embedding_model: string                   // HuggingFace model ID (default: Xenova/all-MiniLM-L6-v2)
  max_auto_results: number                  // Max results for auto-injection
  max_auto_tokens: number                   // Max token budget for auto-injection summary
  similarity_threshold: number              // Min cosine similarity for query results
  auto_inject_on_triage: boolean            // Auto-query memory after triage
}
```

## State: `.agenteam/state.json`

```typescript
interface ProjectState {
  goal: string
  phase: "idle" | "triaged" | "executing" | "done"
  triage_result: TriageResult | null
  tasks: TaskNode[]
  active_tasks: string[]
  log: LogEntry[]
  memory_context?: string                   // Auto-injected memory summary after triage
}

interface TriageResult {
  complexity: "simple" | "complex"
  risk: "low" | "high"
  files: string[]
  suggested_tier: "cheap" | "smart"
}

interface TaskNode {
  id: string
  desc: string
  tier: "cheap" | "smart"
  files: string[]
  deps: string[]
  status: "pending" | "running" | "done" | "failed"
  review: {
    attempts: number
    review_passed: boolean                   // Must be true before dag(complete) succeeds
  }
  started_at?: number
  completed_at?: number
}

interface LogEntry {
  ts: number
  task_id: string | null
  event: string
  detail?: string
  source?: LogSource
}
```

## Memory Types

```typescript
type MemoryCategory = "product" | "technical" | "architecture" | "convention"

interface MemoryEntry {
  id: string
  category: MemoryCategory
  title: string
  content: string
  tags: string[]
  project: string
  created_at: string
  source: "manual"
}

interface MemorySearchResult {
  entry: MemoryEntry
  score: number
}
```

## Debug Types

```typescript
type LogSource = "enforcer" | "tool" | "agent" | "plugin" | "system"

interface DebugLogEntry {
  ts: number
  source: LogSource
  event: string
  payload?: unknown
}
```

## Agents

| Agent | Mode | Model | Write | Bash | Task dispatch |
|-------|------|-------|-------|------|---------------|
| orchestrator | primary | smart | deny | deny | planner,coder,coder-hard,adversary |
| planner | subagent,hidden | smart | deny | deny (wc,find only) | - |
| coder | subagent,hidden | cheap | allow | allow (no rm -rf, git push) | - |
| coder-hard | subagent,hidden | smart | allow | allow (no rm -rf, git push) | - |
| adversary | subagent,hidden | config[adversary_model] | deny | deny (git diff, grep only) | - |

## Custom Tools (plugin-bundled, agenteam_ prefix)

### agenteam_triage
```
in:  { request: string, files: string[] }
out: TriageResult { complexity, risk, files, suggested_tier }
side effect: sets phase=triaged, stores triage_result, writes state to disk
             if memory.auto_inject_on_triage: queries memory and sets state.memory_context
cost: 0 tokens (deterministic scoring)
debug: logs detailed analysis (loc, imports, functions, risk signals) to debug.jsonl
```

### agenteam_state
```
in:  { action: "read"|"full"|"update"|"reset", patch?: Partial<ProjectState> }
out: read -> compact summary (phase, dag tree, active tasks, task counts, memory_context, recent log)
     full -> ProjectState
     update -> { ok, phase }
     reset -> { ok, phase: "idle" }
note: in-memory cache with synchronous disk writes. patchable fields: goal, active_tasks, phase, memory_context
```

### agenteam_dag
```
in:  { action: "create"|"next"|"complete"|"fail"|"retriage",
       tasks?: TaskInput[], task_id?: string, user_confirmed?: boolean,
       retriage_tier?: "cheap"|"smart" }
out: create -> { valid, count, ready[], warning? }
     next   -> { ready: TaskNode[] } (auto-starts ALL ready tasks, returns all)
     complete -> { ok, remaining, all_done } (requires review_passed=true)
     fail    -> { ok, remaining, all_done }
     retriage -> { ok, task_id, old_tier, new_tier }
side effect:
     create sets phase=executing, writes state
     complete sets phase=done if all tasks done
validation: no cycles, no duplicate IDs, deps must exist
timing: tracks started_at/completed_at per task
```

### agenteam_compress
```
in:  { file: string, mode: "full"|"signatures"|"minimal" }
out: { file, mode, content, original_lines, compressed_lines, saved_lines, reduction }
cost: 0 tokens (deterministic)
```

### agenteam_review_gate
```
in:  { task_id: string, lint_ok: boolean, lint_output?: string, adversary?: { passed, critique } }
out: { decision: "pass"|"retry"|"escalate", reason, attempts }
logic: lint fail -> retry (or escalate if max). adversary fail -> retry (or escalate if max). both pass -> pass.
       When always_adversary=true and no adversary provided -> retry.
       Sets review_passed=true only when decision="pass".
       dag(complete) will throw if review_passed is not true.
```

### agenteam_memory
```
in:  { action: "store"|"query"|"list"|"delete"|"update",
       category?, title?, content?, tags?, query?, max_results?, summary?, id? }
out: store  -> { id, stored: true }
     query  -> { results: MemorySearchResult[], count } | { summary, count } (if summary=true)
     list   -> { entries: MemoryEntry[], count }
     delete -> { ok: true }
     update -> { id, updated: true, fields }
cost: 0 LLM tokens (uses local ONNX embeddings)
gating: NOT phase-gated — works in any phase
```

## Debug Logging

All events are appended to `.agenteam/debug.jsonl` in real-time via `debugLog(source, event, payload)`.

Sources: `enforcer`, `tool`, `agent`, `plugin`, `system`

Events logged:
- Tool executions (all agenteam_* tools)
- DAG transitions (create, next, complete, fail, retriage)
- Review gate decisions
- State transitions
- Memory operations (store, query, init, embedding loading)
- State cache operations (write failures)

## Enforcer (tool.execute.before hook)

Reads state from disk on every intercepted call.
Throws descriptive errors on workflow violations.
LLM sees the error and self-corrects.

Key enforcement rules:
- `review_passed` must be `true` before `dag(complete)` succeeds (enforced in dag tool)
- When `always_adversary=true`, review_gate rejects calls without adversary result
- Adversary allowed for all task tiers (both cheap and smart)
- Coder blocked for tier=smart tasks; coder-hard blocked for tier=cheap tasks (tier-matched dispatch)
- Re-triage gated to executing phase
- Memory, compress, and state tools are NOT phase-gated

## CLI Commands

### `npx agenteam init`
Interactive setup wizard. **Must be run inside a project directory.**
- Detects project markers (`package.json`, `pyproject.toml`, `Cargo.toml`, `.git`), warns if none found
- Reads providers/models from `~/.config/opencode/opencode.json`
- Two-step selection: pick provider, then model (for smart and cheap tiers)
- Auto-selects when only one provider or model available
- Creates `agenteam.config.json` with chosen models
- Creates `.agenteam/state.json` (empty state)
- Adds `.agenteam/` to `.gitignore`
- Registers plugin globally in `~/.config/opencode/opencode.json`

### `npx agenteam config`
Re-runs the model selection wizard on an existing project. Preserves all other config values.

### `npx agenteam uninstall`
Removes all AgenTeam artifacts from the current project:
- Deletes `.agenteam/` directory (state, logs, memory index)
- Deletes `agenteam.config.json`
- Removes `.agenteam/` entry from `.gitignore`
- Removes plugin from `~/.config/opencode/opencode.json`

Prompts for confirmation before deleting anything.

### `npx agenteam enable`
Adds `agenteam` to `~/.config/opencode/opencode.json` plugin list.

### `npx agenteam disable`
Removes `agenteam` from `~/.config/opencode/opencode.json` plugin list.
