opencode-usage-coach 0.11.2 → 0.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +41 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -217,7 +217,7 @@ function truncate(input, max) {
|
|
|
217
217
|
return text.length > max ? `${text.slice(0, max)}...` : text;
|
|
218
218
|
}
|
|
219
219
|
function sanitizeQuery(raw) {
|
|
220
|
-
return raw.replace(/```[\s\S]*?```/g, " ").replace(/`[^`]*`/g, " ").replace(/
|
|
220
|
+
return raw.replace(/```[\s\S]*?```/g, " ").replace(/`[^`]*`/g, " ").replace(/https?:\/\/\S+/g, " ").replace(/[^\p{L}\p{N}\s]/gu, " ").replace(/\b(NOT|AND|OR)\b/gi, " ").replace(/\s+/g, " ").trim().slice(0, GH_QUERY_MAX);
|
|
221
221
|
}
|
|
222
222
|
function effectiveFrameworks(frameworks, keyDeps) {
|
|
223
223
|
const set = new Set((frameworks || []).filter(Boolean));
|
|
@@ -231,8 +231,30 @@ function effectiveFrameworks(frameworks, keyDeps) {
|
|
|
231
231
|
async function ghFetch(url, signal) {
|
|
232
232
|
const res = await fetch(url, { headers: ghHeaders(), signal });
|
|
233
233
|
if (!res.ok) {
|
|
234
|
-
|
|
235
|
-
|
|
234
|
+
let ghError = null;
|
|
235
|
+
let ghErrorText = "";
|
|
236
|
+
try {
|
|
237
|
+
ghErrorText = await res.text();
|
|
238
|
+
ghError = JSON.parse(ghErrorText);
|
|
239
|
+
} catch {
|
|
240
|
+
}
|
|
241
|
+
const tag = res.status === 403 || res.status === 429 ? " (rate limit)" : res.status === 422 ? " (validation failed)" : "";
|
|
242
|
+
const errs = ghError?.errors;
|
|
243
|
+
const errMsg = ghError?.message ?? ghErrorText.slice(0, 300);
|
|
244
|
+
const detail = errs ? errs.map((e) => typeof e === "string" ? e : `${e?.field ?? "?"}: ${e?.message ?? e?.code ?? JSON.stringify(e)}`).join("; ") : "";
|
|
245
|
+
console.error(JSON.stringify({
|
|
246
|
+
level: "error",
|
|
247
|
+
module: "web-search",
|
|
248
|
+
event: "gh-fetch-error",
|
|
249
|
+
status: res.status,
|
|
250
|
+
tag,
|
|
251
|
+
url,
|
|
252
|
+
ghMessage: errMsg,
|
|
253
|
+
ghErrors: detail,
|
|
254
|
+
rateLimitRemaining: res.headers.get("x-ratelimit-remaining"),
|
|
255
|
+
rateLimitReset: res.headers.get("x-ratelimit-reset")
|
|
256
|
+
}));
|
|
257
|
+
throw new Error(`HTTP ${res.status}${tag}: ${errMsg}${detail ? ` | ${detail}` : ""}`);
|
|
236
258
|
}
|
|
237
259
|
return await res.json();
|
|
238
260
|
}
|
|
@@ -249,13 +271,15 @@ async function tier1OfficialDocs(query, fws, results, docRefs, seen, signal) {
|
|
|
249
271
|
if (entry) docRefs.push({ name: entry.name, url: entry.docs });
|
|
250
272
|
}
|
|
251
273
|
if (!query) return errors;
|
|
274
|
+
const cleanQuery = sanitizeQuery(query);
|
|
275
|
+
if (!cleanQuery) return errors;
|
|
252
276
|
for (const fw of fws) {
|
|
253
277
|
if (signal.aborted || results.length >= TARGET_RESULT_COUNT) break;
|
|
254
278
|
const entry = FRAMEWORK_DOCS[fw];
|
|
255
279
|
if (!entry?.githubOrg) continue;
|
|
256
280
|
try {
|
|
257
|
-
const
|
|
258
|
-
const url = `https://api.github.com/search/issues?q=${encodeURIComponent(
|
|
281
|
+
const fullQ = `${cleanQuery} org:${entry.githubOrg}`;
|
|
282
|
+
const url = `https://api.github.com/search/issues?q=${encodeURIComponent(fullQ)}&per_page=3`;
|
|
259
283
|
const data = await ghFetch(url, signal);
|
|
260
284
|
for (const item of data.items ?? []) {
|
|
261
285
|
pushResult(results, seen, {
|
|
@@ -276,7 +300,9 @@ async function tier1OfficialDocs(query, fws, results, docRefs, seen, signal) {
|
|
|
276
300
|
async function tier2GitHubIssues(query, results, seen, signal) {
|
|
277
301
|
const errors = [];
|
|
278
302
|
try {
|
|
279
|
-
const
|
|
303
|
+
const q = sanitizeQuery(query);
|
|
304
|
+
if (!q) return errors;
|
|
305
|
+
const url = `https://api.github.com/search/issues?q=${encodeURIComponent(q)}&per_page=5`;
|
|
280
306
|
const data = await ghFetch(url, signal);
|
|
281
307
|
for (const item of data.items ?? []) {
|
|
282
308
|
pushResult(results, seen, {
|
|
@@ -297,7 +323,9 @@ async function tier3GitHubCode(query, results, seen, signal) {
|
|
|
297
323
|
const errors = [];
|
|
298
324
|
if (!ghToken()) return errors;
|
|
299
325
|
try {
|
|
300
|
-
const
|
|
326
|
+
const q = sanitizeQuery(query);
|
|
327
|
+
if (!q) return errors;
|
|
328
|
+
const url = `https://api.github.com/search/code?q=${encodeURIComponent(q)}&per_page=3`;
|
|
301
329
|
const data = await ghFetch(url, signal);
|
|
302
330
|
for (const item of data.items ?? []) {
|
|
303
331
|
const repo = item.repository?.full_name;
|
|
@@ -1613,6 +1641,7 @@ PATH A \u2014 INDEPENDENT (parallel via generate_batch):
|
|
|
1613
1641
|
4. for each i: PASS -> task_update(i, title, "completed", "PASS"); FAIL -> revise (up to 2x) or task_update(i, title, "failed", "FAIL")
|
|
1614
1642
|
|
|
1615
1643
|
PATH B \u2014 DEPENDENT (sequential):
|
|
1644
|
+
Optional: task_update(1..N, title, "pending") \u2190 pre-register all tasks first
|
|
1616
1645
|
for i in 1..${args.total}:
|
|
1617
1646
|
1. task_update(i, title, "generating")
|
|
1618
1647
|
2. generate({prompt:"Task: <title>. Perform it."}) -> work + NEXT
|
|
@@ -1795,25 +1824,23 @@ Then: harness_done(). Follow the [usage-coach NEXT] directive each tool returns.
|
|
|
1795
1824
|
args: {
|
|
1796
1825
|
id: tool.schema.number(),
|
|
1797
1826
|
title: tool.schema.string(),
|
|
1798
|
-
status: tool.schema.string().describe("generating | grading | revising | completed | failed | timed_out"),
|
|
1827
|
+
status: tool.schema.string().describe("pending | generating | grading | revising | completed | failed | timed_out"),
|
|
1799
1828
|
revisions: tool.schema.number().optional(),
|
|
1800
1829
|
score: tool.schema.string().optional().describe("PASS | FAIL"),
|
|
1801
1830
|
model: tool.schema.string().optional()
|
|
1802
1831
|
},
|
|
1803
1832
|
async execute(args, ctx) {
|
|
1804
|
-
const VALID_STATUSES = ["generating", "grading", "revising", "completed", "failed", "timed_out", "halted_quota"];
|
|
1805
|
-
|
|
1806
|
-
return `ERROR: task_update status must be one of: ${VALID_STATUSES.join(", ")}. Got: "${args.status}". Call task_update with a valid status.`;
|
|
1807
|
-
}
|
|
1833
|
+
const VALID_STATUSES = ["pending", "generating", "grading", "revising", "completed", "failed", "timed_out", "halted_quota"];
|
|
1834
|
+
const status = args.status && VALID_STATUSES.includes(args.status) ? args.status : "generating";
|
|
1808
1835
|
const cfg = readHarnessCfg(ctx.directory);
|
|
1809
1836
|
const h = readHarness(ctx.sessionID) ?? { name: "batch", total: 0, current: 0, tasks: [], usage: {}, active: true };
|
|
1810
1837
|
h.tasks = h.tasks.filter((x) => x.id !== args.id);
|
|
1811
1838
|
const model = args.model || cfg.generator || "";
|
|
1812
1839
|
if (!model) return `ERROR: task ${args.id} has no model and no generator configured. Set "generator" in harness.config.json.`;
|
|
1813
|
-
h.tasks.push({ id: args.id, title: args.title, status
|
|
1840
|
+
h.tasks.push({ id: args.id, title: args.title, status, model, revisions: args.revisions ?? 0, score: args.score ?? null, startedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1814
1841
|
if (args.id > h.current) h.current = args.id;
|
|
1815
1842
|
writeHarness(ctx.sessionID, h);
|
|
1816
|
-
return `task ${args.id} -> ${
|
|
1843
|
+
return `task ${args.id} -> ${status}${args.score ? ` (${args.score})` : ""}`;
|
|
1817
1844
|
}
|
|
1818
1845
|
}),
|
|
1819
1846
|
harness_done: tool({
|
package/package.json
CHANGED