great-cto 3.59.0 → 3.59.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.
@@ -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 72 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.59.0",
4
+ "version": "3.59.1",
5
5
  "author": {
6
6
  "name": "Alexander Velikiy",
7
7
  "url": "https://hashnode.com/@Greatcto"
@@ -24,8 +24,9 @@ import { fileURLToPath } from 'node:url';
24
24
 
25
25
  import { classify } from './change-tier.mjs';
26
26
  import { selectJudgeModel } from './judge-model.mjs';
27
- // effectiveGates lives in the built CLI package (TS → dist).
28
- import { effectiveGates } from '../../packages/cli/dist/archetypes.js';
27
+ // Generated from the canonical CLI policy, but shipped in Git too: marketplace
28
+ // installs do not contain the gitignored CLI build.
29
+ import { effectiveGates } from './gate-policy.mjs';
29
30
 
30
31
  /** Parse archetype + project_size out of a .great_cto/PROJECT.md body. */
31
32
  export function parseProject(text) {
@@ -0,0 +1,97 @@
1
+ // Generated by scripts/build-gate-policy.mjs from packages/cli/src/archetypes.ts.
2
+ // Do not edit; regenerate, then run --check. No CLI build is needed at runtime.
3
+ export const GATES_BY_ARCHETYPE = {
4
+ "web-service": ["plan", "qa", "ship"],
5
+ "mobile-app": ["plan", "qa", "ship"],
6
+ "ai-system": ["plan", "cost", "qa", "security", "ship"],
7
+ "agent-product": ["plan", "cost", "qa", "security", "ship"],
8
+ "mlops": ["plan", "cost", "qa", "security", "ship"],
9
+ "data-platform": ["plan", "qa", "ship"],
10
+ "streaming": ["plan", "qa", "ship"],
11
+ "infra": ["plan", "qa", "ship"],
12
+ "library": ["plan", "qa", "ship"],
13
+ "cli-tool": ["plan", "qa", "ship"],
14
+ "commerce": ["plan", "qa", "security", "ship", "compliance"],
15
+ "marketplace": ["plan", "qa", "security", "ship", "compliance"],
16
+ "fintech": ["plan", "qa", "security", "ship", "compliance"],
17
+ "healthcare": ["plan", "qa", "security", "ship", "compliance"],
18
+ "web3": ["plan", "qa", "oracle-review", "security", "ship"],
19
+ "iot-embedded": ["plan", "qa", "security", "ship"],
20
+ "regulated": ["plan", "qa", "security", "ship", "compliance"],
21
+ "devtools": ["plan", "qa", "ship"],
22
+ "browser-extension": ["plan", "qa", "ship"],
23
+ "game": ["plan", "qa", "ship"],
24
+ "cms": ["plan", "qa", "ship"],
25
+ "enterprise-saas": ["plan", "qa", "security", "ship", "compliance"],
26
+ "edtech": ["plan", "qa", "edtech-review", "security", "ship", "compliance"],
27
+ "gov-public": ["plan", "qa", "gov-review", "security", "ship", "compliance"],
28
+ "insurance": ["plan", "qa", "insurance-review", "security", "ship", "compliance"],
29
+ "legal": ["plan", "qa", "upl-review", "security", "ship", "compliance"],
30
+ "defense-govcon": ["plan", "qa", "cmmc-assessment", "security", "ship", "compliance"],
31
+ // Product Builder archetypes (A1–A6) — non-regulated baseline; the single CTO gate
32
+ // emerges at change_tier T1 (effectiveGates → [plan]).
33
+ "vertical-saas": ["plan", "qa", "ship"],
34
+ "booking": ["plan", "qa", "ship"],
35
+ "crm": ["plan", "qa", "ship"],
36
+ "dashboard": ["plan", "qa", "ship"],
37
+ "content-platform": ["plan", "qa", "ship"],
38
+ "marketplace-lite": ["plan", "qa", "ship"],
39
+ "greenfield": ["plan"],
40
+ };
41
+ /**
42
+ * Returns the subset of gates the pipeline will actually open for a
43
+ * given archetype + project_size. Used by the orchestrator before
44
+ * opening any gate to skip unnecessary human checkpoints on smaller
45
+ * projects (e.g. nano always skips QA).
46
+ */
47
+ export function gatesFor(archetype, size) {
48
+ const all = GATES_BY_ARCHETYPE[archetype] ?? [];
49
+ if (size === "nano")
50
+ return all.filter((g) => g === "plan");
51
+ if (size === "small")
52
+ return all.filter((g) => g === "plan" || g === "ship");
53
+ if (size === "medium")
54
+ return all;
55
+ // large + enterprise → ensure compliance is included
56
+ return all.includes("compliance") ? all : [...all, "compliance"];
57
+ }
58
+ /** Gates that form the non-negotiable floor for a *regulated* archetype. */
59
+ const FLOOR_GATES = ["security", "compliance", "ship"];
60
+ /**
61
+ * The human gates the pipeline opens for a change, given its archetype, the
62
+ * project size, AND the per-change risk tier. Composes with `gatesFor()`:
63
+ *
64
+ * - T2 (default): the full size baseline, with `ship` guaranteed even if a
65
+ * small project_size had stripped it. An irreversible change always gets a
66
+ * final go/no-go. Nothing is ever removed → fail-safe for an unknown tier.
67
+ * - T1: only `plan` (you review intent) PLUS the regulated floor. The standalone
68
+ * qa/security-as-ceremony is dropped because CI + tests cover a reversible change.
69
+ * - T0: the regulated floor only — for a plain archetype that is the empty set
70
+ * (CI is the gate); for a regulated one, security+compliance+ship still fire.
71
+ *
72
+ * An archetype is "regulated" iff its baseline contains `security` or `compliance`.
73
+ * That is what keeps the downgrade sound: a fintech repo cannot ship an
74
+ * irreversible change unreviewed even when the change is labelled a fix.
75
+ *
76
+ * NOTE: change-specific T2 triggers (a migration, a new write-connector, a prod
77
+ * deploy) are resolved upstream by the classifier, which escalates such a change
78
+ * to T2 before this function sees it. Here we only map an already-decided tier.
79
+ */
80
+ export function effectiveGates(archetype, size, tier = "T2") {
81
+ const base = gatesFor(archetype, size);
82
+ // T2 (and any unrecognized tier) → full baseline, never downgraded, ship forced.
83
+ if (tier !== "T0" && tier !== "T1") {
84
+ return base.includes("ship") ? base : [...base, "ship"];
85
+ }
86
+ // Regulated archetypes carry a floor that survives every downgrade.
87
+ const isRegulated = base.includes("security") || base.includes("compliance");
88
+ const floor = isRegulated ? base.filter((g) => FLOOR_GATES.includes(g)) : [];
89
+ if (tier === "T1") {
90
+ const keep = new Set(floor);
91
+ if (base.includes("plan"))
92
+ keep.add("plan");
93
+ return base.filter((g) => keep.has(g)); // preserve base order
94
+ }
95
+ // T0 — maintenance.
96
+ return floor;
97
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "great-cto",
3
- "version": "3.59.0",
3
+ "version": "3.59.1",
4
4
  "description": "One command install for the great_cto Claude Code plugin. Auto-detects your stack, picks the right archetype, bootstraps PROJECT.md.",
5
5
  "keywords": [
6
6
  "claude-code",
package/postinstall.mjs CHANGED
@@ -138,32 +138,38 @@ function main() {
138
138
  * - never block. The board takes the better part of a minute to become
139
139
  * responsive; the installer does not wait for it. Detached, unref'd, output
140
140
  * discarded.
141
- * - never in CI, and never when asked not to. Both already guard the notice
142
- * above; `GREAT_CTO_NO_BOARD=1` opts out of this specifically.
141
+ * - never in CI or a Node test runner, and never when asked not to. A test
142
+ * HOME isolates files, NOT ports: a detached board would still claim the
143
+ * user's :3141 and serve fixture projects after the test exits.
144
+ * `GREAT_CTO_NO_BOARD=1` opts out of this specifically.
143
145
  * - never twice. `ensure` probes the port first and adopts a board that is
144
146
  * already answering, whoever started it.
145
147
  */
146
- function ensureBoard() {
147
- if (process.env.GREAT_CTO_NO_BOARD) return;
148
+ export function ensureBoard({ env = process.env, spawnFn = spawn,
149
+ write = (text) => process.stdout.write(text) } = {}) {
150
+ if (env.CI || env.GREAT_CTO_QUIET_POSTINSTALL || env.GREAT_CTO_NO_BOARD || env.NODE_TEST_CONTEXT) return false;
148
151
  try {
149
152
  const here = dirname(fileURLToPath(import.meta.url));
150
153
  const entry = join(here, 'index.mjs');
151
- if (!existsSync(entry)) return;
154
+ if (!existsSync(entry)) return false;
152
155
 
153
- const child = spawn(process.execPath, [entry, 'board', 'ensure'], {
156
+ const child = spawnFn(process.execPath, [entry, 'board', 'ensure'], {
154
157
  detached: true,
155
158
  stdio: 'ignore',
159
+ env,
156
160
  });
157
161
  child.unref();
158
- process.stdout.write(' great-cto: starting the board — http://localhost:3141\n');
162
+ write(' great-cto: starting the board — http://localhost:3141\n');
163
+ return true;
159
164
  } catch (e) {
160
165
  // Never fail the install — but never fail SILENTLY either. The first version
161
166
  // of this function referenced an import that was not there; the catch ate the
162
167
  // ReferenceError and the hook printed nothing, so the board simply did not
163
168
  // start and nothing said why. A swallowed error is the defect this project
164
169
  // spends most of its checks on.
165
- process.stdout.write(` great-cto: could not start the board — ${e?.message || e}\n`);
166
- process.stdout.write(' Start it yourself with: great-cto board\n');
170
+ write(` great-cto: could not start the board — ${e?.message || e}\n`);
171
+ write(' Start it yourself with: great-cto board\n');
172
+ return false;
167
173
  }
168
174
  }
169
175