refactor(hooks): fix empty catches and remove AI slop from code comments
This commit is contained in:
@@ -0,0 +1,298 @@
|
||||
import { statSync } from "node:fs"
|
||||
import {
|
||||
appendSessionId,
|
||||
clearBoulderState,
|
||||
createBoulderState,
|
||||
findPrometheusPlans,
|
||||
getPlanName,
|
||||
getPlanProgress,
|
||||
getTaskSessionState,
|
||||
readBoulderState,
|
||||
readCurrentTopLevelTask,
|
||||
upsertTaskSessionState,
|
||||
writeBoulderState,
|
||||
} from "../../features/boulder-state"
|
||||
import { log } from "../../shared/logger"
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { HOOK_NAME } from "./start-work-hook"
|
||||
|
||||
function findPlanByName(plans: string[], requestedName: string): string | null {
|
||||
const lowerName = requestedName.toLowerCase()
|
||||
const exactMatch = plans.find((p) => getPlanName(p).toLowerCase() === lowerName)
|
||||
if (exactMatch) return exactMatch
|
||||
const partialMatch = plans.find((p) => getPlanName(p).toLowerCase().includes(lowerName))
|
||||
return partialMatch || null
|
||||
}
|
||||
|
||||
function buildAutoSelectedPlanContext(params: {
|
||||
planPath: string
|
||||
sessionId: string
|
||||
timestamp: string
|
||||
activeAgent: string
|
||||
worktreePath: string | undefined
|
||||
worktreeBlock: string
|
||||
directory: string
|
||||
}): string {
|
||||
const { planPath, sessionId, timestamp, activeAgent, worktreePath, worktreeBlock, directory } = params
|
||||
const progress = getPlanProgress(planPath)
|
||||
const newState = createBoulderState(planPath, sessionId, activeAgent, worktreePath)
|
||||
writeBoulderState(directory, newState)
|
||||
|
||||
return `
|
||||
## Auto-Selected Plan
|
||||
|
||||
**Plan**: ${getPlanName(planPath)}
|
||||
**Path**: ${planPath}
|
||||
**Progress**: ${progress.completed}/${progress.total} tasks
|
||||
**Session ID**: ${sessionId}
|
||||
**Started**: ${timestamp}
|
||||
${worktreeBlock}
|
||||
|
||||
boulder.json has been created. Read the plan and begin execution.`
|
||||
}
|
||||
|
||||
function buildMissingPlanContext(explicitPlanName: string, allPlans: string[]): string {
|
||||
const incompletePlans = allPlans.filter((p) => !getPlanProgress(p).isComplete)
|
||||
if (incompletePlans.length > 0) {
|
||||
const planList = incompletePlans
|
||||
.map((p, i) => {
|
||||
const prog = getPlanProgress(p)
|
||||
return `${i + 1}. [${getPlanName(p)}] - Progress: ${prog.completed}/${prog.total}`
|
||||
})
|
||||
.join("\n")
|
||||
|
||||
return `
|
||||
## Plan Not Found
|
||||
|
||||
Could not find a plan matching "${explicitPlanName}".
|
||||
|
||||
Available incomplete plans:
|
||||
${planList}
|
||||
|
||||
Ask the user which plan to work on.`
|
||||
}
|
||||
|
||||
return `
|
||||
## Plan Not Found
|
||||
|
||||
Could not find a plan matching "${explicitPlanName}".
|
||||
No incomplete plans available. Create a new plan with: /plan "your task"`
|
||||
}
|
||||
|
||||
function buildExplicitPlanContext(params: {
|
||||
explicitPlanName: string
|
||||
existingState: ReturnType<typeof readBoulderState>
|
||||
sessionId: string
|
||||
timestamp: string
|
||||
activeAgent: string
|
||||
worktreePath: string | undefined
|
||||
worktreeBlock: string
|
||||
directory: string
|
||||
}): string {
|
||||
const { explicitPlanName, existingState, sessionId, timestamp, activeAgent, worktreePath, worktreeBlock, directory } = params
|
||||
log(`[${HOOK_NAME}] Explicit plan name requested: ${explicitPlanName}`, { sessionID: sessionId })
|
||||
|
||||
const allPlans = findPrometheusPlans(directory)
|
||||
const matchedPlan = findPlanByName(allPlans, explicitPlanName)
|
||||
if (!matchedPlan) {
|
||||
return buildMissingPlanContext(explicitPlanName, allPlans)
|
||||
}
|
||||
|
||||
const progress = getPlanProgress(matchedPlan)
|
||||
if (progress.isComplete) {
|
||||
return `
|
||||
## Plan Already Complete
|
||||
|
||||
The requested plan "${getPlanName(matchedPlan)}" has been completed.
|
||||
All ${progress.total} tasks are done. Create a new plan with: /plan "your task"`
|
||||
}
|
||||
|
||||
if (existingState) {
|
||||
clearBoulderState(directory)
|
||||
}
|
||||
|
||||
return buildAutoSelectedPlanContext({
|
||||
planPath: matchedPlan,
|
||||
sessionId,
|
||||
timestamp,
|
||||
activeAgent,
|
||||
worktreePath,
|
||||
worktreeBlock,
|
||||
directory,
|
||||
})
|
||||
}
|
||||
|
||||
function buildExistingSessionContext(params: {
|
||||
existingState: NonNullable<ReturnType<typeof readBoulderState>>
|
||||
sessionId: string
|
||||
activeAgent: string
|
||||
worktreePath: string | undefined
|
||||
worktreeBlock: string
|
||||
directory: string
|
||||
}): string {
|
||||
const { existingState, sessionId, activeAgent, worktreePath, worktreeBlock, directory } = params
|
||||
const progress = getPlanProgress(existingState.active_plan)
|
||||
if (progress.isComplete) {
|
||||
return `
|
||||
## Previous Work Complete
|
||||
|
||||
The previous plan (${existingState.plan_name}) has been completed.
|
||||
Looking for new plans...`
|
||||
}
|
||||
|
||||
const effectiveWorktree = worktreePath ?? existingState.worktree_path
|
||||
const sessionAlreadyTracked = existingState.session_ids.includes(sessionId)
|
||||
const updatedSessions = sessionAlreadyTracked
|
||||
? existingState.session_ids
|
||||
: [...existingState.session_ids, sessionId]
|
||||
const shouldRewriteState = existingState.agent !== activeAgent || worktreePath !== undefined
|
||||
|
||||
if (shouldRewriteState) {
|
||||
writeBoulderState(directory, {
|
||||
...existingState,
|
||||
agent: activeAgent,
|
||||
...(worktreePath !== undefined ? { worktree_path: worktreePath } : {}),
|
||||
session_ids: updatedSessions,
|
||||
})
|
||||
} else if (!sessionAlreadyTracked) {
|
||||
appendSessionId(directory, sessionId)
|
||||
}
|
||||
|
||||
const worktreeDisplay = effectiveWorktree ? worktreeBlock.replace(worktreePath ?? "", effectiveWorktree) : worktreeBlock
|
||||
|
||||
return `
|
||||
## Active Work Session Found
|
||||
|
||||
**Status**: RESUMING existing work
|
||||
**Plan**: ${existingState.plan_name}
|
||||
**Path**: ${existingState.active_plan}
|
||||
**Progress**: ${progress.completed}/${progress.total} tasks completed
|
||||
**Sessions**: ${existingState.session_ids.length + 1} (current session appended)
|
||||
**Started**: ${existingState.started_at}
|
||||
${worktreeDisplay}
|
||||
|
||||
The current session (${sessionId}) has been added to session_ids.
|
||||
Read the plan file and continue from the first unchecked task.`
|
||||
}
|
||||
|
||||
function shouldDiscoverPlans(
|
||||
existingState: ReturnType<typeof readBoulderState>,
|
||||
explicitPlanName: string | null,
|
||||
): boolean {
|
||||
return (!existingState && !explicitPlanName)
|
||||
|| (existingState !== null && !explicitPlanName && getPlanProgress(existingState.active_plan).isComplete)
|
||||
}
|
||||
|
||||
function buildPlanDiscoveryContext(params: {
|
||||
contextInfo: string
|
||||
sessionId: string
|
||||
timestamp: string
|
||||
activeAgent: string
|
||||
worktreePath: string | undefined
|
||||
worktreeBlock: string
|
||||
directory: string
|
||||
}): string {
|
||||
const { contextInfo, sessionId, timestamp, activeAgent, worktreePath, worktreeBlock, directory } = params
|
||||
const plans = findPrometheusPlans(directory)
|
||||
const incompletePlans = plans.filter((p) => !getPlanProgress(p).isComplete)
|
||||
|
||||
if (plans.length === 0) {
|
||||
return contextInfo + `
|
||||
## No Plans Found
|
||||
|
||||
No Prometheus plan files found at .sisyphus/plans/
|
||||
Use Prometheus to create a work plan first: /plan "your task"`
|
||||
}
|
||||
|
||||
if (incompletePlans.length === 0) {
|
||||
return contextInfo + `
|
||||
|
||||
## All Plans Complete
|
||||
|
||||
All ${plans.length} plan(s) are complete. Create a new plan with: /plan "your task"`
|
||||
}
|
||||
|
||||
if (incompletePlans.length === 1) {
|
||||
return contextInfo + buildAutoSelectedPlanContext({
|
||||
planPath: incompletePlans[0],
|
||||
sessionId,
|
||||
timestamp,
|
||||
activeAgent,
|
||||
worktreePath,
|
||||
worktreeBlock,
|
||||
directory,
|
||||
})
|
||||
}
|
||||
|
||||
const planList = incompletePlans
|
||||
.map((p, i) => {
|
||||
const progress = getPlanProgress(p)
|
||||
const modified = new Date(statSync(p).mtimeMs).toISOString()
|
||||
return `${i + 1}. [${getPlanName(p)}] - Modified: ${modified} - Progress: ${progress.completed}/${progress.total}`
|
||||
})
|
||||
.join("\n")
|
||||
|
||||
return contextInfo + `
|
||||
|
||||
<system-reminder>
|
||||
## Multiple Plans Found
|
||||
|
||||
Current Time: ${timestamp}
|
||||
Session ID: ${sessionId}
|
||||
|
||||
${planList}
|
||||
|
||||
Ask the user which plan to work on. Present the options above and wait for their response.
|
||||
${worktreeBlock}
|
||||
</system-reminder>`
|
||||
}
|
||||
|
||||
export function buildStartWorkContextInfo(params: {
|
||||
ctx: PluginInput
|
||||
explicitPlanName: string | null
|
||||
existingState: ReturnType<typeof readBoulderState>
|
||||
sessionId: string
|
||||
timestamp: string
|
||||
activeAgent: string
|
||||
worktreePath: string | undefined
|
||||
worktreeBlock: string
|
||||
}): string {
|
||||
const { ctx, explicitPlanName, existingState, sessionId, timestamp, activeAgent, worktreePath, worktreeBlock } = params
|
||||
|
||||
let contextInfo = ""
|
||||
if (explicitPlanName) {
|
||||
contextInfo = buildExplicitPlanContext({
|
||||
explicitPlanName,
|
||||
existingState,
|
||||
sessionId,
|
||||
timestamp,
|
||||
activeAgent,
|
||||
worktreePath,
|
||||
worktreeBlock,
|
||||
directory: ctx.directory,
|
||||
})
|
||||
} else if (existingState) {
|
||||
contextInfo = buildExistingSessionContext({
|
||||
existingState,
|
||||
sessionId,
|
||||
activeAgent,
|
||||
worktreePath,
|
||||
worktreeBlock,
|
||||
directory: ctx.directory,
|
||||
})
|
||||
}
|
||||
|
||||
if (shouldDiscoverPlans(existingState, explicitPlanName)) {
|
||||
return buildPlanDiscoveryContext({
|
||||
contextInfo,
|
||||
sessionId,
|
||||
timestamp,
|
||||
activeAgent,
|
||||
worktreePath,
|
||||
worktreeBlock,
|
||||
directory: ctx.directory,
|
||||
})
|
||||
}
|
||||
|
||||
return contextInfo
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
} from "../../features/claude-code-session-state"
|
||||
import { detectWorktreePath } from "./worktree-detector"
|
||||
import { parseUserRequest } from "./parse-user-request"
|
||||
import { buildStartWorkContextInfo } from "./context-info-builder"
|
||||
|
||||
export const HOOK_NAME = "start-work" as const
|
||||
const START_WORK_TEMPLATE_MARKER = "You are starting a Sisyphus work session."
|
||||
@@ -43,24 +44,16 @@ interface StartWorkHookOutput {
|
||||
parts: Array<{ type: string; text?: string }>
|
||||
}
|
||||
|
||||
function findPlanByName(plans: string[], requestedName: string): string | null {
|
||||
const lowerName = requestedName.toLowerCase()
|
||||
const exactMatch = plans.find((p) => getPlanName(p).toLowerCase() === lowerName)
|
||||
if (exactMatch) return exactMatch
|
||||
const partialMatch = plans.find((p) => getPlanName(p).toLowerCase().includes(lowerName))
|
||||
return partialMatch || null
|
||||
}
|
||||
|
||||
function createWorktreeActiveBlock(worktreePath: string): string {
|
||||
return `
|
||||
## Worktree Active
|
||||
|
||||
**Worktree**: \`${worktreePath}\`
|
||||
|
||||
**CRITICAL — DO NOT FORGET**: You are working inside a git worktree. ALL operations MUST be performed exclusively within this worktree directory.
|
||||
**CRITICAL - DO NOT FORGET**: You are working inside a git worktree. ALL operations MUST be performed exclusively within this worktree directory.
|
||||
- Every file read, write, edit, and git operation MUST target paths under: \`${worktreePath}\`
|
||||
- When delegating tasks to subagents, you MUST include the worktree path in your delegation prompt so they also operate exclusively within the worktree
|
||||
- NEVER operate on the main repository directory — always use the worktree path above`
|
||||
- NEVER operate on the main repository directory - always use the worktree path above`
|
||||
}
|
||||
|
||||
function resolveWorktreeContext(
|
||||
@@ -129,174 +122,16 @@ export function createStartWorkHook(ctx: PluginInput) {
|
||||
const { planName: explicitPlanName, explicitWorktreePath } = parseUserRequest(promptText)
|
||||
const { worktreePath, block: worktreeBlock } = resolveWorktreeContext(explicitWorktreePath)
|
||||
|
||||
let contextInfo = ""
|
||||
|
||||
if (explicitPlanName) {
|
||||
log(`[${HOOK_NAME}] Explicit plan name requested: ${explicitPlanName}`, { sessionID: input.sessionID })
|
||||
|
||||
const allPlans = findPrometheusPlans(ctx.directory)
|
||||
const matchedPlan = findPlanByName(allPlans, explicitPlanName)
|
||||
|
||||
if (matchedPlan) {
|
||||
const progress = getPlanProgress(matchedPlan)
|
||||
|
||||
if (progress.isComplete) {
|
||||
contextInfo = `
|
||||
## Plan Already Complete
|
||||
|
||||
The requested plan "${getPlanName(matchedPlan)}" has been completed.
|
||||
All ${progress.total} tasks are done. Create a new plan with: /plan "your task"`
|
||||
} else {
|
||||
if (existingState) clearBoulderState(ctx.directory)
|
||||
const newState = createBoulderState(matchedPlan, sessionId, activeAgent, worktreePath)
|
||||
writeBoulderState(ctx.directory, newState)
|
||||
|
||||
contextInfo = `
|
||||
## Auto-Selected Plan
|
||||
|
||||
**Plan**: ${getPlanName(matchedPlan)}
|
||||
**Path**: ${matchedPlan}
|
||||
**Progress**: ${progress.completed}/${progress.total} tasks
|
||||
**Session ID**: ${sessionId}
|
||||
**Started**: ${timestamp}
|
||||
${worktreeBlock}
|
||||
|
||||
boulder.json has been created. Read the plan and begin execution.`
|
||||
}
|
||||
} else {
|
||||
const incompletePlans = allPlans.filter((p) => !getPlanProgress(p).isComplete)
|
||||
if (incompletePlans.length > 0) {
|
||||
const planList = incompletePlans
|
||||
.map((p, i) => {
|
||||
const prog = getPlanProgress(p)
|
||||
return `${i + 1}. [${getPlanName(p)}] - Progress: ${prog.completed}/${prog.total}`
|
||||
})
|
||||
.join("\n")
|
||||
|
||||
contextInfo = `
|
||||
## Plan Not Found
|
||||
|
||||
Could not find a plan matching "${explicitPlanName}".
|
||||
|
||||
Available incomplete plans:
|
||||
${planList}
|
||||
|
||||
Ask the user which plan to work on.`
|
||||
} else {
|
||||
contextInfo = `
|
||||
## Plan Not Found
|
||||
|
||||
Could not find a plan matching "${explicitPlanName}".
|
||||
No incomplete plans available. Create a new plan with: /plan "your task"`
|
||||
}
|
||||
}
|
||||
} else if (existingState) {
|
||||
const progress = getPlanProgress(existingState.active_plan)
|
||||
|
||||
if (!progress.isComplete) {
|
||||
const effectiveWorktree = worktreePath ?? existingState.worktree_path
|
||||
const sessionAlreadyTracked = existingState.session_ids.includes(sessionId)
|
||||
const updatedSessions = sessionAlreadyTracked
|
||||
? existingState.session_ids
|
||||
: [...existingState.session_ids, sessionId]
|
||||
const shouldRewriteState = existingState.agent !== activeAgent || worktreePath !== undefined
|
||||
|
||||
if (shouldRewriteState) {
|
||||
writeBoulderState(ctx.directory, {
|
||||
...existingState,
|
||||
agent: activeAgent,
|
||||
...(worktreePath !== undefined ? { worktree_path: worktreePath } : {}),
|
||||
session_ids: updatedSessions,
|
||||
})
|
||||
} else if (!sessionAlreadyTracked) {
|
||||
appendSessionId(ctx.directory, sessionId)
|
||||
}
|
||||
|
||||
const worktreeDisplay = effectiveWorktree ? createWorktreeActiveBlock(effectiveWorktree) : worktreeBlock
|
||||
|
||||
contextInfo = `
|
||||
## Active Work Session Found
|
||||
|
||||
**Status**: RESUMING existing work
|
||||
**Plan**: ${existingState.plan_name}
|
||||
**Path**: ${existingState.active_plan}
|
||||
**Progress**: ${progress.completed}/${progress.total} tasks completed
|
||||
**Sessions**: ${existingState.session_ids.length + 1} (current session appended)
|
||||
**Started**: ${existingState.started_at}
|
||||
${worktreeDisplay}
|
||||
|
||||
The current session (${sessionId}) has been added to session_ids.
|
||||
Read the plan file and continue from the first unchecked task.`
|
||||
} else {
|
||||
contextInfo = `
|
||||
## Previous Work Complete
|
||||
|
||||
The previous plan (${existingState.plan_name}) has been completed.
|
||||
Looking for new plans...`
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(!existingState && !explicitPlanName) ||
|
||||
(existingState && !explicitPlanName && getPlanProgress(existingState.active_plan).isComplete)
|
||||
) {
|
||||
const plans = findPrometheusPlans(ctx.directory)
|
||||
const incompletePlans = plans.filter((p) => !getPlanProgress(p).isComplete)
|
||||
|
||||
if (plans.length === 0) {
|
||||
contextInfo += `
|
||||
## No Plans Found
|
||||
|
||||
No Prometheus plan files found at .sisyphus/plans/
|
||||
Use Prometheus to create a work plan first: /plan "your task"`
|
||||
} else if (incompletePlans.length === 0) {
|
||||
contextInfo += `
|
||||
|
||||
## All Plans Complete
|
||||
|
||||
All ${plans.length} plan(s) are complete. Create a new plan with: /plan "your task"`
|
||||
} else if (incompletePlans.length === 1) {
|
||||
const planPath = incompletePlans[0]
|
||||
const progress = getPlanProgress(planPath)
|
||||
const newState = createBoulderState(planPath, sessionId, activeAgent, worktreePath)
|
||||
writeBoulderState(ctx.directory, newState)
|
||||
|
||||
contextInfo += `
|
||||
|
||||
## Auto-Selected Plan
|
||||
|
||||
**Plan**: ${getPlanName(planPath)}
|
||||
**Path**: ${planPath}
|
||||
**Progress**: ${progress.completed}/${progress.total} tasks
|
||||
**Session ID**: ${sessionId}
|
||||
**Started**: ${timestamp}
|
||||
${worktreeBlock}
|
||||
|
||||
boulder.json has been created. Read the plan and begin execution.`
|
||||
} else {
|
||||
const planList = incompletePlans
|
||||
.map((p, i) => {
|
||||
const progress = getPlanProgress(p)
|
||||
const modified = new Date(statSync(p).mtimeMs).toISOString()
|
||||
return `${i + 1}. [${getPlanName(p)}] - Modified: ${modified} - Progress: ${progress.completed}/${progress.total}`
|
||||
})
|
||||
.join("\n")
|
||||
|
||||
contextInfo += `
|
||||
|
||||
<system-reminder>
|
||||
## Multiple Plans Found
|
||||
|
||||
Current Time: ${timestamp}
|
||||
Session ID: ${sessionId}
|
||||
|
||||
${planList}
|
||||
|
||||
Ask the user which plan to work on. Present the options above and wait for their response.
|
||||
${worktreeBlock}
|
||||
</system-reminder>`
|
||||
}
|
||||
}
|
||||
const contextInfo = buildStartWorkContextInfo({
|
||||
ctx,
|
||||
explicitPlanName,
|
||||
existingState,
|
||||
sessionId,
|
||||
timestamp,
|
||||
activeAgent,
|
||||
worktreePath,
|
||||
worktreeBlock,
|
||||
})
|
||||
|
||||
const idx = output.parts.findIndex((p) => p.type === "text" && p.text)
|
||||
if (idx >= 0 && output.parts[idx].text) {
|
||||
|
||||
Reference in New Issue
Block a user