Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"test": "vitest run"
},
"dependencies": {
"@headsdown/sdk": "^0.2.12",
"@headsdown/sdk": "^0.2.13",
"@opencode-ai/plugin": "^1.4.0"
},
"devDependencies": {
Expand Down
54 changes: 38 additions & 16 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as HeadsDownSDK from "@headsdown/sdk";
import { CalibrationTracker, ConfigStore, HeadsDownClient, ProposalStateStore } from "@headsdown/sdk";
import type { Contract, ScheduleResolution } from "@headsdown/sdk";
import type { Contract, ScheduleResolution, Verdict } from "@headsdown/sdk";
import { type Plugin, tool } from "@opencode-ai/plugin";
import { evaluateGate } from "./policy.js";

Expand All @@ -15,18 +16,27 @@ async function getAuthenticatedClient(): Promise<HeadsDownClient> {
}
}

function buildWrapUpInstruction(
guidance:
| {
active?: boolean;
selectedMode?: "auto" | "wrap_up" | "full_depth";
remainingMinutes?: number | null;
reason?: string;
hints?: string[];
}
| null
| undefined,
): string | null {
function resolveExecutionInstruction(input: {
contract?: Contract | null;
schedule?: ScheduleResolution | null;
verdict?: Pick<Verdict, "decision" | "reason" | "wrapUpGuidance"> | null;
}): string | null {
const describeExecutionDirective = (
HeadsDownSDK as unknown as {
describeExecutionDirective?: (value: {
contract?: Contract | null;
schedule?: ScheduleResolution | null;
verdict?: Pick<Verdict, "decision" | "reason" | "wrapUpGuidance"> | null;
}) => { primaryDirective?: string };
}
).describeExecutionDirective;

if (typeof describeExecutionDirective === "function") {
const directive = describeExecutionDirective(input);
return directive.primaryDirective ?? null;
}

const guidance = input.verdict?.wrapUpGuidance ?? input.schedule?.wrapUpGuidance;
if (!guidance || !guidance.active) {
return null;
}
Expand Down Expand Up @@ -98,7 +108,10 @@ function formatAvailabilitySummary(contract: Contract | null, availability: Sche
parts.push(`Wrap-Up guidance: ${timing} (${availability.wrapUpGuidance.selectedMode})`);
}

const wrapUpInstruction = buildWrapUpInstruction(availability.wrapUpGuidance);
const wrapUpInstruction = resolveExecutionInstruction({
contract,
schedule: availability,
});
if (wrapUpInstruction) {
parts.push(`Wrap-Up instruction: ${wrapUpInstruction}`);
}
Expand Down Expand Up @@ -126,7 +139,10 @@ export const HeadsDownOpenCodePlugin: Plugin = async () => {
async execute() {
const client = await getAuthenticatedClient();
const { contract, schedule: availability } = await client.getAvailability();
const wrapUpInstruction = buildWrapUpInstruction(availability.wrapUpGuidance);
const wrapUpInstruction = resolveExecutionInstruction({
contract,
schedule: availability,
});
return JSON.stringify(
{
authenticated: true,
Expand Down Expand Up @@ -187,7 +203,13 @@ export const HeadsDownOpenCodePlugin: Plugin = async () => {
}
}

const wrapUpInstruction = buildWrapUpInstruction(verdict.wrapUpGuidance);
const wrapUpInstruction = resolveExecutionInstruction({
verdict: {
decision: verdict.decision,
reason: verdict.reason,
wrapUpGuidance: verdict.wrapUpGuidance,
},
});
return JSON.stringify(
{
decision: verdict.decision,
Expand Down
3 changes: 2 additions & 1 deletion test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ describe("OpenCode HeadsDown plugin source", () => {
const source = await readFile(join(import.meta.dirname, "..", "src", "index.ts"), "utf8");

expect(source).toContain("Execution policy for this task");
expect(source).toContain("buildWrapUpInstruction");
expect(source).toContain("resolveExecutionInstruction");
expect(source).toContain("wrapUpInstruction");
});

Expand All @@ -16,5 +16,6 @@ describe("OpenCode HeadsDown plugin source", () => {

expect(source).toContain("delivery_mode");
expect(source).toContain("deliveryMode: args.delivery_mode");
expect(source).toContain("describeExecutionDirective");
});
});
Loading