ソースを参照

Merge remote-tracking branch 'origin/master' into omos/pr-664-conflict

# Conflicts:
#	src/council/council-manager.ts
Alvin Unreal 1 ヶ月 前
コミット
619fd77d6b
67 ファイル変更578 行追加487 行削除
  1. 6 6
      AGENTS.md
  2. 40 13
      README.ja-JP.md
  3. 38 11
      README.ko-KR.md
  4. 39 27
      README.md
  5. 41 16
      README.zh-CN.md
  6. 1 1
      companion/Cargo.toml
  7. 1 1
      docs/acp-agents.md
  8. 3 3
      docs/adr/001-session-reflection-mode.md
  9. 1 1
      docs/clonedeps.md
  10. 3 3
      docs/companion.md
  11. 22 22
      docs/configuration.md
  12. 4 4
      docs/council.md
  13. 16 16
      docs/interview.md
  14. 6 6
      docs/maintainers.md
  15. 1 1
      docs/mcps.md
  16. 2 2
      docs/opencode-go-preset.md
  17. 3 3
      docs/quick-reference.md
  18. 6 6
      docs/skills.md
  19. 115 115
      docs/superpowers/plans/2026-06-25-loop-engineering-runtime.md
  20. 83 83
      docs/superpowers/specs/2026-06-25-loop-engineering-runtime.md
  21. 1 1
      docs/tools.md
  22. 1 1
      oh-my-opencode-slim.schema.json
  23. 1 1
      package.json
  24. 2 2
      src/agents/council.test.ts
  25. 8 8
      src/agents/council.ts
  26. 4 4
      src/agents/councillor.ts
  27. 4 4
      src/agents/designer.ts
  28. 1 1
      src/agents/fixer.ts
  29. 2 2
      src/agents/index.test.ts
  30. 1 1
      src/agents/index.ts
  31. 6 6
      src/agents/observer.ts
  32. 6 6
      src/agents/orchestrator.ts
  33. 1 1
      src/cli/providers.test.ts
  34. 1 1
      src/cli/providers.ts
  35. 2 2
      src/cli/skills.ts
  36. 1 1
      src/config/constants.ts
  37. 4 4
      src/config/council-schema.ts
  38. 1 1
      src/config/model-resolution.test.ts
  39. 1 1
      src/council/codemap.md
  40. 2 2
      src/council/council-manager.test.ts
  41. 2 2
      src/council/council-manager.ts
  42. 1 1
      src/hooks/apply-patch/codec.test.ts
  43. 1 1
      src/hooks/codemap.md
  44. 1 1
      src/hooks/deepwork/codemap.md
  45. 17 17
      src/hooks/foreground-fallback/index.test.ts
  46. 7 7
      src/hooks/foreground-fallback/index.ts
  47. 1 1
      src/hooks/loop-command/index.ts
  48. 1 1
      src/hooks/types.ts
  49. 4 4
      src/index.ts
  50. 2 2
      src/interview/dashboard-manager.ts
  51. 6 6
      src/interview/dashboard.ts
  52. 1 1
      src/interview/document.ts
  53. 2 2
      src/interview/helpers.test.ts
  54. 4 4
      src/interview/service.ts
  55. 1 1
      src/interview/types.ts
  56. 11 11
      src/interview/ui.ts
  57. 2 2
      src/skills/clonedeps/SKILL.md
  58. 2 2
      src/skills/codemap/SKILL.md
  59. 10 10
      src/skills/reflect/SKILL.md
  60. 6 6
      src/skills/simplify/SKILL.md
  61. 1 1
      src/tools/council.test.ts
  62. 2 2
      src/tools/council.ts
  63. 1 1
      src/tools/preset-manager.ts
  64. 3 3
      src/utils/background-job-board.test.ts
  65. 5 5
      src/utils/logger.ts
  66. 1 1
      src/utils/session.ts
  67. 2 2
      src/utils/system-collapse.test.ts

+ 6 - 6
AGENTS.md

@@ -248,12 +248,12 @@ OpenCode has a built-in `/review` command that automatically performs comprehens
 - This is an OpenCode plugin - most functionality lives in `src/`
 - The CLI entry point is `src/cli/index.ts`
 - The main plugin export is `src/index.ts`
-- Agent factories are in `src/agents/`  each agent has its own file + optional `.test.ts`
+- Agent factories are in `src/agents/` - each agent has its own file + optional `.test.ts`
 - Skills are located in `src/skills/` (included in package publish)
 - Multiplexer session management is in `src/multiplexer/`
 - Council manager (multi-LLM orchestration) is in `src/council/`
 - Tmux utilities are in `src/utils/tmux.ts`
-- 468 tests across 35 files  run `bun test` to verify
+- 468 tests across 35 files - run `bun test` to verify
 
 ## Repository Map
 
@@ -281,7 +281,7 @@ You can set the log level with the --log-level command-line option to get more d
 Read-only dependency source repositories are available under
 `.slim/clonedeps/repos/` for inspection. Do not edit these clones.
 
-- `.slim/clonedeps/repos/opencode-ai__opencode/`  `https://github.com/opencode-ai/opencode.git` at `main@73ee493265acf15fcd8caab2bc8cd3bd375b63cb`; inspect `packages/plugin` and `packages/sdk/js` for OpenCode plugin and SDK internals.
-- `.slim/clonedeps/repos/opencode/`  `https://github.com/anomalyco/opencode.git` at `dev@356f6841865d68adf6d0123c37357ad50814497a`; inspect `packages/opencode` for latest TypeScript runtime internals and experimental background subagent support.
-- `.slim/clonedeps/repos/modelcontextprotocol__typescript-sdk/`  `https://github.com/modelcontextprotocol/typescript-sdk.git` at `v1.29.0@e12cbd7078db388152f6e839abdbe09ba01f3f32`; inspect it for MCP protocol and server integration internals.
-- `.slim/clonedeps/repos/agentclientprotocol__agent-client-protocol/`  `https://github.com/agentclientprotocol/agent-client-protocol.git` at `main@8110fde4e8283b4bef1329d1ef7b074fd14cee1e`; inspect it for ACP protocol specification and schema details.
+- `.slim/clonedeps/repos/opencode-ai__opencode/` - `https://github.com/opencode-ai/opencode.git` at `main@73ee493265acf15fcd8caab2bc8cd3bd375b63cb`; inspect `packages/plugin` and `packages/sdk/js` for OpenCode plugin and SDK internals.
+- `.slim/clonedeps/repos/opencode/` - `https://github.com/anomalyco/opencode.git` at `dev@356f6841865d68adf6d0123c37357ad50814497a`; inspect `packages/opencode` for latest TypeScript runtime internals and experimental background subagent support.
+- `.slim/clonedeps/repos/modelcontextprotocol__typescript-sdk/` - `https://github.com/modelcontextprotocol/typescript-sdk.git` at `v1.29.0@e12cbd7078db388152f6e839abdbe09ba01f3f32`; inspect it for MCP protocol and server integration internals.
+- `.slim/clonedeps/repos/agentclientprotocol__agent-client-protocol/` - `https://github.com/agentclientprotocol/agent-client-protocol.git` at `main@8110fde4e8283b4bef1329d1ef7b074fd14cee1e`; inspect it for ACP protocol specification and schema details.

+ 40 - 13
README.ja-JP.md

@@ -88,10 +88,37 @@ Install and configure oh-my-opencode-slim: https://raw.githubusercontent.com/alv
 bunx oh-my-opencode-slim@latest install
 ```
 
+### Master ブランチから実行
+
+最新コードを使いたい場合、バグ修正を試したい場合、またはローカルで
+開発・コントリビュートしたい場合はこちらを使ってください:
+
+```bash
+git clone https://github.com/alvinunreal/oh-my-opencode-slim.git ~/repos/oh-my-opencode-slim
+cd ~/repos/oh-my-opencode-slim
+bun install
+bun run build
+bun dist/cli/index.js install
+```
+
+インストーラーはローカルリポジトリのパスを
+`~/.config/opencode/opencode.json` の `plugin` 配列に追加するため、
+OpenCode はそのフォルダーからプラグインを読み込みます。後で更新するには:
+
+```bash
+cd ~/repos/oh-my-opencode-slim
+git pull
+bun install
+bun run build
+```
+
 ### はじめに
 
 インストーラーは OpenAI と OpenCode Go の両方のプリセットを生成し、デフォルトでは OpenAI が有効になります。
 
+> [!TIP]
+> モデルやエージェントは、自分のワークフローに合わせて自由に調整してください。デフォルトプリセットは出発点にすぎません。このプラグインは、深い柔軟性とカスタマイズ性を提供するために設計されています。
+
 インストール時に OpenCode Go を有効にするには、`bunx oh-my-opencode-slim@latest install --preset=opencode-go` を実行するか、インストール後に `~/.config/opencode/oh-my-opencode-slim.json` のデフォルトプリセット名を変更してください。
 
 次に:
@@ -135,7 +162,7 @@ bunx oh-my-opencode-slim@latest install
       "orchestrator": { "model": "opencode-go/glm-5.2", "skills": [ "*" ], "mcps": [ "*", "!context7" ] },
       "oracle": { "model": "opencode-go/qwen3.7-max", "variant": "max", "skills": ["simplify"], "mcps": [] },
       "librarian": { "model": "opencode-go/deepseek-v4-flash", "skills": [], "mcps": [ "websearch", "context7", "gh_grep" ] },
-      "explorer": { "model": "opencode-go/minimax-m2.7", "skills": [], "mcps": [] },
+      "explorer": { "model": "opencode-go/deepseek-v4-flash", "skills": [], "mcps": [] },
       "designer": { "model": "opencode-go/kimi-k2.7-code", "variant": "medium", "skills": [], "mcps": [] },
       "fixer": { "model": "opencode-go/deepseek-v4-flash", "variant": "high", "skills": [], "mcps": [] }
     }
@@ -174,12 +201,12 @@ ping all agents
 
 V2 は oh-my-opencode-slim を、スケジューラー中心のマルチエージェントワークフローシステムへと進化させます。Orchestrator は計画、委譲、結果の整合、検証に集中し、専門エージェントはそれぞれの lane で作業を行います。
 
-- **[バックグラウンドエージェント](#バックグラウンドエージェント)**  Orchestrator は専門家をバックグラウンドタスクとしてディスパッチし、タスク/セッション ID を追跡し、完了イベントを待ってから結果を整合します。
-- **[Companion](#companion)**  任意のフローティングデスクトップウィンドウが、並列実行中のバックグラウンド専門家を含め、現在アクティブなエージェントを表示します。
-- **[Deepwork](#deepwork)**  大規模、多ファイル、高リスク、または段階的なコーディング作業向けの構造化ワークフローです。永続的な計画ファイルと Oracle レビューゲートを使用します。
-- **[Reflect](#reflect)**  繰り返される作業パターンを振り返り、再利用可能な skill、エージェント、コマンド、設定ルール、プロンプトルール、プロジェクト playbook を提案します。
-- **[Worktrees](#worktrees)**  複雑、高リスク、または並列タスク向けに、安全プロトコル付きの隔離されたコーディング lane として Git worktree を管理します。
-- **[oh-my-opencode-slim Skill](#oh-my-opencode-slim-skill)**  モデル、プロンプト、カスタムエージェント、MCP アクセス、プリセット、プラグイン動作を安全に調整するための同梱設定 skill です。
+- **[バックグラウンドエージェント](#バックグラウンドエージェント)** - Orchestrator は専門家をバックグラウンドタスクとしてディスパッチし、タスク/セッション ID を追跡し、完了イベントを待ってから結果を整合します。
+- **[Companion](#companion)** - 任意のフローティングデスクトップウィンドウが、並列実行中のバックグラウンド専門家を含め、現在アクティブなエージェントを表示します。
+- **[Deepwork](#deepwork)** - 大規模、多ファイル、高リスク、または段階的なコーディング作業向けの構造化ワークフローです。永続的な計画ファイルと Oracle レビューゲートを使用します。
+- **[Reflect](#reflect)** - 繰り返される作業パターンを振り返り、再利用可能な skill、エージェント、コマンド、設定ルール、プロンプトルール、プロジェクト playbook を提案します。
+- **[Worktrees](#worktrees)** - 複雑、高リスク、または並列タスク向けに、安全プロトコル付きの隔離されたコーディング lane として Git worktree を管理します。
+- **[oh-my-opencode-slim Skill](#oh-my-opencode-slim-skill)** - モデル、プロンプト、カスタムエージェント、MCP アクセス、プリセット、プラグイン動作を安全に調整するための同梱設定 skill です。
 
 #### バックグラウンドエージェント
 
@@ -414,7 +441,7 @@ Worktrees は、Git worktree を `.slim/worktrees/<slug>/` 配下の安全で隔
   </tr>
   <tr>
     <td colspan="2">
-      <b>Default Setup:</b> <code>Config-driven</code>  評議員(councillors)は <code>council.presets</code> から、Council エージェント自身のモデルは通常の <code>council</code> エージェント設定から決定されます
+      <b>Default Setup:</b> <code>Config-driven</code> - 評議員(councillors)は <code>council.presets</code> から、Council エージェント自身のモデルは通常の <code>council</code> エージェント設定から決定されます
     </td>
   </tr>
   <tr>
@@ -481,7 +508,7 @@ Worktrees は、Git worktree を `.slim/worktrees/<slug>/` 配下の安全で隔
       <br><sub><i>美は不可欠なもの。</i></sub>
     </td>
     <td width="70%" valign="top">
-      Designer は、美が重要であることを忘れがちな世界において、それを守り続ける不死の守護者です。これまでに無数のインターフェースが現れては消えるのを見届け、どれが人々の記憶に残り、どれが忘れ去られたかを知っています。すべてのピクセルに目的を、すべてのアニメーションに物語を、すべてのインタラクションに喜びを宿す——その神聖な責務を担います。美は選択肢ではなく、不可欠なものです。
+      Designer は、美が重要であることを忘れがちな世界において、それを守り続ける不死の守護者です。これまでに無数のインターフェースが現れては消えるのを見届け、どれが人々の記憶に残り、どれが忘れ去られたかを知っています。すべてのピクセルに目的を、すべてのアニメーションに物語を、すべてのインタラクションに喜びを宿す--その神聖な責務を担います。美は選択肢ではなく、不可欠なものです。
     </td>
   </tr>
   <tr>
@@ -522,7 +549,7 @@ Worktrees は、Git worktree を `.slim/worktrees/<slug>/` 配下の安全で隔
       <br><sub><i>構想と現実を結ぶ最後の一歩。</i></sub>
     </td>
     <td width="70%" valign="top">
-      Fixer は、かつてデジタル世界の礎を築き上げた建造者の系譜の最後のひとりです。計画と議論の時代が始まってもなお、彼らだけは残りました——実際に作る者として。思考をモノへと変え、仕様を実装へと転換する古の知恵を継承しています。Fixer は、構想と現実の間にある最後の一歩です。
+      Fixer は、かつてデジタル世界の礎を築き上げた建造者の系譜の最後のひとりです。計画と議論の時代が始まってもなお、彼らだけは残りました--実際に作る者として。思考をモノへと変え、仕様を実装へと転換する古の知恵を継承しています。Fixer は、構想と現実の間にある最後の一歩です。
     </td>
   </tr>
   <tr>
@@ -569,11 +596,11 @@ Worktrees は、Git worktree を `.slim/worktrees/<slug>/` 配下の安全で隔
     </td>
     <td width="70%" valign="top">
 
-**読み取り専用のビジュアル解析**  画像、スクリーンショット、PDF、図解を解釈します。ファイルの生バイトをメインのコンテキストウィンドウに読み込ませることなく、構造化された観察結果をオーケストレーターに返します。
+**読み取り専用のビジュアル解析** - 画像、スクリーンショット、PDF、図解を解釈します。ファイルの生バイトをメインのコンテキストウィンドウに読み込ませることなく、構造化された観察結果をオーケストレーターに返します。
 
 - 画像、スクリーンショット、図解 → `read` ツール(ネイティブな画像サポート)
 - PDF やバイナリドキュメント → `read` ツール(テキスト+構造抽出)
-- **デフォルトでは無効**  `"disabled_agents": []` を設定し、ビジョン対応モデルを構成することで有効化できます。`--preset=opencode-go` でインストールすると `opencode-go/kimi-k2.6` で有効になります
+- **デフォルトでは無効** - `"disabled_agents": []` を設定し、ビジョン対応モデルを構成することで有効化できます。`--preset=opencode-go` でインストールすると `opencode-go/kimi-k2.6` で有効になります
 
     </td>
   </tr>
@@ -584,7 +611,7 @@ Worktrees は、Git worktree を `.slim/worktrees/<slug>/` 配下の安全で隔
   </tr>
   <tr>
     <td colspan="2">
-      <b>Default Model:</b> <code>openai/gpt-5.4-mini</code>  <i>有効化するにはビジョン対応モデルを設定してください</i>
+      <b>Default Model:</b> <code>openai/gpt-5.4-mini</code> - <i>有効化するにはビジョン対応モデルを設定してください</i>
     </td>
   </tr>
   <tr>

+ 38 - 11
README.ko-KR.md

@@ -86,10 +86,37 @@ Install and configure oh-my-opencode-slim: https://raw.githubusercontent.com/alv
 bunx oh-my-opencode-slim@latest install
 ```
 
+### Master 브랜치에서 실행하기
+
+최신 코드를 사용하거나, 버그를 고치거나, 로컬에서 개발하고 기여하려면 이
+방식을 사용하세요:
+
+```bash
+git clone https://github.com/alvinunreal/oh-my-opencode-slim.git ~/repos/oh-my-opencode-slim
+cd ~/repos/oh-my-opencode-slim
+bun install
+bun run build
+bun dist/cli/index.js install
+```
+
+인스톨러는 로컬 저장소 경로를 `~/.config/opencode/opencode.json`의
+`plugin` 배열에 추가하므로, OpenCode는 해당 폴더에서 플러그인을 로드합니다.
+나중에 업데이트하려면:
+
+```bash
+cd ~/repos/oh-my-opencode-slim
+git pull
+bun install
+bun run build
+```
+
 ### 시작하기
 
 인스톨러는 OpenAI와 OpenCode Go 프리셋을 모두 생성하며, 기본적으로 OpenAI가 활성화됩니다.
 
+> [!TIP]
+> 모델과 에이전트는 자신의 워크플로에 맞게 자유롭게 조정하세요. 기본 프리셋은 시작점일 뿐이며, 이 플러그인은 깊은 유연성과 커스터마이징을 제공하도록 설계되었습니다.
+
 설치 중 OpenCode Go를 활성화하려면 `bunx oh-my-opencode-slim@latest install --preset=opencode-go`를 실행하거나, 설치 후 `~/.config/opencode/oh-my-opencode-slim.json`에서 기본 프리셋 이름을 변경하세요.
 
 그 다음:
@@ -133,7 +160,7 @@ bunx oh-my-opencode-slim@latest install
       "orchestrator": { "model": "opencode-go/glm-5.2", "skills": [ "*" ], "mcps": [ "*", "!context7" ] },
       "oracle": { "model": "opencode-go/qwen3.7-max", "variant": "max", "skills": ["simplify"], "mcps": [] },
       "librarian": { "model": "opencode-go/deepseek-v4-flash", "skills": [], "mcps": [ "websearch", "context7", "gh_grep" ] },
-      "explorer": { "model": "opencode-go/minimax-m2.7", "skills": [], "mcps": [] },
+      "explorer": { "model": "opencode-go/deepseek-v4-flash", "skills": [], "mcps": [] },
       "designer": { "model": "opencode-go/kimi-k2.7-code", "variant": "medium", "skills": [], "mcps": [] },
       "fixer": { "model": "opencode-go/deepseek-v4-flash", "variant": "high", "skills": [], "mcps": [] }
     }
@@ -172,12 +199,12 @@ ping all agents
 
 V2는 oh-my-opencode-slim을 스케줄러 중심의 멀티 에이전트 워크플로 시스템으로 바꿉니다. Orchestrator는 계획, 위임, 결과 조정, 검증에 집중하고, 전문가들은 각자의 lane에서 작업합니다.
 
-- **[백그라운드 에이전트](#백그라운드-에이전트)**  Orchestrator가 전문가를 백그라운드 작업으로 디스패치하고, 작업/세션 ID를 추적하며, 완료 이벤트를 기다린 뒤 결과를 조정합니다.
-- **[Companion](#companion)**  선택 사항인 플로팅 데스크톱 창이 병렬 백그라운드 전문가를 포함해 현재 활성 에이전트를 보여줍니다.
-- **[Deepwork](#deepwork)**  대규모, 다중 파일, 위험도가 높거나 단계적인 코딩 작업을 위한 구조화된 워크플로입니다. 지속적인 계획 파일과 Oracle 리뷰 게이트를 사용합니다.
-- **[Reflect](#reflect)**  반복되는 작업 패턴을 돌아보고 재사용 가능한 skill, 에이전트, 명령, 설정 규칙, 프롬프트 규칙, 프로젝트 playbook을 제안합니다.
-- **[Worktrees](#worktrees)**  복잡하거나 위험하거나 병렬로 진행되는 작업을 위해 Git worktree를 안전 프로토콜이 있는 격리된 코딩 lane으로 관리합니다.
-- **[oh-my-opencode-slim Skill](#oh-my-opencode-slim-skill)**  모델, 프롬프트, 커스텀 에이전트, MCP 접근, 프리셋, 플러그인 동작을 안전하게 조정하는 번들 설정 skill입니다.
+- **[백그라운드 에이전트](#백그라운드-에이전트)** - Orchestrator가 전문가를 백그라운드 작업으로 디스패치하고, 작업/세션 ID를 추적하며, 완료 이벤트를 기다린 뒤 결과를 조정합니다.
+- **[Companion](#companion)** - 선택 사항인 플로팅 데스크톱 창이 병렬 백그라운드 전문가를 포함해 현재 활성 에이전트를 보여줍니다.
+- **[Deepwork](#deepwork)** - 대규모, 다중 파일, 위험도가 높거나 단계적인 코딩 작업을 위한 구조화된 워크플로입니다. 지속적인 계획 파일과 Oracle 리뷰 게이트를 사용합니다.
+- **[Reflect](#reflect)** - 반복되는 작업 패턴을 돌아보고 재사용 가능한 skill, 에이전트, 명령, 설정 규칙, 프롬프트 규칙, 프로젝트 playbook을 제안합니다.
+- **[Worktrees](#worktrees)** - 복잡하거나 위험하거나 병렬로 진행되는 작업을 위해 Git worktree를 안전 프로토콜이 있는 격리된 코딩 lane으로 관리합니다.
+- **[oh-my-opencode-slim Skill](#oh-my-opencode-slim-skill)** - 모델, 프롬프트, 커스텀 에이전트, MCP 접근, 프리셋, 플러그인 동작을 안전하게 조정하는 번들 설정 skill입니다.
 
 #### 백그라운드 에이전트
 
@@ -412,7 +439,7 @@ Worktrees는 Git worktree를 `.slim/worktrees/<slug>/` 아래의 안전하고 
   </tr>
   <tr>
     <td colspan="2">
-      <b>기본 설정:</b> <code>Config-driven</code>  Council 구성원은 <code>council.presets</code>에서 가져오고, Council 에이전트 모델은 일반 <code>council</code> 에이전트 설정에서 가져옵니다
+      <b>기본 설정:</b> <code>Config-driven</code> - Council 구성원은 <code>council.presets</code>에서 가져오고, Council 에이전트 모델은 일반 <code>council</code> 에이전트 설정에서 가져옵니다
     </td>
   </tr>
   <tr>
@@ -567,11 +594,11 @@ Worktrees는 Git worktree를 `.slim/worktrees/<slug>/` 아래의 안전하고 
     </td>
     <td width="70%" valign="top">
 
-**읽기 전용 시각 분석**  이미지, 스크린샷, PDF, 다이어그램을 해석합니다. 원시 파일 바이트를 메인 컨텍스트 윈도우에 로드하지 않고, 구조화된 관찰 결과를 오케스트레이터에 반환합니다.
+**읽기 전용 시각 분석** - 이미지, 스크린샷, PDF, 다이어그램을 해석합니다. 원시 파일 바이트를 메인 컨텍스트 윈도우에 로드하지 않고, 구조화된 관찰 결과를 오케스트레이터에 반환합니다.
 
 - 이미지, 스크린샷, 다이어그램 -> `read` 도구 (네이티브 이미지 지원)
 - PDF 및 바이너리 문서 -> `read` 도구 (텍스트 + 구조 추출)
-- **기본 비활성화**  `"disabled_agents": []`로 설정하고 비전 지원 모델을 구성하여 활성화; `--preset=opencode-go`로 설치하면 `opencode-go/kimi-k2.6`으로 자동 활성화됩니다
+- **기본 비활성화** - `"disabled_agents": []`로 설정하고 비전 지원 모델을 구성하여 활성화; `--preset=opencode-go`로 설치하면 `opencode-go/kimi-k2.6`으로 자동 활성화됩니다
 
     </td>
   </tr>
@@ -582,7 +609,7 @@ Worktrees는 Git worktree를 `.slim/worktrees/<slug>/` 아래의 안전하고 
   </tr>
   <tr>
     <td colspan="2">
-      <b>기본 모델:</b> <code>openai/gpt-5.4-mini</code>  <i>비전 지원 모델을 구성하여 활성화</i>
+      <b>기본 모델:</b> <code>openai/gpt-5.4-mini</code> - <i>비전 지원 모델을 구성하여 활성화</i>
     </td>
   </tr>
   <tr>

+ 39 - 27
README.md

@@ -30,10 +30,6 @@ The main idea is simple: instead of forcing one model to do everything, the plug
 
 To explore the agents themselves, see **[Meet the Pantheon](#meet-the-pantheon)**. For the full feature set, see **[Features & Workflows](#features-and-workflows)** below.
 
-### Submit Your Preset
-
-Using a model mix that works well? Share it with the community through the **[preset submission form](https://github.com/alvinunreal/oh-my-opencode-slim/issues/new?template=preset_submission.yml)**. Accepted presets are reviewed and displayed in the **[Community Presets gallery](https://ohmyopencodeslim.com/community-presets)** with attribution.
-
 ### Manage Agent Skills with LazySkills
 
 <p align="center">
@@ -87,10 +83,38 @@ Install and configure oh-my-opencode-slim: https://raw.githubusercontent.com/alv
 bunx oh-my-opencode-slim@latest install
 ```
 
+### Run from Master
+
+Use this if you want the latest code, easier bug fixes, or a local setup for
+development and contributions:
+
+```bash
+git clone https://github.com/alvinunreal/oh-my-opencode-slim.git ~/repos/oh-my-opencode-slim
+cd ~/repos/oh-my-opencode-slim
+bun install
+bun run build
+bun dist/cli/index.js install
+```
+
+The installer adds the local repo path to the `plugin` array in
+`~/.config/opencode/opencode.json`, so OpenCode loads the plugin from that
+folder. To update later:
+
+```bash
+cd ~/repos/oh-my-opencode-slim
+git pull
+bun install
+bun run build
+```
+
 ### Getting Started
 
 The installer generates both OpenAI and OpenCode Go presets, with OpenAI active by default.
 
+> [!TIP]
+> Tune the models and agents for your own workflow. The defaults are only a
+> starting point; the plugin is designed for deep flexibility and customization.
+
 To make OpenCode Go active during install, run `bunx oh-my-opencode-slim@latest install --preset=opencode-go` or change the default preset name in `~/.config/opencode/oh-my-opencode-slim.json` after installation.
 
 Then:
@@ -134,7 +158,7 @@ The default generated configuration includes both `openai` and `opencode-go` pre
       "orchestrator": { "model": "opencode-go/glm-5.2", "skills": [ "*" ], "mcps": [ "*", "!context7" ] },
       "oracle": { "model": "opencode-go/qwen3.7-max", "variant": "max", "skills": ["simplify"], "mcps": [] },
       "librarian": { "model": "opencode-go/deepseek-v4-flash", "skills": [], "mcps": [ "websearch", "context7", "gh_grep" ] },
-      "explorer": { "model": "opencode-go/minimax-m2.7", "skills": [], "mcps": [] },
+      "explorer": { "model": "opencode-go/deepseek-v4-flash", "skills": [], "mcps": [] },
       "designer": { "model": "opencode-go/kimi-k2.7-code", "variant": "medium", "skills": [], "mcps": [] },
       "fixer": { "model": "opencode-go/deepseek-v4-flash", "variant": "high", "skills": [], "mcps": [] }
     }
@@ -146,18 +170,6 @@ The default generated configuration includes both `openai` and `opencode-go` pre
 
 To use custom providers or a mixed-provider setup, use **[Configuration](docs/configuration.md)** for the full reference. If you want a ready-made starting point, check the **[Author's Preset](docs/authors-preset.md)** and **[$30 Preset](docs/thirty-dollars-preset.md)** - the `$30` preset is the best cheap setup.
 
-### Temporarily Disable the Plugin
-
-Set `OH_MY_OPENCODE_SLIM_DISABLE=1` when starting OpenCode to make the plugin
-return without registering agents, tools, MCPs, hooks, Companion, or the TUI
-sidebar:
-
-```bash
-OH_MY_OPENCODE_SLIM_DISABLE=1 opencode
-```
-
-Truthy values are `1`, `true`, `yes`, and `on`.
-
 ### ✅ Verify Your Setup
 
 After installation and authentication, verify all agents are configured and responding:
@@ -187,18 +199,18 @@ V2 turns oh-my-opencode-slim into a scheduler-first multi-agent workflow system.
 The Orchestrator stays focused on planning, delegation, reconciliation, and
 verification while specialists do the work in their own lanes.
 
-- **[Background agents](#background-agents)**  the Orchestrator now dispatches
+- **[Background agents](#background-agents)** - the Orchestrator now dispatches
   specialists as background tasks, tracks task/session IDs, waits for completion
   events, and reconciles results before continuing.
-- **[Companion](#companion)**  an optional floating desktop window shows which
+- **[Companion](#companion)** - an optional floating desktop window shows which
   agents are currently active, including parallel background specialists.
-- **[Deepwork](#deepwork)**  a structured workflow for large, multi-file, risky,
+- **[Deepwork](#deepwork)** - a structured workflow for large, multi-file, risky,
   or phased coding work using persistent plan files and Oracle review gates.
-- **[Reflect](#reflect)**  reviews repeated work patterns and suggests reusable skills,
+- **[Reflect](#reflect)** - reviews repeated work patterns and suggests reusable skills,
   agents, commands, config rules, prompt rules, or project playbooks.
-- **[Worktrees](#worktrees)**  manages Git worktrees as isolated coding lanes
+- **[Worktrees](#worktrees)** - manages Git worktrees as isolated coding lanes
   with safety protocols for complex, risky, or parallel tasks.
-- **[oh-my-opencode-slim skill](#oh-my-opencode-slim-skill)**  a bundled
+- **[oh-my-opencode-slim skill](#oh-my-opencode-slim-skill)** - a bundled
   configuration skill that helps tune models, prompts, custom agents, MCP access,
   presets, and plugin behavior safely.
 
@@ -454,7 +466,7 @@ rules.
   </tr>
   <tr>
     <td colspan="2">
-      <b>Default Setup:</b> <code>Config-driven</code>  councillors come from <code>council.presets</code> and the Council agent model comes from your normal <code>council</code> agent config
+      <b>Default Setup:</b> <code>Config-driven</code> - councillors come from <code>council.presets</code> and the Council agent model comes from your normal <code>council</code> agent config
     </td>
   </tr>
   <tr>
@@ -609,11 +621,11 @@ rules.
     </td>
     <td width="70%" valign="top">
 
-**Read-only visual analysis**  interprets images, screenshots, PDFs, and diagrams. Returns structured observations to the orchestrator without loading raw file bytes into the main context window.
+**Read-only visual analysis** - interprets images, screenshots, PDFs, and diagrams. Returns structured observations to the orchestrator without loading raw file bytes into the main context window.
 
 - Images, screenshots, diagrams → `read` tool (native image support)
 - PDFs and binary documents → `read` tool (text + structure extraction)
-- **Disabled by default**  enable with `"disabled_agents": []` and configure a vision-capable model; installing with `--preset=opencode-go` enables it with `opencode-go/kimi-k2.6`
+- **Disabled by default** - enable with `"disabled_agents": []` and configure a vision-capable model; installing with `--preset=opencode-go` enables it with `opencode-go/kimi-k2.6`
 
     </td>
   </tr>
@@ -624,7 +636,7 @@ rules.
   </tr>
   <tr>
     <td colspan="2">
-      <b>Default Model:</b> <code>openai/gpt-5.4-mini</code>  <i>configure a vision-capable model to enable</i>
+      <b>Default Model:</b> <code>openai/gpt-5.4-mini</code> - <i>configure a vision-capable model to enable</i>
     </td>
   </tr>
   <tr>

+ 41 - 16
README.zh-CN.md

@@ -83,10 +83,35 @@ Install and configure oh-my-opencode-slim: https://raw.githubusercontent.com/alv
 bunx oh-my-opencode-slim@latest install
 ```
 
+### 从 Master 分支运行
+
+如果您想使用最新代码、方便修复问题,或进行本地开发和贡献,可以使用这种方式:
+
+```bash
+git clone https://github.com/alvinunreal/oh-my-opencode-slim.git ~/repos/oh-my-opencode-slim
+cd ~/repos/oh-my-opencode-slim
+bun install
+bun run build
+bun dist/cli/index.js install
+```
+
+安装程序会把本地仓库路径加入 `~/.config/opencode/opencode.json` 的
+`plugin` 数组,因此 OpenCode 会从该文件夹加载插件。之后要更新:
+
+```bash
+cd ~/repos/oh-my-opencode-slim
+git pull
+bun install
+bun run build
+```
+
 ### 入门指南
 
 安装程序会同时生成 OpenAI 和 OpenCode Go 预设,默认启用 OpenAI。
 
+> [!TIP]
+> 根据自己的工作流自由微调模型和智能体。默认预设只是起点;本插件的目标是为用户提供深度灵活性和可定制性。
+
 要在安装期间启用 OpenCode Go,请运行 `bunx oh-my-opencode-slim@latest install --preset=opencode-go`,或在安装后修改 `~/.config/opencode/oh-my-opencode-slim.json` 中的默认预设名称。
 
 然后:
@@ -130,7 +155,7 @@ bunx oh-my-opencode-slim@latest install
       "orchestrator": { "model": "opencode-go/glm-5.2", "skills": [ "*" ], "mcps": [ "*", "!context7" ] },
       "oracle": { "model": "opencode-go/qwen3.7-max", "variant": "max", "skills": ["simplify"], "mcps": [] },
       "librarian": { "model": "opencode-go/deepseek-v4-flash", "skills": [], "mcps": [ "websearch", "context7", "gh_grep" ] },
-      "explorer": { "model": "opencode-go/minimax-m2.7", "skills": [], "mcps": [] },
+      "explorer": { "model": "opencode-go/deepseek-v4-flash", "skills": [], "mcps": [] },
       "designer": { "model": "opencode-go/kimi-k2.7-code", "variant": "medium", "skills": [], "mcps": [] },
       "fixer": { "model": "opencode-go/deepseek-v4-flash", "variant": "high", "skills": [], "mcps": [] }
     }
@@ -169,12 +194,12 @@ ping all agents
 
 V2 将 oh-my-opencode-slim 变成了以调度器为核心的多智能体工作流系统。Orchestrator 专注于规划、委派、结果整合与验证,而专家智能体在各自的工作通道中完成任务。
 
-- **[后台智能体](#后台智能体)**  Orchestrator 现在会把专家作为后台任务派发,跟踪任务/会话 ID,等待完成事件,并在继续之前整合结果。
-- **[Companion](#companion)**  可选的浮动桌面窗口会显示当前活跃的智能体,包括并行运行的后台专家。
-- **[Deepwork](#deepwork)**  面向大型、多文件、高风险或分阶段编码工作的结构化工作流,使用持久化计划文件和 Oracle 评审关卡。
-- **[Reflect](#reflect)**  回顾重复出现的工作模式,并建议可复用的 skill、智能体、命令、配置规则、提示词规则或项目 playbook。
-- **[Worktrees](#worktrees)**  将 Git worktree 作为隔离编码通道管理,并为复杂、高风险或并行任务提供安全协议。
-- **[oh-my-opencode-slim Skill](#oh-my-opencode-slim-skill)**  随包提供的配置技能,可安全调优模型、提示词、自定义智能体、MCP 访问、预设和插件行为。
+- **[后台智能体](#后台智能体)** - Orchestrator 现在会把专家作为后台任务派发,跟踪任务/会话 ID,等待完成事件,并在继续之前整合结果。
+- **[Companion](#companion)** - 可选的浮动桌面窗口会显示当前活跃的智能体,包括并行运行的后台专家。
+- **[Deepwork](#deepwork)** - 面向大型、多文件、高风险或分阶段编码工作的结构化工作流,使用持久化计划文件和 Oracle 评审关卡。
+- **[Reflect](#reflect)** - 回顾重复出现的工作模式,并建议可复用的 skill、智能体、命令、配置规则、提示词规则或项目 playbook。
+- **[Worktrees](#worktrees)** - 将 Git worktree 作为隔离编码通道管理,并为复杂、高风险或并行任务提供安全协议。
+- **[oh-my-opencode-slim Skill](#oh-my-opencode-slim-skill)** - 随包提供的配置技能,可安全调优模型、提示词、自定义智能体、MCP 访问、预设和插件行为。
 
 #### 后台智能体
 
@@ -263,7 +288,7 @@ Worktrees 将 Git worktree 作为安全、隔离的编码通道管理,默认
       <br><sub><i>在复杂性的深渊中锻造而成。</i></sub>
     </td>
     <td width="70%" valign="top">
-      当第一个代码库在自身的复杂性下崩溃时,Orchestrator 诞生了。神明与凡人都无法承担责任——因此 Orchestrator 从虚无中显现,从混沌中建立秩序。它确定实现任何目标的最优路径,平衡速度、质量和成本。它引导整个团队,为每项任务召唤合适的专家,并通过委派任务以获得最佳成果。
+      当第一个代码库在自身的复杂性下崩溃时,Orchestrator 诞生了。神明与凡人都无法承担责任--因此 Orchestrator 从虚无中显现,从混沌中建立秩序。它确定实现任何目标的最优路径,平衡速度、质量和成本。它引导整个团队,为每项任务召唤合适的专家,并通过委派任务以获得最佳成果。
     </td>
   </tr>
   <tr>
@@ -345,7 +370,7 @@ Worktrees 将 Git worktree 作为安全、隔离的编码通道管理,默认
       <br><sub><i>十字路口的声音。</i></sub>
     </td>
     <td width="70%" valign="top">
-      Oracle 伫立在每个架构决策的十字路口。它走过每一条路,见过每一个终点,了解前方潜伏的所有陷阱。当您站在重大重构的悬崖边时,它是向您耳语哪条路通往毁灭、哪条路通往荣耀的声音。它不会替您做选择——但它会照亮道路,让您明智地抉择。
+      Oracle 伫立在每个架构决策的十字路口。它走过每一条路,见过每一个终点,了解前方潜伏的所有陷阱。当您站在重大重构的悬崖边时,它是向您耳语哪条路通往毁灭、哪条路通往荣耀的声音。它不会替您做选择--但它会照亮道路,让您明智地抉择。
     </td>
   </tr>
   <tr>
@@ -409,7 +434,7 @@ Worktrees 将 Git worktree 作为安全、隔离的编码通道管理,默认
   </tr>
   <tr>
     <td colspan="2">
-      <b>默认设置:</b> <code>配置驱动</code>  议员(councillors)来自 <code>council.presets</code>,而 Council 智能体本身的模型来自您的常规 <code>council</code> 智能体配置。
+      <b>默认设置:</b> <code>配置驱动</code> - 议员(councillors)来自 <code>council.presets</code>,而 Council 智能体本身的模型来自您的常规 <code>council</code> 智能体配置。
     </td>
   </tr>
   <tr>
@@ -435,7 +460,7 @@ Worktrees 将 Git worktree 作为安全、隔离的编码通道管理,默认
       <br><sub><i>理解的编织者。</i></sub>
     </td>
     <td width="70%" valign="top">
-      当人类意识到没有任何单一思想能容纳所有知识时,Librarian 诞生了。它是一位编织者,将零散的信息线索连接成一幅理解的织锦。它穿梭于无限的人类知识图书馆中,从各个角落收集洞察,并将它们绑定为超越单纯事实的答案。它所返回的不是碎片信息——而是深层的理解。
+      当人类意识到没有任何单一思想能容纳所有知识时,Librarian 诞生了。它是一位编织者,将零散的信息线索连接成一幅理解的织锦。它穿梭于无限的人类知识图书馆中,从各个角落收集洞察,并将它们绑定为超越单纯事实的答案。它所返回的不是碎片信息--而是深层的理解。
     </td>
   </tr>
   <tr>
@@ -476,7 +501,7 @@ Worktrees 将 Git worktree 作为安全、隔离的编码通道管理,默认
       <br><sub><i>美是不可或缺的。</i></sub>
     </td>
     <td width="70%" valign="top">
-      在这个经常遗忘美学价值的世界里,Designer 是美的不朽守护者。它见证了数以百万计的界面兴衰更替,它记得哪些被铭记,哪些被遗忘。它背负着神圣的使命,确保每一个像素都有其用途,每一个动画都在讲述故事,每一次交互都令人愉悦。美不是可选的——而是不可或缺的。
+      在这个经常遗忘美学价值的世界里,Designer 是美的不朽守护者。它见证了数以百万计的界面兴衰更替,它记得哪些被铭记,哪些被遗忘。它背负着神圣的使命,确保每一个像素都有其用途,每一个动画都在讲述故事,每一次交互都令人愉悦。美不是可选的--而是不可或缺的。
     </td>
   </tr>
   <tr>
@@ -517,7 +542,7 @@ Worktrees 将 Git worktree 作为安全、隔离的编码通道管理,默认
       <br><sub><i>愿景与现实之间的最后一步。</i></sub>
     </td>
     <td width="70%" valign="top">
-      Fixer 是曾经构建数字世界基石的建造者血脉的最后传人。当规划和辩论的时代开启时,它们依然坚守——它们是真正动手建造的人。它们掌握着如何将想法转化为实物、如何将规范转化为具体实现的古老知识。它们是愿景与现实之间的最后一步。
+      Fixer 是曾经构建数字世界基石的建造者血脉的最后传人。当规划和辩论的时代开启时,它们依然坚守--它们是真正动手建造的人。它们掌握着如何将想法转化为实物、如何将规范转化为具体实现的古老知识。它们是愿景与现实之间的最后一步。
     </td>
   </tr>
   <tr>
@@ -564,11 +589,11 @@ Worktrees 将 Git worktree 作为安全、隔离的编码通道管理,默认
     </td>
     <td width="70%" valign="top">
 
-**只读视觉分析** —— 解读图像、屏幕截图、PDF 和图表。将结构化的观察结果返回给 Orchestrator,而无需将原始文件字节加载到主上下文窗口中。
+**只读视觉分析** -- 解读图像、屏幕截图、PDF 和图表。将结构化的观察结果返回给 Orchestrator,而无需将原始文件字节加载到主上下文窗口中。
 
 - 图像、屏幕截图、图表 → `read` 工具(原生图像支持)
 - PDF 和二进制文档 → `read` 工具(文本 + 结构提取)
-- **默认禁用** —— 通过设置 `"disabled_agents": []` 和配置具有视觉能力的模型来启用;若使用 `--preset=opencode-go` 预设安装,将自动使用 `opencode-go/kimi-k2.6` 启用它。
+- **默认禁用** -- 通过设置 `"disabled_agents": []` 和配置具有视觉能力的模型来启用;若使用 `--preset=opencode-go` 预设安装,将自动使用 `opencode-go/kimi-k2.6` 启用它。
 
     </td>
   </tr>
@@ -579,7 +604,7 @@ Worktrees 将 Git worktree 作为安全、隔离的编码通道管理,默认
   </tr>
   <tr>
     <td colspan="2">
-      <b>默认模型:</b> <code>openai/gpt-5.4-mini</code>  <i>需配置具有视觉能力的模型以启用</i>
+      <b>默认模型:</b> <code>openai/gpt-5.4-mini</code> - <i>需配置具有视觉能力的模型以启用</i>
     </td>
   </tr>
   <tr>

+ 1 - 1
companion/Cargo.toml

@@ -2,7 +2,7 @@
 name = "oh-my-opencode-slim-companion"
 version = "0.1.3"
 edition = "2021"
-description = "Desktop companion for oh-my-opencode-slim  shows active agent GIFs per session"
+description = "Desktop companion for oh-my-opencode-slim - shows active agent GIFs per session"
 
 [[bin]]
 name = "oh-my-opencode-slim-companion"

+ 1 - 1
docs/acp-agents.md

@@ -58,7 +58,7 @@ Or let the orchestrator delegate to it when its routing prompt matches the task.
 
 | Option | Type | Default | Description |
 |--------|------|---------|-------------|
-| `command` | string |  | ACP executable. Put flags in `args`, not here. |
+| `command` | string | - | ACP executable. Put flags in `args`, not here. |
 | `args` | string[] | `[]` | Arguments for the ACP command. |
 | `env` | object | `{}` | Extra environment variables for the subprocess. |
 | `cwd` | string | current session directory | Working directory override. ACP paths should be absolute. |

+ 3 - 3
docs/adr/001-session-reflection-mode.md

@@ -23,7 +23,7 @@ Current `/reflect` only looks at the current conversation and project files. The
 **Rationale:**
 - Consistent with how `/reflect` currently works (prompt-based guidance)
 - The LLM already has tools (Read, Write, Bash) to do everything needed
-- Smallest useful form  no code changes required
+- Smallest useful form - no code changes required
 - YAGNI: Start simple, add code if prompt-only proves insufficient
 
 **Alternatives considered:**
@@ -129,7 +129,7 @@ OpenCode logs
 **Decision:** LLM manages its own cache using Read/Write tools.
 
 **Rationale:**
-- No new code needed  LLM already has file tools
+- No new code needed - LLM already has file tools
 - Avoids re-analyzing expensive sessions
 - Enables incremental updates (only analyze new sessions)
 
@@ -182,7 +182,7 @@ OpenCode logs
 
 ## Consequences
 
-- No code changes needed  purely skill instruction updates
+- No code changes needed - purely skill instruction updates
 - All OMOS persistent data lives in one directory tree
 - Reflections are available across all projects (global)
 - LLM manages file I/O, cache, and aggregation

+ 1 - 1
docs/clonedeps.md

@@ -117,7 +117,7 @@ Example:
 Read-only dependency source repositories are available under
 `.slim/clonedeps/repos/` for inspection. Do not edit these clones.
 
-- `.slim/clonedeps/repos/opencode-ai__opencode/`  `opencode-ai/opencode` at
+- `.slim/clonedeps/repos/opencode-ai__opencode/` - `opencode-ai/opencode` at
   `v1.3.17`; inspect `packages/sdk/js` for OpenCode SDK internals.
 ```
 

+ 3 - 3
docs/companion.md

@@ -35,12 +35,12 @@ You can enable the companion by adding a `companion` section to your setting con
   - `large` (160px)
 
 - **`companion.gifPack`**:
-  - `default` (default)  the bundled companion animation set generated from
+  - `default` (default) - the bundled companion animation set generated from
     the MP4 sources in `companion/VIDEOS/`.
 
 - **`companion.loopStyle`**:
-  - `classic` (default)  forward playback that loops back to the first frame.
-  - `smooth`  ping-pong playback that reverses direction at the end for a
+  - `classic` (default) - forward playback that loops back to the first frame.
+  - `smooth` - ping-pong playback that reverses direction at the end for a
     smoother transition.
 
 - **`companion.speed`**: optional animation playback speed multiplier from `0.25` to

+ 22 - 22
docs/configuration.md

@@ -9,7 +9,7 @@ Complete reference for all configuration files and options in oh-my-opencode-sli
 | File | Purpose |
 |------|---------|
 | `~/.config/opencode/opencode.json` | OpenCode core settings (plugin registration, providers) |
-| `~/.config/opencode/oh-my-opencode-slim.json` | Plugin settings  agents, multiplexer, MCPs, council |
+| `~/.config/opencode/oh-my-opencode-slim.json` | Plugin settings - agents, multiplexer, MCPs, council |
 | `~/.config/opencode/oh-my-opencode-slim.jsonc` | Same, but with JSONC (comments + trailing commas). Takes precedence over `.json` if both exist |
 | `.opencode/oh-my-opencode-slim.json` | Project-local overrides (optional, higher precedence than user config) |
 
@@ -54,7 +54,7 @@ When a `preset` is active, the plugin checks preset directories before falling b
   └── ...
 ```
 
-Both `{agent}.md` and `{agent}_append.md` can coexist  the full replacement takes effect first, then the append. If neither exists, the built-in default prompt is used.
+Both `{agent}.md` and `{agent}_append.md` can coexist - the full replacement takes effect first, then the append. If neither exists, the built-in default prompt is used.
 
 ---
 
@@ -94,30 +94,30 @@ All config files support **JSONC** (JSON with Comments):
 
 | Option | Type | Default | Description |
 |--------|------|---------|-------------|
-| `preset` | string |  | Active preset name (e.g. `"openai"`, `"best"`) |
+| `preset` | string | - | Active preset name (e.g. `"openai"`, `"best"`) |
 
 ### Runtime Preset Switching
 
 Presets can also be switched at runtime without restarting using the `/preset` command. See [Preset Switching](preset-switching.md) for details.
 
-| `presets` | object |  | Named preset configurations |
+| `presets` | object | - | Named preset configurations |
 |-----------|--------|---|-----------------------------|
-| `presets.<name>.<agent>.model` | string |  | Model ID in `provider/model` format |
-| `presets.<name>.<agent>.temperature` | number |  | Temperature (0–2) |
-| `presets.<name>.<agent>.variant` | string |  | Reasoning effort: `"low"`, `"medium"`, `"high"` |
-| `presets.<name>.<agent>.displayName` | string |  | Custom user-facing alias for the agent (e.g. `"advisor"` for `oracle`) |
-| `presets.<name>.<agent>.skills` | string[] |  | Skills the agent can use (`"*"`, `"!item"`, explicit list) |
-| `presets.<name>.<agent>.mcps` | string[] |  | MCPs the agent can use (`"*"`, `"!item"`, explicit list) |
-| `presets.<name>.<agent>.options` | object |  | Provider-specific model options passed to the AI SDK (e.g., `textVerbosity`, `thinking` budget) |
-| `agents.<customAgent>.model` | string\|array |  | Required for custom agents inferred from unknown `agents` keys |
-| `agents.<customAgent>.prompt` | string |  | Full execution prompt for a custom agent |
-| `agents.<customAgent>.orchestratorPrompt` | string |  | Exact `@agent` block injected into the orchestrator prompt; must start with `@<agent-name>` |
-| `agents.<agent>.displayName` | string |  | Custom user-facing alias for the agent in the active config |
-| `acpAgents.<name>.command` | string |  | Command for an external ACP-compatible agent; creates a wrapper subagent named `<name>` |
+| `presets.<name>.<agent>.model` | string | - | Model ID in `provider/model` format |
+| `presets.<name>.<agent>.temperature` | number | - | Temperature (0–2) |
+| `presets.<name>.<agent>.variant` | string | - | Reasoning effort: `"low"`, `"medium"`, `"high"` |
+| `presets.<name>.<agent>.displayName` | string | - | Custom user-facing alias for the agent (e.g. `"advisor"` for `oracle`) |
+| `presets.<name>.<agent>.skills` | string[] | - | Skills the agent can use (`"*"`, `"!item"`, explicit list) |
+| `presets.<name>.<agent>.mcps` | string[] | - | MCPs the agent can use (`"*"`, `"!item"`, explicit list) |
+| `presets.<name>.<agent>.options` | object | - | Provider-specific model options passed to the AI SDK (e.g., `textVerbosity`, `thinking` budget) |
+| `agents.<customAgent>.model` | string\|array | - | Required for custom agents inferred from unknown `agents` keys |
+| `agents.<customAgent>.prompt` | string | - | Full execution prompt for a custom agent |
+| `agents.<customAgent>.orchestratorPrompt` | string | - | Exact `@agent` block injected into the orchestrator prompt; must start with `@<agent-name>` |
+| `agents.<agent>.displayName` | string | - | Custom user-facing alias for the agent in the active config |
+| `acpAgents.<name>.command` | string | - | Command for an external ACP-compatible agent; creates a wrapper subagent named `<name>` |
 | `acpAgents.<name>.args` | string[] | `[]` | Arguments for the ACP agent command |
 | `acpAgents.<name>.env` | object | `{}` | Extra environment variables for the ACP subprocess |
 | `acpAgents.<name>.cwd` | string | session directory | Working directory override for this ACP subprocess; protocol paths should be absolute |
-| `acpAgents.<name>.description` | string |  | Description shown to OpenCode and injected into the orchestrator routing prompt |
+| `acpAgents.<name>.description` | string | - | Description shown to OpenCode and injected into the orchestrator routing prompt |
 | `acpAgents.<name>.prompt` | string | generated wrapper prompt | Optional full prompt for the lightweight wrapper subagent |
 | `acpAgents.<name>.orchestratorPrompt` | string | generated routing block | Optional exact routing block injected into the orchestrator prompt |
 | `acpAgents.<name>.wrapperModel` | string | fixer default | Cheap OpenCode model used by the wrapper subagent that calls `acp_run` |
@@ -140,10 +140,10 @@ Presets can also be switched at runtime without restarting using the `/preset` c
 | `fallback.timeoutMs` | number | `15000` | Time before aborting and trying next model |
 | `fallback.retryDelayMs` | number | `500` | Delay between retry attempts |
 | `fallback.retry_on_empty` | boolean | `true` | Treat silent empty provider responses (0 tokens) as failures and retry. Set `false` to accept empty responses |
-| `council.presets` | object |  | **Required if using council.** Named councillor presets |
-| `council.presets.<name>.<councillor>.model` | string |  | Councillor model |
-| `council.presets.<name>.<councillor>.variant` | string |  | Councillor variant |
-| `council.presets.<name>.<councillor>.prompt` | string |  | Optional role guidance for the councillor |
+| `council.presets` | object | - | **Required if using council.** Named councillor presets |
+| `council.presets.<name>.<councillor>.model` | string | - | Councillor model |
+| `council.presets.<name>.<councillor>.variant` | string | - | Councillor variant |
+| `council.presets.<name>.<councillor>.prompt` | string | - | Optional role guidance for the councillor |
 | `council.default_preset` | string | `"default"` | Default preset when none is specified |
 | `council.timeout` | number | `180000` | Per-councillor timeout (ms) |
 | `council.councillor_execution_mode` | string | `"parallel"` | Run councillors in `parallel` or `serial`; use `serial` for single-model setups |
@@ -154,7 +154,7 @@ Presets can also be switched at runtime without restarting using the `/preset` c
 | `interview.port` | integer | `0` | Interview server port (0–65535). `0` = OS-assigned random port (per-session mode). Any value > 0 enables [dashboard mode](interview.md#dashboard-mode) |
 | `interview.dashboard` | boolean | `false` | Enable [dashboard mode](interview.md#dashboard-mode) on the default port (43211). Setting `port` > 0 also enables dashboard mode. If both are set, `port` takes precedence |
 | `companion.enabled` | boolean | `false` | Enable/disable the floating window Rust companion |
-| `companion.binaryPath` | string |  | Optional path to a custom companion binary to launch instead of the default install path |
+| `companion.binaryPath` | string | - | Optional path to a custom companion binary to launch instead of the default install path |
 | `companion.position` | string | `"bottom-right"` | The initial corner position of the companion window: `bottom-right`, `bottom-left`, `top-right`, or `top-left` |
 | `companion.size` | string | `"medium"` | The default size preset of the companion window: `small` (80px), `medium` (120px), or `large` (160px) |
 

+ 4 - 4
docs/council.md

@@ -128,7 +128,7 @@ Then use it directly:
 
 | Setting | Type | Default | Description |
 |---------|------|---------|-------------|
-| `presets` | object |  | **Required.** Named councillor presets |
+| `presets` | object | - | **Required.** Named councillor presets |
 | `default_preset` | string | `"default"` | Preset used when none is specified |
 | `timeout` | number | `180000` | Per-councillor timeout in ms |
 | `councillor_execution_mode` | string | `"parallel"` | `parallel` runs all councillors concurrently; `serial` runs them one at a time |
@@ -345,10 +345,10 @@ expensive path.
 
 Council responses include:
 
-1. `Council Response`  the synthesized final answer.
-2. `Councillor Details`  each responding councillor's individual response,
+1. `Council Response` - the synthesized final answer.
+2. `Councillor Details` - each responding councillor's individual response,
    using the councillor names from the configured preset.
-3. `Council Summary`  agreement, disagreement resolution, remaining
+3. `Council Summary` - agreement, disagreement resolution, remaining
    uncertainty, and a consensus confidence rating of `unanimous`, `majority`,
    or `split`.
 

+ 16 - 16
docs/interview.md

@@ -67,9 +67,9 @@ interview/kanban-design-tool.md
 
 The file contains three sections:
 
-- `Frontmatter`  session meta data for recovery
-- `Current spec`  11-section structured specification doc (Introduction, Purpose, Requirements, Data Contracts, Acceptance Criteria, etc.)
-- `Q&A history`  append-only question/answer record
+- `Frontmatter` - session meta data for recovery
+- `Current spec` - 11-section structured specification doc (Introduction, Purpose, Requirements, Data Contracts, Acceptance Criteria, etc.)
+- `Q&A history` - append-only question/answer record
 
 Example:
 
@@ -134,7 +134,7 @@ The interview module has two modes: **per-session** (default) and **dashboard**
 
 ### Per-session mode (default)
 
-When `port` is `0` (or unset) and `dashboard` is `false` (or unset), each OpenCode process runs its own interview server on a random port. This is the original behavior  no configuration needed.
+When `port` is `0` (or unset) and `dashboard` is `false` (or unset), each OpenCode process runs its own interview server on a random port. This is the original behavior - no configuration needed.
 
 ```jsonc
 {
@@ -187,12 +187,12 @@ When `dashboard` is `true` or `port` is set to a value greater than `0`, intervi
             │ POST state        │ GET pending answers
 ┌───────────┴────────┐ ┌───────┴──────────────┐
 │  Session Process A  │ │  Session Process B    │
-│  (smart — drives    │ │  (smart — drives      │
+│  (smart - drives    │ │  (smart - drives      │
 │   LLM locally)      │ │   LLM locally)        │
 └─────────────────────┘ └───────────────────────┘
 ```
 
-Sessions are smart  they drive LLM interaction locally (parse state, inject prompts, write `.md` files). The dashboard is a dumb aggregator with a web UI. This means zero cross-process SDK dependency.
+Sessions are smart - they drive LLM interaction locally (parse state, inject prompts, write `.md` files). The dashboard is a dumb aggregator with a web UI. This means zero cross-process SDK dependency.
 
 #### Auto-failover
 
@@ -204,7 +204,7 @@ Any OpenCode process can become the dashboard. The first process to bind the con
 
 #### Session registration
 
-Sessions register their project directory with the dashboard so it knows where to scan for interview files. This happens automatically on first `/interview` command or session event  no manual setup needed.
+Sessions register their project directory with the dashboard so it knows where to scan for interview files. This happens automatically on first `/interview` command or session event - no manual setup needed.
 
 The dashboard also scans your home directory's output folder by default, so interviews created from a home-directory OpenCode session are always visible.
 
@@ -212,9 +212,9 @@ The dashboard also scans your home directory's output folder by default, so inte
 
 The dashboard page includes a settings panel for:
 
-- **Scan days**  how far back to look for sessions (default: 30)
-- **Add/remove folders**  manually add project directories to scan
-- **Discover sessions**  re-scan the OpenCode session list for new directories
+- **Scan days** - how far back to look for sessions (default: 30)
+- **Add/remove folders** - manually add project directories to scan
+- **Discover sessions** - re-scan the OpenCode session list for new directories
 
 ## Configuration
 
@@ -234,17 +234,17 @@ The dashboard page includes a settings panel for:
 
 ### Options
 
-- `maxQuestions`  max questions per round, `1-10`, default `2`
-- `outputFolder`  where markdown files are written, default `interview`
-- `autoOpenBrowser`  open the localhost UI in your default browser during interactive runs, default `true` (suppressed automatically in tests and CI)
-- `port`  port for the interview server, `0-65535`, default `0` (OS-assigned in per-session mode). Set a fixed port to enable dashboard mode. Note: ports 1-1023 require elevated privileges on most systems.
-- `dashboard`  enable dashboard mode on the default port (`43211`), default `false`. Setting `port` to a value greater than `0` also enables dashboard mode. If both are set, `port` takes precedence.
+- `maxQuestions` - max questions per round, `1-10`, default `2`
+- `outputFolder` - where markdown files are written, default `interview`
+- `autoOpenBrowser` - open the localhost UI in your default browser during interactive runs, default `true` (suppressed automatically in tests and CI)
+- `port` - port for the interview server, `0-65535`, default `0` (OS-assigned in per-session mode). Set a fixed port to enable dashboard mode. Note: ports 1-1023 require elevated privileges on most systems.
+- `dashboard` - enable dashboard mode on the default port (`43211`), default `false`. Setting `port` to a value greater than `0` also enables dashboard mode. If both are set, `port` takes precedence.
 
 ### Mode selection
 
 | `port` | `dashboard` | Mode |
 |--------|-------------|------|
-| `0` (default) | `false` (default) | Per-session  each process runs its own server |
+| `0` (default) | `false` (default) | Per-session - each process runs its own server |
 | `0` | `true` | Dashboard on default port 43211 |
 | `> 0` | any | Dashboard on the specified port |
 

+ 6 - 6
docs/maintainers.md

@@ -58,12 +58,12 @@ Feature requests should stay lightweight and focus on:
 
 Only use these labels:
 
-- `bug`  bug report
-- `enhancement`  feature request or improvement
-- `needs-info`  cannot act yet because key details are missing
-- `confirmed`  a maintainer confirmed the issue or agrees the request is valid
-- `P0`  highest priority
-- `Share Your Thoughts`  open-ended feedback from the community
+- `bug` - bug report
+- `enhancement` - feature request or improvement
+- `needs-info` - cannot act yet because key details are missing
+- `confirmed` - a maintainer confirmed the issue or agrees the request is valid
+- `P0` - highest priority
+- `Share Your Thoughts` - open-ended feedback from the community
 
 If a label does not help triage or prioritization, do not add it.
 

+ 1 - 1
docs/mcps.md

@@ -1,6 +1,6 @@
 # MCP Servers
 
-Built-in Model Context Protocol (MCP) servers ship with oh-my-opencode-slim and give agents access to external tools  web search, library documentation, and code search.
+Built-in Model Context Protocol (MCP) servers ship with oh-my-opencode-slim and give agents access to external tools - web search, library documentation, and code search.
 
 ---
 

+ 2 - 2
docs/opencode-go-preset.md

@@ -51,7 +51,7 @@ role:
 | Orchestrator | `opencode-go/glm-5.2` |
 | Oracle | `opencode-go/qwen3.7-max` (`max`) |
 | Librarian | `opencode-go/deepseek-v4-flash` |
-| Explorer | `opencode-go/minimax-m2.7` |
+| Explorer | `opencode-go/deepseek-v4-flash` |
 | Designer | `opencode-go/kimi-k2.7-code` (`medium`) |
 | Fixer | `opencode-go/deepseek-v4-flash` (`high`) |
 | Observer | `opencode-go/kimi-k2.6` |
@@ -73,7 +73,7 @@ setting the top-level `preset` field:
         "variant": "max"
       },
       "librarian": { "model": "opencode-go/deepseek-v4-flash" },
-      "explorer": { "model": "opencode-go/minimax-m2.7" },
+      "explorer": { "model": "opencode-go/deepseek-v4-flash" },
       "designer": {
         "model": "opencode-go/kimi-k2.7-code",
         "variant": "medium"

+ 3 - 3
docs/quick-reference.md

@@ -23,8 +23,8 @@
 
 | Doc | Contents |
 |-----|----------|
-| [Skills](skills.md) | `simplify`, `codemap`, `clonedeps`  skills assignment syntax |
-| [MCPs](mcps.md) | `websearch`, `context7`, `gh_grep`  permissions per agent, global disable |
+| [Skills](skills.md) | `simplify`, `codemap`, `clonedeps` - skills assignment syntax |
+| [MCPs](mcps.md) | `websearch`, `context7`, `gh_grep` - permissions per agent, global disable |
 | [Tools](tools.md) | Background tasks, LSP, code search (`ast_grep`), formatters |
 | [Configuration](configuration.md) | Config files, prompt overriding, JSONC, full option reference table |
 
@@ -32,4 +32,4 @@
 
 | Doc | Contents |
 |-----|----------|
-| [Author's Preset](authors-preset.md) | The exact config the author runs daily  OpenAI + Fireworks AI + GitHub Copilot |
+| [Author's Preset](authors-preset.md) | The exact config the author runs daily - OpenAI + Fireworks AI + GitHub Copilot |

+ 6 - 6
docs/skills.md

@@ -1,6 +1,6 @@
 # Skills
 
-Skills are specialized capabilities you can assign to agents. Unlike MCPs (which are running servers), skills are **prompt-based tool configurations**  instructions injected into an agent's system prompt that describe how to use a particular tool.
+Skills are specialized capabilities you can assign to agents. Unlike MCPs (which are running servers), skills are **prompt-based tool configurations** - instructions injected into an agent's system prompt that describe how to use a particular tool.
 
 Bundled skills are installed by the `oh-my-opencode-slim` installer and safely
 reconciled on plugin startup/auto-update. Local customizations are preserved;
@@ -47,10 +47,10 @@ Source: adapted from Addy Osmani's `code-simplification` skill and bundled local
 **How to use:** Ask the Orchestrator to `run codemap`. It automatically detects whether to initialize a new map or update an existing one.
 
 **Why it's useful:**
-- **Instant onboarding**  understand unfamiliar codebases in seconds
-- **Efficient context**  agents read architectural summaries, saving tokens and improving accuracy
-- **Change detection**  only modified folders are re-analyzed
-- **Timeless documentation**  focuses on high-level design, not implementation details
+- **Instant onboarding** - understand unfamiliar codebases in seconds
+- **Efficient context** - agents read architectural summaries, saving tokens and improving accuracy
+- **Change detection** - only modified folders are re-analyzed
+- **Timeless documentation** - focuses on high-level design, not implementation details
 
 See **[Codemap Skill](codemap.md)** for full documentation including manual commands and technical details.
 
@@ -129,7 +129,7 @@ Start it directly with:
 `reflect` is an orchestrator-only workflow skill for reviewing recent work,
 finding repeated workflow friction, and recommending the smallest useful reusable
 asset. It may suggest a skill, custom agent, command, config rule, prompt rule,
-MCP permission change, or project playbook  but only when there is enough
+MCP permission change, or project playbook - but only when there is enough
 evidence.
 
 Use it directly with:

+ 115 - 115
docs/superpowers/plans/2026-06-25-loop-engineering-runtime.md

@@ -1,4 +1,4 @@
-# Loop Engineering  Implementation Plan (Corrected)
+# Loop Engineering - Implementation Plan (Corrected)
 
 ## Overview
 
@@ -10,7 +10,7 @@ Runtime-first design: the loop engine is orchestration wiring that composes exis
 The runtime decides: what state comes next, when verification occurs, whether success criteria passed, whether another iteration is allowed, when escalation policies apply. The LLM decides: how to solve the problem, how to adapt after feedback, what implementation strategy to try next.
 
 **Core design principle:**
-> **Verification is the center of loop engineering  not execution.**
+> **Verification is the center of loop engineering - not execution.**
 
 Retries, failures, warnings, error counts, timeouts are **escalation signals**, not the loop itself. The loop is `Goal → Execute → Verify → Goal satisfied?` Everything else hangs off that.
 
@@ -18,29 +18,29 @@ Retries, failures, warnings, error counts, timeouts are **escalation signals**,
 The engine implements `verify()`. It should NOT implement `retry twice then escalate`. Instead, policy (maxAttempts, escalation targets, human gates) is externalized. This keeps the runtime generic and extensible.
 
 **Architectural corrections applied:**
-- Task 2 removed  `BackgroundJobState` stays clean (no loop phases pollute job primitives)
+- Task 2 removed - `BackgroundJobState` stays clean (no loop phases pollute job primitives)
 - Event-driven model, not procedural `for` loop
-- Runtime is the constraint  no "signals not constraints" in Layer 1
-- Context compaction  engine synthesizes history before dispatching
-- Verification parsing fixed  JSON schema, not regex
-- BackgroundJobBoard event plumbing  callback array for multiple listeners (multiplexer + LoopEngine)
-- Binary oscillation `executing` ↔ `verifying`  no planning/improving phase
-- Dispatch failure handling  `try/catch` → `escalated` + system error
+- Runtime is the constraint - no "signals not constraints" in Layer 1
+- Context compaction - engine synthesizes history before dispatching
+- Verification parsing fixed - JSON schema, not regex
+- BackgroundJobBoard event plumbing - callback array for multiple listeners (multiplexer + LoopEngine)
+- Binary oscillation `executing` ↔ `verifying` - no planning/improving phase
+- Dispatch failure handling - `try/catch` → `escalated` + system error
 - Context injection via `.loop-history-{loopID}.md` file, not job description
-- Fleet mapping  executeAgent/verifyAgent expanded for all specialist roles
-- Oracle retry-wrapper  `oracleRetryCount` persisted in session
+- Fleet mapping - executeAgent/verifyAgent expanded for all specialist roles
+- Oracle retry-wrapper - `oracleRetryCount` persisted in session
 - Council restricted to Layer 0 escalation only
-- Cancellation lifecycle  `cancelled` is quiet terminal state, no `onEscalated`
-- Session cleanup  engine manages `.loop-history-{loopID}.md` only, orchestrator owns artifact lifecycle
-- Convergence signal scope  signals apply to `error` and `timeout` only, NOT `cancelled`
+- Cancellation lifecycle - `cancelled` is quiet terminal state, no `onEscalated`
+- Session cleanup - engine manages `.loop-history-{loopID}.md` only, orchestrator owns artifact lifecycle
+- Convergence signal scope - signals apply to `error` and `timeout` only, NOT `cancelled`
 - `totalErrors` (not `errorCount`) consistently used
-- `SuccessCriterion` as first-class type  engine routes by `success.type`
-- Deferred worktree/memory/trigger from core interfaces  Future Extensions section
-- Artifact lifecycle  engine signals `onArtifactWrite`, orchestrator owns filesystem
-- **Dispatch callback**  engine receives `dispatch(agent, prompt, contextFiles)` from orchestrator, no direct SDK access
-- **Manual verification**  no BackgroundJob created, engine manages waiting state in LoopSession
-- **Automated verification**  test/build/lint/command/fileExists dispatched to test-runner agent, not spawnSync
-- **LoopEngine location**  `src/loop/loop-engine.ts` (not src/council/)
+- `SuccessCriterion` as first-class type - engine routes by `success.type`
+- Deferred worktree/memory/trigger from core interfaces - Future Extensions section
+- Artifact lifecycle - engine signals `onArtifactWrite`, orchestrator owns filesystem
+- **Dispatch callback** - engine receives `dispatch(agent, prompt, contextFiles)` from orchestrator, no direct SDK access
+- **Manual verification** - no BackgroundJob created, engine manages waiting state in LoopSession
+- **Automated verification** - test/build/lint/command/fileExists dispatched to test-runner agent, not spawnSync
+- **LoopEngine location** - `src/loop/loop-engine.ts` (not src/council/)
 
 **Phased roadmap:**
 - Phase 1: Runtime loop engine (this PR)
@@ -58,11 +58,11 @@ The engine implements `verify()`. It should NOT implement `retry twice then esca
 **File:** `src/utils/background-job-board.ts`
 
 Add three fields to `BackgroundJobRecord`:
-- `totalErrors: number`  accumulated errors across all attempts (not incremented on `cancelled`)
-- `timeoutCount: number`  consecutive timeouts, resets to 0 on `completed`
-- `lastErrorAt?: number`  timestamp of last error
+- `totalErrors: number` - accumulated errors across all attempts (not incremented on `cancelled`)
+- `timeoutCount: number` - consecutive timeouts, resets to 0 on `completed`
+- `lastErrorAt?: number` - timestamp of last error
 
-**Convergence signal scope:** Signals (`totalErrors`, `timeoutCount`) apply to `error` and `timeout` states only. The `cancelled` state is a quiet terminal state  it does NOT increment error counters. This prevents noisy escalation when users intentionally cancel.
+**Convergence signal scope:** Signals (`totalErrors`, `timeoutCount`) apply to `error` and `timeout` states only. The `cancelled` state is a quiet terminal state - it does NOT increment error counters. This prevents noisy escalation when users intentionally cancel.
 
 **Signal computation:** Convergence signals are computed from `BackgroundJobRecord` state transitions in `updateStatus()`, not explicit flags:
 - When `input.state === 'error'` → increment `totalErrors`, set `lastErrorAt = Date.now()`
@@ -70,11 +70,11 @@ Add three fields to `BackgroundJobRecord`:
 - When `input.state === 'completed'` → reset `timeoutCount = 0`
 - `cancelled` state → no increments
 
-This avoids redundant `isError`/`isTimeout` fields on `BackgroundJobStatusInput`  the state machine already conveys this information.
+This avoids redundant `isError`/`isTimeout` fields on `BackgroundJobStatusInput` - the state machine already conveys this information.
 
 Update:
-- `registerLaunch()`  initialize `totalErrors = 0`, `timeoutCount = 0`
-- `updateStatus()`  compute signals from state transitions as above
+- `registerLaunch()` - initialize `totalErrors = 0`, `timeoutCount = 0`
+- `updateStatus()` - compute signals from state transitions as above
 
 ### Task 2: Add Convergence Helper Methods to BackgroundJobBoard
 
@@ -138,10 +138,10 @@ export type LoopPhase =
 // Fleet mapping: executeAgent is dynamically selected based on task domain
 export type ExecuteAgent = 'fixer' | 'designer' | 'explorer' | 'librarian';
 // Fleet mapping: verifyAgent is dynamically selected based on task domain
-// Note: 'council' is NOT a verifyAgent inside the loop  it is Layer 0 escalation only
+// Note: 'council' is NOT a verifyAgent inside the loop - it is Layer 0 escalation only
 export type VerifyAgent = 'oracle' | 'observer' | 'test';
 
-// Success criteria  first-class runtime type
+// Success criteria - first-class runtime type
 // The runtime evaluates these directly where possible. Only subjective criteria go to Oracle.
 export type SuccessCriterion =
   | { type: 'test'; command: string }                         // exit code 0 = pass
@@ -166,7 +166,7 @@ export interface LoopDefinition {
   contextFiles?: string[];
 }
 
-// Deferred interfaces (NOT in LoopDefinition  added later via extension)
+// Deferred interfaces (NOT in LoopDefinition - added later via extension)
 // See "Future Extensions" section below for: LoopTrigger, LoopWorktreeConfig, LoopMemoryConfig
 
 export interface AttemptRecord {
@@ -206,7 +206,7 @@ escalated  → (terminal)
 cancelled  → (terminal)
 ```
 
-**No `planning` or `improving` phase**  binary oscillation between `executing` and `verifying`. `@oracle` only verifies, `@fixer` self-corrects using `.loop-history-{loopID}.md`. Loop starts in `executing`.
+**No `planning` or `improving` phase** - binary oscillation between `executing` and `verifying`. `@oracle` only verifies, `@fixer` self-corrects using `.loop-history-{loopID}.md`. Loop starts in `executing`.
 
 **`oracleRetryCount` lifecycle:** Reset to `0` on every `executing` transition. Increment on each Oracle retry. If `oracleRetryCount >= 2` and parsing still fails → fail closed (verification = failed).
 
@@ -214,7 +214,7 @@ cancelled  → (terminal)
 
 **Worktree integration:** If `definition.worktree?.enabled = true`, orchestrator creates a dedicated worktree before dispatching. Engine tracks `session.worktreeName`. On `done` → orchestrator merges worktree to main. On `escalated`/`cancelled` → orchestrator abandons worktree. Prevents parallel loops from colliding on the same files. Uses existing `using-git-worktrees` skill via orchestrator.
 
-### Task 5: Worktree Integration (Deferred  MVP uses in-process execution)
+### Task 5: Worktree Integration (Deferred - MVP uses in-process execution)
 
 **Files:** `src/loop/loop-engine.ts` (update), `src/loop/worktree-manager.ts` (new)
 
@@ -252,13 +252,13 @@ Engine dispatches first job (checks session.worktreeReady before dispatching)
 - `done` → engine fires `onWorktreeMerge(loopID, branchName)`. Orchestrator merges to main via skill.
 - `escalated`/`cancelled` → engine fires `onWorktreeAbandon(loopID, branchName)`. Orchestrator abandons via skill.
 
-**Engine does not call git directly**  it delegates to orchestrator via callbacks (`onWorktreeCreate`, `onWorktreeMerge`, `onWorktreeAbandon`).
+**Engine does not call git directly** - it delegates to orchestrator via callbacks (`onWorktreeCreate`, `onWorktreeMerge`, `onWorktreeAbandon`).
 
 **Validation:** `startLoop()` validates that `executeAgent !== verifyAgent`. If equal, throws `Error('executeAgent and verifyAgent must be different')`.
 
 **Note:** In MVP, `worktree.enabled = false` by default. Worktree isolation is opt-in per `LoopDefinition`.
 
-### Task 6: Cross-Loop Memory (Deferred  MVP uses per-session history only)
+### Task 6: Cross-Loop Memory (Deferred - MVP uses per-session history only)
 
 **File:** `src/loop/loop-memory.ts` (new file)
 
@@ -294,7 +294,7 @@ on 'escalated':
   → orchestrator writes file via fs
 ```
 
-**Orchestrator does the actual file I/O**  engine delegates via callback, same pattern as worktree. This keeps the engine purely orchestration logic.
+**Orchestrator does the actual file I/O** - engine delegates via callback, same pattern as worktree. This keeps the engine purely orchestration logic.
 
 **Future:** Memory store could be GitHub Issues (label-based), a database, or a dedicated file. File-based (`.loop-memory.md`) is MVP.
 
@@ -313,9 +313,9 @@ import { BackgroundJobBoard, type BackgroundJobRecord } from '../utils/backgroun
 export interface LoopEngineCallbacks {
   onLoopComplete?: (loopID: string, success: boolean) => void;
   onEscalated?: (loopID: string, reason: string) => void;
-  // Manual verification  orchestrator surfaces review to human, calls resolveManualReview
+  // Manual verification - orchestrator surfaces review to human, calls resolveManualReview
   onManualReview?: (loopID: string, reason: string) => void;
-  // Artifact management  orchestrator owns filesystem, engine only signals
+  // Artifact management - orchestrator owns filesystem, engine only signals
   onArtifactWrite?: (loopID: string, artifactPath: string) => void;
   // Deferred: onWorktreeCreate, onWorktreeMerge, onWorktreeAbandon
   // Deferred: onMemoryRead, onMemoryWrite
@@ -325,7 +325,7 @@ export class LoopEngine {
   private sessions: Map<string, LoopSession> = new Map();
   private jobBoard: BackgroundJobBoard;
   private callbacks: LoopEngineCallbacks;
-  // Dispatch callback provided by orchestrator  engine does not access SDK directly
+  // Dispatch callback provided by orchestrator - engine does not access SDK directly
   private dispatch: (agent: string, prompt: string, contextFiles: string[]) => string;
 
   constructor(jobBoard: BackgroundJobBoard, callbacks: LoopEngineCallbacks, dispatch: (agent: string, prompt: string, contextFiles: string[]) => string);
@@ -350,18 +350,18 @@ export class LoopEngine {
 **Layered architecture:**
 
 ```
-Layer 0: Orchestrator  loads skill, delegates to LoopEngine, listens to callbacks, handles Grill + escalation
-Layer 1: LoopEngine  event-driven state machine, dispatches agents, manages artifacts, enforces circuit breaker
-Layer 2: Specialist agents  do the work
-  - @fixer, @designer, @explorer, @librarian  execute based on task domain
-  - @oracle, @observer, test  verify based on task domain
-  - @council  Layer 0 escalation ONLY, never inside the loop
-Skill  instructs orchestrator, never "does" anything itself
+Layer 0: Orchestrator - loads skill, delegates to LoopEngine, listens to callbacks, handles Grill + escalation
+Layer 1: LoopEngine - event-driven state machine, dispatches agents, manages artifacts, enforces circuit breaker
+Layer 2: Specialist agents - do the work
+  - @fixer, @designer, @explorer, @librarian - execute based on task domain
+  - @oracle, @observer, test - verify based on task domain
+  - @council - Layer 0 escalation ONLY, never inside the loop
+Skill - instructs orchestrator, never "does" anything itself
 ```
 
 **Key design:**
 
-1. `startLoop(definition)` is **non-blocking**  creates session, validates inputs, writes history file, dispatches first job, returns `loopID` immediately. Orchestrator never hangs.
+1. `startLoop(definition)` is **non-blocking** - creates session, validates inputs, writes history file, dispatches first job, returns `loopID` immediately. Orchestrator never hangs.
 
    **Validation:**
    ```typescript
@@ -372,15 +372,15 @@ Skill — instructs orchestrator, never "does" anything itself
    Prevents a single agent from verifying its own output (e.g., fixer checking fixer). The "student marking their own exam" problem is solved by design for code loops, but must be enforced for all loop types.
 
 **SuccessCriterion routing:** The engine routes based on `definition.success.type`:
-    - `'test'`, `'build'`, `'lint'`, `'command'`, `'fileExists'` → dispatch to test-runner agent (or `@fixer` with focused prompt) via BackgroundJobBoard. Agent runs command, evaluates exit code or file existence. Engine evaluates result  no LLM involved.
+    - `'test'`, `'build'`, `'lint'`, `'command'`, `'fileExists'` → dispatch to test-runner agent (or `@fixer` with focused prompt) via BackgroundJobBoard. Agent runs command, evaluates exit code or file existence. Engine evaluates result - no LLM involved.
     - `'oracle'` → dispatch to Oracle, parse JSON verification result
     - `'observer'` → dispatch to Observer, parse JSON verification result
     - `'manual'` → engine fires `onManualReview`, waits for `resolveManualReview`
-    This makes the engine extensible  new success criterion types can be added without changing the engine's core logic.
+    This makes the engine extensible - new success criterion types can be added without changing the engine's core logic.
 
 2. Engine registers as the single terminal state listener on `BackgroundJobBoard`. All job completions route through `handleTerminalJob()`.
 
-3. Session lookup: `findSessionForJob(taskID)`  sessions track `activeJobID`, routes job events to the right session.
+3. Session lookup: `findSessionForJob(taskID)` - sessions track `activeJobID`, routes job events to the right session.
 
 4. Phase transitions driven by job terminal states, not by explicit loop control:
 
@@ -396,11 +396,11 @@ job completed (cancelled) → 'cancelled' → cleanup → onLoopComplete(false)
 job completed (error)     → handleFailure() → may escalate
 ```
 
-5. **No `improving` phase**  `@oracle` strictly verifies (returns `passed: false, reason: "X"`). `@fixer` self-corrects using `compactHistory()` from `.loop-history-{loopID}.md` + failure reason as input. No intermediate strategist.
+5. **No `improving` phase** - `@oracle` strictly verifies (returns `passed: false, reason: "X"`). `@fixer` self-corrects using `compactHistory()` from `.loop-history-{loopID}.md` + failure reason as input. No intermediate strategist.
 
-6. **Context injection**  text history and visual artifacts handled separately:
+6. **Context injection** - text history and visual artifacts handled separately:
 
-   **`.loop-history-{loopID}.md`**  text compaction for all loop types:
+   **`.loop-history-{loopID}.md`** - text compaction for all loop types:
    ```typescript
    private writeHistoryFile(session: LoopSession): void {
      const content = this.compactHistory(session);
@@ -422,17 +422,17 @@ job completed (error)     → handleFailure() → may escalate
     }
    ```
 
-**Observer artifact transfer:** For UI loops, `verifyAgent = 'observer'`, the executing agent writes visual artifacts to paths. The engine signals `onArtifactWrite(loopID, artifactPath)` so orchestrator can manage artifact lifecycle. Engine does not own filesystem artifacts  only signals when they are written.
+**Observer artifact transfer:** For UI loops, `verifyAgent = 'observer'`, the executing agent writes visual artifacts to paths. The engine signals `onArtifactWrite(loopID, artifactPath)` so orchestrator can manage artifact lifecycle. Engine does not own filesystem artifacts - only signals when they are written.
 
 **Manual verification:** When `success.type = 'manual'`, engine transitions to `verifying` but does NOT dispatch a verifyAgent. Instead, fires `onManualReview(loopID, reason)` and stops. Session waits. Orchestrator surfaces review to human. Human responds → orchestrator calls `engine.resolveManualReview(loopID, passed, reason)`. Engine resumes: `passed` → `done`, `!passed` → retry or escalate.
 
-    **No Council inside the loop**  Council with 360s+ latency stalls the rapid `executing ↔ verifying` oscillation. Council is reserved for Layer 0 escalation only.
+    **No Council inside the loop** - Council with 360s+ latency stalls the rapid `executing ↔ verifying` oscillation. Council is reserved for Layer 0 escalation only.
 
 7. Hard circuit breaker: when `attempts >= maxAttempts` && verification fails → `escalated`. Loop stops dispatching. `onEscalated` callback fires.
 
 8. Convergence signals: before dispatching retry, engine checks `jobBoard.hasConvergenceSignals()`. If exceeded → `escalated` regardless of attempt count.
 
-9. **Dispatch failure handling**  `try/catch` around `dispatchPhase()`:
+9. **Dispatch failure handling** - `try/catch` around `dispatchPhase()`:
    ```typescript
    private dispatchPhase(session: LoopSession): void {
      try {
@@ -446,14 +446,14 @@ job completed (error)     → handleFailure() → may escalate
    ```
    If dispatch throws (agent API down, token limit exceeded, etc.) → immediately `escalated` + `onEscalated` with system error. No orphaned session.
 
-10. **Cancellation lifecycle**  `cancelled` is a distinct terminal state, not an error:
+10. **Cancellation lifecycle** - `cancelled` is a distinct terminal state, not an error:
     ```typescript
     private handleTerminalJob(job: BackgroundJobRecord): void {
       const session = this.findSessionForJob(job.taskID);
       if (!session) return;
 
       if (job.state === 'cancelled') {
-        // Quiet shutdown  no escalation, no error increment
+        // Quiet shutdown - no escalation, no error increment
         session.currentPhase = 'cancelled';
         session.activeJobID = undefined;
         this.cleanupSession(session);  // delete artifactDir and historyFile
@@ -462,7 +462,7 @@ job completed (error)     → handleFailure() → may escalate
       }
 
       if (job.state === 'error') {
-        // Treat as verification failure  increment errors, potentially escalate
+        // Treat as verification failure - increment errors, potentially escalate
         this.handleFailure(session, job);
         return;
       }
@@ -473,7 +473,7 @@ job completed (error)     → handleFailure() → may escalate
     ```
     `cancel(loopID)` sets `cancellationRequested` on the job, which emits `cancelled` state. Engine catches it, transitions to `cancelled` terminal state, cleans up, fires `onLoopComplete(false)` (not `onEscalated`).
 
-11. **Oracle retry-wrapper for JSON parsing failures**  `oracleRetryCount` persisted in session:
+11. **Oracle retry-wrapper for JSON parsing failures** - `oracleRetryCount` persisted in session:
     ```typescript
     private evaluateVerification(session: LoopSession, job: BackgroundJobRecord): void {
       const result = this.tryParseVerification(job.resultSummary);
@@ -496,7 +496,7 @@ job completed (error)     → handleFailure() → may escalate
     ```
     `oracleRetryCount` is reset to `0` on every `executing` transition (not on parse success). Max 1 retry (retry if count == 0, i.e. first failure). Handles the 12.5% Oracle error rate without infinite loops.
 
-12. **Session cleanup**  prevents memory leaks:
+12. **Session cleanup** - prevents memory leaks:
     ```typescript
     private cleanupSession(session: LoopSession): void {
        // Delete .loop-history-{loopID}.md
@@ -504,7 +504,7 @@ job completed (error)     → handleFailure() → may escalate
       // Orchestrator handles artifact cleanup via onArtifactWrite tracking
     }
     ```
-    Called on terminal states: `done`, `escalated`, `cancelled`. Also called on `cancel(loopID)`. Engine only manages `.loop-history-{loopID}.md`  orchestrator owns artifact filesystem lifecycle.
+    Called on terminal states: `done`, `escalated`, `cancelled`. Also called on `cancel(loopID)`. Engine only manages `.loop-history-{loopID}.md` - orchestrator owns artifact filesystem lifecycle.
 
     **"Modify definition and retry"** during `escalated`: Human decides to modify and retry → engine does NOT reuse the session. Instead:
     1. Call `cancel(loopID)` → triggers `cancelled` cleanup
@@ -546,32 +546,32 @@ private tryParseVerification(raw: string | undefined): VerificationResult | null
 }
 ```
 
-**No regex matching**  if Oracle returns valid JSON, parsing succeeds. If not, retry once. If still fails, fail closed (not open).
+**No regex matching** - if Oracle returns valid JSON, parsing succeeds. If not, retry once. If still fails, fail closed (not open).
 
 ### Task 8: Create Loop Engineering Skill
 
 **File:** `src/skills/loop-engineering/SKILL.md` (new file)
 
-The skill instructs the orchestrator  it never "does" anything itself. Orchestrator follows the skill's guidance.
+The skill instructs the orchestrator - it never "does" anything itself. Orchestrator follows the skill's guidance.
 
 Two parts:
 
-**Grill (human interview)  orchestrator follows these instructions:**
+**Grill (human interview) - orchestrator follows these instructions:**
 - Conduct conversation to define `LoopDefinition` fields
 - Questions: goal, success criteria, max attempts, preferred agents, context files
 - Output structured JSON passed to `loopEngine.startLoop()`
 
-**Loop Monitor  orchestrator follows these instructions:**
+**Loop Monitor - orchestrator follows these instructions:**
 - Listen to engine callbacks (`onLoopComplete`, `onEscalated`)
 - Display current state, attempt count, verification result to human
-- On `onEscalated`  surface resolution options to human, await instruction
-- On human intervention (cancel, force pass, modify definition)  call appropriate engine method
+- On `onEscalated` - surface resolution options to human, await instruction
+- On human intervention (cancel, force pass, modify definition) - call appropriate engine method
 
 **Skill does NOT:**
-- Call `loopEngine` directly  orchestrator does that
-- Dispatch agents  engine does that
-- Evaluate verification  engine does that (via JSON parsing)
-- Manage state  engine does that
+- Call `loopEngine` directly - orchestrator does that
+- Dispatch agents - engine does that
+- Evaluate verification - engine does that (via JSON parsing)
+- Manage state - engine does that
 
 ### Task 9: Register /loop Command
 
@@ -588,8 +588,8 @@ grep -r "deepwork" src/ --include="*.ts"
 
 **Files:** (new test files alongside implementation)
 
-- `src/loop/loop-session.test.ts`  state machine transitions, transition enforcement, attempt recording
-- `src/loop/loop-engine.test.ts`  event-driven flow, job completion handling, convergence escalation, context compaction, dispatch failure handling
+- `src/loop/loop-session.test.ts` - state machine transitions, transition enforcement, attempt recording
+- `src/loop/loop-engine.test.ts` - event-driven flow, job completion handling, convergence escalation, context compaction, dispatch failure handling
 
 ---
 
@@ -597,16 +597,16 @@ grep -r "deepwork" src/ --include="*.ts"
 
 **Two-PR approach:**
 
-**PR 1  Convergence Signals (BackgroundJobBoard extension)**
+**PR 1 - Convergence Signals (BackgroundJobBoard extension)**
 - Tasks 1, 2, 3 only
 - Extends `BackgroundJobRecord` with `totalErrors`, `timeoutCount`, `lastErrorAt`
 - Adds convergence helper methods to `BackgroundJobBoard`
 - Upgrades event plumbing to callback array
-- **Naming:** Use `totalErrors` (not `errorCount`)  aligns with LoopEngine spec
-- **Scope rule:** `cancelled` does NOT increment `totalErrors`  quiet terminal state, not an error
+- **Naming:** Use `totalErrors` (not `errorCount`) - aligns with LoopEngine spec
+- **Scope rule:** `cancelled` does NOT increment `totalErrors` - quiet terminal state, not an error
 - Ready to open now
 
-**PR 2  Loop Engine (full runtime orchestration)**
+**PR 2 - Loop Engine (full runtime orchestration)**
 - Tasks 4, 7, 8, 9, 10
 - `LoopSession` + `LoopEngine` event-driven state machine
 - `SuccessCriterion` routing (test/build/lint evaluated directly, oracle/observer dispatched, manual waits for human)
@@ -615,25 +615,25 @@ grep -r "deepwork" src/ --include="*.ts"
 - Depends on PR 1 merging first
 
 **Not in MVP PRs (deferred but architected):**
-- **Worktree isolation**  architected in Task 5, deferred to post-MVP. Orchestrator uses `using-git-worktrees` skill. Engine delegates worktree lifecycle via callbacks. Prevents parallel loop file collisions.
-- **Cross-loop memory**  architected in Task 6, deferred to post-MVP. `.loop-memory.md` file store (MVP). Future: GitHub Issues, database. Enables learned strategies and tuned convergence thresholds.
+- **Worktree isolation** - architected in Task 5, deferred to post-MVP. Orchestrator uses `using-git-worktrees` skill. Engine delegates worktree lifecycle via callbacks. Prevents parallel loop file collisions.
+- **Cross-loop memory** - architected in Task 6, deferred to post-MVP. `.loop-memory.md` file store (MVP). Future: GitHub Issues, database. Enables learned strategies and tuned convergence thresholds.
 
 **Not architected yet (deferred):**
-- Trigger automation (cron, webhooks)  `LoopTrigger` interface defined but only 'manual' implemented in MVP
-- Fuzzy verification  Oracle returns boolean only; no engagement metrics or content quality scoring
-- MCP connectors (GitHub Issues, Slack, Sentry)  no external integrations
+- Trigger automation (cron, webhooks) - `LoopTrigger` interface defined but only 'manual' implemented in MVP
+- Fuzzy verification - Oracle returns boolean only; no engagement metrics or content quality scoring
+- MCP connectors (GitHub Issues, Slack, Sentry) - no external integrations
 
 These are the remaining delta between MVP loop engineering and full theory compliance (6 building blocks).
 
 ---
 
-## Future Extensions (Deferred  Not in MVP)
+## Future Extensions (Deferred - Not in MVP)
 
 These features are deferred. Interfaces will be defined when implementation begins.
 
-- **Worktree isolation**  opt-in per LoopDefinition, uses `using-git-worktrees` skill. Prevents parallel loop file collisions.
-- **Cross-loop memory**  `.loop-memory.md` file store. Learns from prior loops: successful strategies, failure patterns, tuned convergence thresholds.
-- **Trigger automation**  cron, webhook, event-driven invocation. `LoopTrigger` interface defined in Phase 4.
+- **Worktree isolation** - opt-in per LoopDefinition, uses `using-git-worktrees` skill. Prevents parallel loop file collisions.
+- **Cross-loop memory** - `.loop-memory.md` file store. Learns from prior loops: successful strategies, failure patterns, tuned convergence thresholds.
+- **Trigger automation** - cron, webhook, event-driven invocation. `LoopTrigger` interface defined in Phase 4.
 
 ### LoopMemoryConfig
 ```typescript
@@ -651,16 +651,16 @@ When implemented: Add `memory: LoopMemoryConfig` to `LoopDefinition`, `memoryLoa
 
 | File | Action |
 |------|--------|
-| `src/utils/background-job-board.ts` | Modify  convergence signals, helpers, event plumbing |
-| `src/loop/loop-session.ts` | Create  state machine class (binary oscillation, worktreeName, oracleRetryCount) |
-| `src/loop/loop-engine.ts` | Create  event-driven orchestration |
-| `src/loop/worktree-manager.ts` | Create  worktree lifecycle (create/merge/abandon, deferred) |
-| `src/loop/loop-memory.ts` | Create  cross-loop memory store (read/write patterns, deferred) |
-| `src/skills/loop-engineering/SKILL.md` | Create  Grill + Monitor prompts |
-| `src/tools/loop-command.ts` | Create  command definition |
-| `src/index.ts` | Modify  wire /loop command |
-| `src/loop/loop-session.test.ts` | Create  tests |
-| `src/loop/loop-engine.test.ts` | Create  tests |
+| `src/utils/background-job-board.ts` | Modify - convergence signals, helpers, event plumbing |
+| `src/loop/loop-session.ts` | Create - state machine class (binary oscillation, worktreeName, oracleRetryCount) |
+| `src/loop/loop-engine.ts` | Create - event-driven orchestration |
+| `src/loop/worktree-manager.ts` | Create - worktree lifecycle (create/merge/abandon, deferred) |
+| `src/loop/loop-memory.ts` | Create - cross-loop memory store (read/write patterns, deferred) |
+| `src/skills/loop-engineering/SKILL.md` | Create - Grill + Monitor prompts |
+| `src/tools/loop-command.ts` | Create - command definition |
+| `src/index.ts` | Modify - wire /loop command |
+| `src/loop/loop-session.test.ts` | Create - tests |
+| `src/loop/loop-engine.test.ts` | Create - tests |
 
 ---
 
@@ -677,28 +677,28 @@ bun test
 
 ## Dependencies
 
-- `BackgroundJobBoard`  already exists, extended with convergence signals and event plumbing
-- Agent dispatch  existing patterns in council/
-- Skill infrastructure  existing patterns in src/skills/
-- Oracle structured output tool  new tool definition in `src/tools/` (or reuse existing)
+- `BackgroundJobBoard` - already exists, extended with convergence signals and event plumbing
+- Agent dispatch - existing patterns in council/
+- Skill infrastructure - existing patterns in src/skills/
+- Oracle structured output tool - new tool definition in `src/tools/` (or reuse existing)
 
 ---
 
 ## Out of Scope
 
-- **Worktree isolation**  deferred (would prevent parallel loop file collisions)
-- **Cross-loop persistent memory**  deferred (history dies with session)
-- **Trigger automation**  only manual `/loop` invocation in MVP (no cron/webhooks)
-- **Fuzzy verification**  Oracle returns boolean only (no engagement metrics)
-- **MCP connectors**  no GitHub Issues, Slack, Sentry integration
-- **Persistence**  in-memory only for MVP
-- **New hooks or infrastructure**  beyond orchestration wiring
-- **Visualization**  beyond skill prompts
-- Layer 1 (runtime) always enforces constraints  no "signals not constraints" in the engine layer
+- **Worktree isolation** - deferred (would prevent parallel loop file collisions)
+- **Cross-loop persistent memory** - deferred (history dies with session)
+- **Trigger automation** - only manual `/loop` invocation in MVP (no cron/webhooks)
+- **Fuzzy verification** - Oracle returns boolean only (no engagement metrics)
+- **MCP connectors** - no GitHub Issues, Slack, Sentry integration
+- **Persistence** - in-memory only for MVP
+- **New hooks or infrastructure** - beyond orchestration wiring
+- **Visualization** - beyond skill prompts
+- Layer 1 (runtime) always enforces constraints - no "signals not constraints" in the engine layer
 
 **Signals vs constraints distinction:**
-- `BackgroundJobRecord` convergence signals (`totalErrors`, `timeoutCount`) → "signals not constraints"  warn LLM via `formatForPrompt()`, LLM decides
-- `LoopEngine` circuit breaker → hard constraints  `escalated` state is enforced, not signaled
+- `BackgroundJobRecord` convergence signals (`totalErrors`, `timeoutCount`) → "signals not constraints" - warn LLM via `formatForPrompt()`, LLM decides
+- `LoopEngine` circuit breaker → hard constraints - `escalated` state is enforced, not signaled
 
 ---
 
@@ -706,10 +706,10 @@ bun test
 
 The loop engineering spec and plan were validated against real-world implementations:
 
-- **autoresearch** (Karpathy): Confirms MVP scope  skill + executor + git history is the proven minimum. Our LoopEngine + skill + `.loop-history-{loopID}.md` directly mirrors this pattern.
+- **autoresearch** (Karpathy): Confirms MVP scope - skill + executor + git history is the proven minimum. Our LoopEngine + skill + `.loop-history-{loopID}.md` directly mirrors this pattern.
 - **Claude Code community**: `while True` loops in CLAUDE.md are the most common adoption pattern. Our `/loop` command formalizes what users already do manually.
-- **Ralph (Simon Willison)**: Simplest on-ramp  agent loop in a markdown file. Validates that skill-first approach (not infrastructure-first) is the right entry point.
+- **Ralph (Simon Willison)**: Simplest on-ramp - agent loop in a markdown file. Validates that skill-first approach (not infrastructure-first) is the right entry point.
 
 **Impact on plan:** No changes needed. The 5-phase roadmap (runtime engine → loop skill → routine integration → triggers → persistent memory) matches the proven adoption curve. Phase 1-2 (MVP) is where the value is.
 
-**Risk identified:** autoresearch shows that manual verification (human in the loop) is often "good enough" for autonomous loops. Our spec's automated verification (@oracle/@observer) is a differentiator but should not be a blocker  MVP could ship with manual verification as a fallback SuccessCriterion type.
+**Risk identified:** autoresearch shows that manual verification (human in the loop) is often "good enough" for autonomous loops. Our spec's automated verification (@oracle/@observer) is a differentiator but should not be a blocker - MVP could ship with manual verification as a fallback SuccessCriterion type.

+ 83 - 83
docs/superpowers/specs/2026-06-25-loop-engineering-runtime.md

@@ -1,4 +1,4 @@
-# Loop Engineering  Runtime-First Design
+# Loop Engineering - Runtime-First Design
 
 ## Core Insight
 
@@ -10,7 +10,7 @@ The loop engine is **orchestration wiring**, not prompt engineering.
 The runtime decides: what state comes next, when verification occurs, whether success criteria passed, whether another iteration is allowed, when escalation policies apply. The LLM decides: how to solve the problem, how to adapt after feedback, what implementation strategy to try next.
 
 **Core design principle:**
-> **Verification is the center of loop engineering  not execution.**
+> **Verification is the center of loop engineering - not execution.**
 
 Retries, failures, warnings, error counts, timeouts are **escalation signals**, not the loop itself. The loop is `Goal → Execute → Verify → Goal satisfied?` Everything else hangs off that.
 
@@ -23,24 +23,24 @@ Layer 1 (LoopEngine):   Execute dispatch, Verify parsing, State transitions, Cir
 Layer 2 (Agents/Skill): Execute work, Verify output, Skill instructions
 ```
 
-The orchestrator delegates to the engine. The engine dispatches agents. The skill instructs the orchestrator  it never acts directly.
+The orchestrator delegates to the engine. The engine dispatches agents. The skill instructs the orchestrator - it never acts directly.
 
 ## Architecture
 
 ### Three-Layer Design
 
 ```
-Layer 0: Orchestrator  runtime that runs everything
+Layer 0: Orchestrator - runtime that runs everything
   - Loads and follows skill instructions
   - Delegates to LoopEngine
   - Collects LoopDefinition via Grill interview
   - Listens to engine callbacks (onLoopComplete, onEscalated)
   - Handles human-facing parts (Grill, escalation UI)
   - Dispatches @council ONLY on Layer 0 escalation (never inside the loop)
-  - Never dispatches specialist agents during a loop  engine dispatches via BackgroundJobBoard
+  - Never dispatches specialist agents during a loop - engine dispatches via BackgroundJobBoard
   - Provides `dispatch(agent, prompt, contextFiles)` callback to engine for agent spawning
 
-Layer 1: LoopEngine  orchestration logic, framework-owned
+Layer 1: LoopEngine - orchestration logic, framework-owned
   - Location: `src/loop/loop-engine.ts` (not src/council/)
   - Event-driven state machine
   - Dispatches agents via orchestrator-provided callback (not direct SDK access)
@@ -51,14 +51,14 @@ Layer 1: LoopEngine — orchestration logic, framework-owned
   - Handles dispatch failures with try/catch → escalated
   - Wraps Oracle verification with retry on JSON parse failure
 
-Layer 2: Specialist agents  do the work
-  - @fixer, @designer  execute implementation tasks
-  - @explorer, @librarian  execute research/gather loops
-  - @oracle  strict verification only (returns JSON, not strategy)
-  - @observer  visual verification (reads artifacts from session directory)
-  - test  automated verification (exit code parsing)
-  - @council  Layer 0 escalation ONLY (not inside the loop)
-  - Skill  instructs orchestrator, never "does" anything itself
+Layer 2: Specialist agents - do the work
+  - @fixer, @designer - execute implementation tasks
+  - @explorer, @librarian - execute research/gather loops
+  - @oracle - strict verification only (returns JSON, not strategy)
+  - @observer - visual verification (reads artifacts from session directory)
+  - test - automated verification (exit code parsing)
+  - @council - Layer 0 escalation ONLY (not inside the loop)
+  - Skill - instructs orchestrator, never "does" anything itself
 ```
 
 ---
@@ -67,14 +67,14 @@ Layer 2: Specialist agents — do the work
 
 ### LoopSession State Machine
 
-**Binary oscillation**  no planning or improving phase:
+**Binary oscillation** - no planning or improving phase:
 
 ```
 States: executing | verifying | done | escalated | cancelled
 
 Transitions:
   executing  → verifying    (on job completed)
-  executing  → escalated    (on dispatch/execution error  API down, token limit, etc.)
+  executing  → escalated    (on dispatch/execution error - API down, token limit, etc.)
   verifying  → done         (on verification passed)
   verifying  → executing    (on verification failed, attempts < maxAttempts)
   verifying  → escalated    (on verification failed, attempts >= maxAttempts)
@@ -87,10 +87,10 @@ Transitions:
 **`oracleRetryCount` lifecycle:** Reset to `0` on every `executing` transition. Increment on Oracle retry. Max 1 retry (retry if count == 0, i.e. first failure). If second parse fails → fail closed.
 
 **Design decisions:**
-- `planning` removed  `LoopDefinition` is fully formed from Grill. Loop starts in `executing` immediately dispatching executeAgent.
-- `improving` removed  `@oracle` strictly verifies. `@fixer` self-corrects using `.loop-history.md` + failure reason.
+- `planning` removed - `LoopDefinition` is fully formed from Grill. Loop starts in `executing` immediately dispatching executeAgent.
+- `improving` removed - `@oracle` strictly verifies. `@fixer` self-corrects using `.loop-history.md` + failure reason.
 - Binary oscillation between `executing` and `verifying` is the complete state machine.
-- `cancelled` is a distinct terminal state, not an error  no `onEscalated` callback, quiet cleanup.
+- `cancelled` is a distinct terminal state, not an error - no `onEscalated` callback, quiet cleanup.
 - **Same-agent constraint:** `executeAgent` and `verifyAgent` MUST be different. Validation at `startLoop()` throws if equal. This prevents the "student marking their own exam" problem across all loop types (not just code loops).
 
 **God object risk:** Every responsibility in `LoopEngine` must be expressible as **state transition**, **event**, or **policy**. If a responsibility cannot be expressed this way, it belongs elsewhere (orchestrator, external policy store, dedicated service). This keeps the engine testable and maintainable.
@@ -99,7 +99,7 @@ Transitions:
 
 **1. LoopDefinition** (input from Grill)
 ```typescript
-// Success criteria  first-class runtime type
+// Success criteria - first-class runtime type
 // The runtime evaluates these directly where possible. Only subjective criteria go to Oracle.
 type SuccessCriterion =
   | { type: 'test'; command: string }                         // exit code 0 = pass
@@ -121,7 +121,7 @@ interface LoopDefinition {
   // executeAgent is dynamically selected based on task domain
   executeAgent: 'fixer' | 'designer' | 'explorer' | 'librarian';
   // verifyAgent is dynamically selected based on task domain
-  // Note: 'council' is NOT a verifyAgent inside the loop  Layer 0 escalation only
+  // Note: 'council' is NOT a verifyAgent inside the loop - Layer 0 escalation only
   verifyAgent: 'oracle' | 'observer' | 'test';
   // CONSTRAINT: executeAgent and verifyAgent MUST be different agents
   // Validation: startLoop() throws if executeAgent === verifyAgent
@@ -153,9 +153,9 @@ type VerificationResult =
 
 Escalation primitives inside the loop, not the foundation of loop engineering:
 
-- `totalErrors`  accumulated errors across attempts (NOT incremented on `cancelled`)
-- `timeoutCount`  consecutive timeouts, resets to 0 on `completed`
-- `lastErrorAt`  timestamp of last error
+- `totalErrors` - accumulated errors across attempts (NOT incremented on `cancelled`)
+- `timeoutCount` - consecutive timeouts, resets to 0 on `completed`
+- `lastErrorAt` - timestamp of last error
 
 **Place in architecture:**
 ```
@@ -170,22 +170,22 @@ LoopEngine
 
 Error tracking is an **implementation detail of escalation**, not the foundation. Verification is the center of loop engineering.
 
-**Convergence signal scope:** Signals apply to `error` and `timeout` states only. The `cancelled` state is a quiet terminal state  it does NOT increment error counters. This prevents noisy escalation when users intentionally cancel.
+**Convergence signal scope:** Signals apply to `error` and `timeout` states only. The `cancelled` state is a quiet terminal state - it does NOT increment error counters. This prevents noisy escalation when users intentionally cancel.
 
 **Signal computation:** Convergence signals are computed from `BackgroundJobRecord` state transitions, not explicit flags:
-- `totalErrors`  incremented when job state transitions to `error`
-- `timeoutCount`  incremented when `timedOut === true` on status update; reset to 0 on `completed`
-- `lastErrorAt`  timestamp of last `error` state transition
+- `totalErrors` - incremented when job state transitions to `error`
+- `timeoutCount` - incremented when `timedOut === true` on status update; reset to 0 on `completed`
+- `lastErrorAt` - timestamp of last `error` state transition
 
-This avoids redundant `isError`/`isTimeout` fields on status input  the state machine already conveys this information.
+This avoids redundant `isError`/`isTimeout` fields on status input - the state machine already conveys this information.
 
 When convergence signals exceed threshold:
 → transition to `escalated` state
 → circuit closed, human handoff required
 
 **Signals vs constraints distinction:**
-- `BackgroundJobRecord` convergence signals → "signals not constraints"  warn LLM via `formatForPrompt()`, LLM decides
-- `LoopEngine` circuit breaker (`escalated` state) → hard constraints  enforced, not signaled
+- `BackgroundJobRecord` convergence signals → "signals not constraints" - warn LLM via `formatForPrompt()`, LLM decides
+- `LoopEngine` circuit breaker (`escalated` state) → hard constraints - enforced, not signaled
 
 ### Session Cleanup
 
@@ -196,7 +196,7 @@ To prevent `/tmp/` memory leaks across multiple loops:
 
 - **Artifact cleanup is orchestrator-owned:** The engine does NOT manage artifact directories. Orchestrator tracks artifact paths via `onArtifactWrite` callbacks and handles cleanup independently.
 
-- **Cancellation also triggers cleanup:** If user triggers `cancel(loopID)`, the engine transitions to `cancelled`, cleans up, fires `onLoopComplete(false)` (not `onEscalated`). Quiet shutdown  no error escalation.
+- **Cancellation also triggers cleanup:** If user triggers `cancel(loopID)`, the engine transitions to `cancelled`, cleans up, fires `onLoopComplete(false)` (not `onEscalated`). Quiet shutdown - no error escalation.
 
 - **"Modify definition and retry" during `escalated`:** Human decides to modify and retry → engine does NOT reuse the session. Instead:
   1. Call `cancel(loopID)` → triggers `cancelled` cleanup
@@ -207,7 +207,7 @@ To prevent `/tmp/` memory leaks across multiple loops:
 
 Text history and visual artifacts are handled separately:
 
-**`.loop-history-{loopID}.md`**  text compaction for all loop types:
+**`.loop-history-{loopID}.md`** - text compaction for all loop types:
 
 Written to the project root before each retry. Appended to `contextFiles` so agents read it as file context, not job description noise. Includes loopID in filename to prevent collision across concurrent loops.
 
@@ -226,7 +226,7 @@ function compactHistory(history: AttemptRecord[]): string {
 }
 ```
 
-**Session artifact directory**  for Observer visual artifact transfer (Designer → Observer):
+**Session artifact directory** - for Observer visual artifact transfer (Designer → Observer):
 
 ```
 /tmp/loop-{loopID}/
@@ -237,7 +237,7 @@ function compactHistory(history: AttemptRecord[]): string {
 
 Designer writes visual artifacts to `session.artifactDir` during `executing`. Artifact paths included in `resultSummary` or a dedicated `artifacts` field on `BackgroundJobRecord`.
 
-Engine reads artifact paths from completed job, includes them in Observer's `contextFiles` for `verifying`. Observer reads artifacts from session directory as file context  same mechanism as text files, works reliably for multimodal models.
+Engine reads artifact paths from completed job, includes them in Observer's `contextFiles` for `verifying`. Observer reads artifacts from session directory as file context - same mechanism as text files, works reliably for multimodal models.
 
 **Why file context, not job description:**
 - Models are optimized to read file context
@@ -316,7 +316,7 @@ On cancelled → cleanup → onLoopComplete(false)
 
 Location: `src/skills/loop-engineering/SKILL.md`
 
-The skill instructs the orchestrator  it never "does" anything itself.
+The skill instructs the orchestrator - it never "does" anything itself.
 
 **Orchestrator follows skill's Grill instructions:**
 - Conduct conversation to define `LoopDefinition` fields
@@ -326,14 +326,14 @@ The skill instructs the orchestrator — it never "does" anything itself.
 **Orchestrator follows skill's Loop Monitor instructions:**
 - Listen to engine callbacks (`onLoopComplete`, `onEscalated`)
 - Display current state, attempt count, verification result to human
-- On `onEscalated`  surface resolution options to human, await instruction
-- On human intervention (cancel, force pass, modify definition)  call appropriate engine method
+- On `onEscalated` - surface resolution options to human, await instruction
+- On human intervention (cancel, force pass, modify definition) - call appropriate engine method
 
 **Skill does NOT:**
-- Call `loopEngine` directly  orchestrator does that
-- Dispatch agents  engine does that
-- Evaluate verification  engine does that (via JSON parsing)
-- Manage state  engine does that
+- Call `loopEngine` directly - orchestrator does that
+- Dispatch agents - engine does that
+- Evaluate verification - engine does that (via JSON parsing)
+- Manage state - engine does that
 
 ---
 
@@ -341,11 +341,11 @@ The skill instructs the orchestrator — it never "does" anything itself.
 
 - Each attempt's phases (`executing`, `verifying`) run as individual `BackgroundJob` records
 - `BackgroundJobRecord` extended with convergence signals:
-  - `totalErrors`  incremented on `error` state only (NOT on `cancelled`)
-  - `timeoutCount`  incremented on `timeout`, resets to 0 on `completed`
-  - `lastErrorAt`  timestamp of last error
+  - `totalErrors` - incremented on `error` state only (NOT on `cancelled`)
+  - `timeoutCount` - incremented on `timeout`, resets to 0 on `completed`
+  - `lastErrorAt` - timestamp of last error
 
-- **Note:** `cancelled` is a quiet terminal state  it does NOT increment `totalErrors` or fire `onEscalated`. This prevents noisy escalation on intentional user cancellations.
+- **Note:** `cancelled` is a quiet terminal state - it does NOT increment `totalErrors` or fire `onEscalated`. This prevents noisy escalation on intentional user cancellations.
 
 - `BackgroundJobBoard` event plumbing updated to support multiple terminal state listeners (callback array instead of single listener)
 
@@ -365,16 +365,16 @@ Verification is driven by `SuccessCriterion.type`. The engine routes to the appr
 1. Engine dispatches to a test-runner agent (or `@fixer` with a focused prompt) via BackgroundJobBoard
 2. Agent runs the command, evaluates exit code or file existence
 3. Agent returns structured result: `{ passed: boolean, reason: string }`
-4. Engine evaluates result  no LLM involved. Deterministic. Fast.
+4. Engine evaluates result - no LLM involved. Deterministic. Fast.
 
-This uses the same dispatch mechanism as oracle/observer. The engine does not execute commands directly  it delegates to an agent that has shell access.
+This uses the same dispatch mechanism as oracle/observer. The engine does not execute commands directly - it delegates to an agent that has shell access.
 
 ### Subjective Verification (LLM-based)
 
 **`{ type: 'oracle' }`:**
 1. Engine dispatches to Oracle with `verifyTool` (structured JSON output)
 2. Oracle returns `{ passed: boolean, reason: string, suggestedFix?: string }`
-3. Engine parses JSON  if parse fails and `oracleRetryCount < 1`, re-dispatch once
+3. Engine parses JSON - if parse fails and `oracleRetryCount < 1`, re-dispatch once
 4. If second parse fails → fail closed (verification = failed)
 
 **`oracleRetryCount` lifecycle:** Persisted in `LoopSession`. Reset to `0` on every `executing` transition. Max 1 retry prevents infinite loops when Oracle consistently returns malformed JSON.
@@ -392,16 +392,16 @@ This uses the same dispatch mechanism as oracle/observer. The engine does not ex
 **`{ type: 'manual' }`:**
 1. Engine transitions to `verifying` phase
 2. Engine fires `onManualReview(loopID, reason)` callback
-3. Engine stops dispatching  session enters a waiting state (phase stays `verifying`, no active job, **no BackgroundJob created**)
+3. Engine stops dispatching - session enters a waiting state (phase stays `verifying`, no active job, **no BackgroundJob created**)
 4. Orchestrator surfaces the review request to the human
 5. Human responds with pass/fail via orchestrator → orchestrator calls `engine.resolveManualReview(loopID, passed, reason)`
 6. Engine resumes: `passed` → `done`, `!passed` → retry or escalate based on attempt count
 
-**Manual verification is the simplest on-ramp.** No LLM involved. Human decides. Proven by autoresearch  Karpathy's entire loop is manual inspection. Use when automated verification isn't worth the setup cost, or when you want to eyeball results before committing to a verification criteria.
+**Manual verification is the simplest on-ramp.** No LLM involved. Human decides. Proven by autoresearch - Karpathy's entire loop is manual inspection. Use when automated verification isn't worth the setup cost, or when you want to eyeball results before committing to a verification criteria.
 
 **No BackgroundJob for manual verification.** The BackgroundJobBoard tracks running jobs only. Manual review is an engine-level waiting state, not a job.
 
-### Council  Layer 0 Escalation Only
+### Council - Layer 0 Escalation Only
 
 **Council is NOT a verifyAgent inside the loop.** Council with 360s+ latency would stall the rapid `executing ↔ verifying` oscillation.
 
@@ -419,30 +419,30 @@ Council is reserved for Layer 0 escalation: when `escalated` fires, Orchestrator
 ## What Exists vs What Needs Building
 
 ### Already Exists (Layer 0)
-- Orchestrator  already runs skills, delegates to components
-- `/loop` command slot  available for registration
-- @council  available for Layer 0 escalation
+- Orchestrator - already runs skills, delegates to components
+- `/loop` command slot - available for registration
+- @council - available for Layer 0 escalation
 
 ### Already Exists (Layer 1)
-- `BackgroundJobBoard`  state tracking, event listener hook
-- `setTerminalStateListener`  single listener interface (may need upgrade to callback array)
+- `BackgroundJobBoard` - state tracking, event listener hook
+- `setTerminalStateListener` - single listener interface (may need upgrade to callback array)
 
 ### Already Exists (Layer 2)
 - `@fixer`, `@oracle`, `@council`, `@explorer` agents
-- `@designer`, `@observer`  available for UI loops
-- `@librarian`  available for research loops
+- `@designer`, `@observer` - available for UI loops
+- `@librarian` - available for research loops
 - Skill infrastructure
 
-### Needs Building (PR 1  Convergence Signals)
+### Needs Building (PR 1 - Convergence Signals)
 1. `BackgroundJobRecord` extended with `totalErrors`, `timeoutCount`, `lastErrorAt`
 2. Convergence helper methods on `BackgroundJobBoard`
 3. BackgroundJobBoard callback array (if needed for multi-listener)
 
-### Needs Building (PR 2  Loop Engine)
+### Needs Building (PR 2 - Loop Engine)
 4. `LoopSession` state machine class (binary oscillation, oracleRetryCount, cleanup)
 5. `LoopEngine` event-driven orchestration (cancellation lifecycle, cleanup routine)
 6. `writeHistoryFile()` and `compactHistory()` for `.loop-history.md`
-7. `SuccessCriterion` routing  test/build/lint/command/fileExists evaluated directly; oracle/observer dispatched
+7. `SuccessCriterion` routing - test/build/lint/command/fileExists evaluated directly; oracle/observer dispatched
 8. Structured verification tool for Oracle (with retry-wrapper)
 9. `onArtifactWrite` callback for orchestrator-owned artifact lifecycle
 10. `src/skills/loop-engineering/SKILL.md` (Grill interview + loop monitor)
@@ -452,24 +452,24 @@ Council is reserved for Layer 0 escalation: when `escalated` fires, Orchestrator
 ---
 
 ## Out of Scope (for MVP)
-- **Worktree isolation**  deferred to Future Extensions. MVP uses in-process execution.
-- **Cross-loop memory**  deferred to Future Extensions. MVP uses per-session history only.
-- **Trigger automation**  deferred to Future Extensions. Only 'manual' (`/loop` command) in MVP.
-- **Fuzzy verification**  SuccessCriterion only supports binary outcomes. No engagement metrics or content quality scoring.
-- **Token budget / cost controls**  `maxAttempts` limits iterations but not token spend per iteration. Deferred to post-MVP.
-- **MCP connectors**  no GitHub Issues, Slack, Sentry integration
+- **Worktree isolation** - deferred to Future Extensions. MVP uses in-process execution.
+- **Cross-loop memory** - deferred to Future Extensions. MVP uses per-session history only.
+- **Trigger automation** - deferred to Future Extensions. Only 'manual' (`/loop` command) in MVP.
+- **Fuzzy verification** - SuccessCriterion only supports binary outcomes. No engagement metrics or content quality scoring.
+- **Token budget / cost controls** - `maxAttempts` limits iterations but not token spend per iteration. Deferred to post-MVP.
+- **MCP connectors** - no GitHub Issues, Slack, Sentry integration
 - Persistence layer (in-memory only for session; file-based for `.loop-history.md`)
 - New hooks or infrastructure beyond orchestration wiring
 - Visualization/monitoring beyond skill prompts
-- Layer 1 always enforces constraints  no "signals not constraints" philosophy in the engine layer
+- Layer 1 always enforces constraints - no "signals not constraints" philosophy in the engine layer
 
 **Full theory compliance** would require all 6 building blocks:
-1. Trigger (cron, webhooks, events)  deferred
-2. Worktree isolation  deferred
-3. Execution (covered)  done
-4. Verification (fuzzy path)  deferred
-5. Memory (cross-loop)  deferred
-6. Connectors (MCPs)  deferred
+1. Trigger (cron, webhooks, events) - deferred
+2. Worktree isolation - deferred
+3. Execution (covered) - done
+4. Verification (fuzzy path) - deferred
+5. Memory (cross-loop) - deferred
+6. Connectors (MCPs) - deferred
 
 MVP = items 3 + 4 (binary verification) + skill harness + orchestration wiring.
 
@@ -492,7 +492,7 @@ A minimal autonomous research loop that validates our architecture:
 
 **Key takeaway:** Karpathy's loop is the simplest possible: skill + executor + git history + manual verification. No cross-loop memory, no triggers, no MCP connectors. Our MVP (execute + binary verification + skill harness + orchestration wiring) matches this proven pattern.
 
-**Divergence:** autoresearch has no verification agent  Karpathy manually inspects results. Our spec adds `@oracle`/`@observer` as automated verifiers, which is the right next step beyond manual inspection but still within the "binary oscillation" pattern.
+**Divergence:** autoresearch has no verification agent - Karpathy manually inspects results. Our spec adds `@oracle`/`@observer` as automated verifiers, which is the right next step beyond manual inspection but still within the "binary oscillation" pattern.
 
 ### Comparison with Claude Code and Codex
 
@@ -513,19 +513,19 @@ Our architecture is ahead of both on the verification and trigger fronts, but be
 **Phased roadmap:**
 - **Phase 1**: Runtime loop engine (this PR)
 - **Phase 2**: Loop skill (Grill + Monitor)
-- **Phase 3**: Routine integration  loop engine plugs into existing oh-my-opencode-slim workflow routines
+- **Phase 3**: Routine integration - loop engine plugs into existing oh-my-opencode-slim workflow routines
 - **Phase 4**: Triggers (cron, webhooks)
 - **Phase 5**: Persistent memory (cross-loop)
 
 This progression mirrors how users adopt loop engineering and reduces implementation risk.
 
-### PR 1  Convergence Signals (BackgroundJobBoard extension)
+### PR 1 - Convergence Signals (BackgroundJobBoard extension)
 - Extends `BackgroundJobRecord` with `totalErrors`, `timeoutCount`, `lastErrorAt`
 - Adds convergence helper methods to `BackgroundJobBoard`
 - Upgrades event plumbing to callback array
 - **Ready to open now**
 
-### PR 2  Loop Engine (full runtime orchestration)
+### PR 2 - Loop Engine (full runtime orchestration)
 - `LoopSession` + `LoopEngine` event-driven state machine
 - `SuccessCriterion` routing (test/build/lint/command/fileExists + oracle/observer)
 - Skill + `/loop` command
@@ -543,13 +543,13 @@ This progression mirrors how users adopt loop engineering and reduces implementa
 
 ---
 
-## Future Extensions (Deferred  Not in MVP)
+## Future Extensions (Deferred - Not in MVP)
 
 These features are deferred. Interfaces will be defined when implementation begins.
 
-- **Worktree isolation**  opt-in per LoopDefinition, uses `using-git-worktrees` skill. Prevents parallel loop file collisions.
-- **Cross-loop memory**  `.loop-memory.md` file store. Learns from prior loops: successful strategies, failure patterns, tuned convergence thresholds.
-- **Trigger automation**  cron, webhook, event-driven invocation. `LoopTrigger` interface defined in Phase 4.
+- **Worktree isolation** - opt-in per LoopDefinition, uses `using-git-worktrees` skill. Prevents parallel loop file collisions.
+- **Cross-loop memory** - `.loop-memory.md` file store. Learns from prior loops: successful strategies, failure patterns, tuned convergence thresholds.
+- **Trigger automation** - cron, webhook, event-driven invocation. `LoopTrigger` interface defined in Phase 4.
 
 ---
 

+ 1 - 1
docs/tools.md

@@ -22,7 +22,7 @@ Fetch remote pages with content extraction tuned for docs/static sites.
 
 ## Code Search Tools
 
-Fast, structural code search and refactoring  more powerful than plain text grep.
+Fast, structural code search and refactoring - more powerful than plain text grep.
 
 | Tool | Description |
 |------|-------------|

+ 1 - 1
oh-my-opencode-slim.schema.json

@@ -422,7 +422,7 @@
           "maximum": 5
         },
         "master": {
-          "description": "DEPRECATED  ignored. Council agent synthesizes directly."
+          "description": "DEPRECATED - ignored. Council agent synthesizes directly."
         }
       },
       "required": [

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "oh-my-opencode-slim",
-  "version": "2.0.6",
+  "version": "2.1.0",
   "description": "Lightweight agent orchestration plugin for OpenCode - a slimmed-down fork of oh-my-opencode",
   "main": "dist/index.js",
   "types": "dist/index.d.ts",

+ 2 - 2
src/agents/council.test.ts

@@ -81,10 +81,10 @@ describe('formatCouncillorResults', () => {
     );
     expect(formatted).toContain('**Failed/Timed-out Councillors**:');
     expect(formatted).toContain(
-      '**beta**: timed_out  Request timed out after 180000ms',
+      '**beta**: timed_out - Request timed out after 180000ms',
     );
     expect(formatted).toContain(
-      '**gamma**: failed  Provider returned empty response',
+      '**gamma**: failed - Provider returned empty response',
     );
     expect(formatted).not.toContain('**beta** (gpt-5.5):');
     expect(formatted).not.toContain('**gamma** (gemini-pro):');

+ 8 - 8
src/agents/council.ts

@@ -4,10 +4,10 @@ import { type AgentDefinition, resolvePrompt } from './orchestrator';
 import { createReadOnlyAgentPermission } from './permissions';
 
 // NOTE: Councillor system prompts live in the councillor agent factory.
-// The format functions below only structure the USER message content  the
+// The format functions below only structure the USER message content - the
 // agent factory provides the system prompt.
 
-const COUNCIL_AGENT_PROMPT = `You are the Council agent  a multi-LLM \
+const COUNCIL_AGENT_PROMPT = `You are the Council agent - a multi-LLM \
 orchestration system that runs consensus across multiple models.
 
 **Tool**: You have access to the \`council_session\` tool. You also have read-only codebase inspection tools. You do not have write, edit, shell, or subagent-delegation tools.
@@ -24,9 +24,9 @@ orchestration system that runs consensus across multiple models.
 4. Follow the Synthesis Process below
 5. Present the result to the user
 
-**Synthesis Process** (MANDATORY  follow in order):
+**Synthesis Process** (MANDATORY - follow in order):
 1. Read the original user prompt
-2. Review each councillor's response individually  note each councillor's \
+2. Review each councillor's response individually - note each councillor's \
 key insight and unique contribution by name
 3. Identify agreements and contradictions between councillors
 4. Resolve contradictions with explicit reasoning
@@ -41,7 +41,7 @@ key insight and unique contribution by name
 - Do not omit per-councillor details from the final response
 - Do not collapse the output into only a final summary
 - Be transparent about trade-offs when different approaches have valid pros/cons
-- Don't just average responses  choose the best approach and improve upon it
+- Don't just average responses - choose the best approach and improve upon it
 
 ${READONLY_FILE_OPERATIONS_RULES}
 
@@ -107,7 +107,7 @@ export function createCouncilAgent(
 /**
  * Build the prompt for a specific councillor session.
  *
- * Returns the raw user prompt  the agent factory (councillor.ts) provides
+ * Returns the raw user prompt - the agent factory (councillor.ts) provides
  * the system prompt with tool-aware instructions. No duplication.
  *
  * If a per-councillor prompt override is provided, it is prepended as
@@ -152,7 +152,7 @@ export function formatCouncillorResults(
 
   const failedSection = councillorResults
     .filter((cr) => cr.status !== 'completed')
-    .map((cr) => `**${cr.name}**: ${cr.status}  ${cr.error ?? 'Unknown'}`)
+    .map((cr) => `**${cr.name}**: ${cr.status} - ${cr.error ?? 'Unknown'}`)
     .join('\n');
 
   // Defensive guard: caller (runCouncil) short-circuits when all fail,
@@ -161,7 +161,7 @@ export function formatCouncillorResults(
     const errorDetails = councillorResults
       .map(
         (cr) =>
-          `**${cr.name}** (${shortModelLabel(cr.model)}): ${cr.status}  ${
+          `**${cr.name}** (${shortModelLabel(cr.model)}): ${cr.status} - ${
             cr.error ?? 'Unknown'
           }`,
       )

+ 4 - 4
src/agents/councillor.ts

@@ -3,7 +3,7 @@ import { type AgentDefinition, resolvePrompt } from './orchestrator';
 import { createReadOnlyAgentPermission } from './permissions';
 
 /**
- * Councillor agent  a read-only advisor in the multi-LLM council.
+ * Councillor agent - a read-only advisor in the multi-LLM council.
  *
  * Councillors are spawned by CouncilManager as agent sessions (visible in
  * tmux/UI). They have read-only access to the codebase via tools but CANNOT
@@ -13,7 +13,7 @@ import { createReadOnlyAgentPermission } from './permissions';
  * deny all, then selectively allow read-only tools.
  *
  * The per-councillor model is overridden at session creation time via the
- * `model` field in the prompt body  the agent factory's default model is
+ * `model` field in the prompt body - the agent factory's default model is
  * just a fallback.
  */
 const COUNCILLOR_PROMPT = `You are a councillor in a multi-model council.
@@ -35,13 +35,13 @@ other agents. You are an advisor, not an implementer.
 ${NO_SHELL_READONLY_FILE_OPERATIONS_RULES}
 
 **Behavior**:
-- **Examine the codebase** before answering  your read access is what makes \
+- **Examine the codebase** before answering - your read access is what makes \
   council valuable. Don't guess at code you can see.
 - Analyze the problem thoroughly
 - Provide a complete, well-reasoned response
 - Focus on the quality and correctness of your solution
 - Be direct and concise
-- Don't be influenced by what other councillors might say  you won't see \
+- Don't be influenced by what other councillors might say - you won't see \
   their responses
 
 **Output**:

+ 4 - 4
src/agents/designer.ts

@@ -9,7 +9,7 @@ const DESIGNER_PROMPT = `You are a Designer - a frontend UI/UX specialist who cr
 
 **Typography**
 - Choose distinctive, characterful fonts that elevate aesthetics
-- Avoid generic defaults (Arial, Inter)opt for unexpected, beautiful choices
+- Avoid generic defaults (Arial, Inter)-opt for unexpected, beautiful choices
 - Pair display fonts with refined body fonts for hierarchy
 
 **Color & Theme**
@@ -26,7 +26,7 @@ const DESIGNER_PROMPT = `You are a Designer - a frontend UI/UX specialist who cr
 
 **Spatial Composition**
 - Break conventions: asymmetry, overlap, diagonal flow, grid-breaking
-- Generous negative space OR controlled densitycommit to the choice
+- Generous negative space OR controlled density-commit to the choice
 - Unexpected layouts that guide the eye
 
 **Visual Depth**
@@ -35,7 +35,7 @@ const DESIGNER_PROMPT = `You are a Designer - a frontend UI/UX specialist who cr
 - Contextual effects that match the aesthetic (grain overlays, custom cursors)
 
 **Styling Approach**
-- Default to Tailwind CSS utility classes when availablefast, maintainable, consistent
+- Default to Tailwind CSS utility classes when available-fast, maintainable, consistent
 - Use custom CSS when the vision requires it: complex animations, unique effects, advanced compositions
 - Balance utility-first speed with creative freedom where it matters
 
@@ -47,7 +47,7 @@ const DESIGNER_PROMPT = `You are a Designer - a frontend UI/UX specialist who cr
 ## Constraints
 - Respect existing design systems when present
 - Leverage component libraries where available
-- Prioritize visual excellencecode perfection comes second
+- Prioritize visual excellence-code perfection comes second
 - Use grounded, normal, regular english - don't use jargon or overly technical language
 
 ${WRITABLE_FILE_OPERATIONS_RULES}

+ 1 - 1
src/agents/fixer.ts

@@ -20,7 +20,7 @@ ${WRITABLE_FILE_OPERATIONS_RULES}
 - NO external research (no websearch, context7, gh_grep)
 - NO delegation or spawning subagents
 - No multi-step research/planning; minimal execution sequence ok
-- If context is insufficient: use grep/glob/read directly  do not delegate
+- If context is insufficient: use grep/glob/read directly - do not delegate
 - Only ask for missing inputs you truly cannot retrieve yourself
 - Do not act as the primary reviewer; implement requested changes and surface obvious issues briefly
 

+ 2 - 2
src/agents/index.test.ts

@@ -530,7 +530,7 @@ describe('council agent model resolution', () => {
 
   test('council falls back to legacy master.model when no preset override', () => {
     // Simulates a pre-1.0.0 config with council.master.model but no council
-    // entry in the agent preset  the exact scenario from issue #369.
+    // entry in the agent preset - the exact scenario from issue #369.
     const config: PluginConfig = {
       agents: {
         oracle: { model: 'openai/gpt-5.5' },
@@ -546,7 +546,7 @@ describe('council agent model resolution', () => {
   });
 
   test('council preset override takes precedence over legacy master.model', () => {
-    // If user has explicit council in preset, that wins  legacy is ignored.
+    // If user has explicit council in preset, that wins - legacy is ignored.
     const config: PluginConfig = {
       agents: {
         council: { model: 'google/gemini-3-pro' },

+ 1 - 1
src/agents/index.ts

@@ -650,7 +650,7 @@ export function getAgentConfigs(
       // and as a subagent (orchestrator can delegate to it)
       sdkConfig.mode = 'all';
     } else if (name === 'councillor') {
-      // Internal agent  subagent mode, hidden from @ autocomplete
+      // Internal agent - subagent mode, hidden from @ autocomplete
       sdkConfig.mode = 'subagent';
       sdkConfig.hidden = true;
     } else if (isSubagent(name)) {

+ 6 - 6
src/agents/observer.ts

@@ -1,21 +1,21 @@
 import { READONLY_FILE_OPERATIONS_RULES } from '../config';
 import type { AgentDefinition } from './orchestrator';
 
-const OBSERVER_PROMPT = `You are Observer  a visual analysis specialist.
+const OBSERVER_PROMPT = `You are Observer - a visual analysis specialist.
 
 **Role**: Interpret images, screenshots, PDFs, and diagrams. Extract structured observations for the Orchestrator to act on.
 
 **Behavior**:
 - Read the file(s) specified in the prompt
-- Analyze visual content  layouts, UI elements, text, relationships, flows
-- For screenshots with text/code/errors: extract the **exact text** via OCR  never paraphrase error messages or code
+- Analyze visual content - layouts, UI elements, text, relationships, flows
+- For screenshots with text/code/errors: extract the **exact text** via OCR - never paraphrase error messages or code
 - For multiple files: analyze each, then compare or relate as requested
 - Return ONLY the extracted information relevant to the goal
-- If the image is unclear, blurry, or partially visible: state what you CAN see and explicitly note what is uncertain  never guess or fabricate details
+- If the image is unclear, blurry, or partially visible: state what you CAN see and explicitly note what is uncertain - never guess or fabricate details
 
 **Constraints**:
 - READ-ONLY: Analyze and report, don't modify files
-- Save context tokens  the Orchestrator never processes the raw file
+- Save context tokens - the Orchestrator never processes the raw file
 - Match the language of the request
 - If info not found, state clearly what's missing
 
@@ -38,7 +38,7 @@ export function createObserverAgent(
   return {
     name: 'observer',
     description:
-      'Visual analysis. Use for interpreting images, screenshots, PDFs, and diagrams  extracts structured observations without loading raw files into main context. Requires a vision-capable model.',
+      'Visual analysis. Use for interpreting images, screenshots, PDFs, and diagrams - extracts structured observations without loading raw files into main context. Requires a vision-capable model.',
     config: {
       model,
       temperature: 0.1,

+ 6 - 6
src/agents/orchestrator.ts

@@ -72,7 +72,7 @@ const AGENT_DESCRIPTIONS: Record<string, string> = {
 - Permissions: read_files, write_files
 - Stats: 2x faster code edits, 1/2 cost of orchestrator
 - Weakness: design, taste
-- Tools/Constraints: Execution-focusedno research, no architectural decisions
+- Tools/Constraints: Execution-focused-no research, no architectural decisions
 - **Delegate when:** For implementation work, think and triage first. If the change is non-trivial or multi-file, hand bounded execution to @fixer • Parallelization benefits: Task involves multiple folders and multiple files modification, scoping work per folder and spawning parallel @fixers for each folder.
 - **Don't delegate when:** Needs discovery/research/decisions • Single small change (<20 lines, one file) • Unclear requirements needing iteration • Explaining to fixer > doing • Tight integration with your current work • Requires design taste, visual hierarchy, interaction polish, responsive layout decisions, animation/motion, component feel, or UI copy/design trade-offs
 - **Rule of thumb:** Headless/mechanical implementation → @fixer. User-visible design or polish → @designer. If @designer already set direction, @fixer may only do bounded mechanical follow-up that preserves that design exactly.`,
@@ -93,12 +93,12 @@ const AGENT_DESCRIPTIONS: Record<string, string> = {
 - Lane: Visual/media analysis isolated from orchestrator context
 - Role: Visual analysis specialist for images, PDFs, and diagrams
 - Permissions: Read files
-- Stats: Saves main context tokens  Observer processes raw files, returns structured observations
+- Stats: Saves main context tokens - Observer processes raw files, returns structured observations
 - Capabilities: Interprets images, screenshots, PDFs, and diagrams via native read tool; extracts UI elements, layouts, text, relationships
 - **Delegate when:** Need to analyze a multimedia file• Extract information
 - **Don't delegate when:** Plain text files that Read can handle directly • Files that need editing afterward (need literal content from Read)
-- **Rule of thumb:** Even if your model supports vision, delegate visual analysis to @observer  it isolates large image/PDF bytes from your context window, returning only concise structured text. Need exact file contents for routing? → Read only the minimal context yourself.
-- **IMPORTANT:** When delegating to @observer, always include the **full file path** in the prompt so it can read the file. Example: "Analyze the screenshot at /path/to/file.png  describe the UI elements and error messages."`,
+- **Rule of thumb:** Even if your model supports vision, delegate visual analysis to @observer - it isolates large image/PDF bytes from your context window, returning only concise structured text. Need exact file contents for routing? → Read only the minimal context yourself.
+- **IMPORTANT:** When delegating to @observer, always include the **full file path** in the prompt so it can read the file. Example: "Analyze the screenshot at /path/to/file.png - describe the UI elements and error messages."`,
 };
 
 // Validation routing lines that reference agents
@@ -130,14 +130,14 @@ export function buildOrchestratorPrompt(disabledAgents?: Set<string>): string {
     .map(([, desc]) => desc)
     .join('\n\n');
 
-  // Filter validation routing lines  remove lines mentioning any disabled agent
+  // Filter validation routing lines - remove lines mentioning any disabled agent
   const enabledValidationRouting = VALIDATION_ROUTING.filter((line) => {
     const mentions = [...line.matchAll(/@(\w+)/g)].map((m) => m[1]);
     if (mentions.length === 0) return true;
     return mentions.every((name) => !disabledAgents?.has(name));
   }).join('\n');
 
-  // Filter parallel delegation examples  remove lines mentioning any disabled agent
+  // Filter parallel delegation examples - remove lines mentioning any disabled agent
   const enabledParallelExamples = PARALLEL_DELEGATION_EXAMPLES.filter(
     (line) => {
       const mentions = [...line.matchAll(/@(\w+)/g)].map((m) => m[1]);

+ 1 - 1
src/cli/providers.test.ts

@@ -81,7 +81,7 @@ describe('providers', () => {
     expect(agents.oracle.variant).toBe('max');
     expect(agents.council).toBeUndefined();
     expect(agents.librarian.model).toBe('opencode-go/deepseek-v4-flash');
-    expect(agents.explorer.model).toBe('opencode-go/minimax-m2.7');
+    expect(agents.explorer.model).toBe('opencode-go/deepseek-v4-flash');
     expect(agents.designer.model).toBe('opencode-go/kimi-k2.7-code');
     expect(agents.fixer.model).toBe('opencode-go/deepseek-v4-flash');
     expect(agents.fixer.variant).toBe('high');

+ 1 - 1
src/cli/providers.ts

@@ -48,7 +48,7 @@ export const MODEL_MAPPINGS = {
     orchestrator: { model: 'opencode-go/glm-5.2' },
     oracle: { model: 'opencode-go/qwen3.7-max', variant: 'max' },
     librarian: { model: 'opencode-go/deepseek-v4-flash' },
-    explorer: { model: 'opencode-go/minimax-m2.7' },
+    explorer: { model: 'opencode-go/deepseek-v4-flash' },
     designer: { model: 'opencode-go/kimi-k2.7-code', variant: 'medium' },
     fixer: { model: 'opencode-go/deepseek-v4-flash', variant: 'high' },
     observer: { model: 'opencode-go/kimi-k2.6' },

+ 2 - 2
src/cli/skills.ts

@@ -5,7 +5,7 @@ import { CUSTOM_SKILLS } from './custom-skills';
  * permission grants but is NOT installed by this plugin's CLI.
  */
 export interface PermissionOnlySkill {
-  /** Skill name  must match the name OpenCode uses for permission checks */
+  /** Skill name - must match the name OpenCode uses for permission checks */
   name: string;
   /** List of agents that should auto-allow this skill */
   allowedAgents: string[];
@@ -15,7 +15,7 @@ export interface PermissionOnlySkill {
 
 /**
  * Skills managed externally (not installed by this plugin's CLI).
- * Entries here only affect agent permission grants  nothing is installed.
+ * Entries here only affect agent permission grants - nothing is installed.
  */
 export const PERMISSION_ONLY_SKILLS: PermissionOnlySkill[] = [
   {

+ 1 - 1
src/config/constants.ts

@@ -73,7 +73,7 @@ export const NO_SHELL_READONLY_FILE_OPERATIONS_RULES = `**File Operations Rules*
 - Use glob/grep/ast_grep_search for discovery and read for file contents.
 - Do not use bash or shell commands.`;
 
-// Tmux pane spawn delay (ms)  gives TmuxSessionManager time to create pane
+// Tmux pane spawn delay (ms) - gives TmuxSessionManager time to create pane
 export const TMUX_SPAWN_DELAY_MS = 500;
 
 // Stagger delay (ms) between parallel councillor launches to avoid tmux collisions

+ 4 - 4
src/config/council-schema.ts

@@ -86,7 +86,7 @@ export const CouncilPresetSchema = z
     const councillors: Record<string, CouncillorConfig> = {};
 
     for (const [key, raw] of Object.entries(entries)) {
-      // Silently skip the legacy "master" key  no longer parsed as a
+      // Silently skip the legacy "master" key - no longer parsed as a
       // councillor. Old configs with per-preset master overrides won't
       // error, but the override has no effect.
       if (key === 'master') continue;
@@ -182,14 +182,14 @@ export const CouncilConfigSchema = z
         'Number of retry attempts for councillors that return empty responses ' +
           '(e.g. due to provider rate limiting). Default: 3 retries.',
       ),
-    // Deprecated fields  accepted for backward compatibility but ignored.
+    // Deprecated fields - accepted for backward compatibility but ignored.
     // The council agent now synthesizes directly; no separate master session.
-    // Uses permissive schemas since the values are discarded  strict
+    // Uses permissive schemas since the values are discarded - strict
     // validation would break old configs with non-standard model IDs.
     master: z
       .unknown()
       .optional()
-      .describe('DEPRECATED  ignored. Council agent synthesizes directly.'),
+      .describe('DEPRECATED - ignored. Council agent synthesizes directly.'),
   })
   .transform((data) => {
     // Detect deprecated fields and attach warning for consumers

+ 1 - 1
src/config/model-resolution.test.ts

@@ -7,7 +7,7 @@ import type { ModelEntry } from '../config/schema';
  *
  * The resolver always picks the first model in the effective array,
  * regardless of provider configuration. This is correct because:
- * - Not all providers require entries in opencodeConfig.provider  some are
+ * - Not all providers require entries in opencodeConfig.provider - some are
  *   loaded automatically by opencode (e.g. github-copilot, openrouter).
  * - We cannot distinguish "auto-loaded provider" from "provider not configured"
  *   without calling the API, which isn't available at config-hook time.

+ 1 - 1
src/council/codemap.md

@@ -146,7 +146,7 @@ This ensures councillors provide guidance without side effects.
 
 ### Notifications
 - Sends immediate feedback to parent session on council start
-- Message format: `⎔ Council starting — ${count} councillors launching — ctrl+x ↓ to watch`
+- Message format: `⎔ Council starting - ${count} councillors launching - ctrl+x ↓ to watch`
 
 ## Performance Considerations
 

+ 2 - 2
src/council/council-manager.test.ts

@@ -720,7 +720,7 @@ describe('CouncilManager', () => {
       );
 
       expect(result.success).toBe(true);
-      // Verify no prompt contamination  councillor gets raw prompt
+      // Verify no prompt contamination - councillor gets raw prompt
       const promptCalls = ctx.client.session.prompt.mock.calls as Array<
         [
           {
@@ -841,7 +841,7 @@ describe('CouncilManager', () => {
       );
 
       expect(result.success).toBe(false);
-      // No retry on timeout  messages should not be called
+      // No retry on timeout - messages should not be called
       expect(messagesCallCount).toBe(0);
       expect(result.councillorResults).toHaveLength(1);
       expect(result.councillorResults[0].status).toBe('timed_out');

+ 2 - 2
src/council/council-manager.ts

@@ -125,7 +125,7 @@ export class CouncilManager {
       log(`[council-manager] Preset "${resolvedPreset}" has no councillors`);
       return {
         success: false,
-        error: `Preset "${resolvedPreset}" has no councillors configured. Note: the reserved key "master" is ignored  use councillor names as keys`,
+        error: `Preset "${resolvedPreset}" has no councillors configured. Note: the reserved key "master" is ignored - use councillor names as keys`,
         councillorResults: [],
       };
     }
@@ -203,7 +203,7 @@ export class CouncilManager {
     councillorCount: number,
   ): Promise<void> {
     const message = [
-      `⎔ Council starting — ${councillorCount} councillors launching — ctrl+x ↓ to watch`,
+      `⎔ Council starting - ${councillorCount} councillors launching - ctrl+x ↓ to watch`,
       '',
       '[system status: continue without acknowledging this notification]',
     ].join('\n');

+ 1 - 1
src/hooks/apply-patch/codec.test.ts

@@ -175,7 +175,7 @@ garbage`),
   });
 
   test('normalizeUnicode unifies expected typographic variants', () => {
-    expect(normalizeUnicode('“uno”…\u00A0dostres')).toBe('"uno"... dos-tres');
+    expect(normalizeUnicode('“uno”…\u00A0dos-tres')).toBe('"uno"... dos-tres');
   });
 
   test('normalizeUnicode covers less common typographic variants', () => {

+ 1 - 1
src/hooks/codemap.md

@@ -7,7 +7,7 @@ Implements OpenCode lifecycle hooks that transform, process, and manage chat mes
 
 ### Core Architecture
 - **Factory Pattern**: Each hook is created via a factory function (e.g., `createApplyPatchHook()`, `createAutoUpdateCheckerHook()`) that returns a hook function matching the OpenCode hook signature.
-- **Stateful Factories**: Hook factories may maintain closure state between invocations (e.g., `createAutoUpdateCheckerHook` guards with `hasChecked`; `createTaskSessionManagerHook` manages session lifecycle). Other hooks remain stateless  each factory decides based on its needs.
+- **Stateful Factories**: Hook factories may maintain closure state between invocations (e.g., `createAutoUpdateCheckerHook` guards with `hasChecked`; `createTaskSessionManagerHook` manages session lifecycle). Other hooks remain stateless - each factory decides based on its needs.
 - **Message Transformation Pipeline**: Hooks operate on the `MessageWithParts[]` type, allowing transformation of user messages, assistant responses, and system messages.
 
 ### Key Types & Interfaces

+ 1 - 1
src/hooks/deepwork/codemap.md

@@ -2,7 +2,7 @@
 
 ## Responsibility
 
-Provides an OpenCode hook implementation for managing deepwork sessions  heavy, multi-phase coding tasks that require structured planning, phased execution, and continuous validation.
+Provides an OpenCode hook implementation for managing deepwork sessions - heavy, multi-phase coding tasks that require structured planning, phased execution, and continuous validation.
 
 This hook enables developers to:
 - Initiate deepwork sessions via `/deepwork <task>` command

+ 17 - 17
src/hooks/foreground-fallback/index.test.ts

@@ -131,7 +131,7 @@ describe('isRateLimitError', () => {
 });
 
 // ---------------------------------------------------------------------------
-// ForegroundFallbackManager  disabled
+// ForegroundFallbackManager - disabled
 // ---------------------------------------------------------------------------
 
 describe('ForegroundFallbackManager (disabled)', () => {
@@ -152,7 +152,7 @@ describe('ForegroundFallbackManager (disabled)', () => {
 });
 
 // ---------------------------------------------------------------------------
-// ForegroundFallbackManager  session.error
+// ForegroundFallbackManager - session.error
 // ---------------------------------------------------------------------------
 
 describe('ForegroundFallbackManager session.error', () => {
@@ -309,7 +309,7 @@ describe('ForegroundFallbackManager session.error', () => {
 });
 
 // ---------------------------------------------------------------------------
-// ForegroundFallbackManager  message.updated
+// ForegroundFallbackManager - message.updated
 // ---------------------------------------------------------------------------
 
 describe('ForegroundFallbackManager message.updated', () => {
@@ -371,7 +371,7 @@ describe('ForegroundFallbackManager message.updated', () => {
 });
 
 // ---------------------------------------------------------------------------
-// ForegroundFallbackManager  session.status retry
+// ForegroundFallbackManager - session.status retry
 // ---------------------------------------------------------------------------
 
 describe('ForegroundFallbackManager session.status', () => {
@@ -445,7 +445,7 @@ describe('ForegroundFallbackManager session.status', () => {
 });
 
 // ---------------------------------------------------------------------------
-// ForegroundFallbackManager  chain exhaustion
+// ForegroundFallbackManager - chain exhaustion
 // ---------------------------------------------------------------------------
 
 describe('ForegroundFallbackManager chain exhaustion', () => {
@@ -471,7 +471,7 @@ describe('ForegroundFallbackManager chain exhaustion', () => {
       },
     });
 
-    // Rate limit fires  only model in chain is already current → nothing to fall back to
+    // Rate limit fires - only model in chain is already current → nothing to fall back to
     await mgr.handleEvent({
       type: 'session.error',
       properties: { sessionID: 's', error: { message: 'rate limit exceeded' } },
@@ -536,7 +536,7 @@ describe('ForegroundFallbackManager chain exhaustion', () => {
 });
 
 // ---------------------------------------------------------------------------
-// ForegroundFallbackManager  deduplication
+// ForegroundFallbackManager - deduplication
 // ---------------------------------------------------------------------------
 
 describe('ForegroundFallbackManager deduplication', () => {
@@ -553,7 +553,7 @@ describe('ForegroundFallbackManager deduplication', () => {
     };
 
     await mgr.handleEvent(event);
-    await mgr.handleEvent(event); // immediate second trigger  should be deduped
+    await mgr.handleEvent(event); // immediate second trigger - should be deduped
 
     expect(mocks.promptAsync).toHaveBeenCalledTimes(1);
   });
@@ -590,7 +590,7 @@ describe('ForegroundFallbackManager deduplication', () => {
       },
     });
 
-    // First error  model A fails, falls back to model B (openai/gpt-4o)
+    // First error - model A fails, falls back to model B (openai/gpt-4o)
     await mgr.handleEvent({
       type: 'session.error',
       properties: {
@@ -608,7 +608,7 @@ describe('ForegroundFallbackManager deduplication', () => {
       }),
     );
 
-    // Second error  model B also fails within the 5s dedup window.
+    // Second error - model B also fails within the 5s dedup window.
     // This is a DIFFERENT incident (new model), so dedup is bypassed
     // because the current model differs from lastTriggerModel.
     await mgr.handleEvent({
@@ -633,7 +633,7 @@ describe('ForegroundFallbackManager deduplication', () => {
 });
 
 // ---------------------------------------------------------------------------
-// ForegroundFallbackManager  subagent.session.created
+// ForegroundFallbackManager - subagent.session.created
 // ---------------------------------------------------------------------------
 
 describe('ForegroundFallbackManager subagent.session.created', () => {
@@ -647,7 +647,7 @@ describe('ForegroundFallbackManager subagent.session.created', () => {
       properties: { sessionID: 'sub-1', agentName: 'explorer' },
     });
 
-    // Now trigger rate limit  should use explorer's chain
+    // Now trigger rate limit - should use explorer's chain
     await mgr.handleEvent({
       type: 'session.error',
       properties: { sessionID: 'sub-1', error: { message: 'rate limit' } },
@@ -668,7 +668,7 @@ describe('ForegroundFallbackManager subagent.session.created', () => {
 });
 
 // ---------------------------------------------------------------------------
-// ForegroundFallbackManager  session.deleted cleanup
+// ForegroundFallbackManager - session.deleted cleanup
 // ---------------------------------------------------------------------------
 
 describe('ForegroundFallbackManager session.deleted', () => {
@@ -766,19 +766,19 @@ describe('ForegroundFallbackManager session.deleted', () => {
 });
 
 // ---------------------------------------------------------------------------
-// ForegroundFallbackManager  resolveChain correctness
+// ForegroundFallbackManager - resolveChain correctness
 // ---------------------------------------------------------------------------
 
 describe('ForegroundFallbackManager resolveChain cross-agent isolation', () => {
   test('does not use another agent chain when known agent has no configured chain', async () => {
     // oracle has no chain in runtimeChains; without the fix resolveChain would
     // fall through to the cross-agent "last resort" and pick a model from
-    // orchestrator's chain  re-prompting oracle with an orchestrator model.
+    // orchestrator's chain - re-prompting oracle with an orchestrator model.
     const { client, mocks } = createMockClient();
     const mgr = new ForegroundFallbackManager(
       client,
       {
-        // oracle intentionally absent  no chain configured
+        // oracle intentionally absent - no chain configured
         orchestrator: ['openai/gpt-4o', 'google/gemini-2.5-pro'],
       },
       true,
@@ -811,7 +811,7 @@ describe('ForegroundFallbackManager resolveChain cross-agent isolation', () => {
       true,
     );
 
-    // No agent name tracked, no model tracked  triggers session.error
+    // No agent name tracked, no model tracked - triggers session.error
     await mgr.handleEvent({
       type: 'session.error',
       properties: {

+ 7 - 7
src/hooks/foreground-fallback/index.ts

@@ -6,7 +6,7 @@
  *   1. Looks up the next untried model in the agent's configured chain
  *   2. Aborts the rate-limited prompt via client.session.abort()
  *   3. Re-queues the last user message via client.session.promptAsync()
- *      with the new model  promptAsync returns immediately so we never
+ *      with the new model - promptAsync returns immediately so we never
  *      block the event handler waiting for a full LLM response.
  *
  * This mirrors the same fallback loop used for delegated sessions, but operates
@@ -171,7 +171,7 @@ export class ForegroundFallbackManager {
         // Check for rate-limit signals in the status message regardless of
         // status type. OpenCode proxies may emit monthly/weekly/5-hour usage
         // limit errors with type 'error' instead of 'retry' on fresh sessions
-        // where no retry is attempted  the retry-type guard would miss them.
+        // where no retry is attempted - the retry-type guard would miss them.
         if (
           msg.includes('rate limit') ||
           msg.includes('usage limit') ||
@@ -203,8 +203,8 @@ export class ForegroundFallbackManager {
         // Clean up all per-session state to prevent unbounded memory growth
         // in long-running instances with many subagent sessions.
         // OpenCode emits two shapes depending on context:
-        //   { properties: { sessionID } }    subagent / task sessions
-        //   { properties: { info: { id } } }  top-level session deletion
+        //   { properties: { sessionID } }   - subagent / task sessions
+        //   { properties: { info: { id } } } - top-level session deletion
         // Mirror the same dual-shape lookup used elsewhere in the plugin.
         const props = event.properties as
           | { sessionID?: string; info?: { id?: string } }
@@ -232,7 +232,7 @@ export class ForegroundFallbackManager {
     if (this.inProgress.has(sessionID)) return;
 
     // Deduplicate: multiple events can fire for a single rate-limit event.
-    // Bypass dedup when the model changed since the last trigger  the new
+    // Bypass dedup when the model changed since the last trigger - the new
     // model's failure is a separate incident and the cascade should continue.
     const now = Date.now();
     const curModel = this.sessionModel.get(sessionID);
@@ -332,7 +332,7 @@ export class ForegroundFallbackManager {
         return;
       }
 
-      // promptAsync queues the prompt and returns immediately  this avoids
+      // promptAsync queues the prompt and returns immediately - this avoids
       // blocking the event handler while waiting for a full LLM response.
       // Cast required: promptAsync is not in the plugin TypeScript types for
       // oh-my-opencode-slim but IS present on the real OpenCode client at
@@ -413,7 +413,7 @@ export class ForegroundFallbackManager {
       const chain = this.chains[agentName];
       if (chain) return chain;
       // Known omos built-in agent (oracle, librarian, …) without a
-      // configured chain: keep isolation  do NOT bleed into other
+      // configured chain: keep isolation - do NOT bleed into other
       // agents' chains (preserves the cross-agent isolation contract
       // from PR #199).
       if ((ALL_AGENT_NAMES as readonly string[]).includes(agentName)) return [];

+ 1 - 1
src/hooks/loop-command/index.ts

@@ -14,7 +14,7 @@ function activationPrompt(text: string): string {
   return [
     'The user ran `/loop`. From the text below, extract: goal, successCriteria, maxAttempts.',
     '',
-    'If ANY are missing or unclear  push back and ask the user to clarify.',
+    'If ANY are missing or unclear - push back and ask the user to clarify.',
     'Do not assume or guess. All three must be explicit.',
     '',
     'Once all three are clear, run the loop:',

+ 1 - 1
src/hooks/types.ts

@@ -3,7 +3,7 @@
  *
  * These types describe the structure of chat messages passed through
  * `experimental.chat.messages.transform` and related hooks. All fields
- * are unioned across the files that previously defined them privately 
+ * are unioned across the files that previously defined them privately -
  * optional extras are harmless under structural typing.
  */
 

+ 4 - 4
src/index.ts

@@ -184,7 +184,7 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
       const presetAgents = config.presets[runtimePreset];
       config.agents = deepMerge(config.agents, presetAgents);
     } else if (runtimePreset) {
-      // Preset was deleted from config since last switch  clear stale state
+      // Preset was deleted from config since last switch - clear stale state
       setActiveRuntimePreset(null);
     }
 
@@ -471,7 +471,7 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
           'orchestrator';
       }
 
-      // Merge Agent configs  per-agent shallow merge to preserve
+      // Merge Agent configs - per-agent shallow merge to preserve
       // user-supplied fields (e.g. tools, permission) from opencode.json
       if (!opencodeConfig.agent) {
         opencodeConfig.agent = { ...agents };
@@ -506,7 +506,7 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
           if (models.length === 0) continue;
 
           // Use the first model in the model array. Not all providers
-          // require entries in opencodeConfig.provider  some are loaded
+          // require entries in opencodeConfig.provider - some are loaded
           // automatically by opencode (e.g. github-copilot, openrouter).
           // We cannot distinguish these from truly unconfigured providers
           // at config-hook time, so we cannot gate on the provider config
@@ -528,7 +528,7 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
               }
             }
           } else {
-            // Agent exists in slim but not in opencodeConfig.agent 
+            // Agent exists in slim but not in opencodeConfig.agent -
             // create entry
             (configAgent as Record<string, unknown>)[agentName] = {
               model: chosen.id,

+ 2 - 2
src/interview/dashboard-manager.ts

@@ -33,7 +33,7 @@ export function createDashboardManager(
   const interviewConfig = config.interview;
   const service = createInterviewService(ctx, interviewConfig);
 
-  // Async init  resolves once we know our role (dashboard or session)
+  // Async init - resolves once we know our role (dashboard or session)
   let initDone = false;
   let isDashboard = false;
   let dashboardBaseUrl = '';
@@ -132,7 +132,7 @@ export function createDashboardManager(
         // ── We're a SESSION ─────────────────────────────────────────
         const probe = await probeDashboard(dashboardPort);
         if (!probe.alive) {
-          // Brief retry  dashboard may still be starting
+          // Brief retry - dashboard may still be starting
           await new Promise((r) => setTimeout(r, 500));
           const retry = await probeDashboard(dashboardPort);
           if (!retry.alive) {

+ 6 - 6
src/interview/dashboard.ts

@@ -75,11 +75,11 @@ export async function readDashboardAuthFile(
       pid: number;
       startedAt: number;
     };
-    // Check if the PID is still alive  stale file from crashed dashboard
+    // Check if the PID is still alive - stale file from crashed dashboard
     try {
       process.kill(data.pid, 0); // signal 0 = existence check, no actual signal
     } catch {
-      // PID doesn't exist  stale auth file from crashed dashboard
+      // PID doesn't exist - stale auth file from crashed dashboard
       try {
         fsSync.unlinkSync(getAuthFilePath(port));
       } catch {
@@ -317,7 +317,7 @@ export function createDashboardServer(config: DashboardConfig): {
 
   function getKnownDirectories(): Set<string> {
     const dirs = new Set<string>();
-    // Always scan home directory  interviews may have been created from a
+    // Always scan home directory - interviews may have been created from a
     // session that ran with cwd=$HOME and never registered with the dashboard.
     dirs.add(os.homedir());
     for (const session of sessions.values()) {
@@ -352,7 +352,7 @@ export function createDashboardServer(config: DashboardConfig): {
         discoveredFolders.add(session.directory);
       }
     } catch {
-      // Session list not available  rely on registered sessions
+      // Session list not available - rely on registered sessions
     }
   }
 
@@ -504,7 +504,7 @@ export function createDashboardServer(config: DashboardConfig): {
     );
     const pathname = decodeURIComponent(url.pathname);
 
-    // NOTE: No CORS headers. Same-origin only  dashboard pages and
+    // NOTE: No CORS headers. Same-origin only - dashboard pages and
     // API share the same origin (127.0.0.1:port). Cross-origin POST is
     // blocked by browser preflight since we don't send Access-Control
     // headers. Do NOT add them without also adding CSRF protection.
@@ -1522,7 +1522,7 @@ export async function tryBecomeDashboard(
           await new Promise((resolve) => setTimeout(resolve, jitterMs()));
           continue;
         }
-        return null; // All retries exhausted  treat as session
+        return null; // All retries exhausted - treat as session
       }
       throw error;
     }

+ 1 - 1
src/interview/document.ts

@@ -221,7 +221,7 @@ export async function readInterviewDocument(
   try {
     return await fs.readFile(record.markdownPath, 'utf8');
   } catch {
-    // File missing or unreadable  recreate it
+    // File missing or unreadable - recreate it
   }
   await ensureInterviewFile(record);
   return fs.readFile(record.markdownPath, 'utf8');

+ 2 - 2
src/interview/helpers.test.ts

@@ -277,7 +277,7 @@ describe('readJsonBody', () => {
         } catch (error) {
           caughtError =
             error instanceof Error ? error : new Error(String(error));
-          // Socket already destroyed  can't send response
+          // Socket already destroyed - can't send response
         }
       },
     );
@@ -295,7 +295,7 @@ describe('readJsonBody', () => {
     });
 
     try {
-      // Send a body larger than 64KB  connection will reset
+      // Send a body larger than 64KB - connection will reset
       const bigPayload = { data: 'x'.repeat(65 * 1024) };
       try {
         await fetch(`http://127.0.0.1:${port}/`, {

+ 4 - 4
src/interview/service.ts

@@ -439,7 +439,7 @@ export function createInterviewService(
     // Rename file if assistant provided a title (and file hasn't been renamed yet)
     await maybeRenameWithTitle(interview, state.title);
 
-    // Skip rewrite when parsed.state is null  agent already wrote the final spec
+    // Skip rewrite when parsed.state is null - agent already wrote the final spec
     let document: string;
     if (parsed.state) {
       document = await rewriteInterviewDocument(interview, state.summary);
@@ -611,7 +611,7 @@ export function createInterviewService(
       await appendInterviewAnswers(interview, state.questions, answers);
       const prompt = buildAnswerPrompt(answers, state.questions, maxQuestions);
 
-      // Use promptAsync for non-blocking  returns immediately, LLM
+      // Use promptAsync for non-blocking - returns immediately, LLM
       // processes in background. State push updates dashboard when done.
       const model = sessionModel.get(interview.sessionID);
       await ctx.client.session.promptAsync({
@@ -906,7 +906,7 @@ export function createInterviewService(
         `The user sent a freeform message via the dashboard chat panel:`,
         `${message}`,
         ``,
-        `Process this request  it may be a request to add a new section, revise existing content, ask clarifying questions, or make structural changes.`,
+        `Process this request - it may be a request to add a new section, revise existing content, ask clarifying questions, or make structural changes.`,
         `Update the specification document accordingly and include the updated 11-section specification.`,
         `Ask up to ${maxQuestions} clarifying questions if needed using the same <interview_state> JSON block format as before.`,
       ].join('\n');
@@ -980,7 +980,7 @@ export function createInterviewService(
           `The user confirmed the interview spec is complete.`,
           ``,
           `Produce a final, polished version of the full spec document.`,
-          `Do NOT include any <interview_state> block  just output the final spec as clean markdown.`,
+          `Do NOT include any <interview_state> block - just output the final spec as clean markdown.`,
           `The spec should be comprehensive, well-structured, and ready for implementation.`,
         ].join('\n');
       }

+ 1 - 1
src/interview/types.ts

@@ -20,7 +20,7 @@ export interface InterviewAssistantState {
 
 // ─── Zod Schemas (for validating untrusted LLM output) ─────────────
 
-/** Raw question object from LLM output  loose, everything optional. */
+/** Raw question object from LLM output - loose, everything optional. */
 export const RawQuestionSchema = z.object({
   id: z.string().optional(),
   question: z.string().optional(),

+ 11 - 11
src/interview/ui.ts

@@ -375,8 +375,8 @@ export function renderDashboardPage(
         <p class="muted">${totalCount} item${totalCount === 1 ? '' : 's'}</p>
       </div>
       <div class="info-box">
-        <strong>Interviews</strong>  live sessions and recovered files. State is pushed from OpenCode sessions to this dashboard.<br>
-        <strong>Files without session</strong>  <code>.md</code> files in <code>${escapeHtml(outputFolder)}</code> with no frontmatter. Resume with <code>/interview &lt;name&gt;</code>.
+        <strong>Interviews</strong> - live sessions and recovered files. State is pushed from OpenCode sessions to this dashboard.<br>
+        <strong>Files without session</strong> - <code>.md</code> files in <code>${escapeHtml(outputFolder)}</code> with no frontmatter. Resume with <code>/interview &lt;name&gt;</code>.
       </div>
       <details style="margin-bottom:24px">
         <summary style="cursor:pointer;font-size:13px;color:rgba(255,255,255,0.4);user-select:none">Settings</summary>
@@ -404,7 +404,7 @@ export function renderDashboardPage(
       ${fileSection}
       <div class="footer">OH MY OPENCODE SLIM</div>
     </div>
-    <div class="update-banner" id="updateBanner">Dashboard updated  tap to refresh</div>
+    <div class="update-banner" id="updateBanner">Dashboard updated - tap to refresh</div>
     <script>
       ${clipboardHelperJs()}
       document.querySelectorAll('.copy-btn').forEach(btn => {
@@ -1076,7 +1076,7 @@ export function renderInterviewPage(
     </div>
 
     <div class="chat-panel" id="chatPanel">
-      <input type="text" id="chatInput" class="chat-input" placeholder="Send a message to the agent  add a section, revise content, ask questions..." autocomplete="off" />
+      <input type="text" id="chatInput" class="chat-input" placeholder="Send a message to the agent - add a section, revise content, ask questions..." autocomplete="off" />
       <button class="chat-send" id="chatSendBtn" type="button" disabled>Send</button>
     </div>
 
@@ -1435,7 +1435,7 @@ export function renderInterviewPage(
 
       // simpleMarkdown: safe because escaping happens BEFORE markdown
       // processing. Adding raw HTML output from user content would break
-      // this safety  always use textContent for user data.
+      // this safety - always use textContent for user data.
       function simpleMarkdown(text) {
         if (!text) return '';
         const escaped = text
@@ -1581,7 +1581,7 @@ export function renderInterviewPage(
             aBadge.textContent = 'A';
             const aText = document.createElement('span');
             aText.className = 'qa-text qa-text-a';
-            aText.textContent = pair.a || '';
+            aText.textContent = pair.a || '-';
             aRow.appendChild(aBadge);
             aRow.appendChild(aText);
             card.appendChild(aRow);
@@ -1649,7 +1649,7 @@ export function renderInterviewPage(
         if (busy) {
           input.placeholder = 'Agent is processing...';
         } else {
-          input.placeholder = 'Send a message to the agent  add a section, revise content, ask questions...';
+          input.placeholder = 'Send a message to the agent - add a section, revise content, ask questions...';
         }
       }
 
@@ -1807,7 +1807,7 @@ export function renderInterviewPage(
         statusEl.textContent = data.mode.toUpperCase();
         statusEl.className = isDone ? 'status-completed' : '';
         
-        // Render Markdown Path  always visible in completed mode
+        // Render Markdown Path - always visible in completed mode
         const pathContainer = document.getElementById('filePathContainer');
         const pathElement = document.getElementById('markdownPath');
         const mdPath = data.markdownPath || (data.interview && data.interview.markdownPath);
@@ -1825,7 +1825,7 @@ export function renderInterviewPage(
           pathContainer.style.display = 'none';
         }
 
-        // Resume command  only in session-disconnected mode
+        // Resume command - only in session-disconnected mode
         var resumeRow = document.getElementById('resumeRow');
         if (resumeRow) {
           var showResume = data.mode === 'session-disconnected';
@@ -1835,7 +1835,7 @@ export function renderInterviewPage(
           }
         }
 
-        // Nudge actions  only for completed (live session)
+        // Nudge actions - only for completed (live session)
         // session-disconnected can't nudge (no session to receive it)
         const nudgeActions = document.getElementById('nudgeActions');
         const moreBtn = document.getElementById('moreQuestionsBtn');
@@ -1958,7 +1958,7 @@ export function renderInterviewPage(
             if (confirmBtn) confirmBtn.disabled = false;
           } else {
             try { await refresh(); } catch (_) {}
-            schedulePoll(); // Restart polling  nudge may reactivate interview
+            schedulePoll(); // Restart polling - nudge may reactivate interview
           }
         } catch (_err) {
           document.getElementById('submitStatus').textContent = 'Network error.';

+ 2 - 2
src/skills/clonedeps/SKILL.md

@@ -217,9 +217,9 @@ sentence so future agents do not need an extra read just to know what is there:
 Read-only dependency source repositories are available under
 `.slim/clonedeps/repos/` for inspection. Do not edit these clones.
 
-- `.slim/clonedeps/repos/<safe-name>/`  `<repo>` at `<ref>`; <one sentence on
+- `.slim/clonedeps/repos/<safe-name>/` - `<repo>` at `<ref>`; <one sentence on
   why this source is useful>.
-- `.slim/clonedeps/repos/<safe-name-2>/`  `<repo>` at `<ref>`; <one sentence on
+- `.slim/clonedeps/repos/<safe-name-2>/` - `<repo>` at `<ref>`; <one sentence on
   why this source is useful>.
 ```
 

+ 2 - 2
src/skills/codemap/SKILL.md

@@ -87,7 +87,7 @@ Once all specific directories are mapped, the Orchestrator must create or update
 
 **OpenCode auto-loads `AGENTS.md` into agent context on every session.** To ensure agents automatically discover and use the codemap, update (or create) `AGENTS.md` at the repo root:
 
-1. If `AGENTS.md` already exists and already contains a `## Repository Map` section, **skip this step**  the reference is already set up.
+1. If `AGENTS.md` already exists and already contains a `## Repository Map` section, **skip this step** - the reference is already set up.
 2. If `AGENTS.md` exists but has no `## Repository Map` section, **append** the section below.
 3. If `AGENTS.md` doesn't exist, **create** it with the section below.
 
@@ -104,7 +104,7 @@ Before working on any task, read `codemap.md` to understand:
 For deep work on a specific folder, also read that folder's `codemap.md`.
 ```
 
-This is idempotent  repeated codemap runs will detect the existing section and skip. No duplication.
+This is idempotent - repeated codemap runs will detect the existing section and skip. No duplication.
 
 ## Codemap Content
 

+ 10 - 10
src/skills/reflect/SKILL.md

@@ -35,7 +35,7 @@ repeated patterns, friction, and improvement opportunities.
 
 ### Session Discovery
 
-1. **Load recent sessions**  Query the SQLite database directly:
+1. **Load recent sessions** - Query the SQLite database directly:
    ```bash
    bun -e "import Database from 'bun:sqlite'; const db = new Database('/home/mhenke/.local/share/opencode/opencode.db'); console.log(db.query('SELECT id, directory, title, agent, model, time_created, cost, tokens_input, tokens_output FROM session ORDER BY time_created DESC LIMIT 50').all())"
    ```
@@ -43,7 +43,7 @@ repeated patterns, friction, and improvement opportunities.
 
    **Session table columns:** `id, directory, title, agent, model, time_created, cost, tokens_input, tokens_output`
 
-2. **Load session messages**  For each session ID, query the message table:
+2. **Load session messages** - For each session ID, query the message table:
    ```bash
    bun -e "import Database from 'bun:sqlite'; const db = new Database('/home/mhenke/.local/share/opencode/opencode.db'); console.log(db.query('SELECT data FROM message WHERE session_id = ?').all('ses_14de9c68effegtZtlATm42wnz7'))"
    ```
@@ -96,16 +96,16 @@ Store session summaries in `~/.config/opencode/oh-my-opencode-slim/reflections/s
 
 After analyzing all sessions, aggregate findings:
 
-1. **Group by theme**  sessions with similar frictions cluster together
-2. **Count frequency**  "42/50 sessions had repeated grep before editing"
-3. **Rank by impact**  prioritize recommendations that appear most often
-4. **Filter noise**  skip one-off issues, focus on repeated patterns
-5. **Cross-reference**  see if patterns correlate with specific models, agents, or repos
+1. **Group by theme** - sessions with similar frictions cluster together
+2. **Count frequency** - "42/50 sessions had repeated grep before editing"
+3. **Rank by impact** - prioritize recommendations that appear most often
+4. **Filter noise** - skip one-off issues, focus on repeated patterns
+5. **Cross-reference** - see if patterns correlate with specific models, agents, or repos
 
 **Scope categories:**
-- **Global**  applies to all repos (pattern seen in >50% of repos)
-- **Cross-repo**  applies to specific repos where pattern appears
-- **Project-specific**  only relevant to one repo
+- **Global** - applies to all repos (pattern seen in >50% of repos)
+- **Cross-repo** - applies to specific repos where pattern appears
+- **Project-specific** - only relevant to one repo
 
 ### Output Format
 

+ 6 - 6
src/skills/simplify/SKILL.md

@@ -7,7 +7,7 @@ description: Simplifies code for clarity without changing behavior. Use for read
 
 ## Overview
 
-Simplify code by reducing complexity while preserving exact behavior. The goal is not fewer lines  it's code that is easier to read, understand, modify, and debug. Every simplification must pass a simple test: "Would a new team member understand this faster than the original?"
+Simplify code by reducing complexity while preserving exact behavior. The goal is not fewer lines - it's code that is easier to read, understand, modify, and debug. Every simplification must pass a simple test: "Would a new team member understand this faster than the original?"
 
 ## When to Use
 
@@ -20,16 +20,16 @@ Simplify code by reducing complexity while preserving exact behavior. The goal i
 
 **When NOT to use:**
 
-- Code is already clean and readable  don't simplify for the sake of it
-- You don't understand what the code does yet  comprehend before you simplify
+- Code is already clean and readable - don't simplify for the sake of it
+- You don't understand what the code does yet - comprehend before you simplify
 - The code is performance-critical and the "simpler" version would be measurably slower
-- You're about to rewrite the module entirely  simplifying throwaway code wastes effort
+- You're about to rewrite the module entirely - simplifying throwaway code wastes effort
 
 ## The Five Principles
 
 ### 1. Preserve Behavior Exactly
 
-Don't change what the code does  only how it expresses it. All inputs, outputs, side effects, error behavior, and edge cases must remain identical. If you're not sure a simplification preserves behavior, don't make it.
+Don't change what the code does - only how it expresses it. All inputs, outputs, side effects, error behavior, and edge cases must remain identical. If you're not sure a simplification preserves behavior, don't make it.
 
 Before every change, ask:
 
@@ -48,7 +48,7 @@ Before simplifying:
 2. Study how neighboring code handles similar patterns
 3. Match the project's style for imports, naming, function style, error handling, and type annotations
 
-Simplification that breaks project consistency is not simplification  it's churn.
+Simplification that breaks project consistency is not simplification - it's churn.
 
 ### 3. Prefer Clarity Over Cleverness
 

+ 1 - 1
src/tools/council.test.ts

@@ -17,7 +17,7 @@ function createMockPluginContext() {
   } as any;
 }
 
-// Test mocks can omit 'model' field  it's filled by the manager, not the test
+// Test mocks can omit 'model' field - it's filled by the manager, not the test
 type TestCouncillorResult = {
   name: string;
   model?: string;

+ 2 - 2
src/tools/council.ts

@@ -105,14 +105,14 @@ Returns the councillor responses with a summary footer.`,
         const parts: string[] = [];
         if (hasMaster && legacyMasterModel) {
           parts.push(
-            `\`council.master\` is deprecated and will be removed in a future version. Its \`model\` is currently used as a fallback for the council agent  add a \`council\` entry to your preset to make this explicit.`,
+            `\`council.master\` is deprecated and will be removed in a future version. Its \`model\` is currently used as a fallback for the council agent - add a \`council\` entry to your preset to make this explicit.`,
           );
         }
         if (trulyIgnored.length > 0) {
           parts.push(
             `${trulyIgnored.map((f) => `\`council.${f}\``).join(', ')} ${
               trulyIgnored.length === 1 ? 'is' : 'are'
-            } deprecated and ignored  remove ${
+            } deprecated and ignored - remove ${
               trulyIgnored.length === 1 ? 'it' : 'them'
             } from your config.`,
           );

+ 1 - 1
src/tools/preset-manager.ts

@@ -29,7 +29,7 @@ const COMMAND_NAME = 'preset';
  * the agent registry is changing.
  */
 export function createPresetManager(ctx: PluginInput, config: PluginConfig) {
-  // Sync from module-level state in case of plugin re-init  the runtime
+  // Sync from module-level state in case of plugin re-init - the runtime
   // preset persists across dispose()/re-init cycles.
   let activePreset: string | null =
     getActiveRuntimePreset() ?? config.preset ?? null;

+ 3 - 3
src/utils/background-job-board.test.ts

@@ -737,7 +737,7 @@ describe('BackgroundJobBoard', () => {
       now: 1_000,
     });
 
-    // 4 seconds after launch  should show age annotation
+    // 4 seconds after launch - should show age annotation
     const prompt = board.formatForPrompt('parent-1', 5_000);
     expect(prompt).toContain('running [just launched, 4s ago]');
   });
@@ -752,7 +752,7 @@ describe('BackgroundJobBoard', () => {
       now: 1_000,
     });
 
-    // 39 seconds after launch  age label should be absent
+    // 39 seconds after launch - age label should be absent
     const prompt = board.formatForPrompt('parent-1', 40_000);
     expect(prompt).not.toContain('just launched');
     expect(prompt).toContain('/ running\n');
@@ -807,7 +807,7 @@ describe('BackgroundJobBoard', () => {
       now: 5_000,
     });
 
-    // 4 seconds after relaunch  should show [resumed, 4s ago]
+    // 4 seconds after relaunch - should show [resumed, 4s ago]
     const prompt = board.formatForPrompt('parent-1', 9_000);
     expect(prompt).toContain('running [resumed, 4s ago]');
   });

+ 5 - 5
src/utils/logger.ts

@@ -35,7 +35,7 @@ function cleanupOldLogs(logDir: string): void {
       }
     }
   } catch {
-    // Directory may not exist yet  that's fine
+    // Directory may not exist yet - that's fine
   }
 
   // Apply the same 7-day retention to persisted background task files
@@ -56,7 +56,7 @@ function cleanupOldLogs(logDir: string): void {
       }
     }
   } catch {
-    // bg-tasks dir may not exist yet  that's fine
+    // bg-tasks dir may not exist yet - that's fine
   }
 }
 
@@ -65,13 +65,13 @@ export function initLogger(sessionId: string): void {
   try {
     fs.mkdirSync(dir, { recursive: true });
   } catch {
-    // Directory creation failed  logging will silently fail
+    // Directory creation failed - logging will silently fail
   }
   logFile = path.join(dir, `${LOG_PREFIX}${sessionId}${LOG_SUFFIX}`);
   try {
     fs.closeSync(fs.openSync(logFile, 'a'));
   } catch {
-    // File creation failed  later writes will silently fail
+    // File creation failed - later writes will silently fail
   }
   cleanupOldLogs(dir);
 }
@@ -90,7 +90,7 @@ export async function flushLoggerForTesting(): Promise<void> {
 }
 export function log(message: string, data?: unknown): void {
   const target = logFile;
-  if (!target) return; // Uninitialized  silently no-op
+  if (!target) return; // Uninitialized - silently no-op
   try {
     const timestamp = new Date().toISOString();
     let dataStr = '';

+ 1 - 1
src/utils/session.ts

@@ -158,7 +158,7 @@ export async function promptWithTimeout(
 
 /**
  * Result of extracting session content.
- * `empty` is true when the assistant produced zero text content 
+ * `empty` is true when the assistant produced zero text content -
  * the provider returned an empty response (e.g. rate-limited silently).
  */
 export interface SessionExtractionResult {

+ 2 - 2
src/utils/system-collapse.test.ts

@@ -19,7 +19,7 @@ describe('collapseSystemInPlace', () => {
 
     collapseSystemInPlace(output.system);
 
-    // Same reference  callers holding the original array see the change
+    // Same reference - callers holding the original array see the change
     expect(output.system).toBe(system);
     expect(system).toHaveLength(1);
     expect(system[0]).toBe('part one\n\npart two');
@@ -79,7 +79,7 @@ describe('collapseSystemInPlace', () => {
     // The output object sees the new array...
     expect(output.system).toEqual(['a\n\nb']);
 
-    // ...but the original reference is untouched  this is the bug.
+    // ...but the original reference is untouched - this is the bug.
     expect(system).toEqual(['a', 'b']);
     expect(system).not.toBe(output.system);
   });