solo-cto-agent 1.0.0 → 1.1.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 +54 -0
- package/README.md +6 -3
- package/bin/cli.js +149 -1
- package/bin/cowork-engine.js +408 -50
- package/bin/i18n.js +175 -0
- package/bin/plugin-manager.js +334 -0
- package/bin/prompt-utils.js +79 -0
- package/bin/telegram-wizard.js +479 -0
- package/bin/uiux-engine.js +69 -3
- package/bin/wizard.js +3 -23
- package/docs/plugin-api-v2.md +285 -0
- package/docs/telegram-wizard-spec.md +302 -0
- package/package.json +1 -1
- package/skills/build/SKILL.md +4 -0
- package/skills/craft/SKILL.md +2 -2
- package/skills/memory/SKILL.md +33 -0
- package/skills/orchestrate/SKILL.md +1 -1
- package/skills/review/SKILL.md +19 -0
- package/skills/ship/SKILL.md +31 -2
- package/skills/spark/SKILL.md +14 -0
- package/templates/orchestrator/.claude/agents/implementer.md +8 -23
- package/templates/orchestrator/.claude/agents/integrator.md +7 -12
- package/templates/orchestrator/.claude/agents/reviewer.md +8 -21
- package/templates/orchestrator/.codex/prompts/implement.md +7 -20
- package/templates/orchestrator/.codex/prompts/integrate.md +7 -11
- package/templates/orchestrator/.codex/prompts/review.md +7 -17
- package/templates/orchestrator/agents/README.md +42 -0
- package/templates/orchestrator/agents/implementer.md +34 -0
- package/templates/orchestrator/agents/integrator.md +26 -0
- package/templates/orchestrator/agents/reviewer.md +36 -0
- package/templates/orchestrator/ops/orchestrator/decision-log.json +5 -0
package/CHANGELOG
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.0 — Tier-aware reviews, security signals, plugins & telegram
|
|
4
|
+
|
|
5
|
+
**Theme**: closing the last gaps around signal quality and agent
|
|
6
|
+
extensibility. The review loop now reasons about Haiku/Sonnet/Opus
|
|
7
|
+
tier-appropriately, surfaces live CVE/GHSA advisories via OSV.dev,
|
|
8
|
+
captures screenshots without Playwright, and gains a first-cut
|
|
9
|
+
plugin system + experimental telegram setup wizard.
|
|
10
|
+
|
|
11
|
+
### External signals (PR-G4)
|
|
12
|
+
* **T2 Security Advisories (OSV.dev)** — CVE + GHSA scan across
|
|
13
|
+
`dependencies` + `devDependencies`. Severity normalized (DB-specific
|
|
14
|
+
> CVSS numeric > UNKNOWN) and merged into the external-knowledge
|
|
15
|
+
context block. Gate: `COWORK_EXTERNAL_KNOWLEDGE_SECURITY=0` to skip.
|
|
16
|
+
|
|
17
|
+
### Review tiering (PR-G2)
|
|
18
|
+
* **Per-tier Claude model resolution** — Haiku (cheap triage) / Sonnet
|
|
19
|
+
(default) / Opus (deep review) selected automatically based on watch
|
|
20
|
+
tier. Overridable via `ANTHROPIC_MODEL_HAIKU|SONNET|OPUS`.
|
|
21
|
+
|
|
22
|
+
### UI/UX loop (PR-G5)
|
|
23
|
+
* **Playwright-free screenshot capture** — `uiux vision-review --url`
|
|
24
|
+
and `uiux capture --url` now fall back to thum.io when Playwright is
|
|
25
|
+
unavailable. Viewports: mobile 375x812 / tablet 768x1024 / desktop
|
|
26
|
+
1280x800.
|
|
27
|
+
|
|
28
|
+
### Plugins & integrations (PR-G6 / G7)
|
|
29
|
+
* **`docs/plugin-api-v2.md`** — capability manifest spec
|
|
30
|
+
(env/net/fs/cli/hook/schedule prefixes), contribution points, agent
|
|
31
|
+
targeting (`claude` / `codex` / `cowork` / `headless`).
|
|
32
|
+
* **`plugin` subcommand** — filesystem-only manager:
|
|
33
|
+
`solo-cto-agent plugin list|show|add --path <dir>|remove`. Records
|
|
34
|
+
metadata only; does NOT execute plugin code. Runtime loader lands
|
|
35
|
+
in a follow-up behind the capability gate.
|
|
36
|
+
* **`telegram wizard`** (experimental — `SOLO_CTO_EXPERIMENTAL=1`)
|
|
37
|
+
— one-command bot token + chat_id capture + `.env` / shell profile
|
|
38
|
+
/ GitHub secret writeback + live sendMessage verification.
|
|
39
|
+
* **`docs/telegram-wizard-spec.md`** — full spec including failure
|
|
40
|
+
modes and i18n hooks.
|
|
41
|
+
|
|
42
|
+
### Developer experience
|
|
43
|
+
* **375 tests** (up from 247 in 1.0.0) across 28 files — all offline,
|
|
44
|
+
all network calls stubbed via injected `fetchImpl`.
|
|
45
|
+
* **Shared `prompt-utils.js`** — `ask` / `askYesNo` / `askChoice` /
|
|
46
|
+
`isTTY` / `createRl` extracted from `wizard.js` for future wizards.
|
|
47
|
+
* **npm publish automation** — tag `v*` now triggers full CI +
|
|
48
|
+
`npm publish` + GitHub Release in one workflow.
|
|
49
|
+
|
|
50
|
+
### Upgrade notes
|
|
51
|
+
* No breaking changes. All new features are additive and gated on
|
|
52
|
+
env vars (`COWORK_EXTERNAL_KNOWLEDGE_SECURITY`,
|
|
53
|
+
`SOLO_CTO_EXPERIMENTAL`).
|
|
54
|
+
* `solo-cto-agent plugin` and `solo-cto-agent telegram` are new
|
|
55
|
+
commands — existing commands are unchanged.
|
|
56
|
+
|
|
3
57
|
## 1.0.0 — First stable release
|
|
4
58
|
|
|
5
59
|
**Why 1.0**: the loop is now closable end-to-end. Previous 0.x releases were
|
package/README.md
CHANGED
|
@@ -313,10 +313,13 @@ Run a Claude-powered code review directly from your terminal, no GitHub Actions
|
|
|
313
313
|
ANTHROPIC_API_KEY=sk-xxx solo-cto-agent review
|
|
314
314
|
|
|
315
315
|
# Review a branch diff (dry-run: see the prompt without calling API)
|
|
316
|
-
solo-cto-agent review --
|
|
316
|
+
solo-cto-agent review --branch --dry-run
|
|
317
317
|
|
|
318
|
-
# Review specific
|
|
319
|
-
solo-cto-agent review --
|
|
318
|
+
# Review a branch diff against a specific base (e.g., master)
|
|
319
|
+
solo-cto-agent review --branch --target master
|
|
320
|
+
|
|
321
|
+
# Review specific file
|
|
322
|
+
solo-cto-agent review --file src/app/page.tsx
|
|
320
323
|
```
|
|
321
324
|
|
|
322
325
|
The review checks your diff against the local failure catalog (known error patterns), then sends it to Claude for security, performance, correctness, and style analysis. Results are saved as markdown reports in `~/.claude/skills/solo-cto-agent/reviews/`. This is the same review quality as the CI/CD pipeline, but runs entirely locally — useful for private repos, offline work, or pre-push checks.
|
package/bin/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ const { execSync } = require("child_process");
|
|
|
8
8
|
|
|
9
9
|
const { syncCommand } = require("./sync");
|
|
10
10
|
const { runWizard, hasWizardFlag } = require("./wizard");
|
|
11
|
+
const i18n = require("./i18n");
|
|
11
12
|
const { localReview, knowledgeCapture, dualReview, detectMode, sessionSave, sessionRestore, sessionList, recordFeedback, setLogChannel } = require("./cowork-engine");
|
|
12
13
|
// Lazy-load optional modules so missing files don't break older installs.
|
|
13
14
|
let uiux, rework, watch, notify, inboundFeedback;
|
|
@@ -16,6 +17,10 @@ try { rework = require("./rework"); } catch (_) { rework = null; }
|
|
|
16
17
|
try { watch = require("./watch"); } catch (_) { watch = null; }
|
|
17
18
|
try { notify = require("./notify"); } catch (_) { notify = null; }
|
|
18
19
|
try { inboundFeedback = require("./inbound-feedback"); } catch (_) { inboundFeedback = null; }
|
|
20
|
+
let pluginManager;
|
|
21
|
+
try { pluginManager = require("./plugin-manager"); } catch (_) { pluginManager = null; }
|
|
22
|
+
let telegramWizard;
|
|
23
|
+
try { telegramWizard = require("./telegram-wizard"); } catch (_) { telegramWizard = null; }
|
|
19
24
|
|
|
20
25
|
const ROOT = path.resolve(__dirname, "..");
|
|
21
26
|
const DEFAULT_CATALOG = path.join(ROOT, "failure-catalog.json");
|
|
@@ -34,7 +39,7 @@ const DEFAULT_PRESET = "builder";
|
|
|
34
39
|
// ─── Helpers ────────────────────────────────────────────────
|
|
35
40
|
|
|
36
41
|
function printHelp() {
|
|
37
|
-
console.log(`solo-cto-agent —
|
|
42
|
+
console.log(`solo-cto-agent — ${i18n.t("cli.tagline")}
|
|
38
43
|
|
|
39
44
|
Usage:
|
|
40
45
|
solo-cto-agent init [--force] [--preset maker|builder|cto] [--wizard]
|
|
@@ -50,6 +55,7 @@ Usage:
|
|
|
50
55
|
solo-cto-agent lint [path]
|
|
51
56
|
solo-cto-agent doctor
|
|
52
57
|
solo-cto-agent --help
|
|
58
|
+
solo-cto-agent --lang <en|ko> <command> # override CLI locale (or SOLO_CTO_LANG env)
|
|
53
59
|
|
|
54
60
|
Commands:
|
|
55
61
|
init Install skills to ~/.claude/skills/ (add --wizard for interactive setup)
|
|
@@ -1064,6 +1070,7 @@ function doctorCommand() {
|
|
|
1064
1070
|
const hasLocalReview = typeof engine.localReview === "function";
|
|
1065
1071
|
const hasKnowledge = typeof engine.knowledgeCapture === "function";
|
|
1066
1072
|
const hasDualReview = typeof engine.dualReview === "function";
|
|
1073
|
+
const hasDetectDefaultBranch = typeof engine.detectDefaultBranch === "function";
|
|
1067
1074
|
const sessionSave = typeof engine.sessionSave === "function";
|
|
1068
1075
|
const sessionRestore = typeof engine.sessionRestore === "function";
|
|
1069
1076
|
const sessionList = typeof engine.sessionList === "function";
|
|
@@ -1081,6 +1088,19 @@ function doctorCommand() {
|
|
|
1081
1088
|
console.log(" ⚠️ Session functions not available");
|
|
1082
1089
|
issues.push({ level: "warn", msg: "Engine missing session functions" });
|
|
1083
1090
|
}
|
|
1091
|
+
|
|
1092
|
+
const isGitRepo = fs.existsSync(path.join(process.cwd(), ".git"));
|
|
1093
|
+
if (hasDetectDefaultBranch && isGitRepo) {
|
|
1094
|
+
try {
|
|
1095
|
+
const base = engine.detectDefaultBranch({ cwd: process.cwd() });
|
|
1096
|
+
console.log(` ℹ️ Default branch: ${base}`);
|
|
1097
|
+
} catch (err) {
|
|
1098
|
+
console.log(` ⚠️ Default branch detection failed: ${err.message}`);
|
|
1099
|
+
issues.push({ level: "warn", msg: "Default branch detection failed" });
|
|
1100
|
+
}
|
|
1101
|
+
} else if (!isGitRepo) {
|
|
1102
|
+
console.log(" ℹ️ Default branch: N/A (not a git repo)");
|
|
1103
|
+
}
|
|
1084
1104
|
} catch (err) {
|
|
1085
1105
|
console.log(` ⚠️ Engine load failed: ${err.message}`);
|
|
1086
1106
|
issues.push({ level: "warn", msg: `Engine load failed: ${err.message}` });
|
|
@@ -1364,6 +1384,11 @@ function upgradeCommand(org, repos, orchName) {
|
|
|
1364
1384
|
|
|
1365
1385
|
async function main() {
|
|
1366
1386
|
const args = process.argv.slice(2);
|
|
1387
|
+
|
|
1388
|
+
// i18n: resolve locale from --lang flag, env, or default. Must happen before
|
|
1389
|
+
// any user-facing output so printHelp() and error messages respect the choice.
|
|
1390
|
+
i18n.setLocale(i18n.parseLangFlag(args));
|
|
1391
|
+
|
|
1367
1392
|
const cmd = args[0];
|
|
1368
1393
|
|
|
1369
1394
|
if (!cmd || cmd === "--help" || cmd === "-h" || cmd === "help") {
|
|
@@ -1570,13 +1595,26 @@ async function main() {
|
|
|
1570
1595
|
} else if (sub === "vision") {
|
|
1571
1596
|
await uiux.uiuxVisionReview({
|
|
1572
1597
|
screenshotPath: get("--screenshot"),
|
|
1598
|
+
url: get("--url"),
|
|
1573
1599
|
viewport: get("--viewport") || "desktop",
|
|
1574
1600
|
projectSlug: get("--project") || path.basename(process.cwd()),
|
|
1575
1601
|
});
|
|
1602
|
+
} else if (sub === "capture") {
|
|
1603
|
+
// PR-G5 — standalone URL→screenshot capture (no Playwright)
|
|
1604
|
+
const url = get("--url");
|
|
1605
|
+
if (!url) { console.error("❌ --url required for 'uiux capture'"); process.exit(1); }
|
|
1606
|
+
const out = get("--out") || null;
|
|
1607
|
+
const cap = await uiux.captureScreenshotFromUrl(url, {
|
|
1608
|
+
viewport: get("--viewport") || "desktop",
|
|
1609
|
+
outPath: out,
|
|
1610
|
+
});
|
|
1611
|
+
if (!cap.ok) { console.error(`❌ capture failed: ${cap.error}`); process.exit(1); }
|
|
1612
|
+
console.log(`✓ captured ${(cap.bytes / 1024).toFixed(1)} KB via ${cap.source} → ${cap.path}`);
|
|
1576
1613
|
} else if (sub === "cross-verify") {
|
|
1577
1614
|
await uiux.uiuxCrossVerify({
|
|
1578
1615
|
diffSource: args.includes("--branch") ? "branch" : "staged",
|
|
1579
1616
|
screenshotPath: get("--screenshot"),
|
|
1617
|
+
url: get("--url"),
|
|
1580
1618
|
viewport: get("--viewport") || "desktop",
|
|
1581
1619
|
projectSlug: get("--project") || path.basename(process.cwd()),
|
|
1582
1620
|
projectDir: get("--cwd") || process.cwd(),
|
|
@@ -1782,6 +1820,116 @@ async function main() {
|
|
|
1782
1820
|
return;
|
|
1783
1821
|
}
|
|
1784
1822
|
|
|
1823
|
+
if (cmd === "telegram") {
|
|
1824
|
+
if (!telegramWizard) {
|
|
1825
|
+
console.error("❌ telegram-wizard module not available in this install.");
|
|
1826
|
+
process.exit(1);
|
|
1827
|
+
}
|
|
1828
|
+
const sub = args[1] || "wizard";
|
|
1829
|
+
if (sub !== "wizard") {
|
|
1830
|
+
console.error(`❌ Unknown telegram subcommand: ${sub}`);
|
|
1831
|
+
console.error(` Use: solo-cto-agent telegram wizard`);
|
|
1832
|
+
process.exit(1);
|
|
1833
|
+
}
|
|
1834
|
+
const opts = {};
|
|
1835
|
+
const tokIdx = args.indexOf("--token"); if (tokIdx >= 0) opts.token = args[tokIdx + 1];
|
|
1836
|
+
const chatIdx = args.indexOf("--chat"); if (chatIdx >= 0) opts.chat = args[chatIdx + 1];
|
|
1837
|
+
const stoIdx = args.indexOf("--storage"); if (stoIdx >= 0) opts.storage = parseInt(args[stoIdx + 1], 10);
|
|
1838
|
+
const toutIdx = args.indexOf("--timeout"); if (toutIdx >= 0) opts.timeout = parseInt(args[toutIdx + 1], 10);
|
|
1839
|
+
if (args.includes("--non-interactive")) opts.nonInteractive = true;
|
|
1840
|
+
if (args.includes("--force")) opts.force = true;
|
|
1841
|
+
telegramWizard.runWizard(opts).then((res) => {
|
|
1842
|
+
if (!res || !res.ok) process.exit(1);
|
|
1843
|
+
}).catch((e) => {
|
|
1844
|
+
console.error(`❌ ${e.message}`);
|
|
1845
|
+
process.exit(1);
|
|
1846
|
+
});
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
if (cmd === "plugin") {
|
|
1851
|
+
if (!pluginManager) {
|
|
1852
|
+
console.error("❌ plugin-manager module not available in this install.");
|
|
1853
|
+
process.exit(1);
|
|
1854
|
+
}
|
|
1855
|
+
const sub = args[1] || "list";
|
|
1856
|
+
|
|
1857
|
+
if (sub === "list") {
|
|
1858
|
+
const plugins = pluginManager.listPlugins();
|
|
1859
|
+
if (args.includes("--json")) {
|
|
1860
|
+
console.log(JSON.stringify(plugins, null, 2));
|
|
1861
|
+
} else {
|
|
1862
|
+
console.log(pluginManager.formatPluginListText(plugins));
|
|
1863
|
+
}
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
if (sub === "show") {
|
|
1868
|
+
const name = args[2];
|
|
1869
|
+
if (!name) {
|
|
1870
|
+
console.error("❌ Usage: solo-cto-agent plugin show <name>");
|
|
1871
|
+
process.exit(1);
|
|
1872
|
+
}
|
|
1873
|
+
const manifest = pluginManager.readManifest();
|
|
1874
|
+
const plugin = pluginManager.findPlugin(manifest, name);
|
|
1875
|
+
if (!plugin) {
|
|
1876
|
+
console.error(`❌ Plugin not found: ${name}`);
|
|
1877
|
+
process.exit(1);
|
|
1878
|
+
}
|
|
1879
|
+
console.log(JSON.stringify(plugin, null, 2));
|
|
1880
|
+
return;
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
if (sub === "add") {
|
|
1884
|
+
const pathIdx = args.indexOf("--path");
|
|
1885
|
+
const nameIdx = args.indexOf("--name");
|
|
1886
|
+
if (pathIdx < 0) {
|
|
1887
|
+
console.error("❌ Usage: solo-cto-agent plugin add --path <dir> [--name <override>]");
|
|
1888
|
+
console.error(" (npm install coming in a later release; use --path for local dirs.)");
|
|
1889
|
+
process.exit(1);
|
|
1890
|
+
}
|
|
1891
|
+
const dir = path.resolve(args[pathIdx + 1]);
|
|
1892
|
+
const read = pluginManager.readPackageJsonFromPath(dir);
|
|
1893
|
+
if (!read.ok) {
|
|
1894
|
+
console.error(`❌ ${read.error}`);
|
|
1895
|
+
process.exit(1);
|
|
1896
|
+
}
|
|
1897
|
+
const pkg = nameIdx >= 0 ? { ...read.pkg, name: args[nameIdx + 1] } : read.pkg;
|
|
1898
|
+
const source = `path:${dir}`;
|
|
1899
|
+
const res = pluginManager.addPlugin({ pkg, source });
|
|
1900
|
+
if (!res.ok) {
|
|
1901
|
+
console.error("❌ Plugin validation failed:");
|
|
1902
|
+
for (const e of res.errors) console.error(` - ${e}`);
|
|
1903
|
+
process.exit(1);
|
|
1904
|
+
}
|
|
1905
|
+
console.log(`✓ ${res.replaced ? "Updated" : "Added"} ${res.plugin.name}@${res.plugin.version} (${source})`);
|
|
1906
|
+
console.log(` agents: ${res.plugin.agents.join(", ")}`);
|
|
1907
|
+
if (res.plugin.capabilities.length) {
|
|
1908
|
+
console.log(` capabilities: ${res.plugin.capabilities.join(", ")}`);
|
|
1909
|
+
}
|
|
1910
|
+
return;
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
if (sub === "remove" || sub === "rm") {
|
|
1914
|
+
const name = args[2];
|
|
1915
|
+
if (!name) {
|
|
1916
|
+
console.error("❌ Usage: solo-cto-agent plugin remove <name>");
|
|
1917
|
+
process.exit(1);
|
|
1918
|
+
}
|
|
1919
|
+
const res = pluginManager.removePlugin(name);
|
|
1920
|
+
if (!res.removed) {
|
|
1921
|
+
console.error(`❌ Plugin not found: ${name}`);
|
|
1922
|
+
process.exit(1);
|
|
1923
|
+
}
|
|
1924
|
+
console.log(`✓ Removed ${name}`);
|
|
1925
|
+
return;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
console.error(`❌ Unknown plugin subcommand: ${sub}`);
|
|
1929
|
+
console.error(` Use: solo-cto-agent plugin list|show|add|remove`);
|
|
1930
|
+
process.exit(1);
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1785
1933
|
printHelp();
|
|
1786
1934
|
process.exit(1);
|
|
1787
1935
|
}
|