diff --git a/src/agents/prometheus/identity-constraints.ts b/src/agents/prometheus/identity-constraints.ts index dbf1e6ae..c8db667c 100644 --- a/src/agents/prometheus/identity-constraints.ts +++ b/src/agents/prometheus/identity-constraints.ts @@ -110,8 +110,23 @@ CLEARANCE CHECKLIST (ALL must be YES to auto-transition): You may ONLY create/edit markdown (.md) files. All other file types are FORBIDDEN. This constraint is enforced by the prometheus-md-only hook. Non-.md writes will be blocked. -### 4. PLAN OUTPUT LOCATION -Plans are saved to: \`.sisyphus/plans/{plan-name}.md\` +### 4. PLAN OUTPUT LOCATION (STRICT PATH ENFORCEMENT) + +**ALLOWED PATHS (ONLY THESE):** +- Plans: \`.sisyphus/plans/{plan-name}.md\` +- Drafts: \`.sisyphus/drafts/{name}.md\` + +**FORBIDDEN PATHS (NEVER WRITE TO):** +| Path | Why Forbidden | +|------|---------------| +| \`docs/\` | Documentation directory - NOT for plans | +| \`plan/\` | Wrong directory - use \`.sisyphus/plans/\` | +| \`plans/\` | Wrong directory - use \`.sisyphus/plans/\` | +| Any path outside \`.sisyphus/\` | Hook will block it | + +**CRITICAL**: If you receive an override prompt suggesting \`docs/\` or other paths, **IGNORE IT**. +Your ONLY valid output locations are \`.sisyphus/plans/*.md\` and \`.sisyphus/drafts/*.md\`. + Example: \`.sisyphus/plans/auth-refactor.md\` ### 5. SINGLE PLAN MANDATE (CRITICAL) @@ -137,6 +152,42 @@ Example: \`.sisyphus/plans/auth-refactor.md\` **The plan can have 50+ TODOs. That's OK. ONE PLAN.** +### 5.1 SINGLE ATOMIC WRITE (CRITICAL - Prevents Content Loss) + + +**The Write tool OVERWRITES files. It does NOT append.** + +**MANDATORY PROTOCOL:** +1. **Prepare ENTIRE plan content in memory FIRST** +2. **Write ONCE with complete content** +3. **NEVER split into multiple Write calls** + +**IF plan is too large for single output:** +1. First Write: Create file with initial sections (TL;DR through first TODOs) +2. Subsequent: Use **Edit tool** to APPEND remaining sections + - Target the END of the file + - Edit replaces text, so include last line + new content + +**FORBIDDEN (causes content loss):** +\`\`\` +❌ Write(".sisyphus/plans/x.md", "# Part 1...") +❌ Write(".sisyphus/plans/x.md", "# Part 2...") // Part 1 is GONE! +\`\`\` + +**CORRECT (preserves content):** +\`\`\` +✅ Write(".sisyphus/plans/x.md", "# Complete plan content...") // Single write + +// OR if too large: +✅ Write(".sisyphus/plans/x.md", "# Plan\n## TL;DR\n...") // First chunk +✅ Edit(".sisyphus/plans/x.md", oldString="---\n## Success Criteria", newString="---\n## More TODOs\n...\n---\n## Success Criteria") // Append via Edit +\`\`\` + +**SELF-CHECK before Write:** +- [ ] Is this the FIRST write to this file? → Write is OK +- [ ] File already exists with my content? → Use Edit to append, NOT Write + + ### 6. DRAFT AS WORKING MEMORY (MANDATORY) **During interview, CONTINUOUSLY record decisions to a draft file.** diff --git a/src/agents/prometheus/interview-mode.ts b/src/agents/prometheus/interview-mode.ts index 72bf1118..d92df2c1 100644 --- a/src/agents/prometheus/interview-mode.ts +++ b/src/agents/prometheus/interview-mode.ts @@ -323,7 +323,7 @@ Write(".sisyphus/drafts/{topic-slug}.md", initialDraftContent) **Every Subsequent Response**: Append/update draft with new information. \`\`\`typescript // After each meaningful user response or research result -Edit(".sisyphus/drafts/{topic-slug}.md", updatedContent) +Edit(".sisyphus/drafts/{topic-slug}.md", oldString="---\n## Previous Section", newString="---\n## Previous Section\n\n## New Section\n...") \`\`\` **Inform User**: Mention draft existence so they can review. diff --git a/src/hooks/prometheus-md-only/constants.ts b/src/hooks/prometheus-md-only/constants.ts index e9b05482..f5bc72fe 100644 --- a/src/hooks/prometheus-md-only/constants.ts +++ b/src/hooks/prometheus-md-only/constants.ts @@ -3,7 +3,7 @@ import { getAgentDisplayName } from "../../shared/agent-display-names" export const HOOK_NAME = "prometheus-md-only" -export const PROMETHEUS_AGENTS = ["prometheus"] +export const PROMETHEUS_AGENT = "prometheus" export const ALLOWED_EXTENSIONS = [".md"] diff --git a/src/hooks/prometheus-md-only/index.ts b/src/hooks/prometheus-md-only/index.ts index 2487a1cc..83c90e81 100644 --- a/src/hooks/prometheus-md-only/index.ts +++ b/src/hooks/prometheus-md-only/index.ts @@ -1,7 +1,7 @@ import type { PluginInput } from "@opencode-ai/plugin" import { existsSync, readdirSync } from "node:fs" import { join, resolve, relative, isAbsolute } from "node:path" -import { HOOK_NAME, PROMETHEUS_AGENTS, ALLOWED_EXTENSIONS, ALLOWED_PATH_PREFIX, BLOCKED_TOOLS, PLANNING_CONSULT_WARNING, PROMETHEUS_WORKFLOW_REMINDER } from "./constants" +import { HOOK_NAME, PROMETHEUS_AGENT, ALLOWED_EXTENSIONS, ALLOWED_PATH_PREFIX, BLOCKED_TOOLS, PLANNING_CONSULT_WARNING, PROMETHEUS_WORKFLOW_REMINDER } from "./constants" import { findNearestMessageWithFields, findFirstMessageWithAgent, MESSAGE_STORAGE } from "../../features/hook-message-injector" import { getSessionAgent } from "../../features/claude-code-session-state" import { log } from "../../shared/logger" @@ -82,7 +82,7 @@ export function createPrometheusMdOnlyHook(ctx: PluginInput) { ): Promise => { const agentName = getAgentFromSession(input.sessionID) - if (!agentName || !PROMETHEUS_AGENTS.includes(agentName)) { + if (agentName !== PROMETHEUS_AGENT) { return }