great-cto 3.70.0 → 3.71.1
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 +9 -1
- package/board/.claude-plugin/plugin.json +93 -1
- package/board/packages/board/lib/routes.mjs +14 -1
- package/board/packages/board/lib/work.mjs +34 -3
- package/board/packages/board/public/assets/admin-shell.css +63 -0
- package/board/packages/board/public/assets/admin-shell.js +39 -5
- package/board/packages/board/public/assets/work-view.js +320 -37
- package/board/packages/board/public/index.html +43 -1
- package/board/scripts/codex-pipeline.mjs +7 -1
- package/board/scripts/lib/claude-exec.mjs +49 -6
- package/board/scripts/lib/codex-exec.mjs +42 -18
- package/board/scripts/lib/codex-host-state.mjs +18 -2
- package/board/scripts/lib/codex-pipeline.mjs +44 -7
- package/board/scripts/lib/host-graph-bindings.mjs +135 -0
- package/board/scripts/lib/host-graph-events.mjs +157 -0
- package/board/scripts/lib/host-graph-journal.mjs +210 -0
- package/board/scripts/lib/host-graph-observer.mjs +61 -0
- package/board/scripts/lib/host-graph-runtime.mjs +38 -0
- package/board/scripts/work-task.mjs +17 -4
- package/dist/adapt.js +10 -21
- package/dist/codex.js +4 -15
- package/dist/companion.js +6 -122
- package/dist/installer.js +4 -72
- package/dist/main.js +51 -118
- package/dist/release-activation.mjs +561 -0
- package/dist/release-archive.mjs +165 -0
- package/dist/release-trust.mjs +514 -0
- package/dist/release-update.mjs +303 -0
- package/dist/self-upgrade.js +10 -39
- package/dist/update-check.js +1 -0
- package/dist/upgrade.js +5 -120
- package/package.json +1 -1
- package/board/packages/board/fixtures/beads-read-owner.mjs +0 -3
- package/board/packages/board/fixtures/fake-bd-controlled.mjs +0 -55
- package/board/packages/board/fixtures/fake-bd-fail.sh +0 -9
- package/board/packages/board/fixtures/fake-bd-slow.sh +0 -12
- package/board/packages/board/fixtures/held-receipt-git.mjs +0 -21
package/dist/codex.js
CHANGED
|
@@ -72,7 +72,6 @@ export function codexInstallPlan({ repoDir, codexOnPath }) {
|
|
|
72
72
|
import { existsSync as fsExists, readdirSync, readFileSync as fsRead } from "node:fs";
|
|
73
73
|
import { homedir as home } from "node:os";
|
|
74
74
|
import { join as pjoin } from "node:path";
|
|
75
|
-
import { execFileSync } from "node:child_process";
|
|
76
75
|
/** A usable `codex` binary: PATH first, then nvm installs, then the desktop app. Null when none. */
|
|
77
76
|
export function findCodexBinary(env = process.env, h = home()) {
|
|
78
77
|
for (const dir of String(env.PATH || "").split(":")) {
|
|
@@ -134,18 +133,8 @@ export function upgradeCodexPlugin(opts = {}) {
|
|
|
134
133
|
return { status: "skipped", fromVersion: null, toVersion: null, reason: "great_cto is not installed for Codex" };
|
|
135
134
|
}
|
|
136
135
|
const from = installedCodexVersion(codexHome);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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 };
|
|
136
|
+
return {
|
|
137
|
+
status: "failed", fromVersion: from, toVersion: from,
|
|
138
|
+
reason: "verified native Codex activation is unsupported until no-fetch runtime and preactivation/recovery admission are proven; marketplace refresh was not invoked",
|
|
139
|
+
};
|
|
151
140
|
}
|
package/dist/companion.js
CHANGED
|
@@ -1,25 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Companion plugin installer — superpowers + beads.
|
|
3
|
-
*
|
|
4
|
-
* great_cto requires these two plugins to be present. This module installs
|
|
5
|
-
* them automatically during `great-cto init/install` so users get a working
|
|
6
|
-
* setup in one command without reading the "Requires:" line in the README.
|
|
7
|
-
*
|
|
8
|
-
* Install strategy:
|
|
9
|
-
* - Clone (depth=1) the latest semver tag from GitHub into
|
|
10
|
-
* ~/.claude/plugins/cache/local/<name>/<version>/
|
|
11
|
-
* - Enable <name>@local in ~/.claude/settings.json
|
|
12
|
-
* - Idempotent — skips if any version is already present in the cache dir
|
|
13
|
-
* - Best-effort — never fails the parent install; logs a human-friendly
|
|
14
|
-
* hint if git is unavailable or clone fails
|
|
15
|
-
*/
|
|
16
|
-
import { spawnSync, execFileSync } from "node:child_process";
|
|
17
|
-
import { existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
18
|
-
import { homedir } from "node:os";
|
|
19
|
-
import { join } from "node:path";
|
|
20
|
-
import { dim, success, log, warn } from "./ui.js";
|
|
21
|
-
import { enablePlugin } from "./settings.js";
|
|
22
|
-
import { semverDescending } from "./semver.js";
|
|
1
|
+
/** Companion metadata is discoverable; installation requires separate artifact admission. */
|
|
23
2
|
export const COMPANION_PLUGINS = [
|
|
24
3
|
{
|
|
25
4
|
name: "superpowers",
|
|
@@ -32,107 +11,12 @@ export const COMPANION_PLUGINS = [
|
|
|
32
11
|
repoUrl: "https://github.com/steveyegge/beads.git",
|
|
33
12
|
},
|
|
34
13
|
];
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
/** Returns true if any version folder already exists in the cache dir. */
|
|
39
|
-
function isAlreadyInstalled(name) {
|
|
40
|
-
const base = getPluginCacheDir(name);
|
|
41
|
-
if (!existsSync(base))
|
|
42
|
-
return null;
|
|
43
|
-
try {
|
|
44
|
-
const versions = readdirSync(base).filter((v) => /\S/.test(v));
|
|
45
|
-
if (versions.length === 0)
|
|
46
|
-
return null;
|
|
47
|
-
// Return the highest version, not filesystem order — matches
|
|
48
|
-
// upgrade.ts/installer.ts which both sort before picking.
|
|
49
|
-
return versions.sort(semverDescending)[0];
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
/** Detect the latest semver tag from a remote repo without cloning. */
|
|
56
|
-
export function detectLatestTag(repoUrl) {
|
|
57
|
-
try {
|
|
58
|
-
const out = execFileSync("git", ["ls-remote", "--tags", repoUrl], {
|
|
59
|
-
encoding: "utf-8",
|
|
60
|
-
timeout: 15_000,
|
|
61
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
62
|
-
});
|
|
63
|
-
const tags = out
|
|
64
|
-
.split("\n")
|
|
65
|
-
.map((line) => line.match(/refs\/tags\/v?([0-9]+\.[0-9]+\.[0-9]+)(?!\^)/)?.[1])
|
|
66
|
-
.filter((t) => !!t)
|
|
67
|
-
.sort(semverDescending);
|
|
68
|
-
return tags[0] ?? null;
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Install a single companion plugin.
|
|
76
|
-
* Silent no-op if already present. Best-effort on failure.
|
|
77
|
-
*/
|
|
14
|
+
/** Third-party snapshots have no publisher admission or managed recovery contract. */
|
|
15
|
+
export const COMPANION_ADMISSION_REQUIRED = "UNSUPPORTED: companion artifact admission and managed activation are not established; no files changed";
|
|
16
|
+
/** Refuse before network, cache inspection, settings enablement or overlay writes. */
|
|
78
17
|
export function installCompanionPlugin(plugin) {
|
|
79
|
-
|
|
80
|
-
// ── already installed? ─────────────────────────────────────────────────
|
|
81
|
-
const existing = isAlreadyInstalled(name);
|
|
82
|
-
if (existing) {
|
|
83
|
-
// Make sure it's enabled in settings even if it was manually placed
|
|
84
|
-
enablePlugin(pluginKey);
|
|
85
|
-
return { name, status: "already_present", version: existing };
|
|
86
|
-
}
|
|
87
|
-
// ── git available? ─────────────────────────────────────────────────────
|
|
88
|
-
try {
|
|
89
|
-
execFileSync("git", ["--version"], { stdio: "ignore", timeout: 5_000 });
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
const msg = `git not found — install git, then run: npx great-cto install`;
|
|
93
|
-
warn(`${name}: ${msg}`);
|
|
94
|
-
return { name, status: "skipped", version: "—", reason: msg };
|
|
95
|
-
}
|
|
96
|
-
// ── resolve version ────────────────────────────────────────────────────
|
|
97
|
-
const tag = detectLatestTag(repoUrl);
|
|
98
|
-
const version = tag ?? "main";
|
|
99
|
-
const ref = tag ? `v${tag}` : "main";
|
|
100
|
-
const destDir = join(getPluginCacheDir(name), version);
|
|
101
|
-
mkdirSync(getPluginCacheDir(name), { recursive: true });
|
|
102
|
-
log(dim(` installing ${name} ${version}…`));
|
|
103
|
-
// ── clone ──────────────────────────────────────────────────────────────
|
|
104
|
-
const cloneArgs = tag
|
|
105
|
-
? ["clone", "--depth=1", "--branch", ref, repoUrl, destDir]
|
|
106
|
-
: ["clone", "--depth=1", repoUrl, destDir];
|
|
107
|
-
const result = spawnSync("git", cloneArgs, {
|
|
108
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
109
|
-
timeout: 60_000,
|
|
110
|
-
});
|
|
111
|
-
if (result.status !== 0) {
|
|
112
|
-
const stderr = (result.stderr?.toString() ?? "").slice(0, 200);
|
|
113
|
-
const msg = `clone failed: ${stderr}`;
|
|
114
|
-
warn(`${name}: ${msg}`);
|
|
115
|
-
warn(` install manually: claude plugin install github.com/${repoUrl.replace("https://github.com/", "").replace(".git", "")}`);
|
|
116
|
-
return { name, status: "skipped", version: "—", reason: msg };
|
|
117
|
-
}
|
|
118
|
-
// ── enable in settings ─────────────────────────────────────────────────
|
|
119
|
-
enablePlugin(pluginKey);
|
|
120
|
-
success(`${name} ${version} installed`);
|
|
121
|
-
return { name, status: "installed", version };
|
|
18
|
+
return { name: plugin.name, status: "skipped", version: "—", reason: COMPANION_ADMISSION_REQUIRED };
|
|
122
19
|
}
|
|
123
|
-
/**
|
|
124
|
-
* Install all companion plugins declared in COMPANION_PLUGINS.
|
|
125
|
-
* Returns a summary array. Never throws — best-effort for all.
|
|
126
|
-
*/
|
|
127
20
|
export function installAllCompanions() {
|
|
128
|
-
return COMPANION_PLUGINS.map(
|
|
129
|
-
try {
|
|
130
|
-
return installCompanionPlugin(plugin);
|
|
131
|
-
}
|
|
132
|
-
catch (e) {
|
|
133
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
134
|
-
warn(`${plugin.name}: unexpected error — ${msg}`);
|
|
135
|
-
return { name: plugin.name, status: "skipped", version: "—", reason: msg };
|
|
136
|
-
}
|
|
137
|
-
});
|
|
21
|
+
return COMPANION_PLUGINS.map(installCompanionPlugin);
|
|
138
22
|
}
|
package/dist/installer.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
// Install the great_cto plugin into ~/.claude/plugins/cache/local/great_cto/<version>/.
|
|
2
2
|
// Uses git clone. Falls back to tarball fetch if git is unavailable.
|
|
3
|
-
import {
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
4
|
import { cpSync } from "node:fs";
|
|
5
|
-
import { existsSync, mkdirSync,
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync } from "node:fs";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
import { dirname, join } from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
-
import { log, success, warn, dim } from "./ui.js";
|
|
10
9
|
const REPO_URL = "https://github.com/avelikiy/great_cto.git";
|
|
11
10
|
export function hasGit() {
|
|
12
11
|
try {
|
|
@@ -60,75 +59,8 @@ export function findInstalledVersions() {
|
|
|
60
59
|
}
|
|
61
60
|
}
|
|
62
61
|
export function install(opts = {}) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
// Resolve version
|
|
67
|
-
let version = opts.version;
|
|
68
|
-
if (!version) {
|
|
69
|
-
const latest = detectLatestVersion();
|
|
70
|
-
if (!latest) {
|
|
71
|
-
warn("Could not detect latest version from GitHub tags — falling back to main branch.");
|
|
72
|
-
version = "main";
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
version = latest;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
const pluginDir = join(getPluginBaseDir(), version);
|
|
79
|
-
if (existsSync(pluginDir)) {
|
|
80
|
-
const manifest = join(pluginDir, ".claude-plugin", "plugin.json");
|
|
81
|
-
const looksValid = existsSync(manifest);
|
|
82
|
-
if (looksValid && !opts.force) {
|
|
83
|
-
return { installed: false, pluginDir, version, alreadyInstalled: true };
|
|
84
|
-
}
|
|
85
|
-
// Either corrupted, or --force: wipe and reinstall
|
|
86
|
-
if (!looksValid) {
|
|
87
|
-
warn(`Previous install at ${pluginDir} looks corrupted — reinstalling.`);
|
|
88
|
-
}
|
|
89
|
-
rmSync(pluginDir, { recursive: true, force: true });
|
|
90
|
-
}
|
|
91
|
-
mkdirSync(getPluginBaseDir(), { recursive: true });
|
|
92
|
-
log(dim(` cloning ${REPO_URL} into ${pluginDir}`));
|
|
93
|
-
const ref = /^[0-9]+\.[0-9]+\.[0-9]+$/.test(version) ? `v${version}` : version;
|
|
94
|
-
const result = spawnSync("git", ["clone", "--depth=1", "--branch", ref, REPO_URL, pluginDir], {
|
|
95
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
96
|
-
timeout: 120_000,
|
|
97
|
-
});
|
|
98
|
-
if (result.status !== 0) {
|
|
99
|
-
const stderr = result.stderr?.toString() ?? "";
|
|
100
|
-
// If branch/tag doesn't exist, try plain clone of main
|
|
101
|
-
if (stderr.includes("not found") || stderr.includes("Remote branch")) {
|
|
102
|
-
warn(`Tag ${ref} not found — cloning default branch.`);
|
|
103
|
-
rmSync(pluginDir, { recursive: true, force: true });
|
|
104
|
-
const r2 = spawnSync("git", ["clone", "--depth=1", REPO_URL, pluginDir], {
|
|
105
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
106
|
-
timeout: 120_000,
|
|
107
|
-
});
|
|
108
|
-
if (r2.status !== 0) {
|
|
109
|
-
throw new Error(`git clone failed: ${r2.stderr?.toString() ?? "unknown error"}`);
|
|
110
|
-
}
|
|
111
|
-
// Re-read version from actual plugin.json
|
|
112
|
-
version = readPluginVersion(pluginDir) ?? "main";
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
throw new Error(`git clone failed: ${stderr}`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
// A clone carries sources; the plugin runs on the build. Supply it from this
|
|
119
|
-
// CLI's own dist before checking whether the result can run.
|
|
120
|
-
const supplied = supplyBuiltDist(pluginDir);
|
|
121
|
-
if (supplied != null)
|
|
122
|
-
log(dim(` supplied ${supplied} built file(s) into packages/cli/dist/`));
|
|
123
|
-
// Sanity check: can it RUN, not merely "did files arrive".
|
|
124
|
-
const missing = missingRuntimeParts(pluginDir);
|
|
125
|
-
if (missing.length) {
|
|
126
|
-
throw new Error(`Install appeared to succeed but the plugin cannot run — missing:\n` +
|
|
127
|
-
missing.map((m) => ` - ${m}`).join("\n") +
|
|
128
|
-
`\nThis is an install bug, not a configuration problem. Please report it with this list.`);
|
|
129
|
-
}
|
|
130
|
-
success(`plugin installed at ${pluginDir}`);
|
|
131
|
-
return { installed: true, pluginDir, version, alreadyInstalled: false };
|
|
62
|
+
throw new Error("Plugin installation refused: a signed complete runtime and verified managed activation are required. " +
|
|
63
|
+
"Mutable Git/main fallback and supplying dist from the running CLI are disabled.");
|
|
132
64
|
}
|
|
133
65
|
/**
|
|
134
66
|
* The plugin needs BUILT JavaScript, and a git clone does not contain any.
|
package/dist/main.js
CHANGED
|
@@ -121,6 +121,13 @@ function parseArgs(argv) {
|
|
|
121
121
|
args.command = "console";
|
|
122
122
|
else if (a === "telemetry")
|
|
123
123
|
args.command = "telemetry";
|
|
124
|
+
else if (a === "--no-telemetry")
|
|
125
|
+
continue;
|
|
126
|
+
else if (a === "distribution") {
|
|
127
|
+
args.command = "distribution";
|
|
128
|
+
args.taskArgs = argv.slice(i + 1);
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
124
131
|
else if (a === "register")
|
|
125
132
|
args.command = "register";
|
|
126
133
|
else if (a === "ci")
|
|
@@ -759,8 +766,8 @@ ${bold("Usage:")}
|
|
|
759
766
|
npx great-cto run "describe the task" [--host claude-code|codex] [--dir PATH]
|
|
760
767
|
npx great-cto status [RUN_UUID] [--host codex] [--json]
|
|
761
768
|
npx great-cto resume [RUN_UUID] [--host codex]
|
|
762
|
-
npx great-cto install [options]
|
|
763
|
-
npx great-cto [init] [options]
|
|
769
|
+
npx great-cto install [options] Refused until managed plugin admission is supported
|
|
770
|
+
npx great-cto [init] [options] Plugin activation currently unsupported (ADR-045)
|
|
764
771
|
npx great-cto board [--port 3141] [--no-open]
|
|
765
772
|
npx great-cto console [--port 8788] [--bind 0.0.0.0] [--no-open]
|
|
766
773
|
npx great-cto register [--dir PATH]
|
|
@@ -770,8 +777,9 @@ ${bold("Usage:")}
|
|
|
770
777
|
npx great-cto task work decisions|approve|verify|complete|metrics|preview|publish ...
|
|
771
778
|
npx great-cto codex-host doctor|list|start|resume|status ...
|
|
772
779
|
npx great-cto serve [--port 3142]
|
|
773
|
-
npx great-cto upgrade [superpowers|beads]
|
|
774
|
-
npx great-cto upgrade --self
|
|
780
|
+
npx great-cto upgrade [superpowers|beads] Unsupported pending companion artifact admission
|
|
781
|
+
npx great-cto upgrade --self Verified update for an owned plain npm installation
|
|
782
|
+
npx great-cto distribution status Inspect separately configured verifier toolchain
|
|
775
783
|
npx great-cto statusline install Record Claude's plan use (5h / weekly %) for the board; keeps your status line
|
|
776
784
|
npx great-cto statusline uninstall Put your previous status line back
|
|
777
785
|
npx great-cto uninstall [--yes] Show what great_cto installed; --yes removes it (your data stays)
|
|
@@ -807,16 +815,18 @@ ${bold("Register:")}
|
|
|
807
815
|
run this if the project doesn't appear in board)
|
|
808
816
|
|
|
809
817
|
${bold("Upgrade:")}
|
|
810
|
-
great-cto upgrade
|
|
811
|
-
great-cto upgrade superpowers
|
|
812
|
-
great-cto upgrade beads
|
|
813
|
-
great-cto upgrade --self
|
|
814
|
-
great-cto upgrade codex
|
|
818
|
+
great-cto upgrade Refused: choose a supported explicit update route
|
|
819
|
+
great-cto upgrade superpowers Unsupported until companion runtime admission is available
|
|
820
|
+
great-cto upgrade beads Unsupported until companion runtime admission is available
|
|
821
|
+
great-cto upgrade --self Verify signed artifacts, then update an owned plain npm prefix
|
|
822
|
+
great-cto upgrade codex Unsupported until native no-fetch activation is proven
|
|
823
|
+
great-cto distribution status Inspect operator verifier configuration; no release admission
|
|
824
|
+
great-cto distribution configure ... Register explicit tool paths and digests (see ADR-045)
|
|
815
825
|
great-cto uninstall Plan: what would be removed, what is kept (changes nothing)
|
|
816
826
|
great-cto uninstall --yes Remove plugin, agents, commands, board service, caches
|
|
817
827
|
--purge-data Also move ~/.great_cto aside (reversible, never deleted)
|
|
818
828
|
--projects Also remove the pre-push hooks init wrote into your projects
|
|
819
|
-
${dim("(
|
|
829
|
+
${dim("(Missing artifact proof preserves the selected installation; no mutable fallback)")}
|
|
820
830
|
|
|
821
831
|
${bold("CI gate:")}
|
|
822
832
|
great-cto ci Single-command CI gate (archetype + budget check)
|
|
@@ -879,57 +889,8 @@ ${bold("Links:")}
|
|
|
879
889
|
`);
|
|
880
890
|
}
|
|
881
891
|
async function runInitCodex(args) {
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
log(bold("great-cto · Codex host install"));
|
|
885
|
-
log("");
|
|
886
|
-
// Is the host actually there? The previous version of this function wrote
|
|
887
|
-
// hook scripts and a config fragment without checking, into
|
|
888
|
-
// `~/.codex/skills/great_cto/` — a directory a working Codex install does not
|
|
889
|
-
// have. It reported success either way.
|
|
890
|
-
let codexOnPath = false;
|
|
891
|
-
try {
|
|
892
|
-
execFileSync("codex", ["--version"], { stdio: "ignore", timeout: 15000 });
|
|
893
|
-
codexOnPath = true;
|
|
894
|
-
}
|
|
895
|
-
catch {
|
|
896
|
-
codexOnPath = false;
|
|
897
|
-
}
|
|
898
|
-
const plan = codexInstallPlan({ repoDir: args.dir, codexOnPath });
|
|
899
|
-
if (!plan.ok) {
|
|
900
|
-
error(plan.why);
|
|
901
|
-
return 1;
|
|
902
|
-
}
|
|
903
|
-
log(`This registers ${cyan(args.dir)} as a Codex plugin marketplace and installs from it.`);
|
|
904
|
-
log("");
|
|
905
|
-
log(bold("Carries over:"));
|
|
906
|
-
for (const s2 of plan.supported)
|
|
907
|
-
log(` ${green("✓")} ${s2}`);
|
|
908
|
-
log("");
|
|
909
|
-
// Printed on every run, not buried in docs. An install that lists only what it
|
|
910
|
-
// gives promises a pipeline this host cannot run.
|
|
911
|
-
log(bold("Does NOT carry over on Codex:"));
|
|
912
|
-
for (const s2 of plan.notSupported)
|
|
913
|
-
log(` ${yellow("—")} ${s2}`);
|
|
914
|
-
log("");
|
|
915
|
-
if (args.dryRun) {
|
|
916
|
-
log(yellow("dry-run: these commands would run."));
|
|
917
|
-
for (const c of plan.commands)
|
|
918
|
-
log(` ${dim(c)}`);
|
|
919
|
-
return 0;
|
|
920
|
-
}
|
|
921
|
-
for (const c of plan.commands) {
|
|
922
|
-
log(dim(` $ ${c}`));
|
|
923
|
-
const parts = c.split(" ");
|
|
924
|
-
const r = spawnSync(parts[0], parts.slice(1), { stdio: "inherit", timeout: 120000 });
|
|
925
|
-
if (r.status !== 0) {
|
|
926
|
-
error(`command failed: ${c}`);
|
|
927
|
-
return 1;
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
log("");
|
|
931
|
-
log(`${green("✓")} installed. Verify with: ${cyan("codex plugin list")}`);
|
|
932
|
-
return 0;
|
|
892
|
+
error("Codex activation is unsupported until a verified no-fetch local runtime and recovery route are proven. No marketplace or installed plugin was changed.");
|
|
893
|
+
return 1;
|
|
933
894
|
}
|
|
934
895
|
async function runInit(args) {
|
|
935
896
|
// Route to Codex-specific install if --host codex
|
|
@@ -1428,40 +1389,11 @@ function installPrePushHook(projectDir) {
|
|
|
1428
1389
|
}
|
|
1429
1390
|
}
|
|
1430
1391
|
async function runSelfUpgrade() {
|
|
1431
|
-
const {
|
|
1432
|
-
const
|
|
1433
|
-
const
|
|
1434
|
-
log(
|
|
1435
|
-
|
|
1436
|
-
log("");
|
|
1437
|
-
const result = performSelfUpgrade({ currentVersion, binaryPath });
|
|
1438
|
-
if (result.manager === "npx") {
|
|
1439
|
-
log(result.message);
|
|
1440
|
-
return 0;
|
|
1441
|
-
}
|
|
1442
|
-
if (result.exitCode !== 0) {
|
|
1443
|
-
error(result.message);
|
|
1444
|
-
return result.exitCode;
|
|
1445
|
-
}
|
|
1446
|
-
success(result.message);
|
|
1447
|
-
// The CLI is upgraded; the plugin Claude Code actually loads may not be.
|
|
1448
|
-
// `npm i -g` never touches the plugin cache, so without this the user is told
|
|
1449
|
-
// the upgrade succeeded and keeps running the previous release's hooks and
|
|
1450
|
-
// agent prompts with nothing anywhere saying so.
|
|
1451
|
-
try {
|
|
1452
|
-
const { pluginCacheLagWarning } = await import("./self-upgrade.js");
|
|
1453
|
-
const { readdirSync } = await import("node:fs");
|
|
1454
|
-
const { join } = await import("node:path");
|
|
1455
|
-
const cacheRoot = join(homedir(), ".claude", "plugins", "cache", "local", "great_cto");
|
|
1456
|
-
const dirs = readdirSync(cacheRoot);
|
|
1457
|
-
const warning = pluginCacheLagWarning(result.newVersion ?? "", dirs);
|
|
1458
|
-
if (warning) {
|
|
1459
|
-
log("");
|
|
1460
|
-
log(dim(warning));
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
catch { /* no plugin cache is not a lag — the plugin is simply not installed */ }
|
|
1464
|
-
return 0;
|
|
1392
|
+
const { resolveRunningBinaryPath } = await import("./self-upgrade.js");
|
|
1393
|
+
const { runVerifiedSelfUpgrade } = await import("./release-update.mjs");
|
|
1394
|
+
const result = await runVerifiedSelfUpgrade({ currentVersion: getCliVersion(), binaryPath: resolveRunningBinaryPath() });
|
|
1395
|
+
log(JSON.stringify(result));
|
|
1396
|
+
return ["SMOKE_PASSED", "ALREADY_ACTIVE", "ALREADY_CURRENT"].includes(result.state) ? 0 : 1;
|
|
1465
1397
|
}
|
|
1466
1398
|
async function runUpgrade(rawArgv, args) {
|
|
1467
1399
|
// `upgrade --self` / `upgrade self` — upgrade the CLI itself, not companion plugins.
|
|
@@ -1490,22 +1422,10 @@ async function runUpgrade(rawArgv, args) {
|
|
|
1490
1422
|
else {
|
|
1491
1423
|
results = await upgradeAll();
|
|
1492
1424
|
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
else if (r.status === "already_latest") {
|
|
1498
|
-
log(` ${dim(`${r.name} ${r.toVersion} already at latest (overlays re-applied)`)}`);
|
|
1499
|
-
}
|
|
1500
|
-
else {
|
|
1501
|
-
warn(`${r.name} skipped — ${r.reason ?? "unknown reason"}`);
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
// Codex keeps its own snapshot of great_cto and never refreshes it; without this
|
|
1505
|
-
// step every Codex guard shipped after the first install stays out of reach.
|
|
1506
|
-
if (!targetPlugin)
|
|
1507
|
-
await reportCodexUpgrade();
|
|
1508
|
-
return 0;
|
|
1425
|
+
// Unadmitted companion routes fail before fetching, deleting caches or enabling plugins.
|
|
1426
|
+
for (const r of results)
|
|
1427
|
+
warn(`${r.name}: ${r.reason ?? "UNSUPPORTED: companion artifact admission required"}`);
|
|
1428
|
+
return 1;
|
|
1509
1429
|
}
|
|
1510
1430
|
async function reportCodexUpgrade() {
|
|
1511
1431
|
const { upgradeCodexPlugin } = await import("./codex.js");
|
|
@@ -1538,12 +1458,13 @@ async function main() {
|
|
|
1538
1458
|
// widen types at every call site.
|
|
1539
1459
|
const finish = async (code) => {
|
|
1540
1460
|
try {
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1461
|
+
if (!rawArgv.includes("--no-telemetry"))
|
|
1462
|
+
await sendUsagePing({
|
|
1463
|
+
cliVersion: getCliVersion(),
|
|
1464
|
+
subcommand: args.command,
|
|
1465
|
+
exitCode: code,
|
|
1466
|
+
durationMs: Date.now() - __tStart,
|
|
1467
|
+
});
|
|
1547
1468
|
}
|
|
1548
1469
|
catch { /* telemetry never affects the exit */ }
|
|
1549
1470
|
// Update hint / prompt — printed after the command's own output, never
|
|
@@ -1561,6 +1482,12 @@ async function main() {
|
|
|
1561
1482
|
process.exit(code);
|
|
1562
1483
|
};
|
|
1563
1484
|
// `great-cto telemetry <on|off|status|whoami>` — inspect / toggle, never sends.
|
|
1485
|
+
if (args.command === "distribution") {
|
|
1486
|
+
const { runDistributionCommand } = await import("./release-update.mjs");
|
|
1487
|
+
const result = runDistributionCommand(rawArgv.slice(rawArgv.indexOf("distribution") + 1));
|
|
1488
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
1489
|
+
await finish(result.state === "REFUSED" ? 1 : 0);
|
|
1490
|
+
}
|
|
1564
1491
|
if (args.command === "telemetry") {
|
|
1565
1492
|
const { exitCode, output } = telemetrySubcommand(args.positional[0]);
|
|
1566
1493
|
process.stdout.write(output);
|
|
@@ -1680,7 +1607,13 @@ async function main() {
|
|
|
1680
1607
|
if (args.command === "run" || args.command === "status" || args.command === "resume") {
|
|
1681
1608
|
const { runDaily } = await import('./run.js');
|
|
1682
1609
|
const dailyArgs = [...(args.hostInput !== undefined ? ['--host', args.hostInput] : []), ...(args.taskArgs ?? [])];
|
|
1683
|
-
|
|
1610
|
+
const code = runDaily(args.command, dailyArgs, { cwd: args.dir });
|
|
1611
|
+
if (args.command === 'status') {
|
|
1612
|
+
await new Promise((resolve, reject) => {
|
|
1613
|
+
process.stdout.write('', error => error ? reject(error) : resolve());
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
await finish(code);
|
|
1684
1617
|
}
|
|
1685
1618
|
if (args.command === "serve") {
|
|
1686
1619
|
try {
|