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.
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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,
|
|
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