@scanton/phase2s 0.25.0 → 1.0.0
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.
- package/README.md +174 -101
- package/dist/src/providers/codex.d.ts +8 -11
- package/dist/src/providers/codex.d.ts.map +1 -1
- package/dist/src/providers/codex.js +115 -118
- package/dist/src/providers/codex.js.map +1 -1
- package/package.json +20 -5
package/README.md
CHANGED
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
# Phase2S
|
|
2
2
|
|
|
3
|
-
Phase2S
|
|
3
|
+
Three things Phase2S does that most AI coding tools don't:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
1. **Runs on your ChatGPT subscription** — If you pay for ChatGPT Plus or Pro, you can use that same subscription to power a full coding assistant in your terminal. No API key, no per-token billing. The $20/month you're already paying starts pulling its weight.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
you > /review src/core/agent.ts
|
|
9
|
-
assistant > Reviewing src/core/agent.ts...
|
|
10
|
-
|
|
11
|
-
CRIT: The `maxTurns` check runs after tool execution, not before.
|
|
12
|
-
An LLM that loops tool calls can exceed the limit by one turn.
|
|
13
|
-
|
|
14
|
-
WARN: `getConversation()` returns the live object, not a copy.
|
|
15
|
-
Callers that mutate it will corrupt the conversation state.
|
|
7
|
+
2. **Gives Claude Code a second opinion** — If you use Claude Code as your daily driver, Phase2S plugs in as an MCP server and gives Claude a tool to call that runs your plan through GPT from a completely different model with completely different training. Before Claude executes anything big, it gets challenged by a second AI that has no stake in agreeing with it.
|
|
16
8
|
|
|
17
|
-
|
|
18
|
-
```
|
|
9
|
+
3. **Executes specs autonomously** — Write a spec describing what you want built and how you'll know it's done. Run `phase2s goal your-spec.md`. Phase2S breaks it into sub-tasks, implements each one, runs your tests, checks whether the results match your criteria, and retries with failure analysis if anything falls short. You come back when it's done.
|
|
19
10
|
|
|
20
11
|
---
|
|
21
12
|
|
|
@@ -23,9 +14,7 @@ assistant > Reviewing src/core/agent.ts...
|
|
|
23
14
|
|
|
24
15
|
Requires [Node.js](https://nodejs.org) >= 20.
|
|
25
16
|
|
|
26
|
-
**
|
|
27
|
-
|
|
28
|
-
No API key, no per-token billing. All 29 skills work.
|
|
17
|
+
**If you have ChatGPT Plus or Pro (recommended)**
|
|
29
18
|
|
|
30
19
|
```bash
|
|
31
20
|
npm install -g @openai/codex @scanton/phase2s
|
|
@@ -33,9 +22,9 @@ codex auth
|
|
|
33
22
|
phase2s
|
|
34
23
|
```
|
|
35
24
|
|
|
36
|
-
|
|
25
|
+
`codex auth` opens a browser window and logs into your ChatGPT account. You do it once. After that, `phase2s` uses your subscription automatically — no API keys, no credits to manage.
|
|
37
26
|
|
|
38
|
-
|
|
27
|
+
**If you have an OpenAI API key**
|
|
39
28
|
|
|
40
29
|
```bash
|
|
41
30
|
npm install -g @scanton/phase2s
|
|
@@ -44,9 +33,7 @@ export PHASE2S_PROVIDER=openai-api
|
|
|
44
33
|
phase2s
|
|
45
34
|
```
|
|
46
35
|
|
|
47
|
-
**
|
|
48
|
-
|
|
49
|
-
Run all 29 skills on Claude 3.5 Sonnet (or any Anthropic model).
|
|
36
|
+
**If you have an Anthropic API key**
|
|
50
37
|
|
|
51
38
|
```bash
|
|
52
39
|
npm install -g @scanton/phase2s
|
|
@@ -55,9 +42,7 @@ export PHASE2S_PROVIDER=anthropic
|
|
|
55
42
|
phase2s
|
|
56
43
|
```
|
|
57
44
|
|
|
58
|
-
**
|
|
59
|
-
|
|
60
|
-
No API keys. Runs entirely on your machine after the initial model pull.
|
|
45
|
+
**If you want to run everything locally (free, private, no internet)**
|
|
61
46
|
|
|
62
47
|
```bash
|
|
63
48
|
npm install -g @scanton/phase2s
|
|
@@ -68,119 +53,207 @@ phase2s
|
|
|
68
53
|
|
|
69
54
|
---
|
|
70
55
|
|
|
71
|
-
##
|
|
56
|
+
## Feature 1: Your ChatGPT subscription, in your terminal
|
|
72
57
|
|
|
73
|
-
|
|
58
|
+
Most people who pay for ChatGPT Plus use it by opening a browser tab and typing. Phase2S turns it into a programmable coding tool you can use from the command line, from scripts, and from inside Claude Code.
|
|
74
59
|
|
|
75
60
|
```
|
|
76
|
-
you > /review src/core/
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
61
|
+
you > /review src/core/auth.ts
|
|
62
|
+
|
|
63
|
+
CRIT: session.destroy() is called without await on line 83.
|
|
64
|
+
If it rejects (Redis timeout, etc.), the error is silently dropped
|
|
65
|
+
and the response goes out before the session is actually cleared.
|
|
66
|
+
|
|
67
|
+
WARN: The JWT expiry check on line 47 uses Date.now() directly.
|
|
68
|
+
Clock skew between your server and the token issuer can cause
|
|
69
|
+
valid tokens to fail. Use a small leeway (±30s) instead.
|
|
70
|
+
|
|
71
|
+
NIT: The error message on line 91 says "auth failed" but should say
|
|
72
|
+
"session_expired" to match the error codes in api-errors.ts.
|
|
81
73
|
```
|
|
82
74
|
|
|
83
|
-
|
|
75
|
+
```
|
|
76
|
+
you > /satori add rate limiting to the API
|
|
84
77
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
78
|
+
-- Attempt 1 --
|
|
79
|
+
[creates src/utils/rate-limiter.ts]
|
|
80
|
+
[creates src/middleware/rate-limit.ts]
|
|
81
|
+
[registers middleware in app.ts]
|
|
82
|
+
[writes tests in test/middleware/rate-limit.test.ts]
|
|
83
|
+
npm test: FAIL — bucket not clearing on window expiry
|
|
84
|
+
|
|
85
|
+
-- Attempt 2 --
|
|
86
|
+
[fixes resetAt logic in RateLimiter.check()]
|
|
87
|
+
npm test: PASS (23 tests)
|
|
88
|
+
|
|
89
|
+
Done in 2 attempts.
|
|
88
90
|
```
|
|
89
91
|
|
|
90
|
-
|
|
92
|
+
The 29 built-in skills cover the full development loop: specify, plan, implement, test, review, debug, ship, deploy. All of them run on your subscription.
|
|
93
|
+
|
|
94
|
+
[Full skill list →](docs/skills.md)
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Feature 2: Claude Code + Phase2S adversarial review
|
|
99
|
+
|
|
100
|
+
If you use Claude Code, here's the problem: one model reviewing its own work has blind spots. Claude agrees with Claude. The same training data, the same biases, the same failure modes.
|
|
101
|
+
|
|
102
|
+
Phase2S solves this by plugging into Claude Code as an MCP server. When Claude is about to execute a plan, it can call Phase2S — which runs the same plan through GPT using your ChatGPT subscription — and get back a structured challenge:
|
|
91
103
|
|
|
92
|
-
```bash
|
|
93
|
-
phase2s run "/explain src/core/agent.ts" # routes through the explain skill
|
|
94
|
-
phase2s run --dry-run "/satori add auth" # preview which skill + model, no execution
|
|
95
104
|
```
|
|
105
|
+
VERDICT: CHALLENGED
|
|
106
|
+
STRONGEST_CONCERN: The token bucket resets per-request rather than per-window.
|
|
107
|
+
OBJECTIONS:
|
|
108
|
+
1. RateLimiter.check() increments the counter and checks it in the same call.
|
|
109
|
+
When the window expires, the bucket resets on the next request — meaning
|
|
110
|
+
a client can always make exactly one request immediately after the window
|
|
111
|
+
closes, even if they were throttled. The reset should happen on a fixed
|
|
112
|
+
schedule, not lazily on first request.
|
|
113
|
+
2. The middleware is registered after the auth middleware in app.ts line 34.
|
|
114
|
+
Unauthenticated requests bypass rate limiting entirely.
|
|
115
|
+
APPROVE_IF: Fix the window reset logic and move middleware before auth.
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Claude gets specific, falsifiable objections from a model that wasn't involved in writing the plan. You see the verdict. You decide whether to proceed.
|
|
96
119
|
|
|
97
|
-
|
|
120
|
+
**Setup takes about 2 minutes.** [Step-by-step guide →](docs/claude-code.md)
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Feature 3: The dark factory
|
|
125
|
+
|
|
126
|
+
Write a spec. Run one command. Come back when it's done.
|
|
127
|
+
|
|
128
|
+
`phase2s goal` reads your spec, breaks it into sub-tasks, implements each one through the `/satori` skill (which runs implement → test → retry until green), runs your eval command, checks whether your acceptance criteria actually passed, and if they didn't — analyzes what broke, figures out which sub-tasks need to be re-run, and tries again with that failure context.
|
|
129
|
+
|
|
130
|
+
It keeps going until all criteria pass or it runs out of attempts.
|
|
98
131
|
|
|
99
132
|
```bash
|
|
100
|
-
|
|
101
|
-
phase2s
|
|
133
|
+
# Write the spec interactively
|
|
134
|
+
phase2s
|
|
135
|
+
you > /deep-specify add pagination to the search endpoint
|
|
136
|
+
|
|
137
|
+
# Execute it autonomously
|
|
138
|
+
phase2s goal .phase2s/specs/2026-04-04-11-00-pagination.md
|
|
102
139
|
```
|
|
103
140
|
|
|
104
|
-
|
|
141
|
+
```
|
|
142
|
+
Goal executor: Pagination for search endpoint
|
|
143
|
+
Sub-tasks: 3 | Eval: npm test | Max attempts: 3
|
|
105
144
|
|
|
106
|
-
|
|
107
|
-
|
|
145
|
+
=== Attempt 1/3 ===
|
|
146
|
+
Running sub-task: Cursor-based pagination logic
|
|
147
|
+
[satori: implement → test → retry until green]
|
|
148
|
+
|
|
149
|
+
Running sub-task: API response format update
|
|
150
|
+
[satori: implement → test → retry until green]
|
|
151
|
+
|
|
152
|
+
Running sub-task: Frontend page controls
|
|
153
|
+
[satori: implement → test → retry until green]
|
|
154
|
+
|
|
155
|
+
Running evaluation: npm test
|
|
156
|
+
|
|
157
|
+
Acceptance criteria:
|
|
158
|
+
✗ Returns correct next_cursor on paginated results
|
|
159
|
+
✓ Returns 20 items per page by default
|
|
160
|
+
✓ next_cursor is null on last page
|
|
161
|
+
|
|
162
|
+
Retrying 1 sub-task(s): Cursor-based pagination logic
|
|
163
|
+
|
|
164
|
+
=== Attempt 2/3 ===
|
|
165
|
+
Running sub-task: Cursor-based pagination logic
|
|
166
|
+
[satori: implement → test → retry until green]
|
|
167
|
+
|
|
168
|
+
Running evaluation: npm test
|
|
169
|
+
|
|
170
|
+
Acceptance criteria:
|
|
171
|
+
✓ Returns correct next_cursor on paginated results
|
|
172
|
+
✓ Returns 20 items per page by default
|
|
173
|
+
✓ next_cursor is null on last page
|
|
174
|
+
|
|
175
|
+
✓ All acceptance criteria met after 2 attempt(s).
|
|
108
176
|
```
|
|
109
177
|
|
|
110
|
-
|
|
178
|
+
This uses your ChatGPT subscription for all the implementation work. No API key needed.
|
|
111
179
|
|
|
112
|
-
|
|
180
|
+
[Full dark factory guide →](docs/dark-factory.md)
|
|
113
181
|
|
|
114
|
-
|
|
182
|
+
---
|
|
115
183
|
|
|
116
|
-
|
|
117
|
-
- `/consensus-plan` — planner + architect + critic passes before producing a plan. Catches the errors that only show up in implementation.
|
|
118
|
-
- `/deep-specify` — Socratic interview before writing code. Saves a 5-pillar spec (Problem Statement, Acceptance Criteria, Constraints, Decomposition, Eval Design) consumable by `phase2s goal`.
|
|
119
|
-
- `/debug` — reproduce, isolate, fix, and verify a bug end-to-end.
|
|
120
|
-
- `/remember` — save project conventions to persistent memory. Injected into every future session automatically.
|
|
121
|
-
- `/skill` — create a new `/command` from inside Phase2S. Three questions, no YAML editing.
|
|
122
|
-
- `/land-and-deploy` — push, open a PR, merge it, wait for CI, confirm the land. Picks up where `/ship` leaves off.
|
|
184
|
+
## All 29 skills
|
|
123
185
|
|
|
124
|
-
|
|
186
|
+
```
|
|
187
|
+
you > /review src/auth.ts — code review: CRIT / WARN / NIT
|
|
188
|
+
you > /diff — review all uncommitted changes
|
|
189
|
+
you > /satori add pagination — implement + test + retry until green
|
|
190
|
+
you > /deep-specify add OAuth — spec interview → 5-pillar spec file
|
|
191
|
+
you > /consensus-plan add OAuth — planner + architect + critic passes
|
|
192
|
+
you > /debug logout fails — reproduce, isolate, fix, verify
|
|
193
|
+
you > /investigate why 500s — evidence trail to root cause
|
|
194
|
+
you > /health — code quality score (tests, types, lint)
|
|
195
|
+
you > /audit — secrets scan, dependency CVEs, injection
|
|
196
|
+
you > /ship — diff review + commit message
|
|
197
|
+
you > /land-and-deploy — push, PR, CI wait, merge
|
|
198
|
+
you > /remember — save a project convention to memory
|
|
199
|
+
you > /retro — weekly velocity and pattern analysis
|
|
200
|
+
```
|
|
125
201
|
|
|
126
202
|
```bash
|
|
127
|
-
phase2s skills
|
|
203
|
+
phase2s skills # full list with model tier badges
|
|
204
|
+
phase2s skills --json # machine-readable for scripts
|
|
128
205
|
```
|
|
129
206
|
|
|
207
|
+
[Skills reference →](docs/skills.md)
|
|
208
|
+
|
|
130
209
|
---
|
|
131
210
|
|
|
132
211
|
## Docs
|
|
133
212
|
|
|
134
|
-
- [
|
|
135
|
-
- [
|
|
136
|
-
- [
|
|
137
|
-
- [
|
|
138
|
-
- [
|
|
139
|
-
- [
|
|
140
|
-
- [
|
|
141
|
-
- [
|
|
142
|
-
- [
|
|
213
|
+
- [Getting started](docs/getting-started.md) — first install, first session, all four provider options
|
|
214
|
+
- [Dark factory](docs/dark-factory.md) — write a spec, run `phase2s goal`, get a feature
|
|
215
|
+
- [Claude Code integration](docs/claude-code.md) — MCP setup, adversarial review, CLAUDE.md routing rules
|
|
216
|
+
- [Skills reference](docs/skills.md) — all 29 skills with examples
|
|
217
|
+
- [Workflows](docs/workflows.md) — real development sessions end to end
|
|
218
|
+
- [Memory and persistence](docs/memory.md) — session resume, `/remember`, what gets saved
|
|
219
|
+
- [Writing custom skills](docs/writing-skills.md) — create your own `/commands`
|
|
220
|
+
- [GitHub Action](docs/github-action.md) — `uses: scanton/phase2s@v1` for CI (requires API key)
|
|
221
|
+
- [Advanced](docs/advanced.md) — streaming, model routing, tool allow/deny
|
|
222
|
+
- [Configuration](docs/configuration.md) — `.phase2s.yaml` and environment variables
|
|
143
223
|
|
|
144
224
|
---
|
|
145
225
|
|
|
146
226
|
## Roadmap
|
|
147
227
|
|
|
148
|
-
- [x] Codex CLI provider (
|
|
228
|
+
- [x] Codex CLI provider (ChatGPT subscription, no API key required)
|
|
149
229
|
- [x] 29 built-in skills across 6 categories
|
|
150
|
-
- [x]
|
|
151
|
-
- [x]
|
|
152
|
-
- [x]
|
|
153
|
-
- [x]
|
|
154
|
-
- [x]
|
|
155
|
-
- [x]
|
|
156
|
-
- [x]
|
|
157
|
-
- [x]
|
|
158
|
-
- [x]
|
|
159
|
-
- [x]
|
|
160
|
-
- [x]
|
|
161
|
-
- [x]
|
|
162
|
-
- [x]
|
|
163
|
-
- [x]
|
|
164
|
-
- [x]
|
|
165
|
-
- [x] `/
|
|
166
|
-
- [x]
|
|
167
|
-
- [x]
|
|
168
|
-
- [x]
|
|
169
|
-
- [x]
|
|
170
|
-
- [x]
|
|
171
|
-
- [x]
|
|
172
|
-
- [x]
|
|
173
|
-
- [x]
|
|
174
|
-
- [x]
|
|
175
|
-
- [x]
|
|
176
|
-
- [x]
|
|
177
|
-
- [x] Shell completion — `eval "$(phase2s completion bash)"` for tab-complete in bash/zsh
|
|
178
|
-
- [x] Tool allow/deny — `tools:` and `deny:` in `.phase2s.yaml` restrict agent tool access
|
|
179
|
-
- [x] Headless browser tool — navigate, click, type, screenshot, evaluate JS via Playwright (opt-in: `browser: true`)
|
|
180
|
-
- [x] GitHub Action — `uses: scanton/phase2s@v1` runs any skill in CI, posts results as PR comments and Step Summaries (requires Anthropic or OpenAI API key — ChatGPT subscription doesn't work in CI)
|
|
181
|
-
- [x] `phase2s goal <spec.md>` — dark factory mode: give Phase2S a spec, have it execute sub-tasks, run evals, and retry until acceptance criteria pass (uses ChatGPT subscription)
|
|
182
|
-
- [x] 5-pillar spec format — `/deep-specify` outputs structured specs consumable by `phase2s goal`
|
|
183
|
-
- [ ] Real Codex streaming (JSONL stdout parsing)
|
|
230
|
+
- [x] File sandbox: tools reject paths outside project directory, including symlink escapes
|
|
231
|
+
- [x] 399 tests covering all tools, core modules, agent integration, and goal executor
|
|
232
|
+
- [x] CI: runs `npm test` on every push and PR
|
|
233
|
+
- [x] OpenAI API provider with live tool calling
|
|
234
|
+
- [x] Anthropic API provider — Claude 3.5 Sonnet and family
|
|
235
|
+
- [x] Ollama provider — local models, offline, no API keys
|
|
236
|
+
- [x] Streaming output
|
|
237
|
+
- [x] Session persistence — auto-save + `--resume`
|
|
238
|
+
- [x] Model-per-skill routing — `fast_model` / `smart_model` tiers
|
|
239
|
+
- [x] Satori persistent execution — retry loop with shell verification
|
|
240
|
+
- [x] Consensus planning — planner + architect + critic
|
|
241
|
+
- [x] Claude Code MCP integration — all skills as Claude Code tools
|
|
242
|
+
- [x] `/adversarial` — cross-model adversarial review
|
|
243
|
+
- [x] Persistent memory — `/remember` + auto-inject into sessions
|
|
244
|
+
- [x] `/skill` — create new skills from inside Phase2S
|
|
245
|
+
- [x] `/land-and-deploy` — push, PR, CI wait, merge
|
|
246
|
+
- [x] Model tier badges in `phase2s skills` output
|
|
247
|
+
- [x] `--dry-run` for skill routing preview
|
|
248
|
+
- [x] Typed input hints in REPL
|
|
249
|
+
- [x] `phase2s skills --json`
|
|
250
|
+
- [x] Shell completion — `eval "$(phase2s completion bash)"`
|
|
251
|
+
- [x] Tool allow/deny in `.phase2s.yaml`
|
|
252
|
+
- [x] Headless browser tool via Playwright
|
|
253
|
+
- [x] GitHub Action — `uses: scanton/phase2s@v1`
|
|
254
|
+
- [x] `phase2s goal <spec.md>` — dark factory: spec in, feature out
|
|
255
|
+
- [x] 5-pillar spec format — `/deep-specify` output feeds directly into `phase2s goal`
|
|
256
|
+
- [x] Real Codex streaming (JSONL stdout parsing) — step-by-step feedback for multi-step tasks
|
|
184
257
|
|
|
185
258
|
---
|
|
186
259
|
|
|
@@ -5,24 +5,21 @@ import type { OpenAIFunctionDef } from "../tools/types.js";
|
|
|
5
5
|
* Codex CLI provider.
|
|
6
6
|
*
|
|
7
7
|
* Uses `codex exec` in fully non-interactive scripting mode:
|
|
8
|
-
* --json
|
|
9
|
-
* --output-last-message writes the final response to a temp file
|
|
8
|
+
* --json suppresses the terminal UI, outputs JSONL events on stdout
|
|
10
9
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* Each `item.completed` event with `type: "agent_message"` is yielded immediately
|
|
11
|
+
* as a `{ type: "text" }` ProviderEvent. Multi-step tasks (with tool calls) produce
|
|
12
|
+
* multiple agent_message items, so callers see real-time step-by-step progress rather
|
|
13
|
+
* than waiting for the entire run to finish.
|
|
13
14
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* (same batch UX as before, but through the Provider streaming interface).
|
|
17
|
-
* Tool calling is not supported via the --output-last-message mechanism;
|
|
18
|
-
* toolCalls is always [].
|
|
15
|
+
* Malformed JSONL lines are silently skipped.
|
|
16
|
+
* Tool calling is not surfaced via this mechanism; toolCalls is always [].
|
|
19
17
|
*/
|
|
20
18
|
export declare class CodexProvider implements Provider {
|
|
21
19
|
name: string;
|
|
22
20
|
private codexPath;
|
|
23
21
|
private model;
|
|
24
22
|
constructor(config: Config);
|
|
25
|
-
chatStream(messages: Message[],
|
|
26
|
-
private _chat;
|
|
23
|
+
chatStream(messages: Message[], _tools: OpenAIFunctionDef[], options?: import("./types.js").ChatStreamOptions): AsyncIterable<ProviderEvent>;
|
|
27
24
|
}
|
|
28
25
|
//# sourceMappingURL=codex.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../../src/providers/codex.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../../src/providers/codex.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAkD3D;;;;;;;;;;;;;GAaG;AACH,qBAAa,aAAc,YAAW,QAAQ;IAC5C,IAAI,SAAe;IACnB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,MAAM;IAKnB,UAAU,CACf,QAAQ,EAAE,OAAO,EAAE,EACnB,MAAM,EAAE,iBAAiB,EAAE,EAC3B,OAAO,CAAC,EAAE,OAAO,YAAY,EAAE,iBAAiB,GAC/C,aAAa,CAAC,aAAa,CAAC;CAuIhC"}
|
|
@@ -1,56 +1,20 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { tmpdir } from "node:os";
|
|
6
|
-
/** Track all temp dirs created this process so we can clean up on crash/exit. */
|
|
7
|
-
const activeTempDirs = new Set();
|
|
8
|
-
function cleanupTempDirs() {
|
|
9
|
-
// Synchronous cleanup — rmSync is available here (unlike the async rm).
|
|
10
|
-
for (const dir of activeTempDirs) {
|
|
11
|
-
try {
|
|
12
|
-
rmSync(dir, { recursive: true, force: true });
|
|
13
|
-
}
|
|
14
|
-
catch {
|
|
15
|
-
// Best-effort — ignore errors (e.g. already deleted by normal path)
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Guard against double-registration if the module is evaluated multiple times.
|
|
21
|
-
* In vitest environments, modules may be re-evaluated between test files, which
|
|
22
|
-
* would register duplicate handlers and trigger MaxListenersExceededWarning.
|
|
23
|
-
*/
|
|
24
|
-
let _signalHandlersRegistered = false;
|
|
25
|
-
if (!_signalHandlersRegistered) {
|
|
26
|
-
_signalHandlersRegistered = true;
|
|
27
|
-
process.on("exit", cleanupTempDirs);
|
|
28
|
-
// SIGTERM and SIGINT don't trigger "exit" automatically — register them explicitly
|
|
29
|
-
// so that temp dirs (which may contain prompt text) are cleaned up on Ctrl+C and kill.
|
|
30
|
-
process.on("SIGTERM", () => {
|
|
31
|
-
cleanupTempDirs();
|
|
32
|
-
process.exit(0);
|
|
33
|
-
});
|
|
34
|
-
process.on("SIGINT", () => {
|
|
35
|
-
cleanupTempDirs();
|
|
36
|
-
process.exit(0);
|
|
37
|
-
});
|
|
2
|
+
function isAgentMessage(item) {
|
|
3
|
+
return item.type === "agent_message" && "text" in item;
|
|
38
4
|
}
|
|
39
5
|
/**
|
|
40
6
|
* Codex CLI provider.
|
|
41
7
|
*
|
|
42
8
|
* Uses `codex exec` in fully non-interactive scripting mode:
|
|
43
|
-
* --json
|
|
44
|
-
* --output-last-message writes the final response to a temp file
|
|
9
|
+
* --json suppresses the terminal UI, outputs JSONL events on stdout
|
|
45
10
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
11
|
+
* Each `item.completed` event with `type: "agent_message"` is yielded immediately
|
|
12
|
+
* as a `{ type: "text" }` ProviderEvent. Multi-step tasks (with tool calls) produce
|
|
13
|
+
* multiple agent_message items, so callers see real-time step-by-step progress rather
|
|
14
|
+
* than waiting for the entire run to finish.
|
|
48
15
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* (same batch UX as before, but through the Provider streaming interface).
|
|
52
|
-
* Tool calling is not supported via the --output-last-message mechanism;
|
|
53
|
-
* toolCalls is always [].
|
|
16
|
+
* Malformed JSONL lines are silently skipped.
|
|
17
|
+
* Tool calling is not surfaced via this mechanism; toolCalls is always [].
|
|
54
18
|
*/
|
|
55
19
|
export class CodexProvider {
|
|
56
20
|
name = "codex-cli";
|
|
@@ -60,22 +24,8 @@ export class CodexProvider {
|
|
|
60
24
|
this.codexPath = config.codexPath;
|
|
61
25
|
this.model = config.model;
|
|
62
26
|
}
|
|
63
|
-
async *chatStream(messages,
|
|
64
|
-
const
|
|
65
|
-
if (result.text) {
|
|
66
|
-
yield { type: "text", content: result.text };
|
|
67
|
-
}
|
|
68
|
-
// Codex provider currently always returns toolCalls: [] — tool calling
|
|
69
|
-
// is not supported via the --output-last-message mechanism.
|
|
70
|
-
if (result.toolCalls.length > 0) {
|
|
71
|
-
yield { type: "tool_calls", calls: result.toolCalls };
|
|
72
|
-
yield { type: "done", stopReason: "tool_calls" };
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
yield { type: "done", stopReason: "stop" };
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
async _chat(messages, _tools, modelOverride) {
|
|
27
|
+
async *chatStream(messages, _tools, options) {
|
|
28
|
+
const model = options?.model ?? this.model;
|
|
79
29
|
// Build the full prompt: system context + conversation history
|
|
80
30
|
const parts = [];
|
|
81
31
|
const systemMessages = messages.filter((m) => m.role === "system");
|
|
@@ -83,8 +33,7 @@ export class CodexProvider {
|
|
|
83
33
|
parts.push(systemMessages.map((m) => m.content).join("\n"));
|
|
84
34
|
parts.push("---");
|
|
85
35
|
}
|
|
86
|
-
const
|
|
87
|
-
for (const msg of nonSystem) {
|
|
36
|
+
for (const msg of messages.filter((m) => m.role !== "system")) {
|
|
88
37
|
if (msg.role === "user") {
|
|
89
38
|
parts.push(`User: ${msg.content}`);
|
|
90
39
|
}
|
|
@@ -93,70 +42,118 @@ export class CodexProvider {
|
|
|
93
42
|
}
|
|
94
43
|
}
|
|
95
44
|
const prompt = parts.join("\n\n");
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const outputFile = join(tmpDir, "last-message.txt");
|
|
100
|
-
// codex exec --json suppresses the interactive UI (no /dev/tty access)
|
|
101
|
-
// --output-last-message writes the final response to a file we control
|
|
102
|
-
//
|
|
103
|
-
// The "--" separator signals end-of-flags to codex's own arg parser.
|
|
104
|
-
// Without it, a prompt beginning with "--" (e.g. "--help" or "--flags")
|
|
105
|
-
// would be misinterpreted as a codex CLI flag rather than as the prompt.
|
|
106
|
-
// spawn() with an array is NOT shell-injected, so this is the only risk.
|
|
45
|
+
// codex exec --json suppresses the interactive UI and outputs JSONL on stdout.
|
|
46
|
+
// The "--" separator signals end-of-flags so prompts beginning with "--" are safe.
|
|
47
|
+
// spawn() with an array is NOT shell-injected, so this is the only prompt-injection risk.
|
|
107
48
|
const args = [
|
|
108
49
|
"exec",
|
|
109
|
-
"-m",
|
|
50
|
+
"-m", model,
|
|
110
51
|
"--full-auto",
|
|
111
52
|
"-C", process.cwd(),
|
|
112
53
|
"--json",
|
|
113
|
-
"--output-last-message", outputFile,
|
|
114
54
|
"--",
|
|
115
55
|
prompt,
|
|
116
56
|
];
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
env
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
},
|
|
125
|
-
});
|
|
126
|
-
let stderr = "";
|
|
127
|
-
// Consume stdout (JSONL events) so the pipe buffer never fills and
|
|
128
|
-
// blocks codex. We don't parse it — we use --output-last-message instead.
|
|
129
|
-
proc.stdout.resume();
|
|
130
|
-
proc.stderr.on("data", (data) => {
|
|
131
|
-
stderr += data.toString();
|
|
132
|
-
});
|
|
133
|
-
proc.on("close", async (code) => {
|
|
134
|
-
// Try reading the output file first (most reliable)
|
|
135
|
-
try {
|
|
136
|
-
const text = await readFile(outputFile, "utf-8");
|
|
137
|
-
await rm(tmpDir, { recursive: true }).catch(() => { });
|
|
138
|
-
activeTempDirs.delete(tmpDir);
|
|
139
|
-
resolve({ text: text.trim(), toolCalls: [] });
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
catch {
|
|
143
|
-
// Output file missing — fall through to error handling
|
|
144
|
-
}
|
|
145
|
-
await rm(tmpDir, { recursive: true }).catch(() => { });
|
|
146
|
-
activeTempDirs.delete(tmpDir);
|
|
147
|
-
if (code !== 0) {
|
|
148
|
-
reject(new Error(`Codex exited with code ${code}: ${stderr.trim()}`));
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
// Unexpected: exited 0 but no output file
|
|
152
|
-
reject(new Error("Codex produced no output"));
|
|
153
|
-
});
|
|
154
|
-
proc.on("error", async (err) => {
|
|
155
|
-
await rm(tmpDir, { recursive: true }).catch(() => { });
|
|
156
|
-
activeTempDirs.delete(tmpDir);
|
|
157
|
-
reject(new Error(`Failed to spawn codex: ${err.message}`));
|
|
158
|
-
});
|
|
57
|
+
const proc = spawn(this.codexPath, args, {
|
|
58
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
59
|
+
env: {
|
|
60
|
+
...process.env,
|
|
61
|
+
NO_COLOR: "1",
|
|
62
|
+
FORCE_COLOR: "0",
|
|
63
|
+
},
|
|
159
64
|
});
|
|
65
|
+
// -------------------------------------------------------------------------
|
|
66
|
+
// Async queue: bridges the event-emitter world → async generator.
|
|
67
|
+
//
|
|
68
|
+
// We can't `yield` from inside event handlers, so we push events into a
|
|
69
|
+
// queue and use a one-shot resolve callback to wake the generator loop.
|
|
70
|
+
// -------------------------------------------------------------------------
|
|
71
|
+
const pendingEvents = [];
|
|
72
|
+
let finished = false;
|
|
73
|
+
let finishError = null;
|
|
74
|
+
let wakeUp = null;
|
|
75
|
+
let hasProducedText = false;
|
|
76
|
+
const push = (event) => {
|
|
77
|
+
pendingEvents.push(event);
|
|
78
|
+
wakeUp?.();
|
|
79
|
+
wakeUp = null;
|
|
80
|
+
};
|
|
81
|
+
const finish = (err) => {
|
|
82
|
+
if (finished)
|
|
83
|
+
return; // Guard: called at most once
|
|
84
|
+
finished = true;
|
|
85
|
+
finishError = err ?? null;
|
|
86
|
+
wakeUp?.();
|
|
87
|
+
wakeUp = null;
|
|
88
|
+
};
|
|
89
|
+
// JSONL line parser — called for each complete line from stdout
|
|
90
|
+
const processLine = (line) => {
|
|
91
|
+
if (!line)
|
|
92
|
+
return;
|
|
93
|
+
let evt;
|
|
94
|
+
try {
|
|
95
|
+
evt = JSON.parse(line);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Silent fallback: malformed JSONL line, skip it
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (evt.type === "item.completed" && isAgentMessage(evt.item)) {
|
|
102
|
+
hasProducedText = true;
|
|
103
|
+
push({ type: "text", content: evt.item.text });
|
|
104
|
+
}
|
|
105
|
+
else if (evt.type === "error") {
|
|
106
|
+
finish(new Error(`Codex error: ${evt.message}`));
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
// Stream stdout, splitting on newlines as chunks arrive
|
|
110
|
+
let lineBuffer = "";
|
|
111
|
+
proc.stdout.on("data", (chunk) => {
|
|
112
|
+
lineBuffer += chunk.toString();
|
|
113
|
+
let newlineIdx;
|
|
114
|
+
while ((newlineIdx = lineBuffer.indexOf("\n")) !== -1) {
|
|
115
|
+
processLine(lineBuffer.slice(0, newlineIdx).trim());
|
|
116
|
+
lineBuffer = lineBuffer.slice(newlineIdx + 1);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
// Consume stderr so the pipe buffer never fills and blocks codex.
|
|
120
|
+
proc.stderr.resume();
|
|
121
|
+
proc.on("close", (code) => {
|
|
122
|
+
if (finished)
|
|
123
|
+
return;
|
|
124
|
+
// Flush any remaining content without a trailing newline
|
|
125
|
+
const remaining = lineBuffer.trim();
|
|
126
|
+
if (remaining)
|
|
127
|
+
processLine(remaining);
|
|
128
|
+
if (!hasProducedText && code !== 0) {
|
|
129
|
+
finish(new Error(`Codex exited with code ${code ?? "null"}`));
|
|
130
|
+
}
|
|
131
|
+
else if (!hasProducedText) {
|
|
132
|
+
finish(new Error("Codex produced no output"));
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
finish();
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
proc.on("error", (err) => {
|
|
139
|
+
finish(new Error(`Failed to spawn codex: ${err.message}`));
|
|
140
|
+
});
|
|
141
|
+
// Generator loop: drain the queue, wait for more, repeat until done
|
|
142
|
+
while (true) {
|
|
143
|
+
while (pendingEvents.length > 0) {
|
|
144
|
+
yield pendingEvents.shift();
|
|
145
|
+
}
|
|
146
|
+
if (finished) {
|
|
147
|
+
if (finishError)
|
|
148
|
+
throw finishError;
|
|
149
|
+
yield { type: "done", stopReason: "stop" };
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
// Wait for the next push() or finish() call
|
|
153
|
+
await new Promise((resolve) => {
|
|
154
|
+
wakeUp = resolve;
|
|
155
|
+
});
|
|
156
|
+
}
|
|
160
157
|
}
|
|
161
158
|
}
|
|
162
159
|
//# sourceMappingURL=codex.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../../src/providers/codex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../../src/providers/codex.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scanton/phase2s",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "AI
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AI coding assistant with 29 built-in skills, ChatGPT subscription support, and autonomous spec execution",
|
|
5
|
+
"author": "Scott Canton",
|
|
6
|
+
"homepage": "https://github.com/scanton/phase-2-s",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/scanton/phase-2-s.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/scanton/phase-2-s/issues"
|
|
13
|
+
},
|
|
5
14
|
"type": "module",
|
|
6
15
|
"files": [
|
|
7
16
|
"dist/",
|
|
@@ -36,8 +45,14 @@
|
|
|
36
45
|
"ai",
|
|
37
46
|
"codex",
|
|
38
47
|
"openai",
|
|
48
|
+
"anthropic",
|
|
49
|
+
"claude",
|
|
50
|
+
"chatgpt",
|
|
39
51
|
"cli",
|
|
40
|
-
"agent"
|
|
52
|
+
"agent",
|
|
53
|
+
"mcp",
|
|
54
|
+
"coding-assistant",
|
|
55
|
+
"dark-factory"
|
|
41
56
|
],
|
|
42
57
|
"license": "MIT",
|
|
43
58
|
"dependencies": {
|
|
@@ -52,9 +67,9 @@
|
|
|
52
67
|
"zod": "^3.23.0"
|
|
53
68
|
},
|
|
54
69
|
"devDependencies": {
|
|
55
|
-
"@actions/core": "^
|
|
70
|
+
"@actions/core": "^3.0.0",
|
|
56
71
|
"@actions/exec": "^1.1.1",
|
|
57
|
-
"@actions/github": "^
|
|
72
|
+
"@actions/github": "^9.0.0",
|
|
58
73
|
"@types/node": "^22.9.0",
|
|
59
74
|
"@vercel/ncc": "^0.38.4",
|
|
60
75
|
"playwright": ">=1.40.0",
|