@corbat-tech/coco 2.2.5 → 2.4.0
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 -3
- package/dist/cli/index.js +382 -98
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +34 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -4001,8 +4001,10 @@ var init_pricing = __esm({
|
|
|
4001
4001
|
deepseek: { inputPerMillion: 0.14, outputPerMillion: 0.28, contextWindow: 128e3 },
|
|
4002
4002
|
// Very cheap
|
|
4003
4003
|
together: { inputPerMillion: 0.2, outputPerMillion: 0.2, contextWindow: 32768 },
|
|
4004
|
-
huggingface: { inputPerMillion: 0, outputPerMillion: 0, contextWindow: 32768 }
|
|
4004
|
+
huggingface: { inputPerMillion: 0, outputPerMillion: 0, contextWindow: 32768 },
|
|
4005
4005
|
// Free tier
|
|
4006
|
+
qwen: { inputPerMillion: 0.3, outputPerMillion: 1.2, contextWindow: 131072 }
|
|
4007
|
+
// qwen-coder-plus pricing
|
|
4006
4008
|
};
|
|
4007
4009
|
}
|
|
4008
4010
|
});
|
|
@@ -4496,6 +4498,8 @@ function getApiKey(provider) {
|
|
|
4496
4498
|
return process.env["TOGETHER_API_KEY"];
|
|
4497
4499
|
case "huggingface":
|
|
4498
4500
|
return process.env["HF_TOKEN"] ?? process.env["HUGGINGFACE_API_KEY"];
|
|
4501
|
+
case "qwen":
|
|
4502
|
+
return process.env["DASHSCOPE_API_KEY"] ?? process.env["QWEN_API_KEY"];
|
|
4499
4503
|
default:
|
|
4500
4504
|
return void 0;
|
|
4501
4505
|
}
|
|
@@ -4528,6 +4532,8 @@ function getBaseUrl(provider) {
|
|
|
4528
4532
|
return process.env["TOGETHER_BASE_URL"] ?? "https://api.together.xyz/v1";
|
|
4529
4533
|
case "huggingface":
|
|
4530
4534
|
return process.env["HF_BASE_URL"] ?? "https://api-inference.huggingface.co/v1";
|
|
4535
|
+
case "qwen":
|
|
4536
|
+
return process.env["DASHSCOPE_BASE_URL"] ?? "https://dashscope.aliyuncs.com/compatible-mode/v1";
|
|
4531
4537
|
default:
|
|
4532
4538
|
return void 0;
|
|
4533
4539
|
}
|
|
@@ -4562,6 +4568,8 @@ function getDefaultModel(provider) {
|
|
|
4562
4568
|
return process.env["TOGETHER_MODEL"] ?? "Qwen/Qwen2.5-Coder-32B-Instruct";
|
|
4563
4569
|
case "huggingface":
|
|
4564
4570
|
return process.env["HF_MODEL"] ?? "Qwen/Qwen2.5-Coder-32B-Instruct";
|
|
4571
|
+
case "qwen":
|
|
4572
|
+
return process.env["QWEN_MODEL"] ?? "qwen-coder-plus";
|
|
4565
4573
|
default:
|
|
4566
4574
|
return "gpt-5.3-codex";
|
|
4567
4575
|
}
|
|
@@ -4669,7 +4677,8 @@ var init_env = __esm({
|
|
|
4669
4677
|
"mistral",
|
|
4670
4678
|
"deepseek",
|
|
4671
4679
|
"together",
|
|
4672
|
-
"huggingface"
|
|
4680
|
+
"huggingface",
|
|
4681
|
+
"qwen"
|
|
4673
4682
|
];
|
|
4674
4683
|
cachedPreferences = null;
|
|
4675
4684
|
env = {
|
|
@@ -4780,6 +4789,10 @@ async function createProvider(type, config = {}) {
|
|
|
4780
4789
|
provider = new OpenAIProvider("huggingface", "HuggingFace Inference");
|
|
4781
4790
|
mergedConfig.baseUrl = mergedConfig.baseUrl ?? "https://api-inference.huggingface.co/v1";
|
|
4782
4791
|
break;
|
|
4792
|
+
case "qwen":
|
|
4793
|
+
provider = new OpenAIProvider("qwen", "Alibaba Qwen");
|
|
4794
|
+
mergedConfig.baseUrl = mergedConfig.baseUrl ?? "https://dashscope.aliyuncs.com/compatible-mode/v1";
|
|
4795
|
+
break;
|
|
4783
4796
|
default:
|
|
4784
4797
|
throw new ProviderError(`Unknown provider type: ${type}`, {
|
|
4785
4798
|
provider: type
|
|
@@ -4812,6 +4825,7 @@ function listProviders() {
|
|
|
4812
4825
|
{ id: "deepseek", name: "DeepSeek", configured: !!getApiKey("deepseek") },
|
|
4813
4826
|
{ id: "together", name: "Together AI", configured: !!getApiKey("together") },
|
|
4814
4827
|
{ id: "huggingface", name: "HuggingFace Inference", configured: !!getApiKey("huggingface") },
|
|
4828
|
+
{ id: "qwen", name: "Alibaba Qwen (DashScope)", configured: !!getApiKey("qwen") },
|
|
4815
4829
|
{ id: "lmstudio", name: "LM Studio (Local)", configured: true },
|
|
4816
4830
|
{ id: "ollama", name: "Ollama (Local)", configured: true }
|
|
4817
4831
|
];
|
|
@@ -24175,6 +24189,70 @@ var PROVIDER_DEFINITIONS = {
|
|
|
24175
24189
|
}
|
|
24176
24190
|
]
|
|
24177
24191
|
},
|
|
24192
|
+
// Alibaba Qwen - DashScope API (OpenAI-compatible)
|
|
24193
|
+
qwen: {
|
|
24194
|
+
id: "qwen",
|
|
24195
|
+
name: "Alibaba Qwen",
|
|
24196
|
+
emoji: "\u{1F7E6}",
|
|
24197
|
+
description: "Qwen models via Alibaba DashScope \u2014 strong coding at low cost",
|
|
24198
|
+
envVar: "DASHSCOPE_API_KEY",
|
|
24199
|
+
apiKeyUrl: "https://dashscope.aliyuncs.com",
|
|
24200
|
+
docsUrl: "https://help.aliyun.com/zh/model-studio/developer-reference/",
|
|
24201
|
+
baseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
24202
|
+
supportsCustomModels: true,
|
|
24203
|
+
openaiCompatible: true,
|
|
24204
|
+
paymentType: "api",
|
|
24205
|
+
features: {
|
|
24206
|
+
streaming: true,
|
|
24207
|
+
functionCalling: true,
|
|
24208
|
+
vision: true
|
|
24209
|
+
},
|
|
24210
|
+
models: [
|
|
24211
|
+
{
|
|
24212
|
+
id: "qwen-coder-plus",
|
|
24213
|
+
name: "Qwen Coder Plus",
|
|
24214
|
+
description: "Best coding model \u2014 Qwen3 based, 131K context",
|
|
24215
|
+
contextWindow: 131072,
|
|
24216
|
+
maxOutputTokens: 8192,
|
|
24217
|
+
recommended: true
|
|
24218
|
+
},
|
|
24219
|
+
{
|
|
24220
|
+
id: "qwen-max",
|
|
24221
|
+
name: "Qwen Max",
|
|
24222
|
+
description: "Most capable general model \u2014 32K context",
|
|
24223
|
+
contextWindow: 32768,
|
|
24224
|
+
maxOutputTokens: 8192
|
|
24225
|
+
},
|
|
24226
|
+
{
|
|
24227
|
+
id: "qwen-plus",
|
|
24228
|
+
name: "Qwen Plus",
|
|
24229
|
+
description: "Good balance of speed and quality \u2014 131K context",
|
|
24230
|
+
contextWindow: 131072,
|
|
24231
|
+
maxOutputTokens: 8192
|
|
24232
|
+
},
|
|
24233
|
+
{
|
|
24234
|
+
id: "qwen-turbo",
|
|
24235
|
+
name: "Qwen Turbo",
|
|
24236
|
+
description: "Fastest and cheapest \u2014 1M context",
|
|
24237
|
+
contextWindow: 1e6,
|
|
24238
|
+
maxOutputTokens: 8192
|
|
24239
|
+
},
|
|
24240
|
+
{
|
|
24241
|
+
id: "qwen2.5-coder-32b-instruct",
|
|
24242
|
+
name: "Qwen 2.5 Coder 32B",
|
|
24243
|
+
description: "Open weights coding model \u2014 32K context",
|
|
24244
|
+
contextWindow: 32768,
|
|
24245
|
+
maxOutputTokens: 8192
|
|
24246
|
+
},
|
|
24247
|
+
{
|
|
24248
|
+
id: "qwq-plus",
|
|
24249
|
+
name: "QwQ Plus",
|
|
24250
|
+
description: "Reasoning model \u2014 chain-of-thought, 131K context",
|
|
24251
|
+
contextWindow: 131072,
|
|
24252
|
+
maxOutputTokens: 8192
|
|
24253
|
+
}
|
|
24254
|
+
]
|
|
24255
|
+
},
|
|
24178
24256
|
// HuggingFace Inference - Free tier for open models
|
|
24179
24257
|
huggingface: {
|
|
24180
24258
|
id: "huggingface",
|
|
@@ -25659,6 +25737,120 @@ async function loadAgentConfig(projectPath) {
|
|
|
25659
25737
|
}
|
|
25660
25738
|
}
|
|
25661
25739
|
|
|
25740
|
+
// src/swarm/complexity-classifier.ts
|
|
25741
|
+
var AGENT_ROSTERS = {
|
|
25742
|
+
trivial: ["tdd-developer"],
|
|
25743
|
+
simple: ["tdd-developer", "qa"],
|
|
25744
|
+
moderate: ["tdd-developer", "qa", "architect"],
|
|
25745
|
+
complex: ["tdd-developer", "qa", "architect", "security-auditor", "external-reviewer"]
|
|
25746
|
+
};
|
|
25747
|
+
var LEVEL_THRESHOLDS = [
|
|
25748
|
+
[3, "trivial"],
|
|
25749
|
+
[5, "simple"],
|
|
25750
|
+
[7, "moderate"],
|
|
25751
|
+
[10, "complex"]
|
|
25752
|
+
];
|
|
25753
|
+
var COMPLEX_KEYWORDS = ["auth", "security", "migration", "refactor"];
|
|
25754
|
+
var SIMPLE_KEYWORDS = ["fix", "typo", "style"];
|
|
25755
|
+
function scoreToLevel(score) {
|
|
25756
|
+
for (const [threshold, level] of LEVEL_THRESHOLDS) {
|
|
25757
|
+
if (score <= threshold) return level;
|
|
25758
|
+
}
|
|
25759
|
+
return "complex";
|
|
25760
|
+
}
|
|
25761
|
+
function classifyFeatureHeuristic(feature) {
|
|
25762
|
+
const description = feature.description ?? "";
|
|
25763
|
+
const criteria = feature.acceptanceCriteria ?? [];
|
|
25764
|
+
const deps = feature.dependencies ?? [];
|
|
25765
|
+
const wordCount = description.split(/\s+/).filter(Boolean).length;
|
|
25766
|
+
const lowerDesc = description.toLowerCase();
|
|
25767
|
+
let score = 1;
|
|
25768
|
+
const reasons = [
|
|
25769
|
+
`words=${wordCount}`,
|
|
25770
|
+
`criteria=${criteria.length}`,
|
|
25771
|
+
`deps=${deps.length}`
|
|
25772
|
+
];
|
|
25773
|
+
if (wordCount > 50) score += 3;
|
|
25774
|
+
else if (wordCount > 20) score += 2;
|
|
25775
|
+
else if (wordCount > 5) score += 1;
|
|
25776
|
+
if (criteria.length >= 4) score += 3;
|
|
25777
|
+
else if (criteria.length >= 2) score += 2;
|
|
25778
|
+
else if (criteria.length >= 1) score += 1;
|
|
25779
|
+
if (deps.length >= 3) score += 8;
|
|
25780
|
+
else if (deps.length >= 1) score += 4;
|
|
25781
|
+
const hasComplexKeyword = COMPLEX_KEYWORDS.some((kw) => lowerDesc.includes(kw));
|
|
25782
|
+
if (hasComplexKeyword) {
|
|
25783
|
+
score += 8;
|
|
25784
|
+
reasons.push("complex-keywords");
|
|
25785
|
+
}
|
|
25786
|
+
const hasSimpleKeyword = SIMPLE_KEYWORDS.some((kw) => lowerDesc.includes(kw));
|
|
25787
|
+
if (hasSimpleKeyword) {
|
|
25788
|
+
score -= 2;
|
|
25789
|
+
reasons.push("simple-keywords");
|
|
25790
|
+
}
|
|
25791
|
+
score = Math.max(1, Math.min(10, score));
|
|
25792
|
+
const level = scoreToLevel(score);
|
|
25793
|
+
return {
|
|
25794
|
+
score,
|
|
25795
|
+
level,
|
|
25796
|
+
agents: AGENT_ROSTERS[level],
|
|
25797
|
+
reasoning: `Heuristic score ${score}: ${reasons.join(", ")}`
|
|
25798
|
+
};
|
|
25799
|
+
}
|
|
25800
|
+
async function classifyFeatureComplexity(feature, provider) {
|
|
25801
|
+
const criteriaCount = (feature.acceptanceCriteria ?? []).length;
|
|
25802
|
+
const depsCount = (feature.dependencies ?? []).length;
|
|
25803
|
+
const userMessage = `Rate the complexity of this software feature on a scale of 1-10.
|
|
25804
|
+
|
|
25805
|
+
Feature: ${feature.name}
|
|
25806
|
+
Description: ${feature.description ?? ""}
|
|
25807
|
+
Acceptance Criteria count: ${criteriaCount}
|
|
25808
|
+
Dependencies count: ${depsCount}
|
|
25809
|
+
|
|
25810
|
+
Complexity guidelines:
|
|
25811
|
+
- 1-3 (trivial): Fix, typo, style change \u2014 short description, 0-1 criteria, 0 deps
|
|
25812
|
+
- 4-5 (simple): Small enhancement \u2014 medium description, 2-3 criteria
|
|
25813
|
+
- 6-7 (moderate): Feature with logic \u2014 longer description, 4+ criteria or 1+ deps
|
|
25814
|
+
- 8-10 (complex): Security, auth, migration, heavy refactor \u2014 many deps or critical path
|
|
25815
|
+
|
|
25816
|
+
Return only JSON: { "score": <number 1-10>, "reasoning": "<brief explanation>" }`;
|
|
25817
|
+
try {
|
|
25818
|
+
const response = await provider.chat([{ role: "user", content: userMessage }], {
|
|
25819
|
+
maxTokens: 256,
|
|
25820
|
+
temperature: 0.2
|
|
25821
|
+
});
|
|
25822
|
+
const json2 = extractJsonScore(response.content);
|
|
25823
|
+
if (json2 !== null && typeof json2.score === "number") {
|
|
25824
|
+
const score = Math.max(1, Math.min(10, Math.round(json2.score)));
|
|
25825
|
+
const level = scoreToLevel(score);
|
|
25826
|
+
return {
|
|
25827
|
+
score,
|
|
25828
|
+
level,
|
|
25829
|
+
agents: AGENT_ROSTERS[level],
|
|
25830
|
+
reasoning: json2.reasoning ?? `LLM score: ${score}`
|
|
25831
|
+
};
|
|
25832
|
+
}
|
|
25833
|
+
} catch {
|
|
25834
|
+
}
|
|
25835
|
+
return classifyFeatureHeuristic(feature);
|
|
25836
|
+
}
|
|
25837
|
+
function extractJsonScore(text13) {
|
|
25838
|
+
try {
|
|
25839
|
+
const stripped = text13.replace(/^```(?:json)?\s*\n?/, "").replace(/\n?```\s*$/, "").trim();
|
|
25840
|
+
return JSON.parse(stripped);
|
|
25841
|
+
} catch {
|
|
25842
|
+
const match = text13.match(/\{[\s\S]*?\}/);
|
|
25843
|
+
if (match) {
|
|
25844
|
+
try {
|
|
25845
|
+
return JSON.parse(match[0]);
|
|
25846
|
+
} catch {
|
|
25847
|
+
return null;
|
|
25848
|
+
}
|
|
25849
|
+
}
|
|
25850
|
+
return null;
|
|
25851
|
+
}
|
|
25852
|
+
}
|
|
25853
|
+
|
|
25662
25854
|
// src/swarm/task-board.ts
|
|
25663
25855
|
async function createBoard(projectPath, spec) {
|
|
25664
25856
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -26199,6 +26391,12 @@ var AGENT_DEFINITIONS = {
|
|
|
26199
26391
|
};
|
|
26200
26392
|
|
|
26201
26393
|
// src/swarm/lifecycle.ts
|
|
26394
|
+
var COMPLEXITY_RANK = {
|
|
26395
|
+
trivial: 0,
|
|
26396
|
+
simple: 1,
|
|
26397
|
+
moderate: 2,
|
|
26398
|
+
complex: 3
|
|
26399
|
+
};
|
|
26202
26400
|
async function runSwarmLifecycle(options) {
|
|
26203
26401
|
const ctx = {
|
|
26204
26402
|
options,
|
|
@@ -26415,17 +26613,33 @@ async function processFeature(ctx, feature) {
|
|
|
26415
26613
|
);
|
|
26416
26614
|
continue;
|
|
26417
26615
|
}
|
|
26616
|
+
const classified = ctx.options.skipComplexityCheck ? {
|
|
26617
|
+
score: 10,
|
|
26618
|
+
level: "complex",
|
|
26619
|
+
agents: AGENT_ROSTERS.complex} : await classifyFeatureComplexity(feature, provider);
|
|
26620
|
+
const threshold = ctx.options.complexityThreshold;
|
|
26621
|
+
const roster = threshold !== void 0 && COMPLEXITY_RANK[classified.level] >= COMPLEXITY_RANK[threshold] ? {
|
|
26622
|
+
score: 10,
|
|
26623
|
+
level: "complex",
|
|
26624
|
+
agents: AGENT_ROSTERS.complex,
|
|
26625
|
+
reasoning: `complexityThreshold=${threshold} (feature classified as ${classified.level})`
|
|
26626
|
+
} : classified;
|
|
26627
|
+
progress(
|
|
26628
|
+
ctx.options,
|
|
26629
|
+
"feature_loop",
|
|
26630
|
+
`Feature: ${feature.name} \u2014 complexity=${roster.level} (score=${roster.score}) roster=[${roster.agents.join(",")}]`
|
|
26631
|
+
);
|
|
26418
26632
|
const [archReview, secReview, qaReview] = await Promise.all([
|
|
26419
|
-
runArchReview(feature, provider, agentConfig.architect),
|
|
26420
|
-
runSecurityAudit(feature, provider, agentConfig["security-auditor"]),
|
|
26421
|
-
runQAReview(feature, provider, agentConfig.qa)
|
|
26633
|
+
roster.agents.includes("architect") ? runArchReview(feature, provider, agentConfig.architect) : Promise.resolve(void 0),
|
|
26634
|
+
roster.agents.includes("security-auditor") ? runSecurityAudit(feature, provider, agentConfig["security-auditor"]) : Promise.resolve(void 0),
|
|
26635
|
+
roster.agents.includes("qa") ? runQAReview(feature, provider, agentConfig.qa) : Promise.resolve(void 0)
|
|
26422
26636
|
]);
|
|
26423
|
-
const extReview = await runExternalReviewer(
|
|
26637
|
+
const extReview = roster.agents.includes("external-reviewer") ? await runExternalReviewer(
|
|
26424
26638
|
feature,
|
|
26425
26639
|
{ arch: archReview, security: secReview, qa: qaReview },
|
|
26426
26640
|
provider,
|
|
26427
26641
|
agentConfig["external-reviewer"]
|
|
26428
|
-
);
|
|
26642
|
+
) : synthesizeLocalReviews({ arch: archReview, security: secReview, qa: qaReview });
|
|
26429
26643
|
lastReviewScore = extReview.score;
|
|
26430
26644
|
await emitGate(
|
|
26431
26645
|
projectPath,
|
|
@@ -26720,13 +26934,17 @@ Return JSON with: { "score": number, "issues": ["..."], "summary": "..." }`;
|
|
|
26720
26934
|
}
|
|
26721
26935
|
async function runExternalReviewer(feature, reviews, provider, _config) {
|
|
26722
26936
|
const prompt = AGENT_DEFINITIONS["external-reviewer"].systemPrompt;
|
|
26937
|
+
const reviewLines = [
|
|
26938
|
+
reviews.arch ? `Architecture review: ${JSON.stringify(reviews.arch)}` : null,
|
|
26939
|
+
reviews.security ? `Security audit: ${JSON.stringify(reviews.security)}` : null,
|
|
26940
|
+
reviews.qa ? `QA review: ${JSON.stringify(reviews.qa)}` : null
|
|
26941
|
+
].filter(Boolean).join("\n");
|
|
26723
26942
|
const userMessage = `Synthesize these reviews for feature: ${feature.name}
|
|
26724
26943
|
|
|
26725
|
-
|
|
26726
|
-
Security audit: ${JSON.stringify(reviews.security)}
|
|
26727
|
-
QA review: ${JSON.stringify(reviews.qa)}
|
|
26944
|
+
${reviewLines}
|
|
26728
26945
|
|
|
26729
26946
|
Return JSON with: { "verdict": "APPROVE|REQUEST_CHANGES|REJECT", "score": number, "blockers": ["..."], "summary": "..." }`;
|
|
26947
|
+
const fallback = synthesizeLocalReviews(reviews);
|
|
26730
26948
|
try {
|
|
26731
26949
|
const response = await provider.chat([{ role: "user", content: userMessage }], {
|
|
26732
26950
|
system: prompt,
|
|
@@ -26734,27 +26952,24 @@ Return JSON with: { "verdict": "APPROVE|REQUEST_CHANGES|REJECT", "score": number
|
|
|
26734
26952
|
temperature: 0.4
|
|
26735
26953
|
});
|
|
26736
26954
|
const json2 = extractJson(response.content);
|
|
26737
|
-
|
|
26738
|
-
(reviews.arch.score + reviews.security.score + reviews.qa.score) / 3
|
|
26739
|
-
);
|
|
26740
|
-
return json2 ?? {
|
|
26741
|
-
verdict: avgScore >= 85 ? "APPROVE" : "REQUEST_CHANGES",
|
|
26742
|
-
score: avgScore,
|
|
26743
|
-
blockers: [],
|
|
26744
|
-
summary: `Synthesized review score: ${avgScore}`
|
|
26745
|
-
};
|
|
26955
|
+
return json2 ?? fallback;
|
|
26746
26956
|
} catch {
|
|
26747
|
-
|
|
26748
|
-
(reviews.arch.score + reviews.security.score + reviews.qa.score) / 3
|
|
26749
|
-
);
|
|
26750
|
-
return {
|
|
26751
|
-
verdict: avgScore >= 85 ? "APPROVE" : "REQUEST_CHANGES",
|
|
26752
|
-
score: avgScore,
|
|
26753
|
-
blockers: [],
|
|
26754
|
-
summary: `External review score: ${avgScore}`
|
|
26755
|
-
};
|
|
26957
|
+
return fallback;
|
|
26756
26958
|
}
|
|
26757
26959
|
}
|
|
26960
|
+
function synthesizeLocalReviews(reviews) {
|
|
26961
|
+
const scores = [reviews.arch?.score, reviews.security?.score, reviews.qa?.score].filter(
|
|
26962
|
+
(s) => s !== void 0
|
|
26963
|
+
);
|
|
26964
|
+
const avgScore = scores.length > 0 ? Math.round(scores.reduce((a, b) => a + b, 0) / scores.length) : 85;
|
|
26965
|
+
const reviewCount = scores.length;
|
|
26966
|
+
return {
|
|
26967
|
+
verdict: avgScore >= 85 ? "APPROVE" : "REQUEST_CHANGES",
|
|
26968
|
+
score: avgScore,
|
|
26969
|
+
blockers: [],
|
|
26970
|
+
summary: `Local synthesis (${reviewCount} review${reviewCount !== 1 ? "s" : ""}): score ${avgScore}`
|
|
26971
|
+
};
|
|
26972
|
+
}
|
|
26758
26973
|
async function runIntegratorAgent(spec, featureResults, provider, _config) {
|
|
26759
26974
|
const prompt = AGENT_DEFINITIONS.integrator.systemPrompt;
|
|
26760
26975
|
const results = Array.from(featureResults.values());
|
|
@@ -26810,14 +27025,20 @@ async function emitGate(projectPath, gate, passed, reason) {
|
|
|
26810
27025
|
function topologicalSort(features) {
|
|
26811
27026
|
const sorted = [];
|
|
26812
27027
|
const visited = /* @__PURE__ */ new Set();
|
|
27028
|
+
const inProgress = /* @__PURE__ */ new Set();
|
|
26813
27029
|
const featureMap = new Map(features.map((f) => [f.id, f]));
|
|
26814
27030
|
function visit(featureId) {
|
|
26815
27031
|
if (visited.has(featureId)) return;
|
|
27032
|
+
if (inProgress.has(featureId)) {
|
|
27033
|
+
throw new Error(`Circular dependency detected involving feature "${featureId}"`);
|
|
27034
|
+
}
|
|
26816
27035
|
const feature = featureMap.get(featureId);
|
|
26817
27036
|
if (!feature) return;
|
|
27037
|
+
inProgress.add(featureId);
|
|
26818
27038
|
for (const depId of feature.dependencies) {
|
|
26819
27039
|
visit(depId);
|
|
26820
27040
|
}
|
|
27041
|
+
inProgress.delete(featureId);
|
|
26821
27042
|
visited.add(featureId);
|
|
26822
27043
|
sorted.push(feature);
|
|
26823
27044
|
}
|
|
@@ -26984,7 +27205,7 @@ var helpCommand = {
|
|
|
26984
27205
|
title: "Quality Mode",
|
|
26985
27206
|
commands: [
|
|
26986
27207
|
{
|
|
26987
|
-
cmd: "/
|
|
27208
|
+
cmd: "/quality [on|off]",
|
|
26988
27209
|
desc: "Auto-test, self-review, iterate until quality \u2265 85/100",
|
|
26989
27210
|
highlight: true
|
|
26990
27211
|
}
|
|
@@ -28582,7 +28803,6 @@ async function selectProviderInteractively(providers, currentProviderId) {
|
|
|
28582
28803
|
let lastTotalLines = 0;
|
|
28583
28804
|
const clearPrevious = () => {
|
|
28584
28805
|
if (lastTotalLines === 0) return;
|
|
28585
|
-
process.stdout.write("\x1B[2K\r");
|
|
28586
28806
|
for (let i = 0; i < lastTotalLines; i++) {
|
|
28587
28807
|
process.stdout.write("\x1B[1A\x1B[2K");
|
|
28588
28808
|
}
|
|
@@ -28725,6 +28945,7 @@ async function switchProvider(initialProvider, session) {
|
|
|
28725
28945
|
const userFacingProviderId = initialProvider.id;
|
|
28726
28946
|
let internalProviderId = initialProvider.id;
|
|
28727
28947
|
let selectedAuthMethod = "apikey";
|
|
28948
|
+
let newApiKeyForSaving = null;
|
|
28728
28949
|
if (newProvider.id === "lmstudio" || newProvider.id === "ollama") {
|
|
28729
28950
|
const result = newProvider.id === "ollama" ? await setupOllamaProvider() : await setupLMStudioProvider();
|
|
28730
28951
|
if (!result) {
|
|
@@ -28870,6 +29091,7 @@ Using existing API key...`));
|
|
|
28870
29091
|
}
|
|
28871
29092
|
process.env[newProvider.envVar] = key;
|
|
28872
29093
|
selectedAuthMethod = "apikey";
|
|
29094
|
+
newApiKeyForSaving = key;
|
|
28873
29095
|
}
|
|
28874
29096
|
} else if (authChoice === "remove") {
|
|
28875
29097
|
const removeOptions = [];
|
|
@@ -28927,6 +29149,7 @@ ${newProvider.emoji} ${newProvider.name} is not configured.`));
|
|
|
28927
29149
|
}
|
|
28928
29150
|
process.env[newProvider.envVar] = key;
|
|
28929
29151
|
selectedAuthMethod = "apikey";
|
|
29152
|
+
newApiKeyForSaving = key;
|
|
28930
29153
|
}
|
|
28931
29154
|
}
|
|
28932
29155
|
const recommendedModel = getRecommendedModel(newProvider.id);
|
|
@@ -28948,16 +29171,25 @@ ${newProvider.emoji} ${newProvider.name} is not configured.`));
|
|
|
28948
29171
|
spinner19.stop(chalk25.green("Connected!"));
|
|
28949
29172
|
session.config.provider.type = userFacingProviderId;
|
|
28950
29173
|
session.config.provider.model = newModel;
|
|
28951
|
-
|
|
28952
|
-
|
|
28953
|
-
|
|
28954
|
-
|
|
28955
|
-
|
|
29174
|
+
if (newApiKeyForSaving) {
|
|
29175
|
+
await saveConfiguration({
|
|
29176
|
+
type: userFacingProviderId,
|
|
29177
|
+
model: newModel,
|
|
29178
|
+
apiKey: newApiKeyForSaving
|
|
29179
|
+
});
|
|
29180
|
+
} else {
|
|
29181
|
+
await saveProviderPreference(
|
|
29182
|
+
userFacingProviderId,
|
|
29183
|
+
newModel,
|
|
29184
|
+
selectedAuthMethod
|
|
29185
|
+
);
|
|
29186
|
+
}
|
|
28956
29187
|
console.log(chalk25.green(`
|
|
28957
29188
|
\u2713 Switched to ${newProvider.emoji} ${newProvider.name}`));
|
|
28958
29189
|
console.log(chalk25.dim(` Model: ${newModel}`));
|
|
28959
29190
|
if (selectedAuthMethod === "oauth") {
|
|
28960
|
-
|
|
29191
|
+
const authLabel = newProvider.id === "gemini" ? "Google account (OAuth)" : "ChatGPT subscription (OAuth)";
|
|
29192
|
+
console.log(chalk25.dim(` Auth: ${authLabel}`));
|
|
28961
29193
|
}
|
|
28962
29194
|
console.log(chalk25.dim(` Use /model to change models
|
|
28963
29195
|
`));
|
|
@@ -32654,7 +32886,6 @@ var RECOMMENDED_PROJECT = [
|
|
|
32654
32886
|
"copy_file",
|
|
32655
32887
|
"move_file",
|
|
32656
32888
|
"git_add",
|
|
32657
|
-
"git_commit",
|
|
32658
32889
|
"run_tests",
|
|
32659
32890
|
"run_test_file",
|
|
32660
32891
|
"run_script",
|
|
@@ -32673,12 +32904,28 @@ var RECOMMENDED_PROJECT = [
|
|
|
32673
32904
|
"bash:npm:test",
|
|
32674
32905
|
"bash:npm:ci",
|
|
32675
32906
|
"bash:pnpm:install",
|
|
32907
|
+
"bash:pnpm:i",
|
|
32676
32908
|
"bash:pnpm:run",
|
|
32677
32909
|
"bash:pnpm:test",
|
|
32910
|
+
"bash:pnpm:typecheck",
|
|
32911
|
+
"bash:pnpm:lint",
|
|
32912
|
+
"bash:pnpm:build",
|
|
32913
|
+
"bash:pnpm:check",
|
|
32914
|
+
"bash:pnpm:format",
|
|
32915
|
+
"bash:pnpm:dev",
|
|
32916
|
+
"bash:pnpm:add",
|
|
32917
|
+
"bash:pnpm:remove",
|
|
32918
|
+
"bash:pnpm:update",
|
|
32919
|
+
"bash:pnpm:exec",
|
|
32920
|
+
"bash:pnpm:rebuild",
|
|
32678
32921
|
"bash:yarn:install",
|
|
32679
32922
|
"bash:yarn:run",
|
|
32680
32923
|
"bash:yarn:test",
|
|
32681
32924
|
"bash:node",
|
|
32925
|
+
"bash:vitest",
|
|
32926
|
+
"bash:tsc",
|
|
32927
|
+
"bash:tsx",
|
|
32928
|
+
"bash:oxlint",
|
|
32682
32929
|
// ── Bash: JVM toolchain ──
|
|
32683
32930
|
"bash:java",
|
|
32684
32931
|
"bash:javac",
|
|
@@ -32706,11 +32953,13 @@ var RECOMMENDED_PROJECT = [
|
|
|
32706
32953
|
"bash:go:test",
|
|
32707
32954
|
"bash:go:vet",
|
|
32708
32955
|
"bash:pip:install",
|
|
32709
|
-
// ── Bash: git local (staging only — push
|
|
32710
|
-
"bash:git:add"
|
|
32711
|
-
"bash:git:commit"
|
|
32956
|
+
// ── Bash: git local (staging only — commit and push are in ASK) ──
|
|
32957
|
+
"bash:git:add"
|
|
32712
32958
|
];
|
|
32713
32959
|
var ALWAYS_ASK = [
|
|
32960
|
+
// ── Git commit — always ask by default; use /permissions allow-commits to opt in ──
|
|
32961
|
+
"git_commit",
|
|
32962
|
+
"bash:git:commit",
|
|
32714
32963
|
// ── Coco native (risky) ──
|
|
32715
32964
|
"delete_file",
|
|
32716
32965
|
"git_push",
|
|
@@ -32787,6 +33036,7 @@ var RECOMMENDED_DENY = [
|
|
|
32787
33036
|
// ── Package publishing ──
|
|
32788
33037
|
"bash:npm:publish",
|
|
32789
33038
|
"bash:yarn:publish",
|
|
33039
|
+
"bash:pnpm:publish",
|
|
32790
33040
|
"bash:cargo:publish",
|
|
32791
33041
|
// ── Disk / low-level destructive ──
|
|
32792
33042
|
"bash:dd",
|
|
@@ -32835,8 +33085,10 @@ async function showPermissionSuggestion() {
|
|
|
32835
33085
|
console.log(chalk25.magenta.bold(" \u{1F4CB} Recommended Permissions"));
|
|
32836
33086
|
console.log();
|
|
32837
33087
|
console.log(chalk25.dim(" Coco has a curated set of tool permissions for developers:"));
|
|
32838
|
-
console.log(chalk25.dim(" \u2022 Allow: file read/write, search, git
|
|
32839
|
-
console.log(
|
|
33088
|
+
console.log(chalk25.dim(" \u2022 Allow: file read/write, search, git staging, build, tests..."));
|
|
33089
|
+
console.log(
|
|
33090
|
+
chalk25.dim(" \u2022 Ask each time: git commit, curl, rm, git pull, docker exec, cloud...")
|
|
33091
|
+
);
|
|
32840
33092
|
console.log(chalk25.dim(" \u2022 Deny: sudo, git push, git rebase, docker push, k8s apply..."));
|
|
32841
33093
|
console.log();
|
|
32842
33094
|
console.log(chalk25.dim(" Stored in ~/.coco/trusted-tools.json \u2014 edit manually or let"));
|
|
@@ -32927,7 +33179,7 @@ var permissionsCommand = {
|
|
|
32927
33179
|
name: "permissions",
|
|
32928
33180
|
aliases: ["perms"],
|
|
32929
33181
|
description: "Manage tool permissions and recommended allowlist",
|
|
32930
|
-
usage: "/permissions [apply|view|reset]",
|
|
33182
|
+
usage: "/permissions [apply|view|reset|allow-commits|revoke-commits]",
|
|
32931
33183
|
async execute(args, session) {
|
|
32932
33184
|
const subcommand = args[0]?.toLowerCase() ?? "status";
|
|
32933
33185
|
switch (subcommand) {
|
|
@@ -32940,6 +33192,12 @@ var permissionsCommand = {
|
|
|
32940
33192
|
case "reset":
|
|
32941
33193
|
await resetPermissions(session);
|
|
32942
33194
|
return false;
|
|
33195
|
+
case "allow-commits":
|
|
33196
|
+
await allowCommits(session);
|
|
33197
|
+
return false;
|
|
33198
|
+
case "revoke-commits":
|
|
33199
|
+
await revokeCommits(session);
|
|
33200
|
+
return false;
|
|
32943
33201
|
case "status":
|
|
32944
33202
|
default:
|
|
32945
33203
|
await showStatus(session);
|
|
@@ -32984,9 +33242,15 @@ async function showStatus(session) {
|
|
|
32984
33242
|
}
|
|
32985
33243
|
}
|
|
32986
33244
|
console.log();
|
|
32987
|
-
console.log(chalk25.dim(" /permissions apply
|
|
32988
|
-
console.log(chalk25.dim(" /permissions view
|
|
32989
|
-
console.log(chalk25.dim(" /permissions reset
|
|
33245
|
+
console.log(chalk25.dim(" /permissions apply \u2014 Apply recommended permissions"));
|
|
33246
|
+
console.log(chalk25.dim(" /permissions view \u2014 View recommended template"));
|
|
33247
|
+
console.log(chalk25.dim(" /permissions reset \u2014 Reset to empty"));
|
|
33248
|
+
console.log(
|
|
33249
|
+
chalk25.dim(" /permissions allow-commits \u2014 Auto-approve git commit for this project")
|
|
33250
|
+
);
|
|
33251
|
+
console.log(
|
|
33252
|
+
chalk25.dim(" /permissions revoke-commits \u2014 Require confirmation for git commit again")
|
|
33253
|
+
);
|
|
32990
33254
|
console.log();
|
|
32991
33255
|
}
|
|
32992
33256
|
async function applyRecommended(session) {
|
|
@@ -33000,6 +33264,25 @@ async function applyRecommended(session) {
|
|
|
33000
33264
|
console.log(chalk25.green(" \u2713 Recommended permissions applied!"));
|
|
33001
33265
|
console.log(chalk25.dim(" Use /permissions to review."));
|
|
33002
33266
|
}
|
|
33267
|
+
async function allowCommits(session) {
|
|
33268
|
+
const commitTools = ["git_commit", "bash:git:commit"];
|
|
33269
|
+
for (const tool of commitTools) {
|
|
33270
|
+
session.trustedTools.add(tool);
|
|
33271
|
+
await saveTrustedTool(tool, session.projectPath, false);
|
|
33272
|
+
}
|
|
33273
|
+
console.log(chalk25.green(" \u2713 git commit will be auto-approved for this project"));
|
|
33274
|
+
console.log(chalk25.dim(" Use /permissions revoke-commits to require confirmation again."));
|
|
33275
|
+
}
|
|
33276
|
+
async function revokeCommits(session) {
|
|
33277
|
+
const commitTools = ["git_commit", "bash:git:commit"];
|
|
33278
|
+
for (const tool of commitTools) {
|
|
33279
|
+
session.trustedTools.delete(tool);
|
|
33280
|
+
await removeTrustedTool(tool, session.projectPath, false);
|
|
33281
|
+
await removeTrustedTool(tool, session.projectPath, true);
|
|
33282
|
+
}
|
|
33283
|
+
console.log(chalk25.yellow(" \u25CB git commit will now require confirmation for this project"));
|
|
33284
|
+
console.log(chalk25.dim(" Use /permissions allow-commits to enable auto-approve again."));
|
|
33285
|
+
}
|
|
33003
33286
|
async function resetPermissions(session) {
|
|
33004
33287
|
const confirmed = await p25.confirm({
|
|
33005
33288
|
message: "Reset all tool permissions? This removes all trusted tools.",
|
|
@@ -33024,15 +33307,15 @@ async function resetPermissions(session) {
|
|
|
33024
33307
|
console.log(chalk25.green(" \u2713 All tool permissions reset."));
|
|
33025
33308
|
}
|
|
33026
33309
|
|
|
33027
|
-
// src/cli/repl/
|
|
33310
|
+
// src/cli/repl/quality-loop.ts
|
|
33028
33311
|
init_paths();
|
|
33029
|
-
var
|
|
33312
|
+
var qualityLoopEnabled = true;
|
|
33030
33313
|
var hintShown = false;
|
|
33031
|
-
function
|
|
33032
|
-
return
|
|
33314
|
+
function isQualityLoop() {
|
|
33315
|
+
return qualityLoopEnabled;
|
|
33033
33316
|
}
|
|
33034
|
-
function
|
|
33035
|
-
|
|
33317
|
+
function setQualityLoop(enabled) {
|
|
33318
|
+
qualityLoopEnabled = enabled;
|
|
33036
33319
|
}
|
|
33037
33320
|
function wasHintShown() {
|
|
33038
33321
|
return hintShown;
|
|
@@ -33063,8 +33346,8 @@ function looksLikeFeatureRequest(input) {
|
|
|
33063
33346
|
];
|
|
33064
33347
|
return featureKeywords.some((re) => re.test(trimmed));
|
|
33065
33348
|
}
|
|
33066
|
-
function
|
|
33067
|
-
return chalk25.dim(" tip: ") + chalk25.magenta("/
|
|
33349
|
+
function formatQualityLoopHint() {
|
|
33350
|
+
return chalk25.dim(" tip: ") + chalk25.magenta("/quality") + chalk25.dim(" enables auto-test & iterate until quality converges");
|
|
33068
33351
|
}
|
|
33069
33352
|
function formatQualityResult(result) {
|
|
33070
33353
|
const lines = [];
|
|
@@ -33097,19 +33380,20 @@ function formatQualityResult(result) {
|
|
|
33097
33380
|
lines.push("");
|
|
33098
33381
|
return lines.join("\n");
|
|
33099
33382
|
}
|
|
33100
|
-
async function
|
|
33383
|
+
async function loadQualityLoopPreference() {
|
|
33101
33384
|
try {
|
|
33102
33385
|
const content = await fs32__default.readFile(CONFIG_PATHS.config, "utf-8");
|
|
33103
33386
|
const config = JSON.parse(content);
|
|
33104
|
-
|
|
33105
|
-
|
|
33106
|
-
|
|
33387
|
+
const value = config.qualityLoop ?? config.cocoMode;
|
|
33388
|
+
if (typeof value === "boolean") {
|
|
33389
|
+
qualityLoopEnabled = value;
|
|
33390
|
+
return value;
|
|
33107
33391
|
}
|
|
33108
33392
|
} catch {
|
|
33109
33393
|
}
|
|
33110
33394
|
return true;
|
|
33111
33395
|
}
|
|
33112
|
-
async function
|
|
33396
|
+
async function saveQualityLoopPreference(enabled) {
|
|
33113
33397
|
try {
|
|
33114
33398
|
let config = {};
|
|
33115
33399
|
try {
|
|
@@ -33117,13 +33401,13 @@ async function saveCocoModePreference(enabled) {
|
|
|
33117
33401
|
config = JSON.parse(content);
|
|
33118
33402
|
} catch {
|
|
33119
33403
|
}
|
|
33120
|
-
config.
|
|
33404
|
+
config.qualityLoop = enabled;
|
|
33121
33405
|
await fs32__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2) + "\n");
|
|
33122
33406
|
} catch {
|
|
33123
33407
|
}
|
|
33124
33408
|
}
|
|
33125
|
-
function
|
|
33126
|
-
const marker = "
|
|
33409
|
+
function parseQualityLoopReport(content) {
|
|
33410
|
+
const marker = "QUALITY_LOOP_REPORT";
|
|
33127
33411
|
const idx = content.indexOf(marker);
|
|
33128
33412
|
if (idx === -1) return null;
|
|
33129
33413
|
const block = content.slice(idx);
|
|
@@ -33152,11 +33436,11 @@ function parseCocoQualityReport(content) {
|
|
|
33152
33436
|
securityScore: isNaN(security) ? void 0 : security
|
|
33153
33437
|
};
|
|
33154
33438
|
}
|
|
33155
|
-
function
|
|
33439
|
+
function getQualityLoopSystemPrompt() {
|
|
33156
33440
|
return `
|
|
33157
|
-
##
|
|
33441
|
+
## Quality Loop Mode (ACTIVE)
|
|
33158
33442
|
|
|
33159
|
-
You are operating in
|
|
33443
|
+
You are operating in quality loop mode. After implementing code changes, you MUST follow this iteration cycle:
|
|
33160
33444
|
|
|
33161
33445
|
1. **Implement** the requested changes (code + tests)
|
|
33162
33446
|
2. **Run tests** using the run_tests or bash_exec tool
|
|
@@ -33171,7 +33455,7 @@ You are operating in COCO quality mode. After implementing code changes, you MUS
|
|
|
33171
33455
|
After completing the cycle, output a quality summary in this exact format:
|
|
33172
33456
|
|
|
33173
33457
|
\`\`\`
|
|
33174
|
-
|
|
33458
|
+
QUALITY_LOOP_REPORT
|
|
33175
33459
|
score_history: [first_score, ..., final_score]
|
|
33176
33460
|
tests_passed: X
|
|
33177
33461
|
tests_total: Y
|
|
@@ -33190,12 +33474,12 @@ Key rules:
|
|
|
33190
33474
|
- Report honestly - don't inflate scores`;
|
|
33191
33475
|
}
|
|
33192
33476
|
|
|
33193
|
-
// src/cli/repl/commands/
|
|
33194
|
-
var
|
|
33195
|
-
name: "
|
|
33196
|
-
aliases: [],
|
|
33477
|
+
// src/cli/repl/commands/quality.ts
|
|
33478
|
+
var qualityCommand = {
|
|
33479
|
+
name: "quality",
|
|
33480
|
+
aliases: ["coco"],
|
|
33197
33481
|
description: "Toggle quality mode \u2014 auto-test, self-review, iterate until converged",
|
|
33198
|
-
usage: "/
|
|
33482
|
+
usage: "/quality [on|off]",
|
|
33199
33483
|
async execute(args, session) {
|
|
33200
33484
|
const arg = args[0]?.toLowerCase();
|
|
33201
33485
|
let newState;
|
|
@@ -33204,12 +33488,12 @@ var cocoCommand = {
|
|
|
33204
33488
|
} else if (arg === "off") {
|
|
33205
33489
|
newState = false;
|
|
33206
33490
|
} else if (arg === "status") {
|
|
33207
|
-
const state =
|
|
33491
|
+
const state = isQualityLoop();
|
|
33208
33492
|
const skillAvailable = session.skillRegistry?.has("coco-fix-iterate");
|
|
33209
33493
|
const modeType = state && skillAvailable ? chalk25.cyan(" (skill-based)") : state ? chalk25.dim(" (prompt-based)") : "";
|
|
33210
33494
|
console.log();
|
|
33211
33495
|
console.log(
|
|
33212
|
-
chalk25.magenta("
|
|
33496
|
+
chalk25.magenta(" Quality loop: ") + (state ? chalk25.green.bold("ON") : chalk25.dim("OFF")) + modeType
|
|
33213
33497
|
);
|
|
33214
33498
|
console.log();
|
|
33215
33499
|
if (state) {
|
|
@@ -33225,24 +33509,24 @@ var cocoCommand = {
|
|
|
33225
33509
|
console.log(chalk25.dim(" 3. Self-review against 12 quality dimensions"));
|
|
33226
33510
|
console.log(chalk25.dim(" 4. Iterate until quality converges (\u226585/100)"));
|
|
33227
33511
|
} else {
|
|
33228
|
-
console.log(chalk25.dim(" Enable with /
|
|
33512
|
+
console.log(chalk25.dim(" Enable with /quality on for quality-driven development"));
|
|
33229
33513
|
}
|
|
33230
33514
|
console.log();
|
|
33231
33515
|
return false;
|
|
33232
33516
|
} else {
|
|
33233
|
-
newState = !
|
|
33517
|
+
newState = !isQualityLoop();
|
|
33234
33518
|
}
|
|
33235
|
-
|
|
33236
|
-
|
|
33519
|
+
setQualityLoop(newState);
|
|
33520
|
+
saveQualityLoopPreference(newState).catch(() => {
|
|
33237
33521
|
});
|
|
33238
33522
|
console.log();
|
|
33239
33523
|
if (newState) {
|
|
33240
|
-
console.log(chalk25.magenta("
|
|
33524
|
+
console.log(chalk25.magenta(" Quality loop: ") + chalk25.green.bold("ON"));
|
|
33241
33525
|
console.log(
|
|
33242
33526
|
chalk25.dim(" Agent will auto-test, self-review, and iterate until quality \u2265 85/100")
|
|
33243
33527
|
);
|
|
33244
33528
|
} else {
|
|
33245
|
-
console.log(chalk25.magenta("
|
|
33529
|
+
console.log(chalk25.magenta(" Quality loop: ") + chalk25.dim("OFF"));
|
|
33246
33530
|
console.log(chalk25.dim(" Fast mode \u2014 agent responds without quality iteration"));
|
|
33247
33531
|
}
|
|
33248
33532
|
console.log();
|
|
@@ -33447,7 +33731,7 @@ var tutorialCommand = {
|
|
|
33447
33731
|
{
|
|
33448
33732
|
step: "3",
|
|
33449
33733
|
title: "Enable quality mode",
|
|
33450
|
-
desc: "Type /
|
|
33734
|
+
desc: "Type /quality to enable auto-iteration: test \u2192 analyze \u2192 fix \u2192 repeat until score \u2265 85"
|
|
33451
33735
|
},
|
|
33452
33736
|
{
|
|
33453
33737
|
step: "4",
|
|
@@ -33467,7 +33751,7 @@ var tutorialCommand = {
|
|
|
33467
33751
|
}
|
|
33468
33752
|
console.log(chalk25.bold("Useful commands:"));
|
|
33469
33753
|
console.log(
|
|
33470
|
-
` ${chalk25.yellow("/
|
|
33754
|
+
` ${chalk25.yellow("/quality")} ${chalk25.dim("Toggle quality mode (auto-iteration)")}`
|
|
33471
33755
|
);
|
|
33472
33756
|
console.log(` ${chalk25.yellow("/init")} ${chalk25.dim("Initialize a new project")}`);
|
|
33473
33757
|
console.log(` ${chalk25.yellow("/help")} ${chalk25.dim("See all available commands")}`);
|
|
@@ -40741,7 +41025,7 @@ var commands = [
|
|
|
40741
41025
|
copyCommand,
|
|
40742
41026
|
allowPathCommand,
|
|
40743
41027
|
permissionsCommand,
|
|
40744
|
-
|
|
41028
|
+
qualityCommand,
|
|
40745
41029
|
fullAccessCommand,
|
|
40746
41030
|
updateCocoCommand,
|
|
40747
41031
|
imageCommand,
|
|
@@ -40890,11 +41174,11 @@ function createInputHandler(_session) {
|
|
|
40890
41174
|
const getPrompt = () => {
|
|
40891
41175
|
const imageIndicator = hasPendingImage() ? chalk25.cyan(" \u{1F4CE} 1 image") : "";
|
|
40892
41176
|
const imageIndicatorLen = hasPendingImage() ? 10 : 0;
|
|
40893
|
-
if (
|
|
41177
|
+
if (isQualityLoop()) {
|
|
40894
41178
|
return {
|
|
40895
|
-
str: "\u{1F965} " + chalk25.magenta("[
|
|
40896
|
-
// 🥥=2 + space=1 + [
|
|
40897
|
-
visualLen:
|
|
41179
|
+
str: "\u{1F965} " + chalk25.magenta("[quality]") + " \u203A " + imageIndicator,
|
|
41180
|
+
// 🥥=2 + space=1 + [quality]=9 + space=1 + ›=1 + space=1 = 15 + image indicator
|
|
41181
|
+
visualLen: 15 + imageIndicatorLen
|
|
40898
41182
|
};
|
|
40899
41183
|
}
|
|
40900
41184
|
return {
|
|
@@ -43194,8 +43478,8 @@ function formatStatusBar(projectPath, config, gitCtx) {
|
|
|
43194
43478
|
const providerName = config.provider.type;
|
|
43195
43479
|
const modelName = config.provider.model || "default";
|
|
43196
43480
|
parts.push(chalk25.dim(`${providerName}/`) + chalk25.cyan(modelName));
|
|
43197
|
-
if (
|
|
43198
|
-
parts.push(chalk25.green("\u{1F504}
|
|
43481
|
+
if (isQualityLoop()) {
|
|
43482
|
+
parts.push(chalk25.green("\u{1F504} quality loop"));
|
|
43199
43483
|
}
|
|
43200
43484
|
if (isFullAccessMode()) {
|
|
43201
43485
|
parts.push(chalk25.yellow("\u26A1 full-access"));
|
|
@@ -43268,7 +43552,7 @@ async function startRepl(options = {}) {
|
|
|
43268
43552
|
const { loadFullAccessPreference: loadFullAccessPreference2 } = await Promise.resolve().then(() => (init_full_access_mode(), full_access_mode_exports));
|
|
43269
43553
|
const { loadFullPowerRiskPreference: loadFullPowerRiskPreference2 } = await Promise.resolve().then(() => (init_full_power_risk_mode(), full_power_risk_mode_exports));
|
|
43270
43554
|
await Promise.all([
|
|
43271
|
-
|
|
43555
|
+
loadQualityLoopPreference(),
|
|
43272
43556
|
loadFullAccessPreference2(),
|
|
43273
43557
|
loadFullPowerRiskPreference2()
|
|
43274
43558
|
]);
|
|
@@ -43530,13 +43814,13 @@ async function startRepl(options = {}) {
|
|
|
43530
43814
|
};
|
|
43531
43815
|
let originalSystemPrompt;
|
|
43532
43816
|
try {
|
|
43533
|
-
if (typeof agentMessage === "string" && !
|
|
43817
|
+
if (typeof agentMessage === "string" && !isQualityLoop() && !wasHintShown() && looksLikeFeatureRequest(agentMessage)) {
|
|
43534
43818
|
markHintShown();
|
|
43535
|
-
console.log(
|
|
43819
|
+
console.log(formatQualityLoopHint());
|
|
43536
43820
|
}
|
|
43537
43821
|
console.log();
|
|
43538
43822
|
let cocoForkPrompt;
|
|
43539
|
-
if (
|
|
43823
|
+
if (isQualityLoop()) {
|
|
43540
43824
|
const skillId = "coco-fix-iterate";
|
|
43541
43825
|
const skillAvailable = session.skillRegistry?.has(skillId);
|
|
43542
43826
|
if (skillAvailable && typeof agentMessage === "string") {
|
|
@@ -43550,7 +43834,7 @@ async function startRepl(options = {}) {
|
|
|
43550
43834
|
}
|
|
43551
43835
|
} else {
|
|
43552
43836
|
originalSystemPrompt = session.config.agent.systemPrompt;
|
|
43553
|
-
session.config.agent.systemPrompt = originalSystemPrompt + "\n" +
|
|
43837
|
+
session.config.agent.systemPrompt = originalSystemPrompt + "\n" + getQualityLoopSystemPrompt();
|
|
43554
43838
|
}
|
|
43555
43839
|
}
|
|
43556
43840
|
const effectiveMessage = cocoForkPrompt ?? agentMessage;
|
|
@@ -43628,7 +43912,7 @@ async function startRepl(options = {}) {
|
|
|
43628
43912
|
}
|
|
43629
43913
|
renderToolStart(result2.name, result2.input);
|
|
43630
43914
|
renderToolEnd(result2);
|
|
43631
|
-
if (
|
|
43915
|
+
if (isQualityLoop()) {
|
|
43632
43916
|
setSpinner("Processing results & checking quality...");
|
|
43633
43917
|
} else {
|
|
43634
43918
|
setSpinner("Processing...");
|
|
@@ -43645,7 +43929,7 @@ async function startRepl(options = {}) {
|
|
|
43645
43929
|
if (!thinkingStartTime) return;
|
|
43646
43930
|
const elapsed = Math.floor((Date.now() - thinkingStartTime) / 1e3);
|
|
43647
43931
|
if (elapsed < 4) return;
|
|
43648
|
-
if (
|
|
43932
|
+
if (isQualityLoop()) {
|
|
43649
43933
|
if (elapsed < 8) setSpinner("Analyzing request...");
|
|
43650
43934
|
else if (elapsed < 15) setSpinner("Running quality checks...");
|
|
43651
43935
|
else if (elapsed < 25) setSpinner("Iterating for quality...");
|
|
@@ -43732,8 +44016,8 @@ async function startRepl(options = {}) {
|
|
|
43732
44016
|
continue;
|
|
43733
44017
|
}
|
|
43734
44018
|
console.log();
|
|
43735
|
-
if (
|
|
43736
|
-
const qualityResult =
|
|
44019
|
+
if (isQualityLoop() && result.content) {
|
|
44020
|
+
const qualityResult = parseQualityLoopReport(result.content);
|
|
43737
44021
|
if (qualityResult) {
|
|
43738
44022
|
console.log(formatQualityResult(qualityResult));
|
|
43739
44023
|
}
|
|
@@ -43854,7 +44138,7 @@ async function printWelcome(session, gitCtx, mcpManager) {
|
|
|
43854
44138
|
if (gitCtx) {
|
|
43855
44139
|
console.log(` ${formatGitLine(gitCtx)}`);
|
|
43856
44140
|
}
|
|
43857
|
-
const cocoStatus =
|
|
44141
|
+
const cocoStatus = isQualityLoop() ? chalk25.magenta(" \u{1F504} quality mode: ") + chalk25.green.bold("on") + chalk25.dim(" \u2014 iterates until quality \u2265 85. /quality to disable") : chalk25.dim(" \u{1F4A1} /quality on \u2014 enable auto-test & quality iteration");
|
|
43858
44142
|
console.log(cocoStatus);
|
|
43859
44143
|
const skillTotal = session.skillRegistry?.size ?? 0;
|
|
43860
44144
|
const mcpServers = mcpManager?.getConnectedServers() ?? [];
|