roundtable-cli 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- roundtable/__init__.py +10 -0
- roundtable/agents.py +218 -0
- roundtable/cli.py +722 -0
- roundtable/config.py +265 -0
- roundtable/dashboard.py +533 -0
- roundtable/discovery.py +71 -0
- roundtable/engine.py +714 -0
- roundtable/errors.py +20 -0
- roundtable/insights.py +210 -0
- roundtable/llm.py +1048 -0
- roundtable/mcp.py +248 -0
- roundtable/modelpick.py +309 -0
- roundtable/models.py +202 -0
- roundtable/prompts.py +205 -0
- roundtable/runctl.py +212 -0
- roundtable/scan.py +187 -0
- roundtable/store.py +339 -0
- roundtable_cli-0.4.0.dist-info/METADATA +570 -0
- roundtable_cli-0.4.0.dist-info/RECORD +22 -0
- roundtable_cli-0.4.0.dist-info/WHEEL +4 -0
- roundtable_cli-0.4.0.dist-info/entry_points.txt +4 -0
- roundtable_cli-0.4.0.dist-info/licenses/LICENSE +21 -0
roundtable/config.py
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""Roundtable configuration: which backend, which model/agent per role, runtime knobs.
|
|
2
|
+
|
|
3
|
+
Loaded from ``roundtable.config.yaml`` at the project root.
|
|
4
|
+
|
|
5
|
+
Four backends (``provider``):
|
|
6
|
+
|
|
7
|
+
* ``pi`` — drive the `pi` coding agent for every role. Task agents run with
|
|
8
|
+
pi's file tools (they edit the repo); every other role runs
|
|
9
|
+
``pi --no-tools`` as a pure completion. Connectivity, auth and
|
|
10
|
+
model routing are handled by pi/pi-ai. The recommended backend
|
|
11
|
+
when `pi` is installed — it reports exact token usage and cost.
|
|
12
|
+
* ``cli`` — run other LLMs through their terminal CLIs (claude, codex, gemini,
|
|
13
|
+
aider, llm, ollama, ...). Each role's "model" names an entry in
|
|
14
|
+
``agents``. Agents act on the real project files via their own
|
|
15
|
+
tools. This is what roundtable falls back to without pi.
|
|
16
|
+
* ``litellm`` — direct API calls; "model" is a litellm model string.
|
|
17
|
+
* ``scripted``— deterministic offline backend (demo/tests).
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
import yaml
|
|
25
|
+
from pydantic import BaseModel, Field
|
|
26
|
+
|
|
27
|
+
from .models import AgentRef
|
|
28
|
+
|
|
29
|
+
CONFIG_FILENAME = "roundtable.config.yaml"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _ref(agent: str) -> AgentRef:
|
|
33
|
+
return AgentRef(agent=agent)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ModelRoles(BaseModel):
|
|
37
|
+
"""Default (agent, model) assignment per role.
|
|
38
|
+
|
|
39
|
+
Each value is an :class:`AgentRef` — accepts an object ``{agent, model}`` or
|
|
40
|
+
the shorthand string ``agent:model`` in YAML. Per-phase / per-task ``runner``
|
|
41
|
+
entries in the generated plan override these.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
planner: AgentRef = Field(default_factory=lambda: _ref("claude"))
|
|
45
|
+
main: AgentRef = Field(default_factory=lambda: _ref("claude"))
|
|
46
|
+
phase: AgentRef = Field(default_factory=lambda: _ref("claude"))
|
|
47
|
+
task: AgentRef = Field(default_factory=lambda: _ref("claude"))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class AgentSpec(BaseModel):
|
|
51
|
+
"""A terminal command that talks to an LLM.
|
|
52
|
+
|
|
53
|
+
``command`` is an argv list (no shell). Tokens may contain ``{prompt}``,
|
|
54
|
+
``{system}`` and ``{model}`` placeholders. ``{model}`` is replaced with the
|
|
55
|
+
role/task's chosen model so one agent command serves many models. If
|
|
56
|
+
``{system}`` is absent, the system text is prepended to the prompt. If
|
|
57
|
+
``stdin`` is true, the prompt is piped on stdin instead of into ``{prompt}``.
|
|
58
|
+
|
|
59
|
+
``models_command`` is an optional argv that lists the models this CLI offers
|
|
60
|
+
(e.g. ``["opencode", "models"]``, ``["agy", "models"]``, ``["ollama",
|
|
61
|
+
"list"]``); ``roundtable init`` / ``roundtable agents`` runs it to show you what you
|
|
62
|
+
can assign. Omit it for tools with no enumeration command (claude, codex).
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
command: list[str]
|
|
66
|
+
stdin: bool = False
|
|
67
|
+
pty: bool = False # run in a pseudo-terminal so the CLI sees a real TTY
|
|
68
|
+
models_command: list[str] | None = None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class Defaults(BaseModel):
|
|
72
|
+
temperature: float = 0.2
|
|
73
|
+
max_concurrency: int = Field(default=1, ge=1)
|
|
74
|
+
max_retries: int = Field(default=1, ge=0)
|
|
75
|
+
timeout: int = Field(default=900, ge=1) # per agent call, seconds
|
|
76
|
+
hitl_timeout: int = Field(default=0, ge=0) # seconds to wait for HITL approval; 0 = infinite
|
|
77
|
+
validate_timeout: int = Field(default=120, ge=1) # seconds per task/phase validate_command
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# No-config fallback agents. Kept model-less so the bare-agent role defaults
|
|
81
|
+
# (ModelRoles -> agent only) work without an empty {model} argument. The richer
|
|
82
|
+
# {model}-templated commands live in DEFAULT_CONFIG_YAML that `roundtable init` writes.
|
|
83
|
+
DEFAULT_AGENTS: dict[str, AgentSpec] = {
|
|
84
|
+
# Claude Code, non-interactive print mode.
|
|
85
|
+
"claude": AgentSpec(command=["claude", "-p", "{prompt}"]),
|
|
86
|
+
# OpenAI Codex CLI, non-interactive exec mode.
|
|
87
|
+
"codex": AgentSpec(command=["codex", "exec", "{prompt}"]),
|
|
88
|
+
# Gemini CLI.
|
|
89
|
+
"gemini": AgentSpec(command=["gemini", "-p", "{prompt}"]),
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class PiOptions(BaseModel):
|
|
94
|
+
"""Options for ``provider: pi`` — drive a pi-family coding agent for every role.
|
|
95
|
+
|
|
96
|
+
Two flavors are supported (both share the same CLI contract — ``--mode json``,
|
|
97
|
+
``--no-tools``, ``--model``, stdin prompt, and identical usage/cost JSON):
|
|
98
|
+
|
|
99
|
+
* ``pi`` — upstream `pi` (`earendil-works/pi`), binary ``pi``.
|
|
100
|
+
* ``omp`` — `oh-my-pi` (`can1357/oh-my-pi`), binary ``omp``; a batteries-included
|
|
101
|
+
fork (LSP/DAP/subagents). It has no ``--no-context-files`` and gates edits
|
|
102
|
+
behind approval, so task agents get ``--auto-approve`` for autonomous runs.
|
|
103
|
+
|
|
104
|
+
Only the task role runs with file tools (so task agents edit the repo); every
|
|
105
|
+
other role (planner, phase, main, map) runs ``--no-tools`` as a pure completion.
|
|
106
|
+
Each role's ``model`` is passed to ``--model`` (a ``provider/id`` string, glob,
|
|
107
|
+
or fuzzy pattern — ``<tool> --list-models`` shows what's available). Auth is the
|
|
108
|
+
tool's: set a provider key in the environment (``ANTHROPIC_API_KEY`` /
|
|
109
|
+
``OPENAI_API_KEY`` / ``GEMINI_API_KEY`` / ...) or use the tool's own login.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
flavor: str = "pi" # "pi" (upstream) | "omp" (oh-my-pi)
|
|
113
|
+
command: list[str] = Field(default_factory=list) # override binary; [] -> flavor default (["pi"]/["omp"])
|
|
114
|
+
extra_args: list[str] = Field(default_factory=list) # appended to every invocation
|
|
115
|
+
worker_extra_args: list[str] = Field(default_factory=list) # extra flags for the task (worker) role
|
|
116
|
+
orchestrator_extra_args: list[str] = Field(default_factory=list) # extra flags for non-worker roles
|
|
117
|
+
# (pi flavor only) orchestrator roles ignore the repo's AGENTS.md/CLAUDE.md by
|
|
118
|
+
# default (roundtable injects its own context); set true to let them load those.
|
|
119
|
+
orchestrator_context_files: bool = False
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class Config(BaseModel):
|
|
123
|
+
provider: str = "cli" # "pi" | "cli" | "litellm" | "scripted"
|
|
124
|
+
models: ModelRoles = Field(default_factory=ModelRoles)
|
|
125
|
+
agents: dict[str, AgentSpec] = Field(default_factory=lambda: dict(DEFAULT_AGENTS))
|
|
126
|
+
pi: PiOptions = Field(default_factory=PiOptions)
|
|
127
|
+
defaults: Defaults = Field(default_factory=Defaults)
|
|
128
|
+
project_context: str = "" # optional project context injected into agent prompts
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
DEFAULT_CONFIG_YAML = """\
|
|
132
|
+
# roundtable configuration.
|
|
133
|
+
|
|
134
|
+
# Backend:
|
|
135
|
+
# cli -> reach other LLMs through their terminal CLIs (default)
|
|
136
|
+
# litellm -> direct API calls (model = litellm string, needs API keys)
|
|
137
|
+
# scripted -> deterministic offline backend (demo/tests, no network)
|
|
138
|
+
provider: cli
|
|
139
|
+
|
|
140
|
+
# Choosable (agent, model) per role. `agent` names an entry in `agents` below;
|
|
141
|
+
# `model` is passed to that agent's {model} placeholder. Mix CLIs and models
|
|
142
|
+
# freely per role -- e.g. main on claude/opus, phase on antigravity/gemini,
|
|
143
|
+
# tasks on opencode. Per-phase and per-task `runner` entries in the generated
|
|
144
|
+
# plan override these. (With provider: litellm, `model` is the litellm string
|
|
145
|
+
# -- openai/gpt-4o, anthropic/claude-3-5-sonnet-latest, ... -- and `agent` is
|
|
146
|
+
# ignored.) Shorthand `agent:model` strings work too.
|
|
147
|
+
models:
|
|
148
|
+
planner: { agent: claude, model: opus }
|
|
149
|
+
main: { agent: claude, model: opus }
|
|
150
|
+
phase: { agent: antigravity, model: gemini-3.5-flash }
|
|
151
|
+
task: { agent: opencode, model: opencode/mimo-v2.5-free }
|
|
152
|
+
|
|
153
|
+
# Terminal commands for provider: cli. argv lists (no shell). Tokens may use
|
|
154
|
+
# {prompt}, {system} and {model}; {model} is replaced with the role/task's
|
|
155
|
+
# chosen model so one command serves many models. If {system} is absent it is
|
|
156
|
+
# prepended to the prompt. Set stdin: true to pipe the prompt on stdin instead
|
|
157
|
+
# of via {prompt}. Add whatever flags your tool needs to run non-interactively
|
|
158
|
+
# and edit files (these flags are illustrative -- check each tool's own docs).
|
|
159
|
+
# Optional models_command lists that CLI's models; `roundtable init` / `roundtable
|
|
160
|
+
# agents` runs it so you can see what to assign above.
|
|
161
|
+
agents:
|
|
162
|
+
claude:
|
|
163
|
+
command: ["claude", "-p", "{prompt}", "--model", "{model}"]
|
|
164
|
+
codex:
|
|
165
|
+
command: ["codex", "exec", "--model", "{model}", "{prompt}"]
|
|
166
|
+
# Antigravity CLI (formerly the Gemini CLI; binary is `agy`), runs Gemini models.
|
|
167
|
+
antigravity:
|
|
168
|
+
command: ["agy", "-p", "{prompt}", "--model", "{model}"]
|
|
169
|
+
models_command: ["agy", "models"]
|
|
170
|
+
opencode:
|
|
171
|
+
command: ["opencode", "run", "--model", "{model}", "{prompt}"]
|
|
172
|
+
models_command: ["opencode", "models"]
|
|
173
|
+
ollama:
|
|
174
|
+
command: ["ollama", "run", "{model}"]
|
|
175
|
+
models_command: ["ollama", "list"]
|
|
176
|
+
stdin: true
|
|
177
|
+
|
|
178
|
+
defaults:
|
|
179
|
+
temperature: 0.2
|
|
180
|
+
max_concurrency: 1 # raise to run independent tasks in a phase concurrently
|
|
181
|
+
# (keep at 1 when file-editing agents might touch the same files)
|
|
182
|
+
max_retries: 1 # retries on a failing/timed-out agent call
|
|
183
|
+
timeout: 900 # seconds per agent call
|
|
184
|
+
validate_timeout: 120 # seconds per task/phase validate_command
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# Recommended backend when a pi-family CLI is installed: it drives every role and
|
|
189
|
+
# handles LLM connectivity/auth itself. `roundtable init` writes this when it finds
|
|
190
|
+
# `pi` or `omp` on PATH. Model defaults are a mixed strong+cheap start -- edit freely.
|
|
191
|
+
def pi_config_yaml(flavor: str = "pi") -> str:
|
|
192
|
+
tool = "omp" if flavor == "omp" else "pi"
|
|
193
|
+
other = "pi" if flavor == "omp" else "omp"
|
|
194
|
+
models_hint = "`pi --list-models`" if tool == "pi" else "`omp --help` / omp's model config"
|
|
195
|
+
return f"""\
|
|
196
|
+
# roundtable configuration ({tool} backend -- recommended).
|
|
197
|
+
|
|
198
|
+
# Backend:
|
|
199
|
+
# pi -> drive a pi-family coding agent ({tool}) for every role (this file).
|
|
200
|
+
# It handles LLM connectivity + auth; task agents edit files,
|
|
201
|
+
# orchestrator roles run `{tool} --no-tools`. Exact usage & cost reported.
|
|
202
|
+
# flavor `pi` = upstream `pi`; flavor `omp` = oh-my-pi (binary `omp`).
|
|
203
|
+
# cli -> reach other LLMs through their terminal CLIs (claude, codex, ...)
|
|
204
|
+
# litellm -> direct API calls (model = litellm string, needs API keys)
|
|
205
|
+
# scripted -> deterministic offline backend (demo/tests, no network)
|
|
206
|
+
provider: pi
|
|
207
|
+
|
|
208
|
+
# Per-role model (passed to `{tool} --model`: a `provider/id` string, glob, or fuzzy
|
|
209
|
+
# pattern -- see {models_hint} for what you can use). `agent` is ignored
|
|
210
|
+
# on this backend. Mixed strong+cheap defaults: strong models plan/orchestrate, a
|
|
211
|
+
# cheap/fast model does the task work. Per-phase/per-task `runner` entries in the
|
|
212
|
+
# generated plan override these.
|
|
213
|
+
models:
|
|
214
|
+
planner: {{ model: anthropic/claude-opus-4-1 }} # strong: breaks the goal into phases/tasks
|
|
215
|
+
main: {{ model: anthropic/claude-opus-4-1 }} # strong: keeps project docs coherent
|
|
216
|
+
phase: {{ model: anthropic/claude-sonnet-4-5 }} # mid: defines & summarizes each phase
|
|
217
|
+
task: {{ model: anthropic/claude-haiku-4-5 }} # cheap/fast: does the actual task work
|
|
218
|
+
# Cheaper/free task work? Point `task` (and `phase`) at another provider the tool
|
|
219
|
+
# supports, e.g. `openrouter/...` or `opencode/...` -- see {models_hint}.
|
|
220
|
+
|
|
221
|
+
# Connect {tool} to your LLMs (pick one per provider you use):
|
|
222
|
+
# * env key: export ANTHROPIC_API_KEY=... (or OPENAI_API_KEY / GEMINI_API_KEY / ...)
|
|
223
|
+
# * or use {tool}'s own login (e.g. `pi-ai login anthropic` for pi)
|
|
224
|
+
|
|
225
|
+
# Options for the pi backend (all optional).
|
|
226
|
+
pi:
|
|
227
|
+
flavor: {flavor} # "pi" (upstream) or "omp" (oh-my-pi); switch to use `{other}`
|
|
228
|
+
command: ["{tool}"] # base binary; use ["npx", "{tool}"] if not installed globally
|
|
229
|
+
extra_args: [] # appended to every call, e.g. ["--thinking", "medium"]
|
|
230
|
+
worker_extra_args: [] # extra flags for the task role{_omp_worker_note(flavor)}
|
|
231
|
+
orchestrator_extra_args: [] # extra flags for planner/main/phase/map roles
|
|
232
|
+
orchestrator_context_files: false # (pi flavor) true -> orchestrator roles read AGENTS.md/CLAUDE.md
|
|
233
|
+
|
|
234
|
+
defaults:
|
|
235
|
+
temperature: 0.2
|
|
236
|
+
max_concurrency: 1 # raise to run independent tasks in a phase concurrently
|
|
237
|
+
# ({tool} has no per-action permission gate and tasks share the
|
|
238
|
+
# repo dir, so keep at 1 unless you isolate tasks yourself)
|
|
239
|
+
max_retries: 1 # retries on a failing/timed-out {tool} call
|
|
240
|
+
timeout: 900 # seconds per {tool} call
|
|
241
|
+
validate_timeout: 120 # seconds per task/phase validate_command
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _omp_worker_note(flavor: str) -> str:
|
|
246
|
+
if flavor == "omp":
|
|
247
|
+
return " (omp task agents already get --auto-approve)"
|
|
248
|
+
return ""
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def load_config(project_dir: Path) -> Config:
|
|
252
|
+
path = Path(project_dir) / CONFIG_FILENAME
|
|
253
|
+
if not path.exists():
|
|
254
|
+
return Config()
|
|
255
|
+
raw = yaml.safe_load(path.read_text()) or {}
|
|
256
|
+
return Config.model_validate(raw)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def write_default_config(project_dir: Path, *, backend: str = "cli", flavor: str = "pi") -> Path:
|
|
260
|
+
"""Write ``roundtable.config.yaml``. ``backend='pi'`` writes the recommended
|
|
261
|
+
pi-family template for ``flavor`` ('pi' or 'omp'); anything else writes the CLI
|
|
262
|
+
template."""
|
|
263
|
+
path = Path(project_dir) / CONFIG_FILENAME
|
|
264
|
+
path.write_text(pi_config_yaml(flavor) if backend == "pi" else DEFAULT_CONFIG_YAML)
|
|
265
|
+
return path
|