solo-cto-agent 1.3.2 → 1.4.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/CHANGELOG.md +61 -0
- package/README.md +96 -31
- package/bin/cli.js +118 -3
- package/bin/do.js +210 -0
- package/bin/lib/nl-orchestrator.js +237 -0
- package/bin/lib/telegram-commands.js +759 -0
- package/bin/repo-discovery.js +234 -0
- package/bin/telegram-bot.js +154 -5
- package/bin/wizard.js +39 -1
- package/docs/codex-main-install.md +36 -12
- package/docs/hero-banner-prompt.md +85 -0
- package/docs/user-journey.md +369 -0
- package/package.json +2 -2
- package/skills/orchestrate/SKILL.md +30 -16
- package/templates/orchestrator/.github/workflows/auto-diagnose.yml +15 -1
- package/templates/orchestrator/.github/workflows/combined-pr-with-uiux.yml +102 -69
- package/templates/orchestrator/.github/workflows/cross-review-dispatch.yml +11 -0
- package/templates/orchestrator/.github/workflows/cross-review.yml +4 -0
- package/templates/orchestrator/.github/workflows/nl-processor.yml +59 -0
- package/templates/orchestrator/.github/workflows/pr-merge-notify.yml +86 -0
- package/templates/orchestrator/.github/workflows/rework-auto.yml +24 -0
- package/templates/orchestrator/.github/workflows/visual-check.yml +13 -3
- package/templates/orchestrator/.github/workflows/visual-report.yml +179 -0
- package/templates/orchestrator/api/telegram-commands.js +759 -0
- package/templates/orchestrator/api/telegram-webhook.js +84 -0
- package/templates/orchestrator/ops/agents/claude-reviewer.js +1 -1
- package/templates/orchestrator/ops/agents/claude-worker.js +1 -1
- package/templates/orchestrator/ops/agents/cross-reviewer.js +693 -68
- package/templates/orchestrator/ops/agents/nl-processor.js +165 -0
- package/templates/orchestrator/ops/agents/rework-agent.js +178 -8
- package/templates/orchestrator/ops/agents/visual-reporter.js +445 -0
- package/templates/orchestrator/ops/lib/nl-orchestrator.js +237 -0
- package/templates/orchestrator/ops/lib/route-detection.js +135 -0
- package/templates/orchestrator/ops/lib/screenshot-providers/browserless-provider.js +72 -0
- package/templates/orchestrator/ops/lib/screenshot-providers/playwright-provider.js +67 -0
- package/templates/orchestrator/ops/scripts/auto-diagnose.js +80 -13
- package/templates/orchestrator/ops/scripts/visual-check.js +66 -13
- package/templates/product-repo/.github/workflows/solo-cto-pipeline.yml +50 -22
- package/tiers.json +7 -5
- package/templates/product-repo/.github/workflows/cross-review-dispatch.yml +0 -27
- package/templates/product-repo/.github/workflows/rework-dispatch.yml +0 -51
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.4.0 (2026-04-19)
|
|
4
|
+
|
|
5
|
+
**Theme**: End-to-end automation complete. Install is fully automatic; review → rework → visual → merge runs without human copy-paste; Telegram / Discord carry the full operational loop.
|
|
6
|
+
|
|
7
|
+
### Highlights
|
|
8
|
+
* **Natural-language work orders** — `solo-cto-agent do "..."` CLI + Telegram `/do` route a plain-English instruction to the right product repo as a labeled, spec-rich issue the existing worker pipeline picks up.
|
|
9
|
+
* **3-round agent consensus** — `cross-reviewer.js` runs an A/B debate (R1 propose → R2 agree/disagree/add → R3 verdict) with early-exit on agreement; non-consensus after R3 still dispatches rework with a distinguishable reason.
|
|
10
|
+
* **Before/After visual report** — new `visual-report.yml` + `visual-reporter.js` capture screenshots of the Vercel preview at the pre- and post-rework SHA, compose side-by-side PNGs, commit them to the orchestrator, post to PR + Telegram `sendMediaGroup`.
|
|
11
|
+
* **Opt-in GitHub auto-merge** — PR with `auto-merge-when-ready` label is merged by GitHub the moment all required checks pass (native `enablePullRequestAutoMerge` mutation; branch protection respected).
|
|
12
|
+
* **Unified dispatcher** — `solo-cto-pipeline.yml` is now the single product-repo dispatcher with 7-layer anti-loop guards. Legacy `cross-review-dispatch.yml` + `rework-dispatch.yml` deleted; concurrency guards added on orchestrator receivers.
|
|
13
|
+
* **Full install automation** — `setup.sh` now creates the orchestrator repo on GitHub, pushes it, and sets the `TRACKED_REPOS` variable itself instead of printing copy-paste commands.
|
|
14
|
+
* **Telegram CTO command surface** — `/status`, `/list`, `/rework`, `/approve`, `/do`, `/digest`, `/merge` (admin-gated). Every review / rework / report message includes inline buttons for ✅ Approve · ❌ Reject · 🔧 Rework · 🔀 Merge.
|
|
15
|
+
* **Discord mirror** — set `DISCORD_WEBHOOK_URL` and visual-change screenshots / auto-diagnose reports mirror to Discord as file attachments.
|
|
16
|
+
* **Repo auto-discovery** — `init --wizard` shells out to `gh api` and offers a multi-select of the user's repos; saved selection auto-fills `--repos` on every subsequent command.
|
|
17
|
+
|
|
18
|
+
### Pipeline fixes
|
|
19
|
+
* `review-request` dispatch no longer orphaned — solo-cto-pipeline now emits `cross-review` to match the existing orchestrator listener.
|
|
20
|
+
* Anti-loop guards in solo-cto-pipeline extended to recognise new comment formats: `## 🔍 Consensus Review`, `## Visual Report — Before / After`, `[visual-report-skipped:…]`, circuit-breaker comments, auto-merge-enabled comments, `<!-- cross-reviewer:consensus -->` machine tag.
|
|
21
|
+
* Claude model IDs unified to `claude-sonnet-4-6` across rework-agent, claude-reviewer, claude-worker (was mixed 4.0 / 4.6).
|
|
22
|
+
* Hardcoded `seunghunbae-3svs` owner in `solo-cto-pipeline.yml` replaced with `{{GITHUB_OWNER}}` placeholder — every non-maintainer user was hitting silent dispatch failures.
|
|
23
|
+
* OpenAI call in rework-agent was passing `system` as a top-level parameter (wrong shape); moved into `messages[]` as a system-role message.
|
|
24
|
+
|
|
25
|
+
### New notification paths
|
|
26
|
+
* **`pr-merge-notify.yml`** — fires on PR closed (merged or not); posts consolidated Telegram + Discord summary with rework round count.
|
|
27
|
+
* **`combined-pr-with-uiux.yml` rewired** — now triggers on the real workflow name (`Visual Report (Before/After)`), posts the single "all agent checks passed" message exactly once per PR.
|
|
28
|
+
* `visual-check.yml` fires on `workflow_run: Auto Rework on Review completed` → fresh preview screenshots after every rework.
|
|
29
|
+
* `auto-diagnose.yml` fires on rework-auto **failure** → Telegram-attached JSON diagnostic.
|
|
30
|
+
* Skip paths (`[visual-report-skipped:…]`) now notify Telegram/Discord too so silent failures aren't silent.
|
|
31
|
+
|
|
32
|
+
### Docs + hygiene
|
|
33
|
+
* README front section rewritten to describe the full pipeline (consensus, rework, visual, Telegram) instead of just dual-agent review.
|
|
34
|
+
* New `docs/user-journey.md` — install → trigger → review → rework → visual → merge, with ASCII flow diagram, common scenarios, troubleshooting table.
|
|
35
|
+
* `docs/hero-banner-prompt.md` (new) — regeneration prompt for the README hero image aligned with the expanded surface.
|
|
36
|
+
* `.env.example` covers Discord / Vercel / Browserless / admin Telegram chat IDs.
|
|
37
|
+
* PAT scope guidance expanded for classic vs fine-grained tokens.
|
|
38
|
+
* `require.main === module` guards on `visual-check.js` + `auto-diagnose.js` so tests can import them without firing `main()`.
|
|
39
|
+
* Audit report from 2026-04-19 published as a gist: https://gist.github.com/seunghunbae-3svs/4f3da08f149fdb2b2451b43751f6f35c
|
|
40
|
+
|
|
41
|
+
### Merged PRs
|
|
42
|
+
* #106 — vision batch (repo-discovery / consensus / visual-report / NL orders / Telegram CTO)
|
|
43
|
+
* #107 — pipeline consolidation
|
|
44
|
+
* #108 — docs sync with #106/#107
|
|
45
|
+
* #109 — end-to-end loop closure (install automation, merge notifications, D1/D2 wiring, README)
|
|
46
|
+
* (this release) — `require.main` guards + hero banner prompt + de-dupe combined-pr-with-uiux trigger
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
3
50
|
## v1.3.0 (2026-04-17)
|
|
4
51
|
|
|
5
52
|
**Theme**: Tier 3 deep integration features — plugin registry search, setup automation, type system enhancements.
|
|
@@ -121,6 +168,20 @@ non-interactive verify in CI, and tear it all down with one command.
|
|
|
121
168
|
|
|
122
169
|
## Unreleased
|
|
123
170
|
|
|
171
|
+
* Merge pull request #109 from seunghunbae-3svs/claude/e2e-audit
|
|
172
|
+
|
|
173
|
+
* Merge pull request #108 from seunghunbae-3svs/claude/docs-sync
|
|
174
|
+
|
|
175
|
+
* Merge pull request #107 from seunghunbae-3svs/claude/pipeline-consolidation
|
|
176
|
+
|
|
177
|
+
* Merge pull request #106 from seunghunbae-3svs/claude/vision-batch
|
|
178
|
+
|
|
179
|
+
* Merge pull request #105 from seunghunbae-3svs/claude/friendly-black-efec58
|
|
180
|
+
|
|
181
|
+
* docs: sharpen README intro — feature-first, install in 2 lines
|
|
182
|
+
|
|
183
|
+
* chore: v1.3.2 — clean up README slop, sync versions, update metrics
|
|
184
|
+
|
|
124
185
|
* ci: add VS Code extension auto-publish to release workflow
|
|
125
186
|
|
|
126
187
|
* docs: add hero banner to README, update test badge to 996
|
package/README.md
CHANGED
|
@@ -4,46 +4,103 @@
|
|
|
4
4
|
|
|
5
5
|
# solo-cto-agent
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**The full CTO loop for solo founders — dual-agent review, multi-turn consensus, auto-rework, before/after visual reports, and a Telegram/Discord control surface.**
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/solo-cto-agent)
|
|
10
10
|
[](https://github.com/seunghunbae-3svs/solo-cto-agent/actions/workflows/test.yml)
|
|
11
11
|
[](LICENSE)
|
|
12
12
|
[](CONTRIBUTING.md)
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
You push code. Two AI agents review it, debate for up to three rounds until they reach consensus, auto-push fixes for any blockers, shoot before/after screenshots of your Vercel preview, and ping your phone on Telegram with action buttons. When you tap ✅ Merge (or set the `auto-merge-when-ready` label), GitHub merges it once CI is green. You stay in the loop from your phone; you never touch YAML.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
```bash
|
|
17
|
+
npm i -g solo-cto-agent
|
|
18
|
+
solo-cto-agent init --wizard # pick your repos, pick your tier
|
|
19
|
+
solo-cto-agent do "fix the auth bug in tribo" # natural-language work order
|
|
20
|
+
# ...PR opens → review → rework → merge, all visible in Telegram.
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Surfaces**: CLI · [GitHub Action](action.yml) · [VS Code Extension](https://marketplace.visualstudio.com/items?itemName=seunghunbae-3svs.solo-cto-agent) · Telegram bot · optional Discord mirror.
|
|
17
24
|
|
|
18
25
|
> **Languages**: English (primary) - [한국어 안내](#한국어-안내) below.
|
|
19
26
|
|
|
20
|
-
## Quickstart
|
|
27
|
+
## Quickstart
|
|
21
28
|
|
|
22
29
|
```bash
|
|
23
|
-
# 1. Install
|
|
30
|
+
# 1. Install + wizard (wizard auto-discovers your repos via gh CLI,
|
|
31
|
+
# creates the orchestrator repo on GitHub, sets TRACKED_REPOS,
|
|
32
|
+
# and installs workflows on every product repo you pick.)
|
|
24
33
|
npm install -g solo-cto-agent
|
|
25
|
-
|
|
26
|
-
# 2. Initialize (recommended: choose mode during wizard)
|
|
27
34
|
npx solo-cto-agent init --wizard
|
|
28
35
|
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
export
|
|
36
|
+
# 2. Keys (all set in your shell, then copied to repo secrets by setup)
|
|
37
|
+
export ANTHROPIC_API_KEY="sk-ant-..." # required — Claude review + NL orders
|
|
38
|
+
export OPENAI_API_KEY="sk-..." # required for dual-agent / CTO tier
|
|
39
|
+
export ORCHESTRATOR_PAT="ghp_..." # required — cross-repo dispatch + write
|
|
40
|
+
export TELEGRAM_BOT_TOKEN="..." # optional — PR notifications + /commands
|
|
41
|
+
export TELEGRAM_CHAT_ID="..." # optional
|
|
42
|
+
export DISCORD_WEBHOOK_URL="https://..." # optional — mirror of Telegram output
|
|
43
|
+
export VERCEL_TOKEN="..." # optional — before/after visual reports
|
|
32
44
|
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
export OPENAI_API_KEY="sk-..."
|
|
45
|
+
# 3. Run setup-pipeline (reads saved wizard selection; no manual --repos needed)
|
|
46
|
+
solo-cto-agent setup-pipeline --org <your-github-org>
|
|
36
47
|
|
|
37
|
-
#
|
|
38
|
-
solo-cto-agent doctor
|
|
48
|
+
# 4. Verify
|
|
49
|
+
solo-cto-agent doctor
|
|
39
50
|
|
|
40
|
-
#
|
|
41
|
-
solo-cto-agent
|
|
51
|
+
# 5. Kick off a real work order
|
|
52
|
+
solo-cto-agent do "add a monthly ARPU chart to the tribo admin dashboard"
|
|
53
|
+
# → LLM picks target repo, drafts a spec issue, labels agent-claude
|
|
54
|
+
# → claude worker opens a PR
|
|
55
|
+
# → cross-reviewer.js runs 3-round consensus
|
|
56
|
+
# → rework-agent.js pushes fixes if needed
|
|
57
|
+
# → visual-report.yml posts before/after screenshots
|
|
58
|
+
# → combined-pr-gate.yml sends "all checks passed" to Telegram
|
|
59
|
+
# → auto-merge-when-ready label makes GitHub merge on CI green
|
|
60
|
+
# → pr-merge-notify.yml sends final "✅ merged" to Telegram/Discord
|
|
42
61
|
```
|
|
43
62
|
|
|
44
|
-
|
|
63
|
+
Every step above ships end-to-end today. The `doctor` subcommand tells you anything missing with the exact command to run.
|
|
64
|
+
|
|
65
|
+
### Telegram bot — the phone-first control surface
|
|
66
|
+
|
|
67
|
+
After you set `TELEGRAM_BOT_TOKEN` and `TELEGRAM_CHAT_ID`, the bot gives you:
|
|
68
|
+
|
|
69
|
+
| Command | What it does |
|
|
70
|
+
|---|---|
|
|
71
|
+
| `/status [repo]` | Open, non-draft PRs + review state across tracked repos |
|
|
72
|
+
| `/list [repo]` | Last 10 PRs, one-line summary each |
|
|
73
|
+
| `/rework <pr>` | Force a rework cycle on an existing PR |
|
|
74
|
+
| `/approve <pr>` | Approve the PR |
|
|
75
|
+
| `/do "<instruction>"` | Natural-language work order (same as CLI `do`) |
|
|
76
|
+
| `/digest` | Today's PR activity summary |
|
|
77
|
+
| `/merge <pr>` | Immediate merge (admin-only: `TELEGRAM_ADMIN_CHAT_IDS`) |
|
|
78
|
+
|
|
79
|
+
Every review / rework / visual-report message includes inline buttons — ✅ Approve · ❌ Reject · 🔧 Rework · 🔀 Merge. Tap to act without leaving Telegram.
|
|
45
80
|
|
|
46
|
-
|
|
81
|
+
If `DISCORD_WEBHOOK_URL` is set, visual-change screenshots and auto-diagnose reports mirror to Discord as attachments.
|
|
82
|
+
|
|
83
|
+
### External services
|
|
84
|
+
|
|
85
|
+
| Service | Used for | Required? | Setup |
|
|
86
|
+
|---|---|---|---|
|
|
87
|
+
| **GitHub** | orchestrator repo + product repo workflows | ✅ required | wizard auto-creates orchestrator repo via `gh repo create` |
|
|
88
|
+
| **Anthropic** | Claude consensus review, NL orders, rework | ✅ required | `ANTHROPIC_API_KEY` env var |
|
|
89
|
+
| **OpenAI** | Codex counter-review (dual-agent) | CTO tier | `OPENAI_API_KEY` env var |
|
|
90
|
+
| **Vercel** | preview URLs for before/after visual-report | optional | `VERCEL_TOKEN` + `VERCEL_PROJECT_ID`. Works with **Netlify / Cloudflare Pages / Render / Railway previews** too if their `deployment_status` webhooks fire — the visual stage resolves the URL from SHA and shoots whichever host serves it. |
|
|
91
|
+
| **Telegram** | notifications + CTO commands | optional | `/telegram wizard` command + bot token |
|
|
92
|
+
| **Discord** | optional mirror of Telegram output | optional | `DISCORD_WEBHOOK_URL` on orchestrator secrets |
|
|
93
|
+
| **Browserless** | alternate screenshot provider (skips Playwright install cost) | optional | `VISUAL_REVIEW_PROVIDER=browserless` + `BROWSERLESS_API_KEY` |
|
|
94
|
+
|
|
95
|
+
### Compatibility
|
|
96
|
+
|
|
97
|
+
- **Stack-agnostic.** The toolkit never touches your application code directly. Agents produce patches that land on your PR branch; your repo's existing CI / build tools verify them. Works with Next.js, Vite, Remix, SvelteKit, FastAPI, Rails — anything with a PR workflow.
|
|
98
|
+
- **Hosting-agnostic.** Vercel is the default for the visual-report preview URL resolver, but any host that ships preview URLs tied to commit SHAs works. For hosts without that (plain Docker, bare-metal, self-hosted): set `VISUAL_REVIEW_PROVIDER=off` and the pipeline just skips the visual stage — everything else still runs.
|
|
99
|
+
- **Database-agnostic.** The toolkit doesn't read or write your database. Postgres (Supabase / Neon / PlanetScale-Postgres), MySQL, SQLite, MongoDB — all fine. Agent workers DO see your schema files if they're in the repo (prisma/schema.prisma, supabase/schema.sql, etc.) so suggested fixes can be schema-aware.
|
|
100
|
+
- **Docker.** If your product repo is dockerized, nothing changes — GitHub Actions runners handle the build per your existing Dockerfile. The agents commit to the PR branch, your CI rebuilds the container, the auto-merge gate waits on that CI.
|
|
101
|
+
- **Windows / macOS / Linux** for the CLI. GitHub Actions runners are Linux for all automation paths.
|
|
102
|
+
|
|
103
|
+
### Platform-specific CLI setup
|
|
47
104
|
|
|
48
105
|
**macOS / Linux**
|
|
49
106
|
|
|
@@ -64,6 +121,8 @@ solo-cto-agent doctor
|
|
|
64
121
|
If you choose `codex-main` during the wizard, also install:
|
|
65
122
|
- GitHub CLI: [cli.github.com](https://cli.github.com/)
|
|
66
123
|
- GitHub PAT for cross-repo dispatch: [github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new)
|
|
124
|
+
- **Classic PAT**: check `repo` + `workflow` scopes.
|
|
125
|
+
- **Fine-grained PAT**: grant `Contents: write`, `Issues: write`, `Pull requests: write`, `Actions: write` on every product repo listed in `setup-pipeline --repos`. The orchestrator pushes fix commits and posts comments on those repos on your behalf.
|
|
67
126
|
|
|
68
127
|
If you choose `codex-main`, template drift audit is enabled by default:
|
|
69
128
|
- local check: `solo-cto-agent template-audit`
|
|
@@ -259,18 +318,26 @@ solo-cto-agent/
|
|
|
259
318
|
|
|
260
319
|
## Three Axes: Tier / Agent / Mode
|
|
261
320
|
|
|
321
|
+
At a glance:
|
|
322
|
+
|
|
323
|
+
| | Cowork (semi-auto) | Codex (full-auto) |
|
|
324
|
+
|----------|--------------------|-----------------------|
|
|
325
|
+
| Builder | local + manual review | CI dispatch + auto-fix |
|
|
326
|
+
| CTO | local + dual-agent cross-review | CI dispatch + dual + cross-review + scoring |
|
|
327
|
+
|
|
328
|
+
Cowork runs in your terminal with you in the loop. Codex runs in CI and reworks itself until the PR passes.
|
|
329
|
+
|
|
262
330
|
`solo-cto-agent` is configured across three independent axes. You choose each based on your workflow.
|
|
263
331
|
|
|
264
332
|
| Axis | Decision | Options |
|
|
265
333
|
|---|---|---|
|
|
266
|
-
| Tier | Scope of capability |
|
|
334
|
+
| Tier | Scope of capability | Builder / CTO |
|
|
267
335
|
| Agent | Who reviews | Cowork (Claude) / Cowork + Codex |
|
|
268
336
|
| Mode | Automation depth | Semi-auto (cowork-main) / Full-auto (codex-main) |
|
|
269
337
|
|
|
270
338
|
Quick pick if you are unsure:
|
|
271
|
-
- Start with
|
|
272
|
-
- Move to
|
|
273
|
-
- Move to CTO + Full-auto when you want always-on CI/CD and multi-agent routing.
|
|
339
|
+
- Start with Builder + Cowork (single Claude agent, semi-auto, optional Telegram bot).
|
|
340
|
+
- Move to CTO + Full-auto when you want dual-agent cross-review and always-on CI/CD across repos.
|
|
274
341
|
|
|
275
342
|
### Agents (summary)
|
|
276
343
|
|
|
@@ -307,15 +374,13 @@ Full-auto adds:
|
|
|
307
374
|
### Tiers (summary)
|
|
308
375
|
|
|
309
376
|
**Not sure which tier? One question:**
|
|
310
|
-
-
|
|
311
|
-
-
|
|
312
|
-
- Running multi-repo CI/CD with full automation? → **CTO**
|
|
377
|
+
- Solo dev shipping code with one Claude agent? → **Builder** (default, recommended for most users)
|
|
378
|
+
- Want dual-agent cross-review (Claude + Codex) and multi-repo CI/CD? → **CTO**
|
|
313
379
|
|
|
314
|
-
| Tier | Includes | Recommended for |
|
|
315
|
-
|
|
316
|
-
|
|
|
317
|
-
|
|
|
318
|
-
| CTO | Builder + orchestrate | multi-agent + routing |
|
|
380
|
+
| Tier | Includes | Agents | Extras | Recommended for |
|
|
381
|
+
|---|---|---|---|---|
|
|
382
|
+
| Builder | spark + review + memory + craft + build + ship | solo Claude | optional Telegram bot for PR notify/approve | solo dev shipping |
|
|
383
|
+
| CTO | Builder + orchestrate | Claude + Codex (dual-agent cross-review) | agent scoring, routing, decision queue, daily briefing | multi-agent CI/CD across repos |
|
|
319
384
|
|
|
320
385
|
Details: `docs/tier-matrix.md`, `docs/tier-examples.md`, `docs/cto-policy.md`, `docs/cowork-main-install.md`, `docs/configuration.md`.
|
|
321
386
|
|
package/bin/cli.js
CHANGED
|
@@ -30,6 +30,8 @@ let telegramBot;
|
|
|
30
30
|
try { telegramBot = require("./telegram-bot"); } catch (_) { telegramBot = null; }
|
|
31
31
|
let selfEvolve;
|
|
32
32
|
try { selfEvolve = require("./self-evolve"); } catch (_) { selfEvolve = null; }
|
|
33
|
+
let repoDiscovery;
|
|
34
|
+
try { repoDiscovery = require("./repo-discovery"); } catch (_) { repoDiscovery = null; }
|
|
33
35
|
|
|
34
36
|
const ROOT = path.resolve(__dirname, "..");
|
|
35
37
|
const DEFAULT_CATALOG = path.join(ROOT, "failure-catalog.json");
|
|
@@ -65,6 +67,8 @@ function printHelp() {
|
|
|
65
67
|
Usage:
|
|
66
68
|
solo-cto-agent init [--force] [--preset maker|builder|cto] [--wizard]
|
|
67
69
|
solo-cto-agent setup-pipeline --org <github-org> [--tier builder|cto] [--repos <repo1,repo2,...>]
|
|
70
|
+
solo-cto-agent repos list [--org <github-org>] # show/re-pick the saved repo selection
|
|
71
|
+
solo-cto-agent do "<instruction>" [--dry-run] [--repo owner/name] [--agent claude|codex]
|
|
68
72
|
solo-cto-agent setup-repo <repo-path> --org <github-org> [--tier builder|cto]
|
|
69
73
|
solo-cto-agent auto-setup # Install solo-cto-pipeline.yml to your repos (centralized)
|
|
70
74
|
solo-cto-agent setup --central --org <owner> [--orchestrator <repo>] [--repos <r1,r2,...>] [--dry-run]
|
|
@@ -93,6 +97,8 @@ Usage:
|
|
|
93
97
|
Commands:
|
|
94
98
|
init Install skills to ~/.claude/skills/, then run doctor to verify setup
|
|
95
99
|
setup-pipeline Full pipeline setup: create orchestrator repo + install workflows to product repos
|
|
100
|
+
repos list Print current saved repo selection (from init wizard) and re-pick interactively
|
|
101
|
+
do Natural-language work order: LLM parses intent → creates labeled issue → worker runs
|
|
96
102
|
setup-repo Install dual-agent workflows to a single product repo
|
|
97
103
|
auto-setup Install solo-cto-pipeline.yml (centralized thin workflow) to selected repos
|
|
98
104
|
setup --central Centralize cross-repo workflows (digest, bot-runner) to orchestrator repo
|
|
@@ -401,6 +407,82 @@ Style: {{YOUR_STYLE}}
|
|
|
401
407
|
doctorCommand({ exitOnError: false, quick: true });
|
|
402
408
|
}
|
|
403
409
|
|
|
410
|
+
// ─── repos: show/re-pick saved selection ────────────────────
|
|
411
|
+
|
|
412
|
+
async function reposCommand(args) {
|
|
413
|
+
if (!repoDiscovery) {
|
|
414
|
+
console.error("❌ repo-discovery module not available in this install.");
|
|
415
|
+
process.exit(1);
|
|
416
|
+
}
|
|
417
|
+
const sub = args[1] || "list";
|
|
418
|
+
if (sub !== "list") {
|
|
419
|
+
console.error(`Unknown repos subcommand: ${sub}`);
|
|
420
|
+
console.error("Usage: solo-cto-agent repos list [--org <github-org>]");
|
|
421
|
+
process.exit(1);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const orgIndex = args.indexOf("--org");
|
|
425
|
+
let org = orgIndex >= 0 ? args[orgIndex + 1] : null;
|
|
426
|
+
|
|
427
|
+
const saved = repoDiscovery.loadSelection();
|
|
428
|
+
if (saved) {
|
|
429
|
+
console.log(`Saved selection (${repoDiscovery.selectionPath()}):`);
|
|
430
|
+
console.log(` org: ${saved.org || "(user-scoped)"}`);
|
|
431
|
+
console.log(` updated: ${saved.updatedAt || "—"}`);
|
|
432
|
+
console.log(` repos: ${saved.selected.length ? saved.selected.join(", ") : "(none)"}`);
|
|
433
|
+
if (!org && saved.org) org = saved.org;
|
|
434
|
+
} else {
|
|
435
|
+
console.log("No saved selection yet. Run `solo-cto-agent init --wizard` first, or re-pick below.");
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Non-TTY callers (CI) just get the print-out.
|
|
439
|
+
const { isTTY, createRl } = require("./prompt-utils");
|
|
440
|
+
const { ask } = require("./prompt-utils");
|
|
441
|
+
if (!isTTY()) {
|
|
442
|
+
console.log("\nℹ️ Non-interactive terminal — skipping re-pick prompt.");
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const rl = createRl();
|
|
447
|
+
try {
|
|
448
|
+
const again = await ask(rl, "\nRe-pick repos now?", "n");
|
|
449
|
+
if (!/^y(es)?$/i.test(again.trim())) {
|
|
450
|
+
rl.close();
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
let repos = null;
|
|
455
|
+
try {
|
|
456
|
+
repos = repoDiscovery.fetchRepos({ org });
|
|
457
|
+
} catch (err) {
|
|
458
|
+
console.log(`⚠️ ${err.message}`);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (repos == null) {
|
|
462
|
+
console.log("`gh` CLI not found. Install from https://cli.github.com/ then `gh auth login`.");
|
|
463
|
+
const manual = await ask(rl, "Paste repo slugs manually (comma-separated, or blank to cancel)", "");
|
|
464
|
+
const selected = manual.split(",").map((s) => s.trim()).filter(Boolean);
|
|
465
|
+
if (selected.length) {
|
|
466
|
+
const file = repoDiscovery.saveSelection({ org, selected, discovered: [] });
|
|
467
|
+
console.log(`✅ Saved ${selected.length} repo(s) to ${file}`);
|
|
468
|
+
} else {
|
|
469
|
+
console.log("No changes.");
|
|
470
|
+
}
|
|
471
|
+
} else if (repos.length === 0) {
|
|
472
|
+
console.log("No repositories returned from gh.");
|
|
473
|
+
} else {
|
|
474
|
+
const preselected = saved && Array.isArray(saved.selected) && saved.selected.length
|
|
475
|
+
? saved.selected
|
|
476
|
+
: repoDiscovery.defaultPreselect(repos);
|
|
477
|
+
const selected = await repoDiscovery.pickReposInteractive(rl, ask, repos, preselected);
|
|
478
|
+
const file = repoDiscovery.saveSelection({ org, selected, discovered: repos });
|
|
479
|
+
console.log(`✅ Saved ${selected.length} repo(s) to ${file}`);
|
|
480
|
+
}
|
|
481
|
+
} finally {
|
|
482
|
+
rl.close();
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
404
486
|
// ─── setup-pipeline: Full Pipeline Deploy ───────────────────
|
|
405
487
|
|
|
406
488
|
function setupPipelineCommand(tier, org, repos, orchName, force) {
|
|
@@ -2061,6 +2143,17 @@ async function main() {
|
|
|
2061
2143
|
return false;
|
|
2062
2144
|
}
|
|
2063
2145
|
|
|
2146
|
+
if (cmd === "repos") {
|
|
2147
|
+
await reposCommand(args);
|
|
2148
|
+
return;
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
if (cmd === "do") {
|
|
2152
|
+
const doModule = require("./do");
|
|
2153
|
+
await doModule.main();
|
|
2154
|
+
return;
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2064
2157
|
if (cmd === "setup-pipeline") {
|
|
2065
2158
|
if (checkCoworkMainMode()) {
|
|
2066
2159
|
console.log("ℹ️ Not needed in cowork-main mode. Use `review`, `knowledge`, and `sync` commands instead.");
|
|
@@ -2071,7 +2164,15 @@ async function main() {
|
|
|
2071
2164
|
const orgIndex = args.indexOf("--org");
|
|
2072
2165
|
const org = orgIndex >= 0 ? args[orgIndex + 1] : null;
|
|
2073
2166
|
const reposIndex = args.indexOf("--repos");
|
|
2074
|
-
|
|
2167
|
+
let repos = reposIndex >= 0 ? args[reposIndex + 1] : null;
|
|
2168
|
+
// Fall back to the selection persisted by `init --wizard` / `repos list`.
|
|
2169
|
+
if (!repos && repoDiscovery) {
|
|
2170
|
+
const saved = repoDiscovery.loadSelection();
|
|
2171
|
+
if (saved && Array.isArray(saved.selected) && saved.selected.length) {
|
|
2172
|
+
repos = saved.selected.join(",");
|
|
2173
|
+
console.log(`ℹ️ Using saved repo selection (${saved.selected.length} repos from ${repoDiscovery.selectionPath()}).`);
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2075
2176
|
const orchIndex = args.indexOf("--orchestrator-name");
|
|
2076
2177
|
const orchName = orchIndex >= 0 ? args[orchIndex + 1] : null;
|
|
2077
2178
|
setupPipelineCommand(tier, org, repos, orchName, force);
|
|
@@ -2136,7 +2237,14 @@ async function main() {
|
|
|
2136
2237
|
const orgIndex = args.indexOf("--org");
|
|
2137
2238
|
const org = orgIndex >= 0 ? args[orgIndex + 1] : null;
|
|
2138
2239
|
const reposIndex = args.indexOf("--repos");
|
|
2139
|
-
|
|
2240
|
+
let repos = reposIndex >= 0 ? args[reposIndex + 1] : null;
|
|
2241
|
+
if (!repos && repoDiscovery) {
|
|
2242
|
+
const saved = repoDiscovery.loadSelection();
|
|
2243
|
+
if (saved && Array.isArray(saved.selected) && saved.selected.length) {
|
|
2244
|
+
repos = saved.selected.join(",");
|
|
2245
|
+
console.log(`ℹ️ Using saved repo selection (${saved.selected.length} repos).`);
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2140
2248
|
const orchIndex = args.indexOf("--orchestrator-name");
|
|
2141
2249
|
const orchName = orchIndex >= 0 ? args[orchIndex + 1] : null;
|
|
2142
2250
|
upgradeCommand(org, repos, orchName);
|
|
@@ -2147,7 +2255,14 @@ async function main() {
|
|
|
2147
2255
|
const orgIndex = args.indexOf("--org");
|
|
2148
2256
|
const org = orgIndex >= 0 ? args[orgIndex + 1] : null;
|
|
2149
2257
|
const reposIndex = args.indexOf("--repos");
|
|
2150
|
-
|
|
2258
|
+
let repos = reposIndex >= 0 ? args[reposIndex + 1] : null;
|
|
2259
|
+
if (!repos && repoDiscovery) {
|
|
2260
|
+
const saved = repoDiscovery.loadSelection();
|
|
2261
|
+
if (saved && Array.isArray(saved.selected) && saved.selected.length) {
|
|
2262
|
+
repos = saved.selected.join(",");
|
|
2263
|
+
console.log(`ℹ️ Using saved repo selection (${saved.selected.length} repos).`);
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2151
2266
|
const orchIndex = args.indexOf("--orchestrator-name");
|
|
2152
2267
|
const orchName = orchIndex >= 0 ? args[orchIndex + 1] : "dual-agent-orchestrator";
|
|
2153
2268
|
const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN || process.env.ORCHESTRATOR_PAT;
|
package/bin/do.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* do — natural-language work order entry point.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* solo-cto-agent do "fix the login bug in tribo"
|
|
8
|
+
* solo-cto-agent do "improve the hero section typography on the landing page"
|
|
9
|
+
*
|
|
10
|
+
* What it does:
|
|
11
|
+
* 1. Loads tracked repos from the saved wizard selection (bin/repo-discovery).
|
|
12
|
+
* 2. Asks Claude to translate the NL order into a structured issue spec.
|
|
13
|
+
* 3. Creates the issue on the target product repo with an `agent-{claude|codex}`
|
|
14
|
+
* label so existing orchestrator workflows pick it up and run the real
|
|
15
|
+
* implementation agent.
|
|
16
|
+
*
|
|
17
|
+
* Environment:
|
|
18
|
+
* ANTHROPIC_API_KEY required (for intent parsing)
|
|
19
|
+
* GITHUB_TOKEN required (to create the issue)
|
|
20
|
+
*
|
|
21
|
+
* We DO NOT write code here. The code is written by the implementing
|
|
22
|
+
* worker (claude-auto.yml / codex-auto.yml) once the labeled issue lands.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
"use strict";
|
|
26
|
+
|
|
27
|
+
const { parseIntent, dispatchOrder } = require("./lib/nl-orchestrator");
|
|
28
|
+
let repoDiscovery;
|
|
29
|
+
try {
|
|
30
|
+
repoDiscovery = require("./repo-discovery");
|
|
31
|
+
} catch (_) {
|
|
32
|
+
repoDiscovery = null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function printHelp() {
|
|
36
|
+
console.log(`do — issue a natural-language work order
|
|
37
|
+
|
|
38
|
+
Usage:
|
|
39
|
+
solo-cto-agent do "<natural language instruction>"
|
|
40
|
+
|
|
41
|
+
Options:
|
|
42
|
+
--dry-run Print the parsed intent without creating an issue
|
|
43
|
+
--repo owner/name Override auto-selected target repo
|
|
44
|
+
--agent claude|codex Force a specific implementer (default: LLM decides)
|
|
45
|
+
--help, -h Show this
|
|
46
|
+
|
|
47
|
+
Setup:
|
|
48
|
+
- Run \`solo-cto-agent init --wizard\` to discover and save tracked repos.
|
|
49
|
+
- Set ANTHROPIC_API_KEY (for intent parsing).
|
|
50
|
+
- Set GITHUB_TOKEN (for issue creation).
|
|
51
|
+
|
|
52
|
+
Examples:
|
|
53
|
+
solo-cto-agent do "fix the staging deploy error I saw in tribo"
|
|
54
|
+
solo-cto-agent do "redesign the login hero on ohmywork — cleaner, less gradient" --agent claude
|
|
55
|
+
solo-cto-agent do "add unit tests for the ARPU calculator in tribo" --agent codex
|
|
56
|
+
`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function parseArgs(argv) {
|
|
60
|
+
const out = { text: null, dryRun: false, repo: null, agent: null, help: false };
|
|
61
|
+
const rest = [];
|
|
62
|
+
for (let i = 0; i < argv.length; i++) {
|
|
63
|
+
const a = argv[i];
|
|
64
|
+
if (a === "--help" || a === "-h") out.help = true;
|
|
65
|
+
else if (a === "--dry-run") out.dryRun = true;
|
|
66
|
+
else if (a === "--repo" && argv[i + 1]) {
|
|
67
|
+
out.repo = argv[++i];
|
|
68
|
+
} else if (a === "--agent" && argv[i + 1]) {
|
|
69
|
+
out.agent = argv[++i];
|
|
70
|
+
} else {
|
|
71
|
+
rest.push(a);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
out.text = rest.join(" ").trim();
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function main() {
|
|
79
|
+
const args = parseArgs(process.argv.slice(3)); // slice 3: node, cli.js, "do"
|
|
80
|
+
if (args.help || !args.text) {
|
|
81
|
+
printHelp();
|
|
82
|
+
process.exit(args.help ? 0 : 1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 1. Tracked repos
|
|
86
|
+
if (!repoDiscovery) {
|
|
87
|
+
console.error("❌ repo-discovery module missing — reinstall solo-cto-agent.");
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
const saved = repoDiscovery.loadSelection();
|
|
91
|
+
if (!saved || !Array.isArray(saved.discovered) || saved.discovered.length === 0) {
|
|
92
|
+
console.error("❌ No tracked repos saved. Run `solo-cto-agent init --wizard` first.");
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
// If user selected a subset, only consider those; otherwise all discovered.
|
|
96
|
+
const selectedSet = new Set(saved.selected || []);
|
|
97
|
+
const trackedRepos = saved.discovered.filter((r) =>
|
|
98
|
+
selectedSet.size === 0 ? true : selectedSet.has(r.name)
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
if (args.repo) {
|
|
102
|
+
// --repo override: accept only if in the tracked list
|
|
103
|
+
const hit = trackedRepos.find((r) => (r.fullName || r.name) === args.repo);
|
|
104
|
+
if (!hit) {
|
|
105
|
+
console.error(`❌ --repo ${args.repo} is not in your tracked repo list.`);
|
|
106
|
+
console.error(` Tracked: ${trackedRepos.map((r) => r.fullName || r.name).join(", ")}`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 2. Anthropic client — thin fetch shim so solo-cto-agent keeps zero
|
|
112
|
+
// runtime deps. The orchestrator worker uses the real SDK (installed
|
|
113
|
+
// by its workflow) but the CLI doesn't need it.
|
|
114
|
+
if (!process.env.ANTHROPIC_API_KEY) {
|
|
115
|
+
console.error("❌ ANTHROPIC_API_KEY not set. Needed to parse natural-language intent.");
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
const anthropicClient = buildAnthropicFetchClient(process.env.ANTHROPIC_API_KEY);
|
|
119
|
+
|
|
120
|
+
// 3. GitHub client (shell out to gh CLI to keep deps small). We wrap the
|
|
121
|
+
// ghApi shape the nl-orchestrator expects around gh.
|
|
122
|
+
const ghApi = buildGhApi(args.dryRun);
|
|
123
|
+
|
|
124
|
+
// 4. Run
|
|
125
|
+
try {
|
|
126
|
+
const intent = await parseIntent({ userText: args.text, trackedRepos, anthropicClient });
|
|
127
|
+
if (args.repo) intent.repo = args.repo;
|
|
128
|
+
if (args.agent) intent.agent = args.agent;
|
|
129
|
+
|
|
130
|
+
if (args.dryRun) {
|
|
131
|
+
console.log(JSON.stringify(intent, null, 2));
|
|
132
|
+
console.log("\n(dry-run — no issue created)");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const result = await dispatchOrder({ intent, ghApi });
|
|
137
|
+
console.log(`✅ Issue created: ${result.issueUrl}`);
|
|
138
|
+
console.log(` Repo: ${result.repo}`);
|
|
139
|
+
console.log(` Agent: ${result.agent}`);
|
|
140
|
+
console.log(` Scope: ${result.scope}`);
|
|
141
|
+
console.log(` Labels: ${result.labels.join(", ")}`);
|
|
142
|
+
console.log(`\nThe '${result.agent}-auto' workflow will pick this up and open a PR.`);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
console.error(`❌ ${err.message}`);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Thin Octokit-shaped client that only implements the methods nl-orchestrator
|
|
151
|
+
* actually calls. Uses `gh api` so we don't pull in @octokit/rest at runtime.
|
|
152
|
+
* Set dry=true to skip writes entirely (returns a stub).
|
|
153
|
+
*/
|
|
154
|
+
function buildGhApi(dry) {
|
|
155
|
+
const { execFileSync } = require("child_process");
|
|
156
|
+
return {
|
|
157
|
+
issues: {
|
|
158
|
+
create: async ({ owner, repo, title, body, labels }) => {
|
|
159
|
+
if (dry) {
|
|
160
|
+
return { data: { html_url: "(dry-run)", number: 0 } };
|
|
161
|
+
}
|
|
162
|
+
const payload = JSON.stringify({ title, body, labels });
|
|
163
|
+
const out = execFileSync("gh", ["api", "-X", "POST", `/repos/${owner}/${repo}/issues`, "--input", "-"], {
|
|
164
|
+
input: payload,
|
|
165
|
+
encoding: "utf8",
|
|
166
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
167
|
+
});
|
|
168
|
+
const data = JSON.parse(out);
|
|
169
|
+
return { data };
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Anthropic client shim with just the `.messages.create` method the
|
|
177
|
+
* nl-orchestrator expects, calling the public REST endpoint directly.
|
|
178
|
+
* Keeps the CLI free of `@anthropic-ai/sdk` as a runtime dep.
|
|
179
|
+
*/
|
|
180
|
+
function buildAnthropicFetchClient(apiKey) {
|
|
181
|
+
return {
|
|
182
|
+
messages: {
|
|
183
|
+
create: async ({ model, max_tokens, temperature, system, messages }) => {
|
|
184
|
+
const res = await fetch("https://api.anthropic.com/v1/messages", {
|
|
185
|
+
method: "POST",
|
|
186
|
+
headers: {
|
|
187
|
+
"x-api-key": apiKey,
|
|
188
|
+
"anthropic-version": "2023-06-01",
|
|
189
|
+
"content-type": "application/json",
|
|
190
|
+
},
|
|
191
|
+
body: JSON.stringify({ model, max_tokens, temperature, system, messages }),
|
|
192
|
+
});
|
|
193
|
+
if (!res.ok) {
|
|
194
|
+
const body = await res.text();
|
|
195
|
+
throw new Error(`Anthropic ${res.status}: ${body.slice(0, 300)}`);
|
|
196
|
+
}
|
|
197
|
+
return res.json();
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
module.exports = { main };
|
|
204
|
+
|
|
205
|
+
if (require.main === module) {
|
|
206
|
+
main().catch((e) => {
|
|
207
|
+
console.error(e.message);
|
|
208
|
+
process.exit(1);
|
|
209
|
+
});
|
|
210
|
+
}
|