@kodrunhq/opencode-autopilot 1.6.0 → 1.7.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kodrunhq/opencode-autopilot",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Curated agents, skills, and commands for the OpenCode AI coding CLI — autonomous orchestrator, multi-agent code review, model fallback, and in-session asset creation tools.",
5
5
  "main": "src/index.ts",
6
6
  "keywords": [
@@ -1,3 +1,5 @@
1
+ import type { Database } from "bun:sqlite";
2
+ import { getObservationsByProject, getProjectByPath } from "../memory/repository";
1
3
  import { summarizeConfidence } from "./confidence";
2
4
  import type { ConfidenceEntry } from "./types";
3
5
 
@@ -39,3 +41,32 @@ export function shouldTriggerExplorer(
39
41
  const thresholdOrder = LEVEL_ORDER[threshold];
40
42
  return entries.some((entry) => LEVEL_ORDER[entry.level] < thresholdOrder);
41
43
  }
44
+
45
+ /** Minimum error observations to trigger deeper review. */
46
+ const ERROR_DEPTH_THRESHOLD = 3;
47
+
48
+ /**
49
+ * Memory-tuned debate depth: adjusts arena depth based on project error history.
50
+ * Scans the 50 most-recent observations (recency window) — older errors are not counted.
51
+ * Projects with 3+ error observations in the window get deeper review (+1, capped at 3).
52
+ * Best-effort: memory errors never affect pipeline (falls back to standard depth).
53
+ */
54
+ export function getMemoryTunedDepth(
55
+ entries: readonly ConfidenceEntry[],
56
+ projectPath: string,
57
+ db?: Database,
58
+ ): number {
59
+ const baseDepth = getDebateDepth(entries);
60
+ try {
61
+ const project = getProjectByPath(projectPath, db);
62
+ if (!project) return baseDepth;
63
+ const observations = getObservationsByProject(project.id, 50, db);
64
+ const errorCount = observations.filter((o) => o.type === "error").length;
65
+ if (errorCount >= ERROR_DEPTH_THRESHOLD) {
66
+ return Math.min(baseDepth + 1, 3);
67
+ }
68
+ } catch (err) {
69
+ console.warn("[opencode-autopilot] memory-tuned depth failed, using base:", err);
70
+ }
71
+ return baseDepth;
72
+ }
@@ -2,7 +2,7 @@ import { readdir } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { sanitizeTemplateContent } from "../../review/sanitize";
4
4
  import { fileExists } from "../../utils/fs-helpers";
5
- import { getDebateDepth } from "../arena";
5
+ import { getMemoryTunedDepth } from "../arena";
6
6
  import { ensurePhaseDir, getArtifactRef, getPhaseDir } from "../artifacts";
7
7
  import { filterByPhase } from "../confidence";
8
8
  import type { PipelineState } from "../types";
@@ -62,7 +62,7 @@ export async function handleArchitect(
62
62
  // Step 1: Dispatch architect(s) based on confidence depth
63
63
  await ensurePhaseDir(artifactDir, "ARCHITECT");
64
64
  const reconEntries = filterByPhase(state.confidence, "RECON");
65
- const depth = getDebateDepth(reconEntries);
65
+ const depth = getMemoryTunedDepth(reconEntries, join(artifactDir, ".."));
66
66
  const reconRef = getArtifactRef("RECON", "report.md");
67
67
  const challengeRef = getArtifactRef("CHALLENGE", "brief.md");
68
68
  const safeIdea = sanitizeTemplateContent(state.idea).replace(/[\r\n]+/g, " ");
@@ -82,9 +82,9 @@ export async function loadAdaptiveSkillContext(
82
82
 
83
83
  const matchingSkills = filterSkillsByStack(allSkills, projectTags);
84
84
  return buildMultiSkillContext(matchingSkills, tokenBudget);
85
- } catch (error: unknown) {
86
- // Best-effort for I/O errors; re-throw programmer errors
87
- if (error instanceof TypeError || error instanceof RangeError) throw error;
85
+ } catch {
86
+ // Best-effort: all errors return empty string. Caller (injectSkillContext)
87
+ // logs the error no need to re-throw since the call site is also best-effort.
88
88
  return "";
89
89
  }
90
90
  }
@@ -5,7 +5,7 @@ import type { DispatchResult } from "../orchestrator/handlers/types";
5
5
  import { buildLessonContext } from "../orchestrator/lesson-injection";
6
6
  import { loadLessonMemory } from "../orchestrator/lesson-memory";
7
7
  import { completePhase, getNextPhase } from "../orchestrator/phase";
8
- import { buildSkillContext, loadSkillContent } from "../orchestrator/skill-injection";
8
+ import { loadAdaptiveSkillContext } from "../orchestrator/skill-injection";
9
9
  import { createInitialState, loadState, patchState, saveState } from "../orchestrator/state";
10
10
  import type { Phase } from "../orchestrator/types";
11
11
  import { isEnoentError } from "../utils/fs-helpers";
@@ -96,17 +96,16 @@ async function injectLessonContext(
96
96
  }
97
97
 
98
98
  /**
99
- * Attempt to inject coding-standards skill context into a dispatch prompt.
99
+ * Attempt to inject stack-filtered adaptive skill context into a dispatch prompt.
100
100
  * Best-effort: failures are silently swallowed to avoid breaking dispatch.
101
101
  */
102
- async function injectSkillContext(prompt: string): Promise<string> {
102
+ async function injectSkillContext(prompt: string, projectRoot?: string): Promise<string> {
103
103
  try {
104
104
  const baseDir = getGlobalConfigDir();
105
- const content = await loadSkillContent(baseDir);
106
- const ctx = buildSkillContext(content);
105
+ const ctx = await loadAdaptiveSkillContext(baseDir, projectRoot ?? process.cwd());
107
106
  if (ctx) return prompt + ctx;
108
- } catch {
109
- // Best-effort: swallow all errors (same as lesson injection)
107
+ } catch (err) {
108
+ console.warn("[opencode-autopilot] skill injection failed:", err);
110
109
  }
111
110
  return prompt;
112
111
  }
@@ -146,7 +145,7 @@ async function processHandlerResult(
146
145
  handlerResult.phase,
147
146
  artifactDir,
148
147
  );
149
- const withSkills = await injectSkillContext(enrichedPrompt);
148
+ const withSkills = await injectSkillContext(enrichedPrompt, join(artifactDir, ".."));
150
149
  if (withSkills !== handlerResult.prompt) {
151
150
  return JSON.stringify({ ...handlerResult, prompt: withSkills });
152
151
  }
@@ -163,7 +162,7 @@ async function processHandlerResult(
163
162
  handlerResult.phase as string,
164
163
  artifactDir,
165
164
  );
166
- const skillSuffix = await injectSkillContext("");
165
+ const skillSuffix = await injectSkillContext("", join(artifactDir, ".."));
167
166
  const combinedSuffix = lessonSuffix + (skillSuffix || "");
168
167
  if (combinedSuffix) {
169
168
  const enrichedAgents = handlerResult.agents.map((entry) => ({