regent-cli 0.1.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Flavio Alvim
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,52 @@
1
+ Metadata-Version: 2.4
2
+ Name: regent-cli
3
+ Version: 0.1.0
4
+ Summary: Autonomous conduction and mediated adversarial deliberation between AI agents, pluggable into any project.
5
+ Author-email: Flavio Alvim <flavioalvim@gmail.com>
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/flavioalvim/regent
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Dynamic: license-file
16
+
17
+ # regent
18
+
19
+ Autonomous conduction and mediated adversarial deliberation between AI agents, pluggable
20
+ into any project.
21
+
22
+ **regent** governs turns between agents (Claude, Codex, human mediators) under a frozen
23
+ protocol: atomic turn mutex, CAS-versioned state, deliberation rounds with versioned
24
+ acceptances, and a conduction daemon that executes production batches with a confined agent,
25
+ test gates and evidence proof.
26
+
27
+ Extracted from the tool proven end-to-end in the ArtNFT project (IMP-003: first product
28
+ batch fully conducted by the daemon, deliberated, accepted and deployed to production).
29
+
30
+ - Requirements: [`docs/PRD.md`](docs/PRD.md)
31
+ - Extraction scope and decisions: [`docs/ESCOPO.md`](docs/ESCOPO.md) (PT-BR, pre-rename)
32
+ - Deliberation rounds: [`docs/brainstorm/`](docs/brainstorm/) (mediator's language, PT-BR)
33
+ - Status: **pre-extraction** (scope closed 2026-07-20; code not yet migrated)
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ pip install <path-to-this-repo> # package: regent-cli; CLI: regent (not on PyPI yet)
39
+ cd <your-project>
40
+ regent init # seeds .regent/ + .claude/skills symlinks (atomic, idempotent)
41
+ regent doctor # checks executor (claude) and advisor (codex) CLIs
42
+ ```
43
+
44
+ Then open a Claude Code session in the project — `/regent` and `/regent-stop` are available
45
+ (`/regent brainstorm "<question>"` opens the first round). v0 capability is file-driven
46
+ (rounds under `.regent/brainstorm/rodadas/`); the advisor requires the `codex` CLI.
47
+
48
+ Development: `PYTHONPATH=src python3 -m unittest discover -s tests`. Canonical skill content
49
+ lives in `src/regent/templates/` (ships inside the wheel); the repo's own `.regent/skills/`
50
+ symlinks into it (dogfood without duplication).
51
+
52
+ MIT License © 2026 Flavio Alvim.
@@ -0,0 +1,36 @@
1
+ # regent
2
+
3
+ Autonomous conduction and mediated adversarial deliberation between AI agents, pluggable
4
+ into any project.
5
+
6
+ **regent** governs turns between agents (Claude, Codex, human mediators) under a frozen
7
+ protocol: atomic turn mutex, CAS-versioned state, deliberation rounds with versioned
8
+ acceptances, and a conduction daemon that executes production batches with a confined agent,
9
+ test gates and evidence proof.
10
+
11
+ Extracted from the tool proven end-to-end in the ArtNFT project (IMP-003: first product
12
+ batch fully conducted by the daemon, deliberated, accepted and deployed to production).
13
+
14
+ - Requirements: [`docs/PRD.md`](docs/PRD.md)
15
+ - Extraction scope and decisions: [`docs/ESCOPO.md`](docs/ESCOPO.md) (PT-BR, pre-rename)
16
+ - Deliberation rounds: [`docs/brainstorm/`](docs/brainstorm/) (mediator's language, PT-BR)
17
+ - Status: **pre-extraction** (scope closed 2026-07-20; code not yet migrated)
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ pip install <path-to-this-repo> # package: regent-cli; CLI: regent (not on PyPI yet)
23
+ cd <your-project>
24
+ regent init # seeds .regent/ + .claude/skills symlinks (atomic, idempotent)
25
+ regent doctor # checks executor (claude) and advisor (codex) CLIs
26
+ ```
27
+
28
+ Then open a Claude Code session in the project — `/regent` and `/regent-stop` are available
29
+ (`/regent brainstorm "<question>"` opens the first round). v0 capability is file-driven
30
+ (rounds under `.regent/brainstorm/rodadas/`); the advisor requires the `codex` CLI.
31
+
32
+ Development: `PYTHONPATH=src python3 -m unittest discover -s tests`. Canonical skill content
33
+ lives in `src/regent/templates/` (ships inside the wheel); the repo's own `.regent/skills/`
34
+ symlinks into it (dogfood without duplication).
35
+
36
+ MIT License © 2026 Flavio Alvim.
@@ -0,0 +1,30 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "regent-cli"
7
+ version = "0.1.0"
8
+ description = "Autonomous conduction and mediated adversarial deliberation between AI agents, pluggable into any project."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Flavio Alvim", email = "flavioalvim@gmail.com" }]
13
+ classifiers = [
14
+ "License :: OSI Approved :: MIT License",
15
+ "Programming Language :: Python :: 3",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Developers",
18
+ ]
19
+
20
+ [project.urls]
21
+ Repository = "https://github.com/flavioalvim/regent"
22
+
23
+ [project.scripts]
24
+ regent = "regent.cli:entrypoint"
25
+
26
+ [tool.setuptools.packages.find]
27
+ where = ["src"]
28
+
29
+ [tool.setuptools.package-data]
30
+ regent = ["templates/**/*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """regent — autonomous conduction and mediated adversarial deliberation between AI agents."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,40 @@
1
+ """regent CLI entry point: init and doctor subcommands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ from . import __version__
10
+ from .doctor import run_doctor
11
+ from .initcmd import run_init
12
+
13
+
14
+ def main(argv: list[str] | None = None) -> int:
15
+ parser = argparse.ArgumentParser(
16
+ prog="regent",
17
+ description="Autonomous conduction and mediated adversarial deliberation "
18
+ "between AI agents, pluggable into any project.")
19
+ parser.add_argument("--version", action="version", version=f"regent {__version__}")
20
+ sub = parser.add_subparsers(dest="command", required=True)
21
+
22
+ p_init = sub.add_parser("init", help="seed .regent/ and managed integrations here")
23
+ p_init.add_argument("--project", default=".", help="host project root (default: cwd)")
24
+
25
+ p_doctor = sub.add_parser("doctor", help="diagnose agent CLI capabilities")
26
+ p_doctor.add_argument("--project", default=".", help="host project root (default: cwd)")
27
+
28
+ args = parser.parse_args(argv)
29
+ root = Path(args.project)
30
+ if args.command == "init":
31
+ return run_init(root)
32
+ return run_doctor(root)
33
+
34
+
35
+ def entrypoint() -> None:
36
+ sys.exit(main())
37
+
38
+
39
+ if __name__ == "__main__":
40
+ entrypoint()
@@ -0,0 +1,47 @@
1
+ """regent doctor — capability diagnostics via safe non-interactive probes.
2
+
3
+ Contract (PRD REQ-003 §6): exit 0 iff every capability is usable; structured
4
+ per-capability report. Probes never open interactive sessions.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import shutil
10
+ import subprocess
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ EXIT_OK = 0
15
+ EXIT_UNAVAILABLE = 1
16
+
17
+ CAPABILITIES = (
18
+ ("executor", "claude", ["claude", "--version"]),
19
+ ("advisor", "codex", ["codex", "--version"]),
20
+ )
21
+
22
+
23
+ def _default_probe(argv: list[str]) -> tuple[str, str]:
24
+ """Returns (status, detail): OK, MISSING or BROKEN."""
25
+ if shutil.which(argv[0]) is None:
26
+ return "MISSING", f"'{argv[0]}' not found on PATH"
27
+ try:
28
+ proc = subprocess.run(argv, capture_output=True, text=True, timeout=30)
29
+ except (subprocess.TimeoutExpired, OSError) as exc:
30
+ return "BROKEN", f"probe failed: {exc}"
31
+ if proc.returncode != 0:
32
+ return "BROKEN", f"exit {proc.returncode}: {proc.stderr.strip()[:200]}"
33
+ return "OK", proc.stdout.strip().splitlines()[0] if proc.stdout.strip() else "ok"
34
+
35
+
36
+ def run_doctor(project_root: Path, out=sys.stdout, probe=_default_probe) -> int:
37
+ all_ok = True
38
+ for role, _cli, argv in CAPABILITIES:
39
+ status, detail = probe(argv)
40
+ all_ok = all_ok and status == "OK"
41
+ print(f"{role:<9} {status:<8} {detail}", file=out)
42
+
43
+ initialized = (project_root / ".regent").is_dir()
44
+ print(f"project {'INITIALIZED' if initialized else 'NOT-INITIALIZED':<8} "
45
+ f"{project_root}", file=out)
46
+
47
+ return EXIT_OK if all_ok else EXIT_UNAVAILABLE
@@ -0,0 +1,135 @@
1
+ """regent init — seed .regent/ and managed integrations into a host project.
2
+
3
+ Contract (PRD REQ-003 §6, REQ-004): installation is atomic — exit 0 only on
4
+ complete seeding; any failure rolls back every path this run created, leaving
5
+ no partial state. A missing agent CLI is a warning, never an init failure.
6
+ Pre-existing content that diverges from what would be seeded is a conflict:
7
+ nothing is touched. Re-running over an identical installation is a no-op.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import shutil
13
+ import sys
14
+ from importlib import resources
15
+ from pathlib import Path
16
+
17
+ SKILL_NAMES = ("regent", "regent-stop")
18
+ AGENT_CLIS = ("claude", "codex")
19
+
20
+ EXIT_OK = 0
21
+ EXIT_CONFLICT = 2
22
+ EXIT_FAILURE = 1
23
+
24
+
25
+ class SeedConflict(Exception):
26
+ """Pre-existing host content diverges from what init would seed."""
27
+
28
+
29
+ def _template_skill(name: str) -> str:
30
+ ref = resources.files("regent").joinpath(f"templates/skills/{name}/SKILL.md")
31
+ return ref.read_text(encoding="utf-8")
32
+
33
+
34
+ def _plan(project_root: Path) -> list[tuple[str, Path, str]]:
35
+ """Returns [(kind, path, payload)] where kind is 'file' or 'symlink'.
36
+
37
+ payload = file content, or the symlink target (relative)."""
38
+ plan: list[tuple[str, Path, str]] = []
39
+ for name in SKILL_NAMES:
40
+ plan.append(("file", project_root / ".regent" / "skills" / name / "SKILL.md",
41
+ _template_skill(name)))
42
+ plan.append(("symlink", project_root / ".claude" / "skills" / name,
43
+ f"../../.regent/skills/{name}"))
44
+ plan.append(("file", project_root / ".regent" / "brainstorm" / "rodadas" / ".gitkeep", ""))
45
+ return plan
46
+
47
+
48
+ def _existing_state(kind: str, path: Path, payload: str) -> str:
49
+ """'absent' | 'identical' | 'divergent' for one planned entry."""
50
+ if kind == "symlink":
51
+ if not path.is_symlink():
52
+ return "absent" if not path.exists() else "divergent"
53
+ import os
54
+ return "identical" if os.readlink(path) == payload else "divergent"
55
+ if not path.exists():
56
+ return "absent"
57
+ if path.is_file() and path.read_text(encoding="utf-8") == payload:
58
+ return "identical"
59
+ return "divergent"
60
+
61
+
62
+ def run_init(project_root: Path, out=sys.stdout) -> int:
63
+ project_root = project_root.resolve()
64
+ try:
65
+ plan = _plan(project_root)
66
+ except (FileNotFoundError, ModuleNotFoundError) as exc:
67
+ print(f"error: packaged templates unavailable: {exc}", file=out)
68
+ return EXIT_FAILURE
69
+
70
+ states = {path: _existing_state(kind, path, payload) for kind, path, payload in plan}
71
+ divergent = [p for p, s in states.items() if s == "divergent"]
72
+ if divergent:
73
+ print("error: conflict — pre-existing content differs from what regent would seed:",
74
+ file=out)
75
+ for p in divergent:
76
+ print(f" {p.relative_to(project_root)}", file=out)
77
+ print("nothing was changed. Resolve the conflicts and re-run.", file=out)
78
+ return EXIT_CONFLICT
79
+
80
+ todo = [(kind, path, payload) for kind, path, payload in plan
81
+ if states[path] == "absent"]
82
+ if not todo:
83
+ print("already initialized — nothing to do.", file=out)
84
+ _warn_missing_clis(out)
85
+ return EXIT_OK
86
+
87
+ created: list[Path] = []
88
+ try:
89
+ for kind, path, payload in todo:
90
+ for parent in _missing_parents(path):
91
+ parent.mkdir()
92
+ created.append(parent)
93
+ if kind == "file":
94
+ path.write_text(payload, encoding="utf-8")
95
+ else:
96
+ path.symlink_to(payload)
97
+ created.append(path)
98
+ except OSError as exc:
99
+ _rollback(created)
100
+ print(f"error: seeding failed ({exc}); all changes rolled back.", file=out)
101
+ return EXIT_FAILURE
102
+
103
+ for kind, path, _ in todo:
104
+ print(f"seeded {path.relative_to(project_root)}"
105
+ + (" (symlink)" if kind == "symlink" else ""), file=out)
106
+ _warn_missing_clis(out)
107
+ print("regent initialized. Open a Claude Code session here and use /regent.", file=out)
108
+ return EXIT_OK
109
+
110
+
111
+ def _missing_parents(path: Path) -> list[Path]:
112
+ missing = []
113
+ for parent in path.parents:
114
+ if parent.exists():
115
+ break
116
+ missing.append(parent)
117
+ return list(reversed(missing))
118
+
119
+
120
+ def _rollback(created: list[Path]) -> None:
121
+ for path in reversed(created):
122
+ try:
123
+ if path.is_symlink() or path.is_file():
124
+ path.unlink()
125
+ elif path.is_dir():
126
+ path.rmdir()
127
+ except OSError:
128
+ pass # best effort; leftover paths are reported by the failure message
129
+
130
+
131
+ def _warn_missing_clis(out) -> None:
132
+ for cli in AGENT_CLIS:
133
+ if shutil.which(cli) is None:
134
+ print(f"warning: agent CLI '{cli}' not found on PATH "
135
+ f"(run 'regent doctor' for capability diagnostics)", file=out)
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: regent
3
+ description: Start or resume the regent activity in this repo (brainstorm round, planning, implementation). Single state-driven entry point — detects what is open and drives the next step. Use when the owner says /regent, "retome o brainstorm", "próxima rodada", or asks to continue regent work.
4
+ ---
5
+
6
+ # /regent — start or resume the current activity
7
+
8
+ > **Capability level: v0 (file-driven).** State detection is based on round files only.
9
+ > There is no `control.json`, no turn lock, no daemon, no atomic abort and no structured
10
+ > checkpoint yet — those arrive with REQ-004's full implementation. Do not claim
11
+ > capabilities beyond this file.
12
+
13
+ ## 0. Locate the rounds directory (deterministic, never guess)
14
+
15
+ - Host projects: `ROUNDS = .regent/brainstorm/rodadas/` (created on first use).
16
+ - The regent product repo itself keeps its own deliberation in `docs/brainstorm/rodadas/`
17
+ (legacy dogfood location).
18
+ - Resolution: if `.regent/brainstorm/` exists use it; else if `docs/brainstorm/` exists use
19
+ that; if BOTH exist, report the ambiguity and STOP; if neither, this is a fresh host —
20
+ `ROUNDS = .regent/brainstorm/rodadas/`.
21
+
22
+ ## 1. Detect state (never guess)
23
+
24
+ - Open round = a `ROUNDS/RODADA-NNN/` directory WITHOUT `DECISAO.md`.
25
+ - Suspended round = an open round containing `SUSPENSAO.md`.
26
+ - If the state is ambiguous or corrupted (two open rounds, missing PERGUNTA.md), REPORT the
27
+ anomaly and STOP. Default-deny: never repair silently, never pick one arbitrarily.
28
+
29
+ ## 2. Decide by command argument × state
30
+
31
+ - **No argument + exactly one open/suspended round** → resume it (step 3).
32
+ - **No argument + nothing open** → report the state (last closed round, PRD requirement log)
33
+ and ask what to start. Never start anything implicitly.
34
+ - **`brainstorm "<question>"` + nothing open** → create the next `ROUNDS/RODADA-NNN/` with
35
+ `PERGUNTA.md` (owner's question verbatim) and proceed to step 3.
36
+ - **`brainstorm ...` + a DIFFERENT round already open** → error explaining the open state.
37
+ Never ignore the argument silently; never create a second activity.
38
+ - **`plan` / `build`** → not available at v0; say so explicitly and point to REQ-004.
39
+
40
+ ## 3. Drive the round (the loop protocol is THIS section — self-contained)
41
+
42
+ Loop: owner asks → Claude responds → Codex (advisor) opines with an explicit
43
+ CONCORDA/DISCORDA verdict → if CONCORDA, Claude executes; if DISCORDA, one rebuttal cycle;
44
+ persistent divergence → the owner arbitrates. Round language: the mediator's (REQ-002).
45
+
46
+ Resume at the first missing artifact, in order:
47
+ 1. `RESPOSTA-CLAUDE.md` — Claude's reasoned position + concrete proposal.
48
+ 2. `OPINIAO-CODEX-1.md` — headless advisor consultation, saved verbatim:
49
+ `codex --ask-for-approval never --sandbox read-only exec --cd <repo> -o <tmpfile> "<prompt>"`
50
+ (prompt tells Codex which files to read and to end with a line containing only
51
+ CONCORDA or DISCORDA).
52
+ 3. If DISCORDA → `REPLICA-CLAUDE.md` then `OPINIAO-CODEX-2.md` (one rebuttal cycle max).
53
+ 4. Persistent divergence → the owner arbitrates (they are mediating live).
54
+ 5. `DECISAO.md` — what was decided, who closed it, what was executed. Then execute it and
55
+ commit ONLY regent-owned paths (`.regent/`, the `.claude/` symlink integrations, and —
56
+ in the regent repo only — `docs/`) — never unrelated host content. A failed commit does
57
+ not undo the round; report it and leave it pending.
58
+
59
+ If resuming a suspended round, read `SUSPENSAO.md` first, delete it after resuming, and
60
+ continue from the recorded pending step.
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: regent-stop
3
+ description: Safely stop/suspend the regent activity in progress (brainstorm round, planning, implementation) so it can be resumed later with /regent. Use when the owner says /regent-stop, "para o brainstorm", "suspende a rodada", or asks to stop regent work.
4
+ ---
5
+
6
+ # /regent-stop — safe stop of the current activity
7
+
8
+ > **Capability level: v0 (file-driven).** There is no daemon, lock or in-flight abort yet:
9
+ > stopping means recording a durable suspension marker at the next message boundary. Do not
10
+ > claim atomic cancellation — immediate interruption of a running step is the user's Esc,
11
+ > not this command.
12
+
13
+ ## Steps
14
+
15
+ 1. Locate the rounds directory exactly as `/regent` does (step 0 of its skill: host =
16
+ `.regent/brainstorm/rodadas/`; the regent repo itself = `docs/brainstorm/rodadas/`; both
17
+ present = ambiguity, stop). Then detect the open round: `RODADA-NNN/` without `DECISAO.md`.
18
+ - Nothing open → report that the state is clean (nothing to stop) and stop.
19
+ - Ambiguous state → report the anomaly; do not modify anything.
20
+ 2. Write `SUSPENSAO.md` in the open round with: timestamp, reason given by the owner (ask if
21
+ not stated), which artifact/step was pending (the resume checkpoint), and any partial
22
+ output worth keeping (e.g. an advisor consultation already saved).
23
+ 3. Persist everything already produced — never delete partial artifacts; they are evidence.
24
+ 4. Commit ONLY regent-owned paths (`.regent/`, the `.claude/` symlink integrations, and —
25
+ in the regent repo only — `docs/`). A failed commit does NOT prevent the suspension —
26
+ report it and leave the commit pending.
27
+ 5. Confirm to the owner: what was suspended, where it will resume from (`/regent`).
@@ -0,0 +1,52 @@
1
+ Metadata-Version: 2.4
2
+ Name: regent-cli
3
+ Version: 0.1.0
4
+ Summary: Autonomous conduction and mediated adversarial deliberation between AI agents, pluggable into any project.
5
+ Author-email: Flavio Alvim <flavioalvim@gmail.com>
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/flavioalvim/regent
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Dynamic: license-file
16
+
17
+ # regent
18
+
19
+ Autonomous conduction and mediated adversarial deliberation between AI agents, pluggable
20
+ into any project.
21
+
22
+ **regent** governs turns between agents (Claude, Codex, human mediators) under a frozen
23
+ protocol: atomic turn mutex, CAS-versioned state, deliberation rounds with versioned
24
+ acceptances, and a conduction daemon that executes production batches with a confined agent,
25
+ test gates and evidence proof.
26
+
27
+ Extracted from the tool proven end-to-end in the ArtNFT project (IMP-003: first product
28
+ batch fully conducted by the daemon, deliberated, accepted and deployed to production).
29
+
30
+ - Requirements: [`docs/PRD.md`](docs/PRD.md)
31
+ - Extraction scope and decisions: [`docs/ESCOPO.md`](docs/ESCOPO.md) (PT-BR, pre-rename)
32
+ - Deliberation rounds: [`docs/brainstorm/`](docs/brainstorm/) (mediator's language, PT-BR)
33
+ - Status: **pre-extraction** (scope closed 2026-07-20; code not yet migrated)
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ pip install <path-to-this-repo> # package: regent-cli; CLI: regent (not on PyPI yet)
39
+ cd <your-project>
40
+ regent init # seeds .regent/ + .claude/skills symlinks (atomic, idempotent)
41
+ regent doctor # checks executor (claude) and advisor (codex) CLIs
42
+ ```
43
+
44
+ Then open a Claude Code session in the project — `/regent` and `/regent-stop` are available
45
+ (`/regent brainstorm "<question>"` opens the first round). v0 capability is file-driven
46
+ (rounds under `.regent/brainstorm/rodadas/`); the advisor requires the `codex` CLI.
47
+
48
+ Development: `PYTHONPATH=src python3 -m unittest discover -s tests`. Canonical skill content
49
+ lives in `src/regent/templates/` (ships inside the wheel); the repo's own `.regent/skills/`
50
+ symlinks into it (dogfood without duplication).
51
+
52
+ MIT License © 2026 Flavio Alvim.
@@ -0,0 +1,16 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/regent/__init__.py
5
+ src/regent/cli.py
6
+ src/regent/doctor.py
7
+ src/regent/initcmd.py
8
+ src/regent/templates/skills/regent/SKILL.md
9
+ src/regent/templates/skills/regent-stop/SKILL.md
10
+ src/regent_cli.egg-info/PKG-INFO
11
+ src/regent_cli.egg-info/SOURCES.txt
12
+ src/regent_cli.egg-info/dependency_links.txt
13
+ src/regent_cli.egg-info/entry_points.txt
14
+ src/regent_cli.egg-info/top_level.txt
15
+ tests/test_doctor.py
16
+ tests/test_initcmd.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ regent = regent.cli:entrypoint
@@ -0,0 +1,52 @@
1
+ """Tests for regent doctor: exit codes and per-capability report."""
2
+
3
+ import io
4
+ import tempfile
5
+ import unittest
6
+ from pathlib import Path
7
+
8
+ from regent.doctor import EXIT_OK, EXIT_UNAVAILABLE, run_doctor
9
+
10
+
11
+ def probe_all_ok(argv):
12
+ return "OK", f"{argv[0]} 1.0.0"
13
+
14
+
15
+ def probe_advisor_missing(argv):
16
+ if argv[0] == "codex":
17
+ return "MISSING", "'codex' not found on PATH"
18
+ return "OK", "claude 1.0.0"
19
+
20
+
21
+ class DoctorTest(unittest.TestCase):
22
+ def setUp(self):
23
+ self._tmp = tempfile.TemporaryDirectory()
24
+ self.root = Path(self._tmp.name)
25
+ self.addCleanup(self._tmp.cleanup)
26
+
27
+ def _doctor(self, probe):
28
+ out = io.StringIO()
29
+ code = run_doctor(self.root, out=out, probe=probe)
30
+ return code, out.getvalue()
31
+
32
+ def test_all_capabilities_ok(self):
33
+ code, output = self._doctor(probe_all_ok)
34
+ self.assertEqual(code, EXIT_OK)
35
+ self.assertIn("executor", output)
36
+ self.assertIn("advisor", output)
37
+ self.assertIn("NOT-INITIALIZED", output)
38
+
39
+ def test_missing_capability_fails(self):
40
+ code, output = self._doctor(probe_advisor_missing)
41
+ self.assertEqual(code, EXIT_UNAVAILABLE)
42
+ self.assertIn("MISSING", output)
43
+
44
+ def test_initialized_project_is_reported(self):
45
+ (self.root / ".regent").mkdir()
46
+ code, output = self._doctor(probe_all_ok)
47
+ self.assertEqual(code, EXIT_OK)
48
+ self.assertIn("INITIALIZED", output)
49
+
50
+
51
+ if __name__ == "__main__":
52
+ unittest.main()
@@ -0,0 +1,72 @@
1
+ """Tests for regent init: seeding, idempotency, conflict detection, rollback."""
2
+
3
+ import io
4
+ import os
5
+ import tempfile
6
+ import unittest
7
+ from pathlib import Path
8
+
9
+ from regent.initcmd import EXIT_CONFLICT, EXIT_FAILURE, EXIT_OK, run_init
10
+
11
+
12
+ class InitTest(unittest.TestCase):
13
+ def setUp(self):
14
+ self._tmp = tempfile.TemporaryDirectory()
15
+ self.root = Path(self._tmp.name)
16
+ self.addCleanup(self._tmp.cleanup)
17
+
18
+ def _init(self):
19
+ out = io.StringIO()
20
+ code = run_init(self.root, out=out)
21
+ return code, out.getvalue()
22
+
23
+ def test_fresh_project_seeds_everything(self):
24
+ code, output = self._init()
25
+ self.assertEqual(code, EXIT_OK, output)
26
+ for name in ("regent", "regent-stop"):
27
+ skill = self.root / ".regent" / "skills" / name / "SKILL.md"
28
+ self.assertTrue(skill.is_file())
29
+ self.assertIn(f"name: {name}", skill.read_text())
30
+ link = self.root / ".claude" / "skills" / name
31
+ self.assertTrue(link.is_symlink())
32
+ self.assertEqual(os.readlink(link), f"../../.regent/skills/{name}")
33
+ self.assertTrue((link / "SKILL.md").is_file()) # symlink resolves
34
+ self.assertTrue((self.root / ".regent" / "brainstorm" / "rodadas").is_dir())
35
+
36
+ def test_rerun_is_noop(self):
37
+ self.assertEqual(self._init()[0], EXIT_OK)
38
+ code, output = self._init()
39
+ self.assertEqual(code, EXIT_OK)
40
+ self.assertIn("already initialized", output)
41
+
42
+ def test_divergent_content_is_conflict_and_untouched(self):
43
+ skill = self.root / ".regent" / "skills" / "regent" / "SKILL.md"
44
+ skill.parent.mkdir(parents=True)
45
+ skill.write_text("host content that differs")
46
+ code, output = self._init()
47
+ self.assertEqual(code, EXIT_CONFLICT)
48
+ self.assertIn("conflict", output)
49
+ self.assertEqual(skill.read_text(), "host content that differs")
50
+ self.assertFalse((self.root / ".claude").exists()) # nothing else was created
51
+
52
+ def test_failure_rolls_back_everything(self):
53
+ # A FILE at .claude/skills makes the symlink parent creation fail mid-run.
54
+ (self.root / ".claude").mkdir()
55
+ (self.root / ".claude" / "skills").write_text("not a directory")
56
+ code, output = self._init()
57
+ self.assertEqual(code, EXIT_FAILURE)
58
+ self.assertIn("rolled back", output)
59
+ self.assertFalse((self.root / ".regent").exists())
60
+
61
+ def test_missing_seed_completes_partial_prior_state(self):
62
+ # An identical prior artifact is kept; only the absent ones are seeded.
63
+ self.assertEqual(self._init()[0], EXIT_OK)
64
+ link = self.root / ".claude" / "skills" / "regent"
65
+ link.unlink()
66
+ code, output = self._init()
67
+ self.assertEqual(code, EXIT_OK)
68
+ self.assertTrue(link.is_symlink())
69
+
70
+
71
+ if __name__ == "__main__":
72
+ unittest.main()