great-cto 3.68.0 → 3.68.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.68.0",
4
+ "version": "3.68.1",
5
5
  "author": {
6
6
  "name": "Alexander Velikiy",
7
7
  "url": "https://hashnode.com/@Greatcto"
@@ -29,7 +29,8 @@ import { execFileSync } from 'node:child_process';
29
29
  import { createHash } from 'node:crypto';
30
30
  import { readFileSync } from 'node:fs';
31
31
  import * as fsModule from 'node:fs';
32
- import { join } from 'node:path';
32
+ import { dirname, join, resolve } from 'node:path';
33
+ import { fileURLToPath } from 'node:url';
33
34
 
34
35
  const sha = (s) => createHash('sha256').update(String(s)).digest('hex');
35
36
 
@@ -351,12 +352,23 @@ export function describeDrift(cmp, { max = 10 } = {}) {
351
352
  export const APPROVING_AGENTS = Object.freeze(['code-reviewer', 'security-officer', 'qa-engineer']);
352
353
  const APPROVING_VERDICTS = new Set(['APPROVED', 'PASS', 'PASSED']);
353
354
 
354
- export function latestApproval(cwd = process.cwd(), { agents = APPROVING_AGENTS, read = readFileSync } = {}) {
355
+ /** Same state location as log-verdict.sh; the candidate bytes stay in cwd. */
356
+ export function verdictStateDir(cwd = process.cwd(), env = process.env) {
357
+ if (env.GREAT_CTO_DIR) return resolve(cwd, env.GREAT_CTO_DIR);
358
+ const common = git(['rev-parse', '--git-common-dir'], cwd)?.trim();
359
+ const gitdir = git(['rev-parse', '--git-dir'], cwd)?.trim();
360
+ if (common && gitdir && resolve(cwd, common) !== resolve(cwd, gitdir)) {
361
+ return join(dirname(resolve(cwd, common)), '.great_cto');
362
+ }
363
+ return join(cwd, '.great_cto');
364
+ }
365
+
366
+ export function latestApproval(cwd = process.cwd(), { agents = APPROVING_AGENTS, read = readFileSync, stateDir = verdictStateDir(cwd) } = {}) {
355
367
  let best = null;
356
368
  for (const agent of agents) {
357
369
  let text;
358
370
  try {
359
- text = read(join(cwd, '.great_cto', 'verdicts', `${agent}.log`), 'utf8');
371
+ text = read(join(stateDir, 'verdicts', `${agent}.log`), 'utf8');
360
372
  } catch { continue; }
361
373
  for (const line of String(text).split('\n')) {
362
374
  if (!line.trim()) continue;
@@ -454,8 +466,26 @@ function requireFs() {
454
466
  // `--check <file>` compares a recorded receipt held in a file.
455
467
  // `--verify` compares the newest approving verdict's receipt against now.
456
468
 
457
- if (import.meta.url === `file://${process.argv[1]}`) {
469
+ function currentForReceipt(cwd, receipt) {
470
+ // Persisted evidence is data, not Git options or a new revision expression.
471
+ // Missing bases retain legacy behavior; invalid declared bases stay unreadable.
472
+ let base = receipt?.base;
473
+ // The emitter uses HEAD when there is no conventional default branch. Bind
474
+ // that legacy symbolic base to the recorded commit, not today's moving HEAD.
475
+ if (base === 'HEAD') {
476
+ if (!receipt?.head) return null;
477
+ base = receipt.head;
478
+ }
479
+ if (base != null && (typeof base !== 'string' || !/^[0-9a-f]{7,64}$/i.test(base))) return null;
480
+ return treeReceipt(cwd, { base: base || null });
481
+ }
482
+
483
+ if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
458
484
  const argv = process.argv.slice(2);
485
+ if (argv.includes('--state-dir')) {
486
+ console.log(verdictStateDir());
487
+ process.exit(0);
488
+ }
459
489
  if (argv.includes('--emit')) {
460
490
  // `--verdicts <dir>`: measure the change from where the previous stage stood.
461
491
  const vi = argv.indexOf('--verdicts');
@@ -467,7 +497,7 @@ if (import.meta.url === `file://${process.argv[1]}`) {
467
497
  if (argv.includes('--verify')) {
468
498
  const cwd = process.cwd();
469
499
  const approval = latestApproval(cwd);
470
- const current = treeReceipt(cwd);
500
+ const current = currentForReceipt(cwd, approval?.receipt);
471
501
  const cmp = compareReceipts(approval?.receipt ?? null, current);
472
502
  if (cmp.state === 'no-receipt') {
473
503
  // Not silence. A push with no approval to compare against and a push whose
@@ -479,6 +509,7 @@ if (import.meta.url === `file://${process.argv[1]}`) {
479
509
  console.log(`receipt: against ${who}`);
480
510
  console.log(describeDrift(cmp).split('\n').map((l) => ` ${l}`).join('\n'));
481
511
 
512
+ if (cmp.state === 'unreadable') process.exit(2);
482
513
  if (cmp.state !== 'differs') process.exit(0);
483
514
 
484
515
  // Drift, but a human may already have looked at exactly this state.
@@ -507,8 +538,12 @@ if (import.meta.url === `file://${process.argv[1]}`) {
507
538
  if (argv.includes('--accept')) {
508
539
  const cwd = process.cwd();
509
540
  const approval = latestApproval(cwd);
510
- const current = treeReceipt(cwd);
541
+ const current = currentForReceipt(cwd, approval?.receipt);
511
542
  const cmp = compareReceipts(approval?.receipt ?? null, current);
543
+ if (cmp.state === 'unreadable') {
544
+ console.error(`receipt: cannot accept — ${cmp.why}`);
545
+ process.exit(2);
546
+ }
512
547
  if (cmp.state !== 'differs') {
513
548
  console.log(`receipt: nothing to accept — ${cmp.why}`);
514
549
  process.exit(0);
@@ -537,9 +572,9 @@ if (import.meta.url === `file://${process.argv[1]}`) {
537
572
  const { readFileSync } = await import('node:fs');
538
573
  let recorded = null;
539
574
  try { recorded = JSON.parse(readFileSync(argv[i + 1], 'utf8')); } catch { /* absent */ }
540
- const cmp = compareReceipts(recorded, treeReceipt(process.cwd()));
575
+ const cmp = compareReceipts(recorded, currentForReceipt(process.cwd(), recorded));
541
576
  console.log(describeDrift(cmp));
542
- process.exit(cmp.state === 'differs' ? 1 : 0);
577
+ process.exit(cmp.state === 'unreadable' ? 2 : cmp.state === 'differs' ? 1 : 0);
543
578
  }
544
579
  console.log(JSON.stringify(treeReceipt(process.cwd()), null, 2));
545
580
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "great-cto",
3
- "version": "3.68.0",
3
+ "version": "3.68.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",