opencode-usage-coach 0.9.0 → 0.10.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/dist/index.js +86 -11
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -909,6 +909,25 @@ function readHarnessCfg(dir) {
|
|
|
909
909
|
...tryRead(join2(dir, "harness.config.json"))
|
|
910
910
|
};
|
|
911
911
|
}
|
|
912
|
+
function writeHarnessCfg(updates) {
|
|
913
|
+
const configDir = join2(homedir(), ".config", "opencode-usage-coach");
|
|
914
|
+
const configPath = join2(configDir, "harness.config.json");
|
|
915
|
+
try {
|
|
916
|
+
mkdirSync2(configDir, { recursive: true });
|
|
917
|
+
const existing = (() => {
|
|
918
|
+
try {
|
|
919
|
+
return JSON.parse(readFileSync2(configPath, "utf8"));
|
|
920
|
+
} catch {
|
|
921
|
+
return {};
|
|
922
|
+
}
|
|
923
|
+
})();
|
|
924
|
+
const merged = { ...existing, ...updates };
|
|
925
|
+
writeFileSync2(configPath, JSON.stringify(merged, null, 2) + "\n");
|
|
926
|
+
return configPath;
|
|
927
|
+
} catch (e) {
|
|
928
|
+
throw new Error(`Failed to write harness config: ${String(e)}`, { cause: e });
|
|
929
|
+
}
|
|
930
|
+
}
|
|
912
931
|
async function runModel(client, model, prompt, directory, track, maxSteps = DEFAULT_MAX_STEPS) {
|
|
913
932
|
const t0 = Date.now();
|
|
914
933
|
const subStart = Date.now();
|
|
@@ -1169,17 +1188,15 @@ function fetchQuota(provider) {
|
|
|
1169
1188
|
});
|
|
1170
1189
|
});
|
|
1171
1190
|
}
|
|
1172
|
-
function fetchQuotaWithRetry(provider, maxRetries = 3) {
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
await new Promise((r) => setTimeout(r, 1e3 * (attempt + 1)));
|
|
1179
|
-
}
|
|
1191
|
+
async function fetchQuotaWithRetry(provider, maxRetries = 3) {
|
|
1192
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
1193
|
+
const q = await fetchQuota(provider);
|
|
1194
|
+
if (q) return q;
|
|
1195
|
+
if (attempt < maxRetries - 1) {
|
|
1196
|
+
await new Promise((r) => setTimeout(r, 1e3 * (attempt + 1)));
|
|
1180
1197
|
}
|
|
1181
|
-
|
|
1182
|
-
|
|
1198
|
+
}
|
|
1199
|
+
return null;
|
|
1183
1200
|
}
|
|
1184
1201
|
function coach(q, lighter) {
|
|
1185
1202
|
if (!q) return { decision: "GO", advice: "quota unavailable \u2014 retrying. proceeding cautiously.", weekly: -2, monthly: -2, fiveHour: -2 };
|
|
@@ -2141,6 +2158,62 @@ If the task is already well-specified with no significant ambiguities, return {"
|
|
|
2141
2158
|
const qNum = state.currentIndex + 1;
|
|
2142
2159
|
return formatQuestionOutput(qNum, total, q);
|
|
2143
2160
|
}
|
|
2161
|
+
}),
|
|
2162
|
+
coach_config: tool({
|
|
2163
|
+
description: 'View or update harness model configuration (generator, grader, lighterModel, provider). Call with no args to view current config. Pass any combination of generator/grader/lighterModel/provider to update. Example: coach_config({ generator: "anthropic/claude-sonnet-4-20250514", grader: "opencode/mimo-v2.5-free" })',
|
|
2164
|
+
args: {
|
|
2165
|
+
generator: tool.schema.string().optional(),
|
|
2166
|
+
grader: tool.schema.string().optional(),
|
|
2167
|
+
lighterModel: tool.schema.string().optional(),
|
|
2168
|
+
provider: tool.schema.string().optional()
|
|
2169
|
+
},
|
|
2170
|
+
async execute(args, ctx) {
|
|
2171
|
+
const dir = ctx?.directory ?? input.directory;
|
|
2172
|
+
const current2 = readHarnessCfg(dir);
|
|
2173
|
+
const hasUpdates = args.generator !== void 0 || args.grader !== void 0 || args.lighterModel !== void 0 || args.provider !== void 0;
|
|
2174
|
+
if (!hasUpdates) {
|
|
2175
|
+
const envOverrides = [];
|
|
2176
|
+
if (process.env.UC_PROVIDER) envOverrides.push(`UC_PROVIDER=${process.env.UC_PROVIDER}`);
|
|
2177
|
+
if (process.env.UC_LIGHTER_MODEL) envOverrides.push(`UC_LIGHTER_MODEL=${process.env.UC_LIGHTER_MODEL}`);
|
|
2178
|
+
return [
|
|
2179
|
+
`Harness Configuration`,
|
|
2180
|
+
`\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
2181
|
+
` generator: ${current2.generator ?? "(not set \u2014 harness won't work!)"}`,
|
|
2182
|
+
` grader: ${current2.grader ?? "(defaults to generator)"}`,
|
|
2183
|
+
` lighterModel: ${current2.lighterModel ?? "(not set \u2014 no THROTTLE fallback)"}`,
|
|
2184
|
+
` provider: ${current2.provider ?? "(auto-detected from model)"}`,
|
|
2185
|
+
`\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
2186
|
+
envOverrides.length > 0 ? `
|
|
2187
|
+
Environment overrides (take precedence):
|
|
2188
|
+
${envOverrides.join("\n ")}` : "",
|
|
2189
|
+
`
|
|
2190
|
+
Config file: ~/.config/opencode-usage-coach/harness.config.json`,
|
|
2191
|
+
`
|
|
2192
|
+
To update, call: coach_config({ generator: "provider/model-id", ... })`
|
|
2193
|
+
].filter(Boolean).join("\n");
|
|
2194
|
+
}
|
|
2195
|
+
const updates = {};
|
|
2196
|
+
if (args.generator !== void 0) updates.generator = args.generator.trim();
|
|
2197
|
+
if (args.grader !== void 0) updates.grader = args.grader.trim();
|
|
2198
|
+
if (args.lighterModel !== void 0) updates.lighterModel = args.lighterModel.trim();
|
|
2199
|
+
if (args.provider !== void 0) updates.provider = args.provider.trim();
|
|
2200
|
+
const writtenPath = writeHarnessCfg(updates);
|
|
2201
|
+
const updated = readHarnessCfg(dir);
|
|
2202
|
+
return [
|
|
2203
|
+
`\u2705 Harness config updated successfully!`,
|
|
2204
|
+
`\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
2205
|
+
` generator: ${updated.generator ?? "(not set)"}`,
|
|
2206
|
+
` grader: ${updated.grader ?? "(defaults to generator)"}`,
|
|
2207
|
+
` lighterModel: ${updated.lighterModel ?? "(not set)"}`,
|
|
2208
|
+
` provider: ${updated.provider ?? "(auto-detected)"}`,
|
|
2209
|
+
`\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
2210
|
+
`
|
|
2211
|
+
Saved to: ${writtenPath}`,
|
|
2212
|
+
`
|
|
2213
|
+
Note: Changes take effect immediately for new generate/grade calls.`,
|
|
2214
|
+
` A running harness will pick up the new config on the next tool call.`
|
|
2215
|
+
].join("\n");
|
|
2216
|
+
}
|
|
2144
2217
|
})
|
|
2145
2218
|
}
|
|
2146
2219
|
};
|
|
@@ -2170,8 +2243,10 @@ export {
|
|
|
2170
2243
|
providerAdvice,
|
|
2171
2244
|
providerToCodexbar,
|
|
2172
2245
|
readHarness,
|
|
2246
|
+
readHarnessCfg,
|
|
2173
2247
|
readRules,
|
|
2174
2248
|
setStateDir,
|
|
2175
2249
|
updateSubSession,
|
|
2176
|
-
writeHarness
|
|
2250
|
+
writeHarness,
|
|
2251
|
+
writeHarnessCfg
|
|
2177
2252
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-usage-coach",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "opencode closed-loop usage coach — quota SENSE -> coaching DECIDE -> loop ACT + TUI integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"@opencode-ai/plugin": "*",
|
|
54
54
|
"@opentui/core": ">=0.4.0",
|
|
55
55
|
"@opentui/solid": ">=0.4.0",
|
|
56
|
+
"@types/node": "^26.1.1",
|
|
56
57
|
"c8": "^11.0.0",
|
|
57
58
|
"esbuild-plugin-solid": "^0.6.0",
|
|
58
59
|
"eslint": "^10.6.0",
|