workermill 0.4.0 → 0.4.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.
|
@@ -3162,6 +3162,42 @@ var CostTracker = class {
|
|
|
3162
3162
|
}
|
|
3163
3163
|
};
|
|
3164
3164
|
|
|
3165
|
+
// src/logger.ts
|
|
3166
|
+
init_esm_shims();
|
|
3167
|
+
import fs9 from "fs";
|
|
3168
|
+
import path11 from "path";
|
|
3169
|
+
var LOG_DIR = path11.join(process.cwd(), ".workermill");
|
|
3170
|
+
var LOG_FILE = path11.join(LOG_DIR, "cli.log");
|
|
3171
|
+
var logStream = null;
|
|
3172
|
+
function ensureLogDir() {
|
|
3173
|
+
if (!fs9.existsSync(LOG_DIR)) {
|
|
3174
|
+
fs9.mkdirSync(LOG_DIR, { recursive: true });
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
function getStream() {
|
|
3178
|
+
if (!logStream) {
|
|
3179
|
+
ensureLogDir();
|
|
3180
|
+
logStream = fs9.createWriteStream(LOG_FILE, { flags: "a" });
|
|
3181
|
+
}
|
|
3182
|
+
return logStream;
|
|
3183
|
+
}
|
|
3184
|
+
function timestamp() {
|
|
3185
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
3186
|
+
}
|
|
3187
|
+
function log(level, message, data) {
|
|
3188
|
+
const entry = data ? `[${timestamp()}] ${level}: ${message} ${JSON.stringify(data)}` : `[${timestamp()}] ${level}: ${message}`;
|
|
3189
|
+
getStream().write(entry + "\n");
|
|
3190
|
+
}
|
|
3191
|
+
function info(message, data) {
|
|
3192
|
+
log("INFO", message, data);
|
|
3193
|
+
}
|
|
3194
|
+
function error(message, data) {
|
|
3195
|
+
log("ERROR", message, data);
|
|
3196
|
+
}
|
|
3197
|
+
function debug(message, data) {
|
|
3198
|
+
log("DEBUG", message, data);
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3165
3201
|
export {
|
|
3166
3202
|
__dirname,
|
|
3167
3203
|
init_esm_shims,
|
|
@@ -3172,5 +3208,8 @@ export {
|
|
|
3172
3208
|
createModel,
|
|
3173
3209
|
killActiveProcess,
|
|
3174
3210
|
createToolDefinitions,
|
|
3175
|
-
CostTracker
|
|
3211
|
+
CostTracker,
|
|
3212
|
+
info,
|
|
3213
|
+
error,
|
|
3214
|
+
debug
|
|
3176
3215
|
};
|
package/dist/index.js
CHANGED
|
@@ -4,12 +4,15 @@ import {
|
|
|
4
4
|
buildOllamaOptions,
|
|
5
5
|
createModel,
|
|
6
6
|
createToolDefinitions,
|
|
7
|
+
debug,
|
|
8
|
+
error,
|
|
7
9
|
getProviderForPersona,
|
|
10
|
+
info,
|
|
8
11
|
init_esm_shims,
|
|
9
12
|
killActiveProcess,
|
|
10
13
|
loadConfig,
|
|
11
14
|
saveConfig
|
|
12
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-77CAVW3Y.js";
|
|
13
16
|
|
|
14
17
|
// src/index.ts
|
|
15
18
|
init_esm_shims();
|
|
@@ -49,8 +52,8 @@ var PROVIDERS = [
|
|
|
49
52
|
needsKey: true,
|
|
50
53
|
envVar: "OPENAI_API_KEY",
|
|
51
54
|
models: [
|
|
52
|
-
{ id: "gpt-5.
|
|
53
|
-
{ id: "gpt-5", label: "GPT-5 (
|
|
55
|
+
{ id: "gpt-5.4", label: "GPT-5.4 (latest flagship)" },
|
|
56
|
+
{ id: "gpt-5.3-codex", label: "GPT-5.3 Codex (built for code)" }
|
|
54
57
|
]
|
|
55
58
|
},
|
|
56
59
|
{
|
|
@@ -115,12 +118,14 @@ async function getApiKey(rl, provider, existingKeys) {
|
|
|
115
118
|
existingKeys.set(provider.name, key2);
|
|
116
119
|
return key2;
|
|
117
120
|
}
|
|
118
|
-
|
|
121
|
+
rl.pause();
|
|
122
|
+
const key = await readKeyMasked(chalk.dim(` ${provider.display} API key: `));
|
|
123
|
+
rl.resume();
|
|
119
124
|
const trimmed = key.trim();
|
|
120
125
|
existingKeys.set(provider.name, trimmed);
|
|
121
126
|
return trimmed;
|
|
122
127
|
}
|
|
123
|
-
function readKeyMasked(
|
|
128
|
+
function readKeyMasked(prompt) {
|
|
124
129
|
return new Promise((resolve) => {
|
|
125
130
|
let buffer = "";
|
|
126
131
|
let revealed = false;
|
|
@@ -338,8 +343,8 @@ init_esm_shims();
|
|
|
338
343
|
import { useState as useState5, useCallback as useCallback3, useRef as useRef3, useEffect as useEffect2 } from "react";
|
|
339
344
|
import { useApp as useApp2 } from "ink";
|
|
340
345
|
import { execSync as execSync2 } from "child_process";
|
|
341
|
-
import
|
|
342
|
-
import
|
|
346
|
+
import fs2 from "fs";
|
|
347
|
+
import path2 from "path";
|
|
343
348
|
import os from "os";
|
|
344
349
|
|
|
345
350
|
// src/ui/useAgent.ts
|
|
@@ -474,42 +479,6 @@ ${result.text}` },
|
|
|
474
479
|
}
|
|
475
480
|
}
|
|
476
481
|
|
|
477
|
-
// src/logger.ts
|
|
478
|
-
init_esm_shims();
|
|
479
|
-
import fs2 from "fs";
|
|
480
|
-
import path2 from "path";
|
|
481
|
-
var LOG_DIR = path2.join(process.cwd(), ".workermill");
|
|
482
|
-
var LOG_FILE = path2.join(LOG_DIR, "cli.log");
|
|
483
|
-
var logStream = null;
|
|
484
|
-
function ensureLogDir() {
|
|
485
|
-
if (!fs2.existsSync(LOG_DIR)) {
|
|
486
|
-
fs2.mkdirSync(LOG_DIR, { recursive: true });
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
function getStream() {
|
|
490
|
-
if (!logStream) {
|
|
491
|
-
ensureLogDir();
|
|
492
|
-
logStream = fs2.createWriteStream(LOG_FILE, { flags: "a" });
|
|
493
|
-
}
|
|
494
|
-
return logStream;
|
|
495
|
-
}
|
|
496
|
-
function timestamp() {
|
|
497
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
498
|
-
}
|
|
499
|
-
function log(level, message, data) {
|
|
500
|
-
const entry = data ? `[${timestamp()}] ${level}: ${message} ${JSON.stringify(data)}` : `[${timestamp()}] ${level}: ${message}`;
|
|
501
|
-
getStream().write(entry + "\n");
|
|
502
|
-
}
|
|
503
|
-
function info(message, data) {
|
|
504
|
-
log("INFO", message, data);
|
|
505
|
-
}
|
|
506
|
-
function error(message, data) {
|
|
507
|
-
log("ERROR", message, data);
|
|
508
|
-
}
|
|
509
|
-
function debug(message, data) {
|
|
510
|
-
log("DEBUG", message, data);
|
|
511
|
-
}
|
|
512
|
-
|
|
513
482
|
// src/ui/useAgent.ts
|
|
514
483
|
var DANGEROUS_PATTERNS = [
|
|
515
484
|
{
|
|
@@ -1028,7 +997,7 @@ function useOrchestrator(addMessage2, setCost) {
|
|
|
1028
997
|
setRunning(false);
|
|
1029
998
|
return;
|
|
1030
999
|
}
|
|
1031
|
-
const { classifyComplexity, runOrchestration } = await import("./orchestrator-
|
|
1000
|
+
const { classifyComplexity, runOrchestration } = await import("./orchestrator-L5LDQYO7.js");
|
|
1032
1001
|
const output = {
|
|
1033
1002
|
log(persona, message) {
|
|
1034
1003
|
const emoji = getEmoji(persona);
|
|
@@ -1608,7 +1577,13 @@ function StatusBar(props) {
|
|
|
1608
1577
|
break;
|
|
1609
1578
|
}
|
|
1610
1579
|
const bgColor = theme.subtleDark;
|
|
1611
|
-
const
|
|
1580
|
+
const rm = props.roleModels;
|
|
1581
|
+
const workerStr = rm?.worker || `${props.provider}/${props.model}`;
|
|
1582
|
+
const extraRoles = [];
|
|
1583
|
+
if (rm && rm.planner !== rm.worker) extraRoles.push(`plan:${rm.planner}`);
|
|
1584
|
+
if (rm && rm.reviewer !== rm.worker) extraRoles.push(`review:${rm.reviewer}`);
|
|
1585
|
+
const rolesStr = extraRoles.length > 0 ? ` (${extraRoles.join(", ")})` : "";
|
|
1586
|
+
const modelStr = ` ${workerStr}${rolesStr} `;
|
|
1612
1587
|
const pct = props.maxContext > 0 ? Math.round(usage * 100) : 0;
|
|
1613
1588
|
const tokenStr = ` ${pct}% `;
|
|
1614
1589
|
const costStr = formatCost(props.cost);
|
|
@@ -1768,7 +1743,8 @@ function App(props) {
|
|
|
1768
1743
|
cost: props.cost,
|
|
1769
1744
|
mode,
|
|
1770
1745
|
gitBranch: props.gitBranch,
|
|
1771
|
-
cwd: props.workingDir.split("/").pop() || ""
|
|
1746
|
+
cwd: props.workingDir.split("/").pop() || "",
|
|
1747
|
+
roleModels: props.roleModels
|
|
1772
1748
|
}
|
|
1773
1749
|
),
|
|
1774
1750
|
/* @__PURE__ */ jsx6(
|
|
@@ -1785,13 +1761,13 @@ function App(props) {
|
|
|
1785
1761
|
|
|
1786
1762
|
// src/ui/Root.tsx
|
|
1787
1763
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1788
|
-
var HISTORY_DIR =
|
|
1789
|
-
var HISTORY_FILE =
|
|
1764
|
+
var HISTORY_DIR = path2.join(os.homedir(), ".workermill");
|
|
1765
|
+
var HISTORY_FILE = path2.join(HISTORY_DIR, "history");
|
|
1790
1766
|
var MAX_HISTORY = 1e3;
|
|
1791
1767
|
function loadHistory() {
|
|
1792
1768
|
try {
|
|
1793
|
-
if (
|
|
1794
|
-
const raw =
|
|
1769
|
+
if (fs2.existsSync(HISTORY_FILE)) {
|
|
1770
|
+
const raw = fs2.readFileSync(HISTORY_FILE, "utf-8").trim();
|
|
1795
1771
|
if (!raw) return [];
|
|
1796
1772
|
return raw.split("\n").slice(-MAX_HISTORY);
|
|
1797
1773
|
}
|
|
@@ -1801,10 +1777,10 @@ function loadHistory() {
|
|
|
1801
1777
|
}
|
|
1802
1778
|
function appendHistory(line) {
|
|
1803
1779
|
try {
|
|
1804
|
-
if (!
|
|
1805
|
-
|
|
1780
|
+
if (!fs2.existsSync(HISTORY_DIR)) {
|
|
1781
|
+
fs2.mkdirSync(HISTORY_DIR, { recursive: true });
|
|
1806
1782
|
}
|
|
1807
|
-
|
|
1783
|
+
fs2.appendFileSync(HISTORY_FILE, line + "\n", "utf-8");
|
|
1808
1784
|
} catch {
|
|
1809
1785
|
}
|
|
1810
1786
|
}
|
|
@@ -2046,15 +2022,15 @@ To change: edit \`~/.workermill/cli.json\` or restart with \`--provider\` / \`--
|
|
|
2046
2022
|
// ---- /editor ----
|
|
2047
2023
|
case "editor": {
|
|
2048
2024
|
const editor = process.env.EDITOR || process.env.VISUAL || "vi";
|
|
2049
|
-
const tmpFile =
|
|
2025
|
+
const tmpFile = path2.join(os.tmpdir(), `workermill-${Date.now()}.md`);
|
|
2050
2026
|
try {
|
|
2051
|
-
|
|
2027
|
+
fs2.writeFileSync(tmpFile, "", "utf-8");
|
|
2052
2028
|
execSync2(`${editor} ${tmpFile}`, {
|
|
2053
2029
|
cwd: props.workingDir,
|
|
2054
2030
|
stdio: "inherit",
|
|
2055
2031
|
timeout: 5 * 60 * 1e3
|
|
2056
2032
|
});
|
|
2057
|
-
const contents =
|
|
2033
|
+
const contents = fs2.readFileSync(tmpFile, "utf-8").trim();
|
|
2058
2034
|
if (contents) {
|
|
2059
2035
|
agent.addUserMessage(contents);
|
|
2060
2036
|
agent.submit(contents);
|
|
@@ -2066,7 +2042,7 @@ To change: edit \`~/.workermill/cli.json\` or restart with \`--provider\` / \`--
|
|
|
2066
2042
|
agent.addSystemMessage(`Failed to open editor (\`${editor}\`): ${errMsg}`);
|
|
2067
2043
|
} finally {
|
|
2068
2044
|
try {
|
|
2069
|
-
|
|
2045
|
+
fs2.unlinkSync(tmpFile);
|
|
2070
2046
|
} catch {
|
|
2071
2047
|
}
|
|
2072
2048
|
}
|
|
@@ -2188,27 +2164,44 @@ ${trimmedOutput}
|
|
|
2188
2164
|
tokens: agent.tokens,
|
|
2189
2165
|
cost: agent.cost,
|
|
2190
2166
|
gitBranch,
|
|
2191
|
-
inputHistory
|
|
2167
|
+
inputHistory,
|
|
2168
|
+
roleModels: props.roleModels
|
|
2192
2169
|
}
|
|
2193
2170
|
);
|
|
2194
2171
|
}
|
|
2195
2172
|
|
|
2196
2173
|
// src/index.ts
|
|
2197
|
-
function
|
|
2174
|
+
function getRoleModelsFromConfig(config) {
|
|
2175
|
+
const worker = getProviderForPersona(config);
|
|
2176
|
+
const planner = getProviderForPersona(config, "planner");
|
|
2177
|
+
const reviewer = getProviderForPersona(config, "tech_lead");
|
|
2178
|
+
return {
|
|
2179
|
+
worker: `${worker.provider}/${worker.model}`,
|
|
2180
|
+
planner: `${planner.provider}/${planner.model}`,
|
|
2181
|
+
reviewer: `${reviewer.provider}/${reviewer.model}`
|
|
2182
|
+
};
|
|
2183
|
+
}
|
|
2184
|
+
function printWelcome(roleModels, workingDir) {
|
|
2198
2185
|
const brand = chalk2.hex("#D77757");
|
|
2199
2186
|
const dim = chalk2.dim;
|
|
2200
2187
|
const white = chalk2.white;
|
|
2201
2188
|
console.log();
|
|
2202
2189
|
console.log(` ${brand("\u25C6")} ${white.bold("WorkerMill")} ${dim("v" + VERSION)}`);
|
|
2203
2190
|
console.log();
|
|
2204
|
-
|
|
2191
|
+
if (roleModels.planner === roleModels.worker && roleModels.reviewer === roleModels.worker) {
|
|
2192
|
+
console.log(dim(` ${roleModels.worker}`));
|
|
2193
|
+
} else {
|
|
2194
|
+
console.log(dim(` worker: ${roleModels.worker}`));
|
|
2195
|
+
console.log(dim(` planner: ${roleModels.planner}`));
|
|
2196
|
+
console.log(dim(` reviewer: ${roleModels.reviewer}`));
|
|
2197
|
+
}
|
|
2205
2198
|
console.log(dim(` cwd: ${workingDir}`));
|
|
2206
2199
|
console.log();
|
|
2207
2200
|
console.log(dim(" Ask me anything, or use ") + brand("/build") + dim(" to create software with multi-expert AI."));
|
|
2208
2201
|
console.log(dim(" Type ") + white("/help") + dim(" for all commands."));
|
|
2209
2202
|
console.log();
|
|
2210
2203
|
}
|
|
2211
|
-
var VERSION = "0.4.
|
|
2204
|
+
var VERSION = "0.4.2";
|
|
2212
2205
|
function addSharedOptions(cmd) {
|
|
2213
2206
|
return cmd.option("--provider <provider>", "Override default provider").option("--model <model>", "Override model").option("--trust", "Skip all tool permission prompts").option("--full-disk", "Allow tools to access files outside working directory");
|
|
2214
2207
|
}
|
|
@@ -2233,7 +2226,8 @@ var defaultCmd = program.command("chat", { isDefault: true }).description("Inter
|
|
|
2233
2226
|
const config = await resolveConfig(options);
|
|
2234
2227
|
const { provider, model, apiKey, host, contextLength } = getProviderForPersona(config);
|
|
2235
2228
|
const workingDir = process.cwd();
|
|
2236
|
-
|
|
2229
|
+
const roleModels = getRoleModelsFromConfig(config);
|
|
2230
|
+
printWelcome(roleModels, workingDir);
|
|
2237
2231
|
const { waitUntilExit } = render(
|
|
2238
2232
|
React5.createElement(Root, {
|
|
2239
2233
|
provider,
|
|
@@ -2245,7 +2239,8 @@ var defaultCmd = program.command("chat", { isDefault: true }).description("Inter
|
|
|
2245
2239
|
planMode: options.plan || false,
|
|
2246
2240
|
sandboxed: !options.fullDisk,
|
|
2247
2241
|
resume: options.resume || false,
|
|
2248
|
-
workingDir
|
|
2242
|
+
workingDir,
|
|
2243
|
+
roleModels
|
|
2249
2244
|
})
|
|
2250
2245
|
);
|
|
2251
2246
|
await waitUntilExit();
|
|
@@ -2265,6 +2260,7 @@ var buildCmd = program.command("build [task...]").description("Build software wi
|
|
|
2265
2260
|
config.review = { ...config.review, useCritic: true };
|
|
2266
2261
|
}
|
|
2267
2262
|
const { provider, model, apiKey, host, contextLength } = getProviderForPersona(config);
|
|
2263
|
+
const roleModels = getRoleModelsFromConfig(config);
|
|
2268
2264
|
const trustAll = options.trust || false;
|
|
2269
2265
|
const sandboxed = !options.fullDisk;
|
|
2270
2266
|
if (apiKey) {
|
|
@@ -2290,7 +2286,8 @@ var buildCmd = program.command("build [task...]").description("Build software wi
|
|
|
2290
2286
|
sandboxed,
|
|
2291
2287
|
resume: false,
|
|
2292
2288
|
workingDir: process.cwd(),
|
|
2293
|
-
initialBuildTask: task
|
|
2289
|
+
initialBuildTask: task,
|
|
2290
|
+
roleModels
|
|
2294
2291
|
})
|
|
2295
2292
|
);
|
|
2296
2293
|
await waitUntilExit();
|
|
@@ -4,9 +4,11 @@ import {
|
|
|
4
4
|
buildOllamaOptions,
|
|
5
5
|
createModel,
|
|
6
6
|
createToolDefinitions,
|
|
7
|
+
error,
|
|
7
8
|
getProviderForPersona,
|
|
9
|
+
info,
|
|
8
10
|
init_esm_shims
|
|
9
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-77CAVW3Y.js";
|
|
10
12
|
|
|
11
13
|
// src/orchestrator.ts
|
|
12
14
|
init_esm_shims();
|
|
@@ -153,9 +155,9 @@ function buildReasoningOptions(provider, modelName) {
|
|
|
153
155
|
return {};
|
|
154
156
|
}
|
|
155
157
|
}
|
|
156
|
-
function isTransientError(
|
|
157
|
-
if (!
|
|
158
|
-
const msg =
|
|
158
|
+
function isTransientError(error2) {
|
|
159
|
+
if (!error2 || typeof error2 !== "object") return false;
|
|
160
|
+
const msg = error2 instanceof Error ? error2.message : String(error2);
|
|
159
161
|
if (/status code (502|503|504)|socket hang up|ECONNRESET|ETIMEDOUT|network error|ECONNREFUSED/i.test(msg)) {
|
|
160
162
|
return true;
|
|
161
163
|
}
|
|
@@ -219,6 +221,7 @@ function formatToolCallDisplay(toolName, toolInput) {
|
|
|
219
221
|
return msg;
|
|
220
222
|
}
|
|
221
223
|
async function classifyComplexity(config, userInput, output) {
|
|
224
|
+
info("Classifying complexity", { input: userInput.slice(0, 200) });
|
|
222
225
|
const resolvedInput = resolveTaskInput(userInput, process.cwd());
|
|
223
226
|
const { provider, model: modelName, apiKey, host, contextLength } = getProviderForPersona(config);
|
|
224
227
|
if (apiKey) {
|
|
@@ -635,6 +638,7 @@ ${plannerStories.map((s) => `- ${s.id}: ${s.title} (${s.persona}) \u2014 ${s.des
|
|
|
635
638
|
output.coordinatorLog(`Task claimed by orchestrator`);
|
|
636
639
|
output.log(story.persona, `Starting ${story.title}`);
|
|
637
640
|
output.log(story.persona, `Executing story with AIClient (model: ${modelName})...`);
|
|
641
|
+
info(`Story ${i + 1}/${sorted.length} started`, { persona: story.persona, title: story.title, provider, model: modelName });
|
|
638
642
|
output.status("");
|
|
639
643
|
const model = createModel(provider, modelName, host, contextLength);
|
|
640
644
|
const allTools = createToolDefinitions(workingDir, model, sandboxed);
|
|
@@ -765,13 +769,16 @@ ${revisionFeedback}` : ""}`;
|
|
|
765
769
|
costTracker.addUsage(persona.name, provider, modelName, inTokens, outTokens);
|
|
766
770
|
output.updateCost?.(costTracker.getTotalCost());
|
|
767
771
|
output.log(story.persona, `${story.title} \u2014 completed! (${i + 1}/${sorted.length})`);
|
|
772
|
+
info(`Story ${i + 1} completed`, { persona: story.persona, inputTokens: inTokens, outputTokens: outTokens });
|
|
768
773
|
output.log("system", "");
|
|
769
774
|
break;
|
|
770
775
|
} catch (err) {
|
|
771
776
|
output.statusDone();
|
|
772
777
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
778
|
+
error(`Story ${i + 1} error`, { persona: story.persona, error: errMsg, revision });
|
|
773
779
|
if (isTransientError(err) && revision < 2) {
|
|
774
780
|
output.log(story.persona, `Transient error: ${errMsg} \u2014 retrying...`);
|
|
781
|
+
info(`Story ${i + 1} retrying (transient)`, { revision });
|
|
775
782
|
continue;
|
|
776
783
|
}
|
|
777
784
|
output.error(`Story ${i + 1} failed: ${errMsg}`);
|
|
@@ -812,8 +819,10 @@ ${revisionFeedback}` : ""}`;
|
|
|
812
819
|
}
|
|
813
820
|
}
|
|
814
821
|
let previousReviewFeedback = "";
|
|
822
|
+
info("Starting review loop", { maxRevisions, approvalThreshold, provider: revProvider, model: revModel });
|
|
815
823
|
for (let reviewRound = 0; reviewRound <= maxRevisions; reviewRound++) {
|
|
816
824
|
const isRevision = reviewRound > 0;
|
|
825
|
+
info(`Review round ${reviewRound}`, { isRevision, maxRevisions });
|
|
817
826
|
output.coordinatorLog(isRevision ? `Starting Tech Lead review (revision ${reviewRound}/${maxRevisions})...` : "Starting Tech Lead review...");
|
|
818
827
|
output.log("tech_lead", `Starting agent execution (model: ${revModel})`);
|
|
819
828
|
output.status(isRevision ? "Reviewer -- Re-checking after revisions" : "Reviewer -- Checking code quality");
|
|
@@ -907,6 +916,7 @@ AFFECTED_REASONS: {"2": "Missing error handling in auth controller", "3": "Front
|
|
|
907
916
|
output.statusDone();
|
|
908
917
|
const score = extractScore(reviewText);
|
|
909
918
|
const approved = score >= approvalThreshold;
|
|
919
|
+
info(`Review round ${reviewRound} result`, { score, approved, threshold: approvalThreshold, reviewTextLength: reviewText.length });
|
|
910
920
|
output.log("tech_lead", `::code_quality_score::${score}`);
|
|
911
921
|
output.log("tech_lead", `::review_decision::${approved ? "approved" : "needs_revision"}`);
|
|
912
922
|
output.coordinatorLog(approved ? `Review approved (score: ${score}/100)` : `Review needs revision (score: ${score}/100)`);
|