@corbat-tech/coco 2.28.2 → 2.28.4
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 +5 -0
- package/dist/cli/index.js +1055 -676
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as fs5 from 'fs';
|
|
3
|
-
import fs5__default, { accessSync, readFileSync, constants } from 'fs';
|
|
3
|
+
import fs5__default, { accessSync, readFileSync, constants as constants$1 } from 'fs';
|
|
4
4
|
import * as path39 from 'path';
|
|
5
5
|
import path39__default, { join, dirname, resolve, basename } from 'path';
|
|
6
6
|
import { URL as URL$1, fileURLToPath } from 'url';
|
|
@@ -8,7 +8,7 @@ import { z } from 'zod';
|
|
|
8
8
|
import * as os4 from 'os';
|
|
9
9
|
import os4__default, { homedir } from 'os';
|
|
10
10
|
import * as fs35 from 'fs/promises';
|
|
11
|
-
import fs35__default, { mkdir, writeFile, readFile, access, readdir, rm } from 'fs/promises';
|
|
11
|
+
import fs35__default, { mkdir, writeFile, readFile, access, constants, readdir, rm } from 'fs/promises';
|
|
12
12
|
import JSON5 from 'json5';
|
|
13
13
|
import * as crypto from 'crypto';
|
|
14
14
|
import { randomUUID, randomBytes, createHash } from 'crypto';
|
|
@@ -707,8 +707,8 @@ async function configExists(configPath, scope = "any") {
|
|
|
707
707
|
}
|
|
708
708
|
return false;
|
|
709
709
|
}
|
|
710
|
-
function getConfigValue(config,
|
|
711
|
-
const keys =
|
|
710
|
+
function getConfigValue(config, path62) {
|
|
711
|
+
const keys = path62.split(".");
|
|
712
712
|
let current = config;
|
|
713
713
|
for (const key of keys) {
|
|
714
714
|
if (current === null || current === void 0 || typeof current !== "object") {
|
|
@@ -6980,7 +6980,7 @@ var init_fallback = __esm({
|
|
|
6980
6980
|
*/
|
|
6981
6981
|
async initialize(config) {
|
|
6982
6982
|
const results = await Promise.allSettled(
|
|
6983
|
-
this.providers.map((
|
|
6983
|
+
this.providers.map((p47) => p47.provider.initialize(config))
|
|
6984
6984
|
);
|
|
6985
6985
|
const anySuccess = results.some((r) => r.status === "fulfilled");
|
|
6986
6986
|
if (!anySuccess) {
|
|
@@ -7102,11 +7102,11 @@ var init_fallback = __esm({
|
|
|
7102
7102
|
*/
|
|
7103
7103
|
async isAvailable() {
|
|
7104
7104
|
const results = await Promise.all(
|
|
7105
|
-
this.providers.map(async (
|
|
7106
|
-
if (
|
|
7105
|
+
this.providers.map(async (p47) => {
|
|
7106
|
+
if (p47.breaker.isOpen()) {
|
|
7107
7107
|
return false;
|
|
7108
7108
|
}
|
|
7109
|
-
return
|
|
7109
|
+
return p47.provider.isAvailable();
|
|
7110
7110
|
})
|
|
7111
7111
|
);
|
|
7112
7112
|
return results.some((available) => available);
|
|
@@ -7137,10 +7137,10 @@ var init_fallback = __esm({
|
|
|
7137
7137
|
* @returns Array of provider status objects
|
|
7138
7138
|
*/
|
|
7139
7139
|
getCircuitStatus() {
|
|
7140
|
-
return this.providers.map((
|
|
7141
|
-
providerId:
|
|
7142
|
-
state:
|
|
7143
|
-
failureCount:
|
|
7140
|
+
return this.providers.map((p47) => ({
|
|
7141
|
+
providerId: p47.provider.id,
|
|
7142
|
+
state: p47.breaker.getState(),
|
|
7143
|
+
failureCount: p47.breaker.getFailureCount()
|
|
7144
7144
|
}));
|
|
7145
7145
|
}
|
|
7146
7146
|
/**
|
|
@@ -7150,8 +7150,8 @@ var init_fallback = __esm({
|
|
|
7150
7150
|
* previously failing providers to be tried again.
|
|
7151
7151
|
*/
|
|
7152
7152
|
resetCircuits() {
|
|
7153
|
-
for (const
|
|
7154
|
-
|
|
7153
|
+
for (const p47 of this.providers) {
|
|
7154
|
+
p47.breaker.reset();
|
|
7155
7155
|
}
|
|
7156
7156
|
}
|
|
7157
7157
|
/**
|
|
@@ -7726,9 +7726,9 @@ async function migrateGlobalConfig(oldDir, newConfigPath) {
|
|
|
7726
7726
|
);
|
|
7727
7727
|
}
|
|
7728
7728
|
}
|
|
7729
|
-
async function fileExists(
|
|
7729
|
+
async function fileExists(path62) {
|
|
7730
7730
|
try {
|
|
7731
|
-
await access(
|
|
7731
|
+
await access(path62);
|
|
7732
7732
|
return true;
|
|
7733
7733
|
} catch {
|
|
7734
7734
|
return false;
|
|
@@ -8111,8 +8111,8 @@ var init_registry = __esm({
|
|
|
8111
8111
|
/**
|
|
8112
8112
|
* Ensure directory exists
|
|
8113
8113
|
*/
|
|
8114
|
-
async ensureDir(
|
|
8115
|
-
await mkdir(dirname(
|
|
8114
|
+
async ensureDir(path62) {
|
|
8115
|
+
await mkdir(dirname(path62), { recursive: true });
|
|
8116
8116
|
}
|
|
8117
8117
|
};
|
|
8118
8118
|
}
|
|
@@ -9749,9 +9749,9 @@ function createEmptyMemoryContext() {
|
|
|
9749
9749
|
errors: []
|
|
9750
9750
|
};
|
|
9751
9751
|
}
|
|
9752
|
-
function createMissingMemoryFile(
|
|
9752
|
+
function createMissingMemoryFile(path62, level) {
|
|
9753
9753
|
return {
|
|
9754
|
-
path:
|
|
9754
|
+
path: path62,
|
|
9755
9755
|
level,
|
|
9756
9756
|
content: "",
|
|
9757
9757
|
sections: [],
|
|
@@ -10217,6 +10217,9 @@ After outputting the plan, the user will decide to approve, edit, or reject it.
|
|
|
10217
10217
|
if (subcommand === "status") {
|
|
10218
10218
|
if (session.planMode) {
|
|
10219
10219
|
p26.log.info(chalk.cyan("Plan mode is ACTIVE (read-only tools only)"));
|
|
10220
|
+
if (session.config.agent.planModeStrict) {
|
|
10221
|
+
p26.log.info("Strict plan mode is ON. Only the strict read-only allowlist is available.");
|
|
10222
|
+
}
|
|
10220
10223
|
if (session.pendingPlan) {
|
|
10221
10224
|
p26.log.info("A plan is pending approval. Use /plan approve or /plan reject.");
|
|
10222
10225
|
}
|
|
@@ -10271,6 +10274,9 @@ ${plan}`
|
|
|
10271
10274
|
const instruction = args.join(" ");
|
|
10272
10275
|
p26.log.success(chalk.cyan("Plan mode ACTIVATED"));
|
|
10273
10276
|
p26.log.info("Agent will explore the codebase and create a plan.");
|
|
10277
|
+
if (session.config.agent.planModeStrict) {
|
|
10278
|
+
p26.log.info("Strict plan mode is ON. Execution tools remain blocked until approval.");
|
|
10279
|
+
}
|
|
10274
10280
|
p26.log.info(chalk.dim(`Task: ${instruction}`));
|
|
10275
10281
|
p26.log.info("After the plan is generated, use /plan approve or /plan reject.");
|
|
10276
10282
|
session.messages.push({
|
|
@@ -10357,10 +10363,27 @@ async function createDefaultReplConfig() {
|
|
|
10357
10363
|
systemPrompt: COCO_SYSTEM_PROMPT,
|
|
10358
10364
|
maxToolIterations: 25,
|
|
10359
10365
|
confirmDestructive: true,
|
|
10360
|
-
enableAutoSwitchProvider: false
|
|
10366
|
+
enableAutoSwitchProvider: false,
|
|
10367
|
+
recoveryV2: readBooleanFlag("COCO_AGENT_RECOVERY_V2", true),
|
|
10368
|
+
planModeStrict: readBooleanFlag("COCO_AGENT_PLAN_MODE_STRICT", true),
|
|
10369
|
+
doctorV2: readBooleanFlag("COCO_AGENT_DOCTOR_V2", true),
|
|
10370
|
+
outputOffload: readBooleanFlag("COCO_AGENT_OUTPUT_OFFLOAD", false)
|
|
10361
10371
|
}
|
|
10362
10372
|
};
|
|
10363
10373
|
}
|
|
10374
|
+
function readBooleanFlag(envName, defaultValue) {
|
|
10375
|
+
const value = process.env[envName]?.trim().toLowerCase();
|
|
10376
|
+
if (value === void 0) {
|
|
10377
|
+
return defaultValue;
|
|
10378
|
+
}
|
|
10379
|
+
if (value === "1" || value === "true" || value === "yes" || value === "on") {
|
|
10380
|
+
return true;
|
|
10381
|
+
}
|
|
10382
|
+
if (value === "0" || value === "false" || value === "no" || value === "off") {
|
|
10383
|
+
return false;
|
|
10384
|
+
}
|
|
10385
|
+
return defaultValue;
|
|
10386
|
+
}
|
|
10364
10387
|
async function createSession(projectPath, config) {
|
|
10365
10388
|
const defaultConfig = await createDefaultReplConfig();
|
|
10366
10389
|
return {
|
|
@@ -11234,8 +11257,8 @@ __export(trust_store_exports, {
|
|
|
11234
11257
|
saveTrustStore: () => saveTrustStore,
|
|
11235
11258
|
updateLastAccessed: () => updateLastAccessed
|
|
11236
11259
|
});
|
|
11237
|
-
async function ensureDir(
|
|
11238
|
-
await mkdir(dirname(
|
|
11260
|
+
async function ensureDir(path62) {
|
|
11261
|
+
await mkdir(dirname(path62), { recursive: true });
|
|
11239
11262
|
}
|
|
11240
11263
|
async function loadTrustStore(storePath = TRUST_STORE_PATH) {
|
|
11241
11264
|
try {
|
|
@@ -11320,8 +11343,8 @@ function canPerformOperation(store, projectPath, operation) {
|
|
|
11320
11343
|
};
|
|
11321
11344
|
return permissions[level]?.includes(operation) ?? false;
|
|
11322
11345
|
}
|
|
11323
|
-
function normalizePath(
|
|
11324
|
-
return join(
|
|
11346
|
+
function normalizePath(path62) {
|
|
11347
|
+
return join(path62);
|
|
11325
11348
|
}
|
|
11326
11349
|
function createTrustStore(storePath = TRUST_STORE_PATH) {
|
|
11327
11350
|
let store = null;
|
|
@@ -11677,12 +11700,12 @@ function humanizeError(message, toolName) {
|
|
|
11677
11700
|
return msg;
|
|
11678
11701
|
}
|
|
11679
11702
|
if (/ENOENT/i.test(msg)) {
|
|
11680
|
-
const
|
|
11681
|
-
return
|
|
11703
|
+
const path62 = extractQuotedPath(msg);
|
|
11704
|
+
return path62 ? `File or directory not found: ${path62}` : "File or directory not found";
|
|
11682
11705
|
}
|
|
11683
11706
|
if (/EACCES/i.test(msg)) {
|
|
11684
|
-
const
|
|
11685
|
-
return
|
|
11707
|
+
const path62 = extractQuotedPath(msg);
|
|
11708
|
+
return path62 ? `Permission denied: ${path62}` : "Permission denied \u2014 check file permissions";
|
|
11686
11709
|
}
|
|
11687
11710
|
if (/EISDIR/i.test(msg)) {
|
|
11688
11711
|
return "Expected a file but found a directory at the specified path";
|
|
@@ -11774,7 +11797,7 @@ function humanizeError(message, toolName) {
|
|
|
11774
11797
|
return msg;
|
|
11775
11798
|
}
|
|
11776
11799
|
function looksLikeTechnicalJargon(message) {
|
|
11777
|
-
return JARGON_PATTERNS.some((
|
|
11800
|
+
return JARGON_PATTERNS.some((p47) => p47.test(message));
|
|
11778
11801
|
}
|
|
11779
11802
|
async function humanizeWithLLM(errorMessage, toolName, provider) {
|
|
11780
11803
|
const prompt = [
|
|
@@ -12535,9 +12558,9 @@ function renderFileBlock(file, opts) {
|
|
|
12535
12558
|
);
|
|
12536
12559
|
}
|
|
12537
12560
|
const pairs = pairAdjacentLines(hunk.lines);
|
|
12538
|
-
const pairedDeleteIndices = new Set(pairs.map((
|
|
12539
|
-
const pairedAddIndices = new Set(pairs.map((
|
|
12540
|
-
const pairByAdd = new Map(pairs.map((
|
|
12561
|
+
const pairedDeleteIndices = new Set(pairs.map((p47) => p47.deleteIdx));
|
|
12562
|
+
const pairedAddIndices = new Set(pairs.map((p47) => p47.addIdx));
|
|
12563
|
+
const pairByAdd = new Map(pairs.map((p47) => [p47.addIdx, p47.deleteIdx]));
|
|
12541
12564
|
const wordHighlights = /* @__PURE__ */ new Map();
|
|
12542
12565
|
for (const pair of pairs) {
|
|
12543
12566
|
const delLine = hunk.lines[pair.deleteIdx];
|
|
@@ -12616,9 +12639,9 @@ var init_diff_renderer = __esm({
|
|
|
12616
12639
|
getTerminalWidth = () => process.stdout.columns || 80;
|
|
12617
12640
|
}
|
|
12618
12641
|
});
|
|
12619
|
-
async function fileExists4(
|
|
12642
|
+
async function fileExists4(path62) {
|
|
12620
12643
|
try {
|
|
12621
|
-
await access(
|
|
12644
|
+
await access(path62);
|
|
12622
12645
|
return true;
|
|
12623
12646
|
} catch {
|
|
12624
12647
|
return false;
|
|
@@ -12816,13 +12839,13 @@ var init_subprocess_registry = __esm({
|
|
|
12816
12839
|
});
|
|
12817
12840
|
async function detectTestFramework(projectPath) {
|
|
12818
12841
|
try {
|
|
12819
|
-
await access(join(projectPath, "pom.xml"), constants.R_OK);
|
|
12842
|
+
await access(join(projectPath, "pom.xml"), constants$1.R_OK);
|
|
12820
12843
|
return "maven";
|
|
12821
12844
|
} catch {
|
|
12822
12845
|
}
|
|
12823
12846
|
for (const f of ["build.gradle", "build.gradle.kts"]) {
|
|
12824
12847
|
try {
|
|
12825
|
-
await access(join(projectPath, f), constants.R_OK);
|
|
12848
|
+
await access(join(projectPath, f), constants$1.R_OK);
|
|
12826
12849
|
return "gradle";
|
|
12827
12850
|
} catch {
|
|
12828
12851
|
}
|
|
@@ -12980,7 +13003,7 @@ var init_coverage = __esm({
|
|
|
12980
13003
|
] : [join(this.projectPath, "build", "reports", "jacoco", "test", "jacocoTestReport.csv")];
|
|
12981
13004
|
for (const csvPath of jacocoPaths) {
|
|
12982
13005
|
try {
|
|
12983
|
-
await access(csvPath, constants.R_OK);
|
|
13006
|
+
await access(csvPath, constants$1.R_OK);
|
|
12984
13007
|
const csv = await readFile(csvPath, "utf-8");
|
|
12985
13008
|
return parseJacocoCsv(csv);
|
|
12986
13009
|
} catch {
|
|
@@ -12993,10 +13016,10 @@ var init_coverage = __esm({
|
|
|
12993
13016
|
join(this.projectPath, ".coverage", "coverage-summary.json"),
|
|
12994
13017
|
join(this.projectPath, "coverage", "lcov-report", "coverage-summary.json")
|
|
12995
13018
|
];
|
|
12996
|
-
for (const
|
|
13019
|
+
for (const p47 of possiblePaths) {
|
|
12997
13020
|
try {
|
|
12998
|
-
await access(
|
|
12999
|
-
const content = await readFile(
|
|
13021
|
+
await access(p47, constants$1.R_OK);
|
|
13022
|
+
const content = await readFile(p47, "utf-8");
|
|
13000
13023
|
const report = JSON.parse(content);
|
|
13001
13024
|
return parseCoverageSummary(report);
|
|
13002
13025
|
} catch {
|
|
@@ -14198,7 +14221,7 @@ var init_completeness = __esm({
|
|
|
14198
14221
|
];
|
|
14199
14222
|
for (const entry of entryPoints) {
|
|
14200
14223
|
try {
|
|
14201
|
-
await access(join(this.projectPath, entry), constants.R_OK);
|
|
14224
|
+
await access(join(this.projectPath, entry), constants$1.R_OK);
|
|
14202
14225
|
return true;
|
|
14203
14226
|
} catch {
|
|
14204
14227
|
}
|
|
@@ -14691,7 +14714,7 @@ var init_documentation = __esm({
|
|
|
14691
14714
|
}
|
|
14692
14715
|
async fileExists(relativePath) {
|
|
14693
14716
|
try {
|
|
14694
|
-
await access(join(this.projectPath, relativePath), constants.R_OK);
|
|
14717
|
+
await access(join(this.projectPath, relativePath), constants$1.R_OK);
|
|
14695
14718
|
return true;
|
|
14696
14719
|
} catch {
|
|
14697
14720
|
return false;
|
|
@@ -16998,16 +17021,16 @@ var init_evaluator = __esm({
|
|
|
16998
17021
|
* Find source files in project, adapting to the detected language stack.
|
|
16999
17022
|
*/
|
|
17000
17023
|
async findSourceFiles() {
|
|
17001
|
-
const { access:
|
|
17024
|
+
const { access: access18 } = await import('fs/promises');
|
|
17002
17025
|
const { join: join26 } = await import('path');
|
|
17003
17026
|
let isJava = false;
|
|
17004
17027
|
try {
|
|
17005
|
-
await
|
|
17028
|
+
await access18(join26(this.projectPath, "pom.xml"));
|
|
17006
17029
|
isJava = true;
|
|
17007
17030
|
} catch {
|
|
17008
17031
|
for (const f of ["build.gradle", "build.gradle.kts"]) {
|
|
17009
17032
|
try {
|
|
17010
|
-
await
|
|
17033
|
+
await access18(join26(this.projectPath, f));
|
|
17011
17034
|
isJava = true;
|
|
17012
17035
|
break;
|
|
17013
17036
|
} catch {
|
|
@@ -17934,13 +17957,13 @@ function buildReviewMarkdown(result) {
|
|
|
17934
17957
|
const { summary, required, suggestions, maturity } = result;
|
|
17935
17958
|
const lines = [];
|
|
17936
17959
|
lines.push("## Code Review\n");
|
|
17937
|
-
const
|
|
17960
|
+
const statusIcon2 = summary.status === "approved" ? "Approved" : "Needs Work";
|
|
17938
17961
|
lines.push(`**Branch:** \`${summary.branch}\` \u2192 \`${summary.baseBranch}\``);
|
|
17939
17962
|
lines.push(
|
|
17940
17963
|
`**Files:** ${summary.filesChanged} changed | +${summary.additions} -${summary.deletions}`
|
|
17941
17964
|
);
|
|
17942
17965
|
lines.push(`**Maturity:** ${maturity}`);
|
|
17943
|
-
lines.push(`**Status:** ${
|
|
17966
|
+
lines.push(`**Status:** ${statusIcon2}`);
|
|
17944
17967
|
lines.push("");
|
|
17945
17968
|
if (required.length > 0) {
|
|
17946
17969
|
lines.push(`### Required (${required.length})
|
|
@@ -18366,9 +18389,9 @@ Examples:
|
|
|
18366
18389
|
if (file) {
|
|
18367
18390
|
options.file = file;
|
|
18368
18391
|
}
|
|
18369
|
-
const
|
|
18392
|
+
const log39 = await git.log(options);
|
|
18370
18393
|
return {
|
|
18371
|
-
commits:
|
|
18394
|
+
commits: log39.all.map((commit) => ({
|
|
18372
18395
|
hash: commit.hash,
|
|
18373
18396
|
message: commit.message,
|
|
18374
18397
|
author: commit.author_name,
|
|
@@ -20031,18 +20054,18 @@ async function runVersion(ctx) {
|
|
|
20031
20054
|
});
|
|
20032
20055
|
const tag = lastTag.stdout.trim();
|
|
20033
20056
|
if (tag) {
|
|
20034
|
-
const
|
|
20057
|
+
const log39 = await bashExecTool.execute({
|
|
20035
20058
|
command: `git log ${tag}..HEAD --oneline`,
|
|
20036
20059
|
cwd: ctx.cwd
|
|
20037
20060
|
});
|
|
20038
|
-
commitMessages =
|
|
20061
|
+
commitMessages = log39.stdout.trim().split("\n").filter(Boolean);
|
|
20039
20062
|
} else {
|
|
20040
|
-
const
|
|
20041
|
-
commitMessages =
|
|
20063
|
+
const log39 = await gitLogTool.execute({ cwd: ctx.cwd, maxCount: 20 });
|
|
20064
|
+
commitMessages = log39.commits.map((c) => c.message);
|
|
20042
20065
|
}
|
|
20043
20066
|
} catch {
|
|
20044
|
-
const
|
|
20045
|
-
commitMessages =
|
|
20067
|
+
const log39 = await gitLogTool.execute({ cwd: ctx.cwd, maxCount: 10 });
|
|
20068
|
+
commitMessages = log39.commits.map((c) => c.message);
|
|
20046
20069
|
}
|
|
20047
20070
|
detectedBump = detectBumpFromCommits(commitMessages);
|
|
20048
20071
|
}
|
|
@@ -20080,8 +20103,8 @@ async function runVersion(ctx) {
|
|
|
20080
20103
|
p26.log.success(`Updated ${versionFile.path}: ${currentVersion} \u2192 ${finalVersion}`);
|
|
20081
20104
|
if (ctx.profile.changelog && !ctx.options.noChangelog) {
|
|
20082
20105
|
try {
|
|
20083
|
-
const
|
|
20084
|
-
const entries = generateChangelogEntries(
|
|
20106
|
+
const log39 = await gitLogTool.execute({ cwd: ctx.cwd, maxCount: 30 });
|
|
20107
|
+
const entries = generateChangelogEntries(log39.commits.map((c) => c.message));
|
|
20085
20108
|
if (entries.length > 0) {
|
|
20086
20109
|
await insertChangelogEntry(ctx.cwd, ctx.profile.changelog, finalVersion, entries);
|
|
20087
20110
|
p26.log.success(`Updated ${ctx.profile.changelog.path} with ${entries.length} entries`);
|
|
@@ -20799,11 +20822,11 @@ function isBlockedPath(absolute) {
|
|
|
20799
20822
|
return void 0;
|
|
20800
20823
|
}
|
|
20801
20824
|
function isBlockedExecFile(filePath) {
|
|
20802
|
-
return BLOCKED_EXEC_PATTERNS.some((
|
|
20825
|
+
return BLOCKED_EXEC_PATTERNS.some((p47) => p47.test(filePath));
|
|
20803
20826
|
}
|
|
20804
20827
|
function hasDangerousArgs(args) {
|
|
20805
20828
|
const joined = args.join(" ");
|
|
20806
|
-
return DANGEROUS_ARG_PATTERNS.some((
|
|
20829
|
+
return DANGEROUS_ARG_PATTERNS.some((p47) => p47.test(joined));
|
|
20807
20830
|
}
|
|
20808
20831
|
function getInterpreter(ext) {
|
|
20809
20832
|
return INTERPRETER_MAP[ext.toLowerCase()];
|
|
@@ -23311,413 +23334,6 @@ var init_allow_path_prompt = __esm({
|
|
|
23311
23334
|
}
|
|
23312
23335
|
});
|
|
23313
23336
|
|
|
23314
|
-
// src/cli/repl/interruptions/types.ts
|
|
23315
|
-
var init_types7 = __esm({
|
|
23316
|
-
"src/cli/repl/interruptions/types.ts"() {
|
|
23317
|
-
}
|
|
23318
|
-
});
|
|
23319
|
-
|
|
23320
|
-
// src/cli/repl/interruptions/classifier.ts
|
|
23321
|
-
function matchPatterns(text15, patterns) {
|
|
23322
|
-
for (let i = 0; i < patterns.length; i++) {
|
|
23323
|
-
if (patterns[i].test(text15)) {
|
|
23324
|
-
return 1 - i * 0.1;
|
|
23325
|
-
}
|
|
23326
|
-
}
|
|
23327
|
-
return 0;
|
|
23328
|
-
}
|
|
23329
|
-
function classifyInterruption(message) {
|
|
23330
|
-
const text15 = message.text;
|
|
23331
|
-
const abortConf = matchPatterns(text15, ABORT_PATTERNS);
|
|
23332
|
-
const modifyConf = matchPatterns(text15, MODIFY_PATTERNS);
|
|
23333
|
-
const correctConf = matchPatterns(text15, CORRECT_PATTERNS);
|
|
23334
|
-
if (abortConf > 0 && abortConf >= modifyConf && abortConf >= correctConf) {
|
|
23335
|
-
return {
|
|
23336
|
-
text: text15,
|
|
23337
|
-
type: "abort" /* Abort */,
|
|
23338
|
-
confidence: Math.min(1, abortConf),
|
|
23339
|
-
timestamp: message.timestamp
|
|
23340
|
-
};
|
|
23341
|
-
}
|
|
23342
|
-
if (modifyConf > 0 && modifyConf >= correctConf) {
|
|
23343
|
-
return {
|
|
23344
|
-
text: text15,
|
|
23345
|
-
type: "modify" /* Modify */,
|
|
23346
|
-
confidence: Math.min(1, modifyConf),
|
|
23347
|
-
timestamp: message.timestamp
|
|
23348
|
-
};
|
|
23349
|
-
}
|
|
23350
|
-
if (correctConf > 0) {
|
|
23351
|
-
return {
|
|
23352
|
-
text: text15,
|
|
23353
|
-
type: "correct" /* Correct */,
|
|
23354
|
-
confidence: Math.min(1, correctConf),
|
|
23355
|
-
timestamp: message.timestamp
|
|
23356
|
-
};
|
|
23357
|
-
}
|
|
23358
|
-
return {
|
|
23359
|
-
text: text15,
|
|
23360
|
-
type: "info" /* Info */,
|
|
23361
|
-
confidence: 0.5,
|
|
23362
|
-
timestamp: message.timestamp
|
|
23363
|
-
};
|
|
23364
|
-
}
|
|
23365
|
-
var ABORT_PATTERNS, MODIFY_PATTERNS, CORRECT_PATTERNS;
|
|
23366
|
-
var init_classifier = __esm({
|
|
23367
|
-
"src/cli/repl/interruptions/classifier.ts"() {
|
|
23368
|
-
init_types7();
|
|
23369
|
-
ABORT_PATTERNS = [
|
|
23370
|
-
/^(para|stop|cancel|abort|quit|exit|detente|basta)$/i,
|
|
23371
|
-
/^(para\s+ya|stop\s+it|cancel\s+that)$/i,
|
|
23372
|
-
/\b(para|stop|cancel|abort)\b/i
|
|
23373
|
-
];
|
|
23374
|
-
MODIFY_PATTERNS = [
|
|
23375
|
-
/^(a[ñn]ade|add|incluye|include|pon|put|agrega)\b/i,
|
|
23376
|
-
/^(cambia|change|modifica|modify|usa|use|haz|make)\b/i,
|
|
23377
|
-
/^no[,.]?\s+/i,
|
|
23378
|
-
// "no, mejor de la griega" — negation signals redirection
|
|
23379
|
-
/^(prefiero|prefer|quiero|i\s+want)\b/i,
|
|
23380
|
-
// "prefiero en gijon" — preference signals redirection
|
|
23381
|
-
/\b(a[ñn]ade|add|incluye|include)\s+/i,
|
|
23382
|
-
/\b(cambia|change|modifica|modify)\s+/i,
|
|
23383
|
-
/\b(en\s+vez\s+de|instead\s+of|rather\s+than)\b/i,
|
|
23384
|
-
/\b(prefiero|prefer|mejor|better|más|more|less|menos|bigger|smaller|larger)\b/i,
|
|
23385
|
-
/\b(también|also|además|additionally)\b/i
|
|
23386
|
-
];
|
|
23387
|
-
CORRECT_PATTERNS = [
|
|
23388
|
-
/^(error|bug|fallo|wrong|mal|incorrect)\b/i,
|
|
23389
|
-
/^(arregla|fix|corrige|correct|repara|repair)\b/i,
|
|
23390
|
-
/\b(error\s+en|bug\s+in|fallo\s+en)\b/i,
|
|
23391
|
-
/\b(está\s+mal|is\s+wrong|no\s+funciona|doesn'?t\s+work)\b/i,
|
|
23392
|
-
/\b(arregla|fix|corrige|correct)\s+/i
|
|
23393
|
-
];
|
|
23394
|
-
}
|
|
23395
|
-
});
|
|
23396
|
-
function mapClassificationToAction(type) {
|
|
23397
|
-
switch (type) {
|
|
23398
|
-
case "abort" /* Abort */:
|
|
23399
|
-
return "abort" /* Abort */;
|
|
23400
|
-
// Explicit modification/correction keywords → pre-select Modify
|
|
23401
|
-
case "modify" /* Modify */:
|
|
23402
|
-
case "correct" /* Correct */:
|
|
23403
|
-
return "modify" /* Modify */;
|
|
23404
|
-
// Info or unclassified → pre-select Queue (likely a new topic/task)
|
|
23405
|
-
// The user can always switch to Modify via the menu if needed
|
|
23406
|
-
case "info" /* Info */:
|
|
23407
|
-
default:
|
|
23408
|
-
return "queue" /* Queue */;
|
|
23409
|
-
}
|
|
23410
|
-
}
|
|
23411
|
-
var init_action_selector = __esm({
|
|
23412
|
-
"src/cli/repl/input/action-selector.ts"() {
|
|
23413
|
-
init_types7();
|
|
23414
|
-
}
|
|
23415
|
-
});
|
|
23416
|
-
|
|
23417
|
-
// src/cli/repl/interruptions/llm-classifier.ts
|
|
23418
|
-
var llm_classifier_exports = {};
|
|
23419
|
-
__export(llm_classifier_exports, {
|
|
23420
|
-
createLLMClassifier: () => createLLMClassifier
|
|
23421
|
-
});
|
|
23422
|
-
function buildClassificationPrompt(userMessage, currentTask) {
|
|
23423
|
-
if (currentTask) {
|
|
23424
|
-
return `Current task: "${currentTask}"
|
|
23425
|
-
User's new message: "${userMessage}"`;
|
|
23426
|
-
}
|
|
23427
|
-
return `User's new message: "${userMessage}"`;
|
|
23428
|
-
}
|
|
23429
|
-
function parseResponse(response) {
|
|
23430
|
-
const normalized = response.trim().toUpperCase();
|
|
23431
|
-
if (normalized.includes("STEER")) return "steer" /* Steer */;
|
|
23432
|
-
if (normalized.includes("MODIFY")) return "modify" /* Modify */;
|
|
23433
|
-
if (normalized.includes("QUEUE")) return "queue" /* Queue */;
|
|
23434
|
-
if (normalized.includes("ABORT")) return "abort" /* Abort */;
|
|
23435
|
-
return null;
|
|
23436
|
-
}
|
|
23437
|
-
function classifyWithKeywords(message) {
|
|
23438
|
-
const classified = classifyInterruption(message);
|
|
23439
|
-
return mapClassificationToAction(classified.type);
|
|
23440
|
-
}
|
|
23441
|
-
function createLLMClassifier(provider, config) {
|
|
23442
|
-
const cfg = { ...DEFAULT_CONFIG6, ...config };
|
|
23443
|
-
return {
|
|
23444
|
-
/**
|
|
23445
|
-
* Classify a user message captured during agent execution.
|
|
23446
|
-
*
|
|
23447
|
-
* Makes a fast parallel LLM call. If the LLM doesn't respond within
|
|
23448
|
-
* the timeout, falls back to keyword-based classification.
|
|
23449
|
-
*
|
|
23450
|
-
* @param message - The captured message
|
|
23451
|
-
* @param currentTask - The original task the agent is working on (for context)
|
|
23452
|
-
* @returns Classification result with action and source
|
|
23453
|
-
*/
|
|
23454
|
-
async classify(message, currentTask) {
|
|
23455
|
-
const llmPromise = classifyWithLLM(provider, message, currentTask, cfg);
|
|
23456
|
-
let timeoutId;
|
|
23457
|
-
const timeoutPromise = new Promise((resolve4) => {
|
|
23458
|
-
timeoutId = setTimeout(() => resolve4(null), cfg.timeoutMs);
|
|
23459
|
-
});
|
|
23460
|
-
try {
|
|
23461
|
-
const llmResult = await Promise.race([llmPromise, timeoutPromise]);
|
|
23462
|
-
if (llmResult !== null) {
|
|
23463
|
-
return { action: llmResult, source: "llm" };
|
|
23464
|
-
}
|
|
23465
|
-
return {
|
|
23466
|
-
action: classifyWithKeywords(message),
|
|
23467
|
-
source: "keywords"
|
|
23468
|
-
};
|
|
23469
|
-
} finally {
|
|
23470
|
-
clearTimeout(timeoutId);
|
|
23471
|
-
}
|
|
23472
|
-
}
|
|
23473
|
-
};
|
|
23474
|
-
}
|
|
23475
|
-
async function classifyWithLLM(provider, message, currentTask, cfg) {
|
|
23476
|
-
try {
|
|
23477
|
-
const userPrompt = buildClassificationPrompt(message.text, currentTask);
|
|
23478
|
-
const options = {
|
|
23479
|
-
maxTokens: cfg.maxTokens,
|
|
23480
|
-
temperature: cfg.temperature,
|
|
23481
|
-
system: CLASSIFICATION_SYSTEM_PROMPT2,
|
|
23482
|
-
timeout: cfg.timeoutMs
|
|
23483
|
-
};
|
|
23484
|
-
const response = await provider.chat([{ role: "user", content: userPrompt }], options);
|
|
23485
|
-
return parseResponse(response.content);
|
|
23486
|
-
} catch {
|
|
23487
|
-
return null;
|
|
23488
|
-
}
|
|
23489
|
-
}
|
|
23490
|
-
var DEFAULT_CONFIG6, CLASSIFICATION_SYSTEM_PROMPT2;
|
|
23491
|
-
var init_llm_classifier = __esm({
|
|
23492
|
-
"src/cli/repl/interruptions/llm-classifier.ts"() {
|
|
23493
|
-
init_types7();
|
|
23494
|
-
init_classifier();
|
|
23495
|
-
init_action_selector();
|
|
23496
|
-
DEFAULT_CONFIG6 = {
|
|
23497
|
-
timeoutMs: 8e3,
|
|
23498
|
-
maxTokens: 10,
|
|
23499
|
-
temperature: 0
|
|
23500
|
-
};
|
|
23501
|
-
CLASSIFICATION_SYSTEM_PROMPT2 = `You are a message classifier for a coding assistant. The user sent a message while the assistant was working on a task.
|
|
23502
|
-
|
|
23503
|
-
Classify the message into exactly ONE category. Reply with ONLY the category word, nothing else.
|
|
23504
|
-
|
|
23505
|
-
Categories:
|
|
23506
|
-
- STEER: The message provides a hint, preference, or minor adjustment to the CURRENT task without requiring a restart (e.g. "also add a test", "use camelCase", "make sure to handle errors", "btw the file is in src/", "prefer async/await")
|
|
23507
|
-
- MODIFY: The message fundamentally changes the CURRENT task requiring a fresh start (e.g. "actually use Python instead of JS", "no, do it completely differently", "start over with a new approach")
|
|
23508
|
-
- QUEUE: The message is a NEW, DIFFERENT task unrelated to what's being done (e.g. "what's 2+2", "tell me the weather", "create another file for X")
|
|
23509
|
-
- ABORT: The message asks to stop/cancel the current work (e.g. "stop", "cancel", "para", "never mind")
|
|
23510
|
-
|
|
23511
|
-
Reply with exactly one word: STEER, MODIFY, QUEUE, or ABORT`;
|
|
23512
|
-
}
|
|
23513
|
-
});
|
|
23514
|
-
|
|
23515
|
-
// src/cli/repl/context/stack-detector.ts
|
|
23516
|
-
var stack_detector_exports = {};
|
|
23517
|
-
__export(stack_detector_exports, {
|
|
23518
|
-
detectProjectStack: () => detectProjectStack
|
|
23519
|
-
});
|
|
23520
|
-
async function detectStack2(cwd) {
|
|
23521
|
-
if (await fileExists3(path39__default.join(cwd, "package.json"))) return "node";
|
|
23522
|
-
if (await fileExists3(path39__default.join(cwd, "Cargo.toml"))) return "rust";
|
|
23523
|
-
if (await fileExists3(path39__default.join(cwd, "pyproject.toml"))) return "python";
|
|
23524
|
-
if (await fileExists3(path39__default.join(cwd, "go.mod"))) return "go";
|
|
23525
|
-
if (await fileExists3(path39__default.join(cwd, "pom.xml"))) return "java";
|
|
23526
|
-
if (await fileExists3(path39__default.join(cwd, "build.gradle"))) return "java";
|
|
23527
|
-
if (await fileExists3(path39__default.join(cwd, "build.gradle.kts"))) return "java";
|
|
23528
|
-
return "unknown";
|
|
23529
|
-
}
|
|
23530
|
-
async function detectPackageManager3(cwd, stack) {
|
|
23531
|
-
if (stack === "rust") return "cargo";
|
|
23532
|
-
if (stack === "python") return "pip";
|
|
23533
|
-
if (stack === "go") return "go";
|
|
23534
|
-
if (stack === "java") {
|
|
23535
|
-
if (await fileExists3(path39__default.join(cwd, "build.gradle")) || await fileExists3(path39__default.join(cwd, "build.gradle.kts"))) {
|
|
23536
|
-
return "gradle";
|
|
23537
|
-
}
|
|
23538
|
-
if (await fileExists3(path39__default.join(cwd, "pom.xml"))) {
|
|
23539
|
-
return "maven";
|
|
23540
|
-
}
|
|
23541
|
-
}
|
|
23542
|
-
if (stack === "node") {
|
|
23543
|
-
if (await fileExists3(path39__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
23544
|
-
if (await fileExists3(path39__default.join(cwd, "yarn.lock"))) return "yarn";
|
|
23545
|
-
if (await fileExists3(path39__default.join(cwd, "bun.lockb"))) return "bun";
|
|
23546
|
-
return "npm";
|
|
23547
|
-
}
|
|
23548
|
-
return null;
|
|
23549
|
-
}
|
|
23550
|
-
async function parsePackageJson(cwd) {
|
|
23551
|
-
const packageJsonPath = path39__default.join(cwd, "package.json");
|
|
23552
|
-
try {
|
|
23553
|
-
const content = await fs35__default.readFile(packageJsonPath, "utf-8");
|
|
23554
|
-
const pkg = JSON.parse(content);
|
|
23555
|
-
const allDeps = {
|
|
23556
|
-
...pkg.dependencies,
|
|
23557
|
-
...pkg.devDependencies
|
|
23558
|
-
};
|
|
23559
|
-
const frameworks = [];
|
|
23560
|
-
if (allDeps.react) frameworks.push("React");
|
|
23561
|
-
if (allDeps.vue) frameworks.push("Vue");
|
|
23562
|
-
if (allDeps["@angular/core"]) frameworks.push("Angular");
|
|
23563
|
-
if (allDeps.next) frameworks.push("Next.js");
|
|
23564
|
-
if (allDeps.nuxt) frameworks.push("Nuxt");
|
|
23565
|
-
if (allDeps.express) frameworks.push("Express");
|
|
23566
|
-
if (allDeps.fastify) frameworks.push("Fastify");
|
|
23567
|
-
if (allDeps.nestjs || allDeps["@nestjs/core"]) frameworks.push("NestJS");
|
|
23568
|
-
const buildTools2 = [];
|
|
23569
|
-
if (allDeps.webpack) buildTools2.push("webpack");
|
|
23570
|
-
if (allDeps.vite) buildTools2.push("vite");
|
|
23571
|
-
if (allDeps.rollup) buildTools2.push("rollup");
|
|
23572
|
-
if (allDeps.tsup) buildTools2.push("tsup");
|
|
23573
|
-
if (allDeps.esbuild) buildTools2.push("esbuild");
|
|
23574
|
-
if (pkg.scripts?.build) buildTools2.push("build");
|
|
23575
|
-
const testingFrameworks = [];
|
|
23576
|
-
if (allDeps.vitest) testingFrameworks.push("vitest");
|
|
23577
|
-
if (allDeps.jest) testingFrameworks.push("jest");
|
|
23578
|
-
if (allDeps.mocha) testingFrameworks.push("mocha");
|
|
23579
|
-
if (allDeps.chai) testingFrameworks.push("chai");
|
|
23580
|
-
if (allDeps["@playwright/test"]) testingFrameworks.push("playwright");
|
|
23581
|
-
if (allDeps.cypress) testingFrameworks.push("cypress");
|
|
23582
|
-
const languages = ["JavaScript"];
|
|
23583
|
-
if (allDeps.typescript || await fileExists3(path39__default.join(cwd, "tsconfig.json"))) {
|
|
23584
|
-
languages.push("TypeScript");
|
|
23585
|
-
}
|
|
23586
|
-
return {
|
|
23587
|
-
dependencies: allDeps,
|
|
23588
|
-
frameworks,
|
|
23589
|
-
buildTools: buildTools2,
|
|
23590
|
-
testingFrameworks,
|
|
23591
|
-
languages
|
|
23592
|
-
};
|
|
23593
|
-
} catch {
|
|
23594
|
-
return {
|
|
23595
|
-
dependencies: {},
|
|
23596
|
-
frameworks: [],
|
|
23597
|
-
buildTools: [],
|
|
23598
|
-
testingFrameworks: [],
|
|
23599
|
-
languages: []
|
|
23600
|
-
};
|
|
23601
|
-
}
|
|
23602
|
-
}
|
|
23603
|
-
async function parsePomXml(cwd) {
|
|
23604
|
-
const pomPath = path39__default.join(cwd, "pom.xml");
|
|
23605
|
-
try {
|
|
23606
|
-
const content = await fs35__default.readFile(pomPath, "utf-8");
|
|
23607
|
-
const dependencies = {};
|
|
23608
|
-
const frameworks = [];
|
|
23609
|
-
const buildTools2 = ["maven"];
|
|
23610
|
-
const testingFrameworks = [];
|
|
23611
|
-
const depRegex = /<groupId>([^<]+)<\/groupId>\s*<artifactId>([^<]+)<\/artifactId>/g;
|
|
23612
|
-
let match;
|
|
23613
|
-
while ((match = depRegex.exec(content)) !== null) {
|
|
23614
|
-
const groupId = match[1];
|
|
23615
|
-
const artifactId = match[2];
|
|
23616
|
-
if (!groupId || !artifactId) continue;
|
|
23617
|
-
const fullName = `${groupId}:${artifactId}`;
|
|
23618
|
-
dependencies[fullName] = "unknown";
|
|
23619
|
-
if (artifactId.includes("spring-boot")) {
|
|
23620
|
-
if (!frameworks.includes("Spring Boot")) frameworks.push("Spring Boot");
|
|
23621
|
-
}
|
|
23622
|
-
if (artifactId.includes("spring-webmvc") || artifactId.includes("spring-web")) {
|
|
23623
|
-
if (!frameworks.includes("Spring MVC")) frameworks.push("Spring MVC");
|
|
23624
|
-
}
|
|
23625
|
-
if (artifactId.includes("hibernate")) {
|
|
23626
|
-
if (!frameworks.includes("Hibernate")) frameworks.push("Hibernate");
|
|
23627
|
-
}
|
|
23628
|
-
if (artifactId === "junit-jupiter" || artifactId === "junit") {
|
|
23629
|
-
if (!testingFrameworks.includes("JUnit")) testingFrameworks.push("JUnit");
|
|
23630
|
-
}
|
|
23631
|
-
if (artifactId === "mockito-core") {
|
|
23632
|
-
if (!testingFrameworks.includes("Mockito")) testingFrameworks.push("Mockito");
|
|
23633
|
-
}
|
|
23634
|
-
}
|
|
23635
|
-
return { dependencies, frameworks, buildTools: buildTools2, testingFrameworks };
|
|
23636
|
-
} catch {
|
|
23637
|
-
return { dependencies: {}, frameworks: [], buildTools: ["maven"], testingFrameworks: [] };
|
|
23638
|
-
}
|
|
23639
|
-
}
|
|
23640
|
-
async function parsePyprojectToml(cwd) {
|
|
23641
|
-
const pyprojectPath = path39__default.join(cwd, "pyproject.toml");
|
|
23642
|
-
try {
|
|
23643
|
-
const content = await fs35__default.readFile(pyprojectPath, "utf-8");
|
|
23644
|
-
const dependencies = {};
|
|
23645
|
-
const frameworks = [];
|
|
23646
|
-
const buildTools2 = ["pip"];
|
|
23647
|
-
const testingFrameworks = [];
|
|
23648
|
-
const lines = content.split("\n");
|
|
23649
|
-
for (const line of lines) {
|
|
23650
|
-
const trimmed = line.trim();
|
|
23651
|
-
if (trimmed.match(/^["']?[\w-]+["']?\s*=\s*["'][\^~>=<]+[\d.]+["']/)) {
|
|
23652
|
-
const depMatch = trimmed.match(/^["']?([\w-]+)["']?\s*=\s*["']([\^~>=<]+[\d.]+)["']/);
|
|
23653
|
-
if (depMatch && depMatch[1] && depMatch[2]) {
|
|
23654
|
-
dependencies[depMatch[1]] = depMatch[2];
|
|
23655
|
-
}
|
|
23656
|
-
}
|
|
23657
|
-
if (trimmed.includes("fastapi")) frameworks.push("FastAPI");
|
|
23658
|
-
if (trimmed.includes("django")) frameworks.push("Django");
|
|
23659
|
-
if (trimmed.includes("flask")) frameworks.push("Flask");
|
|
23660
|
-
if (trimmed.includes("pytest")) testingFrameworks.push("pytest");
|
|
23661
|
-
if (trimmed.includes("unittest")) testingFrameworks.push("unittest");
|
|
23662
|
-
}
|
|
23663
|
-
return { dependencies, frameworks, buildTools: buildTools2, testingFrameworks };
|
|
23664
|
-
} catch {
|
|
23665
|
-
return { dependencies: {}, frameworks: [], buildTools: ["pip"], testingFrameworks: [] };
|
|
23666
|
-
}
|
|
23667
|
-
}
|
|
23668
|
-
async function detectProjectStack(cwd) {
|
|
23669
|
-
const stack = await detectStack2(cwd);
|
|
23670
|
-
const packageManager = await detectPackageManager3(cwd, stack);
|
|
23671
|
-
let dependencies = {};
|
|
23672
|
-
let frameworks = [];
|
|
23673
|
-
let buildTools2 = [];
|
|
23674
|
-
let testingFrameworks = [];
|
|
23675
|
-
let languages = [];
|
|
23676
|
-
if (stack === "node") {
|
|
23677
|
-
const parsed = await parsePackageJson(cwd);
|
|
23678
|
-
dependencies = parsed.dependencies;
|
|
23679
|
-
frameworks = parsed.frameworks;
|
|
23680
|
-
buildTools2 = parsed.buildTools;
|
|
23681
|
-
testingFrameworks = parsed.testingFrameworks;
|
|
23682
|
-
languages = parsed.languages;
|
|
23683
|
-
} else if (stack === "java") {
|
|
23684
|
-
const isGradle = await fileExists3(path39__default.join(cwd, "build.gradle")) || await fileExists3(path39__default.join(cwd, "build.gradle.kts"));
|
|
23685
|
-
const parsed = isGradle ? { dependencies: {}, frameworks: [], buildTools: ["gradle"], testingFrameworks: ["JUnit"] } : await parsePomXml(cwd);
|
|
23686
|
-
dependencies = parsed.dependencies;
|
|
23687
|
-
frameworks = parsed.frameworks;
|
|
23688
|
-
buildTools2 = parsed.buildTools;
|
|
23689
|
-
testingFrameworks = parsed.testingFrameworks;
|
|
23690
|
-
languages = ["Java"];
|
|
23691
|
-
} else if (stack === "python") {
|
|
23692
|
-
const parsed = await parsePyprojectToml(cwd);
|
|
23693
|
-
dependencies = parsed.dependencies;
|
|
23694
|
-
frameworks = parsed.frameworks;
|
|
23695
|
-
buildTools2 = parsed.buildTools;
|
|
23696
|
-
testingFrameworks = parsed.testingFrameworks;
|
|
23697
|
-
languages = ["Python"];
|
|
23698
|
-
} else if (stack === "go") {
|
|
23699
|
-
languages = ["Go"];
|
|
23700
|
-
buildTools2 = ["go"];
|
|
23701
|
-
} else if (stack === "rust") {
|
|
23702
|
-
languages = ["Rust"];
|
|
23703
|
-
buildTools2 = ["cargo"];
|
|
23704
|
-
}
|
|
23705
|
-
return {
|
|
23706
|
-
stack,
|
|
23707
|
-
packageManager,
|
|
23708
|
-
dependencies,
|
|
23709
|
-
frameworks,
|
|
23710
|
-
buildTools: buildTools2,
|
|
23711
|
-
testingFrameworks,
|
|
23712
|
-
languages
|
|
23713
|
-
};
|
|
23714
|
-
}
|
|
23715
|
-
var init_stack_detector = __esm({
|
|
23716
|
-
"src/cli/repl/context/stack-detector.ts"() {
|
|
23717
|
-
init_files();
|
|
23718
|
-
}
|
|
23719
|
-
});
|
|
23720
|
-
|
|
23721
23337
|
// src/cli/repl/hooks/types.ts
|
|
23722
23338
|
function isHookEvent(value) {
|
|
23723
23339
|
return typeof value === "string" && HOOK_EVENTS.includes(value);
|
|
@@ -23729,7 +23345,7 @@ function isHookAction(value) {
|
|
|
23729
23345
|
return value === "allow" || value === "deny" || value === "modify";
|
|
23730
23346
|
}
|
|
23731
23347
|
var HOOK_EVENTS;
|
|
23732
|
-
var
|
|
23348
|
+
var init_types7 = __esm({
|
|
23733
23349
|
"src/cli/repl/hooks/types.ts"() {
|
|
23734
23350
|
HOOK_EVENTS = [
|
|
23735
23351
|
"PreToolUse",
|
|
@@ -23748,7 +23364,7 @@ function createHookRegistry() {
|
|
|
23748
23364
|
var HookRegistry;
|
|
23749
23365
|
var init_registry5 = __esm({
|
|
23750
23366
|
"src/cli/repl/hooks/registry.ts"() {
|
|
23751
|
-
|
|
23367
|
+
init_types7();
|
|
23752
23368
|
HookRegistry = class {
|
|
23753
23369
|
/** Hooks indexed by event type for O(1) lookup */
|
|
23754
23370
|
hooksByEvent;
|
|
@@ -24386,12 +24002,419 @@ __export(hooks_exports, {
|
|
|
24386
24002
|
});
|
|
24387
24003
|
var init_hooks = __esm({
|
|
24388
24004
|
"src/cli/repl/hooks/index.ts"() {
|
|
24389
|
-
|
|
24005
|
+
init_types7();
|
|
24390
24006
|
init_registry5();
|
|
24391
24007
|
init_executor();
|
|
24392
24008
|
}
|
|
24393
24009
|
});
|
|
24394
24010
|
|
|
24011
|
+
// src/cli/repl/interruptions/types.ts
|
|
24012
|
+
var init_types8 = __esm({
|
|
24013
|
+
"src/cli/repl/interruptions/types.ts"() {
|
|
24014
|
+
}
|
|
24015
|
+
});
|
|
24016
|
+
|
|
24017
|
+
// src/cli/repl/interruptions/classifier.ts
|
|
24018
|
+
function matchPatterns(text15, patterns) {
|
|
24019
|
+
for (let i = 0; i < patterns.length; i++) {
|
|
24020
|
+
if (patterns[i].test(text15)) {
|
|
24021
|
+
return 1 - i * 0.1;
|
|
24022
|
+
}
|
|
24023
|
+
}
|
|
24024
|
+
return 0;
|
|
24025
|
+
}
|
|
24026
|
+
function classifyInterruption(message) {
|
|
24027
|
+
const text15 = message.text;
|
|
24028
|
+
const abortConf = matchPatterns(text15, ABORT_PATTERNS);
|
|
24029
|
+
const modifyConf = matchPatterns(text15, MODIFY_PATTERNS);
|
|
24030
|
+
const correctConf = matchPatterns(text15, CORRECT_PATTERNS);
|
|
24031
|
+
if (abortConf > 0 && abortConf >= modifyConf && abortConf >= correctConf) {
|
|
24032
|
+
return {
|
|
24033
|
+
text: text15,
|
|
24034
|
+
type: "abort" /* Abort */,
|
|
24035
|
+
confidence: Math.min(1, abortConf),
|
|
24036
|
+
timestamp: message.timestamp
|
|
24037
|
+
};
|
|
24038
|
+
}
|
|
24039
|
+
if (modifyConf > 0 && modifyConf >= correctConf) {
|
|
24040
|
+
return {
|
|
24041
|
+
text: text15,
|
|
24042
|
+
type: "modify" /* Modify */,
|
|
24043
|
+
confidence: Math.min(1, modifyConf),
|
|
24044
|
+
timestamp: message.timestamp
|
|
24045
|
+
};
|
|
24046
|
+
}
|
|
24047
|
+
if (correctConf > 0) {
|
|
24048
|
+
return {
|
|
24049
|
+
text: text15,
|
|
24050
|
+
type: "correct" /* Correct */,
|
|
24051
|
+
confidence: Math.min(1, correctConf),
|
|
24052
|
+
timestamp: message.timestamp
|
|
24053
|
+
};
|
|
24054
|
+
}
|
|
24055
|
+
return {
|
|
24056
|
+
text: text15,
|
|
24057
|
+
type: "info" /* Info */,
|
|
24058
|
+
confidence: 0.5,
|
|
24059
|
+
timestamp: message.timestamp
|
|
24060
|
+
};
|
|
24061
|
+
}
|
|
24062
|
+
var ABORT_PATTERNS, MODIFY_PATTERNS, CORRECT_PATTERNS;
|
|
24063
|
+
var init_classifier = __esm({
|
|
24064
|
+
"src/cli/repl/interruptions/classifier.ts"() {
|
|
24065
|
+
init_types8();
|
|
24066
|
+
ABORT_PATTERNS = [
|
|
24067
|
+
/^(para|stop|cancel|abort|quit|exit|detente|basta)$/i,
|
|
24068
|
+
/^(para\s+ya|stop\s+it|cancel\s+that)$/i,
|
|
24069
|
+
/\b(para|stop|cancel|abort)\b/i
|
|
24070
|
+
];
|
|
24071
|
+
MODIFY_PATTERNS = [
|
|
24072
|
+
/^(a[ñn]ade|add|incluye|include|pon|put|agrega)\b/i,
|
|
24073
|
+
/^(cambia|change|modifica|modify|usa|use|haz|make)\b/i,
|
|
24074
|
+
/^no[,.]?\s+/i,
|
|
24075
|
+
// "no, mejor de la griega" — negation signals redirection
|
|
24076
|
+
/^(prefiero|prefer|quiero|i\s+want)\b/i,
|
|
24077
|
+
// "prefiero en gijon" — preference signals redirection
|
|
24078
|
+
/\b(a[ñn]ade|add|incluye|include)\s+/i,
|
|
24079
|
+
/\b(cambia|change|modifica|modify)\s+/i,
|
|
24080
|
+
/\b(en\s+vez\s+de|instead\s+of|rather\s+than)\b/i,
|
|
24081
|
+
/\b(prefiero|prefer|mejor|better|más|more|less|menos|bigger|smaller|larger)\b/i,
|
|
24082
|
+
/\b(también|also|además|additionally)\b/i
|
|
24083
|
+
];
|
|
24084
|
+
CORRECT_PATTERNS = [
|
|
24085
|
+
/^(error|bug|fallo|wrong|mal|incorrect)\b/i,
|
|
24086
|
+
/^(arregla|fix|corrige|correct|repara|repair)\b/i,
|
|
24087
|
+
/\b(error\s+en|bug\s+in|fallo\s+en)\b/i,
|
|
24088
|
+
/\b(está\s+mal|is\s+wrong|no\s+funciona|doesn'?t\s+work)\b/i,
|
|
24089
|
+
/\b(arregla|fix|corrige|correct)\s+/i
|
|
24090
|
+
];
|
|
24091
|
+
}
|
|
24092
|
+
});
|
|
24093
|
+
function mapClassificationToAction(type) {
|
|
24094
|
+
switch (type) {
|
|
24095
|
+
case "abort" /* Abort */:
|
|
24096
|
+
return "abort" /* Abort */;
|
|
24097
|
+
// Explicit modification/correction keywords → pre-select Modify
|
|
24098
|
+
case "modify" /* Modify */:
|
|
24099
|
+
case "correct" /* Correct */:
|
|
24100
|
+
return "modify" /* Modify */;
|
|
24101
|
+
// Info or unclassified → pre-select Queue (likely a new topic/task)
|
|
24102
|
+
// The user can always switch to Modify via the menu if needed
|
|
24103
|
+
case "info" /* Info */:
|
|
24104
|
+
default:
|
|
24105
|
+
return "queue" /* Queue */;
|
|
24106
|
+
}
|
|
24107
|
+
}
|
|
24108
|
+
var init_action_selector = __esm({
|
|
24109
|
+
"src/cli/repl/input/action-selector.ts"() {
|
|
24110
|
+
init_types8();
|
|
24111
|
+
}
|
|
24112
|
+
});
|
|
24113
|
+
|
|
24114
|
+
// src/cli/repl/interruptions/llm-classifier.ts
|
|
24115
|
+
var llm_classifier_exports = {};
|
|
24116
|
+
__export(llm_classifier_exports, {
|
|
24117
|
+
createLLMClassifier: () => createLLMClassifier
|
|
24118
|
+
});
|
|
24119
|
+
function buildClassificationPrompt(userMessage, currentTask) {
|
|
24120
|
+
if (currentTask) {
|
|
24121
|
+
return `Current task: "${currentTask}"
|
|
24122
|
+
User's new message: "${userMessage}"`;
|
|
24123
|
+
}
|
|
24124
|
+
return `User's new message: "${userMessage}"`;
|
|
24125
|
+
}
|
|
24126
|
+
function parseResponse(response) {
|
|
24127
|
+
const normalized = response.trim().toUpperCase();
|
|
24128
|
+
if (normalized.includes("STEER")) return "steer" /* Steer */;
|
|
24129
|
+
if (normalized.includes("MODIFY")) return "modify" /* Modify */;
|
|
24130
|
+
if (normalized.includes("QUEUE")) return "queue" /* Queue */;
|
|
24131
|
+
if (normalized.includes("ABORT")) return "abort" /* Abort */;
|
|
24132
|
+
return null;
|
|
24133
|
+
}
|
|
24134
|
+
function classifyWithKeywords(message) {
|
|
24135
|
+
const classified = classifyInterruption(message);
|
|
24136
|
+
return mapClassificationToAction(classified.type);
|
|
24137
|
+
}
|
|
24138
|
+
function createLLMClassifier(provider, config) {
|
|
24139
|
+
const cfg = { ...DEFAULT_CONFIG6, ...config };
|
|
24140
|
+
return {
|
|
24141
|
+
/**
|
|
24142
|
+
* Classify a user message captured during agent execution.
|
|
24143
|
+
*
|
|
24144
|
+
* Makes a fast parallel LLM call. If the LLM doesn't respond within
|
|
24145
|
+
* the timeout, falls back to keyword-based classification.
|
|
24146
|
+
*
|
|
24147
|
+
* @param message - The captured message
|
|
24148
|
+
* @param currentTask - The original task the agent is working on (for context)
|
|
24149
|
+
* @returns Classification result with action and source
|
|
24150
|
+
*/
|
|
24151
|
+
async classify(message, currentTask) {
|
|
24152
|
+
const llmPromise = classifyWithLLM(provider, message, currentTask, cfg);
|
|
24153
|
+
let timeoutId;
|
|
24154
|
+
const timeoutPromise = new Promise((resolve4) => {
|
|
24155
|
+
timeoutId = setTimeout(() => resolve4(null), cfg.timeoutMs);
|
|
24156
|
+
});
|
|
24157
|
+
try {
|
|
24158
|
+
const llmResult = await Promise.race([llmPromise, timeoutPromise]);
|
|
24159
|
+
if (llmResult !== null) {
|
|
24160
|
+
return { action: llmResult, source: "llm" };
|
|
24161
|
+
}
|
|
24162
|
+
return {
|
|
24163
|
+
action: classifyWithKeywords(message),
|
|
24164
|
+
source: "keywords"
|
|
24165
|
+
};
|
|
24166
|
+
} finally {
|
|
24167
|
+
clearTimeout(timeoutId);
|
|
24168
|
+
}
|
|
24169
|
+
}
|
|
24170
|
+
};
|
|
24171
|
+
}
|
|
24172
|
+
async function classifyWithLLM(provider, message, currentTask, cfg) {
|
|
24173
|
+
try {
|
|
24174
|
+
const userPrompt = buildClassificationPrompt(message.text, currentTask);
|
|
24175
|
+
const options = {
|
|
24176
|
+
maxTokens: cfg.maxTokens,
|
|
24177
|
+
temperature: cfg.temperature,
|
|
24178
|
+
system: CLASSIFICATION_SYSTEM_PROMPT2,
|
|
24179
|
+
timeout: cfg.timeoutMs
|
|
24180
|
+
};
|
|
24181
|
+
const response = await provider.chat([{ role: "user", content: userPrompt }], options);
|
|
24182
|
+
return parseResponse(response.content);
|
|
24183
|
+
} catch {
|
|
24184
|
+
return null;
|
|
24185
|
+
}
|
|
24186
|
+
}
|
|
24187
|
+
var DEFAULT_CONFIG6, CLASSIFICATION_SYSTEM_PROMPT2;
|
|
24188
|
+
var init_llm_classifier = __esm({
|
|
24189
|
+
"src/cli/repl/interruptions/llm-classifier.ts"() {
|
|
24190
|
+
init_types8();
|
|
24191
|
+
init_classifier();
|
|
24192
|
+
init_action_selector();
|
|
24193
|
+
DEFAULT_CONFIG6 = {
|
|
24194
|
+
timeoutMs: 8e3,
|
|
24195
|
+
maxTokens: 10,
|
|
24196
|
+
temperature: 0
|
|
24197
|
+
};
|
|
24198
|
+
CLASSIFICATION_SYSTEM_PROMPT2 = `You are a message classifier for a coding assistant. The user sent a message while the assistant was working on a task.
|
|
24199
|
+
|
|
24200
|
+
Classify the message into exactly ONE category. Reply with ONLY the category word, nothing else.
|
|
24201
|
+
|
|
24202
|
+
Categories:
|
|
24203
|
+
- STEER: The message provides a hint, preference, or minor adjustment to the CURRENT task without requiring a restart (e.g. "also add a test", "use camelCase", "make sure to handle errors", "btw the file is in src/", "prefer async/await")
|
|
24204
|
+
- MODIFY: The message fundamentally changes the CURRENT task requiring a fresh start (e.g. "actually use Python instead of JS", "no, do it completely differently", "start over with a new approach")
|
|
24205
|
+
- QUEUE: The message is a NEW, DIFFERENT task unrelated to what's being done (e.g. "what's 2+2", "tell me the weather", "create another file for X")
|
|
24206
|
+
- ABORT: The message asks to stop/cancel the current work (e.g. "stop", "cancel", "para", "never mind")
|
|
24207
|
+
|
|
24208
|
+
Reply with exactly one word: STEER, MODIFY, QUEUE, or ABORT`;
|
|
24209
|
+
}
|
|
24210
|
+
});
|
|
24211
|
+
|
|
24212
|
+
// src/cli/repl/context/stack-detector.ts
|
|
24213
|
+
var stack_detector_exports = {};
|
|
24214
|
+
__export(stack_detector_exports, {
|
|
24215
|
+
detectProjectStack: () => detectProjectStack
|
|
24216
|
+
});
|
|
24217
|
+
async function detectStack2(cwd) {
|
|
24218
|
+
if (await fileExists3(path39__default.join(cwd, "package.json"))) return "node";
|
|
24219
|
+
if (await fileExists3(path39__default.join(cwd, "Cargo.toml"))) return "rust";
|
|
24220
|
+
if (await fileExists3(path39__default.join(cwd, "pyproject.toml"))) return "python";
|
|
24221
|
+
if (await fileExists3(path39__default.join(cwd, "go.mod"))) return "go";
|
|
24222
|
+
if (await fileExists3(path39__default.join(cwd, "pom.xml"))) return "java";
|
|
24223
|
+
if (await fileExists3(path39__default.join(cwd, "build.gradle"))) return "java";
|
|
24224
|
+
if (await fileExists3(path39__default.join(cwd, "build.gradle.kts"))) return "java";
|
|
24225
|
+
return "unknown";
|
|
24226
|
+
}
|
|
24227
|
+
async function detectPackageManager3(cwd, stack) {
|
|
24228
|
+
if (stack === "rust") return "cargo";
|
|
24229
|
+
if (stack === "python") return "pip";
|
|
24230
|
+
if (stack === "go") return "go";
|
|
24231
|
+
if (stack === "java") {
|
|
24232
|
+
if (await fileExists3(path39__default.join(cwd, "build.gradle")) || await fileExists3(path39__default.join(cwd, "build.gradle.kts"))) {
|
|
24233
|
+
return "gradle";
|
|
24234
|
+
}
|
|
24235
|
+
if (await fileExists3(path39__default.join(cwd, "pom.xml"))) {
|
|
24236
|
+
return "maven";
|
|
24237
|
+
}
|
|
24238
|
+
}
|
|
24239
|
+
if (stack === "node") {
|
|
24240
|
+
if (await fileExists3(path39__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
24241
|
+
if (await fileExists3(path39__default.join(cwd, "yarn.lock"))) return "yarn";
|
|
24242
|
+
if (await fileExists3(path39__default.join(cwd, "bun.lockb"))) return "bun";
|
|
24243
|
+
return "npm";
|
|
24244
|
+
}
|
|
24245
|
+
return null;
|
|
24246
|
+
}
|
|
24247
|
+
async function parsePackageJson(cwd) {
|
|
24248
|
+
const packageJsonPath = path39__default.join(cwd, "package.json");
|
|
24249
|
+
try {
|
|
24250
|
+
const content = await fs35__default.readFile(packageJsonPath, "utf-8");
|
|
24251
|
+
const pkg = JSON.parse(content);
|
|
24252
|
+
const allDeps = {
|
|
24253
|
+
...pkg.dependencies,
|
|
24254
|
+
...pkg.devDependencies
|
|
24255
|
+
};
|
|
24256
|
+
const frameworks = [];
|
|
24257
|
+
if (allDeps.react) frameworks.push("React");
|
|
24258
|
+
if (allDeps.vue) frameworks.push("Vue");
|
|
24259
|
+
if (allDeps["@angular/core"]) frameworks.push("Angular");
|
|
24260
|
+
if (allDeps.next) frameworks.push("Next.js");
|
|
24261
|
+
if (allDeps.nuxt) frameworks.push("Nuxt");
|
|
24262
|
+
if (allDeps.express) frameworks.push("Express");
|
|
24263
|
+
if (allDeps.fastify) frameworks.push("Fastify");
|
|
24264
|
+
if (allDeps.nestjs || allDeps["@nestjs/core"]) frameworks.push("NestJS");
|
|
24265
|
+
const buildTools2 = [];
|
|
24266
|
+
if (allDeps.webpack) buildTools2.push("webpack");
|
|
24267
|
+
if (allDeps.vite) buildTools2.push("vite");
|
|
24268
|
+
if (allDeps.rollup) buildTools2.push("rollup");
|
|
24269
|
+
if (allDeps.tsup) buildTools2.push("tsup");
|
|
24270
|
+
if (allDeps.esbuild) buildTools2.push("esbuild");
|
|
24271
|
+
if (pkg.scripts?.build) buildTools2.push("build");
|
|
24272
|
+
const testingFrameworks = [];
|
|
24273
|
+
if (allDeps.vitest) testingFrameworks.push("vitest");
|
|
24274
|
+
if (allDeps.jest) testingFrameworks.push("jest");
|
|
24275
|
+
if (allDeps.mocha) testingFrameworks.push("mocha");
|
|
24276
|
+
if (allDeps.chai) testingFrameworks.push("chai");
|
|
24277
|
+
if (allDeps["@playwright/test"]) testingFrameworks.push("playwright");
|
|
24278
|
+
if (allDeps.cypress) testingFrameworks.push("cypress");
|
|
24279
|
+
const languages = ["JavaScript"];
|
|
24280
|
+
if (allDeps.typescript || await fileExists3(path39__default.join(cwd, "tsconfig.json"))) {
|
|
24281
|
+
languages.push("TypeScript");
|
|
24282
|
+
}
|
|
24283
|
+
return {
|
|
24284
|
+
dependencies: allDeps,
|
|
24285
|
+
frameworks,
|
|
24286
|
+
buildTools: buildTools2,
|
|
24287
|
+
testingFrameworks,
|
|
24288
|
+
languages
|
|
24289
|
+
};
|
|
24290
|
+
} catch {
|
|
24291
|
+
return {
|
|
24292
|
+
dependencies: {},
|
|
24293
|
+
frameworks: [],
|
|
24294
|
+
buildTools: [],
|
|
24295
|
+
testingFrameworks: [],
|
|
24296
|
+
languages: []
|
|
24297
|
+
};
|
|
24298
|
+
}
|
|
24299
|
+
}
|
|
24300
|
+
async function parsePomXml(cwd) {
|
|
24301
|
+
const pomPath = path39__default.join(cwd, "pom.xml");
|
|
24302
|
+
try {
|
|
24303
|
+
const content = await fs35__default.readFile(pomPath, "utf-8");
|
|
24304
|
+
const dependencies = {};
|
|
24305
|
+
const frameworks = [];
|
|
24306
|
+
const buildTools2 = ["maven"];
|
|
24307
|
+
const testingFrameworks = [];
|
|
24308
|
+
const depRegex = /<groupId>([^<]+)<\/groupId>\s*<artifactId>([^<]+)<\/artifactId>/g;
|
|
24309
|
+
let match;
|
|
24310
|
+
while ((match = depRegex.exec(content)) !== null) {
|
|
24311
|
+
const groupId = match[1];
|
|
24312
|
+
const artifactId = match[2];
|
|
24313
|
+
if (!groupId || !artifactId) continue;
|
|
24314
|
+
const fullName = `${groupId}:${artifactId}`;
|
|
24315
|
+
dependencies[fullName] = "unknown";
|
|
24316
|
+
if (artifactId.includes("spring-boot")) {
|
|
24317
|
+
if (!frameworks.includes("Spring Boot")) frameworks.push("Spring Boot");
|
|
24318
|
+
}
|
|
24319
|
+
if (artifactId.includes("spring-webmvc") || artifactId.includes("spring-web")) {
|
|
24320
|
+
if (!frameworks.includes("Spring MVC")) frameworks.push("Spring MVC");
|
|
24321
|
+
}
|
|
24322
|
+
if (artifactId.includes("hibernate")) {
|
|
24323
|
+
if (!frameworks.includes("Hibernate")) frameworks.push("Hibernate");
|
|
24324
|
+
}
|
|
24325
|
+
if (artifactId === "junit-jupiter" || artifactId === "junit") {
|
|
24326
|
+
if (!testingFrameworks.includes("JUnit")) testingFrameworks.push("JUnit");
|
|
24327
|
+
}
|
|
24328
|
+
if (artifactId === "mockito-core") {
|
|
24329
|
+
if (!testingFrameworks.includes("Mockito")) testingFrameworks.push("Mockito");
|
|
24330
|
+
}
|
|
24331
|
+
}
|
|
24332
|
+
return { dependencies, frameworks, buildTools: buildTools2, testingFrameworks };
|
|
24333
|
+
} catch {
|
|
24334
|
+
return { dependencies: {}, frameworks: [], buildTools: ["maven"], testingFrameworks: [] };
|
|
24335
|
+
}
|
|
24336
|
+
}
|
|
24337
|
+
async function parsePyprojectToml(cwd) {
|
|
24338
|
+
const pyprojectPath = path39__default.join(cwd, "pyproject.toml");
|
|
24339
|
+
try {
|
|
24340
|
+
const content = await fs35__default.readFile(pyprojectPath, "utf-8");
|
|
24341
|
+
const dependencies = {};
|
|
24342
|
+
const frameworks = [];
|
|
24343
|
+
const buildTools2 = ["pip"];
|
|
24344
|
+
const testingFrameworks = [];
|
|
24345
|
+
const lines = content.split("\n");
|
|
24346
|
+
for (const line of lines) {
|
|
24347
|
+
const trimmed = line.trim();
|
|
24348
|
+
if (trimmed.match(/^["']?[\w-]+["']?\s*=\s*["'][\^~>=<]+[\d.]+["']/)) {
|
|
24349
|
+
const depMatch = trimmed.match(/^["']?([\w-]+)["']?\s*=\s*["']([\^~>=<]+[\d.]+)["']/);
|
|
24350
|
+
if (depMatch && depMatch[1] && depMatch[2]) {
|
|
24351
|
+
dependencies[depMatch[1]] = depMatch[2];
|
|
24352
|
+
}
|
|
24353
|
+
}
|
|
24354
|
+
if (trimmed.includes("fastapi")) frameworks.push("FastAPI");
|
|
24355
|
+
if (trimmed.includes("django")) frameworks.push("Django");
|
|
24356
|
+
if (trimmed.includes("flask")) frameworks.push("Flask");
|
|
24357
|
+
if (trimmed.includes("pytest")) testingFrameworks.push("pytest");
|
|
24358
|
+
if (trimmed.includes("unittest")) testingFrameworks.push("unittest");
|
|
24359
|
+
}
|
|
24360
|
+
return { dependencies, frameworks, buildTools: buildTools2, testingFrameworks };
|
|
24361
|
+
} catch {
|
|
24362
|
+
return { dependencies: {}, frameworks: [], buildTools: ["pip"], testingFrameworks: [] };
|
|
24363
|
+
}
|
|
24364
|
+
}
|
|
24365
|
+
async function detectProjectStack(cwd) {
|
|
24366
|
+
const stack = await detectStack2(cwd);
|
|
24367
|
+
const packageManager = await detectPackageManager3(cwd, stack);
|
|
24368
|
+
let dependencies = {};
|
|
24369
|
+
let frameworks = [];
|
|
24370
|
+
let buildTools2 = [];
|
|
24371
|
+
let testingFrameworks = [];
|
|
24372
|
+
let languages = [];
|
|
24373
|
+
if (stack === "node") {
|
|
24374
|
+
const parsed = await parsePackageJson(cwd);
|
|
24375
|
+
dependencies = parsed.dependencies;
|
|
24376
|
+
frameworks = parsed.frameworks;
|
|
24377
|
+
buildTools2 = parsed.buildTools;
|
|
24378
|
+
testingFrameworks = parsed.testingFrameworks;
|
|
24379
|
+
languages = parsed.languages;
|
|
24380
|
+
} else if (stack === "java") {
|
|
24381
|
+
const isGradle = await fileExists3(path39__default.join(cwd, "build.gradle")) || await fileExists3(path39__default.join(cwd, "build.gradle.kts"));
|
|
24382
|
+
const parsed = isGradle ? { dependencies: {}, frameworks: [], buildTools: ["gradle"], testingFrameworks: ["JUnit"] } : await parsePomXml(cwd);
|
|
24383
|
+
dependencies = parsed.dependencies;
|
|
24384
|
+
frameworks = parsed.frameworks;
|
|
24385
|
+
buildTools2 = parsed.buildTools;
|
|
24386
|
+
testingFrameworks = parsed.testingFrameworks;
|
|
24387
|
+
languages = ["Java"];
|
|
24388
|
+
} else if (stack === "python") {
|
|
24389
|
+
const parsed = await parsePyprojectToml(cwd);
|
|
24390
|
+
dependencies = parsed.dependencies;
|
|
24391
|
+
frameworks = parsed.frameworks;
|
|
24392
|
+
buildTools2 = parsed.buildTools;
|
|
24393
|
+
testingFrameworks = parsed.testingFrameworks;
|
|
24394
|
+
languages = ["Python"];
|
|
24395
|
+
} else if (stack === "go") {
|
|
24396
|
+
languages = ["Go"];
|
|
24397
|
+
buildTools2 = ["go"];
|
|
24398
|
+
} else if (stack === "rust") {
|
|
24399
|
+
languages = ["Rust"];
|
|
24400
|
+
buildTools2 = ["cargo"];
|
|
24401
|
+
}
|
|
24402
|
+
return {
|
|
24403
|
+
stack,
|
|
24404
|
+
packageManager,
|
|
24405
|
+
dependencies,
|
|
24406
|
+
frameworks,
|
|
24407
|
+
buildTools: buildTools2,
|
|
24408
|
+
testingFrameworks,
|
|
24409
|
+
languages
|
|
24410
|
+
};
|
|
24411
|
+
}
|
|
24412
|
+
var init_stack_detector = __esm({
|
|
24413
|
+
"src/cli/repl/context/stack-detector.ts"() {
|
|
24414
|
+
init_files();
|
|
24415
|
+
}
|
|
24416
|
+
});
|
|
24417
|
+
|
|
24395
24418
|
// src/cli/repl/input/message-queue.ts
|
|
24396
24419
|
function createMessageQueue(maxSize = 50) {
|
|
24397
24420
|
let messages = [];
|
|
@@ -24769,7 +24792,7 @@ function createFeedbackSystem(getSpinner, config) {
|
|
|
24769
24792
|
var DEFAULT_FEEDBACK_CONFIG;
|
|
24770
24793
|
var init_feedback_system = __esm({
|
|
24771
24794
|
"src/cli/repl/feedback/feedback-system.ts"() {
|
|
24772
|
-
|
|
24795
|
+
init_types8();
|
|
24773
24796
|
DEFAULT_FEEDBACK_CONFIG = {
|
|
24774
24797
|
method: "spinner",
|
|
24775
24798
|
displayDurationMs: 2e3,
|
|
@@ -25045,8 +25068,8 @@ Generated by Corbat-Coco v0.1.0
|
|
|
25045
25068
|
|
|
25046
25069
|
// src/cli/commands/init.ts
|
|
25047
25070
|
function registerInitCommand(program2) {
|
|
25048
|
-
program2.command("init").description("Initialize a new Corbat-Coco project").argument("[path]", "Project directory path", ".").option("-t, --template <template>", "Project template to use").option("-y, --yes", "Skip prompts and use defaults").option("--skip-discovery", "Skip the discovery phase (use existing spec)").action(async (
|
|
25049
|
-
await runInit(
|
|
25071
|
+
program2.command("init").description("Initialize a new Corbat-Coco project").argument("[path]", "Project directory path", ".").option("-t, --template <template>", "Project template to use").option("-y, --yes", "Skip prompts and use defaults").option("--skip-discovery", "Skip the discovery phase (use existing spec)").action(async (path62, options) => {
|
|
25072
|
+
await runInit(path62, options);
|
|
25050
25073
|
});
|
|
25051
25074
|
}
|
|
25052
25075
|
async function runInit(projectPath, options) {
|
|
@@ -25125,18 +25148,18 @@ async function gatherProjectInfo() {
|
|
|
25125
25148
|
language
|
|
25126
25149
|
};
|
|
25127
25150
|
}
|
|
25128
|
-
function getDefaultProjectInfo(
|
|
25129
|
-
const name =
|
|
25151
|
+
function getDefaultProjectInfo(path62) {
|
|
25152
|
+
const name = path62 === "." ? "my-project" : path62.split("/").pop() || "my-project";
|
|
25130
25153
|
return {
|
|
25131
25154
|
name,
|
|
25132
25155
|
description: "",
|
|
25133
25156
|
language: "typescript"
|
|
25134
25157
|
};
|
|
25135
25158
|
}
|
|
25136
|
-
async function checkExistingProject(
|
|
25159
|
+
async function checkExistingProject(path62) {
|
|
25137
25160
|
try {
|
|
25138
25161
|
const fs56 = await import('fs/promises');
|
|
25139
|
-
await fs56.access(`${
|
|
25162
|
+
await fs56.access(`${path62}/.coco`);
|
|
25140
25163
|
return true;
|
|
25141
25164
|
} catch {
|
|
25142
25165
|
return false;
|
|
@@ -28637,20 +28660,20 @@ async function createCliPhaseContext(projectPath, _onUserInput) {
|
|
|
28637
28660
|
},
|
|
28638
28661
|
tools: {
|
|
28639
28662
|
file: {
|
|
28640
|
-
async read(
|
|
28663
|
+
async read(path62) {
|
|
28641
28664
|
const fs56 = await import('fs/promises');
|
|
28642
|
-
return fs56.readFile(
|
|
28665
|
+
return fs56.readFile(path62, "utf-8");
|
|
28643
28666
|
},
|
|
28644
|
-
async write(
|
|
28667
|
+
async write(path62, content) {
|
|
28645
28668
|
const fs56 = await import('fs/promises');
|
|
28646
28669
|
const nodePath = await import('path');
|
|
28647
|
-
await fs56.mkdir(nodePath.dirname(
|
|
28648
|
-
await fs56.writeFile(
|
|
28670
|
+
await fs56.mkdir(nodePath.dirname(path62), { recursive: true });
|
|
28671
|
+
await fs56.writeFile(path62, content, "utf-8");
|
|
28649
28672
|
},
|
|
28650
|
-
async exists(
|
|
28673
|
+
async exists(path62) {
|
|
28651
28674
|
const fs56 = await import('fs/promises');
|
|
28652
28675
|
try {
|
|
28653
|
-
await fs56.access(
|
|
28676
|
+
await fs56.access(path62);
|
|
28654
28677
|
return true;
|
|
28655
28678
|
} catch {
|
|
28656
28679
|
return false;
|
|
@@ -29006,10 +29029,10 @@ function getPhaseStatusForPhase(phase) {
|
|
|
29006
29029
|
}
|
|
29007
29030
|
async function loadProjectState(cwd, config) {
|
|
29008
29031
|
const fs56 = await import('fs/promises');
|
|
29009
|
-
const
|
|
29010
|
-
const statePath =
|
|
29011
|
-
const backlogPath =
|
|
29012
|
-
const checkpointDir =
|
|
29032
|
+
const path62 = await import('path');
|
|
29033
|
+
const statePath = path62.join(cwd, ".coco", "state.json");
|
|
29034
|
+
const backlogPath = path62.join(cwd, ".coco", "planning", "backlog.json");
|
|
29035
|
+
const checkpointDir = path62.join(cwd, ".coco", "checkpoints");
|
|
29013
29036
|
let currentPhase = "idle";
|
|
29014
29037
|
let metrics;
|
|
29015
29038
|
let sprint;
|
|
@@ -30352,7 +30375,7 @@ function getProviderDefinition(type) {
|
|
|
30352
30375
|
return PROVIDER_DEFINITIONS[type];
|
|
30353
30376
|
}
|
|
30354
30377
|
function getAllProviders() {
|
|
30355
|
-
return Object.values(PROVIDER_DEFINITIONS).filter((
|
|
30378
|
+
return Object.values(PROVIDER_DEFINITIONS).filter((p47) => !p47.internal);
|
|
30356
30379
|
}
|
|
30357
30380
|
function getRecommendedModel(type) {
|
|
30358
30381
|
const provider = PROVIDER_DEFINITIONS[type];
|
|
@@ -30373,20 +30396,20 @@ function hasLocalProviderConfig(type) {
|
|
|
30373
30396
|
return process.env["COCO_PROVIDER"] === "ollama" || !!process.env["OLLAMA_MODEL"] || !!process.env["OLLAMA_BASE_URL"];
|
|
30374
30397
|
}
|
|
30375
30398
|
function getConfiguredProviders() {
|
|
30376
|
-
return getAllProviders().filter((
|
|
30377
|
-
if (
|
|
30399
|
+
return getAllProviders().filter((p47) => {
|
|
30400
|
+
if (p47.id === "copilot") {
|
|
30378
30401
|
return !!process.env["GITHUB_TOKEN"] || !!process.env["GH_TOKEN"] || hasCopilotCredentials();
|
|
30379
30402
|
}
|
|
30380
|
-
if (
|
|
30381
|
-
return !!process.env[
|
|
30403
|
+
if (p47.id === "openai") {
|
|
30404
|
+
return !!process.env[p47.envVar] || !!process.env["OPENAI_CODEX_TOKEN"] || !!process.env["OPENAI_ACCESS_TOKEN"];
|
|
30382
30405
|
}
|
|
30383
|
-
if (
|
|
30384
|
-
return hasLocalProviderConfig(
|
|
30406
|
+
if (p47.id === "lmstudio" || p47.id === "ollama") {
|
|
30407
|
+
return hasLocalProviderConfig(p47.id);
|
|
30385
30408
|
}
|
|
30386
|
-
if (
|
|
30409
|
+
if (p47.id === "vertex") {
|
|
30387
30410
|
return !!(process.env["VERTEX_API_KEY"] ?? process.env["GOOGLE_API_KEY"] ?? process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"]);
|
|
30388
30411
|
}
|
|
30389
|
-
return !!process.env[
|
|
30412
|
+
return !!process.env[p47.envVar];
|
|
30390
30413
|
});
|
|
30391
30414
|
}
|
|
30392
30415
|
function isProviderConfigured(type) {
|
|
@@ -30554,8 +30577,8 @@ async function saveConfig2(config) {
|
|
|
30554
30577
|
await fs56.mkdir(dir, { recursive: true });
|
|
30555
30578
|
await fs56.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
30556
30579
|
}
|
|
30557
|
-
function getNestedValue(obj,
|
|
30558
|
-
const keys =
|
|
30580
|
+
function getNestedValue(obj, path62) {
|
|
30581
|
+
const keys = path62.split(".");
|
|
30559
30582
|
let current = obj;
|
|
30560
30583
|
for (const key of keys) {
|
|
30561
30584
|
if (current === null || current === void 0 || typeof current !== "object") {
|
|
@@ -30565,8 +30588,8 @@ function getNestedValue(obj, path60) {
|
|
|
30565
30588
|
}
|
|
30566
30589
|
return current;
|
|
30567
30590
|
}
|
|
30568
|
-
function setNestedValue(obj,
|
|
30569
|
-
const keys =
|
|
30591
|
+
function setNestedValue(obj, path62, value) {
|
|
30592
|
+
const keys = path62.split(".");
|
|
30570
30593
|
let current = obj;
|
|
30571
30594
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
30572
30595
|
const key = keys[i];
|
|
@@ -31598,9 +31621,9 @@ function registerCheckCommand(program2) {
|
|
|
31598
31621
|
// src/swarm/spec-parser.ts
|
|
31599
31622
|
async function parseSwarmSpec(filePath) {
|
|
31600
31623
|
const fs56 = await import('fs/promises');
|
|
31601
|
-
const
|
|
31624
|
+
const path62 = await import('path');
|
|
31602
31625
|
const rawContent = await fs56.readFile(filePath, "utf-8");
|
|
31603
|
-
const ext =
|
|
31626
|
+
const ext = path62.extname(filePath).toLowerCase();
|
|
31604
31627
|
if (ext === ".yaml" || ext === ".yml") {
|
|
31605
31628
|
return parseYamlSpec(rawContent);
|
|
31606
31629
|
}
|
|
@@ -31930,8 +31953,8 @@ var DEFAULT_AGENT_CONFIG = {
|
|
|
31930
31953
|
};
|
|
31931
31954
|
async function loadAgentConfig(projectPath) {
|
|
31932
31955
|
const fs56 = await import('fs/promises');
|
|
31933
|
-
const
|
|
31934
|
-
const configPath =
|
|
31956
|
+
const path62 = await import('path');
|
|
31957
|
+
const configPath = path62.join(projectPath, ".coco", "swarm", "agents.json");
|
|
31935
31958
|
try {
|
|
31936
31959
|
const raw = await fs56.readFile(configPath, "utf-8");
|
|
31937
31960
|
const parsed = JSON.parse(raw);
|
|
@@ -32122,16 +32145,16 @@ async function createBoard(projectPath, spec) {
|
|
|
32122
32145
|
}
|
|
32123
32146
|
async function loadBoard(projectPath) {
|
|
32124
32147
|
const fs56 = await import('fs/promises');
|
|
32125
|
-
const
|
|
32126
|
-
const boardPath =
|
|
32148
|
+
const path62 = await import('path');
|
|
32149
|
+
const boardPath = path62.join(projectPath, ".coco", "swarm", "task-board.json");
|
|
32127
32150
|
const raw = await fs56.readFile(boardPath, "utf-8");
|
|
32128
32151
|
return JSON.parse(raw);
|
|
32129
32152
|
}
|
|
32130
32153
|
async function saveBoard(projectPath, board) {
|
|
32131
32154
|
const fs56 = await import('fs/promises');
|
|
32132
|
-
const
|
|
32133
|
-
const boardDir =
|
|
32134
|
-
const boardPath =
|
|
32155
|
+
const path62 = await import('path');
|
|
32156
|
+
const boardDir = path62.join(projectPath, ".coco", "swarm");
|
|
32157
|
+
const boardPath = path62.join(boardDir, "task-board.json");
|
|
32135
32158
|
await fs56.mkdir(boardDir, { recursive: true });
|
|
32136
32159
|
await fs56.writeFile(boardPath, JSON.stringify(board, null, 2), "utf-8");
|
|
32137
32160
|
}
|
|
@@ -32273,25 +32296,25 @@ Rules:
|
|
|
32273
32296
|
}
|
|
32274
32297
|
}
|
|
32275
32298
|
async function defaultPromptHandler(q) {
|
|
32276
|
-
const
|
|
32299
|
+
const p47 = await import('@clack/prompts');
|
|
32277
32300
|
if (q.options && q.options.length > 0) {
|
|
32278
|
-
const result = await
|
|
32301
|
+
const result = await p47.select({
|
|
32279
32302
|
message: q.question,
|
|
32280
32303
|
options: [
|
|
32281
32304
|
...q.options.map((o) => ({ value: o, label: o })),
|
|
32282
32305
|
{ value: q.assumedAnswer, label: `${q.assumedAnswer} (default)` }
|
|
32283
32306
|
]
|
|
32284
32307
|
});
|
|
32285
|
-
if (
|
|
32308
|
+
if (p47.isCancel(result)) {
|
|
32286
32309
|
return q.assumedAnswer;
|
|
32287
32310
|
}
|
|
32288
32311
|
return result;
|
|
32289
32312
|
} else {
|
|
32290
|
-
const result = await
|
|
32313
|
+
const result = await p47.text({
|
|
32291
32314
|
message: q.question,
|
|
32292
32315
|
placeholder: q.assumedAnswer
|
|
32293
32316
|
});
|
|
32294
|
-
if (
|
|
32317
|
+
if (p47.isCancel(result) || !result) {
|
|
32295
32318
|
return q.assumedAnswer;
|
|
32296
32319
|
}
|
|
32297
32320
|
return result;
|
|
@@ -32299,9 +32322,9 @@ async function defaultPromptHandler(q) {
|
|
|
32299
32322
|
}
|
|
32300
32323
|
async function writeAssumptionsFile(projectPath, projectName, questions, assumptions) {
|
|
32301
32324
|
const fs56 = await import('fs/promises');
|
|
32302
|
-
const
|
|
32303
|
-
const swarmDir =
|
|
32304
|
-
const assumptionsPath =
|
|
32325
|
+
const path62 = await import('path');
|
|
32326
|
+
const swarmDir = path62.join(projectPath, ".coco", "swarm");
|
|
32327
|
+
const assumptionsPath = path62.join(swarmDir, "assumptions.md");
|
|
32305
32328
|
await fs56.mkdir(swarmDir, { recursive: true });
|
|
32306
32329
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
32307
32330
|
const content = [
|
|
@@ -32325,9 +32348,9 @@ async function writeAssumptionsFile(projectPath, projectName, questions, assumpt
|
|
|
32325
32348
|
// src/swarm/events.ts
|
|
32326
32349
|
async function appendSwarmEvent(projectPath, event) {
|
|
32327
32350
|
const fs56 = await import('fs/promises');
|
|
32328
|
-
const
|
|
32329
|
-
const eventsDir =
|
|
32330
|
-
const eventsFile =
|
|
32351
|
+
const path62 = await import('path');
|
|
32352
|
+
const eventsDir = path62.join(projectPath, ".coco", "swarm");
|
|
32353
|
+
const eventsFile = path62.join(eventsDir, "events.jsonl");
|
|
32331
32354
|
await fs56.mkdir(eventsDir, { recursive: true });
|
|
32332
32355
|
await fs56.appendFile(eventsFile, JSON.stringify(event) + "\n", "utf-8");
|
|
32333
32356
|
}
|
|
@@ -32338,9 +32361,9 @@ function createEventId() {
|
|
|
32338
32361
|
// src/swarm/knowledge.ts
|
|
32339
32362
|
async function appendKnowledge(projectPath, entry) {
|
|
32340
32363
|
const fs56 = await import('fs/promises');
|
|
32341
|
-
const
|
|
32342
|
-
const knowledgeDir =
|
|
32343
|
-
const knowledgeFile =
|
|
32364
|
+
const path62 = await import('path');
|
|
32365
|
+
const knowledgeDir = path62.join(projectPath, ".coco", "swarm");
|
|
32366
|
+
const knowledgeFile = path62.join(knowledgeDir, "knowledge.jsonl");
|
|
32344
32367
|
await fs56.mkdir(knowledgeDir, { recursive: true });
|
|
32345
32368
|
await fs56.appendFile(knowledgeFile, JSON.stringify(entry) + "\n", "utf-8");
|
|
32346
32369
|
}
|
|
@@ -32647,10 +32670,10 @@ async function runSwarmLifecycle(options) {
|
|
|
32647
32670
|
async function stageInit(ctx) {
|
|
32648
32671
|
const { projectPath, spec } = ctx.options;
|
|
32649
32672
|
const fs56 = await import('fs/promises');
|
|
32650
|
-
const
|
|
32651
|
-
await fs56.mkdir(
|
|
32673
|
+
const path62 = await import('path');
|
|
32674
|
+
await fs56.mkdir(path62.join(projectPath, ".coco", "swarm"), { recursive: true });
|
|
32652
32675
|
await fs56.mkdir(ctx.options.outputPath, { recursive: true });
|
|
32653
|
-
const specSummaryPath =
|
|
32676
|
+
const specSummaryPath = path62.join(projectPath, ".coco", "swarm", "spec-summary.json");
|
|
32654
32677
|
const specSummary = {
|
|
32655
32678
|
projectName: spec.projectName,
|
|
32656
32679
|
description: spec.description,
|
|
@@ -32722,8 +32745,8 @@ async function stagePlan(ctx) {
|
|
|
32722
32745
|
]);
|
|
32723
32746
|
await createBoard(projectPath, spec);
|
|
32724
32747
|
const fs56 = await import('fs/promises');
|
|
32725
|
-
const
|
|
32726
|
-
const planPath =
|
|
32748
|
+
const path62 = await import('path');
|
|
32749
|
+
const planPath = path62.join(projectPath, ".coco", "swarm", "plan.json");
|
|
32727
32750
|
await fs56.writeFile(
|
|
32728
32751
|
planPath,
|
|
32729
32752
|
JSON.stringify({ pm: pmResult, architect: archResult, bestPractices: bpResult }, null, 2),
|
|
@@ -32940,7 +32963,7 @@ async function stageIntegrate(ctx) {
|
|
|
32940
32963
|
async function stageOutput(ctx) {
|
|
32941
32964
|
const { projectPath, outputPath } = ctx.options;
|
|
32942
32965
|
const fs56 = await import('fs/promises');
|
|
32943
|
-
const
|
|
32966
|
+
const path62 = await import('path');
|
|
32944
32967
|
const board = await loadBoard(projectPath);
|
|
32945
32968
|
const featureResults = Array.from(ctx.featureResults.values());
|
|
32946
32969
|
const summary = {
|
|
@@ -32955,7 +32978,7 @@ async function stageOutput(ctx) {
|
|
|
32955
32978
|
globalScore: computeGlobalScore(featureResults)
|
|
32956
32979
|
};
|
|
32957
32980
|
await fs56.mkdir(outputPath, { recursive: true });
|
|
32958
|
-
const summaryPath =
|
|
32981
|
+
const summaryPath = path62.join(outputPath, "swarm-summary.json");
|
|
32959
32982
|
await fs56.writeFile(summaryPath, JSON.stringify(summary, null, 2), "utf-8");
|
|
32960
32983
|
const passed = summary.globalScore >= ctx.options.minScore;
|
|
32961
32984
|
await emitGate(projectPath, "global-score", passed, `Global score: ${summary.globalScore}`);
|
|
@@ -33302,8 +33325,8 @@ var SwarmOrchestrator = class {
|
|
|
33302
33325
|
noQuestions = false,
|
|
33303
33326
|
onProgress
|
|
33304
33327
|
} = options;
|
|
33305
|
-
const
|
|
33306
|
-
const projectPath =
|
|
33328
|
+
const path62 = await import('path');
|
|
33329
|
+
const projectPath = path62.dirname(path62.resolve(specFile));
|
|
33307
33330
|
onProgress?.("init", `Parsing spec file: ${specFile}`);
|
|
33308
33331
|
const spec = await parseSwarmSpec(specFile);
|
|
33309
33332
|
onProgress?.("init", `Initializing provider: ${providerType}`);
|
|
@@ -33314,7 +33337,7 @@ var SwarmOrchestrator = class {
|
|
|
33314
33337
|
await runSwarmLifecycle({
|
|
33315
33338
|
spec,
|
|
33316
33339
|
projectPath,
|
|
33317
|
-
outputPath:
|
|
33340
|
+
outputPath: path62.resolve(outputPath),
|
|
33318
33341
|
provider,
|
|
33319
33342
|
agentConfig,
|
|
33320
33343
|
minScore,
|
|
@@ -33446,6 +33469,7 @@ var helpCommand = {
|
|
|
33446
33469
|
commands: [
|
|
33447
33470
|
{ cmd: "/model, /m", desc: "View or change the current model" },
|
|
33448
33471
|
{ cmd: "/provider", desc: "View or change the LLM provider" },
|
|
33472
|
+
{ cmd: "/doctor, /dr", desc: "Run local diagnostics for config, auth, hooks, and tools" },
|
|
33449
33473
|
{ cmd: "/compact", desc: "Toggle compact mode (less verbose)" },
|
|
33450
33474
|
{ cmd: "/cost, /tokens", desc: "Show token usage and cost" },
|
|
33451
33475
|
{ cmd: "/trust", desc: "Manage project trust permissions" },
|
|
@@ -34256,7 +34280,7 @@ async function runOnboardingV2() {
|
|
|
34256
34280
|
console.log(chalk.magenta(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
|
|
34257
34281
|
console.log();
|
|
34258
34282
|
p26.log.info(
|
|
34259
|
-
`Found ${configuredProviders.length} configured provider(s): ${configuredProviders.map((
|
|
34283
|
+
`Found ${configuredProviders.length} configured provider(s): ${configuredProviders.map((p47) => p47.emoji + " " + p47.name).join(", ")}`
|
|
34260
34284
|
);
|
|
34261
34285
|
const useExisting = await p26.confirm({
|
|
34262
34286
|
message: "Use an existing provider?",
|
|
@@ -35008,9 +35032,9 @@ async function setupOllamaProvider(port) {
|
|
|
35008
35032
|
return setupLocalProvider("ollama", port);
|
|
35009
35033
|
}
|
|
35010
35034
|
async function selectExistingProvider(providers) {
|
|
35011
|
-
const options = providers.map((
|
|
35012
|
-
value:
|
|
35013
|
-
label: `${
|
|
35035
|
+
const options = providers.map((p47) => ({
|
|
35036
|
+
value: p47.id,
|
|
35037
|
+
label: `${p47.emoji} ${p47.name}`,
|
|
35014
35038
|
hint: "Configured"
|
|
35015
35039
|
}));
|
|
35016
35040
|
options.push({ value: "__new__", label: "\u2795 Setup new provider", hint: "" });
|
|
@@ -35402,7 +35426,7 @@ async function ensureConfiguredV2(config) {
|
|
|
35402
35426
|
} catch {
|
|
35403
35427
|
}
|
|
35404
35428
|
}
|
|
35405
|
-
const preferredProviderDef = providers.find((
|
|
35429
|
+
const preferredProviderDef = providers.find((p47) => p47.id === config.provider.type);
|
|
35406
35430
|
const preferredIsLocal = preferredProviderDef?.requiresApiKey === false && preferredProviderDef?.id !== "copilot";
|
|
35407
35431
|
const preferredHasApiKey = preferredProviderDef ? !!process.env[preferredProviderDef.envVar] : false;
|
|
35408
35432
|
const preferredHasOpenAIOAuth = preferredProviderDef?.id === "openai" && hasOpenAIOAuthTokens;
|
|
@@ -35432,12 +35456,12 @@ async function ensureConfiguredV2(config) {
|
|
|
35432
35456
|
preferredUnavailableWasLocal = preferredIsLocal;
|
|
35433
35457
|
}
|
|
35434
35458
|
if (!preferredWasConfiguredButUnavailable || !preferredUnavailableWasLocal) {
|
|
35435
|
-
const configuredProviders = providers.filter((
|
|
35436
|
-
if (
|
|
35437
|
-
if (
|
|
35438
|
-
return hasOpenAIOAuthTokens || !!process.env[
|
|
35459
|
+
const configuredProviders = providers.filter((p47) => {
|
|
35460
|
+
if (p47.id === "copilot") return isProviderConfigured();
|
|
35461
|
+
if (p47.id === "openai") {
|
|
35462
|
+
return hasOpenAIOAuthTokens || !!process.env[p47.envVar];
|
|
35439
35463
|
}
|
|
35440
|
-
return
|
|
35464
|
+
return p47.requiresApiKey === false || !!process.env[p47.envVar];
|
|
35441
35465
|
});
|
|
35442
35466
|
for (const prov of configuredProviders) {
|
|
35443
35467
|
try {
|
|
@@ -35510,7 +35534,7 @@ init_auth();
|
|
|
35510
35534
|
init_env();
|
|
35511
35535
|
async function selectProviderInteractively(providers, currentProviderId) {
|
|
35512
35536
|
return new Promise((resolve4) => {
|
|
35513
|
-
let selectedIndex = providers.findIndex((
|
|
35537
|
+
let selectedIndex = providers.findIndex((p47) => p47.id === currentProviderId);
|
|
35514
35538
|
if (selectedIndex === -1) selectedIndex = 0;
|
|
35515
35539
|
let lastTotalLines = 0;
|
|
35516
35540
|
const clearPrevious = () => {
|
|
@@ -35608,12 +35632,12 @@ var providerCommand = {
|
|
|
35608
35632
|
`));
|
|
35609
35633
|
const allProviders2 = getAllProviders();
|
|
35610
35634
|
const configuredProviders = getConfiguredProviders();
|
|
35611
|
-
const providerOptions = allProviders2.map((
|
|
35612
|
-
id:
|
|
35613
|
-
name:
|
|
35614
|
-
emoji:
|
|
35615
|
-
description:
|
|
35616
|
-
isConfigured: configuredProviders.some((cp) => cp.id ===
|
|
35635
|
+
const providerOptions = allProviders2.map((p47) => ({
|
|
35636
|
+
id: p47.id,
|
|
35637
|
+
name: p47.name,
|
|
35638
|
+
emoji: p47.emoji,
|
|
35639
|
+
description: p47.description,
|
|
35640
|
+
isConfigured: configuredProviders.some((cp) => cp.id === p47.id)
|
|
35617
35641
|
}));
|
|
35618
35642
|
const selectedProviderId = await selectProviderInteractively(
|
|
35619
35643
|
providerOptions,
|
|
@@ -35628,15 +35652,15 @@ var providerCommand = {
|
|
|
35628
35652
|
`));
|
|
35629
35653
|
return false;
|
|
35630
35654
|
}
|
|
35631
|
-
const newProvider2 = allProviders2.find((
|
|
35655
|
+
const newProvider2 = allProviders2.find((p47) => p47.id === selectedProviderId);
|
|
35632
35656
|
return await switchProvider(newProvider2, session);
|
|
35633
35657
|
}
|
|
35634
35658
|
const newProviderId = args[0]?.toLowerCase();
|
|
35635
35659
|
const allProviders = getAllProviders();
|
|
35636
|
-
const newProvider = allProviders.find((
|
|
35660
|
+
const newProvider = allProviders.find((p47) => p47.id === newProviderId);
|
|
35637
35661
|
if (!newProvider) {
|
|
35638
35662
|
console.log(chalk.red(`Unknown provider: ${newProviderId}`));
|
|
35639
|
-
console.log(chalk.dim(`Available: ${allProviders.map((
|
|
35663
|
+
console.log(chalk.dim(`Available: ${allProviders.map((p47) => p47.id).join(", ")}
|
|
35640
35664
|
`));
|
|
35641
35665
|
return false;
|
|
35642
35666
|
}
|
|
@@ -36368,9 +36392,9 @@ function formatChangeSummary(files, options = {}) {
|
|
|
36368
36392
|
const filesToShow = opts.maxFiles && opts.maxFiles > 0 ? files.slice(0, opts.maxFiles) : files;
|
|
36369
36393
|
const remaining = files.length - filesToShow.length;
|
|
36370
36394
|
for (const file of filesToShow) {
|
|
36371
|
-
const
|
|
36395
|
+
const statusIcon2 = file.status === "added" ? chalk.green("A") : file.status === "deleted" ? chalk.red("D") : chalk.yellow("M");
|
|
36372
36396
|
const stats = `${chalk.green("+" + file.additions)} ${chalk.red("-" + file.deletions)}`;
|
|
36373
|
-
output += ` ${
|
|
36397
|
+
output += ` ${statusIcon2} ${chalk.white(file.path)} ${chalk.gray(stats)}
|
|
36374
36398
|
`;
|
|
36375
36399
|
}
|
|
36376
36400
|
if (remaining > 0) {
|
|
@@ -36795,7 +36819,7 @@ async function showTrustStatus(session, trustStore) {
|
|
|
36795
36819
|
}
|
|
36796
36820
|
const level = trustStore.getLevel(projectPath);
|
|
36797
36821
|
const list = trustStore.list();
|
|
36798
|
-
const project = list.find((
|
|
36822
|
+
const project = list.find((p47) => p47.path === projectPath);
|
|
36799
36823
|
p26.log.message("");
|
|
36800
36824
|
p26.log.message(`\u{1F510} Project Trust Status`);
|
|
36801
36825
|
p26.log.message(` Path: ${projectPath}`);
|
|
@@ -36888,8 +36912,8 @@ async function listTrustedProjects2(trustStore) {
|
|
|
36888
36912
|
p26.log.message("");
|
|
36889
36913
|
for (const project of projects) {
|
|
36890
36914
|
const level = project.approvalLevel.toUpperCase().padEnd(5);
|
|
36891
|
-
const
|
|
36892
|
-
p26.log.message(` [${level}] ${
|
|
36915
|
+
const path62 = project.path.length > 50 ? "..." + project.path.slice(-47) : project.path;
|
|
36916
|
+
p26.log.message(` [${level}] ${path62}`);
|
|
36893
36917
|
p26.log.message(` Last accessed: ${new Date(project.lastAccessed).toLocaleString()}`);
|
|
36894
36918
|
}
|
|
36895
36919
|
p26.log.message("");
|
|
@@ -38120,8 +38144,8 @@ function displayRewindResult(result) {
|
|
|
38120
38144
|
const fileName = filePath.split("/").pop() ?? filePath;
|
|
38121
38145
|
console.log(`${chalk.green(String.fromCodePoint(10003))} Restored: ${fileName}`);
|
|
38122
38146
|
}
|
|
38123
|
-
for (const { path:
|
|
38124
|
-
const fileName =
|
|
38147
|
+
for (const { path: path62, error } of result.filesFailed) {
|
|
38148
|
+
const fileName = path62.split("/").pop() ?? path62;
|
|
38125
38149
|
console.log(`${chalk.red(String.fromCodePoint(10007))} Failed: ${fileName} (${error})`);
|
|
38126
38150
|
}
|
|
38127
38151
|
if (result.conversationRestored) {
|
|
@@ -38580,10 +38604,10 @@ function getStatusIcon(status) {
|
|
|
38580
38604
|
function displaySessionEntry(index, session, isCurrentProject) {
|
|
38581
38605
|
const timeAgo = formatRelativeTime2(new Date(session.lastSavedAt));
|
|
38582
38606
|
const tokenStr = formatTokenCount(session.totalTokens);
|
|
38583
|
-
const
|
|
38607
|
+
const statusIcon2 = getStatusIcon(session.status);
|
|
38584
38608
|
console.log();
|
|
38585
38609
|
console.log(
|
|
38586
|
-
`${chalk.yellow(`${index}.`)} ${
|
|
38610
|
+
`${chalk.yellow(`${index}.`)} ${statusIcon2} ${chalk.dim(`[${timeAgo}]`)} ${session.messageCount} messages, ${tokenStr}`
|
|
38587
38611
|
);
|
|
38588
38612
|
if (session.title) {
|
|
38589
38613
|
console.log(` ${chalk.cyan(`"${session.title}"`)}`);
|
|
@@ -38762,8 +38786,8 @@ var STARTUP_TIMEOUT_MS = 5500;
|
|
|
38762
38786
|
var CACHE_DIR = path39__default.join(os4__default.homedir(), ".coco");
|
|
38763
38787
|
var CACHE_FILE = path39__default.join(CACHE_DIR, "version-check-cache.json");
|
|
38764
38788
|
function compareVersions(a, b) {
|
|
38765
|
-
const partsA = a.replace(/^v/, "").split(".").map((
|
|
38766
|
-
const partsB = b.replace(/^v/, "").split(".").map((
|
|
38789
|
+
const partsA = a.replace(/^v/, "").split(".").map((p47) => Number(p47.replace(/-.*$/, "")));
|
|
38790
|
+
const partsB = b.replace(/^v/, "").split(".").map((p47) => Number(p47.replace(/-.*$/, "")));
|
|
38767
38791
|
for (let i = 0; i < 3; i++) {
|
|
38768
38792
|
const numA = partsA[i] ?? 0;
|
|
38769
38793
|
const numB = partsB[i] ?? 0;
|
|
@@ -38887,13 +38911,13 @@ async function checkForUpdatesInteractive() {
|
|
|
38887
38911
|
]);
|
|
38888
38912
|
clearTimeout(startupTimerId);
|
|
38889
38913
|
if (!updateInfo) return;
|
|
38890
|
-
const
|
|
38914
|
+
const p47 = await import('@clack/prompts');
|
|
38891
38915
|
printUpdateBanner(updateInfo);
|
|
38892
|
-
const answer = await
|
|
38916
|
+
const answer = await p47.confirm({
|
|
38893
38917
|
message: "Exit now to update?",
|
|
38894
38918
|
initialValue: true
|
|
38895
38919
|
});
|
|
38896
|
-
if (!
|
|
38920
|
+
if (!p47.isCancel(answer) && answer) {
|
|
38897
38921
|
console.log();
|
|
38898
38922
|
console.log(chalk.dim(` Running: ${updateInfo.updateCommand}`));
|
|
38899
38923
|
console.log();
|
|
@@ -48530,11 +48554,11 @@ var getLearnedPatternsTool = defineTool({
|
|
|
48530
48554
|
const patterns = store.getFrequentPatterns(typedInput.limit);
|
|
48531
48555
|
return {
|
|
48532
48556
|
totalPatterns: patterns.length,
|
|
48533
|
-
patterns: patterns.map((
|
|
48534
|
-
pattern:
|
|
48535
|
-
preference:
|
|
48536
|
-
frequency:
|
|
48537
|
-
lastUsed: new Date(
|
|
48557
|
+
patterns: patterns.map((p47) => ({
|
|
48558
|
+
pattern: p47.pattern,
|
|
48559
|
+
preference: p47.userPreference,
|
|
48560
|
+
frequency: p47.frequency,
|
|
48561
|
+
lastUsed: new Date(p47.lastUsed).toISOString()
|
|
48538
48562
|
}))
|
|
48539
48563
|
};
|
|
48540
48564
|
}
|
|
@@ -50047,6 +50071,188 @@ var bestOfNCommand = {
|
|
|
50047
50071
|
}
|
|
50048
50072
|
};
|
|
50049
50073
|
|
|
50074
|
+
// src/cli/repl/commands/doctor.ts
|
|
50075
|
+
init_paths();
|
|
50076
|
+
init_loader();
|
|
50077
|
+
init_hooks();
|
|
50078
|
+
init_auth();
|
|
50079
|
+
function statusIcon(status) {
|
|
50080
|
+
if (status === "pass") return "PASS";
|
|
50081
|
+
if (status === "warn") return "WARN";
|
|
50082
|
+
return "FAIL";
|
|
50083
|
+
}
|
|
50084
|
+
async function checkProjectAccess(projectPath) {
|
|
50085
|
+
try {
|
|
50086
|
+
await access(projectPath, constants.R_OK | constants.W_OK);
|
|
50087
|
+
return {
|
|
50088
|
+
name: "Project access",
|
|
50089
|
+
status: "pass",
|
|
50090
|
+
detail: `Readable and writable: ${projectPath}`
|
|
50091
|
+
};
|
|
50092
|
+
} catch (error) {
|
|
50093
|
+
return {
|
|
50094
|
+
name: "Project access",
|
|
50095
|
+
status: "fail",
|
|
50096
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
50097
|
+
};
|
|
50098
|
+
}
|
|
50099
|
+
}
|
|
50100
|
+
async function checkConfig(projectPath) {
|
|
50101
|
+
try {
|
|
50102
|
+
const paths = await findAllConfigPaths(projectPath);
|
|
50103
|
+
await loadConfig(path39__default.join(projectPath, ".coco", "config.json"));
|
|
50104
|
+
const found = [paths.project, paths.global].filter(Boolean).length;
|
|
50105
|
+
return {
|
|
50106
|
+
name: "Configuration",
|
|
50107
|
+
status: "pass",
|
|
50108
|
+
detail: found > 0 ? `${found} config file(s) parsed successfully` : "Using built-in defaults"
|
|
50109
|
+
};
|
|
50110
|
+
} catch (error) {
|
|
50111
|
+
return {
|
|
50112
|
+
name: "Configuration",
|
|
50113
|
+
status: "fail",
|
|
50114
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
50115
|
+
};
|
|
50116
|
+
}
|
|
50117
|
+
}
|
|
50118
|
+
async function checkAuth(session) {
|
|
50119
|
+
const provider = session.config.provider.type;
|
|
50120
|
+
if (provider === "anthropic") {
|
|
50121
|
+
return process.env["ANTHROPIC_API_KEY"] ? {
|
|
50122
|
+
name: "Provider auth",
|
|
50123
|
+
status: "pass",
|
|
50124
|
+
detail: "ANTHROPIC_API_KEY detected"
|
|
50125
|
+
} : {
|
|
50126
|
+
name: "Provider auth",
|
|
50127
|
+
status: "warn",
|
|
50128
|
+
detail: "Missing ANTHROPIC_API_KEY for current provider"
|
|
50129
|
+
};
|
|
50130
|
+
}
|
|
50131
|
+
if (provider === "openai" || provider === "codex") {
|
|
50132
|
+
const hasApiKey = !!process.env["OPENAI_API_KEY"];
|
|
50133
|
+
const hasOauth = await isOAuthConfigured("openai").catch(() => false);
|
|
50134
|
+
return hasApiKey || hasOauth ? {
|
|
50135
|
+
name: "Provider auth",
|
|
50136
|
+
status: "pass",
|
|
50137
|
+
detail: hasApiKey ? "OPENAI_API_KEY detected" : "OpenAI OAuth is configured"
|
|
50138
|
+
} : {
|
|
50139
|
+
name: "Provider auth",
|
|
50140
|
+
status: "warn",
|
|
50141
|
+
detail: "Missing OPENAI_API_KEY and no OpenAI OAuth configuration found"
|
|
50142
|
+
};
|
|
50143
|
+
}
|
|
50144
|
+
if (provider === "gemini") {
|
|
50145
|
+
const hasApiKey = !!process.env["GEMINI_API_KEY"] || !!process.env["GOOGLE_API_KEY"];
|
|
50146
|
+
const hasOauth = await isOAuthConfigured("gemini").catch(() => false);
|
|
50147
|
+
const hasAdc = await isADCConfigured().catch(() => false);
|
|
50148
|
+
return hasApiKey || hasOauth || hasAdc ? {
|
|
50149
|
+
name: "Provider auth",
|
|
50150
|
+
status: "pass",
|
|
50151
|
+
detail: hasApiKey ? "Gemini API key detected" : hasAdc ? "Google ADC is configured" : "Gemini OAuth is configured"
|
|
50152
|
+
} : {
|
|
50153
|
+
name: "Provider auth",
|
|
50154
|
+
status: "warn",
|
|
50155
|
+
detail: "Missing Gemini auth (API key, OAuth, or ADC)"
|
|
50156
|
+
};
|
|
50157
|
+
}
|
|
50158
|
+
if (provider === "copilot") {
|
|
50159
|
+
const hasOauth = await isOAuthConfigured("copilot").catch(() => false);
|
|
50160
|
+
return hasOauth ? {
|
|
50161
|
+
name: "Provider auth",
|
|
50162
|
+
status: "pass",
|
|
50163
|
+
detail: "Copilot auth is configured"
|
|
50164
|
+
} : {
|
|
50165
|
+
name: "Provider auth",
|
|
50166
|
+
status: "warn",
|
|
50167
|
+
detail: "Copilot auth not configured"
|
|
50168
|
+
};
|
|
50169
|
+
}
|
|
50170
|
+
return {
|
|
50171
|
+
name: "Provider auth",
|
|
50172
|
+
status: "warn",
|
|
50173
|
+
detail: `No doctor auth rule for provider '${provider}'`
|
|
50174
|
+
};
|
|
50175
|
+
}
|
|
50176
|
+
async function checkHooks(projectPath) {
|
|
50177
|
+
const hooksPath = path39__default.join(projectPath, ".coco", "hooks.json");
|
|
50178
|
+
try {
|
|
50179
|
+
await access(hooksPath, constants.R_OK);
|
|
50180
|
+
} catch {
|
|
50181
|
+
return {
|
|
50182
|
+
name: "Hooks",
|
|
50183
|
+
status: "pass",
|
|
50184
|
+
detail: "No project hooks configured"
|
|
50185
|
+
};
|
|
50186
|
+
}
|
|
50187
|
+
try {
|
|
50188
|
+
const registry = createHookRegistry();
|
|
50189
|
+
await registry.loadFromFile(hooksPath);
|
|
50190
|
+
return {
|
|
50191
|
+
name: "Hooks",
|
|
50192
|
+
status: "pass",
|
|
50193
|
+
detail: `${registry.size} hook(s) loaded from .coco/hooks.json`
|
|
50194
|
+
};
|
|
50195
|
+
} catch (error) {
|
|
50196
|
+
return {
|
|
50197
|
+
name: "Hooks",
|
|
50198
|
+
status: "fail",
|
|
50199
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
50200
|
+
};
|
|
50201
|
+
}
|
|
50202
|
+
}
|
|
50203
|
+
async function checkTooling() {
|
|
50204
|
+
try {
|
|
50205
|
+
const registry = createFullToolRegistry();
|
|
50206
|
+
const defs = registry.getToolDefinitionsForLLM();
|
|
50207
|
+
return {
|
|
50208
|
+
name: "Tool registry",
|
|
50209
|
+
status: defs.length > 0 ? "pass" : "fail",
|
|
50210
|
+
detail: `${defs.length} tool(s) available to the agent`
|
|
50211
|
+
};
|
|
50212
|
+
} catch (error) {
|
|
50213
|
+
return {
|
|
50214
|
+
name: "Tool registry",
|
|
50215
|
+
status: "fail",
|
|
50216
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
50217
|
+
};
|
|
50218
|
+
}
|
|
50219
|
+
}
|
|
50220
|
+
async function runDoctorChecks(session) {
|
|
50221
|
+
return Promise.all([
|
|
50222
|
+
checkProjectAccess(session.projectPath),
|
|
50223
|
+
checkConfig(session.projectPath),
|
|
50224
|
+
checkAuth(session),
|
|
50225
|
+
checkHooks(session.projectPath),
|
|
50226
|
+
checkTooling()
|
|
50227
|
+
]);
|
|
50228
|
+
}
|
|
50229
|
+
var doctorCommand = {
|
|
50230
|
+
name: "doctor",
|
|
50231
|
+
aliases: ["dr"],
|
|
50232
|
+
description: "Run read-only diagnostics for config, auth, hooks, and tools",
|
|
50233
|
+
usage: "/doctor",
|
|
50234
|
+
async execute(_args, session) {
|
|
50235
|
+
if (!session.config.agent.doctorV2) {
|
|
50236
|
+
p26.log.warn("Doctor v2 is disabled. Set agent.doctorV2=true to enable it.");
|
|
50237
|
+
return false;
|
|
50238
|
+
}
|
|
50239
|
+
p26.intro(chalk.cyan("Coco Doctor"));
|
|
50240
|
+
const checks = await runDoctorChecks(session);
|
|
50241
|
+
const failures = checks.filter((check) => check.status === "fail").length;
|
|
50242
|
+
const warnings = checks.filter((check) => check.status === "warn").length;
|
|
50243
|
+
for (const check of checks) {
|
|
50244
|
+
p26.log.message(`${statusIcon(check.status)} ${check.name}: ${check.detail}`);
|
|
50245
|
+
}
|
|
50246
|
+
p26.log.message("");
|
|
50247
|
+
p26.log.info(
|
|
50248
|
+
`Summary: ${checks.length - failures - warnings} pass, ${warnings} warn, ${failures} fail`
|
|
50249
|
+
);
|
|
50250
|
+
p26.log.message(`Config home: ${CONFIG_PATHS.home}`);
|
|
50251
|
+
p26.outro(failures > 0 ? "Doctor finished with failures" : "Doctor finished");
|
|
50252
|
+
return false;
|
|
50253
|
+
}
|
|
50254
|
+
};
|
|
50255
|
+
|
|
50050
50256
|
// src/cli/repl/output/renderer.ts
|
|
50051
50257
|
init_syntax();
|
|
50052
50258
|
var lineBuffer = "";
|
|
@@ -50815,9 +51021,9 @@ function printEditDiff(oldStr, newStr) {
|
|
|
50815
51021
|
}
|
|
50816
51022
|
if (diffLineList.length === 0) return;
|
|
50817
51023
|
const pairs = pairAdjacentDiffLines(diffLineList);
|
|
50818
|
-
const pairedDeletes = new Set(pairs.map((
|
|
50819
|
-
const pairedAdds = new Set(pairs.map((
|
|
50820
|
-
const pairByAdd = new Map(pairs.map((
|
|
51024
|
+
const pairedDeletes = new Set(pairs.map((p47) => p47.deleteIdx));
|
|
51025
|
+
const pairedAdds = new Set(pairs.map((p47) => p47.addIdx));
|
|
51026
|
+
const pairByAdd = new Map(pairs.map((p47) => [p47.addIdx, p47.deleteIdx]));
|
|
50821
51027
|
const wordHighlights = /* @__PURE__ */ new Map();
|
|
50822
51028
|
for (const pair of pairs) {
|
|
50823
51029
|
const del = diffLineList[pair.deleteIdx];
|
|
@@ -50897,8 +51103,8 @@ function formatToolSummary(toolName, input) {
|
|
|
50897
51103
|
case "grep":
|
|
50898
51104
|
case "search_files": {
|
|
50899
51105
|
const pattern = String(input.pattern || "");
|
|
50900
|
-
const
|
|
50901
|
-
return `"${pattern}"${
|
|
51106
|
+
const path62 = input.path ? ` in ${input.path}` : "";
|
|
51107
|
+
return `"${pattern}"${path62}`;
|
|
50902
51108
|
}
|
|
50903
51109
|
case "bash_exec": {
|
|
50904
51110
|
const cmd = String(input.command || "");
|
|
@@ -50919,8 +51125,8 @@ function formatToolSummary(toolName, input) {
|
|
|
50919
51125
|
function formatUrl(url) {
|
|
50920
51126
|
try {
|
|
50921
51127
|
const u = new URL(url);
|
|
50922
|
-
const
|
|
50923
|
-
const display =
|
|
51128
|
+
const path62 = u.pathname.replace(/\/$/, "");
|
|
51129
|
+
const display = path62 ? `${u.hostname} \u203A ${path62.slice(1)}` : u.hostname;
|
|
50924
51130
|
const max = Math.max(getTerminalWidth2() - 20, 50);
|
|
50925
51131
|
return display.length > max ? display.slice(0, max - 1) + "\u2026" : display;
|
|
50926
51132
|
} catch {
|
|
@@ -51077,7 +51283,8 @@ var commands = [
|
|
|
51077
51283
|
fullPowerRiskCommand,
|
|
51078
51284
|
buildAppCommand,
|
|
51079
51285
|
contextCommand,
|
|
51080
|
-
bestOfNCommand
|
|
51286
|
+
bestOfNCommand,
|
|
51287
|
+
doctorCommand
|
|
51081
51288
|
];
|
|
51082
51289
|
function isSlashCommand(input) {
|
|
51083
51290
|
return input.startsWith("/");
|
|
@@ -51280,8 +51487,8 @@ function getCursorVisualPos(text15, cursorPos, promptLen, termCols) {
|
|
|
51280
51487
|
function computeWordWrap(text15, startCol, termCols) {
|
|
51281
51488
|
const passthrough = {
|
|
51282
51489
|
display: text15,
|
|
51283
|
-
toDisplayPos: (
|
|
51284
|
-
toOrigPos: (
|
|
51490
|
+
toDisplayPos: (p47) => p47,
|
|
51491
|
+
toOrigPos: (p47) => p47
|
|
51285
51492
|
};
|
|
51286
51493
|
if (!text15 || termCols <= 1) return passthrough;
|
|
51287
51494
|
const origToDisp = new Int32Array(text15.length + 1);
|
|
@@ -51908,7 +52115,7 @@ function createInputHandler(_session) {
|
|
|
51908
52115
|
}
|
|
51909
52116
|
|
|
51910
52117
|
// src/cli/repl/index.ts
|
|
51911
|
-
|
|
52118
|
+
init_types8();
|
|
51912
52119
|
var COCO_SPINNER = {
|
|
51913
52120
|
interval: 120,
|
|
51914
52121
|
frames: ["\u{1F965} ", " \u{1F965} ", " \u{1F965} ", " \u{1F965} ", " \u{1F965}", " \u{1F965} ", " \u{1F965} ", " \u{1F965} "]
|
|
@@ -52540,16 +52747,16 @@ function formatToolCallForConfirmation(toolCall, metadata) {
|
|
|
52540
52747
|
const reason = input.reason ? String(input.reason) : void 0;
|
|
52541
52748
|
const actionLabel = action === "allow" ? chalk.green.bold("ALLOW") : chalk.red.bold(action.toUpperCase());
|
|
52542
52749
|
const scopeLabel = scope === "global" ? chalk.blue("Global (all projects)") : chalk.magenta("Project (current only)");
|
|
52543
|
-
const patternList = patterns.map((
|
|
52750
|
+
const patternList = patterns.map((p47) => chalk.cyan(p47)).join(", ");
|
|
52544
52751
|
const lines = [`${actionLabel}: ${patternList}`];
|
|
52545
52752
|
lines.push(`${chalk.dim(" Scope:")} ${scopeLabel}`);
|
|
52546
52753
|
if (reason) {
|
|
52547
52754
|
lines.push(`${chalk.dim(" Reason:")} ${reason}`);
|
|
52548
52755
|
}
|
|
52549
|
-
for (const
|
|
52550
|
-
lines.push(`${chalk.dim(" Risk:")} ${getRiskDescription(
|
|
52756
|
+
for (const p47 of patterns) {
|
|
52757
|
+
lines.push(`${chalk.dim(" Risk:")} ${getRiskDescription(p47)}`);
|
|
52551
52758
|
lines.push(
|
|
52552
|
-
`${chalk.dim(" Effect:")} ${getEffectDescription(action,
|
|
52759
|
+
`${chalk.dim(" Effect:")} ${getEffectDescription(action, p47, scope)}`
|
|
52553
52760
|
);
|
|
52554
52761
|
}
|
|
52555
52762
|
description = lines.join("\n ");
|
|
@@ -53219,7 +53426,9 @@ function computeTurnQualityMetrics(input) {
|
|
|
53219
53426
|
successfulToolCalls,
|
|
53220
53427
|
failedToolCalls,
|
|
53221
53428
|
hadError: input.hadError,
|
|
53222
|
-
repeatedOutputsSuppressed: input.repeatedOutputsSuppressed
|
|
53429
|
+
repeatedOutputsSuppressed: input.repeatedOutputsSuppressed,
|
|
53430
|
+
observedLargeOutputs: input.observedLargeOutputs ?? 0,
|
|
53431
|
+
observedLargeOutputChars: input.observedLargeOutputChars ?? 0
|
|
53223
53432
|
};
|
|
53224
53433
|
}
|
|
53225
53434
|
var RepeatedOutputSuppressor = class {
|
|
@@ -53254,13 +53463,20 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
53254
53463
|
let finalContent = "";
|
|
53255
53464
|
let hadTurnError = false;
|
|
53256
53465
|
let repeatedOutputsSuppressed = 0;
|
|
53466
|
+
let observedLargeOutputs = 0;
|
|
53467
|
+
let observedLargeOutputChars = 0;
|
|
53257
53468
|
const repeatedOutputSuppressor = new RepeatedOutputSuppressor();
|
|
53469
|
+
const recoveryV2Enabled = session.config.agent.recoveryV2 === true;
|
|
53470
|
+
const strictPlanModeEnabled = session.planMode && session.config.agent.planModeStrict === true;
|
|
53471
|
+
const outputOffloadObservationEnabled = session.config.agent.outputOffload === true;
|
|
53258
53472
|
const buildQualityMetrics = () => computeTurnQualityMetrics({
|
|
53259
53473
|
iterationsUsed: iteration,
|
|
53260
53474
|
maxIterations,
|
|
53261
53475
|
executedTools,
|
|
53262
53476
|
hadError: hadTurnError,
|
|
53263
|
-
repeatedOutputsSuppressed
|
|
53477
|
+
repeatedOutputsSuppressed,
|
|
53478
|
+
observedLargeOutputs,
|
|
53479
|
+
observedLargeOutputChars
|
|
53264
53480
|
});
|
|
53265
53481
|
const abortReturn = () => {
|
|
53266
53482
|
session.messages.length = messageSnapshot;
|
|
@@ -53275,7 +53491,7 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
53275
53491
|
};
|
|
53276
53492
|
};
|
|
53277
53493
|
const allTools = toolRegistry.getToolDefinitionsForLLM();
|
|
53278
|
-
const tools = session.planMode ? filterReadOnlyTools(allTools) : allTools;
|
|
53494
|
+
const tools = session.planMode ? strictPlanModeEnabled ? filterStrictPlanModeTools(allTools) : filterReadOnlyTools(allTools) : allTools;
|
|
53279
53495
|
const availableMcpToolNames = allTools.map((t) => t.name).filter((name) => name.startsWith("mcp_"));
|
|
53280
53496
|
function extractPlainText(content) {
|
|
53281
53497
|
if (typeof content === "string") return content;
|
|
@@ -53318,6 +53534,38 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
53318
53534
|
const INLINE_RESULT_MAX_CHARS = 8e3;
|
|
53319
53535
|
const INLINE_RESULT_HEAD_CHARS = 6500;
|
|
53320
53536
|
const INLINE_RESULT_TAIL_CHARS = 1e3;
|
|
53537
|
+
const OUTPUT_OFFLOAD_OBSERVATION_THRESHOLD = 12e3;
|
|
53538
|
+
const OUTPUT_OFFLOAD_PERSIST_THRESHOLD = 2e4;
|
|
53539
|
+
function shouldRetryStreamError(classification, responseSoFar, toolCallsSoFar, attempt, maxAttempts) {
|
|
53540
|
+
if (!recoveryV2Enabled) return false;
|
|
53541
|
+
if (attempt >= maxAttempts) return false;
|
|
53542
|
+
if (toolCallsSoFar.length > 0) return false;
|
|
53543
|
+
if (responseSoFar.trim().length > 0) return false;
|
|
53544
|
+
return classification.kind === "provider_retryable" || classification.kind === "unexpected";
|
|
53545
|
+
}
|
|
53546
|
+
async function pauseBeforeRetry(attempt) {
|
|
53547
|
+
const delayMs = Math.min(1200, 250 * attempt);
|
|
53548
|
+
await new Promise((resolve4) => setTimeout(resolve4, delayMs));
|
|
53549
|
+
}
|
|
53550
|
+
async function offloadLargeOutput(toolCall, content) {
|
|
53551
|
+
const artifactDir = path39__default.join(session.projectPath, ".coco", "session-artifacts", session.id);
|
|
53552
|
+
const safeToolName = toolCall.name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
53553
|
+
const artifactPath = path39__default.join(artifactDir, `${safeToolName}-${toolCall.id}.txt`);
|
|
53554
|
+
const relativeArtifactPath = path39__default.relative(session.projectPath, artifactPath);
|
|
53555
|
+
await mkdir(artifactDir, { recursive: true });
|
|
53556
|
+
await writeFile(artifactPath, content, "utf-8");
|
|
53557
|
+
return {
|
|
53558
|
+
artifactPath,
|
|
53559
|
+
toolResultContent: [
|
|
53560
|
+
`Large tool output stored in local artifact: ${relativeArtifactPath}`,
|
|
53561
|
+
`Original size: ${content.length.toLocaleString()} characters.`,
|
|
53562
|
+
"Use read_file on the artifact path if you need the full output.",
|
|
53563
|
+
"",
|
|
53564
|
+
"Preview:",
|
|
53565
|
+
truncateInlineResult(content, toolCall.name)
|
|
53566
|
+
].join("\n")
|
|
53567
|
+
};
|
|
53568
|
+
}
|
|
53321
53569
|
function truncateInlineResult(content, toolName) {
|
|
53322
53570
|
if (content.length <= INLINE_RESULT_MAX_CHARS) return content;
|
|
53323
53571
|
const head = content.slice(0, INLINE_RESULT_HEAD_CHARS);
|
|
@@ -53353,28 +53601,80 @@ ${tail}`;
|
|
|
53353
53601
|
}
|
|
53354
53602
|
function shouldRecoverNoToolTurn(stopReason, content) {
|
|
53355
53603
|
const trimmed = content.trim();
|
|
53604
|
+
if (trimmed.length === 0) {
|
|
53605
|
+
return {
|
|
53606
|
+
recover: true,
|
|
53607
|
+
reason: "The previous response produced no usable output. Continue the task immediately or explain the blocker in one concise sentence.",
|
|
53608
|
+
category: "empty"
|
|
53609
|
+
};
|
|
53610
|
+
}
|
|
53356
53611
|
if (stopReason === "tool_use") {
|
|
53357
53612
|
return {
|
|
53358
53613
|
recover: true,
|
|
53359
|
-
reason: "The previous response indicated tool use, but no tool calls were received. Re-emit the tool call(s) now."
|
|
53614
|
+
reason: "The previous response indicated tool use, but no tool calls were received. Re-emit the tool call(s) now.",
|
|
53615
|
+
category: "tool_use"
|
|
53360
53616
|
};
|
|
53361
53617
|
}
|
|
53362
53618
|
if (stopReason === "max_tokens" && trimmed.length === 0) {
|
|
53363
53619
|
return {
|
|
53364
53620
|
recover: true,
|
|
53365
|
-
reason: "The previous response was cut off before producing any usable output. Continue immediately."
|
|
53621
|
+
reason: "The previous response was cut off before producing any usable output. Continue immediately.",
|
|
53622
|
+
category: "empty"
|
|
53366
53623
|
};
|
|
53367
53624
|
}
|
|
53368
|
-
const planningOnly = trimmed.length > 0 && trimmed.length < 320 && /^(voy a|ahora voy|i('| )?ll|i will|let me|starting|preparing|activating|continuo|contin[uú]o|de acuerdo[, ]+voy)\b/i.test(
|
|
53625
|
+
const planningOnly = trimmed.length > 0 && trimmed.length < 320 && /^(voy a|ahora voy|voy a revisar|voy a comprobar|voy a mirar|déjame|dejame|a continuación|i('| )?ll|i will|let me|starting|preparing|activating|checking|reviewing|looking into|working on|continuo|contin[uú]o|de acuerdo[, ]+voy)\b/i.test(
|
|
53369
53626
|
trimmed
|
|
53370
53627
|
);
|
|
53371
53628
|
if (planningOnly) {
|
|
53372
53629
|
return {
|
|
53373
53630
|
recover: true,
|
|
53374
|
-
reason: "Do not only describe the next step. Execute it now with concrete tool calls."
|
|
53631
|
+
reason: "Do not only describe the next step. Execute it now with concrete tool calls.",
|
|
53632
|
+
category: "planning_only"
|
|
53375
53633
|
};
|
|
53376
53634
|
}
|
|
53377
|
-
return { recover: false, reason: "" };
|
|
53635
|
+
return { recover: false, reason: "", category: "none" };
|
|
53636
|
+
}
|
|
53637
|
+
async function requestNoToolFallbackExplanation(priorContent, reason) {
|
|
53638
|
+
let explanation = "";
|
|
53639
|
+
let explanationThinkingEnded = false;
|
|
53640
|
+
options.onThinkingStart?.();
|
|
53641
|
+
try {
|
|
53642
|
+
const finalMessages = [
|
|
53643
|
+
...getConversationContext(session, toolRegistry),
|
|
53644
|
+
{
|
|
53645
|
+
role: "assistant",
|
|
53646
|
+
content: priorContent || "[No output returned in previous step.]"
|
|
53647
|
+
},
|
|
53648
|
+
{
|
|
53649
|
+
role: "user",
|
|
53650
|
+
content: `[System: ${reason} Do not call tools. Either explain the exact blocker and ask the user for the smallest missing input, or, if the task is already complete, summarize it briefly. Do not return an empty response.]`
|
|
53651
|
+
}
|
|
53652
|
+
];
|
|
53653
|
+
for await (const chunk of provider.streamWithTools(finalMessages, {
|
|
53654
|
+
tools: [],
|
|
53655
|
+
maxTokens: session.config.provider.maxTokens,
|
|
53656
|
+
signal: options.signal
|
|
53657
|
+
})) {
|
|
53658
|
+
if (options.signal?.aborted) break;
|
|
53659
|
+
if (chunk.type === "text" && chunk.text) {
|
|
53660
|
+
if (!explanationThinkingEnded) {
|
|
53661
|
+
options.onThinkingEnd?.();
|
|
53662
|
+
explanationThinkingEnded = true;
|
|
53663
|
+
}
|
|
53664
|
+
explanation += chunk.text;
|
|
53665
|
+
options.onStream?.(chunk);
|
|
53666
|
+
}
|
|
53667
|
+
if (chunk.type === "done") break;
|
|
53668
|
+
}
|
|
53669
|
+
} catch {
|
|
53670
|
+
} finally {
|
|
53671
|
+
if (!explanationThinkingEnded) options.onThinkingEnd?.();
|
|
53672
|
+
}
|
|
53673
|
+
const trimmed = explanation.trim();
|
|
53674
|
+
if (trimmed.length > 0) {
|
|
53675
|
+
return explanation;
|
|
53676
|
+
}
|
|
53677
|
+
return "I could not continue because the model stopped returning actionable output. Please retry, switch provider/model, or tell me the exact next step you want me to take.";
|
|
53378
53678
|
}
|
|
53379
53679
|
function shouldAutoExtendIterationBudget(latestExecuted, latestToolResults, isInErrorLoop) {
|
|
53380
53680
|
if (isInErrorLoop) return false;
|
|
@@ -53393,80 +53693,110 @@ ${tail}`;
|
|
|
53393
53693
|
const messages = getConversationContext(session, toolRegistry);
|
|
53394
53694
|
options.onThinkingStart?.();
|
|
53395
53695
|
let responseContent = "";
|
|
53396
|
-
|
|
53696
|
+
let collectedToolCalls = [];
|
|
53397
53697
|
let thinkingEnded = false;
|
|
53398
53698
|
let lastStopReason;
|
|
53399
|
-
const
|
|
53699
|
+
const maxStreamAttempts = recoveryV2Enabled ? 2 : 1;
|
|
53400
53700
|
try {
|
|
53401
|
-
|
|
53402
|
-
|
|
53403
|
-
|
|
53404
|
-
|
|
53405
|
-
|
|
53406
|
-
|
|
53407
|
-
break;
|
|
53408
|
-
}
|
|
53701
|
+
let streamAttempt = 0;
|
|
53702
|
+
while (streamAttempt < maxStreamAttempts) {
|
|
53703
|
+
responseContent = "";
|
|
53704
|
+
collectedToolCalls = [];
|
|
53705
|
+
lastStopReason = void 0;
|
|
53706
|
+
const toolCallBuilders = /* @__PURE__ */ new Map();
|
|
53409
53707
|
try {
|
|
53410
|
-
|
|
53411
|
-
|
|
53412
|
-
|
|
53413
|
-
|
|
53414
|
-
|
|
53415
|
-
|
|
53416
|
-
|
|
53417
|
-
iterationTextChunks.push(chunk);
|
|
53418
|
-
}
|
|
53419
|
-
if (chunk.type === "tool_use_start" && chunk.toolCall) {
|
|
53420
|
-
flushLineBuffer();
|
|
53421
|
-
if (!thinkingEnded) {
|
|
53422
|
-
options.onThinkingEnd?.();
|
|
53423
|
-
thinkingEnded = true;
|
|
53424
|
-
}
|
|
53425
|
-
const id = chunk.toolCall.id ?? `tool_${toolCallBuilders.size}`;
|
|
53426
|
-
const toolName = chunk.toolCall.name ?? "";
|
|
53427
|
-
toolCallBuilders.set(id, {
|
|
53428
|
-
id,
|
|
53429
|
-
name: toolName,
|
|
53430
|
-
input: {},
|
|
53431
|
-
geminiThoughtSignature: chunk.toolCall.geminiThoughtSignature
|
|
53432
|
-
});
|
|
53433
|
-
if (toolName) {
|
|
53434
|
-
options.onToolPreparing?.(toolName);
|
|
53708
|
+
for await (const chunk of provider.streamWithTools(messages, {
|
|
53709
|
+
tools,
|
|
53710
|
+
maxTokens: session.config.provider.maxTokens,
|
|
53711
|
+
signal: options.signal
|
|
53712
|
+
})) {
|
|
53713
|
+
if (options.signal?.aborted) {
|
|
53714
|
+
break;
|
|
53435
53715
|
}
|
|
53436
|
-
|
|
53437
|
-
|
|
53438
|
-
|
|
53439
|
-
|
|
53440
|
-
|
|
53441
|
-
|
|
53442
|
-
|
|
53443
|
-
|
|
53444
|
-
|
|
53445
|
-
|
|
53446
|
-
|
|
53447
|
-
|
|
53448
|
-
|
|
53449
|
-
|
|
53450
|
-
|
|
53451
|
-
|
|
53452
|
-
|
|
53453
|
-
|
|
53454
|
-
|
|
53716
|
+
try {
|
|
53717
|
+
if (chunk.type === "text" && chunk.text) {
|
|
53718
|
+
if (!thinkingEnded) {
|
|
53719
|
+
options.onThinkingEnd?.();
|
|
53720
|
+
thinkingEnded = true;
|
|
53721
|
+
}
|
|
53722
|
+
responseContent += chunk.text;
|
|
53723
|
+
finalContent += chunk.text;
|
|
53724
|
+
iterationTextChunks.push(chunk);
|
|
53725
|
+
}
|
|
53726
|
+
if (chunk.type === "tool_use_start" && chunk.toolCall) {
|
|
53727
|
+
flushLineBuffer();
|
|
53728
|
+
if (!thinkingEnded) {
|
|
53729
|
+
options.onThinkingEnd?.();
|
|
53730
|
+
thinkingEnded = true;
|
|
53731
|
+
}
|
|
53732
|
+
const id = chunk.toolCall.id ?? `tool_${toolCallBuilders.size}`;
|
|
53733
|
+
const toolName = chunk.toolCall.name ?? "";
|
|
53734
|
+
toolCallBuilders.set(id, {
|
|
53735
|
+
id,
|
|
53736
|
+
name: toolName,
|
|
53737
|
+
input: {},
|
|
53738
|
+
geminiThoughtSignature: chunk.toolCall.geminiThoughtSignature
|
|
53739
|
+
});
|
|
53740
|
+
if (toolName) {
|
|
53741
|
+
options.onToolPreparing?.(toolName);
|
|
53742
|
+
}
|
|
53743
|
+
}
|
|
53744
|
+
if (chunk.type === "tool_use_end" && chunk.toolCall) {
|
|
53745
|
+
const id = chunk.toolCall.id ?? "";
|
|
53746
|
+
const builder = toolCallBuilders.get(id);
|
|
53747
|
+
if (builder) {
|
|
53748
|
+
const finalToolCall = {
|
|
53749
|
+
id: builder.id,
|
|
53750
|
+
name: chunk.toolCall.name ?? builder.name,
|
|
53751
|
+
input: chunk.toolCall.input ?? builder.input,
|
|
53752
|
+
geminiThoughtSignature: chunk.toolCall.geminiThoughtSignature ?? builder.geminiThoughtSignature
|
|
53753
|
+
};
|
|
53754
|
+
collectedToolCalls.push(finalToolCall);
|
|
53755
|
+
} else if (chunk.toolCall.id && chunk.toolCall.name) {
|
|
53756
|
+
collectedToolCalls.push({
|
|
53757
|
+
id: chunk.toolCall.id,
|
|
53758
|
+
name: chunk.toolCall.name,
|
|
53759
|
+
input: chunk.toolCall.input ?? {},
|
|
53760
|
+
geminiThoughtSignature: chunk.toolCall.geminiThoughtSignature
|
|
53761
|
+
});
|
|
53762
|
+
}
|
|
53763
|
+
}
|
|
53764
|
+
if (chunk.type === "done") {
|
|
53765
|
+
if (chunk.stopReason) {
|
|
53766
|
+
lastStopReason = chunk.stopReason;
|
|
53767
|
+
}
|
|
53768
|
+
if (!thinkingEnded) {
|
|
53769
|
+
options.onThinkingEnd?.();
|
|
53770
|
+
thinkingEnded = true;
|
|
53771
|
+
}
|
|
53772
|
+
break;
|
|
53773
|
+
}
|
|
53774
|
+
} catch (chunkError) {
|
|
53775
|
+
const errorMsg = chunkError instanceof Error ? chunkError.message : String(chunkError);
|
|
53776
|
+
console.error(`[agent-loop] Error processing chunk: ${errorMsg}`);
|
|
53455
53777
|
}
|
|
53456
53778
|
}
|
|
53457
|
-
|
|
53458
|
-
|
|
53459
|
-
|
|
53460
|
-
|
|
53461
|
-
|
|
53462
|
-
|
|
53463
|
-
|
|
53464
|
-
|
|
53465
|
-
|
|
53779
|
+
break;
|
|
53780
|
+
} catch (streamError) {
|
|
53781
|
+
const classification = classifyAgentLoopError(streamError, options.signal);
|
|
53782
|
+
if (classification.kind === "abort") {
|
|
53783
|
+
throw classification.original;
|
|
53784
|
+
}
|
|
53785
|
+
if (classification.kind === "provider_non_retryable") {
|
|
53786
|
+
throw classification.original;
|
|
53787
|
+
}
|
|
53788
|
+
streamAttempt++;
|
|
53789
|
+
if (shouldRetryStreamError(
|
|
53790
|
+
classification,
|
|
53791
|
+
responseContent,
|
|
53792
|
+
collectedToolCalls,
|
|
53793
|
+
streamAttempt,
|
|
53794
|
+
maxStreamAttempts
|
|
53795
|
+
)) {
|
|
53796
|
+
await pauseBeforeRetry(streamAttempt);
|
|
53797
|
+
continue;
|
|
53466
53798
|
}
|
|
53467
|
-
|
|
53468
|
-
const errorMsg = chunkError instanceof Error ? chunkError.message : String(chunkError);
|
|
53469
|
-
console.error(`[agent-loop] Error processing chunk: ${errorMsg}`);
|
|
53799
|
+
throw classification.original;
|
|
53470
53800
|
}
|
|
53471
53801
|
}
|
|
53472
53802
|
} catch (streamError) {
|
|
@@ -53544,6 +53874,15 @@ ${tail}`;
|
|
|
53544
53874
|
});
|
|
53545
53875
|
continue;
|
|
53546
53876
|
}
|
|
53877
|
+
if (noToolRecovery.recover) {
|
|
53878
|
+
noToolRecoveryAttempts = 0;
|
|
53879
|
+
finalContent += await requestNoToolFallbackExplanation(
|
|
53880
|
+
responseContent,
|
|
53881
|
+
noToolRecovery.reason
|
|
53882
|
+
);
|
|
53883
|
+
addMessage(session, { role: "assistant", content: finalContent });
|
|
53884
|
+
break;
|
|
53885
|
+
}
|
|
53547
53886
|
noToolRecoveryAttempts = 0;
|
|
53548
53887
|
addMessage(session, { role: "assistant", content: responseContent });
|
|
53549
53888
|
break;
|
|
@@ -53590,6 +53929,14 @@ ${tail}`;
|
|
|
53590
53929
|
options.onToolSkipped?.(toolCall, "Use mcp_list_servers instead of coco mcp CLI");
|
|
53591
53930
|
continue;
|
|
53592
53931
|
}
|
|
53932
|
+
if (strictPlanModeEnabled && !STRICT_PLAN_MODE_ALLOWED_TOOLS.has(toolCall.name)) {
|
|
53933
|
+
declinedTools.set(
|
|
53934
|
+
toolCall.id,
|
|
53935
|
+
`Blocked by strict plan mode: tool '${toolCall.name}' is not read-only`
|
|
53936
|
+
);
|
|
53937
|
+
options.onToolSkipped?.(toolCall, "Blocked by strict plan mode");
|
|
53938
|
+
continue;
|
|
53939
|
+
}
|
|
53593
53940
|
const trustPattern = getTrustPattern(toolCall.name, toolCall.input);
|
|
53594
53941
|
const needsConfirmation = !options.skipConfirmation && !session.trustedTools.has(trustPattern) && requiresConfirmation(toolCall.name, toolCall.input);
|
|
53595
53942
|
if (needsConfirmation) {
|
|
@@ -53674,29 +54021,29 @@ ${tail}`;
|
|
|
53674
54021
|
const patterns = executed.input.patterns;
|
|
53675
54022
|
const scope = executed.input.scope || "project";
|
|
53676
54023
|
if (Array.isArray(patterns)) {
|
|
53677
|
-
for (const
|
|
54024
|
+
for (const p47 of patterns) {
|
|
53678
54025
|
if (action === "allow") {
|
|
53679
|
-
session.trustedTools.add(
|
|
54026
|
+
session.trustedTools.add(p47);
|
|
53680
54027
|
if (scope === "global") {
|
|
53681
|
-
saveTrustedTool(
|
|
54028
|
+
saveTrustedTool(p47, null, true).catch(() => {
|
|
53682
54029
|
});
|
|
53683
54030
|
} else {
|
|
53684
|
-
saveTrustedTool(
|
|
54031
|
+
saveTrustedTool(p47, session.projectPath, false).catch(() => {
|
|
53685
54032
|
});
|
|
53686
54033
|
}
|
|
53687
|
-
removeDeniedTool(
|
|
54034
|
+
removeDeniedTool(p47, session.projectPath).catch(() => {
|
|
53688
54035
|
});
|
|
53689
54036
|
} else if (action === "deny") {
|
|
53690
|
-
session.trustedTools.delete(
|
|
54037
|
+
session.trustedTools.delete(p47);
|
|
53691
54038
|
if (scope === "global") {
|
|
53692
|
-
removeTrustedTool(
|
|
54039
|
+
removeTrustedTool(p47, session.projectPath, true).catch(() => {
|
|
53693
54040
|
});
|
|
53694
54041
|
} else {
|
|
53695
|
-
saveDeniedTool(
|
|
54042
|
+
saveDeniedTool(p47, session.projectPath).catch(() => {
|
|
53696
54043
|
});
|
|
53697
54044
|
}
|
|
53698
54045
|
} else {
|
|
53699
|
-
session.trustedTools.delete(
|
|
54046
|
+
session.trustedTools.delete(p47);
|
|
53700
54047
|
}
|
|
53701
54048
|
}
|
|
53702
54049
|
}
|
|
@@ -53742,10 +54089,21 @@ ${tail}`;
|
|
|
53742
54089
|
}
|
|
53743
54090
|
const executedCall = executedTools.find((e) => e.id === toolCall.id);
|
|
53744
54091
|
if (executedCall) {
|
|
53745
|
-
|
|
54092
|
+
if (outputOffloadObservationEnabled && executedCall.result.output.length > OUTPUT_OFFLOAD_OBSERVATION_THRESHOLD) {
|
|
54093
|
+
observedLargeOutputs++;
|
|
54094
|
+
observedLargeOutputChars += executedCall.result.output.length;
|
|
54095
|
+
}
|
|
54096
|
+
let toolResultContent = truncateInlineResult(executedCall.result.output, toolCall.name);
|
|
54097
|
+
if (outputOffloadObservationEnabled && executedCall.result.success && executedCall.result.output.length > OUTPUT_OFFLOAD_PERSIST_THRESHOLD) {
|
|
54098
|
+
try {
|
|
54099
|
+
const offloaded = await offloadLargeOutput(toolCall, executedCall.result.output);
|
|
54100
|
+
toolResultContent = offloaded.toolResultContent;
|
|
54101
|
+
} catch {
|
|
54102
|
+
}
|
|
54103
|
+
}
|
|
53746
54104
|
const transformedOutput = repeatedOutputSuppressor.transform(
|
|
53747
54105
|
toolCall.name,
|
|
53748
|
-
|
|
54106
|
+
toolResultContent
|
|
53749
54107
|
);
|
|
53750
54108
|
if (transformedOutput.suppressed) {
|
|
53751
54109
|
repeatedOutputsSuppressed++;
|
|
@@ -53993,9 +54351,30 @@ var PLAN_MODE_ALLOWED_TOOLS = /* @__PURE__ */ new Set([
|
|
|
53993
54351
|
"spawnSimpleAgent",
|
|
53994
54352
|
"checkAgentCapability"
|
|
53995
54353
|
]);
|
|
54354
|
+
var STRICT_PLAN_MODE_ALLOWED_TOOLS = /* @__PURE__ */ new Set([
|
|
54355
|
+
"glob",
|
|
54356
|
+
"read_file",
|
|
54357
|
+
"list_dir",
|
|
54358
|
+
"tree",
|
|
54359
|
+
"grep",
|
|
54360
|
+
"find_in_file",
|
|
54361
|
+
"semantic_search",
|
|
54362
|
+
"codebase_map",
|
|
54363
|
+
"git_status",
|
|
54364
|
+
"git_log",
|
|
54365
|
+
"git_diff",
|
|
54366
|
+
"git_show",
|
|
54367
|
+
"git_branch",
|
|
54368
|
+
"recall_memory",
|
|
54369
|
+
"list_memories",
|
|
54370
|
+
"list_checkpoints"
|
|
54371
|
+
]);
|
|
53996
54372
|
function filterReadOnlyTools(tools) {
|
|
53997
54373
|
return tools.filter((tool) => PLAN_MODE_ALLOWED_TOOLS.has(tool.name));
|
|
53998
54374
|
}
|
|
54375
|
+
function filterStrictPlanModeTools(tools) {
|
|
54376
|
+
return tools.filter((tool) => STRICT_PLAN_MODE_ALLOWED_TOOLS.has(tool.name));
|
|
54377
|
+
}
|
|
53999
54378
|
|
|
54000
54379
|
// src/cli/repl/index.ts
|
|
54001
54380
|
init_error_resilience();
|
|
@@ -54775,8 +55154,8 @@ async function startRepl(options = {}) {
|
|
|
54775
55154
|
};
|
|
54776
55155
|
const getAutoSwitchCandidates = (current) => {
|
|
54777
55156
|
const ordered = [];
|
|
54778
|
-
const push = (
|
|
54779
|
-
if (
|
|
55157
|
+
const push = (p47) => {
|
|
55158
|
+
if (p47 !== current && !ordered.includes(p47)) ordered.push(p47);
|
|
54780
55159
|
};
|
|
54781
55160
|
if (current === "openai") {
|
|
54782
55161
|
push("codex");
|
|
@@ -54814,7 +55193,7 @@ async function startRepl(options = {}) {
|
|
|
54814
55193
|
"lmstudio",
|
|
54815
55194
|
"ollama"
|
|
54816
55195
|
];
|
|
54817
|
-
for (const
|
|
55196
|
+
for (const p47 of genericOrder) push(p47);
|
|
54818
55197
|
return ordered;
|
|
54819
55198
|
};
|
|
54820
55199
|
const attemptAutoProviderSwitch = async (reason, originalMessage) => {
|