great-cto 3.45.0 → 3.46.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/board/.claude-plugin/plugin.json +1 -1
- package/dist/codex.js +90 -1
- package/dist/main.js +26 -0
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "great-cto",
|
|
3
3
|
"description": "You already have the agent. This is everything around it. great_cto runs Claude Code as a pipeline of 71 specialist agents \u2014 an independent model checks each stage before the next builds on it, spending caps refuse rather than warn, and three decisions stay yours: what gets built, how, and whether it ships.",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.46.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Alexander Velikiy",
|
|
7
7
|
"url": "https://hashnode.com/@Greatcto"
|
package/dist/codex.js
CHANGED
|
@@ -48,7 +48,7 @@ export function codexInstallPlan({ repoDir, codexOnPath }) {
|
|
|
48
48
|
"skills — all of them, the same tree Claude Code reads",
|
|
49
49
|
"the MCP server (great_cto), resolved from npm",
|
|
50
50
|
"the controlled role pipeline — run `npx --yes great-cto codex-host doctor` then `npx --yes great-cto codex-host start`",
|
|
51
|
-
"six safety guards as Codex plugin hooks (destructive commands, gate bypasses, a neighbour session's work, secrets in files, frozen gates, weakened checks) and two hints that never block (edit-impact, lesson-tripwire) — Codex asks you to review them once on the next interactive `codex` start; they do not run until you do",
|
|
51
|
+
"six safety guards as Codex plugin hooks (destructive commands, gate bypasses, a neighbour session's work, secrets in files, frozen gates, weakened checks) and two hints that never block (edit-impact, lesson-tripwire) — Codex asks you to review them once on the next interactive `codex` start; they do not run until you do. Codex does not update great_cto by itself — `great-cto upgrade` (or `great-cto upgrade codex`) does",
|
|
52
52
|
],
|
|
53
53
|
// Codex runs plugin hooks (verified 2026-09-28, codex-cli 0.153.4) but has no
|
|
54
54
|
// plugin surface for commands or role agents. Saying "installed" without saying
|
|
@@ -60,3 +60,92 @@ export function codexInstallPlan({ repoDir, codexOnPath }) {
|
|
|
60
60
|
],
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
|
+
// ── Keeping the Codex install current ────────────────────────────────────────
|
|
64
|
+
//
|
|
65
|
+
// Codex installs great_cto from a Git marketplace snapshot and never refreshes it on
|
|
66
|
+
// its own. `release.sh` and `install-local` update the Claude Code plugin only, so on
|
|
67
|
+
// the maintainer's machine Codex sat at 3.37.0 while 3.45.0 shipped — every Codex
|
|
68
|
+
// guard released in between (3.42–3.45) had never reached it. Measured 2026-09-30:
|
|
69
|
+
// `codex plugin marketplace upgrade great-cto` refreshes the snapshot AND the installed
|
|
70
|
+
// plugin cache in one step. `great-cto upgrade` now runs it when Codex has the
|
|
71
|
+
// great-cto marketplace configured.
|
|
72
|
+
import { existsSync as fsExists, readdirSync, readFileSync as fsRead } from "node:fs";
|
|
73
|
+
import { homedir as home } from "node:os";
|
|
74
|
+
import { join as pjoin } from "node:path";
|
|
75
|
+
import { execFileSync } from "node:child_process";
|
|
76
|
+
/** A usable `codex` binary: PATH first, then nvm installs, then the desktop app. Null when none. */
|
|
77
|
+
export function findCodexBinary(env = process.env, h = home()) {
|
|
78
|
+
for (const dir of String(env.PATH || "").split(":")) {
|
|
79
|
+
if (dir && fsExists(pjoin(dir, "codex")))
|
|
80
|
+
return pjoin(dir, "codex");
|
|
81
|
+
}
|
|
82
|
+
// A shell that did not load nvm (a conda base, a login shell without .nvmrc) cannot
|
|
83
|
+
// see an npm-installed codex; look where nvm puts it.
|
|
84
|
+
const nvm = pjoin(h, ".nvm", "versions", "node");
|
|
85
|
+
let versions = [];
|
|
86
|
+
try {
|
|
87
|
+
versions = readdirSync(nvm).sort().reverse();
|
|
88
|
+
}
|
|
89
|
+
catch { /* no nvm */ }
|
|
90
|
+
for (const v of versions) {
|
|
91
|
+
const p = pjoin(nvm, v, "bin", "codex");
|
|
92
|
+
if (fsExists(p))
|
|
93
|
+
return p;
|
|
94
|
+
}
|
|
95
|
+
const app = "/Applications/Codex.app/Contents/Resources/codex";
|
|
96
|
+
return fsExists(app) ? app : null;
|
|
97
|
+
}
|
|
98
|
+
/** Does Codex have great_cto's marketplace configured? Read from its config.toml. */
|
|
99
|
+
export function codexHasGreatCtoMarketplace(codexHome = pjoin(home(), ".codex")) {
|
|
100
|
+
try {
|
|
101
|
+
return /^\s*\[marketplaces\.(?:"great-cto"|great-cto)\]\s*$/m.test(fsRead(pjoin(codexHome, "config.toml"), "utf8"));
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** The newest great_cto version in Codex's plugin cache, or null. */
|
|
108
|
+
export function installedCodexVersion(codexHome = pjoin(home(), ".codex")) {
|
|
109
|
+
const cache = pjoin(codexHome, "plugins", "cache");
|
|
110
|
+
let markets = [];
|
|
111
|
+
try {
|
|
112
|
+
markets = readdirSync(cache);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const versions = [];
|
|
118
|
+
for (const m of markets) {
|
|
119
|
+
try {
|
|
120
|
+
versions.push(...readdirSync(pjoin(cache, m, "great-cto")).filter((v) => /^\d+\.\d+\.\d+/.test(v)));
|
|
121
|
+
}
|
|
122
|
+
catch { /* not this market */ }
|
|
123
|
+
}
|
|
124
|
+
const key = (v) => v.split(".").map((n) => parseInt(n, 10) || 0);
|
|
125
|
+
versions.sort((a, b) => { const x = key(a), y = key(b); for (let i = 0; i < 3; i++)
|
|
126
|
+
if (x[i] !== y[i])
|
|
127
|
+
return x[i] - y[i]; return 0; });
|
|
128
|
+
return versions.at(-1) ?? null;
|
|
129
|
+
}
|
|
130
|
+
/** Refresh great_cto in Codex. Skips, with the reason, when Codex or the marketplace is absent. */
|
|
131
|
+
export function upgradeCodexPlugin(opts = {}) {
|
|
132
|
+
const codexHome = opts.codexHome ?? pjoin(home(), ".codex");
|
|
133
|
+
if (!codexHasGreatCtoMarketplace(codexHome)) {
|
|
134
|
+
return { status: "skipped", fromVersion: null, toVersion: null, reason: "great_cto is not installed for Codex" };
|
|
135
|
+
}
|
|
136
|
+
const from = installedCodexVersion(codexHome);
|
|
137
|
+
const bin = opts.bin === undefined ? findCodexBinary() : opts.bin;
|
|
138
|
+
if (!bin) {
|
|
139
|
+
return { status: "skipped", fromVersion: from, toVersion: from, reason: "codex not found on PATH or under ~/.nvm — run: codex plugin marketplace upgrade great-cto" };
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
execFileSync(bin, ["plugin", "marketplace", "upgrade", "great-cto"], {
|
|
143
|
+
stdio: ["ignore", "pipe", "pipe"], timeout: 180_000, env: { ...process.env, CODEX_HOME: codexHome },
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
return { status: "failed", fromVersion: from, toVersion: from, reason: String(e.message).split("\n")[0] };
|
|
148
|
+
}
|
|
149
|
+
const to = installedCodexVersion(codexHome);
|
|
150
|
+
return { status: to && to !== from ? "upgraded" : "already_latest", fromVersion: from, toVersion: to };
|
|
151
|
+
}
|
package/dist/main.js
CHANGED
|
@@ -772,6 +772,7 @@ ${bold("Upgrade:")}
|
|
|
772
772
|
great-cto upgrade superpowers Upgrade superpowers only
|
|
773
773
|
great-cto upgrade beads Upgrade beads only
|
|
774
774
|
great-cto upgrade --self Upgrade the great-cto CLI itself (also: upgrade self)
|
|
775
|
+
great-cto upgrade codex Refresh great_cto in Codex (also part of plain upgrade)
|
|
775
776
|
great-cto uninstall Plan: what would be removed, what is kept (changes nothing)
|
|
776
777
|
great-cto uninstall --yes Remove plugin, agents, commands, board service, caches
|
|
777
778
|
--purge-data Also move ~/.great_cto aside (reversible, never deleted)
|
|
@@ -1435,6 +1436,9 @@ async function runUpgrade(rawArgv, args) {
|
|
|
1435
1436
|
// Optional positional: great-cto upgrade [plugin-name]
|
|
1436
1437
|
const pluginArg = firstArgAfterUpgrade;
|
|
1437
1438
|
const targetPlugin = pluginArg && !pluginArg.startsWith("--") ? pluginArg : undefined;
|
|
1439
|
+
// `great-cto upgrade codex` refreshes only the Codex install.
|
|
1440
|
+
if (targetPlugin === "codex")
|
|
1441
|
+
return reportCodexUpgrade();
|
|
1438
1442
|
let results;
|
|
1439
1443
|
if (targetPlugin) {
|
|
1440
1444
|
const plugin = COMPANION_PLUGINS.find((p) => p.name === targetPlugin);
|
|
@@ -1458,6 +1462,28 @@ async function runUpgrade(rawArgv, args) {
|
|
|
1458
1462
|
warn(`${r.name} skipped — ${r.reason ?? "unknown reason"}`);
|
|
1459
1463
|
}
|
|
1460
1464
|
}
|
|
1465
|
+
// Codex keeps its own snapshot of great_cto and never refreshes it; without this
|
|
1466
|
+
// step every Codex guard shipped after the first install stays out of reach.
|
|
1467
|
+
if (!targetPlugin)
|
|
1468
|
+
await reportCodexUpgrade();
|
|
1469
|
+
return 0;
|
|
1470
|
+
}
|
|
1471
|
+
async function reportCodexUpgrade() {
|
|
1472
|
+
const { upgradeCodexPlugin } = await import("./codex.js");
|
|
1473
|
+
const r = upgradeCodexPlugin();
|
|
1474
|
+
if (r.status === "upgraded") {
|
|
1475
|
+
success(`codex: great_cto ${r.fromVersion ?? "—"} → ${r.toVersion} — new or changed hooks need one review at the next \`codex\` start`);
|
|
1476
|
+
}
|
|
1477
|
+
else if (r.status === "already_latest") {
|
|
1478
|
+
log(` ${dim(`codex: great_cto ${r.toVersion} already at latest`)}`);
|
|
1479
|
+
}
|
|
1480
|
+
else if (r.status === "failed") {
|
|
1481
|
+
warn(`codex: upgrade failed — ${r.reason}`);
|
|
1482
|
+
return 1;
|
|
1483
|
+
}
|
|
1484
|
+
else if (r.reason && r.reason !== "great_cto is not installed for Codex") {
|
|
1485
|
+
warn(`codex: ${r.reason}`);
|
|
1486
|
+
}
|
|
1461
1487
|
return 0;
|
|
1462
1488
|
}
|
|
1463
1489
|
async function main() {
|
package/package.json
CHANGED