refactor: wave 1 - extract leaf modules, rename catch-all files, split index.ts hooks
- Split 25+ index.ts files into hook.ts + extracted modules - Rename all catch-all utils.ts/helpers.ts to domain-specific names - Split src/tools/lsp/ into ~15 focused modules - Split src/tools/delegate-task/ into ~18 focused modules - Separate shared types from implementation - 155 files changed, 60+ new files created - All typecheck clean, 61 tests pass
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import { DELEGATE_TASK_ERROR_PATTERNS, type DetectedError } from "./patterns"
|
||||
|
||||
function extractAvailableList(output: string): string | null {
|
||||
const availableMatch = output.match(/Available[^:]*:\s*(.+)$/m)
|
||||
return availableMatch ? availableMatch[1].trim() : null
|
||||
}
|
||||
|
||||
export function buildRetryGuidance(errorInfo: DetectedError): string {
|
||||
const pattern = DELEGATE_TASK_ERROR_PATTERNS.find(
|
||||
(p) => p.errorType === errorInfo.errorType
|
||||
)
|
||||
|
||||
if (!pattern) {
|
||||
return `[task ERROR] Fix the error and retry with correct parameters.`
|
||||
}
|
||||
|
||||
let guidance = `
|
||||
[task CALL FAILED - IMMEDIATE RETRY REQUIRED]
|
||||
|
||||
**Error Type**: ${errorInfo.errorType}
|
||||
**Fix**: ${pattern.fixHint}
|
||||
`
|
||||
|
||||
const availableList = extractAvailableList(errorInfo.originalOutput)
|
||||
if (availableList) {
|
||||
guidance += `\n**Available Options**: ${availableList}\n`
|
||||
}
|
||||
|
||||
guidance += `
|
||||
**Action**: Retry task NOW with corrected parameters.
|
||||
|
||||
Example of CORRECT call:
|
||||
\`\`\`
|
||||
task(
|
||||
description="Task description",
|
||||
prompt="Detailed prompt...",
|
||||
category="unspecified-low", // OR subagent_type="explore"
|
||||
run_in_background=false,
|
||||
load_skills=[]
|
||||
)
|
||||
\`\`\`
|
||||
`
|
||||
|
||||
return guidance
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
|
||||
import { buildRetryGuidance } from "./guidance"
|
||||
import { detectDelegateTaskError } from "./patterns"
|
||||
|
||||
export function createDelegateTaskRetryHook(_ctx: PluginInput) {
|
||||
return {
|
||||
"tool.execute.after": async (
|
||||
input: { tool: string; sessionID: string; callID: string },
|
||||
output: { title: string; output: string; metadata: unknown }
|
||||
) => {
|
||||
if (input.tool.toLowerCase() !== "task") return
|
||||
|
||||
const errorInfo = detectDelegateTaskError(output.output)
|
||||
if (errorInfo) {
|
||||
const guidance = buildRetryGuidance(errorInfo)
|
||||
output.output += `\n${guidance}`
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,136 +1,4 @@
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
|
||||
export interface DelegateTaskErrorPattern {
|
||||
pattern: string
|
||||
errorType: string
|
||||
fixHint: string
|
||||
}
|
||||
|
||||
export const DELEGATE_TASK_ERROR_PATTERNS: DelegateTaskErrorPattern[] = [
|
||||
{
|
||||
pattern: "run_in_background",
|
||||
errorType: "missing_run_in_background",
|
||||
fixHint: "Add run_in_background=false (for delegation) or run_in_background=true (for parallel exploration)",
|
||||
},
|
||||
{
|
||||
pattern: "load_skills",
|
||||
errorType: "missing_load_skills",
|
||||
fixHint: "Add load_skills=[] parameter (empty array if no skills needed). Note: Calling Skill tool does NOT populate this.",
|
||||
},
|
||||
{
|
||||
pattern: "category OR subagent_type",
|
||||
errorType: "mutual_exclusion",
|
||||
fixHint: "Provide ONLY one of: category (e.g., 'general', 'quick') OR subagent_type (e.g., 'oracle', 'explore')",
|
||||
},
|
||||
{
|
||||
pattern: "Must provide either category or subagent_type",
|
||||
errorType: "missing_category_or_agent",
|
||||
fixHint: "Add either category='general' OR subagent_type='explore'",
|
||||
},
|
||||
{
|
||||
pattern: "Unknown category",
|
||||
errorType: "unknown_category",
|
||||
fixHint: "Use a valid category from the Available list in the error message",
|
||||
},
|
||||
{
|
||||
pattern: "Agent name cannot be empty",
|
||||
errorType: "empty_agent",
|
||||
fixHint: "Provide a non-empty subagent_type value",
|
||||
},
|
||||
{
|
||||
pattern: "Unknown agent",
|
||||
errorType: "unknown_agent",
|
||||
fixHint: "Use a valid agent from the Available agents list in the error message",
|
||||
},
|
||||
{
|
||||
pattern: "Cannot call primary agent",
|
||||
errorType: "primary_agent",
|
||||
fixHint: "Primary agents cannot be called via task. Use a subagent like 'explore', 'oracle', or 'librarian'",
|
||||
},
|
||||
{
|
||||
pattern: "Skills not found",
|
||||
errorType: "unknown_skills",
|
||||
fixHint: "Use valid skill names from the Available list in the error message",
|
||||
},
|
||||
]
|
||||
|
||||
export interface DetectedError {
|
||||
errorType: string
|
||||
originalOutput: string
|
||||
}
|
||||
|
||||
export function detectDelegateTaskError(output: string): DetectedError | null {
|
||||
if (!output.includes("[ERROR]") && !output.includes("Invalid arguments")) return null
|
||||
|
||||
for (const errorPattern of DELEGATE_TASK_ERROR_PATTERNS) {
|
||||
if (output.includes(errorPattern.pattern)) {
|
||||
return {
|
||||
errorType: errorPattern.errorType,
|
||||
originalOutput: output,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function extractAvailableList(output: string): string | null {
|
||||
const availableMatch = output.match(/Available[^:]*:\s*(.+)$/m)
|
||||
return availableMatch ? availableMatch[1].trim() : null
|
||||
}
|
||||
|
||||
export function buildRetryGuidance(errorInfo: DetectedError): string {
|
||||
const pattern = DELEGATE_TASK_ERROR_PATTERNS.find(
|
||||
(p) => p.errorType === errorInfo.errorType
|
||||
)
|
||||
|
||||
if (!pattern) {
|
||||
return `[task ERROR] Fix the error and retry with correct parameters.`
|
||||
}
|
||||
|
||||
let guidance = `
|
||||
[task CALL FAILED - IMMEDIATE RETRY REQUIRED]
|
||||
|
||||
**Error Type**: ${errorInfo.errorType}
|
||||
**Fix**: ${pattern.fixHint}
|
||||
`
|
||||
|
||||
const availableList = extractAvailableList(errorInfo.originalOutput)
|
||||
if (availableList) {
|
||||
guidance += `\n**Available Options**: ${availableList}\n`
|
||||
}
|
||||
|
||||
guidance += `
|
||||
**Action**: Retry task NOW with corrected parameters.
|
||||
|
||||
Example of CORRECT call:
|
||||
\`\`\`
|
||||
task(
|
||||
description="Task description",
|
||||
prompt="Detailed prompt...",
|
||||
category="unspecified-low", // OR subagent_type="explore"
|
||||
run_in_background=false,
|
||||
load_skills=[]
|
||||
)
|
||||
\`\`\`
|
||||
`
|
||||
|
||||
return guidance
|
||||
}
|
||||
|
||||
export function createDelegateTaskRetryHook(_ctx: PluginInput) {
|
||||
return {
|
||||
"tool.execute.after": async (
|
||||
input: { tool: string; sessionID: string; callID: string },
|
||||
output: { title: string; output: string; metadata: unknown }
|
||||
) => {
|
||||
if (input.tool.toLowerCase() !== "task") return
|
||||
|
||||
const errorInfo = detectDelegateTaskError(output.output)
|
||||
if (errorInfo) {
|
||||
const guidance = buildRetryGuidance(errorInfo)
|
||||
output.output += `\n${guidance}`
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
export type { DelegateTaskErrorPattern, DetectedError } from "./patterns"
|
||||
export { DELEGATE_TASK_ERROR_PATTERNS, detectDelegateTaskError } from "./patterns"
|
||||
export { buildRetryGuidance } from "./guidance"
|
||||
export { createDelegateTaskRetryHook } from "./hook"
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
export interface DelegateTaskErrorPattern {
|
||||
pattern: string
|
||||
errorType: string
|
||||
fixHint: string
|
||||
}
|
||||
|
||||
export const DELEGATE_TASK_ERROR_PATTERNS: DelegateTaskErrorPattern[] = [
|
||||
{
|
||||
pattern: "run_in_background",
|
||||
errorType: "missing_run_in_background",
|
||||
fixHint:
|
||||
"Add run_in_background=false (for delegation) or run_in_background=true (for parallel exploration)",
|
||||
},
|
||||
{
|
||||
pattern: "load_skills",
|
||||
errorType: "missing_load_skills",
|
||||
fixHint:
|
||||
"Add load_skills=[] parameter (empty array if no skills needed). Note: Calling Skill tool does NOT populate this.",
|
||||
},
|
||||
{
|
||||
pattern: "category OR subagent_type",
|
||||
errorType: "mutual_exclusion",
|
||||
fixHint:
|
||||
"Provide ONLY one of: category (e.g., 'general', 'quick') OR subagent_type (e.g., 'oracle', 'explore')",
|
||||
},
|
||||
{
|
||||
pattern: "Must provide either category or subagent_type",
|
||||
errorType: "missing_category_or_agent",
|
||||
fixHint: "Add either category='general' OR subagent_type='explore'",
|
||||
},
|
||||
{
|
||||
pattern: "Unknown category",
|
||||
errorType: "unknown_category",
|
||||
fixHint: "Use a valid category from the Available list in the error message",
|
||||
},
|
||||
{
|
||||
pattern: "Agent name cannot be empty",
|
||||
errorType: "empty_agent",
|
||||
fixHint: "Provide a non-empty subagent_type value",
|
||||
},
|
||||
{
|
||||
pattern: "Unknown agent",
|
||||
errorType: "unknown_agent",
|
||||
fixHint: "Use a valid agent from the Available agents list in the error message",
|
||||
},
|
||||
{
|
||||
pattern: "Cannot call primary agent",
|
||||
errorType: "primary_agent",
|
||||
fixHint:
|
||||
"Primary agents cannot be called via task. Use a subagent like 'explore', 'oracle', or 'librarian'",
|
||||
},
|
||||
{
|
||||
pattern: "Skills not found",
|
||||
errorType: "unknown_skills",
|
||||
fixHint: "Use valid skill names from the Available list in the error message",
|
||||
},
|
||||
]
|
||||
|
||||
export interface DetectedError {
|
||||
errorType: string
|
||||
originalOutput: string
|
||||
}
|
||||
|
||||
export function detectDelegateTaskError(output: string): DetectedError | null {
|
||||
if (!output.includes("[ERROR]") && !output.includes("Invalid arguments")) return null
|
||||
|
||||
for (const errorPattern of DELEGATE_TASK_ERROR_PATTERNS) {
|
||||
if (output.includes(errorPattern.pattern)) {
|
||||
return {
|
||||
errorType: errorPattern.errorType,
|
||||
originalOutput: output,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
Reference in New Issue
Block a user