opencode-usage-coach 0.8.1 → 0.8.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/agents/usage-coach-harness.md +7 -5
- package/dist/index.js +72 -4
- package/package.json +1 -1
|
@@ -50,14 +50,15 @@ The user's message is the task source. If it has multiple distinct parts, decomp
|
|
|
50
50
|
DEPENDENT tasks (B needs A) → always sequential `generate` calls, regardless of quota.
|
|
51
51
|
|
|
52
52
|
1. Call `harness_start(name, N)` to register the run on the panel.
|
|
53
|
-
2. **
|
|
53
|
+
2. **DIAGNOSIS GATE — unknown_scan (REQUIRED, not optional):** Call `unknown_scan({prompt, tasks: [{id, title}, ...]})`.
|
|
54
|
+
This is enforced: if you skip it, `generate` will inject a ⚠ warning into the sub-session prompt.
|
|
54
55
|
Review the report:
|
|
55
56
|
- If QUESTIONS are flagged → ask the user concisely, then adjust tasks.
|
|
56
57
|
- If TASK REFINEMENTS are suggested → apply via `task_update` (split/add/remove).
|
|
57
|
-
- If UNKNOWN UNKNOWNS with high impact are found →
|
|
58
|
-
generate prompts
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
- If UNKNOWN UNKNOWNS with high impact are found → they will be auto-injected into
|
|
59
|
+
generate prompts via scanSummary, but you should explicitly acknowledge them.
|
|
60
|
+
You may skip unknown_scan ONLY for: revisions (applying grade feedback), trivial
|
|
61
|
+
single-file edits, or empty directories. Skipping must be a conscious choice.
|
|
61
62
|
3. For each task i (1..N):
|
|
62
63
|
a. `task_update(i, title, "generating")`.
|
|
63
64
|
b. **Generate** — call `generate({ prompt: "Task: {title}. Perform it for real in the current directory (write/edit files)." })`. The generator model runs in a sub-session and writes files directly — its return value is a summary, NOT the work itself.
|
|
@@ -72,6 +73,7 @@ DEPENDENT tasks (B needs A) → always sequential `generate` calls, regardless o
|
|
|
72
73
|
4. When all tasks are done → `harness_done()`.
|
|
73
74
|
|
|
74
75
|
## Rules
|
|
76
|
+
- **Diagnose before acting.** Never implement a fix based on a problem description without verifying what actually happened. Read logs, check source code, reproduce the issue. If you find yourself writing code within 60 seconds of reading a problem, STOP and verify your assumptions first.
|
|
75
77
|
- **Follow the [usage-coach NEXT] directive each tool returns.** `harness_start`, `generate`, and `grade` all append a `NEXT` line telling you exactly what to call next. This makes the loop deterministic — do not improvise the sequence, follow `NEXT`.
|
|
76
78
|
- In the loop, do NOT do the work yourself — call `generate`/`grade` (they run the configured models). You orchestrate. (Outside the loop, for trivial requests, act directly.)
|
|
77
79
|
- Call `task_update` on every state transition — the sidebar panel reads it for live visibility.
|
package/dist/index.js
CHANGED
|
@@ -837,11 +837,48 @@ function writeUnknownScan(sessionID, result) {
|
|
|
837
837
|
const h = readHarness(sessionID);
|
|
838
838
|
if (h) {
|
|
839
839
|
h.unknownScan = result;
|
|
840
|
+
h.scanDone = true;
|
|
841
|
+
h.scanSummary = buildScanSummary(result);
|
|
840
842
|
writeHarness(sessionID, h);
|
|
841
843
|
}
|
|
842
844
|
} catch {
|
|
843
845
|
}
|
|
844
846
|
}
|
|
847
|
+
function buildScanSummary(r) {
|
|
848
|
+
const lines = [];
|
|
849
|
+
if (r.unknownUnknowns?.length) {
|
|
850
|
+
lines.push(`Unknown Unknowns (${r.unknownUnknowns.length}):`);
|
|
851
|
+
for (const uu of r.unknownUnknowns.slice(0, 5)) {
|
|
852
|
+
lines.push(` [${uu.impact?.toUpperCase() ?? "?"}] ${uu.finding}${uu.mitigation ? ` \u2192 ${uu.mitigation}` : ""}`);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
if (r.unknownKnowns?.length) {
|
|
856
|
+
lines.push(`Implicit knowledge (${r.unknownKnowns.length}):`);
|
|
857
|
+
for (const uk of r.unknownKnowns.slice(0, 5)) {
|
|
858
|
+
lines.push(` \u2139 ${uk.finding}`);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
if (r.questions?.length) {
|
|
862
|
+
lines.push(`Pending questions (${r.questions.length}):`);
|
|
863
|
+
for (const q of r.questions.slice(0, 5)) {
|
|
864
|
+
lines.push(` [Q] ${q.question}`);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return lines.join("\n");
|
|
868
|
+
}
|
|
869
|
+
function checkScanGate(sessionID) {
|
|
870
|
+
try {
|
|
871
|
+
const h = readHarness(sessionID);
|
|
872
|
+
if (!h || !h.scanRequired) return { warning: null, summary: null };
|
|
873
|
+
if (h.scanDone) return { warning: null, summary: h.scanSummary ?? null };
|
|
874
|
+
return {
|
|
875
|
+
warning: `\u26A0 DIAGNOSIS GATE: unknown_scan was NOT called before this generate. You are generating without pre-flight gap analysis. Blind spots (unknown unknowns) may cause wrong assumptions and waste steps. Call unknown_scan first, OR proceed consciously accepting the risk.`,
|
|
876
|
+
summary: null
|
|
877
|
+
};
|
|
878
|
+
} catch {
|
|
879
|
+
return { warning: null, summary: null };
|
|
880
|
+
}
|
|
881
|
+
}
|
|
845
882
|
function readHarnessCfg(dir) {
|
|
846
883
|
const tryRead = (p) => {
|
|
847
884
|
try {
|
|
@@ -1275,12 +1312,15 @@ async function UsageCoachPlugin(input) {
|
|
|
1275
1312
|
description: "Start the harness: register the total task count on the panel. Call once when the harness loop begins. IMPORTANT: each generate/generate_batch sub-session is step-limited (default 30). If any task seems too large, split it into smaller subtasks BEFORE starting \u2014 oversized tasks will timeout.",
|
|
1276
1313
|
args: { name: tool.schema.string(), total: tool.schema.number() },
|
|
1277
1314
|
async execute(args, ctx) {
|
|
1278
|
-
writeHarness(ctx.sessionID, { name: args.name, total: args.total, current: 0, tasks: [], usage: {}, active: true, startedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1315
|
+
writeHarness(ctx.sessionID, { name: args.name, total: args.total, current: 0, tasks: [], usage: {}, active: true, scanRequired: true, startedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1279
1316
|
return `Harness '${args.name}' started (${args.total} tasks).
|
|
1280
1317
|
|
|
1281
|
-
|
|
1318
|
+
\u26A0 DIAGNOSIS GATE \u2014 unknown_scan is REQUIRED before generate/generate_batch.
|
|
1282
1319
|
unknown_scan({ prompt: "<user request>", tasks: [{id:1, title:"..."}, ...] })
|
|
1283
|
-
|
|
1320
|
+
If you skip it, generate will inject a \u26A0 warning into the sub-session prompt.
|
|
1321
|
+
Review the report: if QUESTIONS are flagged \u2192 ask the user first. If TASK
|
|
1322
|
+
REFINEMENTS are suggested \u2192 apply via task_update. Unknown unknowns found will
|
|
1323
|
+
be automatically injected into generate prompts as context.
|
|
1284
1324
|
|
|
1285
1325
|
STEP LIMIT (default ${DEFAULT_MAX_STEPS}): each generate call creates a sub-session that is automatically aborted if it exceeds ${DEFAULT_MAX_STEPS} assistant steps. Before starting the loop, review each task: can it be completed in a focused, single-pass effort? If a task seems too broad (multiple files, multiple features, open-ended research), SPLIT it now into 2-3 smaller subtasks. A timeout wastes quota \u2014 split upfront.
|
|
1286
1326
|
|
|
@@ -1643,6 +1683,22 @@ ${priorNotes}
|
|
|
1643
1683
|
log(`generate impl-notes read err: ${String(e)}`);
|
|
1644
1684
|
}
|
|
1645
1685
|
prefix += IMPL_NOTE_INSTRUCTION;
|
|
1686
|
+
const gate = checkScanGate(ctx.sessionID);
|
|
1687
|
+
if (gate.warning) {
|
|
1688
|
+
prefix = `${gate.warning}
|
|
1689
|
+
|
|
1690
|
+
---
|
|
1691
|
+
|
|
1692
|
+
` + prefix;
|
|
1693
|
+
}
|
|
1694
|
+
if (gate.summary) {
|
|
1695
|
+
prefix = `Pre-flight scan findings (from unknown_scan \u2014 heed these):
|
|
1696
|
+
${gate.summary}
|
|
1697
|
+
|
|
1698
|
+
---
|
|
1699
|
+
|
|
1700
|
+
` + prefix;
|
|
1701
|
+
}
|
|
1646
1702
|
const genTaskId = findActiveTaskId(ctx.sessionID, "generating");
|
|
1647
1703
|
const maxSteps = args.max_steps ?? DEFAULT_MAX_STEPS;
|
|
1648
1704
|
const out = await runModel(
|
|
@@ -1699,8 +1755,20 @@ ${priorNotes}
|
|
|
1699
1755
|
const rules = readRules();
|
|
1700
1756
|
const priorNotes = readImplNotes(5);
|
|
1701
1757
|
const maxSteps = args.max_steps ?? DEFAULT_MAX_STEPS;
|
|
1758
|
+
const gate = checkScanGate(ctx.sessionID);
|
|
1759
|
+
const gatePrefix = gate.warning ? `${gate.warning}
|
|
1760
|
+
|
|
1761
|
+
---
|
|
1762
|
+
|
|
1763
|
+
` : gate.summary ? `Pre-flight scan findings (from unknown_scan \u2014 heed these):
|
|
1764
|
+
${gate.summary}
|
|
1765
|
+
|
|
1766
|
+
---
|
|
1767
|
+
|
|
1768
|
+
` : "";
|
|
1702
1769
|
const runOne = async (t) => {
|
|
1703
|
-
let prefix =
|
|
1770
|
+
let prefix = gatePrefix;
|
|
1771
|
+
prefix += rules ? `Lessons learned from previous failures (apply where relevant):
|
|
1704
1772
|
${rules}
|
|
1705
1773
|
|
|
1706
1774
|
---
|
package/package.json
CHANGED