@corbat-tech/coco 2.27.1 → 2.27.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +540 -397
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +26 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2324,13 +2324,20 @@ async function isADCConfigured() {
|
|
|
2324
2324
|
}
|
|
2325
2325
|
async function runGcloudADCLogin() {
|
|
2326
2326
|
try {
|
|
2327
|
-
await execAsync(
|
|
2328
|
-
timeout:
|
|
2329
|
-
//
|
|
2327
|
+
await execAsync(ADC_LOGIN_COMMAND, {
|
|
2328
|
+
timeout: 3e5
|
|
2329
|
+
// 5 minutes for interactive auth
|
|
2330
2330
|
});
|
|
2331
2331
|
return true;
|
|
2332
2332
|
} catch {
|
|
2333
|
-
|
|
2333
|
+
try {
|
|
2334
|
+
await execAsync(`${ADC_LOGIN_COMMAND} --no-launch-browser`, {
|
|
2335
|
+
timeout: 3e5
|
|
2336
|
+
});
|
|
2337
|
+
return true;
|
|
2338
|
+
} catch {
|
|
2339
|
+
return false;
|
|
2340
|
+
}
|
|
2334
2341
|
}
|
|
2335
2342
|
}
|
|
2336
2343
|
async function getGeminiADCKey() {
|
|
@@ -2504,7 +2511,7 @@ function getApiKey(provider) {
|
|
|
2504
2511
|
case "gemini":
|
|
2505
2512
|
return process.env["GEMINI_API_KEY"] ?? process.env["GOOGLE_API_KEY"];
|
|
2506
2513
|
case "vertex":
|
|
2507
|
-
return
|
|
2514
|
+
return process.env["VERTEX_API_KEY"] ?? process.env["GOOGLE_API_KEY"];
|
|
2508
2515
|
case "kimi":
|
|
2509
2516
|
return process.env["KIMI_API_KEY"] ?? process.env["MOONSHOT_API_KEY"];
|
|
2510
2517
|
case "kimi-code":
|
|
@@ -6117,21 +6124,26 @@ var init_vertex = __esm({
|
|
|
6117
6124
|
config = {};
|
|
6118
6125
|
project = "";
|
|
6119
6126
|
location = DEFAULT_LOCATION;
|
|
6127
|
+
apiKey;
|
|
6120
6128
|
retryConfig = DEFAULT_RETRY_CONFIG;
|
|
6121
6129
|
async initialize(config) {
|
|
6122
6130
|
this.config = config;
|
|
6123
6131
|
this.project = config.project ?? process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"] ?? "";
|
|
6124
6132
|
this.location = config.location ?? process.env["VERTEX_LOCATION"] ?? process.env["GOOGLE_CLOUD_LOCATION"] ?? DEFAULT_LOCATION;
|
|
6133
|
+
this.apiKey = config.apiKey ?? process.env["VERTEX_API_KEY"] ?? process.env["GOOGLE_API_KEY"];
|
|
6125
6134
|
if (!this.project.trim()) {
|
|
6126
6135
|
throw new ProviderError(
|
|
6127
6136
|
"Vertex AI project not configured. Set provider.project, VERTEX_PROJECT, or GOOGLE_CLOUD_PROJECT.",
|
|
6128
6137
|
{ provider: this.id }
|
|
6129
6138
|
);
|
|
6130
6139
|
}
|
|
6140
|
+
if (this.apiKey?.trim()) {
|
|
6141
|
+
return;
|
|
6142
|
+
}
|
|
6131
6143
|
const token = await getCachedADCToken();
|
|
6132
6144
|
if (!token) {
|
|
6133
6145
|
throw new ProviderError(
|
|
6134
|
-
"Vertex AI
|
|
6146
|
+
"Vertex AI authentication is not configured. Set VERTEX_API_KEY (or GOOGLE_API_KEY), or run `gcloud auth application-default login`.",
|
|
6135
6147
|
{ provider: this.id }
|
|
6136
6148
|
);
|
|
6137
6149
|
}
|
|
@@ -6250,10 +6262,17 @@ var init_vertex = __esm({
|
|
|
6250
6262
|
return `${this.getResolvedBaseUrl()}/projects/${encodeURIComponent(this.project)}/locations/${encodeURIComponent(this.location)}/publishers/google/models/${encodeURIComponent(this.getModel(model))}:${action}`;
|
|
6251
6263
|
}
|
|
6252
6264
|
async getHeaders() {
|
|
6265
|
+
if (this.apiKey?.trim()) {
|
|
6266
|
+
return {
|
|
6267
|
+
"Content-Type": "application/json",
|
|
6268
|
+
"x-goog-api-key": this.apiKey,
|
|
6269
|
+
"x-goog-user-project": this.project
|
|
6270
|
+
};
|
|
6271
|
+
}
|
|
6253
6272
|
const token = await getCachedADCToken();
|
|
6254
6273
|
if (!token) {
|
|
6255
6274
|
throw new ProviderError(
|
|
6256
|
-
"Vertex AI
|
|
6275
|
+
"Vertex AI token is unavailable. Re-authenticate with gcloud or configure VERTEX_API_KEY.",
|
|
6257
6276
|
{ provider: this.id }
|
|
6258
6277
|
);
|
|
6259
6278
|
}
|
|
@@ -20329,11 +20348,11 @@ async function runMergeRelease(ctx) {
|
|
|
20329
20348
|
}
|
|
20330
20349
|
const tagName = ctx.newVersion ? `v${ctx.newVersion}` : void 0;
|
|
20331
20350
|
const mergeMsg = tagName ? `Merge PR #${ctx.prNumber} to ${ctx.profile.defaultBranch} and create release ${tagName}?` : `Merge PR #${ctx.prNumber} to ${ctx.profile.defaultBranch}?`;
|
|
20332
|
-
const
|
|
20351
|
+
const confirm23 = await p26.confirm({
|
|
20333
20352
|
message: mergeMsg,
|
|
20334
20353
|
initialValue: true
|
|
20335
20354
|
});
|
|
20336
|
-
if (p26.isCancel(
|
|
20355
|
+
if (p26.isCancel(confirm23) || !confirm23) {
|
|
20337
20356
|
return {
|
|
20338
20357
|
step: "merge-release",
|
|
20339
20358
|
status: "skipped",
|
|
@@ -22508,9 +22527,10 @@ var init_lifecycle = __esm({
|
|
|
22508
22527
|
init_errors2();
|
|
22509
22528
|
init_logger();
|
|
22510
22529
|
init_version();
|
|
22511
|
-
MCPServerManager = class {
|
|
22530
|
+
MCPServerManager = class _MCPServerManager {
|
|
22512
22531
|
connections = /* @__PURE__ */ new Map();
|
|
22513
22532
|
logger = getLogger();
|
|
22533
|
+
static STOP_TIMEOUT_MS = 5e3;
|
|
22514
22534
|
/**
|
|
22515
22535
|
* Create transport for a server config
|
|
22516
22536
|
*/
|
|
@@ -22597,7 +22617,15 @@ var init_lifecycle = __esm({
|
|
|
22597
22617
|
}
|
|
22598
22618
|
this.logger.info(`Stopping MCP server: ${name}`);
|
|
22599
22619
|
try {
|
|
22600
|
-
await
|
|
22620
|
+
await Promise.race([
|
|
22621
|
+
connection.transport.disconnect(),
|
|
22622
|
+
new Promise(
|
|
22623
|
+
(_, reject) => setTimeout(
|
|
22624
|
+
() => reject(new Error("MCP disconnect timeout")),
|
|
22625
|
+
_MCPServerManager.STOP_TIMEOUT_MS
|
|
22626
|
+
)
|
|
22627
|
+
)
|
|
22628
|
+
]);
|
|
22601
22629
|
} catch (error) {
|
|
22602
22630
|
this.logger.error(
|
|
22603
22631
|
`Error disconnecting server '${name}': ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -29186,13 +29214,14 @@ var PROVIDER_DEFINITIONS = {
|
|
|
29186
29214
|
functionCalling: true,
|
|
29187
29215
|
vision: true
|
|
29188
29216
|
},
|
|
29189
|
-
// Updated:
|
|
29217
|
+
// Updated: April 2026 — from docs.github.com/en/copilot/reference/ai-models/supported-models
|
|
29218
|
+
// Premium request multipliers in descriptions are for paid Copilot plans.
|
|
29190
29219
|
models: [
|
|
29191
29220
|
// Anthropic models
|
|
29192
29221
|
{
|
|
29193
29222
|
id: "claude-sonnet-4.6",
|
|
29194
29223
|
name: "Claude Sonnet 4.6",
|
|
29195
|
-
description: "
|
|
29224
|
+
description: "Balanced Claude model via Copilot \u2014 Premium x1",
|
|
29196
29225
|
contextWindow: 2e5,
|
|
29197
29226
|
maxOutputTokens: 64e3,
|
|
29198
29227
|
recommended: true
|
|
@@ -29200,28 +29229,28 @@ var PROVIDER_DEFINITIONS = {
|
|
|
29200
29229
|
{
|
|
29201
29230
|
id: "claude-opus-4.6",
|
|
29202
29231
|
name: "Claude Opus 4.6",
|
|
29203
|
-
description: "
|
|
29232
|
+
description: "Most capable Claude model via Copilot \u2014 Premium x3",
|
|
29204
29233
|
contextWindow: 2e5,
|
|
29205
29234
|
maxOutputTokens: 128e3
|
|
29206
29235
|
},
|
|
29207
29236
|
{
|
|
29208
29237
|
id: "claude-sonnet-4.5",
|
|
29209
29238
|
name: "Claude Sonnet 4.5",
|
|
29210
|
-
description: "Previous balanced Claude model via Copilot",
|
|
29239
|
+
description: "Previous balanced Claude model via Copilot \u2014 Premium x1",
|
|
29211
29240
|
contextWindow: 2e5,
|
|
29212
29241
|
maxOutputTokens: 64e3
|
|
29213
29242
|
},
|
|
29214
29243
|
{
|
|
29215
29244
|
id: "claude-opus-4.5",
|
|
29216
29245
|
name: "Claude Opus 4.5",
|
|
29217
|
-
description: "Previous flagship Claude model via Copilot",
|
|
29246
|
+
description: "Previous flagship Claude model via Copilot \u2014 Premium x3",
|
|
29218
29247
|
contextWindow: 2e5,
|
|
29219
29248
|
maxOutputTokens: 64e3
|
|
29220
29249
|
},
|
|
29221
29250
|
{
|
|
29222
29251
|
id: "claude-haiku-4.5",
|
|
29223
29252
|
name: "Claude Haiku 4.5",
|
|
29224
|
-
description: "Fast
|
|
29253
|
+
description: "Fast low-cost Claude model via Copilot \u2014 Premium x0.33",
|
|
29225
29254
|
contextWindow: 2e5,
|
|
29226
29255
|
maxOutputTokens: 64e3
|
|
29227
29256
|
},
|
|
@@ -29229,7 +29258,7 @@ var PROVIDER_DEFINITIONS = {
|
|
|
29229
29258
|
{
|
|
29230
29259
|
id: "gpt-5.4-codex",
|
|
29231
29260
|
name: "GPT-5.4 Codex",
|
|
29232
|
-
description: "
|
|
29261
|
+
description: "Latest coding model via Copilot \u2014 Premium x1",
|
|
29233
29262
|
contextWindow: 4e5,
|
|
29234
29263
|
maxOutputTokens: 128e3,
|
|
29235
29264
|
recommended: true
|
|
@@ -29237,28 +29266,28 @@ var PROVIDER_DEFINITIONS = {
|
|
|
29237
29266
|
{
|
|
29238
29267
|
id: "gpt-5.3-codex",
|
|
29239
29268
|
name: "GPT-5.3 Codex",
|
|
29240
|
-
description: "
|
|
29269
|
+
description: "Previous coding model via Copilot \u2014 Premium x1",
|
|
29241
29270
|
contextWindow: 4e5,
|
|
29242
29271
|
maxOutputTokens: 128e3
|
|
29243
29272
|
},
|
|
29244
29273
|
{
|
|
29245
29274
|
id: "gpt-5.2-codex",
|
|
29246
29275
|
name: "GPT-5.2 Codex",
|
|
29247
|
-
description: "
|
|
29276
|
+
description: "Previous coding model via Copilot \u2014 Premium x1",
|
|
29248
29277
|
contextWindow: 4e5,
|
|
29249
29278
|
maxOutputTokens: 128e3
|
|
29250
29279
|
},
|
|
29251
29280
|
{
|
|
29252
29281
|
id: "gpt-5.1-codex-max",
|
|
29253
29282
|
name: "GPT-5.1 Codex Max",
|
|
29254
|
-
description: "Frontier agentic coding model via Copilot",
|
|
29283
|
+
description: "Frontier agentic coding model via Copilot \u2014 Premium x1",
|
|
29255
29284
|
contextWindow: 4e5,
|
|
29256
29285
|
maxOutputTokens: 128e3
|
|
29257
29286
|
},
|
|
29258
29287
|
{
|
|
29259
29288
|
id: "gpt-4.1",
|
|
29260
29289
|
name: "GPT-4.1",
|
|
29261
|
-
description: "OpenAI long-context model via Copilot (1M)",
|
|
29290
|
+
description: "OpenAI long-context model via Copilot (1M) \u2014 Premium x0",
|
|
29262
29291
|
contextWindow: 1048576,
|
|
29263
29292
|
maxOutputTokens: 32768
|
|
29264
29293
|
},
|
|
@@ -29266,21 +29295,21 @@ var PROVIDER_DEFINITIONS = {
|
|
|
29266
29295
|
{
|
|
29267
29296
|
id: "gemini-3.1-pro-preview",
|
|
29268
29297
|
name: "Gemini 3.1 Pro",
|
|
29269
|
-
description: "Google's latest model via Copilot (1M)",
|
|
29298
|
+
description: "Google's latest model via Copilot (1M) \u2014 Premium x1",
|
|
29270
29299
|
contextWindow: 1e6,
|
|
29271
29300
|
maxOutputTokens: 64e3
|
|
29272
29301
|
},
|
|
29273
29302
|
{
|
|
29274
29303
|
id: "gemini-3-flash-preview",
|
|
29275
29304
|
name: "Gemini 3 Flash",
|
|
29276
|
-
description: "Google's fast model via Copilot (1M)",
|
|
29305
|
+
description: "Google's fast model via Copilot (1M) \u2014 Premium x0.33",
|
|
29277
29306
|
contextWindow: 1e6,
|
|
29278
29307
|
maxOutputTokens: 64e3
|
|
29279
29308
|
},
|
|
29280
29309
|
{
|
|
29281
29310
|
id: "gemini-2.5-pro",
|
|
29282
29311
|
name: "Gemini 2.5 Pro",
|
|
29283
|
-
description: "Google stable model via Copilot (1M)",
|
|
29312
|
+
description: "Google stable model via Copilot (1M) \u2014 Premium x1",
|
|
29284
29313
|
contextWindow: 1048576,
|
|
29285
29314
|
maxOutputTokens: 65536
|
|
29286
29315
|
}
|
|
@@ -29419,8 +29448,8 @@ var PROVIDER_DEFINITIONS = {
|
|
|
29419
29448
|
name: "Google Vertex AI Gemini",
|
|
29420
29449
|
emoji: "\u2601\uFE0F",
|
|
29421
29450
|
description: "Gemini on Vertex AI with GCP project, IAM and ADC",
|
|
29422
|
-
envVar: "
|
|
29423
|
-
apiKeyUrl: "https://
|
|
29451
|
+
envVar: "VERTEX_API_KEY",
|
|
29452
|
+
apiKeyUrl: "https://cloud.google.com/vertex-ai/generative-ai/docs/start/api-keys",
|
|
29424
29453
|
docsUrl: "https://cloud.google.com/vertex-ai/generative-ai/docs/start/quickstart",
|
|
29425
29454
|
baseUrl: "https://aiplatform.googleapis.com/v1",
|
|
29426
29455
|
supportsCustomModels: true,
|
|
@@ -30098,7 +30127,7 @@ function getConfiguredProviders() {
|
|
|
30098
30127
|
return hasLocalProviderConfig(p45.id);
|
|
30099
30128
|
}
|
|
30100
30129
|
if (p45.id === "vertex") {
|
|
30101
|
-
return !!(process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"]);
|
|
30130
|
+
return !!(process.env["VERTEX_API_KEY"] ?? process.env["GOOGLE_API_KEY"] ?? process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"]);
|
|
30102
30131
|
}
|
|
30103
30132
|
return !!process.env[p45.envVar];
|
|
30104
30133
|
});
|
|
@@ -30451,11 +30480,11 @@ async function runRemoveServer(name, options) {
|
|
|
30451
30480
|
process.exit(1);
|
|
30452
30481
|
}
|
|
30453
30482
|
if (!options.yes) {
|
|
30454
|
-
const
|
|
30483
|
+
const confirm23 = await p26.confirm({
|
|
30455
30484
|
message: `Remove server '${name}'?`,
|
|
30456
30485
|
initialValue: false
|
|
30457
30486
|
});
|
|
30458
|
-
if (p26.isCancel(
|
|
30487
|
+
if (p26.isCancel(confirm23) || !confirm23) {
|
|
30459
30488
|
p26.outro("Cancelled");
|
|
30460
30489
|
return;
|
|
30461
30490
|
}
|
|
@@ -30727,10 +30756,10 @@ async function runRemove(name, options) {
|
|
|
30727
30756
|
return;
|
|
30728
30757
|
}
|
|
30729
30758
|
if (!options.yes) {
|
|
30730
|
-
const
|
|
30759
|
+
const confirm23 = await p26.confirm({
|
|
30731
30760
|
message: `Remove skill "${name}" from ${targetDir}?`
|
|
30732
30761
|
});
|
|
30733
|
-
if (p26.isCancel(
|
|
30762
|
+
if (p26.isCancel(confirm23) || !confirm23) {
|
|
30734
30763
|
p26.log.info("Cancelled.");
|
|
30735
30764
|
p26.outro("");
|
|
30736
30765
|
return;
|
|
@@ -33282,6 +33311,301 @@ function showToolsHelp() {
|
|
|
33282
33311
|
|
|
33283
33312
|
// src/cli/repl/commands/clear.ts
|
|
33284
33313
|
init_session();
|
|
33314
|
+
async function getGitContext(projectPath) {
|
|
33315
|
+
const controller = new AbortController();
|
|
33316
|
+
const timer = setTimeout(() => controller.abort(), 3e3);
|
|
33317
|
+
try {
|
|
33318
|
+
const git = simpleGit({ baseDir: projectPath, abort: controller.signal });
|
|
33319
|
+
const status = await git.status();
|
|
33320
|
+
clearTimeout(timer);
|
|
33321
|
+
return {
|
|
33322
|
+
branch: status.current ?? "HEAD",
|
|
33323
|
+
isDirty: !status.isClean(),
|
|
33324
|
+
staged: status.staged.length,
|
|
33325
|
+
modified: status.modified.length,
|
|
33326
|
+
untracked: status.not_added.length,
|
|
33327
|
+
ahead: status.ahead,
|
|
33328
|
+
behind: status.behind
|
|
33329
|
+
};
|
|
33330
|
+
} catch {
|
|
33331
|
+
clearTimeout(timer);
|
|
33332
|
+
return null;
|
|
33333
|
+
}
|
|
33334
|
+
}
|
|
33335
|
+
function formatGitLine(ctx) {
|
|
33336
|
+
const branchColor = ctx.isDirty ? chalk.yellow : chalk.green;
|
|
33337
|
+
const parts = [chalk.dim("\u{1F33F} ") + branchColor(ctx.branch)];
|
|
33338
|
+
const changes = [];
|
|
33339
|
+
if (ctx.staged > 0) changes.push(chalk.green(`+${ctx.staged}`));
|
|
33340
|
+
if (ctx.modified > 0) changes.push(chalk.yellow(`~${ctx.modified}`));
|
|
33341
|
+
if (ctx.untracked > 0) changes.push(chalk.dim(`?${ctx.untracked}`));
|
|
33342
|
+
if (ctx.ahead > 0) changes.push(chalk.cyan(`\u2191${ctx.ahead}`));
|
|
33343
|
+
if (ctx.behind > 0) changes.push(chalk.red(`\u2193${ctx.behind}`));
|
|
33344
|
+
if (changes.length > 0) parts.push(changes.join(" "));
|
|
33345
|
+
return parts.join(" \u2022 ");
|
|
33346
|
+
}
|
|
33347
|
+
function formatGitShort(ctx) {
|
|
33348
|
+
const branch = ctx.isDirty ? chalk.yellow(ctx.branch) : chalk.green(ctx.branch);
|
|
33349
|
+
const dirty = ctx.isDirty ? chalk.yellow(" \u25CF") : "";
|
|
33350
|
+
return chalk.dim("\u{1F33F} ") + branch + dirty;
|
|
33351
|
+
}
|
|
33352
|
+
|
|
33353
|
+
// src/cli/repl/startup-panel.ts
|
|
33354
|
+
init_version();
|
|
33355
|
+
init_trust_store();
|
|
33356
|
+
init_env();
|
|
33357
|
+
|
|
33358
|
+
// src/cli/repl/quality-loop.ts
|
|
33359
|
+
init_paths();
|
|
33360
|
+
var qualityLoopEnabled = true;
|
|
33361
|
+
var hintShown = false;
|
|
33362
|
+
function isQualityLoop() {
|
|
33363
|
+
return qualityLoopEnabled;
|
|
33364
|
+
}
|
|
33365
|
+
function setQualityLoop(enabled) {
|
|
33366
|
+
qualityLoopEnabled = enabled;
|
|
33367
|
+
}
|
|
33368
|
+
function wasHintShown() {
|
|
33369
|
+
return hintShown;
|
|
33370
|
+
}
|
|
33371
|
+
function markHintShown() {
|
|
33372
|
+
hintShown = true;
|
|
33373
|
+
}
|
|
33374
|
+
function looksLikeFeatureRequest(input) {
|
|
33375
|
+
const trimmed = input.trim();
|
|
33376
|
+
if (trimmed.length < 20) return false;
|
|
33377
|
+
if (trimmed.endsWith("?") && trimmed.length < 80) return false;
|
|
33378
|
+
const featureKeywords = [
|
|
33379
|
+
/\bimplement/i,
|
|
33380
|
+
/\bcreate\b/i,
|
|
33381
|
+
/\bbuild\b/i,
|
|
33382
|
+
/\badd\b.*\b(feature|function|component|endpoint|service|module|class)/i,
|
|
33383
|
+
/\brefactor/i,
|
|
33384
|
+
/\bmigrate/i,
|
|
33385
|
+
/\bsetup\b/i,
|
|
33386
|
+
/\bintegrate/i,
|
|
33387
|
+
/\bwrite\b.*\b(code|function|test|module)/i,
|
|
33388
|
+
/\bdevelop/i,
|
|
33389
|
+
/\bdesign\b/i,
|
|
33390
|
+
/\bfix\b.*\b(bug|issue|error|problem)/i,
|
|
33391
|
+
/\bupdate\b.*\b(function|component|service|module)/i,
|
|
33392
|
+
/\bgenerate\b/i,
|
|
33393
|
+
/\bconvert\b/i
|
|
33394
|
+
];
|
|
33395
|
+
return featureKeywords.some((re) => re.test(trimmed));
|
|
33396
|
+
}
|
|
33397
|
+
function formatQualityLoopHint() {
|
|
33398
|
+
return chalk.dim(" tip: ") + chalk.magenta("/quality") + chalk.dim(" enables auto-test & iterate until quality converges");
|
|
33399
|
+
}
|
|
33400
|
+
function formatQualityResult(result) {
|
|
33401
|
+
const lines = [];
|
|
33402
|
+
const scores = result.scoreHistory;
|
|
33403
|
+
const progressStr = scores.map((s) => String(s)).join(" \u2192 ");
|
|
33404
|
+
const convergedLabel = result.converged ? chalk.green("converged") : chalk.yellow("max iterations");
|
|
33405
|
+
lines.push("");
|
|
33406
|
+
lines.push(
|
|
33407
|
+
chalk.magenta("\u2500\u2500 Quality: ") + chalk.white(progressStr) + chalk.dim(` (${convergedLabel})`) + chalk.magenta(" \u2500\u2500")
|
|
33408
|
+
);
|
|
33409
|
+
const parts = [];
|
|
33410
|
+
if (result.testsPassed !== void 0 && result.testsTotal !== void 0) {
|
|
33411
|
+
const testsColor = result.testsPassed === result.testsTotal ? chalk.green : chalk.yellow;
|
|
33412
|
+
parts.push(testsColor(`Tests: ${result.testsPassed}/${result.testsTotal}`));
|
|
33413
|
+
}
|
|
33414
|
+
if (result.coverage !== void 0) {
|
|
33415
|
+
const covColor = result.coverage >= 80 ? chalk.green : chalk.yellow;
|
|
33416
|
+
parts.push(covColor(`Coverage: ${result.coverage}%`));
|
|
33417
|
+
}
|
|
33418
|
+
if (result.securityScore !== void 0) {
|
|
33419
|
+
const secColor = result.securityScore === 100 ? chalk.green : chalk.red;
|
|
33420
|
+
parts.push(secColor(`Security: ${result.securityScore}`));
|
|
33421
|
+
}
|
|
33422
|
+
parts.push(chalk.dim(`Iterations: ${result.iterations}`));
|
|
33423
|
+
if (result.durationMs !== void 0) {
|
|
33424
|
+
const secs = (result.durationMs / 1e3).toFixed(1);
|
|
33425
|
+
parts.push(chalk.dim(`Time: ${secs}s`));
|
|
33426
|
+
}
|
|
33427
|
+
lines.push(" " + parts.join(" "));
|
|
33428
|
+
lines.push("");
|
|
33429
|
+
return lines.join("\n");
|
|
33430
|
+
}
|
|
33431
|
+
async function loadQualityLoopPreference() {
|
|
33432
|
+
try {
|
|
33433
|
+
const content = await fs35__default.readFile(CONFIG_PATHS.config, "utf-8");
|
|
33434
|
+
const config = JSON.parse(content);
|
|
33435
|
+
const value = config.qualityLoop ?? config.cocoMode;
|
|
33436
|
+
if (typeof value === "boolean") {
|
|
33437
|
+
qualityLoopEnabled = value;
|
|
33438
|
+
return value;
|
|
33439
|
+
}
|
|
33440
|
+
} catch {
|
|
33441
|
+
}
|
|
33442
|
+
return true;
|
|
33443
|
+
}
|
|
33444
|
+
async function saveQualityLoopPreference(enabled) {
|
|
33445
|
+
try {
|
|
33446
|
+
let config = {};
|
|
33447
|
+
try {
|
|
33448
|
+
const content = await fs35__default.readFile(CONFIG_PATHS.config, "utf-8");
|
|
33449
|
+
config = JSON.parse(content);
|
|
33450
|
+
} catch {
|
|
33451
|
+
}
|
|
33452
|
+
config.qualityLoop = enabled;
|
|
33453
|
+
await fs35__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2) + "\n");
|
|
33454
|
+
} catch {
|
|
33455
|
+
}
|
|
33456
|
+
}
|
|
33457
|
+
function parseQualityLoopReport(content) {
|
|
33458
|
+
const marker = "QUALITY_LOOP_REPORT";
|
|
33459
|
+
const idx = content.indexOf(marker);
|
|
33460
|
+
if (idx === -1) return null;
|
|
33461
|
+
const block = content.slice(idx);
|
|
33462
|
+
const getField = (name) => {
|
|
33463
|
+
const match = block.match(new RegExp(`${name}:\\s*(.+)`));
|
|
33464
|
+
return match?.[1]?.trim();
|
|
33465
|
+
};
|
|
33466
|
+
const scoreHistoryRaw = getField("score_history");
|
|
33467
|
+
if (!scoreHistoryRaw) return null;
|
|
33468
|
+
const scores = scoreHistoryRaw.replace(/[[\]]/g, "").split(",").map((s) => parseFloat(s.trim())).filter((n) => !isNaN(n));
|
|
33469
|
+
if (scores.length === 0) return null;
|
|
33470
|
+
const testsPassed = parseInt(getField("tests_passed") ?? "", 10);
|
|
33471
|
+
const testsTotal = parseInt(getField("tests_total") ?? "", 10);
|
|
33472
|
+
const coverage = parseInt(getField("coverage") ?? "", 10);
|
|
33473
|
+
const security = parseInt(getField("security") ?? "", 10);
|
|
33474
|
+
const iterations = parseInt(getField("iterations") ?? "", 10) || scores.length;
|
|
33475
|
+
const converged = getField("converged") === "true";
|
|
33476
|
+
return {
|
|
33477
|
+
converged,
|
|
33478
|
+
scoreHistory: scores,
|
|
33479
|
+
finalScore: scores[scores.length - 1] ?? 0,
|
|
33480
|
+
iterations,
|
|
33481
|
+
testsPassed: isNaN(testsPassed) ? void 0 : testsPassed,
|
|
33482
|
+
testsTotal: isNaN(testsTotal) ? void 0 : testsTotal,
|
|
33483
|
+
coverage: isNaN(coverage) ? void 0 : coverage,
|
|
33484
|
+
securityScore: isNaN(security) ? void 0 : security
|
|
33485
|
+
};
|
|
33486
|
+
}
|
|
33487
|
+
function getQualityLoopSystemPrompt() {
|
|
33488
|
+
return `
|
|
33489
|
+
## Quality Loop Mode (ACTIVE)
|
|
33490
|
+
|
|
33491
|
+
You are operating in quality loop mode. After implementing code changes, you MUST follow this iteration cycle:
|
|
33492
|
+
|
|
33493
|
+
1. **Implement** the requested changes (code + tests)
|
|
33494
|
+
2. **Run tests** using the run_tests or bash_exec tool
|
|
33495
|
+
3. **Self-review**: Analyze your code against these 12 quality dimensions:
|
|
33496
|
+
- Correctness, Completeness, Robustness, Readability
|
|
33497
|
+
- Maintainability, Complexity, Duplication, Test Coverage
|
|
33498
|
+
- Test Quality, Security, Documentation, Style
|
|
33499
|
+
4. **Score** your implementation 0-100 for each dimension
|
|
33500
|
+
5. **If issues found**: Fix them and go back to step 2
|
|
33501
|
+
6. **If quality is good** (overall \u2265 85 and improving < 2 points): Stop and report
|
|
33502
|
+
|
|
33503
|
+
After completing the cycle, output a quality summary in this exact format:
|
|
33504
|
+
|
|
33505
|
+
\`\`\`
|
|
33506
|
+
QUALITY_LOOP_REPORT
|
|
33507
|
+
score_history: [first_score, ..., final_score]
|
|
33508
|
+
tests_passed: X
|
|
33509
|
+
tests_total: Y
|
|
33510
|
+
coverage: Z
|
|
33511
|
+
security: 100
|
|
33512
|
+
iterations: N
|
|
33513
|
+
converged: true|false
|
|
33514
|
+
\`\`\`
|
|
33515
|
+
|
|
33516
|
+
Key rules:
|
|
33517
|
+
- Always write tests alongside code
|
|
33518
|
+
- Run tests after every change
|
|
33519
|
+
- Minimum 2 iterations before declaring convergence
|
|
33520
|
+
- Maximum 10 iterations
|
|
33521
|
+
- Fix critical issues before moving on
|
|
33522
|
+
- Report honestly - don't inflate scores`;
|
|
33523
|
+
}
|
|
33524
|
+
|
|
33525
|
+
// src/cli/repl/startup-panel.ts
|
|
33526
|
+
async function renderStartupPanel(session, gitCtx, mcpServers = []) {
|
|
33527
|
+
const trustStore = createTrustStore();
|
|
33528
|
+
await trustStore.init();
|
|
33529
|
+
const trustLevel = trustStore.getLevel(session.projectPath);
|
|
33530
|
+
const boxWidth = 41;
|
|
33531
|
+
const innerWidth = boxWidth - 2;
|
|
33532
|
+
const versionText = `v${VERSION}`;
|
|
33533
|
+
const subtitleText = "open source \u2022 corbat.tech";
|
|
33534
|
+
const boxLine = (content) => {
|
|
33535
|
+
const pad = Math.max(0, innerWidth - stringWidth2(content));
|
|
33536
|
+
return chalk.magenta("\u2502") + content + " ".repeat(pad) + chalk.magenta("\u2502");
|
|
33537
|
+
};
|
|
33538
|
+
const titleLeftRaw = " COCO";
|
|
33539
|
+
const titleRightRaw = versionText + " ";
|
|
33540
|
+
const titleLeftStyled = " " + chalk.bold.white("COCO");
|
|
33541
|
+
const titleGap = Math.max(1, innerWidth - stringWidth2(titleLeftRaw) - stringWidth2(titleRightRaw));
|
|
33542
|
+
const titleContent = titleLeftStyled + " ".repeat(titleGap) + chalk.dim(titleRightRaw);
|
|
33543
|
+
const taglineText = "code that converges to quality";
|
|
33544
|
+
const taglineContent = " " + chalk.magenta(taglineText) + " ";
|
|
33545
|
+
const subtitleContent = " " + chalk.dim(subtitleText) + " ";
|
|
33546
|
+
console.log();
|
|
33547
|
+
console.log(chalk.magenta(" \u256D" + "\u2500".repeat(boxWidth - 2) + "\u256E"));
|
|
33548
|
+
console.log(" " + boxLine(titleContent));
|
|
33549
|
+
console.log(" " + boxLine(taglineContent));
|
|
33550
|
+
console.log(" " + boxLine(subtitleContent));
|
|
33551
|
+
console.log(chalk.magenta(" \u2570" + "\u2500".repeat(boxWidth - 2) + "\u256F"));
|
|
33552
|
+
const maxPathLen = 50;
|
|
33553
|
+
let displayPath = session.projectPath;
|
|
33554
|
+
if (displayPath.length > maxPathLen) {
|
|
33555
|
+
displayPath = "..." + displayPath.slice(-maxPathLen + 3);
|
|
33556
|
+
}
|
|
33557
|
+
const lastSep = displayPath.lastIndexOf("/");
|
|
33558
|
+
const parentPath = lastSep > 0 ? displayPath.slice(0, lastSep + 1) : "";
|
|
33559
|
+
const projectName = lastSep > 0 ? displayPath.slice(lastSep + 1) : displayPath;
|
|
33560
|
+
const providerName = session.config.provider.type;
|
|
33561
|
+
const configuredModel = session.config.provider.model?.trim();
|
|
33562
|
+
const modelName = configuredModel && !["default", "none", "null", "undefined"].includes(configuredModel.toLowerCase()) ? configuredModel : getDefaultModel(session.config.provider.type);
|
|
33563
|
+
const trustText = trustLevel === "full" ? "full" : trustLevel === "write" ? "write" : trustLevel === "read" ? "read" : "";
|
|
33564
|
+
console.log();
|
|
33565
|
+
console.log(chalk.dim(` \u{1F4C1} ${parentPath}`) + chalk.magenta.bold(projectName));
|
|
33566
|
+
console.log(
|
|
33567
|
+
chalk.dim(` \u{1F916} ${providerName}/`) + chalk.magenta(modelName) + (trustText ? chalk.dim(` \u2022 \u{1F510} ${trustText}`) : "")
|
|
33568
|
+
);
|
|
33569
|
+
if (gitCtx) {
|
|
33570
|
+
console.log(` ${formatGitLine(gitCtx)}`);
|
|
33571
|
+
}
|
|
33572
|
+
const cocoStatus = isQualityLoop() ? chalk.magenta(" \u{1F504} quality mode: ") + chalk.green.bold("on") + chalk.dim(" \u2014 iterates until quality \u2265 85. /quality to disable") : chalk.dim(" \u{1F4A1} /quality on \u2014 enable auto-test & quality iteration");
|
|
33573
|
+
console.log(cocoStatus);
|
|
33574
|
+
const skillTotal = session.skillRegistry?.size ?? 0;
|
|
33575
|
+
const hasSomething = skillTotal > 0 || mcpServers.length > 0;
|
|
33576
|
+
if (hasSomething) {
|
|
33577
|
+
if (skillTotal > 0) {
|
|
33578
|
+
const allMeta = session.skillRegistry.getAllMetadata();
|
|
33579
|
+
const builtinCount = allMeta.filter((s) => s.scope === "builtin").length;
|
|
33580
|
+
const projectCount = skillTotal - builtinCount;
|
|
33581
|
+
const parts = [];
|
|
33582
|
+
if (builtinCount > 0) parts.push(`${builtinCount} builtin`);
|
|
33583
|
+
if (projectCount > 0) parts.push(`${projectCount} project`);
|
|
33584
|
+
const detail = parts.length > 0 ? ` (${parts.join(" \xB7 ")})` : "";
|
|
33585
|
+
console.log(chalk.green(" \u2713") + chalk.dim(` Skills: ${skillTotal} loaded${detail}`));
|
|
33586
|
+
} else {
|
|
33587
|
+
console.log(chalk.dim(" \xB7 Skills: none loaded"));
|
|
33588
|
+
}
|
|
33589
|
+
if (mcpServers.length > 0) {
|
|
33590
|
+
const names = mcpServers.join(", ");
|
|
33591
|
+
console.log(
|
|
33592
|
+
chalk.green(" \u2713") + chalk.dim(
|
|
33593
|
+
` MCP: ${names} (${mcpServers.length} server${mcpServers.length === 1 ? "" : "s"} active)`
|
|
33594
|
+
)
|
|
33595
|
+
);
|
|
33596
|
+
}
|
|
33597
|
+
}
|
|
33598
|
+
console.log();
|
|
33599
|
+
console.log(
|
|
33600
|
+
chalk.dim(" Type your request or ") + chalk.magenta("/help") + chalk.dim(" for commands")
|
|
33601
|
+
);
|
|
33602
|
+
const pasteHint = process.platform === "darwin" ? chalk.dim(" \u{1F4CB} \u2318V paste text \u2022 \u2303V paste image") : chalk.dim(" \u{1F4CB} Ctrl+V paste image from clipboard");
|
|
33603
|
+
console.log(pasteHint);
|
|
33604
|
+
console.log();
|
|
33605
|
+
}
|
|
33606
|
+
|
|
33607
|
+
// src/cli/repl/commands/clear.ts
|
|
33608
|
+
init_env();
|
|
33285
33609
|
var clearCommand = {
|
|
33286
33610
|
name: "clear",
|
|
33287
33611
|
aliases: ["c"],
|
|
@@ -33289,7 +33613,22 @@ var clearCommand = {
|
|
|
33289
33613
|
usage: "/clear",
|
|
33290
33614
|
async execute(_args, session) {
|
|
33291
33615
|
clearSession(session);
|
|
33292
|
-
|
|
33616
|
+
process.stdout.write("\x1B[2J\x1B[H");
|
|
33617
|
+
const projectPath = session.projectPath || process.cwd();
|
|
33618
|
+
const gitCtx = await getGitContext(projectPath);
|
|
33619
|
+
const panelSession = {
|
|
33620
|
+
...session,
|
|
33621
|
+
projectPath,
|
|
33622
|
+
config: session.config ?? {
|
|
33623
|
+
provider: {
|
|
33624
|
+
type: "anthropic",
|
|
33625
|
+
model: getDefaultModel("anthropic"),
|
|
33626
|
+
maxTokens: 8192
|
|
33627
|
+
}
|
|
33628
|
+
}
|
|
33629
|
+
};
|
|
33630
|
+
await renderStartupPanel(panelSession, gitCtx);
|
|
33631
|
+
console.log(chalk.dim("Context cleared.\n"));
|
|
33293
33632
|
return false;
|
|
33294
33633
|
}
|
|
33295
33634
|
};
|
|
@@ -33763,6 +34102,12 @@ async function setupProviderWithAuth(provider) {
|
|
|
33763
34102
|
showProviderInfo(provider);
|
|
33764
34103
|
const apiKey = await requestApiKey(provider);
|
|
33765
34104
|
if (!apiKey) return null;
|
|
34105
|
+
let vertexSettings;
|
|
34106
|
+
if (provider.id === "vertex") {
|
|
34107
|
+
const settings = await promptVertexSettings();
|
|
34108
|
+
if (!settings) return null;
|
|
34109
|
+
vertexSettings = settings;
|
|
34110
|
+
}
|
|
33766
34111
|
let baseUrl;
|
|
33767
34112
|
if (provider.askForCustomUrl) {
|
|
33768
34113
|
const wantsCustomUrl = await p26.confirm({
|
|
@@ -33786,7 +34131,7 @@ async function setupProviderWithAuth(provider) {
|
|
|
33786
34131
|
}
|
|
33787
34132
|
const model = await selectModel(provider);
|
|
33788
34133
|
if (!model) return null;
|
|
33789
|
-
const valid = await testConnection(provider, apiKey, model, baseUrl);
|
|
34134
|
+
const valid = await testConnection(provider, apiKey, model, baseUrl, vertexSettings);
|
|
33790
34135
|
if (!valid) {
|
|
33791
34136
|
const retry = await p26.confirm({
|
|
33792
34137
|
message: "Would you like to try again?",
|
|
@@ -33801,7 +34146,9 @@ async function setupProviderWithAuth(provider) {
|
|
|
33801
34146
|
type: provider.id,
|
|
33802
34147
|
model,
|
|
33803
34148
|
apiKey,
|
|
33804
|
-
baseUrl
|
|
34149
|
+
baseUrl,
|
|
34150
|
+
project: vertexSettings?.project,
|
|
34151
|
+
location: vertexSettings?.location
|
|
33805
34152
|
};
|
|
33806
34153
|
}
|
|
33807
34154
|
async function setupGcloudADC(provider) {
|
|
@@ -33817,11 +34164,6 @@ async function setupGcloudADC(provider) {
|
|
|
33817
34164
|
p26.log.error("gcloud CLI is not installed");
|
|
33818
34165
|
console.log(chalk.dim(" Install it from: https://cloud.google.com/sdk/docs/install"));
|
|
33819
34166
|
console.log();
|
|
33820
|
-
if (provider.id === "vertex") {
|
|
33821
|
-
console.log(chalk.dim(" Vertex AI requires gcloud ADC plus a Google Cloud project."));
|
|
33822
|
-
console.log();
|
|
33823
|
-
return null;
|
|
33824
|
-
}
|
|
33825
34167
|
const useFallback2 = await p26.confirm({
|
|
33826
34168
|
message: "Use API key instead?",
|
|
33827
34169
|
initialValue: true
|
|
@@ -33832,25 +34174,37 @@ async function setupGcloudADC(provider) {
|
|
|
33832
34174
|
if (!apiKey2) return null;
|
|
33833
34175
|
const model2 = await selectModel(provider);
|
|
33834
34176
|
if (!model2) return null;
|
|
33835
|
-
|
|
34177
|
+
let vertexSettings2;
|
|
34178
|
+
if (provider.id === "vertex") {
|
|
34179
|
+
const settings = await promptVertexSettings();
|
|
34180
|
+
if (!settings) return null;
|
|
34181
|
+
vertexSettings2 = settings;
|
|
34182
|
+
}
|
|
34183
|
+
const valid2 = await testConnection(provider, apiKey2, model2, void 0, vertexSettings2);
|
|
33836
34184
|
if (!valid2) return null;
|
|
33837
|
-
return {
|
|
34185
|
+
return {
|
|
34186
|
+
type: provider.id,
|
|
34187
|
+
model: model2,
|
|
34188
|
+
apiKey: apiKey2,
|
|
34189
|
+
project: vertexSettings2?.project,
|
|
34190
|
+
location: vertexSettings2?.location
|
|
34191
|
+
};
|
|
33838
34192
|
}
|
|
33839
34193
|
let adc = await inspectADC();
|
|
33840
34194
|
if (adc.status === "ok" && adc.token) {
|
|
33841
34195
|
console.log(chalk.green(" \u2713 gcloud ADC is already configured!"));
|
|
33842
34196
|
console.log();
|
|
33843
34197
|
p26.log.success("Authentication verified");
|
|
33844
|
-
const
|
|
33845
|
-
if (provider.id === "vertex" && !
|
|
34198
|
+
const vertexSettings2 = provider.id === "vertex" ? await promptVertexSettings() : void 0;
|
|
34199
|
+
if (provider.id === "vertex" && !vertexSettings2) return null;
|
|
33846
34200
|
const model2 = await selectModel(provider);
|
|
33847
34201
|
if (!model2) return null;
|
|
33848
34202
|
return {
|
|
33849
34203
|
type: provider.id,
|
|
33850
34204
|
model: model2,
|
|
33851
34205
|
apiKey: "__gcloud_adc__",
|
|
33852
|
-
project:
|
|
33853
|
-
location:
|
|
34206
|
+
project: vertexSettings2?.project,
|
|
34207
|
+
location: vertexSettings2?.location
|
|
33854
34208
|
};
|
|
33855
34209
|
}
|
|
33856
34210
|
console.log(chalk.yellow(" No reusable gcloud ADC session was found for Coco."));
|
|
@@ -33873,16 +34227,16 @@ async function setupGcloudADC(provider) {
|
|
|
33873
34227
|
console.log(chalk.green(" \u2713 gcloud ADC is now configured."));
|
|
33874
34228
|
console.log();
|
|
33875
34229
|
p26.log.success("Authentication verified");
|
|
33876
|
-
const
|
|
33877
|
-
if (provider.id === "vertex" && !
|
|
34230
|
+
const vertexSettings2 = provider.id === "vertex" ? await promptVertexSettings() : void 0;
|
|
34231
|
+
if (provider.id === "vertex" && !vertexSettings2) return null;
|
|
33878
34232
|
const model2 = await selectModel(provider);
|
|
33879
34233
|
if (!model2) return null;
|
|
33880
34234
|
return {
|
|
33881
34235
|
type: provider.id,
|
|
33882
34236
|
model: model2,
|
|
33883
34237
|
apiKey: "__gcloud_adc__",
|
|
33884
|
-
project:
|
|
33885
|
-
location:
|
|
34238
|
+
project: vertexSettings2?.project,
|
|
34239
|
+
location: vertexSettings2?.location
|
|
33886
34240
|
};
|
|
33887
34241
|
}
|
|
33888
34242
|
}
|
|
@@ -33901,13 +34255,6 @@ async function setupGcloudADC(provider) {
|
|
|
33901
34255
|
}
|
|
33902
34256
|
console.log(chalk.dim(" Coco will reuse the login on the next attempt if ADC is valid."));
|
|
33903
34257
|
console.log();
|
|
33904
|
-
if (provider.id === "vertex") {
|
|
33905
|
-
console.log(
|
|
33906
|
-
chalk.dim(" Vertex AI does not use API keys in Coco. Configure ADC, then retry.")
|
|
33907
|
-
);
|
|
33908
|
-
console.log();
|
|
33909
|
-
return null;
|
|
33910
|
-
}
|
|
33911
34258
|
const useFallback = await p26.confirm({
|
|
33912
34259
|
message: "Use API key for now?",
|
|
33913
34260
|
initialValue: true
|
|
@@ -33918,9 +34265,21 @@ async function setupGcloudADC(provider) {
|
|
|
33918
34265
|
if (!apiKey) return null;
|
|
33919
34266
|
const model = await selectModel(provider);
|
|
33920
34267
|
if (!model) return null;
|
|
33921
|
-
|
|
34268
|
+
let vertexSettings;
|
|
34269
|
+
if (provider.id === "vertex") {
|
|
34270
|
+
const settings = await promptVertexSettings();
|
|
34271
|
+
if (!settings) return null;
|
|
34272
|
+
vertexSettings = settings;
|
|
34273
|
+
}
|
|
34274
|
+
const valid = await testConnection(provider, apiKey, model, void 0, vertexSettings);
|
|
33922
34275
|
if (!valid) return null;
|
|
33923
|
-
return {
|
|
34276
|
+
return {
|
|
34277
|
+
type: provider.id,
|
|
34278
|
+
model,
|
|
34279
|
+
apiKey,
|
|
34280
|
+
project: vertexSettings?.project,
|
|
34281
|
+
location: vertexSettings?.location
|
|
34282
|
+
};
|
|
33924
34283
|
}
|
|
33925
34284
|
async function promptVertexSettings() {
|
|
33926
34285
|
const projectDefault = process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"] ?? "";
|
|
@@ -34454,8 +34813,8 @@ async function testConnection(provider, apiKey, model, baseUrl, vertexSettings)
|
|
|
34454
34813
|
process.env[`${provider.id.toUpperCase()}_BASE_URL`] = baseUrl;
|
|
34455
34814
|
}
|
|
34456
34815
|
if (provider.id === "vertex") {
|
|
34457
|
-
if (vertexSettings?.project) ;
|
|
34458
|
-
if (vertexSettings?.location) ;
|
|
34816
|
+
if (vertexSettings?.project) process.env["VERTEX_PROJECT"] = vertexSettings.project;
|
|
34817
|
+
if (vertexSettings?.location) process.env["VERTEX_LOCATION"] = vertexSettings.location;
|
|
34459
34818
|
}
|
|
34460
34819
|
const testProvider = await createProvider(provider.id, {
|
|
34461
34820
|
model,
|
|
@@ -34754,8 +35113,9 @@ async function ensureConfiguredV2(config) {
|
|
|
34754
35113
|
});
|
|
34755
35114
|
for (const prov of configuredProviders) {
|
|
34756
35115
|
try {
|
|
35116
|
+
const rememberedModel = await getLastUsedModel(prov.id);
|
|
34757
35117
|
const recommended = getRecommendedModel(prov.id);
|
|
34758
|
-
const model = recommended?.id || prov.models[0]?.id || "";
|
|
35118
|
+
const model = rememberedModel || recommended?.id || prov.models[0]?.id || "";
|
|
34759
35119
|
let providerId = prov.id;
|
|
34760
35120
|
if (prov.id === "openai" && hasOpenAIOAuthTokens && !process.env[prov.envVar]) {
|
|
34761
35121
|
const tokenResult = await getOrRefreshOAuthToken("openai");
|
|
@@ -34983,7 +35343,7 @@ async function switchProvider(initialProvider, session) {
|
|
|
34983
35343
|
`));
|
|
34984
35344
|
return false;
|
|
34985
35345
|
}
|
|
34986
|
-
const supportsApiKey = newProvider.
|
|
35346
|
+
const supportsApiKey = newProvider.requiresApiKey !== false;
|
|
34987
35347
|
const apiKey = supportsApiKey ? process.env[newProvider.envVar] : void 0;
|
|
34988
35348
|
const hasOAuth = supportsOAuth(newProvider.id) || newProvider.supportsOAuth;
|
|
34989
35349
|
const hasGcloudADC = newProvider.supportsGcloudADC;
|
|
@@ -35117,7 +35477,10 @@ Using existing OAuth session...`));
|
|
|
35117
35477
|
if (!adcResult) return false;
|
|
35118
35478
|
selectedAuthMethod = "gcloud";
|
|
35119
35479
|
if (newProvider.id === "vertex") {
|
|
35120
|
-
const settings = await promptVertexSettings2(
|
|
35480
|
+
const settings = await promptVertexSettings2({
|
|
35481
|
+
project: session.config.provider.project,
|
|
35482
|
+
location: session.config.provider.location
|
|
35483
|
+
});
|
|
35121
35484
|
if (!settings) return false;
|
|
35122
35485
|
vertexSettings = settings;
|
|
35123
35486
|
}
|
|
@@ -35138,6 +35501,14 @@ Using existing API key...`));
|
|
|
35138
35501
|
selectedAuthMethod = "apikey";
|
|
35139
35502
|
newApiKeyForSaving = key;
|
|
35140
35503
|
}
|
|
35504
|
+
if (newProvider.id === "vertex") {
|
|
35505
|
+
const settings = await promptVertexSettings2({
|
|
35506
|
+
project: session.config.provider.project,
|
|
35507
|
+
location: session.config.provider.location
|
|
35508
|
+
});
|
|
35509
|
+
if (!settings) return false;
|
|
35510
|
+
vertexSettings = settings;
|
|
35511
|
+
}
|
|
35141
35512
|
} else if (authChoice === "remove") {
|
|
35142
35513
|
const removeOptions = [];
|
|
35143
35514
|
if (oauthConnected) {
|
|
@@ -35194,7 +35565,10 @@ ${newProvider.emoji} ${newProvider.name} is not configured.`));
|
|
|
35194
35565
|
if (!adcResult) return false;
|
|
35195
35566
|
selectedAuthMethod = "gcloud";
|
|
35196
35567
|
if (newProvider.id === "vertex") {
|
|
35197
|
-
const settings = await promptVertexSettings2(
|
|
35568
|
+
const settings = await promptVertexSettings2({
|
|
35569
|
+
project: session.config.provider.project,
|
|
35570
|
+
location: session.config.provider.location
|
|
35571
|
+
});
|
|
35198
35572
|
if (!settings) return false;
|
|
35199
35573
|
vertexSettings = settings;
|
|
35200
35574
|
}
|
|
@@ -35245,7 +35619,9 @@ ${newProvider.emoji} ${newProvider.name} is not configured.`));
|
|
|
35245
35619
|
await saveConfiguration({
|
|
35246
35620
|
type: userFacingProviderId,
|
|
35247
35621
|
model: newModel,
|
|
35248
|
-
apiKey: newApiKeyForSaving
|
|
35622
|
+
apiKey: newApiKeyForSaving,
|
|
35623
|
+
project: vertexSettings?.project,
|
|
35624
|
+
location: vertexSettings?.location
|
|
35249
35625
|
});
|
|
35250
35626
|
} else {
|
|
35251
35627
|
await saveProviderPreference(userFacingProviderId, newModel, {
|
|
@@ -35285,8 +35661,26 @@ async function setupGcloudADCForProvider(_provider) {
|
|
|
35285
35661
|
return true;
|
|
35286
35662
|
}
|
|
35287
35663
|
console.log(chalk.yellow("\n No reusable gcloud ADC session was found for Coco."));
|
|
35288
|
-
|
|
35289
|
-
|
|
35664
|
+
console.log();
|
|
35665
|
+
if (adc.message) console.log(chalk.dim(` ${adc.message}`));
|
|
35666
|
+
console.log();
|
|
35667
|
+
const runLoginNow = await p26.confirm({
|
|
35668
|
+
message: "Authenticate with gcloud now from Coco?",
|
|
35669
|
+
initialValue: true
|
|
35670
|
+
});
|
|
35671
|
+
if (p26.isCancel(runLoginNow)) return false;
|
|
35672
|
+
if (runLoginNow) {
|
|
35673
|
+
p26.log.step("Running `gcloud auth application-default login`...");
|
|
35674
|
+
const loginOk = await runGcloudADCLogin();
|
|
35675
|
+
if (loginOk) {
|
|
35676
|
+
const refreshed = await inspectADC();
|
|
35677
|
+
if (refreshed.status === "ok" && refreshed.token) {
|
|
35678
|
+
console.log(chalk.green(" \u2713 gcloud ADC is now configured.\n"));
|
|
35679
|
+
return true;
|
|
35680
|
+
}
|
|
35681
|
+
}
|
|
35682
|
+
p26.log.error("Could not complete gcloud ADC login from Coco.");
|
|
35683
|
+
console.log();
|
|
35290
35684
|
}
|
|
35291
35685
|
console.log(chalk.dim("\n Check the current ADC state with:"));
|
|
35292
35686
|
console.log(chalk.cyan(" $ gcloud auth application-default print-access-token"));
|
|
@@ -35299,9 +35693,9 @@ async function setupGcloudADCForProvider(_provider) {
|
|
|
35299
35693
|
console.log();
|
|
35300
35694
|
return false;
|
|
35301
35695
|
}
|
|
35302
|
-
async function promptVertexSettings2() {
|
|
35303
|
-
const projectDefault = process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"] ?? "";
|
|
35304
|
-
const locationDefault = process.env["VERTEX_LOCATION"] ?? process.env["GOOGLE_CLOUD_LOCATION"] ?? "global";
|
|
35696
|
+
async function promptVertexSettings2(defaults) {
|
|
35697
|
+
const projectDefault = defaults?.project ?? process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"] ?? "";
|
|
35698
|
+
const locationDefault = defaults?.location ?? process.env["VERTEX_LOCATION"] ?? process.env["GOOGLE_CLOUD_LOCATION"] ?? "global";
|
|
35305
35699
|
const project = await p26.text({
|
|
35306
35700
|
message: "Google Cloud project ID:",
|
|
35307
35701
|
placeholder: projectDefault || "my-gcp-project",
|
|
@@ -36010,11 +36404,11 @@ async function revokeTrust(session, trustStore) {
|
|
|
36010
36404
|
p26.log.info("This project is not currently trusted");
|
|
36011
36405
|
return;
|
|
36012
36406
|
}
|
|
36013
|
-
const
|
|
36407
|
+
const confirm23 = await p26.confirm({
|
|
36014
36408
|
message: "Revoke all access to this project?",
|
|
36015
36409
|
initialValue: false
|
|
36016
36410
|
});
|
|
36017
|
-
if (p26.isCancel(
|
|
36411
|
+
if (p26.isCancel(confirm23) || !confirm23) {
|
|
36018
36412
|
p26.outro("Cancelled");
|
|
36019
36413
|
return;
|
|
36020
36414
|
}
|
|
@@ -36131,11 +36525,11 @@ var initCommand = {
|
|
|
36131
36525
|
p26.log.message(` Description: ${description}`);
|
|
36132
36526
|
}
|
|
36133
36527
|
p26.log.message("");
|
|
36134
|
-
const
|
|
36528
|
+
const confirm23 = await p26.confirm({
|
|
36135
36529
|
message: "Create project?",
|
|
36136
36530
|
initialValue: true
|
|
36137
36531
|
});
|
|
36138
|
-
if (p26.isCancel(
|
|
36532
|
+
if (p26.isCancel(confirm23) || !confirm23) {
|
|
36139
36533
|
p26.outro("Cancelled");
|
|
36140
36534
|
return false;
|
|
36141
36535
|
}
|
|
@@ -37361,11 +37755,11 @@ async function runInteractiveMode(session) {
|
|
|
37361
37755
|
);
|
|
37362
37756
|
}
|
|
37363
37757
|
console.log();
|
|
37364
|
-
const
|
|
37758
|
+
const confirm23 = await p26.confirm({
|
|
37365
37759
|
message: "Proceed with restoration?",
|
|
37366
37760
|
initialValue: true
|
|
37367
37761
|
});
|
|
37368
|
-
if (p26.isCancel(
|
|
37762
|
+
if (p26.isCancel(confirm23) || !confirm23) {
|
|
37369
37763
|
p26.outro("Cancelled");
|
|
37370
37764
|
return false;
|
|
37371
37765
|
}
|
|
@@ -37408,11 +37802,11 @@ async function runDirectMode(session, checkpointId) {
|
|
|
37408
37802
|
console.log(`${chalk.dim("Conversation:")} ${checkpoint.conversation?.messageCount} messages`);
|
|
37409
37803
|
}
|
|
37410
37804
|
console.log();
|
|
37411
|
-
const
|
|
37805
|
+
const confirm23 = await p26.confirm({
|
|
37412
37806
|
message: "Restore this checkpoint?",
|
|
37413
37807
|
initialValue: true
|
|
37414
37808
|
});
|
|
37415
|
-
if (p26.isCancel(
|
|
37809
|
+
if (p26.isCancel(confirm23) || !confirm23) {
|
|
37416
37810
|
p26.outro("Cancelled");
|
|
37417
37811
|
return false;
|
|
37418
37812
|
}
|
|
@@ -37863,11 +38257,11 @@ async function runInteractiveMode2(session) {
|
|
|
37863
38257
|
return false;
|
|
37864
38258
|
}
|
|
37865
38259
|
displaySessionDetails(selectedSession);
|
|
37866
|
-
const
|
|
38260
|
+
const confirm23 = await p26.confirm({
|
|
37867
38261
|
message: "Resume this session?",
|
|
37868
38262
|
initialValue: true
|
|
37869
38263
|
});
|
|
37870
|
-
if (p26.isCancel(
|
|
38264
|
+
if (p26.isCancel(confirm23) || !confirm23) {
|
|
37871
38265
|
p26.outro("Cancelled");
|
|
37872
38266
|
return false;
|
|
37873
38267
|
}
|
|
@@ -37885,11 +38279,11 @@ async function runDirectMode2(session, sessionId) {
|
|
|
37885
38279
|
return false;
|
|
37886
38280
|
}
|
|
37887
38281
|
displaySessionDetails(targetSession);
|
|
37888
|
-
const
|
|
38282
|
+
const confirm23 = await p26.confirm({
|
|
37889
38283
|
message: "Resume this session?",
|
|
37890
38284
|
initialValue: true
|
|
37891
38285
|
});
|
|
37892
|
-
if (p26.isCancel(
|
|
38286
|
+
if (p26.isCancel(confirm23) || !confirm23) {
|
|
37893
38287
|
p26.outro("Cancelled");
|
|
37894
38288
|
return false;
|
|
37895
38289
|
}
|
|
@@ -38902,7 +39296,9 @@ async function loadPermissionPreferences() {
|
|
|
38902
39296
|
recommendedAllowlistApplied: config.recommendedAllowlistApplied,
|
|
38903
39297
|
recommendedAllowlistDismissed: config.recommendedAllowlistDismissed,
|
|
38904
39298
|
recommendedAllowlistPrompted: config.recommendedAllowlistPrompted,
|
|
38905
|
-
recommendedAllowlistPromptedProjects: config.recommendedAllowlistPromptedProjects
|
|
39299
|
+
recommendedAllowlistPromptedProjects: config.recommendedAllowlistPromptedProjects,
|
|
39300
|
+
recommendedAllowlistAppliedProjects: config.recommendedAllowlistAppliedProjects,
|
|
39301
|
+
recommendedAllowlistDismissedProjects: config.recommendedAllowlistDismissedProjects
|
|
38906
39302
|
};
|
|
38907
39303
|
} catch {
|
|
38908
39304
|
return {};
|
|
@@ -38922,7 +39318,27 @@ async function savePermissionPreference(key, value) {
|
|
|
38922
39318
|
} catch {
|
|
38923
39319
|
}
|
|
38924
39320
|
}
|
|
38925
|
-
|
|
39321
|
+
function isRecommendedAllowlistAppliedForProject(prefs, projectPath) {
|
|
39322
|
+
const projectKey = getProjectPreferenceKey(projectPath);
|
|
39323
|
+
if (prefs.recommendedAllowlistAppliedProjects?.[projectKey] === true) {
|
|
39324
|
+
return true;
|
|
39325
|
+
}
|
|
39326
|
+
if (prefs.recommendedAllowlistApplied === true && !prefs.recommendedAllowlistAppliedProjects) {
|
|
39327
|
+
return true;
|
|
39328
|
+
}
|
|
39329
|
+
return false;
|
|
39330
|
+
}
|
|
39331
|
+
function isRecommendedAllowlistDismissedForProject(prefs, projectPath) {
|
|
39332
|
+
const projectKey = getProjectPreferenceKey(projectPath);
|
|
39333
|
+
if (prefs.recommendedAllowlistDismissedProjects?.[projectKey] === true) {
|
|
39334
|
+
return true;
|
|
39335
|
+
}
|
|
39336
|
+
if (prefs.recommendedAllowlistDismissed === true && !prefs.recommendedAllowlistDismissedProjects) {
|
|
39337
|
+
return true;
|
|
39338
|
+
}
|
|
39339
|
+
return false;
|
|
39340
|
+
}
|
|
39341
|
+
async function saveProjectPermissionPreference(key, projectPath, value) {
|
|
38926
39342
|
try {
|
|
38927
39343
|
let config = {};
|
|
38928
39344
|
try {
|
|
@@ -38930,12 +39346,12 @@ async function markPermissionSuggestionShownForProject(projectPath) {
|
|
|
38930
39346
|
config = JSON.parse(content);
|
|
38931
39347
|
} catch {
|
|
38932
39348
|
}
|
|
38933
|
-
const
|
|
38934
|
-
|
|
38935
|
-
|
|
39349
|
+
const projectKey = getProjectPreferenceKey(projectPath);
|
|
39350
|
+
const currentMap = config[key] ?? {};
|
|
39351
|
+
config[key] = {
|
|
39352
|
+
...currentMap,
|
|
39353
|
+
[projectKey]: value
|
|
38936
39354
|
};
|
|
38937
|
-
config.recommendedAllowlistPromptedProjects = promptedProjects;
|
|
38938
|
-
config.recommendedAllowlistPrompted = true;
|
|
38939
39355
|
await fs35__default.mkdir(path39__default.dirname(CONFIG_PATHS.config), { recursive: true });
|
|
38940
39356
|
await fs35__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2), "utf-8");
|
|
38941
39357
|
} catch {
|
|
@@ -38943,26 +39359,26 @@ async function markPermissionSuggestionShownForProject(projectPath) {
|
|
|
38943
39359
|
}
|
|
38944
39360
|
async function shouldShowPermissionSuggestion(projectPath = process.cwd()) {
|
|
38945
39361
|
const prefs = await loadPermissionPreferences();
|
|
38946
|
-
if (prefs
|
|
38947
|
-
return false;
|
|
38948
|
-
}
|
|
38949
|
-
if (prefs.recommendedAllowlistApplied) {
|
|
39362
|
+
if (isRecommendedAllowlistDismissedForProject(prefs, projectPath)) {
|
|
38950
39363
|
return false;
|
|
38951
39364
|
}
|
|
38952
|
-
|
|
38953
|
-
if (prefs.recommendedAllowlistPromptedProjects?.[projectKey]) {
|
|
39365
|
+
if (isRecommendedAllowlistAppliedForProject(prefs, projectPath)) {
|
|
38954
39366
|
return false;
|
|
38955
39367
|
}
|
|
38956
39368
|
return true;
|
|
38957
39369
|
}
|
|
38958
|
-
async function applyRecommendedPermissions() {
|
|
39370
|
+
async function applyRecommendedPermissions(projectPath = process.cwd()) {
|
|
38959
39371
|
for (const tool of [...RECOMMENDED_GLOBAL, ...RECOMMENDED_PROJECT]) {
|
|
38960
39372
|
await saveTrustedTool(tool, null, true);
|
|
38961
39373
|
}
|
|
38962
|
-
await
|
|
39374
|
+
await saveProjectPermissionPreference("recommendedAllowlistAppliedProjects", projectPath, true);
|
|
39375
|
+
await saveProjectPermissionPreference(
|
|
39376
|
+
"recommendedAllowlistDismissedProjects",
|
|
39377
|
+
projectPath,
|
|
39378
|
+
false
|
|
39379
|
+
);
|
|
38963
39380
|
}
|
|
38964
39381
|
async function showPermissionSuggestion(projectPath = process.cwd()) {
|
|
38965
|
-
await markPermissionSuggestionShownForProject(projectPath);
|
|
38966
39382
|
console.log();
|
|
38967
39383
|
console.log(chalk.magenta.bold(" \u{1F4CB} Recommended Permissions"));
|
|
38968
39384
|
console.log();
|
|
@@ -38989,8 +39405,11 @@ async function showPermissionSuggestion(projectPath = process.cwd()) {
|
|
|
38989
39405
|
return;
|
|
38990
39406
|
}
|
|
38991
39407
|
if (action === "dismiss") {
|
|
38992
|
-
await
|
|
38993
|
-
|
|
39408
|
+
await saveProjectPermissionPreference(
|
|
39409
|
+
"recommendedAllowlistDismissedProjects",
|
|
39410
|
+
projectPath,
|
|
39411
|
+
true
|
|
39412
|
+
);
|
|
38994
39413
|
console.log(chalk.dim(" Won't show again. Use /permissions to apply later."));
|
|
38995
39414
|
return;
|
|
38996
39415
|
}
|
|
@@ -39004,8 +39423,7 @@ async function showPermissionSuggestion(projectPath = process.cwd()) {
|
|
|
39004
39423
|
return;
|
|
39005
39424
|
}
|
|
39006
39425
|
}
|
|
39007
|
-
await applyRecommendedPermissions();
|
|
39008
|
-
await savePermissionPreference("recommendedAllowlistPrompted", true);
|
|
39426
|
+
await applyRecommendedPermissions(projectPath);
|
|
39009
39427
|
console.log(chalk.green(" \u2713 Recommended permissions applied"));
|
|
39010
39428
|
console.log(chalk.dim(" Use /permissions to review or modify anytime."));
|
|
39011
39429
|
}
|
|
@@ -39096,7 +39514,7 @@ async function showStatus(session) {
|
|
|
39096
39514
|
console.log(chalk.magenta.bold(" \u{1F510} Tool Permissions"));
|
|
39097
39515
|
console.log();
|
|
39098
39516
|
const allowCount = RECOMMENDED_GLOBAL.length + RECOMMENDED_PROJECT.length;
|
|
39099
|
-
if (prefs.
|
|
39517
|
+
if (isRecommendedAllowlistAppliedForProject(prefs, session.projectPath)) {
|
|
39100
39518
|
console.log(
|
|
39101
39519
|
chalk.green(" \u2713 Recommended allowlist applied") + chalk.dim(` (${allowCount} allow, ${RECOMMENDED_DENY.length} deny)`)
|
|
39102
39520
|
);
|
|
@@ -39138,7 +39556,7 @@ async function showStatus(session) {
|
|
|
39138
39556
|
console.log();
|
|
39139
39557
|
}
|
|
39140
39558
|
async function applyRecommended(session) {
|
|
39141
|
-
await applyRecommendedPermissions();
|
|
39559
|
+
await applyRecommendedPermissions(session.projectPath);
|
|
39142
39560
|
for (const tool of RECOMMENDED_GLOBAL) {
|
|
39143
39561
|
session.trustedTools.add(tool);
|
|
39144
39562
|
}
|
|
@@ -39188,177 +39606,18 @@ async function resetPermissions(session) {
|
|
|
39188
39606
|
} catch {
|
|
39189
39607
|
}
|
|
39190
39608
|
await savePermissionPreference("recommendedAllowlistApplied", false);
|
|
39191
|
-
|
|
39192
|
-
|
|
39193
|
-
|
|
39194
|
-
|
|
39195
|
-
init_paths();
|
|
39196
|
-
var qualityLoopEnabled = true;
|
|
39197
|
-
var hintShown = false;
|
|
39198
|
-
function isQualityLoop() {
|
|
39199
|
-
return qualityLoopEnabled;
|
|
39200
|
-
}
|
|
39201
|
-
function setQualityLoop(enabled) {
|
|
39202
|
-
qualityLoopEnabled = enabled;
|
|
39203
|
-
}
|
|
39204
|
-
function wasHintShown() {
|
|
39205
|
-
return hintShown;
|
|
39206
|
-
}
|
|
39207
|
-
function markHintShown() {
|
|
39208
|
-
hintShown = true;
|
|
39209
|
-
}
|
|
39210
|
-
function looksLikeFeatureRequest(input) {
|
|
39211
|
-
const trimmed = input.trim();
|
|
39212
|
-
if (trimmed.length < 20) return false;
|
|
39213
|
-
if (trimmed.endsWith("?") && trimmed.length < 80) return false;
|
|
39214
|
-
const featureKeywords = [
|
|
39215
|
-
/\bimplement/i,
|
|
39216
|
-
/\bcreate\b/i,
|
|
39217
|
-
/\bbuild\b/i,
|
|
39218
|
-
/\badd\b.*\b(feature|function|component|endpoint|service|module|class)/i,
|
|
39219
|
-
/\brefactor/i,
|
|
39220
|
-
/\bmigrate/i,
|
|
39221
|
-
/\bsetup\b/i,
|
|
39222
|
-
/\bintegrate/i,
|
|
39223
|
-
/\bwrite\b.*\b(code|function|test|module)/i,
|
|
39224
|
-
/\bdevelop/i,
|
|
39225
|
-
/\bdesign\b/i,
|
|
39226
|
-
/\bfix\b.*\b(bug|issue|error|problem)/i,
|
|
39227
|
-
/\bupdate\b.*\b(function|component|service|module)/i,
|
|
39228
|
-
/\bgenerate\b/i,
|
|
39229
|
-
/\bconvert\b/i
|
|
39230
|
-
];
|
|
39231
|
-
return featureKeywords.some((re) => re.test(trimmed));
|
|
39232
|
-
}
|
|
39233
|
-
function formatQualityLoopHint() {
|
|
39234
|
-
return chalk.dim(" tip: ") + chalk.magenta("/quality") + chalk.dim(" enables auto-test & iterate until quality converges");
|
|
39235
|
-
}
|
|
39236
|
-
function formatQualityResult(result) {
|
|
39237
|
-
const lines = [];
|
|
39238
|
-
const scores = result.scoreHistory;
|
|
39239
|
-
const progressStr = scores.map((s) => String(s)).join(" \u2192 ");
|
|
39240
|
-
const convergedLabel = result.converged ? chalk.green("converged") : chalk.yellow("max iterations");
|
|
39241
|
-
lines.push("");
|
|
39242
|
-
lines.push(
|
|
39243
|
-
chalk.magenta("\u2500\u2500 Quality: ") + chalk.white(progressStr) + chalk.dim(` (${convergedLabel})`) + chalk.magenta(" \u2500\u2500")
|
|
39609
|
+
await saveProjectPermissionPreference(
|
|
39610
|
+
"recommendedAllowlistAppliedProjects",
|
|
39611
|
+
session.projectPath,
|
|
39612
|
+
false
|
|
39244
39613
|
);
|
|
39245
|
-
|
|
39246
|
-
|
|
39247
|
-
|
|
39248
|
-
|
|
39249
|
-
|
|
39250
|
-
|
|
39251
|
-
const covColor = result.coverage >= 80 ? chalk.green : chalk.yellow;
|
|
39252
|
-
parts.push(covColor(`Coverage: ${result.coverage}%`));
|
|
39253
|
-
}
|
|
39254
|
-
if (result.securityScore !== void 0) {
|
|
39255
|
-
const secColor = result.securityScore === 100 ? chalk.green : chalk.red;
|
|
39256
|
-
parts.push(secColor(`Security: ${result.securityScore}`));
|
|
39257
|
-
}
|
|
39258
|
-
parts.push(chalk.dim(`Iterations: ${result.iterations}`));
|
|
39259
|
-
if (result.durationMs !== void 0) {
|
|
39260
|
-
const secs = (result.durationMs / 1e3).toFixed(1);
|
|
39261
|
-
parts.push(chalk.dim(`Time: ${secs}s`));
|
|
39262
|
-
}
|
|
39263
|
-
lines.push(" " + parts.join(" "));
|
|
39264
|
-
lines.push("");
|
|
39265
|
-
return lines.join("\n");
|
|
39266
|
-
}
|
|
39267
|
-
async function loadQualityLoopPreference() {
|
|
39268
|
-
try {
|
|
39269
|
-
const content = await fs35__default.readFile(CONFIG_PATHS.config, "utf-8");
|
|
39270
|
-
const config = JSON.parse(content);
|
|
39271
|
-
const value = config.qualityLoop ?? config.cocoMode;
|
|
39272
|
-
if (typeof value === "boolean") {
|
|
39273
|
-
qualityLoopEnabled = value;
|
|
39274
|
-
return value;
|
|
39275
|
-
}
|
|
39276
|
-
} catch {
|
|
39277
|
-
}
|
|
39278
|
-
return true;
|
|
39279
|
-
}
|
|
39280
|
-
async function saveQualityLoopPreference(enabled) {
|
|
39281
|
-
try {
|
|
39282
|
-
let config = {};
|
|
39283
|
-
try {
|
|
39284
|
-
const content = await fs35__default.readFile(CONFIG_PATHS.config, "utf-8");
|
|
39285
|
-
config = JSON.parse(content);
|
|
39286
|
-
} catch {
|
|
39287
|
-
}
|
|
39288
|
-
config.qualityLoop = enabled;
|
|
39289
|
-
await fs35__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2) + "\n");
|
|
39290
|
-
} catch {
|
|
39291
|
-
}
|
|
39292
|
-
}
|
|
39293
|
-
function parseQualityLoopReport(content) {
|
|
39294
|
-
const marker = "QUALITY_LOOP_REPORT";
|
|
39295
|
-
const idx = content.indexOf(marker);
|
|
39296
|
-
if (idx === -1) return null;
|
|
39297
|
-
const block = content.slice(idx);
|
|
39298
|
-
const getField = (name) => {
|
|
39299
|
-
const match = block.match(new RegExp(`${name}:\\s*(.+)`));
|
|
39300
|
-
return match?.[1]?.trim();
|
|
39301
|
-
};
|
|
39302
|
-
const scoreHistoryRaw = getField("score_history");
|
|
39303
|
-
if (!scoreHistoryRaw) return null;
|
|
39304
|
-
const scores = scoreHistoryRaw.replace(/[[\]]/g, "").split(",").map((s) => parseFloat(s.trim())).filter((n) => !isNaN(n));
|
|
39305
|
-
if (scores.length === 0) return null;
|
|
39306
|
-
const testsPassed = parseInt(getField("tests_passed") ?? "", 10);
|
|
39307
|
-
const testsTotal = parseInt(getField("tests_total") ?? "", 10);
|
|
39308
|
-
const coverage = parseInt(getField("coverage") ?? "", 10);
|
|
39309
|
-
const security = parseInt(getField("security") ?? "", 10);
|
|
39310
|
-
const iterations = parseInt(getField("iterations") ?? "", 10) || scores.length;
|
|
39311
|
-
const converged = getField("converged") === "true";
|
|
39312
|
-
return {
|
|
39313
|
-
converged,
|
|
39314
|
-
scoreHistory: scores,
|
|
39315
|
-
finalScore: scores[scores.length - 1] ?? 0,
|
|
39316
|
-
iterations,
|
|
39317
|
-
testsPassed: isNaN(testsPassed) ? void 0 : testsPassed,
|
|
39318
|
-
testsTotal: isNaN(testsTotal) ? void 0 : testsTotal,
|
|
39319
|
-
coverage: isNaN(coverage) ? void 0 : coverage,
|
|
39320
|
-
securityScore: isNaN(security) ? void 0 : security
|
|
39321
|
-
};
|
|
39322
|
-
}
|
|
39323
|
-
function getQualityLoopSystemPrompt() {
|
|
39324
|
-
return `
|
|
39325
|
-
## Quality Loop Mode (ACTIVE)
|
|
39326
|
-
|
|
39327
|
-
You are operating in quality loop mode. After implementing code changes, you MUST follow this iteration cycle:
|
|
39328
|
-
|
|
39329
|
-
1. **Implement** the requested changes (code + tests)
|
|
39330
|
-
2. **Run tests** using the run_tests or bash_exec tool
|
|
39331
|
-
3. **Self-review**: Analyze your code against these 12 quality dimensions:
|
|
39332
|
-
- Correctness, Completeness, Robustness, Readability
|
|
39333
|
-
- Maintainability, Complexity, Duplication, Test Coverage
|
|
39334
|
-
- Test Quality, Security, Documentation, Style
|
|
39335
|
-
4. **Score** your implementation 0-100 for each dimension
|
|
39336
|
-
5. **If issues found**: Fix them and go back to step 2
|
|
39337
|
-
6. **If quality is good** (overall \u2265 85 and improving < 2 points): Stop and report
|
|
39338
|
-
|
|
39339
|
-
After completing the cycle, output a quality summary in this exact format:
|
|
39340
|
-
|
|
39341
|
-
\`\`\`
|
|
39342
|
-
QUALITY_LOOP_REPORT
|
|
39343
|
-
score_history: [first_score, ..., final_score]
|
|
39344
|
-
tests_passed: X
|
|
39345
|
-
tests_total: Y
|
|
39346
|
-
coverage: Z
|
|
39347
|
-
security: 100
|
|
39348
|
-
iterations: N
|
|
39349
|
-
converged: true|false
|
|
39350
|
-
\`\`\`
|
|
39351
|
-
|
|
39352
|
-
Key rules:
|
|
39353
|
-
- Always write tests alongside code
|
|
39354
|
-
- Run tests after every change
|
|
39355
|
-
- Minimum 2 iterations before declaring convergence
|
|
39356
|
-
- Maximum 10 iterations
|
|
39357
|
-
- Fix critical issues before moving on
|
|
39358
|
-
- Report honestly - don't inflate scores`;
|
|
39614
|
+
await saveProjectPermissionPreference(
|
|
39615
|
+
"recommendedAllowlistDismissedProjects",
|
|
39616
|
+
session.projectPath,
|
|
39617
|
+
false
|
|
39618
|
+
);
|
|
39619
|
+
console.log(chalk.green(" \u2713 All tool permissions reset."));
|
|
39359
39620
|
}
|
|
39360
|
-
|
|
39361
|
-
// src/cli/repl/commands/quality.ts
|
|
39362
39621
|
var qualityCommand = {
|
|
39363
39622
|
name: "quality",
|
|
39364
39623
|
aliases: ["coco"],
|
|
@@ -40129,11 +40388,11 @@ Response format (JSON only, no prose):
|
|
|
40129
40388
|
}
|
|
40130
40389
|
}
|
|
40131
40390
|
if (!options?.skipConfirmation) {
|
|
40132
|
-
const
|
|
40391
|
+
const confirm23 = await p26.confirm({
|
|
40133
40392
|
message: "Start building with this plan?",
|
|
40134
40393
|
initialValue: true
|
|
40135
40394
|
});
|
|
40136
|
-
if (p26.isCancel(
|
|
40395
|
+
if (p26.isCancel(confirm23) || !confirm23) {
|
|
40137
40396
|
cancel5("Build cancelled.");
|
|
40138
40397
|
}
|
|
40139
40398
|
}
|
|
@@ -41691,8 +41950,8 @@ Examples:
|
|
|
41691
41950
|
recursive: z.boolean().optional().default(false).describe("Delete directories recursively"),
|
|
41692
41951
|
confirm: z.boolean().optional().describe("Must be true to confirm deletion")
|
|
41693
41952
|
}),
|
|
41694
|
-
async execute({ path: filePath, recursive, confirm:
|
|
41695
|
-
if (
|
|
41953
|
+
async execute({ path: filePath, recursive, confirm: confirm23 }) {
|
|
41954
|
+
if (confirm23 !== true) {
|
|
41696
41955
|
throw new ToolError(
|
|
41697
41956
|
"Deletion requires explicit confirmation. Set confirm: true to proceed.",
|
|
41698
41957
|
{ tool: "delete_file" }
|
|
@@ -48675,11 +48934,11 @@ var buildAppCommand = {
|
|
|
48675
48934
|
return false;
|
|
48676
48935
|
}
|
|
48677
48936
|
if (!isAutonomous && !parsed.skipConfirmation) {
|
|
48678
|
-
const
|
|
48937
|
+
const confirm23 = await p26.confirm({
|
|
48679
48938
|
message: `Build "${spec.projectName}" with ${spec.sprints.length} sprints?`,
|
|
48680
48939
|
initialValue: true
|
|
48681
48940
|
});
|
|
48682
|
-
if (p26.isCancel(
|
|
48941
|
+
if (p26.isCancel(confirm23) || !confirm23) {
|
|
48683
48942
|
p26.cancel("Build cancelled.");
|
|
48684
48943
|
return false;
|
|
48685
48944
|
}
|
|
@@ -53640,44 +53899,6 @@ function createIntentRecognizer(config = {}) {
|
|
|
53640
53899
|
// src/cli/repl/index.ts
|
|
53641
53900
|
init_env();
|
|
53642
53901
|
init_allowed_paths();
|
|
53643
|
-
async function getGitContext(projectPath) {
|
|
53644
|
-
const controller = new AbortController();
|
|
53645
|
-
const timer = setTimeout(() => controller.abort(), 3e3);
|
|
53646
|
-
try {
|
|
53647
|
-
const git = simpleGit({ baseDir: projectPath, abort: controller.signal });
|
|
53648
|
-
const status = await git.status();
|
|
53649
|
-
clearTimeout(timer);
|
|
53650
|
-
return {
|
|
53651
|
-
branch: status.current ?? "HEAD",
|
|
53652
|
-
isDirty: !status.isClean(),
|
|
53653
|
-
staged: status.staged.length,
|
|
53654
|
-
modified: status.modified.length,
|
|
53655
|
-
untracked: status.not_added.length,
|
|
53656
|
-
ahead: status.ahead,
|
|
53657
|
-
behind: status.behind
|
|
53658
|
-
};
|
|
53659
|
-
} catch {
|
|
53660
|
-
clearTimeout(timer);
|
|
53661
|
-
return null;
|
|
53662
|
-
}
|
|
53663
|
-
}
|
|
53664
|
-
function formatGitLine(ctx) {
|
|
53665
|
-
const branchColor = ctx.isDirty ? chalk.yellow : chalk.green;
|
|
53666
|
-
const parts = [chalk.dim("\u{1F33F} ") + branchColor(ctx.branch)];
|
|
53667
|
-
const changes = [];
|
|
53668
|
-
if (ctx.staged > 0) changes.push(chalk.green(`+${ctx.staged}`));
|
|
53669
|
-
if (ctx.modified > 0) changes.push(chalk.yellow(`~${ctx.modified}`));
|
|
53670
|
-
if (ctx.untracked > 0) changes.push(chalk.dim(`?${ctx.untracked}`));
|
|
53671
|
-
if (ctx.ahead > 0) changes.push(chalk.cyan(`\u2191${ctx.ahead}`));
|
|
53672
|
-
if (ctx.behind > 0) changes.push(chalk.red(`\u2193${ctx.behind}`));
|
|
53673
|
-
if (changes.length > 0) parts.push(changes.join(" "));
|
|
53674
|
-
return parts.join(" \u2022 ");
|
|
53675
|
-
}
|
|
53676
|
-
function formatGitShort(ctx) {
|
|
53677
|
-
const branch = ctx.isDirty ? chalk.yellow(ctx.branch) : chalk.green(ctx.branch);
|
|
53678
|
-
const dirty = ctx.isDirty ? chalk.yellow(" \u25CF") : "";
|
|
53679
|
-
return chalk.dim("\u{1F33F} ") + branch + dirty;
|
|
53680
|
-
}
|
|
53681
53902
|
|
|
53682
53903
|
// src/cli/repl/status-bar.ts
|
|
53683
53904
|
init_env();
|
|
@@ -54782,85 +55003,7 @@ ${imagePrompts}`.trim() : imagePrompts;
|
|
|
54782
55003
|
process.off("SIGTERM", sigtermHandler);
|
|
54783
55004
|
}
|
|
54784
55005
|
async function printWelcome(session, gitCtx, mcpManager) {
|
|
54785
|
-
|
|
54786
|
-
await trustStore.init();
|
|
54787
|
-
const trustLevel = trustStore.getLevel(session.projectPath);
|
|
54788
|
-
const boxWidth = 41;
|
|
54789
|
-
const innerWidth = boxWidth - 2;
|
|
54790
|
-
const versionText = `v${VERSION}`;
|
|
54791
|
-
const subtitleText = "open source \u2022 corbat.tech";
|
|
54792
|
-
const boxLine = (content) => {
|
|
54793
|
-
const pad = Math.max(0, innerWidth - stringWidth2(content));
|
|
54794
|
-
return chalk.magenta("\u2502") + content + " ".repeat(pad) + chalk.magenta("\u2502");
|
|
54795
|
-
};
|
|
54796
|
-
const titleLeftRaw = " COCO";
|
|
54797
|
-
const titleRightRaw = versionText + " ";
|
|
54798
|
-
const titleLeftStyled = " " + chalk.bold.white("COCO");
|
|
54799
|
-
const titleGap = Math.max(1, innerWidth - stringWidth2(titleLeftRaw) - stringWidth2(titleRightRaw));
|
|
54800
|
-
const titleContent = titleLeftStyled + " ".repeat(titleGap) + chalk.dim(titleRightRaw);
|
|
54801
|
-
const taglineText = "code that converges to quality";
|
|
54802
|
-
const taglineContent = " " + chalk.magenta(taglineText) + " ";
|
|
54803
|
-
const subtitleContent = " " + chalk.dim(subtitleText) + " ";
|
|
54804
|
-
console.log();
|
|
54805
|
-
console.log(chalk.magenta(" \u256D" + "\u2500".repeat(boxWidth - 2) + "\u256E"));
|
|
54806
|
-
console.log(" " + boxLine(titleContent));
|
|
54807
|
-
console.log(" " + boxLine(taglineContent));
|
|
54808
|
-
console.log(" " + boxLine(subtitleContent));
|
|
54809
|
-
console.log(chalk.magenta(" \u2570" + "\u2500".repeat(boxWidth - 2) + "\u256F"));
|
|
54810
|
-
const maxPathLen = 50;
|
|
54811
|
-
let displayPath = session.projectPath;
|
|
54812
|
-
if (displayPath.length > maxPathLen) {
|
|
54813
|
-
displayPath = "..." + displayPath.slice(-maxPathLen + 3);
|
|
54814
|
-
}
|
|
54815
|
-
const lastSep = displayPath.lastIndexOf("/");
|
|
54816
|
-
const parentPath = lastSep > 0 ? displayPath.slice(0, lastSep + 1) : "";
|
|
54817
|
-
const projectName = lastSep > 0 ? displayPath.slice(lastSep + 1) : displayPath;
|
|
54818
|
-
const providerName = session.config.provider.type;
|
|
54819
|
-
const configuredModel = session.config.provider.model?.trim();
|
|
54820
|
-
const modelName = configuredModel && !["default", "none", "null", "undefined"].includes(configuredModel.toLowerCase()) ? configuredModel : getDefaultModel(session.config.provider.type);
|
|
54821
|
-
const trustText = trustLevel === "full" ? "full" : trustLevel === "write" ? "write" : trustLevel === "read" ? "read" : "";
|
|
54822
|
-
console.log();
|
|
54823
|
-
console.log(chalk.dim(` \u{1F4C1} ${parentPath}`) + chalk.magenta.bold(projectName));
|
|
54824
|
-
console.log(
|
|
54825
|
-
chalk.dim(` \u{1F916} ${providerName}/`) + chalk.magenta(modelName) + (trustText ? chalk.dim(` \u2022 \u{1F510} ${trustText}`) : "")
|
|
54826
|
-
);
|
|
54827
|
-
if (gitCtx) {
|
|
54828
|
-
console.log(` ${formatGitLine(gitCtx)}`);
|
|
54829
|
-
}
|
|
54830
|
-
const cocoStatus = isQualityLoop() ? chalk.magenta(" \u{1F504} quality mode: ") + chalk.green.bold("on") + chalk.dim(" \u2014 iterates until quality \u2265 85. /quality to disable") : chalk.dim(" \u{1F4A1} /quality on \u2014 enable auto-test & quality iteration");
|
|
54831
|
-
console.log(cocoStatus);
|
|
54832
|
-
const skillTotal = session.skillRegistry?.size ?? 0;
|
|
54833
|
-
const mcpServers = mcpManager?.getConnectedServers() ?? [];
|
|
54834
|
-
const hasSomething = skillTotal > 0 || mcpServers.length > 0;
|
|
54835
|
-
if (hasSomething) {
|
|
54836
|
-
if (skillTotal > 0) {
|
|
54837
|
-
const allMeta = session.skillRegistry.getAllMetadata();
|
|
54838
|
-
const builtinCount = allMeta.filter((s) => s.scope === "builtin").length;
|
|
54839
|
-
const projectCount = skillTotal - builtinCount;
|
|
54840
|
-
const parts = [];
|
|
54841
|
-
if (builtinCount > 0) parts.push(`${builtinCount} builtin`);
|
|
54842
|
-
if (projectCount > 0) parts.push(`${projectCount} project`);
|
|
54843
|
-
const detail = parts.length > 0 ? ` (${parts.join(" \xB7 ")})` : "";
|
|
54844
|
-
console.log(chalk.green(" \u2713") + chalk.dim(` Skills: ${skillTotal} loaded${detail}`));
|
|
54845
|
-
} else {
|
|
54846
|
-
console.log(chalk.dim(" \xB7 Skills: none loaded"));
|
|
54847
|
-
}
|
|
54848
|
-
if (mcpServers.length > 0) {
|
|
54849
|
-
const names = mcpServers.join(", ");
|
|
54850
|
-
console.log(
|
|
54851
|
-
chalk.green(" \u2713") + chalk.dim(
|
|
54852
|
-
` MCP: ${names} (${mcpServers.length} server${mcpServers.length === 1 ? "" : "s"} active)`
|
|
54853
|
-
)
|
|
54854
|
-
);
|
|
54855
|
-
}
|
|
54856
|
-
}
|
|
54857
|
-
console.log();
|
|
54858
|
-
console.log(
|
|
54859
|
-
chalk.dim(" Type your request or ") + chalk.magenta("/help") + chalk.dim(" for commands")
|
|
54860
|
-
);
|
|
54861
|
-
const pasteHint = process.platform === "darwin" ? chalk.dim(" \u{1F4CB} \u2318V paste text \u2022 \u2303V paste image") : chalk.dim(" \u{1F4CB} Ctrl+V paste image from clipboard");
|
|
54862
|
-
console.log(pasteHint);
|
|
54863
|
-
console.log();
|
|
55006
|
+
await renderStartupPanel(session, gitCtx, mcpManager?.getConnectedServers() ?? []);
|
|
54864
55007
|
}
|
|
54865
55008
|
async function checkProjectTrust(projectPath) {
|
|
54866
55009
|
const trustStore = createTrustStore();
|