docs: add module-level AGENTS.md for config-manager, keyword-detector, ralph-loop, session-recovery, todo-continuation-enforcer

🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
YeonGyu-Kim
2026-02-19 03:22:39 +09:00
parent a28e989f83
commit 2034cf137a
5 changed files with 295 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
# src/cli/config-manager/ — CLI Installation Utilities
**Generated:** 2026-02-19
## OVERVIEW
20 files. Stateless utility functions for the `install` command. Handles OpenCode config manipulation, provider configuration, JSONC operations, binary detection, and npm registry queries. No class — flat utility collection.
## FILE CATALOG
| File | Purpose |
|------|---------|
| `add-plugin-to-opencode-config.ts` | Register `oh-my-opencode` in `.opencode/opencode.json` plugin array |
| `add-provider-config.ts` | Add provider API key to OpenCode config (user-level) |
| `antigravity-provider-configuration.ts` | Handle Antigravity provider setup (special case) |
| `auth-plugins.ts` | Detect auth plugin requirements per provider (oauth vs key) |
| `bun-install.ts` | Run `bun install` / `npm install` for plugin setup |
| `config-context.ts` | `ConfigContext` — shared config state across install steps |
| `deep-merge-record.ts` | Deep merge utility for JSONC config objects |
| `detect-current-config.ts` | Read existing OpenCode config, detect installed plugins |
| `ensure-config-directory-exists.ts` | Create `.opencode/` dir if missing |
| `format-error-with-suggestion.ts` | Format errors with actionable suggestions |
| `generate-omo-config.ts` | Generate `oh-my-opencode.jsonc` from install selections |
| `jsonc-provider-editor.ts` | Read/write JSONC files with comment preservation |
| `npm-dist-tags.ts` | Fetch latest version from npm registry (dist-tags) |
| `opencode-binary.ts` | Detect OpenCode binary location, verify it's installed |
| `opencode-config-format.ts` | OpenCode config format constants and type guards |
| `parse-opencode-config-file.ts` | Parse opencode.json/opencode.jsonc with fallback |
| `plugin-name-with-version.ts` | Resolve `oh-my-opencode@X.Y.Z` for installation |
| `write-omo-config.ts` | Write generated config to `.opencode/oh-my-opencode.jsonc` |
## USAGE PATTERN
Functions are called sequentially by `src/cli/install.ts` / `src/cli/tui-installer.ts`:
```
1. ensure-config-directory-exists
2. detect-current-config (check what's already set up)
3. opencode-binary (verify opencode installed)
4. npm-dist-tags (get latest version)
5. generate-omo-config (build config from user selections)
6. write-omo-config
7. add-plugin-to-opencode-config
8. add-provider-config (for each provider selected)
9. bun-install
```
## NOTES
- All functions are pure / stateless (except disk I/O) — no shared module state
- `jsonc-provider-editor.ts` uses comment-preserving JSONC library — NEVER use `JSON.parse` on JSONC files
- `opencode-binary.ts` searches PATH + common install locations (`.local/bin`, `~/.bun/bin`, etc.)

View File

@@ -0,0 +1,57 @@
# src/hooks/keyword-detector/ — Mode Keyword Injection
**Generated:** 2026-02-19
## OVERVIEW
8 files + 3 mode subdirs (~1665 LOC). Transform Tier hook on `messages.transform`. Scans first user message for mode keywords (ultrawork, search, analyze) and injects mode-specific system prompts.
## KEYWORDS
| Keyword | Pattern | Effect |
|---------|---------|--------|
| `ultrawork` / `ulw` | `/\b(ultrawork|ulw)\b/i` | Full orchestration mode — parallel agents, deep exploration, relentless execution |
| Search mode | `SEARCH_PATTERN` (from `search/`) | Web/doc search focus prompt injection |
| Analyze mode | `ANALYZE_PATTERN` (from `analyze/`) | Deep analysis mode prompt injection |
## STRUCTURE
```
keyword-detector/
├── index.ts # Barrel export
├── hook.ts # createKeywordDetectorHook() — chat.message handler
├── detector.ts # detectKeywordsWithType() + extractPromptText()
├── constants.ts # KEYWORD_DETECTORS array, re-exports from submodules
├── types.ts # KeywordDetector, DetectedKeyword types
├── ultrawork/
│ ├── index.ts
│ ├── message.ts # getUltraworkMessage() — dynamic prompt by agent/model
│ └── isPlannerAgent.ts
├── search/
│ ├── index.ts
│ ├── pattern.ts # SEARCH_PATTERN regex
│ └── message.ts # SEARCH_MESSAGE
└── analyze/
├── index.ts
├── pattern.ts # ANALYZE_PATTERN regex
└── message.ts # ANALYZE_MESSAGE
```
## DETECTION LOGIC
```
chat.message (user input)
→ extractPromptText(parts)
→ isSystemDirective? → skip
→ removeSystemReminders(text) # strip <SYSTEM_REMINDER> blocks
→ detectKeywordsWithType(cleanText, agentName, modelID)
→ isPlannerAgent(agentName)? → filter out ultrawork
→ for each detected keyword: inject mode message into output
```
## GUARDS
- **System directive skip**: Messages tagged as system directives are not scanned (prevents infinite loops)
- **Planner agent filter**: Prometheus/plan agents do not receive `ultrawork` injection
- **Session agent tracking**: Uses `getSessionAgent()` to get actual agent (not just input hint)
- **Model-aware messages**: `getUltraworkMessage(agentName, modelID)` adapts message to active model

View File

@@ -0,0 +1,62 @@
# src/hooks/ralph-loop/ — Self-Referential Dev Loop
**Generated:** 2026-02-19
## OVERVIEW
14 files (~1687 LOC). The `ralphLoop` Session Tier hook — powers the `/ralph-loop` command. Iterates a development loop until the agent emits `<promise>DONE</promise>` or max iterations reached.
## LOOP LIFECYCLE
```
/ralph-loop → startLoop(sessionID, prompt, options)
→ loopState.startLoop() → persists state to .sisyphus/ralph-loop.local.md
→ session.idle events → createRalphLoopEventHandler()
→ completionPromiseDetector: scan output for <promise>DONE</promise>
→ if not done: inject continuation prompt → loop
→ if done or maxIterations: cancelLoop()
```
## KEY FILES
| File | Purpose |
|------|---------|
| `ralph-loop-hook.ts` | `createRalphLoopHook()` — composes controller + recovery + event handler |
| `ralph-loop-event-handler.ts` | `createRalphLoopEventHandler()` — handles session.idle, drives loop |
| `loop-state-controller.ts` | State CRUD: startLoop, cancelLoop, getState, persist to disk |
| `loop-session-recovery.ts` | Recover from crashed/interrupted loop sessions |
| `completion-promise-detector.ts` | Scan session transcript for `<promise>DONE</promise>` |
| `continuation-prompt-builder.ts` | Build continuation message for next iteration |
| `continuation-prompt-injector.ts` | Inject built prompt into active session |
| `storage.ts` | Read/write `.sisyphus/ralph-loop.local.md` state file |
| `message-storage-directory.ts` | Temp dir for prompt injection |
| `with-timeout.ts` | API call wrapper with timeout (default 5000ms) |
| `types.ts` | `RalphLoopState`, `RalphLoopOptions`, loop iteration types |
## STATE FILE
```
.sisyphus/ralph-loop.local.md (gitignored)
→ sessionID, prompt, iteration count, maxIterations, completionPromise, ultrawork flag
```
## OPTIONS
```typescript
startLoop(sessionID, prompt, {
maxIterations?: number // Default from config (default: 100)
completionPromise?: string // Custom "done" signal (default: "<promise>DONE</promise>")
ultrawork?: boolean // Enable ultrawork mode for iterations
})
```
## EXPORTED INTERFACE
```typescript
interface RalphLoopHook {
event: (input) => Promise<void> // session.idle handler
startLoop: (sessionID, prompt, options?) => boolean
cancelLoop: (sessionID) => boolean
getState: () => RalphLoopState | null
}
```

View File

@@ -0,0 +1,59 @@
# src/hooks/session-recovery/ — Auto Session Error Recovery
**Generated:** 2026-02-19
## OVERVIEW
16 files + storage/ subdir. Session Tier hook handling `session.error` events. Detects recoverable error types, applies targeted recovery strategies, and resumes the session transparently.
## RECOVERY STRATEGIES
| Error Type | File | Recovery Action |
|------------|------|-----------------|
| `tool_result_missing` | `recover-tool-result-missing.ts` | Reconstruct missing tool results from storage |
| `thinking_block_order` | `recover-thinking-block-order.ts` | Reorder malformed thinking blocks |
| `thinking_disabled_violation` | `recover-thinking-disabled-violation.ts` | Strip thinking blocks when disabled |
| `empty_content_message` | `recover-empty-content-message*.ts` | Handle empty/null content blocks |
## KEY FILES
| File | Purpose |
|------|---------|
| `hook.ts` | `createSessionRecoveryHook()` — error detection, strategy dispatch, resume |
| `detect-error-type.ts` | `detectErrorType(error)``RecoveryErrorType \| null` |
| `resume.ts` | `resumeSession()` — rebuild session context, trigger retry |
| `storage.ts` | Per-session message storage for recovery reconstruction |
| `recover-tool-result-missing.ts` | Reconstruct tool results from stored metadata |
| `recover-thinking-block-order.ts` | Fix malformed thinking block sequences |
| `recover-thinking-disabled-violation.ts` | Remove thinking blocks from model context |
| `recover-empty-content-message.ts` | Handle empty assistant messages |
| `recover-empty-content-message-sdk.ts` | SDK variant for empty content recovery |
| `types.ts` | `StoredMessageMeta`, `StoredPart`, `ResumeConfig`, `MessageData` |
## STORAGE SUBDIRECTORY
```
storage/
├── message-store.ts # In-memory + file message cache
├── part-store.ts # Individual message parts storage
└── index.ts # Barrel export
```
Stores message metadata and parts per session for recovery reconstruction.
## HOOK INTERFACE
```typescript
interface SessionRecoveryHook {
handleSessionRecovery: (info: MessageInfo) => Promise<boolean>
isRecoverableError: (error: unknown) => boolean
setOnAbortCallback: (cb: (sessionID: string) => void) => void
setOnRecoveryCompleteCallback: (cb: (sessionID: string) => void) => void
}
```
## NOTES
- Guards with `processingErrors` Set to prevent duplicate recovery attempts on same error
- Supports `experimental` config for behavior flags
- Distinct from `anthropic-context-window-limit-recovery` (handles token limit; this handles structural errors)

View File

@@ -0,0 +1,65 @@
# src/hooks/todo-continuation-enforcer/ — Boulder Continuation Mechanism
**Generated:** 2026-02-19
## OVERVIEW
14 files (~2061 LOC). The "boulder" — Continuation Tier hook that forces Sisyphus to keep rolling when incomplete todos remain. Fires on `session.idle`, injects continuation prompt after 2s countdown toast.
## HOW IT WORKS
```
session.idle
→ Is main session (not prometheus/compaction)? (DEFAULT_SKIP_AGENTS)
→ No abort detected recently? (ABORT_WINDOW_MS = 3s)
→ Todos still incomplete? (todo.ts)
→ No background tasks running?
→ Cooldown passed? (CONTINUATION_COOLDOWN_MS = 30s)
→ Failure count < max? (MAX_CONSECUTIVE_FAILURES = 5)
→ Start 2s countdown toast → inject CONTINUATION_PROMPT
```
## KEY FILES
| File | Purpose |
|------|---------|
| `handler.ts` | `createTodoContinuationHandler()` — event router, delegates to idle/non-idle handlers |
| `idle-event.ts` | `handleSessionIdle()` — main decision gate for session.idle |
| `non-idle-events.ts` | `handleNonIdleEvent()` — handles session.error (abort detection) |
| `session-state.ts` | `SessionStateStore` — per-session failure/abort/cooldown state |
| `todo.ts` | Check todo completion status via session store |
| `countdown.ts` | 2s countdown toast before injection |
| `abort-detection.ts` | Detect MessageAbortedError / AbortError |
| `continuation-injection.ts` | Build + inject CONTINUATION_PROMPT into session |
| `message-directory.ts` | Temp dir for message injection exchange |
| `constants.ts` | Timing constants, CONTINUATION_PROMPT, skip agents |
| `types.ts` | `SessionState`, handler argument types |
## CONSTANTS
```typescript
DEFAULT_SKIP_AGENTS = ["prometheus", "compaction"]
CONTINUATION_COOLDOWN_MS = 30_000 // 30s between injections
MAX_CONSECUTIVE_FAILURES = 5 // Then 5min pause (exponential backoff)
FAILURE_RESET_WINDOW_MS = 5 * 60_000 // 5min window for failure reset
COUNTDOWN_SECONDS = 2
ABORT_WINDOW_MS = 3000 // Grace after abort signal
```
## STATE PER SESSION
```typescript
interface SessionState {
failureCount: number // Consecutive failures
lastFailureAt?: number // Timestamp
abortDetectedAt?: number // Reset after ABORT_WINDOW_MS
cooldownUntil?: number // Next injection allowed after
countdownTimer?: Timer // Active countdown reference
}
```
## RELATIONSHIP TO ATLAS
`todoContinuationEnforcer` handles **main Sisyphus sessions** only.
`atlasHook` handles **boulder/ralph/subagent sessions** with a different decision gate.
Both fire on `session.idle` but check session type first.