@kodrunhq/opencode-autopilot 1.14.1 → 1.15.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/package.json +1 -1
- package/src/orchestrator/artifacts.ts +1 -1
- package/src/orchestrator/contracts/invariants.ts +121 -0
- package/src/orchestrator/contracts/legacy-result-adapter.ts +47 -0
- package/src/orchestrator/contracts/phase-artifacts.ts +90 -0
- package/src/orchestrator/contracts/result-envelope.ts +23 -0
- package/src/orchestrator/handlers/architect.ts +5 -1
- package/src/orchestrator/handlers/build.ts +110 -18
- package/src/orchestrator/handlers/challenge.ts +3 -1
- package/src/orchestrator/handlers/explore.ts +1 -0
- package/src/orchestrator/handlers/plan.ts +85 -8
- package/src/orchestrator/handlers/recon.ts +3 -1
- package/src/orchestrator/handlers/retrospective.ts +8 -0
- package/src/orchestrator/handlers/ship.ts +6 -1
- package/src/orchestrator/handlers/types.ts +21 -2
- package/src/orchestrator/renderers/tasks-markdown.ts +22 -0
- package/src/orchestrator/replay.ts +14 -0
- package/src/orchestrator/schemas.ts +19 -0
- package/src/orchestrator/state.ts +48 -7
- package/src/orchestrator/types.ts +4 -0
- package/src/review/pipeline.ts +41 -6
- package/src/review/schemas.ts +6 -0
- package/src/review/types.ts +2 -0
- package/src/tools/doctor.ts +34 -0
- package/src/tools/forensics.ts +34 -0
- package/src/tools/orchestrate.ts +418 -54
- package/src/tools/quick.ts +4 -0
- package/src/tools/review.ts +27 -2
- package/src/types/inquirer-shims.d.ts +42 -0
package/src/tools/doctor.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
1
3
|
import type { Config } from "@opencode-ai/plugin";
|
|
2
4
|
import { tool } from "@opencode-ai/plugin";
|
|
3
5
|
import { runHealthChecks } from "../health/runner";
|
|
4
6
|
import type { HealthResult } from "../health/types";
|
|
7
|
+
import { getProjectArtifactDir } from "../utils/paths";
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* A single check in the doctor report, with an optional fix suggestion.
|
|
@@ -13,6 +16,35 @@ interface DoctorCheck {
|
|
|
13
16
|
readonly fixSuggestion: string | null;
|
|
14
17
|
}
|
|
15
18
|
|
|
19
|
+
interface ContractHealth {
|
|
20
|
+
readonly legacyTasksFallbackSeen: boolean;
|
|
21
|
+
readonly legacyResultParserSeen: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function detectContractHealth(projectRoot?: string): Promise<ContractHealth> {
|
|
25
|
+
if (!projectRoot) {
|
|
26
|
+
return {
|
|
27
|
+
legacyTasksFallbackSeen: false,
|
|
28
|
+
legacyResultParserSeen: false,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const artifactDir = getProjectArtifactDir(projectRoot);
|
|
34
|
+
const logPath = join(artifactDir, "orchestration.jsonl");
|
|
35
|
+
const content = await readFile(logPath, "utf-8");
|
|
36
|
+
return {
|
|
37
|
+
legacyTasksFallbackSeen: content.includes("PLAN fallback: parsed legacy tasks.md"),
|
|
38
|
+
legacyResultParserSeen: content.includes("Legacy result parser path used"),
|
|
39
|
+
};
|
|
40
|
+
} catch {
|
|
41
|
+
return {
|
|
42
|
+
legacyTasksFallbackSeen: false,
|
|
43
|
+
legacyResultParserSeen: false,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
16
48
|
/**
|
|
17
49
|
* Options for doctorCore — all optional for testability.
|
|
18
50
|
*/
|
|
@@ -92,6 +124,7 @@ export async function doctorCore(options?: DoctorOptions): Promise<string> {
|
|
|
92
124
|
});
|
|
93
125
|
|
|
94
126
|
const allChecks = [...healthChecks, hookCheck];
|
|
127
|
+
const contractHealth = await detectContractHealth(options?.projectRoot);
|
|
95
128
|
const allPassed = report.allPassed && hookCheck.status === "pass";
|
|
96
129
|
const displayText = buildDisplayText(allChecks, report.duration);
|
|
97
130
|
|
|
@@ -99,6 +132,7 @@ export async function doctorCore(options?: DoctorOptions): Promise<string> {
|
|
|
99
132
|
action: "doctor",
|
|
100
133
|
checks: allChecks,
|
|
101
134
|
allPassed,
|
|
135
|
+
contractHealth,
|
|
102
136
|
displayText,
|
|
103
137
|
duration: report.duration,
|
|
104
138
|
});
|
package/src/tools/forensics.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
1
3
|
import { tool } from "@opencode-ai/plugin";
|
|
2
4
|
import { loadState } from "../orchestrator/state";
|
|
3
5
|
import { getProjectArtifactDir } from "../utils/paths";
|
|
@@ -21,6 +23,36 @@ function getSuggestedAction(failedPhase: string, recoverable: boolean): "resume"
|
|
|
21
23
|
return "resume";
|
|
22
24
|
}
|
|
23
25
|
|
|
26
|
+
async function readRecentContractEvents(artifactDir: string): Promise<readonly string[]> {
|
|
27
|
+
try {
|
|
28
|
+
const raw = await readFile(join(artifactDir, "orchestration.jsonl"), "utf-8");
|
|
29
|
+
const lines = raw
|
|
30
|
+
.split("\n")
|
|
31
|
+
.map((line) => line.trim())
|
|
32
|
+
.filter(Boolean)
|
|
33
|
+
.slice(-120);
|
|
34
|
+
const codes = new Set<string>();
|
|
35
|
+
for (const line of lines) {
|
|
36
|
+
for (const code of [
|
|
37
|
+
"E_INVALID_RESULT",
|
|
38
|
+
"E_STALE_RESULT",
|
|
39
|
+
"E_PHASE_MISMATCH",
|
|
40
|
+
"E_UNKNOWN_DISPATCH",
|
|
41
|
+
"E_DUPLICATE_RESULT",
|
|
42
|
+
"E_BUILD_TASK_ID_REQUIRED",
|
|
43
|
+
"E_BUILD_UNKNOWN_TASK",
|
|
44
|
+
]) {
|
|
45
|
+
if (line.includes(code)) {
|
|
46
|
+
codes.add(code);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return Object.freeze([...codes].sort());
|
|
51
|
+
} catch {
|
|
52
|
+
return Object.freeze([]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
24
56
|
export async function forensicsCore(
|
|
25
57
|
_args: Record<string, never>,
|
|
26
58
|
projectRoot: string,
|
|
@@ -66,6 +98,7 @@ export async function forensicsCore(
|
|
|
66
98
|
const recoverable = isRecoverable(failureContext.failedPhase);
|
|
67
99
|
const suggestedAction = getSuggestedAction(failureContext.failedPhase, recoverable);
|
|
68
100
|
const phasesCompleted = state.phases.filter((p) => p.status === "DONE").map((p) => p.name);
|
|
101
|
+
const deterministicErrorCodes = await readRecentContractEvents(artifactDir);
|
|
69
102
|
|
|
70
103
|
return JSON.stringify({
|
|
71
104
|
failedPhase: failureContext.failedPhase,
|
|
@@ -75,6 +108,7 @@ export async function forensicsCore(
|
|
|
75
108
|
recoverable,
|
|
76
109
|
suggestedAction,
|
|
77
110
|
phasesCompleted,
|
|
111
|
+
deterministicErrorCodes,
|
|
78
112
|
});
|
|
79
113
|
}
|
|
80
114
|
|