@codemoot/cli 0.2.12 → 0.2.14
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/dist/index.js +154 -111
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -424,12 +424,8 @@ async function buildReviewCommand(buildId) {
|
|
|
424
424
|
}
|
|
425
425
|
const sessionMgr = new SessionManager(db);
|
|
426
426
|
const session2 = sessionMgr.resolveActive("build-review");
|
|
427
|
-
const overflowCheck = sessionMgr.preCallOverflowCheck(session2.id);
|
|
428
|
-
if (overflowCheck.rolled) {
|
|
429
|
-
console.error(chalk3.yellow(` ${overflowCheck.message}`));
|
|
430
|
-
}
|
|
431
427
|
const currentSession = sessionMgr.get(session2.id);
|
|
432
|
-
const existingSession =
|
|
428
|
+
const existingSession = currentSession?.codexThreadId ?? run.reviewCodexSession ?? void 0;
|
|
433
429
|
const prompt = buildHandoffEnvelope({
|
|
434
430
|
command: "build-review",
|
|
435
431
|
task: `Review code changes for the task: "${run.task}"
|
|
@@ -447,11 +443,23 @@ Review for:
|
|
|
447
443
|
constraints: run.reviewCycles > 0 ? [`This is review cycle ${run.reviewCycles + 1}. Focus on whether prior issues were addressed.`] : void 0
|
|
448
444
|
});
|
|
449
445
|
const progress = createProgressCallbacks("build-review");
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
446
|
+
let result;
|
|
447
|
+
try {
|
|
448
|
+
result = await adapter.callWithResume(prompt, {
|
|
449
|
+
sessionId: existingSession,
|
|
450
|
+
timeout: 6e5,
|
|
451
|
+
...progress
|
|
452
|
+
});
|
|
453
|
+
} catch (err) {
|
|
454
|
+
if (existingSession) {
|
|
455
|
+
console.error(chalk3.yellow(" Clearing stale codex thread ID after failure."));
|
|
456
|
+
sessionMgr.updateThreadId(session2.id, null);
|
|
457
|
+
}
|
|
458
|
+
throw err;
|
|
459
|
+
}
|
|
460
|
+
if (existingSession && result.sessionId !== existingSession) {
|
|
461
|
+
sessionMgr.updateThreadId(session2.id, null);
|
|
462
|
+
}
|
|
455
463
|
if (result.sessionId) {
|
|
456
464
|
sessionMgr.updateThreadId(session2.id, result.sessionId);
|
|
457
465
|
}
|
|
@@ -474,7 +482,7 @@ Review for:
|
|
|
474
482
|
buildStore.updateWithEvent(
|
|
475
483
|
buildId,
|
|
476
484
|
{
|
|
477
|
-
reviewCodexSession: result.sessionId ??
|
|
485
|
+
reviewCodexSession: result.sessionId ?? void 0,
|
|
478
486
|
reviewCycles: run.reviewCycles + 1
|
|
479
487
|
},
|
|
480
488
|
{
|
|
@@ -538,7 +546,8 @@ import {
|
|
|
538
546
|
preflightTokenCheck
|
|
539
547
|
} from "@codemoot/core";
|
|
540
548
|
import chalk4 from "chalk";
|
|
541
|
-
import { writeFileSync } from "fs";
|
|
549
|
+
import { mkdirSync as mkdirSync2, writeFileSync } from "fs";
|
|
550
|
+
import { join as join3 } from "path";
|
|
542
551
|
async function debateStartCommand(topic, options) {
|
|
543
552
|
let db;
|
|
544
553
|
try {
|
|
@@ -578,6 +587,40 @@ async function debateStartCommand(topic, options) {
|
|
|
578
587
|
process.exit(1);
|
|
579
588
|
}
|
|
580
589
|
}
|
|
590
|
+
var DEFAULT_RESPONSE_CAP = 16384;
|
|
591
|
+
var MAX_RESPONSE_CAP = 24576;
|
|
592
|
+
function buildResponseOutput(debateId, round, responseText, cap, explicitOutput) {
|
|
593
|
+
const text = responseText ?? "";
|
|
594
|
+
const byteLen = Buffer.byteLength(text, "utf-8");
|
|
595
|
+
const truncated = byteLen > cap;
|
|
596
|
+
let responseFile;
|
|
597
|
+
if (truncated && !explicitOutput) {
|
|
598
|
+
const dir = join3(process.cwd(), ".codemoot", "debates");
|
|
599
|
+
mkdirSync2(dir, { recursive: true, mode: 448 });
|
|
600
|
+
responseFile = join3(dir, `${debateId}-r${round}.txt`);
|
|
601
|
+
writeFileSync(responseFile, text, { encoding: "utf-8", mode: 384 });
|
|
602
|
+
} else if (explicitOutput && text.length > 0) {
|
|
603
|
+
responseFile = explicitOutput;
|
|
604
|
+
}
|
|
605
|
+
let sliced = text;
|
|
606
|
+
if (truncated) {
|
|
607
|
+
let lo = 0;
|
|
608
|
+
let hi = text.length;
|
|
609
|
+
while (lo < hi) {
|
|
610
|
+
const mid = lo + hi + 1 >>> 1;
|
|
611
|
+
if (Buffer.byteLength(text.slice(0, mid), "utf-8") <= cap) lo = mid;
|
|
612
|
+
else hi = mid - 1;
|
|
613
|
+
}
|
|
614
|
+
sliced = text.slice(0, lo);
|
|
615
|
+
}
|
|
616
|
+
return {
|
|
617
|
+
response: sliced,
|
|
618
|
+
responseTruncated: truncated,
|
|
619
|
+
responseBytes: byteLen,
|
|
620
|
+
responseCap: cap,
|
|
621
|
+
responseFile
|
|
622
|
+
};
|
|
623
|
+
}
|
|
581
624
|
async function debateTurnCommand(debateId, prompt, options) {
|
|
582
625
|
let db;
|
|
583
626
|
try {
|
|
@@ -607,6 +650,11 @@ async function debateTurnCommand(debateId, prompt, options) {
|
|
|
607
650
|
}
|
|
608
651
|
const rawRound = options.round ?? criticRow.round + 1;
|
|
609
652
|
const newRound = Number.isFinite(rawRound) && rawRound > 0 ? rawRound : criticRow.round + 1;
|
|
653
|
+
const responseCap = Math.min(
|
|
654
|
+
options.responseCap && options.responseCap > 0 ? options.responseCap : DEFAULT_RESPONSE_CAP,
|
|
655
|
+
MAX_RESPONSE_CAP
|
|
656
|
+
);
|
|
657
|
+
const quiet = options.quiet ?? false;
|
|
610
658
|
const proposerStateForLimit = store.loadState(debateId, "proposer");
|
|
611
659
|
const storedTimeout = proposerStateForLimit?.defaultTimeout;
|
|
612
660
|
const rawTimeout = options.timeout ?? storedTimeout ?? 600;
|
|
@@ -631,14 +679,13 @@ async function debateTurnCommand(debateId, prompt, options) {
|
|
|
631
679
|
}
|
|
632
680
|
if (options.output && existing.responseText) {
|
|
633
681
|
writeFileSync(options.output, existing.responseText, "utf-8");
|
|
634
|
-
console.error(chalk4.dim(`Full response written to ${options.output}`));
|
|
682
|
+
if (!quiet) console.error(chalk4.dim(`Full response written to ${options.output}`));
|
|
635
683
|
}
|
|
684
|
+
const responseFields = buildResponseOutput(debateId, newRound, existing.responseText, responseCap, options.output);
|
|
636
685
|
const output = {
|
|
637
686
|
debateId,
|
|
638
687
|
round: newRound,
|
|
639
|
-
|
|
640
|
-
responseTruncated: (existing.responseText?.length ?? 0) > 2e3,
|
|
641
|
-
responseFile: options.output ?? void 0,
|
|
688
|
+
...responseFields,
|
|
642
689
|
sessionId: existing.sessionId,
|
|
643
690
|
resumed: false,
|
|
644
691
|
cached: true,
|
|
@@ -657,7 +704,7 @@ async function debateTurnCommand(debateId, prompt, options) {
|
|
|
657
704
|
}
|
|
658
705
|
const staleThreshold = timeout + 6e4;
|
|
659
706
|
const recovered = msgStore.recoverStaleForDebate(debateId, staleThreshold);
|
|
660
|
-
if (recovered > 0) {
|
|
707
|
+
if (recovered > 0 && !quiet) {
|
|
661
708
|
console.error(chalk4.yellow(` Recovered ${recovered} stale message(s) from prior crash.`));
|
|
662
709
|
}
|
|
663
710
|
const current = msgStore.getByRound(debateId, newRound, "critic");
|
|
@@ -682,14 +729,13 @@ async function debateTurnCommand(debateId, prompt, options) {
|
|
|
682
729
|
if (recheckRow?.status === "completed") {
|
|
683
730
|
if (options.output && recheckRow.responseText) {
|
|
684
731
|
writeFileSync(options.output, recheckRow.responseText, "utf-8");
|
|
685
|
-
console.error(chalk4.dim(`Full response written to ${options.output}`));
|
|
732
|
+
if (!quiet) console.error(chalk4.dim(`Full response written to ${options.output}`));
|
|
686
733
|
}
|
|
734
|
+
const recheckResponseFields = buildResponseOutput(debateId, newRound, recheckRow.responseText, responseCap, options.output);
|
|
687
735
|
const output = {
|
|
688
736
|
debateId,
|
|
689
737
|
round: newRound,
|
|
690
|
-
|
|
691
|
-
responseTruncated: (recheckRow.responseText?.length ?? 0) > 2e3,
|
|
692
|
-
responseFile: options.output ?? void 0,
|
|
738
|
+
...recheckResponseFields,
|
|
693
739
|
sessionId: recheckRow.sessionId,
|
|
694
740
|
resumed: false,
|
|
695
741
|
cached: true,
|
|
@@ -722,28 +768,32 @@ async function debateTurnCommand(debateId, prompt, options) {
|
|
|
722
768
|
db.close();
|
|
723
769
|
process.exit(1);
|
|
724
770
|
} else if (preflight.shouldStop && options.force) {
|
|
725
|
-
console.error(chalk4.yellow(` Token budget at ${Math.round(preflight.utilizationRatio * 100)}% (${preflight.totalTokensUsed}/${maxContext}) \u2014 forced past budget limit.`));
|
|
771
|
+
if (!quiet) console.error(chalk4.yellow(` Token budget at ${Math.round(preflight.utilizationRatio * 100)}% (${preflight.totalTokensUsed}/${maxContext}) \u2014 forced past budget limit.`));
|
|
726
772
|
} else if (preflight.shouldSummarize) {
|
|
727
|
-
console.error(chalk4.yellow(` Token budget at ${Math.round(preflight.utilizationRatio * 100)}%. Older rounds will be summarized on resume failure.`));
|
|
728
|
-
}
|
|
729
|
-
const overflowCheck = sessionMgr.preCallOverflowCheck(unifiedSession.id);
|
|
730
|
-
if (overflowCheck.rolled) {
|
|
731
|
-
console.error(chalk4.yellow(` ${overflowCheck.message}`));
|
|
732
|
-
console.error(chalk4.yellow(" Warning: Session rolled over \u2014 previous conversation context has been lost."));
|
|
733
|
-
sessionMgr.updateThreadId(unifiedSession.id, null);
|
|
734
|
-
existingSessionId = void 0;
|
|
773
|
+
if (!quiet) console.error(chalk4.yellow(` Token budget at ${Math.round(preflight.utilizationRatio * 100)}%. Older rounds will be summarized on resume failure.`));
|
|
735
774
|
}
|
|
736
775
|
try {
|
|
737
776
|
const progress = createProgressCallbacks("debate");
|
|
738
|
-
let result
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
777
|
+
let result;
|
|
778
|
+
try {
|
|
779
|
+
result = await adapter.callWithResume(prompt, {
|
|
780
|
+
sessionId: existingSessionId,
|
|
781
|
+
timeout,
|
|
782
|
+
...progress
|
|
783
|
+
});
|
|
784
|
+
} catch (err) {
|
|
785
|
+
if (existingSessionId) {
|
|
786
|
+
sessionMgr.updateThreadId(unifiedSession.id, null);
|
|
787
|
+
}
|
|
788
|
+
throw err;
|
|
789
|
+
}
|
|
790
|
+
if (existingSessionId && result.sessionId !== existingSessionId) {
|
|
791
|
+
sessionMgr.updateThreadId(unifiedSession.id, null);
|
|
792
|
+
}
|
|
743
793
|
const resumed = attemptedResume && result.sessionId === existingSessionId;
|
|
744
794
|
const resumeFailed = attemptedResume && !resumed;
|
|
745
795
|
if (resumeFailed && result.text.length < 50) {
|
|
746
|
-
console.error(chalk4.yellow(" Resume failed with minimal response. Reconstructing from ledger..."));
|
|
796
|
+
if (!quiet) console.error(chalk4.yellow(" Resume failed with minimal response. Reconstructing from ledger..."));
|
|
747
797
|
const history = msgStore.getHistory(debateId);
|
|
748
798
|
const reconstructed = buildReconstructionPrompt(history, prompt);
|
|
749
799
|
result = await adapter.callWithResume(reconstructed, {
|
|
@@ -752,7 +802,7 @@ async function debateTurnCommand(debateId, prompt, options) {
|
|
|
752
802
|
});
|
|
753
803
|
}
|
|
754
804
|
if (result.text.length < 200 && (result.durationMs ?? 0) > 6e4) {
|
|
755
|
-
console.error(chalk4.yellow(` Warning: GPT response is only ${result.text.length} chars after ${Math.round((result.durationMs ?? 0) / 1e3)}s \u2014 possible output truncation (codex may have spent its turn on tool calls).`));
|
|
805
|
+
if (!quiet) console.error(chalk4.yellow(` Warning: GPT response is only ${result.text.length} chars after ${Math.round((result.durationMs ?? 0) / 1e3)}s \u2014 possible output truncation (codex may have spent its turn on tool calls).`));
|
|
756
806
|
}
|
|
757
807
|
const verdict = parseDebateVerdict(result.text);
|
|
758
808
|
const completed = msgStore.markCompleted(msgId, {
|
|
@@ -789,7 +839,7 @@ async function debateTurnCommand(debateId, prompt, options) {
|
|
|
789
839
|
store.upsert({
|
|
790
840
|
debateId,
|
|
791
841
|
role: "critic",
|
|
792
|
-
codexSessionId: result.sessionId ??
|
|
842
|
+
codexSessionId: result.sessionId ?? void 0,
|
|
793
843
|
round: newRound,
|
|
794
844
|
status: "active"
|
|
795
845
|
});
|
|
@@ -804,14 +854,13 @@ async function debateTurnCommand(debateId, prompt, options) {
|
|
|
804
854
|
}
|
|
805
855
|
if (options.output) {
|
|
806
856
|
writeFileSync(options.output, result.text, "utf-8");
|
|
807
|
-
console.error(chalk4.dim(`Full response written to ${options.output}`));
|
|
857
|
+
if (!quiet) console.error(chalk4.dim(`Full response written to ${options.output}`));
|
|
808
858
|
}
|
|
859
|
+
const freshResponseFields = buildResponseOutput(debateId, newRound, result.text, responseCap, options.output);
|
|
809
860
|
const output = {
|
|
810
861
|
debateId,
|
|
811
862
|
round: newRound,
|
|
812
|
-
|
|
813
|
-
responseTruncated: result.text.length > 2e3,
|
|
814
|
-
responseFile: options.output ?? void 0,
|
|
863
|
+
...freshResponseFields,
|
|
815
864
|
sessionId: result.sessionId,
|
|
816
865
|
resumed,
|
|
817
866
|
cached: false,
|
|
@@ -819,14 +868,16 @@ async function debateTurnCommand(debateId, prompt, options) {
|
|
|
819
868
|
usage: result.usage,
|
|
820
869
|
durationMs: result.durationMs
|
|
821
870
|
};
|
|
822
|
-
|
|
823
|
-
|
|
871
|
+
if (!quiet) {
|
|
872
|
+
const stanceColor = verdict.stance === "SUPPORT" ? chalk4.green : verdict.stance === "OPPOSE" ? chalk4.red : chalk4.yellow;
|
|
873
|
+
console.error(stanceColor(`
|
|
824
874
|
Round ${newRound} \u2014 Stance: ${verdict.stance}`));
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
875
|
+
const previewLines = result.text.split("\n").filter((l) => l.trim().length > 10).slice(0, 3);
|
|
876
|
+
for (const line of previewLines) {
|
|
877
|
+
console.error(chalk4.dim(` ${line.trim().slice(0, 120)}`));
|
|
878
|
+
}
|
|
879
|
+
console.error(chalk4.dim(`Duration: ${(result.durationMs / 1e3).toFixed(1)}s | Output: ${result.usage?.outputTokens ?? "?"} tokens | Input: ${result.usage?.inputTokens ?? "?"} (includes sandbox) | Resumed: ${resumed}`));
|
|
828
880
|
}
|
|
829
|
-
console.error(chalk4.dim(`Duration: ${(result.durationMs / 1e3).toFixed(1)}s | Output: ${result.usage?.outputTokens ?? "?"} tokens | Input: ${result.usage?.inputTokens ?? "?"} (includes sandbox) | Resumed: ${resumed}`));
|
|
830
881
|
console.log(JSON.stringify(output, null, 2));
|
|
831
882
|
} catch (error) {
|
|
832
883
|
msgStore.markFailed(msgId, error instanceof Error ? error.message : String(error));
|
|
@@ -1115,10 +1166,6 @@ async function cleanupCommand(path, options) {
|
|
|
1115
1166
|
}
|
|
1116
1167
|
const sessionMgr = new SessionManager3(db);
|
|
1117
1168
|
const session2 = sessionMgr.resolveActive("cleanup");
|
|
1118
|
-
const overflowCheck = sessionMgr.preCallOverflowCheck(session2.id);
|
|
1119
|
-
if (overflowCheck.rolled) {
|
|
1120
|
-
console.error(chalk5.yellow(` ${overflowCheck.message}`));
|
|
1121
|
-
}
|
|
1122
1169
|
const currentSession = sessionMgr.get(session2.id);
|
|
1123
1170
|
const sessionThreadId = currentSession?.codexThreadId ?? void 0;
|
|
1124
1171
|
const [deterministicFindings, semanticFindings] = await Promise.all([
|
|
@@ -1310,7 +1357,18 @@ IMPORTANT KEY FORMAT: The key will be built as scope:file:symbol \u2014 use the
|
|
|
1310
1357
|
});
|
|
1311
1358
|
try {
|
|
1312
1359
|
const progress = createProgressCallbacks("cleanup-scan");
|
|
1313
|
-
|
|
1360
|
+
let result;
|
|
1361
|
+
try {
|
|
1362
|
+
result = await adapter.callWithResume(prompt, { sessionId: sessionThreadId, timeout: timeoutSec * 1e3, ...progress });
|
|
1363
|
+
} catch (err) {
|
|
1364
|
+
if (sessionThreadId && sessionMgr && sessionId) {
|
|
1365
|
+
sessionMgr.updateThreadId(sessionId, null);
|
|
1366
|
+
}
|
|
1367
|
+
throw err;
|
|
1368
|
+
}
|
|
1369
|
+
if (sessionThreadId && result.sessionId !== sessionThreadId && sessionMgr && sessionId) {
|
|
1370
|
+
sessionMgr.updateThreadId(sessionId, null);
|
|
1371
|
+
}
|
|
1314
1372
|
if (sessionMgr && sessionId) {
|
|
1315
1373
|
if (result.sessionId) {
|
|
1316
1374
|
sessionMgr.updateThreadId(sessionId, result.sessionId);
|
|
@@ -1488,7 +1546,7 @@ async function costCommand(options) {
|
|
|
1488
1546
|
// src/commands/doctor.ts
|
|
1489
1547
|
import { existsSync, accessSync, constants } from "fs";
|
|
1490
1548
|
import { execSync as execSync2 } from "child_process";
|
|
1491
|
-
import { join as
|
|
1549
|
+
import { join as join4 } from "path";
|
|
1492
1550
|
import chalk7 from "chalk";
|
|
1493
1551
|
import { VERSION } from "@codemoot/core";
|
|
1494
1552
|
async function doctorCommand() {
|
|
@@ -1508,7 +1566,7 @@ async function doctorCommand() {
|
|
|
1508
1566
|
fix: "npm install -g @openai/codex"
|
|
1509
1567
|
});
|
|
1510
1568
|
}
|
|
1511
|
-
const configPath =
|
|
1569
|
+
const configPath = join4(cwd, ".cowork.yml");
|
|
1512
1570
|
if (existsSync(configPath)) {
|
|
1513
1571
|
checks.push({ name: "config", status: "pass", message: ".cowork.yml found" });
|
|
1514
1572
|
} else {
|
|
@@ -1519,8 +1577,8 @@ async function doctorCommand() {
|
|
|
1519
1577
|
fix: "codemoot init"
|
|
1520
1578
|
});
|
|
1521
1579
|
}
|
|
1522
|
-
const dbDir =
|
|
1523
|
-
const dbPath =
|
|
1580
|
+
const dbDir = join4(cwd, ".cowork", "db");
|
|
1581
|
+
const dbPath = join4(dbDir, "cowork.db");
|
|
1524
1582
|
if (existsSync(dbDir)) {
|
|
1525
1583
|
try {
|
|
1526
1584
|
accessSync(dbDir, constants.W_OK);
|
|
@@ -1548,11 +1606,11 @@ async function doctorCommand() {
|
|
|
1548
1606
|
let gitFound = false;
|
|
1549
1607
|
let searchDir = cwd;
|
|
1550
1608
|
while (searchDir) {
|
|
1551
|
-
if (existsSync(
|
|
1609
|
+
if (existsSync(join4(searchDir, ".git"))) {
|
|
1552
1610
|
gitFound = true;
|
|
1553
1611
|
break;
|
|
1554
1612
|
}
|
|
1555
|
-
const parent =
|
|
1613
|
+
const parent = join4(searchDir, "..");
|
|
1556
1614
|
if (parent === searchDir) break;
|
|
1557
1615
|
searchDir = parent;
|
|
1558
1616
|
}
|
|
@@ -1770,11 +1828,6 @@ async function fixCommand(fileOrGlob, options) {
|
|
|
1770
1828
|
)
|
|
1771
1829
|
);
|
|
1772
1830
|
for (let round = 1; round <= options.maxRounds; round++) {
|
|
1773
|
-
const overflowCheck = sessionMgr.preCallOverflowCheck(session2.id);
|
|
1774
|
-
if (overflowCheck.rolled) {
|
|
1775
|
-
console.error(chalk9.yellow(` ${overflowCheck.message}`));
|
|
1776
|
-
threadId = void 0;
|
|
1777
|
-
}
|
|
1778
1831
|
const roundStart = Date.now();
|
|
1779
1832
|
console.error(chalk9.dim(`
|
|
1780
1833
|
\u2500\u2500 Round ${round}/${options.maxRounds} \u2500\u2500`));
|
|
@@ -2030,12 +2083,12 @@ Result: ${converged ? "CONVERGED" : "NOT CONVERGED"} (${exitReason})`));
|
|
|
2030
2083
|
|
|
2031
2084
|
// src/commands/init.ts
|
|
2032
2085
|
import { existsSync as existsSync2 } from "fs";
|
|
2033
|
-
import { basename, join as
|
|
2086
|
+
import { basename, join as join5 } from "path";
|
|
2034
2087
|
import { loadConfig as loadConfig5, writeConfig } from "@codemoot/core";
|
|
2035
2088
|
import chalk10 from "chalk";
|
|
2036
2089
|
async function initCommand(options) {
|
|
2037
2090
|
const cwd = process.cwd();
|
|
2038
|
-
const configPath =
|
|
2091
|
+
const configPath = join5(cwd, ".cowork.yml");
|
|
2039
2092
|
if (existsSync2(configPath) && !options.force) {
|
|
2040
2093
|
console.error(chalk10.red("Already initialized. Use --force to overwrite."));
|
|
2041
2094
|
process.exit(1);
|
|
@@ -2067,8 +2120,8 @@ Initialized with '${presetName}' preset`));
|
|
|
2067
2120
|
}
|
|
2068
2121
|
|
|
2069
2122
|
// src/commands/install-skills.ts
|
|
2070
|
-
import { existsSync as existsSync3, mkdirSync as
|
|
2071
|
-
import { dirname, join as
|
|
2123
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
2124
|
+
import { dirname, join as join6 } from "path";
|
|
2072
2125
|
import chalk11 from "chalk";
|
|
2073
2126
|
var SKILLS = [
|
|
2074
2127
|
{
|
|
@@ -2509,21 +2562,21 @@ async function installSkillsCommand(options) {
|
|
|
2509
2562
|
console.error(chalk11.cyan("\n Installing CodeMoot integration for Claude Code\n"));
|
|
2510
2563
|
console.error(chalk11.dim(" Skills & Agents:"));
|
|
2511
2564
|
for (const skill of SKILLS) {
|
|
2512
|
-
const fullPath =
|
|
2565
|
+
const fullPath = join6(cwd, skill.path);
|
|
2513
2566
|
const dir = dirname(fullPath);
|
|
2514
2567
|
if (existsSync3(fullPath) && !options.force) {
|
|
2515
2568
|
console.error(chalk11.dim(` SKIP ${skill.path} (exists)`));
|
|
2516
2569
|
skipped++;
|
|
2517
2570
|
continue;
|
|
2518
2571
|
}
|
|
2519
|
-
|
|
2572
|
+
mkdirSync3(dir, { recursive: true });
|
|
2520
2573
|
writeFileSync3(fullPath, skill.content, "utf-8");
|
|
2521
2574
|
console.error(chalk11.green(` OK ${skill.path}`));
|
|
2522
2575
|
installed++;
|
|
2523
2576
|
}
|
|
2524
2577
|
console.error("");
|
|
2525
2578
|
console.error(chalk11.dim(" CLAUDE.md:"));
|
|
2526
|
-
const claudeMdPath =
|
|
2579
|
+
const claudeMdPath = join6(cwd, "CLAUDE.md");
|
|
2527
2580
|
const marker = "## CodeMoot \u2014 Multi-Model Collaboration";
|
|
2528
2581
|
if (existsSync3(claudeMdPath)) {
|
|
2529
2582
|
const existing = readFileSync3(claudeMdPath, "utf-8");
|
|
@@ -2555,8 +2608,8 @@ ${CLAUDE_MD_SECTION}`, "utf-8");
|
|
|
2555
2608
|
}
|
|
2556
2609
|
console.error("");
|
|
2557
2610
|
console.error(chalk11.dim(" Hooks:"));
|
|
2558
|
-
const settingsDir =
|
|
2559
|
-
const settingsPath =
|
|
2611
|
+
const settingsDir = join6(cwd, ".claude");
|
|
2612
|
+
const settingsPath = join6(settingsDir, "settings.json");
|
|
2560
2613
|
if (existsSync3(settingsPath)) {
|
|
2561
2614
|
try {
|
|
2562
2615
|
const existing = JSON.parse(readFileSync3(settingsPath, "utf-8"));
|
|
@@ -2580,7 +2633,7 @@ ${CLAUDE_MD_SECTION}`, "utf-8");
|
|
|
2580
2633
|
skipped++;
|
|
2581
2634
|
}
|
|
2582
2635
|
} else {
|
|
2583
|
-
|
|
2636
|
+
mkdirSync3(settingsDir, { recursive: true });
|
|
2584
2637
|
writeFileSync3(settingsPath, JSON.stringify(HOOKS_CONFIG, null, 2), "utf-8");
|
|
2585
2638
|
console.error(chalk11.green(" OK .claude/settings.json (created with post-commit hook)"));
|
|
2586
2639
|
installed++;
|
|
@@ -2633,18 +2686,12 @@ async function sessionCurrentCommand() {
|
|
|
2633
2686
|
return;
|
|
2634
2687
|
}
|
|
2635
2688
|
const events = mgr.getEvents(session2.id, 5);
|
|
2636
|
-
const overflow = mgr.getOverflowStatus(session2.id);
|
|
2637
2689
|
console.log(JSON.stringify({
|
|
2638
2690
|
sessionId: session2.id,
|
|
2639
2691
|
name: session2.name,
|
|
2640
2692
|
codexThreadId: session2.codexThreadId,
|
|
2641
2693
|
status: session2.status,
|
|
2642
|
-
|
|
2643
|
-
used: overflow.cumulativeTokens,
|
|
2644
|
-
lastTurnInput: overflow.lastTurnInputTokens,
|
|
2645
|
-
max: overflow.maxContext,
|
|
2646
|
-
utilization: `${Math.round(overflow.utilizationRatio * 100)}%`
|
|
2647
|
-
},
|
|
2694
|
+
tokenUsage: session2.tokenUsage,
|
|
2648
2695
|
recentEvents: events.map((e) => ({
|
|
2649
2696
|
command: e.command,
|
|
2650
2697
|
subcommand: e.subcommand,
|
|
@@ -2684,20 +2731,12 @@ async function sessionStatusCommand(sessionId) {
|
|
|
2684
2731
|
process.exit(1);
|
|
2685
2732
|
}
|
|
2686
2733
|
const events = mgr.getEvents(sessionId, 20);
|
|
2687
|
-
const overflow = mgr.getOverflowStatus(sessionId);
|
|
2688
2734
|
console.log(JSON.stringify({
|
|
2689
2735
|
sessionId: session2.id,
|
|
2690
2736
|
name: session2.name,
|
|
2691
2737
|
codexThreadId: session2.codexThreadId,
|
|
2692
2738
|
status: session2.status,
|
|
2693
|
-
|
|
2694
|
-
used: overflow.cumulativeTokens,
|
|
2695
|
-
lastTurnInput: overflow.lastTurnInputTokens,
|
|
2696
|
-
max: overflow.maxContext,
|
|
2697
|
-
utilization: `${Math.round(overflow.utilizationRatio * 100)}%`,
|
|
2698
|
-
shouldWarn: overflow.shouldWarn,
|
|
2699
|
-
shouldReconstruct: overflow.shouldReconstruct
|
|
2700
|
-
},
|
|
2739
|
+
tokenUsage: session2.tokenUsage,
|
|
2701
2740
|
eventCount: events.length,
|
|
2702
2741
|
events: events.map((e) => ({
|
|
2703
2742
|
command: e.command,
|
|
@@ -3037,12 +3076,8 @@ async function planReviewCommand(planFile, options) {
|
|
|
3037
3076
|
db = openDatabase9(dbPath);
|
|
3038
3077
|
const sessionMgr = new SessionManager7(db);
|
|
3039
3078
|
const session2 = sessionMgr.resolveActive("plan-review");
|
|
3040
|
-
const overflowCheck = sessionMgr.preCallOverflowCheck(session2.id);
|
|
3041
|
-
if (overflowCheck.rolled) {
|
|
3042
|
-
console.error(chalk15.yellow(` ${overflowCheck.message}`));
|
|
3043
|
-
}
|
|
3044
3079
|
const currentSession = sessionMgr.get(session2.id);
|
|
3045
|
-
const threadId =
|
|
3080
|
+
const threadId = currentSession?.codexThreadId ?? void 0;
|
|
3046
3081
|
const phaseContext = options.phase ? `
|
|
3047
3082
|
This is Phase ${options.phase} of a multi-phase plan.` : "";
|
|
3048
3083
|
const buildContext = options.build ? `
|
|
@@ -3076,11 +3111,23 @@ Output format:
|
|
|
3076
3111
|
const timeoutMs = (options.timeout ?? 300) * 1e3;
|
|
3077
3112
|
const progress = createProgressCallbacks("plan-review");
|
|
3078
3113
|
console.error(chalk15.cyan("Sending plan to codex for review..."));
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3114
|
+
let result;
|
|
3115
|
+
try {
|
|
3116
|
+
result = await adapter.callWithResume(prompt, {
|
|
3117
|
+
sessionId: threadId,
|
|
3118
|
+
timeout: timeoutMs,
|
|
3119
|
+
...progress
|
|
3120
|
+
});
|
|
3121
|
+
} catch (err) {
|
|
3122
|
+
if (threadId) {
|
|
3123
|
+
console.error(chalk15.yellow(" Clearing stale codex thread ID after failure."));
|
|
3124
|
+
sessionMgr.updateThreadId(session2.id, null);
|
|
3125
|
+
}
|
|
3126
|
+
throw err;
|
|
3127
|
+
}
|
|
3128
|
+
if (threadId && result.sessionId !== threadId) {
|
|
3129
|
+
sessionMgr.updateThreadId(session2.id, null);
|
|
3130
|
+
}
|
|
3084
3131
|
if (result.sessionId) {
|
|
3085
3132
|
sessionMgr.updateThreadId(session2.id, result.sessionId);
|
|
3086
3133
|
}
|
|
@@ -3231,10 +3278,6 @@ async function reviewCommand(fileOrGlob, options) {
|
|
|
3231
3278
|
db.close();
|
|
3232
3279
|
process.exit(1);
|
|
3233
3280
|
}
|
|
3234
|
-
const overflowCheck = sessionMgr.preCallOverflowCheck(session2.id);
|
|
3235
|
-
if (overflowCheck.rolled) {
|
|
3236
|
-
console.error(chalk16.yellow(` ${overflowCheck.message}`));
|
|
3237
|
-
}
|
|
3238
3281
|
const preset = options.preset ? getReviewPreset(options.preset) : void 0;
|
|
3239
3282
|
if (options.preset && !preset) {
|
|
3240
3283
|
console.error(chalk16.red(`Unknown preset: ${options.preset}. Use: security-audit, performance, quick-scan, pre-commit, api-review`));
|
|
@@ -3600,7 +3643,7 @@ async function shipitCommand(options) {
|
|
|
3600
3643
|
// src/commands/start.ts
|
|
3601
3644
|
import { existsSync as existsSync5 } from "fs";
|
|
3602
3645
|
import { execFileSync as execFileSync4, execSync as execSync6 } from "child_process";
|
|
3603
|
-
import { join as
|
|
3646
|
+
import { join as join7, basename as basename2 } from "path";
|
|
3604
3647
|
import chalk19 from "chalk";
|
|
3605
3648
|
import { loadConfig as loadConfig9, writeConfig as writeConfig2 } from "@codemoot/core";
|
|
3606
3649
|
async function startCommand() {
|
|
@@ -3620,7 +3663,7 @@ async function startCommand() {
|
|
|
3620
3663
|
}
|
|
3621
3664
|
console.error(chalk19.green(` Codex CLI ${codexVersion} found.`));
|
|
3622
3665
|
console.error(chalk19.dim(" [2/4] Checking project config..."));
|
|
3623
|
-
const configPath =
|
|
3666
|
+
const configPath = join7(cwd, ".cowork.yml");
|
|
3624
3667
|
if (existsSync5(configPath)) {
|
|
3625
3668
|
console.error(chalk19.green(" .cowork.yml exists \u2014 using it."));
|
|
3626
3669
|
} else {
|
|
@@ -3630,9 +3673,9 @@ async function startCommand() {
|
|
|
3630
3673
|
console.error(chalk19.green(" Created .cowork.yml with cli-first preset."));
|
|
3631
3674
|
}
|
|
3632
3675
|
console.error(chalk19.dim(" [3/4] Detecting project..."));
|
|
3633
|
-
const hasGit = existsSync5(
|
|
3634
|
-
const hasSrc = existsSync5(
|
|
3635
|
-
const hasPackageJson = existsSync5(
|
|
3676
|
+
const hasGit = existsSync5(join7(cwd, ".git"));
|
|
3677
|
+
const hasSrc = existsSync5(join7(cwd, "src"));
|
|
3678
|
+
const hasPackageJson = existsSync5(join7(cwd, "package.json"));
|
|
3636
3679
|
let reviewTarget = "";
|
|
3637
3680
|
if (hasGit) {
|
|
3638
3681
|
try {
|
|
@@ -4070,8 +4113,8 @@ debate.command("start").description("Start a new debate").argument("<topic>", "D
|
|
|
4070
4113
|
if (n <= 0) throw new InvalidArgumentError("Timeout must be a positive integer");
|
|
4071
4114
|
return n;
|
|
4072
4115
|
}).action(debateStartCommand);
|
|
4073
|
-
debate.command("turn").description("Send a prompt to GPT and get critique (with session resume)").argument("<debate-id>", "Debate ID from start command").argument("<prompt>", "Prompt to send to GPT").option("--round <n>", "Round number", (v) => Number.parseInt(v, 10)).option("--timeout <seconds>", "Timeout in seconds", (v) => Number.parseInt(v, 10)).option("--output <file>", "Write full untruncated response to file").option("--force", "Continue past token budget limit", false).action(debateTurnCommand);
|
|
4074
|
-
debate.command("next").description("Continue debate with auto-generated prompt").argument("<debate-id>", "Debate ID").option("--timeout <seconds>", "Timeout in seconds", (v) => Number.parseInt(v, 10)).option("--output <file>", "Write full untruncated response to file").option("--force", "Continue past token budget limit", false).action(debateNextCommand);
|
|
4116
|
+
debate.command("turn").description("Send a prompt to GPT and get critique (with session resume)").argument("<debate-id>", "Debate ID from start command").argument("<prompt>", "Prompt to send to GPT").option("--round <n>", "Round number", (v) => Number.parseInt(v, 10)).option("--timeout <seconds>", "Timeout in seconds", (v) => Number.parseInt(v, 10)).option("--output <file>", "Write full untruncated response to file").option("--force", "Continue past token budget limit", false).option("--quiet", "Suppress non-error stderr output", false).option("--response-cap <bytes>", "Max response bytes in JSON output (default: 16384)", (v) => Number.parseInt(v, 10)).action(debateTurnCommand);
|
|
4117
|
+
debate.command("next").description("Continue debate with auto-generated prompt").argument("<debate-id>", "Debate ID").option("--timeout <seconds>", "Timeout in seconds", (v) => Number.parseInt(v, 10)).option("--output <file>", "Write full untruncated response to file").option("--force", "Continue past token budget limit", false).option("--quiet", "Suppress non-error stderr output", false).option("--response-cap <bytes>", "Max response bytes in JSON output (default: 16384)", (v) => Number.parseInt(v, 10)).action(debateNextCommand);
|
|
4075
4118
|
debate.command("status").description("Show debate status and session info").argument("<debate-id>", "Debate ID").action(debateStatusCommand);
|
|
4076
4119
|
debate.command("list").description("List all debates").option("--status <status>", "Filter by status (active|completed|stale)").option("--limit <n>", "Max results", (v) => Number.parseInt(v, 10), 20).action(debateListCommand);
|
|
4077
4120
|
debate.command("history").description("Show full message history with token budget").argument("<debate-id>", "Debate ID").option("--output <file>", "Write full untruncated history to file").action(debateHistoryCommand);
|