paircode 0.11.1__tar.gz → 0.11.5__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.
- {paircode-0.11.1 → paircode-0.11.5}/.gitignore +3 -0
- paircode-0.11.5/PKG-INFO +154 -0
- paircode-0.11.5/README.md +126 -0
- {paircode-0.11.1 → paircode-0.11.5}/pyproject.toml +2 -2
- {paircode-0.11.1 → paircode-0.11.5}/src/paircode/__init__.py +1 -1
- {paircode-0.11.1 → paircode-0.11.5}/src/paircode/cli.py +1 -1
- paircode-0.11.1/src/paircode/seal.py → paircode-0.11.5/src/paircode/converge.py +8 -4
- {paircode-0.11.1 → paircode-0.11.5}/src/paircode/installer.py +1 -10
- {paircode-0.11.1 → paircode-0.11.5}/src/paircode/runner.py +7 -1
- {paircode-0.11.1 → paircode-0.11.5}/src/paircode/state.py +15 -23
- {paircode-0.11.1 → paircode-0.11.5}/src/paircode/templates/claude/commands/paircode.md +31 -30
- {paircode-0.11.1 → paircode-0.11.5}/src/paircode/templates/codex/commands/paircode.md +3 -3
- {paircode-0.11.1 → paircode-0.11.5}/src/paircode/templates/gemini/commands/paircode.toml +3 -3
- {paircode-0.11.1 → paircode-0.11.5}/src/paircode/templates/peers.yaml +1 -1
- paircode-0.11.5/src/paircode/util.py +18 -0
- paircode-0.11.1/tests/test_seal.py → paircode-0.11.5/tests/test_converge.py +2 -2
- {paircode-0.11.1 → paircode-0.11.5}/tests/test_state.py +8 -8
- paircode-0.11.1/PKG-INFO +0 -160
- paircode-0.11.1/README.md +0 -132
- paircode-0.11.1/src/paircode/journey.py +0 -63
- {paircode-0.11.1 → paircode-0.11.5}/LICENSE +0 -0
- {paircode-0.11.1 → paircode-0.11.5}/diary/001-step-a-architecture.md +0 -0
- {paircode-0.11.1 → paircode-0.11.5}/diary/002-v0.10-release-pipeline.md +0 -0
- {paircode-0.11.1 → paircode-0.11.5}/diary/003-arch-b-pivot-grappling.md +0 -0
- {paircode-0.11.1 → paircode-0.11.5}/src/paircode/__main__.py +0 -0
- {paircode-0.11.1 → paircode-0.11.5}/src/paircode/detect.py +0 -0
- {paircode-0.11.1 → paircode-0.11.5}/src/paircode/handshake.py +0 -0
- {paircode-0.11.1 → paircode-0.11.5}/src/paircode/templates/FOCUS.md +0 -0
- {paircode-0.11.1 → paircode-0.11.5}/src/paircode/templates/JOURNEY.md +0 -0
- {paircode-0.11.1 → paircode-0.11.5}/tests/__init__.py +0 -0
- {paircode-0.11.1 → paircode-0.11.5}/tests/test_cli_smoke.py +0 -0
- {paircode-0.11.1 → paircode-0.11.5}/tests/test_smoke.py +0 -0
paircode-0.11.5/PKG-INFO
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: paircode
|
|
3
|
+
Version: 0.11.5
|
|
4
|
+
Summary: Adversarial journey framework — orchestrate multiple LLM peers through research, plan, and execute stages with file-trace peer review.
|
|
5
|
+
Project-URL: Homepage, https://github.com/starshipagentic/paircode
|
|
6
|
+
Project-URL: Repository, https://github.com/starshipagentic/paircode
|
|
7
|
+
Project-URL: Issues, https://github.com/starshipagentic/paircode/issues
|
|
8
|
+
Author-email: Travis Somerville <travis.somerville@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,claude,codex,gemini,llm,multi-agent,ollama,orchestration,peer-review
|
|
12
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: click>=8.1
|
|
24
|
+
Requires-Dist: cliworker>=0.8.3
|
|
25
|
+
Requires-Dist: pyyaml>=6.0
|
|
26
|
+
Requires-Dist: rich>=13.0
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# paircode
|
|
30
|
+
|
|
31
|
+
**Multi-LLM peer review for your code, with file-traces on disk.** One primary LLM (alpha, usually Claude Code) + any number of peer LLMs (Codex, Gemini, Ollama, …) running research / plan / execute / ask cycles with structured cross-review rounds stored entirely as Markdown on disk.
|
|
32
|
+
|
|
33
|
+
> Born from 31 iterations of dual-LLM silent-agreement hunting on a real ML project. See `diary/001-step-a-architecture.md` for the origin story and `diary/003-arch-b-pivot-grappling.md` for the current (v0.11) team-lead architecture.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pipx install paircode # or: pip install --user paircode
|
|
39
|
+
paircode install # registers /paircode in every detected LLM CLI
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`paircode install` deploys `/paircode` into each LLM CLI it finds on your PATH:
|
|
43
|
+
|
|
44
|
+
| CLI | How it's installed | What you get |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| Claude Code | file-drop `~/.claude/commands/paircode.md` | `/paircode` slash command |
|
|
47
|
+
| Codex CLI | `codex marketplace add starshipagentic/paircode-codex` | `/paircode` slash command |
|
|
48
|
+
| Gemini CLI | `gemini extensions install github.com/starshipagentic/paircode-gemini --consent` | `/paircode` slash command |
|
|
49
|
+
|
|
50
|
+
In Gemini you may need `/commands reload` the first time. In Codex the marketplace fetches on first use.
|
|
51
|
+
|
|
52
|
+
paircode delegates LLM subprocess invocation to [`cliworker`](https://pypi.org/project/cliworker/) — that's where speed flags, MCP strip tricks, skip-cache, and subscription-first fallback live. paircode adds the peer-review orchestration on top (file-traces, stages, rosters, convergence).
|
|
53
|
+
|
|
54
|
+
## Use it
|
|
55
|
+
|
|
56
|
+
Inside any LLM CLI that has `/paircode` installed, just type:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
/paircode "build a KISS PHQ-9 depression risk engine"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
or
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
/paircode "review my auth middleware approach at src/auth/"
|
|
66
|
+
/paircode "plan the refactor" --peers codex,gemini
|
|
67
|
+
/paircode "get a second opinion on this PR" --peer gemini
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The slash command's team-lead prompt (the LLM you're inside) reads your prompt, picks a stage (`research | plan | execute | ask`), fires peers, collects their markdown, iterates through review rounds until convergence, and writes `consensus.md` at the end. Everything lands under `.paircode/` in your current project.
|
|
71
|
+
|
|
72
|
+
## What ends up on disk
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
your-project/
|
|
76
|
+
.paircode/
|
|
77
|
+
JOURNEY.md # fleet log
|
|
78
|
+
peers.yaml # roster: who's on the team
|
|
79
|
+
sandbox/
|
|
80
|
+
peer-a-codex/ # codex's persistent sandbox (code goes here)
|
|
81
|
+
peer-b-gemini/ # gemini's persistent sandbox
|
|
82
|
+
focus-01-<slug>/
|
|
83
|
+
FOCUS.md # this focus's prompt + metadata
|
|
84
|
+
research/
|
|
85
|
+
alpha-v1.md ... alpha-vN.md
|
|
86
|
+
peer-a-codex-v1.md ...
|
|
87
|
+
reviews/round-01-peer-a-codex-critiques-alpha.md
|
|
88
|
+
alpha-FINAL.md
|
|
89
|
+
peer-a-codex-FINAL.md
|
|
90
|
+
consensus.md # team-lead synthesis (last thing written)
|
|
91
|
+
plan/ (same shape)
|
|
92
|
+
execute/ (same shape)
|
|
93
|
+
ask/ (same shape)
|
|
94
|
+
focus-02-<slug>/
|
|
95
|
+
...
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Code vs. reports.** Files inside `focus-*/` are markdown *reports* (opinions, plans, critiques, summaries of work). Actual code lives elsewhere:
|
|
99
|
+
|
|
100
|
+
- **Peers** code in their sandboxed workspaces at `.paircode/sandbox/<peer-id>/` — persistent across focuses.
|
|
101
|
+
- **Alpha** codes directly in the project root (the real repo) — alpha *is* the project.
|
|
102
|
+
|
|
103
|
+
## Stages
|
|
104
|
+
|
|
105
|
+
Stage is picked by the team-lead LLM based on the prompt:
|
|
106
|
+
|
|
107
|
+
| Stage | When | Typical prompt |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| `research` | Explore new ground | "build X", "find the right Y", "how should we approach Z" |
|
|
110
|
+
| `plan` | Concrete implementation plan from prior research | "plan the refactor based on focus-02" |
|
|
111
|
+
| `execute` | Do the work from an existing plan | "execute the plan at focus-02" |
|
|
112
|
+
| `ask` | Get opinions on existing work | "what does codex think of this PR", "review my approach at <path>" |
|
|
113
|
+
|
|
114
|
+
The team lead can chain stages in one invocation: `research → plan → execute → done`. No hard round cap — the team lead converges when peers stop surfacing new signal.
|
|
115
|
+
|
|
116
|
+
## Commands (the helper CLI)
|
|
117
|
+
|
|
118
|
+
Most users will only ever type `paircode install` and then use `/paircode` inside their LLM CLI. The binary's other commands are helpers the team-lead slash command calls on your behalf:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
paircode print current .paircode/ state
|
|
122
|
+
paircode install register /paircode in every detected LLM CLI
|
|
123
|
+
paircode uninstall remove /paircode from LLM CLIs (idempotent)
|
|
124
|
+
paircode ensure-scaffold idempotent .paircode/ init + handshake (silent)
|
|
125
|
+
paircode focus new <slug> create a new focus dir, print its path
|
|
126
|
+
paircode focus active print the active focus path
|
|
127
|
+
paircode roster [--alpha <cli>] [--peer <id>] [--peers <id,id>]
|
|
128
|
+
print peer ids, best-effort, never errors
|
|
129
|
+
paircode invoke <peer-id> "<prompt>" --out <path>
|
|
130
|
+
fire one peer, write file-trace to --out
|
|
131
|
+
paircode converge <stage> copy each participant's latest vN to *-FINAL.md
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Model compatibility
|
|
135
|
+
|
|
136
|
+
| CLI | `/paircode` slash command | Peer invocation | Parallel peers? | Status |
|
|
137
|
+
|---|---|---|---|---|
|
|
138
|
+
| Claude Code (`claude`) | ✓ file-drop at `~/.claude/commands/paircode.md` | ✓ `claude -p <prompt>` | ✓ via Agent tool + `run_in_background=true` | stable |
|
|
139
|
+
| Codex (`codex`) | ✓ via `codex marketplace add` | ✓ `codex exec <prompt>` | ✗ serial-only (codex constraint) | stable |
|
|
140
|
+
| Gemini (`gemini`) | ✓ via `gemini extensions install` | ✓ `gemini -p <prompt>` | ✗ serial-only (gemini constraint) | stable |
|
|
141
|
+
| Ollama (`ollama`) | — (local models, no slash-cmd host) | ✓ `ollama run <model> <prompt>` | n/a | peer-only |
|
|
142
|
+
| Aider / others | — | best-effort, PRs welcome | — | planned |
|
|
143
|
+
|
|
144
|
+
Peer roster is auto-detected at first install via `paircode handshake` (silent, called by `ensure-scaffold`). Edit `.paircode/peers.yaml` to customize.
|
|
145
|
+
|
|
146
|
+
## Why this exists
|
|
147
|
+
|
|
148
|
+
See [`diary/001-step-a-architecture.md`](diary/001-step-a-architecture.md). Short version: running two LLMs adversarially surfaces silent-agreement bug classes that neither engine alone catches — cross-engine agreement is not the same as correctness when both engines share a blind spot.
|
|
149
|
+
|
|
150
|
+
See [`diary/003-arch-b-pivot-grappling.md`](diary/003-arch-b-pivot-grappling.md) for why v0.11 moved orchestration out of a Python driver and into the slash-command's team-lead LLM.
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# paircode
|
|
2
|
+
|
|
3
|
+
**Multi-LLM peer review for your code, with file-traces on disk.** One primary LLM (alpha, usually Claude Code) + any number of peer LLMs (Codex, Gemini, Ollama, …) running research / plan / execute / ask cycles with structured cross-review rounds stored entirely as Markdown on disk.
|
|
4
|
+
|
|
5
|
+
> Born from 31 iterations of dual-LLM silent-agreement hunting on a real ML project. See `diary/001-step-a-architecture.md` for the origin story and `diary/003-arch-b-pivot-grappling.md` for the current (v0.11) team-lead architecture.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pipx install paircode # or: pip install --user paircode
|
|
11
|
+
paircode install # registers /paircode in every detected LLM CLI
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`paircode install` deploys `/paircode` into each LLM CLI it finds on your PATH:
|
|
15
|
+
|
|
16
|
+
| CLI | How it's installed | What you get |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| Claude Code | file-drop `~/.claude/commands/paircode.md` | `/paircode` slash command |
|
|
19
|
+
| Codex CLI | `codex marketplace add starshipagentic/paircode-codex` | `/paircode` slash command |
|
|
20
|
+
| Gemini CLI | `gemini extensions install github.com/starshipagentic/paircode-gemini --consent` | `/paircode` slash command |
|
|
21
|
+
|
|
22
|
+
In Gemini you may need `/commands reload` the first time. In Codex the marketplace fetches on first use.
|
|
23
|
+
|
|
24
|
+
paircode delegates LLM subprocess invocation to [`cliworker`](https://pypi.org/project/cliworker/) — that's where speed flags, MCP strip tricks, skip-cache, and subscription-first fallback live. paircode adds the peer-review orchestration on top (file-traces, stages, rosters, convergence).
|
|
25
|
+
|
|
26
|
+
## Use it
|
|
27
|
+
|
|
28
|
+
Inside any LLM CLI that has `/paircode` installed, just type:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
/paircode "build a KISS PHQ-9 depression risk engine"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
or
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
/paircode "review my auth middleware approach at src/auth/"
|
|
38
|
+
/paircode "plan the refactor" --peers codex,gemini
|
|
39
|
+
/paircode "get a second opinion on this PR" --peer gemini
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The slash command's team-lead prompt (the LLM you're inside) reads your prompt, picks a stage (`research | plan | execute | ask`), fires peers, collects their markdown, iterates through review rounds until convergence, and writes `consensus.md` at the end. Everything lands under `.paircode/` in your current project.
|
|
43
|
+
|
|
44
|
+
## What ends up on disk
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
your-project/
|
|
48
|
+
.paircode/
|
|
49
|
+
JOURNEY.md # fleet log
|
|
50
|
+
peers.yaml # roster: who's on the team
|
|
51
|
+
sandbox/
|
|
52
|
+
peer-a-codex/ # codex's persistent sandbox (code goes here)
|
|
53
|
+
peer-b-gemini/ # gemini's persistent sandbox
|
|
54
|
+
focus-01-<slug>/
|
|
55
|
+
FOCUS.md # this focus's prompt + metadata
|
|
56
|
+
research/
|
|
57
|
+
alpha-v1.md ... alpha-vN.md
|
|
58
|
+
peer-a-codex-v1.md ...
|
|
59
|
+
reviews/round-01-peer-a-codex-critiques-alpha.md
|
|
60
|
+
alpha-FINAL.md
|
|
61
|
+
peer-a-codex-FINAL.md
|
|
62
|
+
consensus.md # team-lead synthesis (last thing written)
|
|
63
|
+
plan/ (same shape)
|
|
64
|
+
execute/ (same shape)
|
|
65
|
+
ask/ (same shape)
|
|
66
|
+
focus-02-<slug>/
|
|
67
|
+
...
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Code vs. reports.** Files inside `focus-*/` are markdown *reports* (opinions, plans, critiques, summaries of work). Actual code lives elsewhere:
|
|
71
|
+
|
|
72
|
+
- **Peers** code in their sandboxed workspaces at `.paircode/sandbox/<peer-id>/` — persistent across focuses.
|
|
73
|
+
- **Alpha** codes directly in the project root (the real repo) — alpha *is* the project.
|
|
74
|
+
|
|
75
|
+
## Stages
|
|
76
|
+
|
|
77
|
+
Stage is picked by the team-lead LLM based on the prompt:
|
|
78
|
+
|
|
79
|
+
| Stage | When | Typical prompt |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `research` | Explore new ground | "build X", "find the right Y", "how should we approach Z" |
|
|
82
|
+
| `plan` | Concrete implementation plan from prior research | "plan the refactor based on focus-02" |
|
|
83
|
+
| `execute` | Do the work from an existing plan | "execute the plan at focus-02" |
|
|
84
|
+
| `ask` | Get opinions on existing work | "what does codex think of this PR", "review my approach at <path>" |
|
|
85
|
+
|
|
86
|
+
The team lead can chain stages in one invocation: `research → plan → execute → done`. No hard round cap — the team lead converges when peers stop surfacing new signal.
|
|
87
|
+
|
|
88
|
+
## Commands (the helper CLI)
|
|
89
|
+
|
|
90
|
+
Most users will only ever type `paircode install` and then use `/paircode` inside their LLM CLI. The binary's other commands are helpers the team-lead slash command calls on your behalf:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
paircode print current .paircode/ state
|
|
94
|
+
paircode install register /paircode in every detected LLM CLI
|
|
95
|
+
paircode uninstall remove /paircode from LLM CLIs (idempotent)
|
|
96
|
+
paircode ensure-scaffold idempotent .paircode/ init + handshake (silent)
|
|
97
|
+
paircode focus new <slug> create a new focus dir, print its path
|
|
98
|
+
paircode focus active print the active focus path
|
|
99
|
+
paircode roster [--alpha <cli>] [--peer <id>] [--peers <id,id>]
|
|
100
|
+
print peer ids, best-effort, never errors
|
|
101
|
+
paircode invoke <peer-id> "<prompt>" --out <path>
|
|
102
|
+
fire one peer, write file-trace to --out
|
|
103
|
+
paircode converge <stage> copy each participant's latest vN to *-FINAL.md
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Model compatibility
|
|
107
|
+
|
|
108
|
+
| CLI | `/paircode` slash command | Peer invocation | Parallel peers? | Status |
|
|
109
|
+
|---|---|---|---|---|
|
|
110
|
+
| Claude Code (`claude`) | ✓ file-drop at `~/.claude/commands/paircode.md` | ✓ `claude -p <prompt>` | ✓ via Agent tool + `run_in_background=true` | stable |
|
|
111
|
+
| Codex (`codex`) | ✓ via `codex marketplace add` | ✓ `codex exec <prompt>` | ✗ serial-only (codex constraint) | stable |
|
|
112
|
+
| Gemini (`gemini`) | ✓ via `gemini extensions install` | ✓ `gemini -p <prompt>` | ✗ serial-only (gemini constraint) | stable |
|
|
113
|
+
| Ollama (`ollama`) | — (local models, no slash-cmd host) | ✓ `ollama run <model> <prompt>` | n/a | peer-only |
|
|
114
|
+
| Aider / others | — | best-effort, PRs welcome | — | planned |
|
|
115
|
+
|
|
116
|
+
Peer roster is auto-detected at first install via `paircode handshake` (silent, called by `ensure-scaffold`). Edit `.paircode/peers.yaml` to customize.
|
|
117
|
+
|
|
118
|
+
## Why this exists
|
|
119
|
+
|
|
120
|
+
See [`diary/001-step-a-architecture.md`](diary/001-step-a-architecture.md). Short version: running two LLMs adversarially surfaces silent-agreement bug classes that neither engine alone catches — cross-engine agreement is not the same as correctness when both engines share a blind spot.
|
|
121
|
+
|
|
122
|
+
See [`diary/003-arch-b-pivot-grappling.md`](diary/003-arch-b-pivot-grappling.md) for why v0.11 moved orchestration out of a Python driver and into the slash-command's team-lead LLM.
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "paircode"
|
|
7
|
-
version = "0.11.
|
|
7
|
+
version = "0.11.5"
|
|
8
8
|
description = "Adversarial journey framework — orchestrate multiple LLM peers through research, plan, and execute stages with file-trace peer review."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -39,7 +39,7 @@ dependencies = [
|
|
|
39
39
|
"click>=8.1",
|
|
40
40
|
"pyyaml>=6.0",
|
|
41
41
|
"rich>=13.0",
|
|
42
|
-
"cliworker>=0.8.
|
|
42
|
+
"cliworker>=0.8.3",
|
|
43
43
|
]
|
|
44
44
|
|
|
45
45
|
[project.urls]
|
|
@@ -243,7 +243,7 @@ def converge(stage: str) -> None:
|
|
|
243
243
|
Runs on the active focus. Mechanical file-copy only; the slash command
|
|
244
244
|
writes `consensus.md` afterward from the FINAL files.
|
|
245
245
|
"""
|
|
246
|
-
from paircode.
|
|
246
|
+
from paircode.converge import seal_stage
|
|
247
247
|
|
|
248
248
|
state = find_paircode()
|
|
249
249
|
if state is None or state.active_focus is None:
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Converge a stage — copy the latest version per peer to {peer}-FINAL.md.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
each peer's *-FINAL.md exist, the stage is considered complete and the
|
|
5
|
-
stage can consume its outputs.
|
|
3
|
+
Convergence is the declarative exit signal for a stage. Once alpha-FINAL.md
|
|
4
|
+
and each peer's *-FINAL.md exist, the stage is considered complete and the
|
|
5
|
+
next stage can consume its outputs.
|
|
6
|
+
|
|
7
|
+
Renamed from `seal.py` in v0.11.1 to match the user-facing CLI verb
|
|
8
|
+
(`paircode converge <stage>`). The public function name stayed `seal_stage`
|
|
9
|
+
because it describes a file-copy primitive, not the user-level action.
|
|
6
10
|
"""
|
|
7
11
|
from __future__ import annotations
|
|
8
12
|
|
|
@@ -22,12 +22,12 @@ Also cleans up legacy files from pre-v0.10 paircode versions on every install.
|
|
|
22
22
|
from __future__ import annotations
|
|
23
23
|
|
|
24
24
|
from dataclasses import dataclass
|
|
25
|
-
from importlib import resources
|
|
26
25
|
from pathlib import Path
|
|
27
26
|
|
|
28
27
|
from cliworker import invoke
|
|
29
28
|
|
|
30
29
|
from paircode.detect import detect_all, CliInfo
|
|
30
|
+
from paircode.util import read_template as _read_template
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
# Satellite repo coordinates — update here when repos move.
|
|
@@ -46,15 +46,6 @@ class InstallResult:
|
|
|
46
46
|
message: str
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
def _read_template(relative_path: str) -> str:
|
|
50
|
-
"""Read a file under `paircode/templates/` by relative path.
|
|
51
|
-
|
|
52
|
-
Host templates live at `<host>/commands/...`, host-agnostic scaffold
|
|
53
|
-
templates at the root (`FOCUS.md`, `JOURNEY.md`, `peers.yaml`).
|
|
54
|
-
"""
|
|
55
|
-
return resources.files("paircode.templates").joinpath(relative_path).read_text(encoding="utf-8")
|
|
56
|
-
|
|
57
|
-
|
|
58
49
|
# ---------------------------------------------------------------------------
|
|
59
50
|
# Per-CLI installers
|
|
60
51
|
# ---------------------------------------------------------------------------
|
|
@@ -107,8 +107,14 @@ def run_peer(
|
|
|
107
107
|
f"<!-- cli: {cli} -->\n"
|
|
108
108
|
f"<!-- model: {model or '(default)'} -->\n"
|
|
109
109
|
f"<!-- duration_s: {cli_result.duration_s:.1f} -->\n"
|
|
110
|
-
f"<!-- ok: {cli_result.ok} -->\n
|
|
110
|
+
f"<!-- ok: {cli_result.ok} -->\n"
|
|
111
111
|
)
|
|
112
|
+
# Surface the cliworker watchdog's reason on failure so downstream tools
|
|
113
|
+
# can tell startup-idle hangs apart from hard timeouts apart from real errors.
|
|
114
|
+
timeout_kind = getattr(cli_result, "timeout_kind", None)
|
|
115
|
+
if timeout_kind:
|
|
116
|
+
header += f"<!-- timeout_kind: {timeout_kind} -->\n"
|
|
117
|
+
header += "\n"
|
|
112
118
|
if cli_result.ok:
|
|
113
119
|
body = cli_result.stdout
|
|
114
120
|
else:
|
|
@@ -4,9 +4,9 @@ The .paircode/ layout (from diary/001-step-a-architecture.md):
|
|
|
4
4
|
|
|
5
5
|
.paircode/
|
|
6
6
|
JOURNEY.md ← fleet log, focus transitions
|
|
7
|
-
peers.yaml ← roster
|
|
8
|
-
|
|
9
|
-
peer-a-{cli}/ ← per-peer
|
|
7
|
+
peers.yaml ← roster (who's on the team)
|
|
8
|
+
sandbox/
|
|
9
|
+
peer-a-{cli}/ ← per-peer persistent sandbox (code/scripts/prototypes)
|
|
10
10
|
focus-01-{slug}/
|
|
11
11
|
FOCUS.md
|
|
12
12
|
research/
|
|
@@ -18,12 +18,13 @@ from __future__ import annotations
|
|
|
18
18
|
import datetime as _dt
|
|
19
19
|
import re
|
|
20
20
|
from dataclasses import dataclass
|
|
21
|
-
from importlib import resources
|
|
22
21
|
from pathlib import Path
|
|
23
22
|
from typing import Iterable
|
|
24
23
|
|
|
25
24
|
import yaml
|
|
26
25
|
|
|
26
|
+
from paircode.util import read_template as _read_template
|
|
27
|
+
|
|
27
28
|
|
|
28
29
|
PAIRCODE_DIRNAME = ".paircode"
|
|
29
30
|
PEERS_FILE = "peers.yaml"
|
|
@@ -35,15 +36,6 @@ def _now_iso() -> str:
|
|
|
35
36
|
return _dt.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
36
37
|
|
|
37
38
|
|
|
38
|
-
def _read_template(relative_path: str) -> str:
|
|
39
|
-
"""Read a file under `paircode/templates/` by relative path.
|
|
40
|
-
|
|
41
|
-
Examples: `_read_template("FOCUS.md")` or
|
|
42
|
-
`_read_template("claude/commands/paircode.md")`.
|
|
43
|
-
"""
|
|
44
|
-
return resources.files("paircode.templates").joinpath(relative_path).read_text(encoding="utf-8")
|
|
45
|
-
|
|
46
|
-
|
|
47
39
|
def _render(template: str, vars: dict[str, str]) -> str:
|
|
48
40
|
out = template
|
|
49
41
|
for k, v in vars.items():
|
|
@@ -61,7 +53,7 @@ class PaircodeState:
|
|
|
61
53
|
root: Path # path to .paircode/ dir itself
|
|
62
54
|
journey_path: Path
|
|
63
55
|
peers_path: Path
|
|
64
|
-
|
|
56
|
+
sandbox_dir: Path
|
|
65
57
|
focus_dirs: list[Path] # sorted by name
|
|
66
58
|
|
|
67
59
|
@property
|
|
@@ -98,7 +90,7 @@ def load_state(root: Path) -> PaircodeState:
|
|
|
98
90
|
root=root,
|
|
99
91
|
journey_path=root / JOURNEY_FILE,
|
|
100
92
|
peers_path=root / PEERS_FILE,
|
|
101
|
-
|
|
93
|
+
sandbox_dir=root / "sandbox",
|
|
102
94
|
focus_dirs=focus_dirs,
|
|
103
95
|
)
|
|
104
96
|
|
|
@@ -117,19 +109,19 @@ def _peer_id(entry) -> str | None:
|
|
|
117
109
|
|
|
118
110
|
|
|
119
111
|
def ensure_peer_dirs(state: PaircodeState, proposed: Iterable) -> list[Path]:
|
|
120
|
-
"""Create .paircode/
|
|
112
|
+
"""Create .paircode/sandbox/{peer.id}/ for each entry. Idempotent.
|
|
121
113
|
|
|
122
114
|
Accepts iterables of ProposedPeer dataclasses OR plain dicts (anything
|
|
123
115
|
with an `id` attribute / key). Silently skips entries without a usable id.
|
|
124
|
-
Returns the list of
|
|
116
|
+
Returns the list of sandbox-dir paths that now exist (created or pre-existing).
|
|
125
117
|
"""
|
|
126
|
-
state.
|
|
118
|
+
state.sandbox_dir.mkdir(exist_ok=True)
|
|
127
119
|
created: list[Path] = []
|
|
128
120
|
for entry in proposed:
|
|
129
121
|
pid = _peer_id(entry)
|
|
130
122
|
if not pid:
|
|
131
123
|
continue
|
|
132
|
-
peer_dir = state.
|
|
124
|
+
peer_dir = state.sandbox_dir / pid
|
|
133
125
|
peer_dir.mkdir(exist_ok=True)
|
|
134
126
|
created.append(peer_dir)
|
|
135
127
|
return created
|
|
@@ -138,7 +130,7 @@ def ensure_peer_dirs(state: PaircodeState, proposed: Iterable) -> list[Path]:
|
|
|
138
130
|
def init_paircode(project_root: Path | None = None, force: bool = False) -> PaircodeState:
|
|
139
131
|
"""Bootstrap .paircode/ in `project_root` (or cwd). Returns the new state.
|
|
140
132
|
|
|
141
|
-
Also pre-creates a subdir per auto-detected peer under .paircode/
|
|
133
|
+
Also pre-creates a subdir per auto-detected peer under .paircode/sandbox/.
|
|
142
134
|
If no peers are detected, the parent dir exists but stays empty.
|
|
143
135
|
"""
|
|
144
136
|
if project_root is None:
|
|
@@ -150,7 +142,7 @@ def init_paircode(project_root: Path | None = None, force: bool = False) -> Pair
|
|
|
150
142
|
f"{root} already exists. Use --force to overwrite, or `paircode status` to inspect."
|
|
151
143
|
)
|
|
152
144
|
root.mkdir(parents=True, exist_ok=True)
|
|
153
|
-
(root / "
|
|
145
|
+
(root / "sandbox").mkdir(exist_ok=True)
|
|
154
146
|
|
|
155
147
|
journey = _render(
|
|
156
148
|
_read_template("JOURNEY.md"),
|
|
@@ -170,8 +162,8 @@ def init_paircode(project_root: Path | None = None, force: bool = False) -> Pair
|
|
|
170
162
|
|
|
171
163
|
ensure_peer_dirs(state, propose_roster())
|
|
172
164
|
except Exception:
|
|
173
|
-
# Scaffolding
|
|
174
|
-
# block bootstrap. The
|
|
165
|
+
# Scaffolding sandboxes is best-effort; a detect failure shouldn't
|
|
166
|
+
# block bootstrap. The sandbox/ parent dir is already there.
|
|
175
167
|
pass
|
|
176
168
|
|
|
177
169
|
return state
|
|
@@ -18,12 +18,11 @@ Inside every `$FOCUS/{stage}/`:
|
|
|
18
18
|
|
|
19
19
|
`N` in `vN` is the current round number, starting at 1. Only write v1 in the first round; bump to v2, v3, … if you do review rounds.
|
|
20
20
|
|
|
21
|
-
**
|
|
21
|
+
**Reports vs. code — the three rules.** Files inside `$FOCUS/{stage}/` are always **markdown reports** (opinions, plans, critiques, summaries). Actual code lives elsewhere, and sandboxes are **always available, never required**:
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
The `$FOCUS/execute/*-v1.md` files describe what was done; the `$FOCUS/execute/` dir is never where code is written.
|
|
23
|
+
1. **Always available.** Peers can use their persistent sandbox at `.paircode/sandbox/{peer-id}/` at **any stage** — not just execute. Verifying a bug in `ask`, grounding research in real data, prototyping a tricky plan section — all fine. Alpha (this Claude session) works directly in the project root (the user's repo). **Code only when it makes your answer materially better** — a 5-line verification script is cheap insurance; a 500-line prototype during an `ask` is over-engineering.
|
|
24
|
+
2. **Separation.** Markdown is the deliverable; the sandbox is the laboratory. Scripts, prototypes, dumps, evidence files live in `.paircode/sandbox/{peer-id}/`. The `$FOCUS/{stage}/*-v1.md` file summarizes what you did + what it proved. The `$FOCUS/` tree is never where code is written.
|
|
25
|
+
3. **Isolation.** Peers never touch files outside their own sandbox. Alpha never touches other peers' sandboxes. The project root is alpha's.
|
|
27
26
|
|
|
28
27
|
## Step 1 — Bootstrap
|
|
29
28
|
|
|
@@ -44,34 +43,41 @@ PEERS=$(paircode roster --alpha claude <user's --peer/--peers flags if any>)
|
|
|
44
43
|
|
|
45
44
|
`paircode roster` always returns a usable list. Trust it.
|
|
46
45
|
|
|
47
|
-
## Step 2 — Pick the stage
|
|
46
|
+
## Step 2 — Pick the flow (not just a stage)
|
|
47
|
+
|
|
48
|
+
Read the user's quoted prompt and pick the **whole flow** you're committing to. Store it as `{flow}`. Each flow is an ordered list of stages; you run them in order.
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
| Flow | Deliverable | Trigger phrases |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `ask` | Opinion on existing work | "what do you think", "review X", "critique", "second opinion" |
|
|
53
|
+
| `research` | Investigation report | "figure out", "explore", "find the right", "how should we" |
|
|
54
|
+
| `research → plan` | Concrete plan | "plan the X", "design the Y", "lay out the approach" |
|
|
55
|
+
| `research → plan → execute` | **Shipped code / action done** | "build", "fix", "implement", "write code for", "ship", "do", and most imperative tasks |
|
|
56
|
+
| `execute` | Just do it (plan already exists) | "execute the plan at focus-NN", "run the migration from focus-05" |
|
|
50
57
|
|
|
51
|
-
|
|
52
|
-
- **plan** — produce a concrete implementation plan, usually building on prior research. ("plan the refactor based on focus-02's research")
|
|
53
|
-
- **execute** — do the work from an existing plan. ("execute the plan at focus-02")
|
|
54
|
-
- **ask** — get opinions on existing work. ("what does codex think of this PR", "review my approach at <path>")
|
|
58
|
+
**Default when ambiguous: `research → plan → execute`.** It's safer to over-deliver than to stop at research for a prompt that meant "build this." The only prompts that aren't build-shaped are explicit opinion/review requests.
|
|
55
59
|
|
|
56
|
-
|
|
60
|
+
Write `{flow}` into `$FOCUS/FOCUS.md` (append a line like `flow: research → plan → execute`) so later steps can see your commitment.
|
|
61
|
+
|
|
62
|
+
The stage for **this iteration** is the first stage in `{flow}`. Store that as `{stage}`. All four stage subdirs already exist under `$FOCUS`.
|
|
57
63
|
|
|
58
64
|
## Step 3 — Fire peers in parallel
|
|
59
65
|
|
|
60
66
|
For each peer id in `$PEERS`, spawn one subagent using the **Agent tool** with `subagent_type=general-purpose` and `run_in_background=true`. Send them all in a single message so they run truly concurrently.
|
|
61
67
|
|
|
62
|
-
Construct a stage-appropriate peer prompt.
|
|
68
|
+
Construct a stage-appropriate peer prompt. Every prompt reminds the peer its sandbox is available; the deliverable is always the markdown report. General shape for each `{stage}`:
|
|
63
69
|
|
|
64
|
-
- **research**: "Read $FOCUS/FOCUS.md. Give an honest, skeptical, specific **cold take** on the prompt. Clean markdown, no preamble."
|
|
65
|
-
- **plan**: "Read $FOCUS/FOCUS.md and any `../research/*-FINAL.md` if present. Write a **concrete step-by-step plan
|
|
66
|
-
- **execute**: "Read $FOCUS/FOCUS.md and the plan at `$FOCUS/plan/*-FINAL.md`. **
|
|
67
|
-
- **ask**: "Read $FOCUS/FOCUS.md and the artifact it points to. Give your **honest critique**
|
|
70
|
+
- **research**: "Read $FOCUS/FOCUS.md. Give an honest, skeptical, specific **cold take** on the prompt. Use your sandbox at `.paircode/sandbox/{peer-id}/` to write quick scripts grounding your claims in real data (not README marketing) if it would sharpen your take. Clean markdown report, no preamble."
|
|
71
|
+
- **plan**: "Read $FOCUS/FOCUS.md and any `../research/*-FINAL.md` if present. Write a **concrete step-by-step plan**: goal, scope, numbered steps, risks, success criteria. Be KISS. Use your sandbox at `.paircode/sandbox/{peer-id}/` to prototype tricky sections and verify feasibility. Clean markdown."
|
|
72
|
+
- **execute**: "Read $FOCUS/FOCUS.md and the plan at `$FOCUS/plan/*-FINAL.md`. **Carry out the plan and produce working artifacts.** For code-shipping plans: work in your peer workspace at `.paircode/sandbox/{peer-id}/`, write code there, run commands there, run tests there. For action plans (run a migration, make an API call, file a ticket): do the action and report results. Do NOT touch files outside your workspace. Your output is a markdown report summarizing what you built/ran, files touched, verification status, what's open."
|
|
73
|
+
- **ask**: "Read $FOCUS/FOCUS.md and the artifact it points to. Give your **honest critique** — severity-ranked findings with file:line citations where possible. If reproducing a claim by running a quick script in your sandbox (`.paircode/sandbox/{peer-id}/`) would make your critique sharper, do it. Your output is still a critique report, not a prototype. Clean markdown, no preamble."
|
|
68
74
|
|
|
69
75
|
Each subagent's prompt:
|
|
70
76
|
|
|
71
77
|
```
|
|
72
78
|
You are the {peer-id} peer in a paircode {stage} cycle.
|
|
73
79
|
|
|
74
|
-
Your persistent workspace: .paircode/
|
|
80
|
+
Your persistent workspace: .paircode/sandbox/{peer-id}/
|
|
75
81
|
The focus we're on: $FOCUS
|
|
76
82
|
The prompt from the user: "<the quoted prompt>"
|
|
77
83
|
The stage: {stage}
|
|
@@ -93,7 +99,7 @@ Do not try to "fix" the peer's output. We want the raw file-trace on disk.
|
|
|
93
99
|
|
|
94
100
|
While the peer subagents run, you (the team lead) write your own v1 at `$FOCUS/{stage}/alpha-v1.md`. Use the Write tool. Same stage-appropriate rules as above — and your advantage over the peers is you have this session's full context (memory, recent work, the maintainer's voice).
|
|
95
101
|
|
|
96
|
-
**Execute-stage asymmetry:** when `{stage}` is `execute`, peers code inside their sandboxed peer workspaces (`.paircode/
|
|
102
|
+
**Execute-stage asymmetry:** when `{stage}` is `execute`, peers code inside their sandboxed peer workspaces (`.paircode/sandbox/{peer-id}/`). You, alpha, code directly in the project root — you're working on the user's actual repo. Your `alpha-v1.md` is a report summarizing what landed in the real project files; the code itself lives in the repo, not in `$FOCUS/execute/`.
|
|
97
103
|
|
|
98
104
|
## Step 5 — Wait and read
|
|
99
105
|
|
|
@@ -136,20 +142,13 @@ Then read every `*-FINAL.md` and write `$FOCUS/{stage}/consensus.md` via the Wri
|
|
|
136
142
|
|
|
137
143
|
Adversarial-but-honest. No pile-on. No rubber-stamping either.
|
|
138
144
|
|
|
139
|
-
## Step 8 —
|
|
140
|
-
|
|
141
|
-
After a stage converges, decide whether to transition to another stage or wrap up:
|
|
145
|
+
## Step 8 — Advance the flow (mechanical)
|
|
142
146
|
|
|
143
|
-
|
|
144
|
-
- **Next stage** — move to whichever stage follows naturally from what you just finished. Pick the new `{stage}`, then jump back to **Step 3** (Fire peers). Skip Step 1 — focus is still open. Skip Step 2 — you already picked. The previous stage's `*-FINAL.md` files are input for the next stage's peer prompts.
|
|
147
|
+
Look at the `{flow}` you committed to in Step 2. Set `{stage}` to the **next** stage in that flow and jump back to **Step 3** (Fire peers). Skip Step 1 (focus is still open) and Step 2 (flow already chosen). The previous stage's `*-FINAL.md` files are input for the next stage's peer prompts.
|
|
145
148
|
|
|
146
|
-
|
|
147
|
-
- `ask → done`
|
|
148
|
-
- `research → done` (if research itself was the deliverable)
|
|
149
|
-
- `research → plan → execute → done` (most common full-build chain)
|
|
150
|
-
- `research → plan → done` (when the plan is the deliverable and the maintainer will execute manually)
|
|
149
|
+
**If the last stage just finished, go to Step 9.**
|
|
151
150
|
|
|
152
|
-
|
|
151
|
+
This step is mechanical. Do not re-deliberate. Do not ask the user whether to continue. The user's prompt in `$ARGUMENTS` was implicit go-ahead for the whole flow; stopping early is an exception that only happens if **something blocked you** (a peer hard-errored and the blocker is critical, the consensus explicitly said "do not proceed", or the user interrupted). If you do stop early, say so explicitly in the final report with the reason.
|
|
153
152
|
|
|
154
153
|
## Step 9 — Final report
|
|
155
154
|
|
|
@@ -166,4 +165,6 @@ Keep it under 200 words. Brief, honest, actionable.
|
|
|
166
165
|
- **Never skip Step 1.** The focus and roster helpers do the work Python is better at.
|
|
167
166
|
- **Never invent peer commands.** Always route through `paircode invoke <id> "..." --out <path>` — that's how cliworker's speed flags, MCP strip, and output tracing work.
|
|
168
167
|
- **Never pass the interactive Claude session as a peer by mistake.** `paircode roster --alpha claude` handles that for you.
|
|
168
|
+
- **Peer sandboxes are always available, never required.** Use them when it materially sharpens your answer, not by default.
|
|
169
|
+
- **The user's prompt is implicit go-ahead for the whole `{flow}`.** Do not pause for permission between stages. If you must stop early, say so explicitly in the final report with the reason.
|
|
169
170
|
- **No AI attribution** in any artifact you write (consensus.md, alpha-*.md). See the maintainer's CLAUDE.md.
|
|
@@ -21,7 +21,7 @@ Inside every `$FOCUS/{stage}/`:
|
|
|
21
21
|
|
|
22
22
|
**Code vs. reports.** Every file inside `$FOCUS/{stage}/` is a **markdown report** (opinion, critique, plan, or summary of work). Actual code lives elsewhere:
|
|
23
23
|
|
|
24
|
-
- **Peers** code in their sandboxed workspace at `.paircode/
|
|
24
|
+
- **Peers** code in their sandboxed workspace at `.paircode/sandbox/{peer-id}/` — persistent across focuses.
|
|
25
25
|
- **Alpha** codes directly in the project root (the user's repo) — alpha IS the project, no sandbox.
|
|
26
26
|
|
|
27
27
|
The `$FOCUS/execute/*-v1.md` files describe what was done; the `$FOCUS/execute/` dir is never where code is written.
|
|
@@ -77,14 +77,14 @@ Construct one per stage — keep it reusable across peers:
|
|
|
77
77
|
|
|
78
78
|
- **research**: "Read $FOCUS/FOCUS.md. Give an honest, skeptical, specific **cold take** on the prompt. Clean markdown, no preamble."
|
|
79
79
|
- **plan**: "Read $FOCUS/FOCUS.md and any `../research/*-FINAL.md` if present. Write a **concrete step-by-step plan** for this prompt: goal, scope, numbered steps, risks, success criteria. Be KISS. Clean markdown."
|
|
80
|
-
- **execute**: "Read $FOCUS/FOCUS.md and the plan at `$FOCUS/plan/*-FINAL.md`. **Execute the plan IN YOUR PEER WORKSPACE at `.paircode/
|
|
80
|
+
- **execute**: "Read $FOCUS/FOCUS.md and the plan at `$FOCUS/plan/*-FINAL.md`. **Execute the plan IN YOUR PEER WORKSPACE at `.paircode/sandbox/{peer-id}/`** — that is your persistent sandbox. Write code there, run commands there. Do NOT touch files outside that workspace. Then write a markdown report to your output file summarizing: what you built, files touched (paths relative to your peer workspace), tests/verification status, what's open. The output file holds the *report*; the *code* lives in your peer workspace."
|
|
81
81
|
- **ask**: "Read $FOCUS/FOCUS.md and the artifact it points to. Give your **honest critique** — severity-ranked findings with file:line citations where possible. Clean markdown, no preamble."
|
|
82
82
|
|
|
83
83
|
## Step 4 — Team lead's own take
|
|
84
84
|
|
|
85
85
|
After the serial peer loop completes (or right after you kick it off if your tools allow), write your own v1 at `$FOCUS/{stage}/alpha-v1.md` using the shell tool (e.g. `cat > ... <<'EOF'`). Same stage-appropriate rules as above — your advantage over the peers is session context (memory, recent work, the maintainer's voice).
|
|
86
86
|
|
|
87
|
-
**Execute-stage asymmetry:** when `{stage}` is `execute`, peers code inside their sandboxed peer workspaces (`.paircode/
|
|
87
|
+
**Execute-stage asymmetry:** when `{stage}` is `execute`, peers code inside their sandboxed peer workspaces (`.paircode/sandbox/{peer-id}/`). You, alpha, code directly in the project root — the user's actual repo. Your `alpha-v1.md` is a report summarizing what landed in the real project files; the code itself lives in the repo, not in `$FOCUS/execute/`.
|
|
88
88
|
|
|
89
89
|
## Step 5 — Wait and read
|
|
90
90
|
|
|
@@ -21,7 +21,7 @@ N in vN is the current round number, starting at 1. Only write v1 in the first r
|
|
|
21
21
|
|
|
22
22
|
**Code vs. reports.** Every file inside $FOCUS/{stage}/ is a markdown report (opinion, critique, plan, or summary of work). Actual code lives elsewhere:
|
|
23
23
|
|
|
24
|
-
- Peers code in their sandboxed workspace at .paircode/
|
|
24
|
+
- Peers code in their sandboxed workspace at .paircode/sandbox/{peer-id}/ — persistent across focuses.
|
|
25
25
|
- Alpha codes directly in the project root (the user's repo) — alpha IS the project, no sandbox.
|
|
26
26
|
|
|
27
27
|
The $FOCUS/execute/*-v1.md files describe what was done; the $FOCUS/execute/ dir is never where code is written.
|
|
@@ -71,14 +71,14 @@ Construct one per stage — keep it reusable across peers:
|
|
|
71
71
|
|
|
72
72
|
- research: "Read $FOCUS/FOCUS.md. Give an honest, skeptical, specific cold take on the prompt. Clean markdown, no preamble."
|
|
73
73
|
- plan: "Read $FOCUS/FOCUS.md and any ../research/*-FINAL.md if present. Write a concrete step-by-step plan for this prompt: goal, scope, numbered steps, risks, success criteria. Be KISS. Clean markdown."
|
|
74
|
-
- execute: "Read $FOCUS/FOCUS.md and the plan at $FOCUS/plan/*-FINAL.md. Execute the plan IN YOUR PEER WORKSPACE at .paircode/
|
|
74
|
+
- execute: "Read $FOCUS/FOCUS.md and the plan at $FOCUS/plan/*-FINAL.md. Execute the plan IN YOUR PEER WORKSPACE at .paircode/sandbox/{peer-id}/ — that is your persistent sandbox. Write code there, run commands there. Do NOT touch files outside that workspace. Then write a markdown report to your output file summarizing: what you built, files touched (paths relative to your peer workspace), tests/verification status, what's open. The output file holds the report; the code lives in your peer workspace."
|
|
75
75
|
- ask: "Read $FOCUS/FOCUS.md and the artifact it points to. Give your honest critique — severity-ranked findings with file:line citations where possible. Clean markdown, no preamble."
|
|
76
76
|
|
|
77
77
|
## Step 4 — Team lead's own take
|
|
78
78
|
|
|
79
79
|
After the serial peer loop completes, write your own v1 to $FOCUS/{stage}/alpha-v1.md via run_shell_command (cat > ... <<'EOF' or equivalent). Same stage-appropriate rules — your advantage over the peers is session context.
|
|
80
80
|
|
|
81
|
-
**Execute-stage asymmetry:** when {stage} is execute, peers code inside their sandboxed peer workspaces (.paircode/
|
|
81
|
+
**Execute-stage asymmetry:** when {stage} is execute, peers code inside their sandboxed peer workspaces (.paircode/sandbox/{peer-id}/). You, alpha, code directly in the project root — the user's actual repo. Your alpha-v1.md is a report summarizing what landed in the real project files; the code itself lives in the repo, not in $FOCUS/execute/.
|
|
82
82
|
|
|
83
83
|
## Step 5 — Wait and read
|
|
84
84
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# project root.
|
|
6
6
|
#
|
|
7
7
|
# Every other LLM that participates is a peer. Each peer gets an entry
|
|
8
|
-
# below AND a folder at .paircode/
|
|
8
|
+
# below AND a folder at .paircode/sandbox/peer-N-{cli}/.
|
|
9
9
|
#
|
|
10
10
|
# Fields:
|
|
11
11
|
# id — peer-a, peer-b, peer-c, ... (unique, immutable)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Small shared helpers for paircode internals."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from importlib import resources
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def read_template(relative_path: str) -> str:
|
|
8
|
+
"""Read a file under `paircode/templates/` by relative path.
|
|
9
|
+
|
|
10
|
+
Examples:
|
|
11
|
+
read_template("FOCUS.md")
|
|
12
|
+
read_template("claude/commands/paircode.md")
|
|
13
|
+
"""
|
|
14
|
+
return (
|
|
15
|
+
resources.files("paircode.templates")
|
|
16
|
+
.joinpath(relative_path)
|
|
17
|
+
.read_text(encoding="utf-8")
|
|
18
|
+
)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
"""Tests for stage
|
|
1
|
+
"""Tests for stage convergence — copying latest vN to *-FINAL.md."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
-
from paircode.
|
|
6
|
+
from paircode.converge import discover_latest_versions, seal_stage
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def test_discover_latest_versions_picks_highest_per_peer(tmp_path: Path):
|
|
@@ -22,13 +22,13 @@ def test_init_creates_paircode_structure(tmp_path: Path):
|
|
|
22
22
|
assert state.root.is_dir()
|
|
23
23
|
assert state.journey_path.exists()
|
|
24
24
|
assert state.peers_path.exists()
|
|
25
|
-
assert state.
|
|
25
|
+
assert state.sandbox_dir.is_dir()
|
|
26
26
|
assert state.focus_count == 0
|
|
27
27
|
assert state.active_focus is None
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
def test_init_scaffolds_peer_dirs_from_detected_clis(tmp_path: Path, monkeypatch):
|
|
31
|
-
"""init_paircode should create .paircode/
|
|
31
|
+
"""init_paircode should create .paircode/sandbox/{id}/ for each detected peer."""
|
|
32
32
|
fake_proposed = [
|
|
33
33
|
ProposedPeer(id="peer-a-codex", cli="codex", priority="high", notes=""),
|
|
34
34
|
ProposedPeer(id="peer-b-gemini", cli="gemini", priority="low", notes=""),
|
|
@@ -36,15 +36,15 @@ def test_init_scaffolds_peer_dirs_from_detected_clis(tmp_path: Path, monkeypatch
|
|
|
36
36
|
monkeypatch.setattr("paircode.handshake.propose_roster", lambda: fake_proposed)
|
|
37
37
|
|
|
38
38
|
state = init_paircode(tmp_path)
|
|
39
|
-
assert (state.
|
|
40
|
-
assert (state.
|
|
39
|
+
assert (state.sandbox_dir / "peer-a-codex").is_dir()
|
|
40
|
+
assert (state.sandbox_dir / "peer-b-gemini").is_dir()
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
def
|
|
43
|
+
def test_init_with_no_detected_peers_leaves_sandbox_dir_empty(tmp_path: Path, monkeypatch):
|
|
44
44
|
monkeypatch.setattr("paircode.handshake.propose_roster", lambda: [])
|
|
45
45
|
state = init_paircode(tmp_path)
|
|
46
|
-
assert state.
|
|
47
|
-
assert list(state.
|
|
46
|
+
assert state.sandbox_dir.is_dir()
|
|
47
|
+
assert list(state.sandbox_dir.iterdir()) == []
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
def test_ensure_peer_dirs_accepts_dicts_and_is_idempotent(tmp_path: Path, monkeypatch):
|
|
@@ -54,7 +54,7 @@ def test_ensure_peer_dirs_accepts_dicts_and_is_idempotent(tmp_path: Path, monkey
|
|
|
54
54
|
dicts = [{"id": "peer-a-codex", "cli": "codex"}, {"id": "peer-c-ollama"}]
|
|
55
55
|
created = ensure_peer_dirs(state, dicts)
|
|
56
56
|
assert len(created) == 2
|
|
57
|
-
assert (state.
|
|
57
|
+
assert (state.sandbox_dir / "peer-a-codex").is_dir()
|
|
58
58
|
# Running again is a no-op (idempotent)
|
|
59
59
|
created_again = ensure_peer_dirs(state, dicts)
|
|
60
60
|
assert len(created_again) == 2
|
paircode-0.11.1/PKG-INFO
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: paircode
|
|
3
|
-
Version: 0.11.1
|
|
4
|
-
Summary: Adversarial journey framework — orchestrate multiple LLM peers through research, plan, and execute stages with file-trace peer review.
|
|
5
|
-
Project-URL: Homepage, https://github.com/starshipagentic/paircode
|
|
6
|
-
Project-URL: Repository, https://github.com/starshipagentic/paircode
|
|
7
|
-
Project-URL: Issues, https://github.com/starshipagentic/paircode/issues
|
|
8
|
-
Author-email: Travis Somerville <travis.somerville@gmail.com>
|
|
9
|
-
License: MIT
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
Keywords: agents,claude,codex,gemini,llm,multi-agent,ollama,orchestration,peer-review
|
|
12
|
-
Classifier: Development Status :: 2 - Pre-Alpha
|
|
13
|
-
Classifier: Intended Audience :: Developers
|
|
14
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
-
Classifier: Operating System :: OS Independent
|
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
-
Classifier: Topic :: Software Development
|
|
22
|
-
Requires-Python: >=3.10
|
|
23
|
-
Requires-Dist: click>=8.1
|
|
24
|
-
Requires-Dist: cliworker>=0.8.1
|
|
25
|
-
Requires-Dist: pyyaml>=6.0
|
|
26
|
-
Requires-Dist: rich>=13.0
|
|
27
|
-
Description-Content-Type: text/markdown
|
|
28
|
-
|
|
29
|
-
# paircode
|
|
30
|
-
|
|
31
|
-
**Multi-LLM peer review for your code, with file-traces on disk.** One primary LLM (alpha) + any number of peer LLMs (Codex, Gemini, Ollama, …) running independent research, plans, and code, with structured cross-review rounds stored entirely as Markdown on disk.
|
|
32
|
-
|
|
33
|
-
> Born from 31 iterations of dual-LLM silent-agreement hunting on a real ML project. See `diary/001-step-a-architecture.md` for the full design rationale.
|
|
34
|
-
|
|
35
|
-
## Install
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
pipx install paircode # or: pip install --user paircode
|
|
39
|
-
paircode install # registers /paircode in every detected LLM CLI
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
After install, `/paircode` is available in all three:
|
|
43
|
-
|
|
44
|
-
| CLI | File installed |
|
|
45
|
-
|---|---|
|
|
46
|
-
| Claude Code | `~/.claude/commands/paircode.md` |
|
|
47
|
-
| Codex CLI | `~/.codex/prompts/paircode.md` |
|
|
48
|
-
| Gemini CLI | `~/.gemini/commands/paircode.toml` |
|
|
49
|
-
|
|
50
|
-
Open any of them and type `/paircode`. In Gemini, you may need `/commands reload` the first time.
|
|
51
|
-
|
|
52
|
-
As of v0.8.0, paircode delegates all CLI invocation to [`cliworker`](https://pypi.org/project/cliworker/) — one place to own
|
|
53
|
-
the speed flags, MCP strip tricks, skip-cache, and subscription-first fallback
|
|
54
|
-
logic. paircode adds the peer-review orchestration on top (file-traces, stages,
|
|
55
|
-
gates, journey).
|
|
56
|
-
|
|
57
|
-
## Use it — three entry points
|
|
58
|
-
|
|
59
|
-
### 1. From Claude Code (or any supported LLM) as a slash command
|
|
60
|
-
|
|
61
|
-
Inside a Claude Code session:
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
/paircode drive "build a KISS PHQ-9 depression risk engine"
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Claude relays that to the CLI. paircode opens a focus, runs research → plan → execute with peer-reviewed rounds, writes everything to `.paircode/` as Markdown.
|
|
68
|
-
|
|
69
|
-
### 2. From the shell directly
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
paircode init # bootstrap .paircode/ in cwd
|
|
73
|
-
paircode handshake --write # detect CLIs + write peer roster
|
|
74
|
-
paircode drive "refactor the auth middleware" # full loop
|
|
75
|
-
paircode status # see where you are
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### 3. Piece by piece
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
paircode focus "try GitHub Actions migration"
|
|
82
|
-
paircode stage research --rounds 2 # cold v1 + one review/revise round
|
|
83
|
-
paircode seal research # mark research FINAL
|
|
84
|
-
paircode stage plan --rounds 3
|
|
85
|
-
paircode seal plan
|
|
86
|
-
paircode stage execute
|
|
87
|
-
paircode seal execute
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## What ends up on disk
|
|
91
|
-
|
|
92
|
-
```
|
|
93
|
-
your-project/
|
|
94
|
-
.paircode/
|
|
95
|
-
JOURNEY.md # fleet log (auto-updated)
|
|
96
|
-
peers.yaml # who's on the team
|
|
97
|
-
peers/
|
|
98
|
-
peer-a-codex/ # peer's profile (and code if full-fork mode)
|
|
99
|
-
focus-01-<slug>/
|
|
100
|
-
FOCUS.md # this focus's goal, roster override, gate config
|
|
101
|
-
research/
|
|
102
|
-
alpha-v1.md ... alpha-vN.md
|
|
103
|
-
peer-a-codex-v1.md ...
|
|
104
|
-
reviews/round-01-peer-a-codex-critiques-alpha.md
|
|
105
|
-
alpha-FINAL.md # sealed exit artifact
|
|
106
|
-
peer-a-codex-FINAL.md
|
|
107
|
-
plan/
|
|
108
|
-
(same shape)
|
|
109
|
-
execute/
|
|
110
|
-
(same shape)
|
|
111
|
-
focus-02-<slug>/
|
|
112
|
-
...
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
Every LLM's every thought lands as a Markdown file. That's how heterogeneous LLM tools communicate reliably across vendors, sessions, and days.
|
|
116
|
-
|
|
117
|
-
## Three peer modes
|
|
118
|
-
|
|
119
|
-
| Mode | What the peer does | When to use |
|
|
120
|
-
|---|---|---|
|
|
121
|
-
| **full-fork** | Writes its own cold codebase + markdown artifacts | Silent-agreement hunting, safety-critical code |
|
|
122
|
-
| **pair-code** | Contributes directly to alpha's codebase via patches + reviews | Feature work, regular dev |
|
|
123
|
-
| **opinion-only** | Reads alpha's work, writes reviews, never touches code | Budget peers, quick sanity checks |
|
|
124
|
-
|
|
125
|
-
Configured per peer in `.paircode/peers.yaml`.
|
|
126
|
-
|
|
127
|
-
## Model compatibility
|
|
128
|
-
|
|
129
|
-
| CLI | Slash command | Subprocess driver | Status |
|
|
130
|
-
|---|---|---|---|
|
|
131
|
-
| Claude Code (`claude`) | ✓ `/paircode` via `~/.claude/commands/paircode.md` | ✓ `claude -p <prompt>` | stable |
|
|
132
|
-
| Codex (`codex`) | ✓ context rule via `~/.codex/rules/paircode.rules` | ✓ `codex exec <prompt>` | stable |
|
|
133
|
-
| Gemini CLI (`gemini`) | ✓ reference file at `~/.gemini/paircode.md` | ✓ `gemini -p <prompt>` | stable |
|
|
134
|
-
| Ollama (`ollama`) | — (local models, no slash-cmd primitive) | ✓ `ollama run <model> <prompt>` | stable |
|
|
135
|
-
| Aider / others | — | best-effort, PRs welcome | planned |
|
|
136
|
-
|
|
137
|
-
## Commands
|
|
138
|
-
|
|
139
|
-
```
|
|
140
|
-
paircode --help full command list
|
|
141
|
-
paircode install register /paircode in all detected LLM CLIs
|
|
142
|
-
paircode uninstall remove /paircode from LLM CLIs (idempotent)
|
|
143
|
-
paircode handshake detect CLIs, propose peer roster
|
|
144
|
-
paircode handshake --write save roster to .paircode/peers.yaml
|
|
145
|
-
paircode init bootstrap .paircode/ in cwd
|
|
146
|
-
paircode status summarize current state
|
|
147
|
-
paircode focus <name> open a new focus
|
|
148
|
-
paircode focus list existing focuses
|
|
149
|
-
paircode stage <name> run one stage N rounds on active focus
|
|
150
|
-
paircode seal <stage> seal stage — copy each peer's latest vN to {peer}-FINAL.md
|
|
151
|
-
paircode drive <topic> full loop: research → plan → execute
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## Why this exists
|
|
155
|
-
|
|
156
|
-
See [`diary/001-step-a-architecture.md`](diary/001-step-a-architecture.md) for the full backstory. The short version: running two LLMs adversarially surfaces silent-agreement bug classes that neither engine alone can catch, because cross-engine agreement is not the same as correctness when both share the same blind spot.
|
|
157
|
-
|
|
158
|
-
## License
|
|
159
|
-
|
|
160
|
-
MIT. See [LICENSE](LICENSE).
|
paircode-0.11.1/README.md
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
# paircode
|
|
2
|
-
|
|
3
|
-
**Multi-LLM peer review for your code, with file-traces on disk.** One primary LLM (alpha) + any number of peer LLMs (Codex, Gemini, Ollama, …) running independent research, plans, and code, with structured cross-review rounds stored entirely as Markdown on disk.
|
|
4
|
-
|
|
5
|
-
> Born from 31 iterations of dual-LLM silent-agreement hunting on a real ML project. See `diary/001-step-a-architecture.md` for the full design rationale.
|
|
6
|
-
|
|
7
|
-
## Install
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
pipx install paircode # or: pip install --user paircode
|
|
11
|
-
paircode install # registers /paircode in every detected LLM CLI
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
After install, `/paircode` is available in all three:
|
|
15
|
-
|
|
16
|
-
| CLI | File installed |
|
|
17
|
-
|---|---|
|
|
18
|
-
| Claude Code | `~/.claude/commands/paircode.md` |
|
|
19
|
-
| Codex CLI | `~/.codex/prompts/paircode.md` |
|
|
20
|
-
| Gemini CLI | `~/.gemini/commands/paircode.toml` |
|
|
21
|
-
|
|
22
|
-
Open any of them and type `/paircode`. In Gemini, you may need `/commands reload` the first time.
|
|
23
|
-
|
|
24
|
-
As of v0.8.0, paircode delegates all CLI invocation to [`cliworker`](https://pypi.org/project/cliworker/) — one place to own
|
|
25
|
-
the speed flags, MCP strip tricks, skip-cache, and subscription-first fallback
|
|
26
|
-
logic. paircode adds the peer-review orchestration on top (file-traces, stages,
|
|
27
|
-
gates, journey).
|
|
28
|
-
|
|
29
|
-
## Use it — three entry points
|
|
30
|
-
|
|
31
|
-
### 1. From Claude Code (or any supported LLM) as a slash command
|
|
32
|
-
|
|
33
|
-
Inside a Claude Code session:
|
|
34
|
-
|
|
35
|
-
```
|
|
36
|
-
/paircode drive "build a KISS PHQ-9 depression risk engine"
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Claude relays that to the CLI. paircode opens a focus, runs research → plan → execute with peer-reviewed rounds, writes everything to `.paircode/` as Markdown.
|
|
40
|
-
|
|
41
|
-
### 2. From the shell directly
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
paircode init # bootstrap .paircode/ in cwd
|
|
45
|
-
paircode handshake --write # detect CLIs + write peer roster
|
|
46
|
-
paircode drive "refactor the auth middleware" # full loop
|
|
47
|
-
paircode status # see where you are
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### 3. Piece by piece
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
paircode focus "try GitHub Actions migration"
|
|
54
|
-
paircode stage research --rounds 2 # cold v1 + one review/revise round
|
|
55
|
-
paircode seal research # mark research FINAL
|
|
56
|
-
paircode stage plan --rounds 3
|
|
57
|
-
paircode seal plan
|
|
58
|
-
paircode stage execute
|
|
59
|
-
paircode seal execute
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
## What ends up on disk
|
|
63
|
-
|
|
64
|
-
```
|
|
65
|
-
your-project/
|
|
66
|
-
.paircode/
|
|
67
|
-
JOURNEY.md # fleet log (auto-updated)
|
|
68
|
-
peers.yaml # who's on the team
|
|
69
|
-
peers/
|
|
70
|
-
peer-a-codex/ # peer's profile (and code if full-fork mode)
|
|
71
|
-
focus-01-<slug>/
|
|
72
|
-
FOCUS.md # this focus's goal, roster override, gate config
|
|
73
|
-
research/
|
|
74
|
-
alpha-v1.md ... alpha-vN.md
|
|
75
|
-
peer-a-codex-v1.md ...
|
|
76
|
-
reviews/round-01-peer-a-codex-critiques-alpha.md
|
|
77
|
-
alpha-FINAL.md # sealed exit artifact
|
|
78
|
-
peer-a-codex-FINAL.md
|
|
79
|
-
plan/
|
|
80
|
-
(same shape)
|
|
81
|
-
execute/
|
|
82
|
-
(same shape)
|
|
83
|
-
focus-02-<slug>/
|
|
84
|
-
...
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
Every LLM's every thought lands as a Markdown file. That's how heterogeneous LLM tools communicate reliably across vendors, sessions, and days.
|
|
88
|
-
|
|
89
|
-
## Three peer modes
|
|
90
|
-
|
|
91
|
-
| Mode | What the peer does | When to use |
|
|
92
|
-
|---|---|---|
|
|
93
|
-
| **full-fork** | Writes its own cold codebase + markdown artifacts | Silent-agreement hunting, safety-critical code |
|
|
94
|
-
| **pair-code** | Contributes directly to alpha's codebase via patches + reviews | Feature work, regular dev |
|
|
95
|
-
| **opinion-only** | Reads alpha's work, writes reviews, never touches code | Budget peers, quick sanity checks |
|
|
96
|
-
|
|
97
|
-
Configured per peer in `.paircode/peers.yaml`.
|
|
98
|
-
|
|
99
|
-
## Model compatibility
|
|
100
|
-
|
|
101
|
-
| CLI | Slash command | Subprocess driver | Status |
|
|
102
|
-
|---|---|---|---|
|
|
103
|
-
| Claude Code (`claude`) | ✓ `/paircode` via `~/.claude/commands/paircode.md` | ✓ `claude -p <prompt>` | stable |
|
|
104
|
-
| Codex (`codex`) | ✓ context rule via `~/.codex/rules/paircode.rules` | ✓ `codex exec <prompt>` | stable |
|
|
105
|
-
| Gemini CLI (`gemini`) | ✓ reference file at `~/.gemini/paircode.md` | ✓ `gemini -p <prompt>` | stable |
|
|
106
|
-
| Ollama (`ollama`) | — (local models, no slash-cmd primitive) | ✓ `ollama run <model> <prompt>` | stable |
|
|
107
|
-
| Aider / others | — | best-effort, PRs welcome | planned |
|
|
108
|
-
|
|
109
|
-
## Commands
|
|
110
|
-
|
|
111
|
-
```
|
|
112
|
-
paircode --help full command list
|
|
113
|
-
paircode install register /paircode in all detected LLM CLIs
|
|
114
|
-
paircode uninstall remove /paircode from LLM CLIs (idempotent)
|
|
115
|
-
paircode handshake detect CLIs, propose peer roster
|
|
116
|
-
paircode handshake --write save roster to .paircode/peers.yaml
|
|
117
|
-
paircode init bootstrap .paircode/ in cwd
|
|
118
|
-
paircode status summarize current state
|
|
119
|
-
paircode focus <name> open a new focus
|
|
120
|
-
paircode focus list existing focuses
|
|
121
|
-
paircode stage <name> run one stage N rounds on active focus
|
|
122
|
-
paircode seal <stage> seal stage — copy each peer's latest vN to {peer}-FINAL.md
|
|
123
|
-
paircode drive <topic> full loop: research → plan → execute
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## Why this exists
|
|
127
|
-
|
|
128
|
-
See [`diary/001-step-a-architecture.md`](diary/001-step-a-architecture.md) for the full backstory. The short version: running two LLMs adversarially surfaces silent-agreement bug classes that neither engine alone can catch, because cross-engine agreement is not the same as correctness when both share the same blind spot.
|
|
129
|
-
|
|
130
|
-
## License
|
|
131
|
-
|
|
132
|
-
MIT. See [LICENSE](LICENSE).
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"""Update JOURNEY.md as focuses open/close."""
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
import datetime as _dt
|
|
5
|
-
import re
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
from paircode.state import PaircodeState
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
_HISTORY_HEADER_RE = re.compile(r"^## History\s*$", re.MULTILINE)
|
|
12
|
-
_HISTORY_TABLE_RE = re.compile(
|
|
13
|
-
r"\| # \| Focus \| Opened \| Closed \| Iterations \| Result \|\n\|[-|\s]+\|\n"
|
|
14
|
-
)
|
|
15
|
-
_ACTIVE_FOCUS_RE = re.compile(
|
|
16
|
-
r"## Active focus\s*\n\n(.+?)\n\n##", re.DOTALL
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _now() -> str:
|
|
21
|
-
return _dt.datetime.now().strftime("%Y-%m-%d %H:%M")
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def note_focus_opened(state: PaircodeState, focus_name: str) -> None:
|
|
25
|
-
"""Update JOURNEY.md: set active focus, append history row."""
|
|
26
|
-
j = state.journey_path
|
|
27
|
-
if not j.exists():
|
|
28
|
-
return
|
|
29
|
-
text = j.read_text(encoding="utf-8")
|
|
30
|
-
|
|
31
|
-
# Replace the "Active focus" section
|
|
32
|
-
new_active = f"## Active focus\n\n**{focus_name}** — opened {_now()}\n\n##"
|
|
33
|
-
text = _ACTIVE_FOCUS_RE.sub(new_active, text, count=1)
|
|
34
|
-
|
|
35
|
-
# Append to history table
|
|
36
|
-
history_match = _HISTORY_TABLE_RE.search(text)
|
|
37
|
-
if history_match:
|
|
38
|
-
# Count existing rows in history table
|
|
39
|
-
rest = text[history_match.end():]
|
|
40
|
-
existing_rows = rest.split("\n\n")[0]
|
|
41
|
-
row_count = len([r for r in existing_rows.split("\n") if r.startswith("|")])
|
|
42
|
-
new_row = f"| {row_count + 1} | {focus_name} | {_now()} | — | 0 | — |\n"
|
|
43
|
-
text = text[:history_match.end()] + new_row + text[history_match.end():]
|
|
44
|
-
|
|
45
|
-
j.write_text(text, encoding="utf-8")
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def note_focus_closed(state: PaircodeState, focus_name: str, iterations: int, result: str) -> None:
|
|
49
|
-
"""Update JOURNEY.md: mark the focus row as closed with iteration count + result."""
|
|
50
|
-
j = state.journey_path
|
|
51
|
-
if not j.exists():
|
|
52
|
-
return
|
|
53
|
-
text = j.read_text(encoding="utf-8")
|
|
54
|
-
# Find row for this focus and update its Closed/Iterations/Result columns
|
|
55
|
-
pattern = re.compile(
|
|
56
|
-
rf"(\| \d+ \| {re.escape(focus_name)} \| [^|]+\| )—( \| )0( \| )—( \|)"
|
|
57
|
-
)
|
|
58
|
-
text = pattern.sub(
|
|
59
|
-
rf"\g<1>{_now()}\g<2>{iterations}\g<3>{result}\g<4>",
|
|
60
|
-
text,
|
|
61
|
-
count=1,
|
|
62
|
-
)
|
|
63
|
-
j.write_text(text, encoding="utf-8")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|