Browse Source

Remove goal command feature

Alvin Unreal 2 months ago
parent
commit
9b8f72716c

+ 0 - 1
README.ja-JP.md

@@ -501,7 +501,6 @@ ping all agents
 | **[Council](docs/council.md)** | 複数のモデルを並列実行し、`@council` で 1 つの回答に統合します |
 | **[Multiplexer Integration](docs/multiplexer-integration.md)** | エージェントの動作を Tmux や Zellij のペインでライブ表示します |
 | **[Session Management](docs/session-management.md)** | 短いエイリアスで最近の子エージェントセッションを再利用し、最初からやり直さずに済みます |
-| **[Session Goal](docs/session-goal.md)** | `/goal` でセッションの目標をピン留めし、TODO・委譲・検証の整合性を保ちます |
 | **[Todo Continuation](docs/todo-continuation.md)** | クールダウンと安全チェック付きで Orchestrator セッションを自動継続します |
 | **[Preset Switching](docs/preset-switching.md)** | `/preset` で実行時にエージェントモデルのプリセットを切り替えます |
 | **[Custom Agents](docs/configuration.md#custom-agents)** | カスタムプロンプト、モデル、MCP アクセス、Orchestrator の委譲ルールを備えた独自の専門エージェントを定義します |

+ 0 - 1
README.md

@@ -503,7 +503,6 @@ Use this section as a map: start with installation, then jump to features, confi
 | **[V2 Background Orchestration](docs/v2-background-orchestration.md)** | Scheduler-first orchestrator model built around native background subagents |
 | **[Multiplexer Integration](docs/multiplexer-integration.md)** | Watch agents work live in Tmux or Zellij panes |
 | **[Session Management](docs/session-management.md)** | Reuse recent child-agent sessions with short aliases instead of starting over |
-| **[Goal](docs/goal.md)** | Pin an objective with `/goal` so todos, delegation, and verification stay aligned |
 | **[Todo Continuation](docs/todo-continuation.md)** | Auto-continue orchestrator sessions with cooldowns and safety checks |
 | **[Preset Switching](docs/preset-switching.md)** | Switch agent model presets at runtime with `/preset` |
 | **[Custom Agents](docs/configuration.md#custom-agents)** | Define your own specialists with custom prompts, models, MCP access, and Orchestrator delegation rules |

+ 0 - 1
README.zh-CN.md

@@ -495,7 +495,6 @@ ping all agents
 | **[Council (议会) (docs/council.md)](docs/council.md)** | 使用 `@council` 并行运行多个模型并合成单一答案 |
 | **[多路复用器集成 (docs/multiplexer-integration.md)](docs/multiplexer-integration.md)** | 在 Tmux 或 Zellij 窗格中实时观看智能体的工作过程 |
 | **[会话管理 (docs/session-management.md)](docs/session-management.md)** | 使用短别名复用最近的子智能体会话,而不是重新开始 |
-| **[会话目标 (docs/session-goal.md)](docs/session-goal.md)** | 用 `/goal` 固定会话目标,以确保待办事项、委派和验证保持一致 |
 | **[待办事项持续执行 (docs/todo-continuation.md)](docs/todo-continuation.md)** | 具备冷却时间和安全检查的编排者会话自动持续执行 |
 | **[运行时预设切换 (docs/preset-switching.md)](docs/preset-switching.md)** | 在运行时使用 `/preset` 切换智能体模型预设 |
 | **[自定义智能体 (docs/configuration.md#custom-agents)](docs/configuration.md#custom-agents)** | 自定义专家智能体:配置独特的提示词、模型、MCP 权限和编排者委派规则 |

+ 0 - 51
docs/goal.md

@@ -1,51 +0,0 @@
-# Goal
-
-`/goal` pins a session-scoped objective so long work keeps a clear north star.
-
-Use it when the task is bigger than one prompt and has a clear success condition,
-but you do not want a separate project-management system.
-
-## Commands
-
-| Command | Description |
-|---------|-------------|
-| `/goal <objective>` | Set the current goal |
-| `/goal` | Show the active goal and how it relates to todos and auto-continuation |
-| `/goal clear` | Clear the current goal |
-| `/goal from <interview>` | Set the goal from an existing interview markdown spec |
-
-Examples:
-
-```text
-/goal Add lightweight goals. Done when UX, docs, and tests are complete.
-/goal from kanban-design-tool
-/goal clear
-```
-
-## How it fits with other features
-
-```text
-Interview → Goal → Todos → Auto-continuation → Delegation → Verify
-```
-
-- **Goal** is the why and definition of done.
-- **Todos** are the execution ledger.
-- **Auto-continuation** keeps executing unfinished todos when enabled.
-- **Interview** turns a rough idea into a markdown spec that can become a goal.
-- **Task/delegation** inherits the parent goal as context, while each
-  delegated prompt remains the bounded task.
-
-## Important behavior
-
-Goal does not run anything by itself. It only reminds the orchestrator and child
-sessions what the session is trying to achieve.
-
-Auto-continuation remains todo-driven:
-
-```text
-Goal alone never causes continuation.
-Only incomplete todos trigger auto-continuation.
-```
-
-This keeps the feature slim: one pinned objective, no dashboard, no second todo
-system, and no project-global state.

+ 0 - 9
docs/interview.md

@@ -42,15 +42,6 @@ You can also resume by basename if it exists in the configured output folder:
 /interview kanban-design-tool
 ```
 
-Promote an interview spec into the current goal:
-
-```text
-/goal from kanban-design-tool
-```
-
-This uses the interview title and `Current spec` section as the pinned goal, so
-todos, delegation, and verification stay aligned with the clarified spec.
-
 ## What the browser UI gives you
 
 - focused question flow instead of open-ended chat

+ 0 - 4
docs/todo-continuation.md

@@ -2,10 +2,6 @@
 
 Auto-continue the orchestrator when it stops with incomplete todos. Opt-in only — nothing resumes automatically unless you enable it.
 
-If a [Goal](goal.md) is active, auto-continuation resumes under that
-goal, but the goal does not trigger continuation by itself. Only incomplete
-todos do.
-
 ## Controls
 
 | Tool / Command | Description |

+ 0 - 14
docs/tools.md

@@ -64,17 +64,3 @@ Auto-continue has its own guide now:
 - [Todo Continuation](todo-continuation.md) — controls, safety gates, behavior, and config
 
 ---
-
-## Goal
-
-Pin a goal that keeps planning, todos, delegation, and
-verification aligned.
-
-| Command | Description |
-|---------|-------------|
-| `/goal <objective>` | Set the current goal |
-| `/goal` | Show the active goal |
-| `/goal clear` | Clear the active goal |
-| `/goal from <interview>` | Promote an interview markdown spec into the active goal |
-
-See [Goal](goal.md) for the full workflow.

+ 0 - 1
docs/v2-workstreams.md

@@ -7,7 +7,6 @@ there, and publish npm `beta` releases from there.
 | Branch | Worktree | Purpose | Status | Notes |
 |---|---|---|---|---|
 | `v2-beta` | repo root | V2 integration/release | Active | Source of truth for combined V2 testing and `@beta` publishes. |
-| `v2/goal-rename` | `.slim/worktrees/v2-goal-rename` | Rename session goal to goal | Merged | Can prune when no follow-up is needed. |
 | `v2/misc` | `.slim/worktrees/v2-misc` | Misc V2 cleanup | Merged | Removed custom subtask feature; can continue misc follow-ups here if desired. |
 | `v2/tui` | `.slim/worktrees/v2-tui` | TUI integration | Planned | No feature work merged yet. |
 

+ 1 - 1
src/hooks/deepwork/index.test.ts

@@ -69,7 +69,7 @@ describe('deepwork command hook', () => {
     const output = { parts: [{ type: 'text', text: 'template' }] };
 
     await hook.handleCommandExecuteBefore(
-      { command: 'goal', sessionID: 's1', arguments: 'x' },
+      { command: 'preset', sessionID: 's1', arguments: 'x' },
       output,
     );
 

+ 0 - 231
src/hooks/goal/index.test.ts

@@ -1,231 +0,0 @@
-import { describe, expect, test } from 'bun:test';
-import { mkdir, mkdtemp, writeFile } from 'node:fs/promises';
-import { tmpdir } from 'node:os';
-import path from 'node:path';
-import { createGoalHook } from './index';
-
-function createHook(directory = '.') {
-  return createGoalHook(
-    { directory } as Parameters<typeof createGoalHook>[0],
-    { interview: { outputFolder: 'interview' } } as Parameters<
-      typeof createGoalHook
-    >[1],
-    { getAgentName: () => 'orchestrator' },
-  );
-}
-
-describe('createGoalHook', () => {
-  test('sets and shows a manual goal', async () => {
-    const hook = createHook();
-    const output = { parts: [] as Array<{ type: string; text?: string }> };
-
-    await hook.handleCommandExecuteBefore(
-      {
-        command: 'goal',
-        sessionID: 'ses_1',
-        arguments: 'Ship the goal feature. Done when tests pass.',
-      },
-      output,
-    );
-
-    expect(output.parts[0].text).toContain('Set active goal:');
-    expect(hook.getGoal('ses_1')?.text).toBe(
-      'Ship the goal feature. Done when tests pass.',
-    );
-
-    const showOutput = { parts: [] as Array<{ type: string; text?: string }> };
-    await hook.handleCommandExecuteBefore(
-      { command: 'goal', sessionID: 'ses_1', arguments: '' },
-      showOutput,
-    );
-
-    expect(showOutput.parts[0].text).toContain('Active goal:');
-    expect(showOutput.parts[0].text).toContain('Auto-continuation');
-  });
-
-  test('injects active goal into orchestrator system prompt', async () => {
-    const hook = createHook();
-    await hook.handleCommandExecuteBefore(
-      { command: 'goal', sessionID: 'ses_1', arguments: 'Stay on target.' },
-      { parts: [] },
-    );
-    const output = { system: ['base prompt'] };
-
-    hook.handleSystemTransform({ sessionID: 'ses_1' }, output);
-
-    expect(output.system.join('\n')).toContain('<active_goal>');
-    expect(output.system.join('\n')).toContain('Stay on target.');
-    expect(output.system.join('\n')).toContain(
-      'Use todos as the execution ledger',
-    );
-  });
-
-  test('inherits parent goal for child sessions', async () => {
-    const hook = createGoalHook(
-      { directory: '.' } as Parameters<typeof createGoalHook>[0],
-      {} as Parameters<typeof createGoalHook>[1],
-      { getAgentName: () => 'explorer' },
-    );
-    await hook.handleCommandExecuteBefore(
-      { command: 'goal', sessionID: 'parent', arguments: 'Parent objective.' },
-      { parts: [] },
-    );
-
-    hook.handleEvent({
-      event: {
-        type: 'session.created',
-        properties: { info: { id: 'child', parentID: 'parent' } },
-      },
-    });
-
-    const output = { system: [] as string[] };
-    hook.handleSystemTransform({ sessionID: 'child' }, output);
-
-    expect(output.system.join('\n')).toContain('<parent_goal>');
-    expect(output.system.join('\n')).toContain('Parent objective.');
-    expect(output.system.join('\n')).toContain('bounded task');
-  });
-
-  test('child sessions resolve updated parent goal live', async () => {
-    const hook = createGoalHook(
-      { directory: '.' } as Parameters<typeof createGoalHook>[0],
-      {} as Parameters<typeof createGoalHook>[1],
-      { getAgentName: () => 'explorer' },
-    );
-    await hook.handleCommandExecuteBefore(
-      { command: 'goal', sessionID: 'parent', arguments: 'Original.' },
-      { parts: [] },
-    );
-    hook.handleEvent({
-      event: {
-        type: 'session.created',
-        properties: { info: { id: 'child', parentID: 'parent' } },
-      },
-    });
-    await hook.handleCommandExecuteBefore(
-      { command: 'goal', sessionID: 'parent', arguments: 'Updated.' },
-      { parts: [] },
-    );
-
-    const output = { system: [] as string[] };
-    hook.handleSystemTransform({ sessionID: 'child' }, output);
-
-    expect(output.system.join('\n')).toContain('Updated.');
-    expect(output.system.join('\n')).not.toContain('Original.');
-  });
-
-  test('grandchild sessions inherit the root goal', async () => {
-    const hook = createGoalHook(
-      { directory: '.' } as Parameters<typeof createGoalHook>[0],
-      {} as Parameters<typeof createGoalHook>[1],
-      { getAgentName: () => 'explorer' },
-    );
-    await hook.handleCommandExecuteBefore(
-      { command: 'goal', sessionID: 'root', arguments: 'Root objective.' },
-      { parts: [] },
-    );
-    hook.handleEvent({
-      event: {
-        type: 'session.created',
-        properties: { info: { id: 'child', parentID: 'root' } },
-      },
-    });
-    hook.handleEvent({
-      event: {
-        type: 'session.created',
-        properties: { info: { id: 'grandchild', parentID: 'child' } },
-      },
-    });
-
-    const output = { system: [] as string[] };
-    hook.handleSystemTransform({ sessionID: 'grandchild' }, output);
-
-    expect(output.system.join('\n')).toContain('<parent_goal>');
-    expect(output.system.join('\n')).toContain('Root objective.');
-    expect(output.system.join('\n')).not.toContain('Objective: \n');
-  });
-
-  test('child sessions stop inheriting after parent goal is cleared', async () => {
-    const hook = createGoalHook(
-      { directory: '.' } as Parameters<typeof createGoalHook>[0],
-      {} as Parameters<typeof createGoalHook>[1],
-      { getAgentName: () => 'explorer' },
-    );
-    await hook.handleCommandExecuteBefore(
-      { command: 'goal', sessionID: 'parent', arguments: 'Parent objective.' },
-      { parts: [] },
-    );
-    hook.handleEvent({
-      event: {
-        type: 'session.created',
-        properties: { info: { id: 'child', parentID: 'parent' } },
-      },
-    });
-    await hook.handleCommandExecuteBefore(
-      { command: 'goal', sessionID: 'parent', arguments: 'clear' },
-      { parts: [] },
-    );
-
-    const output = { system: [] as string[] };
-    hook.handleSystemTransform({ sessionID: 'child' }, output);
-
-    expect(output.system).toEqual([]);
-    expect(hook.getGoal('child')).toBeUndefined();
-  });
-
-  test('sets goal from an interview document', async () => {
-    const directory = await mkdtemp(path.join(tmpdir(), 'goal-test-'));
-    const interviewDir = path.join(directory, 'interview');
-    await mkdir(interviewDir, { recursive: true });
-    await writeFile(
-      path.join(interviewDir, 'feature.md'),
-      [
-        '# Feature Goal',
-        '',
-        '## Current spec',
-        '',
-        'Build the feature with minimal scope.',
-        '',
-        '## Q&A history',
-        '',
-        'No answers yet.',
-      ].join('\n'),
-      'utf8',
-    );
-
-    const hook = createHook(directory);
-    const output = { parts: [] as Array<{ type: string; text?: string }> };
-
-    await hook.handleCommandExecuteBefore(
-      { command: 'goal', sessionID: 'ses_1', arguments: 'from feature' },
-      output,
-    );
-
-    expect(output.parts[0].text).toContain('Set active goal from interview');
-    expect(hook.getGoal('ses_1')?.text).toBe(
-      'From interview: Feature Goal\n\nBuild the feature with minimal scope.',
-    );
-  });
-
-  test('clears goals on command and session deletion', async () => {
-    const hook = createHook();
-    await hook.handleCommandExecuteBefore(
-      { command: 'goal', sessionID: 'ses_1', arguments: 'Temporary goal.' },
-      { parts: [] },
-    );
-    await hook.handleCommandExecuteBefore(
-      { command: 'goal', sessionID: 'ses_1', arguments: 'clear' },
-      { parts: [] },
-    );
-    expect(hook.getGoal('ses_1')).toBeUndefined();
-
-    await hook.handleCommandExecuteBefore(
-      { command: 'goal', sessionID: 'ses_1', arguments: 'Temporary goal.' },
-      { parts: [] },
-    );
-    hook.handleEvent({
-      event: { type: 'session.deleted', properties: { sessionID: 'ses_1' } },
-    });
-    expect(hook.getGoal('ses_1')).toBeUndefined();
-  });
-});

+ 0 - 243
src/hooks/goal/index.ts

@@ -1,243 +0,0 @@
-import * as fs from 'node:fs/promises';
-import type { PluginInput } from '@opencode-ai/plugin';
-import type { PluginConfig } from '../../config';
-import {
-  extractSummarySection,
-  extractTitle,
-  resolveExistingInterviewPath,
-} from '../../interview/document';
-import { createInternalAgentTextPart } from '../../utils';
-
-const COMMAND_NAME = 'goal';
-const MAX_GOAL_LENGTH = 4000;
-
-interface GoalState {
-  text: string;
-  source?: 'manual' | 'interview';
-  sourcePath?: string;
-  inheritedFrom?: string;
-  createdAt: number;
-}
-
-interface SystemTransformOutput {
-  system: string[];
-}
-
-function normalizeGoalText(text: string): string {
-  return text.trim().replace(/\s+/g, ' ').slice(0, MAX_GOAL_LENGTH);
-}
-
-function trimGoalText(text: string): string {
-  return text.trim().slice(0, MAX_GOAL_LENGTH);
-}
-
-function pushText(
-  output: { parts: Array<{ type: string; text?: string }> },
-  text: string,
-) {
-  output.parts.push(createInternalAgentTextPart(text));
-}
-
-function formatGoal(state: GoalState, inherited: boolean): string {
-  const tag = inherited ? 'parent_goal' : 'active_goal';
-  const guidance = inherited
-    ? 'This is context only. Your delegated prompt remains the bounded task.'
-    : 'Use todos as the execution ledger. Keep planning, delegation, edits, and verification aligned to this goal. Do not broaden scope unless the user changes the goal.';
-  return `<${tag}>\nObjective: ${state.text}\n${guidance}\n</${tag}>`;
-}
-
-async function readInterviewGoal(
-  directory: string,
-  outputFolder: string,
-  value: string,
-): Promise<{ text: string; sourcePath: string } | null> {
-  try {
-    const sourcePath = resolveExistingInterviewPath(
-      directory,
-      outputFolder,
-      value,
-    );
-    if (!sourcePath) return null;
-
-    const content = await fs.readFile(sourcePath, 'utf8');
-    const title = extractTitle(content);
-    const summary = extractSummarySection(content);
-    const text = trimGoalText(
-      [title ? `From interview: ${title}` : '', summary]
-        .filter(Boolean)
-        .join('\n\n'),
-    );
-    return text ? { text, sourcePath } : null;
-  } catch {
-    return null;
-  }
-}
-
-function resolveGoal(
-  goals: Map<string, GoalState>,
-  sessionID: string,
-): { goal: GoalState; inherited: boolean } | null {
-  const seen = new Set<string>();
-  let currentSessionID = sessionID;
-  let inherited = false;
-
-  while (true) {
-    if (seen.has(currentSessionID)) {
-      goals.delete(sessionID);
-      return null;
-    }
-    seen.add(currentSessionID);
-
-    const goal = goals.get(currentSessionID);
-    if (!goal) {
-      goals.delete(sessionID);
-      return null;
-    }
-
-    if (!goal.inheritedFrom) {
-      return { goal, inherited };
-    }
-
-    inherited = true;
-    currentSessionID = goal.inheritedFrom;
-  }
-}
-
-export function createGoalHook(
-  ctx: PluginInput,
-  config: PluginConfig,
-  options?: { getAgentName?: (sessionID: string) => string | undefined },
-): {
-  registerCommand: (config: Record<string, unknown>) => void;
-  handleCommandExecuteBefore: (
-    input: { command: string; sessionID: string; arguments: string },
-    output: { parts: Array<{ type: string; text?: string }> },
-  ) => Promise<void>;
-  handleEvent: (input: {
-    event: { type: string; properties?: Record<string, unknown> };
-  }) => void;
-  handleSystemTransform: (
-    input: { sessionID?: string },
-    output: SystemTransformOutput,
-  ) => void;
-  getGoal: (sessionID: string) => GoalState | undefined;
-} {
-  const goals = new Map<string, GoalState>();
-  const outputFolder = config.interview?.outputFolder ?? 'interview';
-
-  return {
-    registerCommand: (opencodeConfig) => {
-      const commandConfig = opencodeConfig.command as
-        | Record<string, unknown>
-        | undefined;
-      if (commandConfig?.[COMMAND_NAME]) return;
-      if (!opencodeConfig.command) opencodeConfig.command = {};
-      (opencodeConfig.command as Record<string, unknown>)[COMMAND_NAME] = {
-        template: 'Set or show the current goal',
-        description:
-          'Pin a session objective that keeps todos, delegation, and verification aligned',
-      };
-    },
-
-    handleCommandExecuteBefore: async (input, output) => {
-      if (input.command !== COMMAND_NAME) return;
-
-      output.parts.length = 0;
-
-      const args = input.arguments.trim();
-      if (!args) {
-        const resolved = resolveGoal(goals, input.sessionID);
-        pushText(
-          output,
-          resolved
-            ? `Active goal:\n${resolved.goal.text}\n\nUse todos for execution steps. Auto-continuation continues only while todos remain.`
-            : 'No active goal. Set one with /goal <objective>.',
-        );
-        return;
-      }
-
-      if (args === 'clear') {
-        goals.delete(input.sessionID);
-        pushText(output, 'Cleared the active goal for this session.');
-        return;
-      }
-
-      if (args.startsWith('from ')) {
-        const value = args.slice('from '.length).trim();
-        const interviewGoal = await readInterviewGoal(
-          ctx.directory,
-          outputFolder,
-          value,
-        );
-        if (!interviewGoal) {
-          pushText(
-            output,
-            `Could not find a readable interview spec for "${value}".`,
-          );
-          return;
-        }
-        goals.set(input.sessionID, {
-          text: interviewGoal.text,
-          source: 'interview',
-          sourcePath: interviewGoal.sourcePath,
-          createdAt: Date.now(),
-        });
-        pushText(
-          output,
-          `Set active goal from interview:\n${interviewGoal.text}`,
-        );
-        return;
-      }
-
-      const text = normalizeGoalText(args);
-      goals.set(input.sessionID, {
-        text,
-        source: 'manual',
-        createdAt: Date.now(),
-      });
-      pushText(output, `Set active goal:\n${text}`);
-    },
-
-    handleEvent: (input) => {
-      const event = input.event;
-      if (event.type === 'session.created') {
-        const info = event.properties?.info as
-          | { id?: string; parentID?: string }
-          | undefined;
-        if (!info?.id || !info.parentID) return;
-        const parentGoal = goals.get(info.parentID);
-        if (!parentGoal) return;
-        goals.set(info.id, {
-          inheritedFrom: info.parentID,
-          createdAt: Date.now(),
-          text: '',
-        });
-        return;
-      }
-
-      if (event.type === 'session.deleted') {
-        const props = event.properties as
-          | { info?: { id?: string }; sessionID?: string }
-          | undefined;
-        const sessionID = props?.info?.id ?? props?.sessionID;
-        if (sessionID) goals.delete(sessionID);
-      }
-    },
-
-    handleSystemTransform: (input, output) => {
-      if (!input.sessionID) return;
-      const resolved = resolveGoal(goals, input.sessionID);
-      if (!resolved) return;
-
-      const agentName = options?.getAgentName?.(input.sessionID);
-      const { goal, inherited } = resolved;
-      if (!inherited && agentName && agentName !== 'orchestrator') return;
-
-      const block = formatGoal(goal, inherited);
-      if (output.system.some((entry) => entry.includes(block))) return;
-      output.system.push(block);
-    },
-
-    getGoal: (sessionID) => resolveGoal(goals, sessionID)?.goal,
-  };
-}

+ 0 - 1
src/hooks/index.ts

@@ -9,7 +9,6 @@ export {
   ForegroundFallbackManager,
   isRateLimitError,
 } from './foreground-fallback';
-export { createGoalHook } from './goal';
 export { processImageAttachments } from './image-hook';
 export { createJsonErrorRecoveryHook } from './json-error-recovery';
 export { createPhaseReminderHook } from './phase-reminder';

+ 0 - 23
src/index.ts

@@ -23,7 +23,6 @@ import {
   createDeepworkCommandHook,
   createDelegateTaskRetryHook,
   createFilterAvailableSkillsHook,
-  createGoalHook,
   createJsonErrorRecoveryHook,
   createPhaseReminderHook,
   createPostFileToolNudgeHook,
@@ -138,7 +137,6 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
   let foregroundFallback: ForegroundFallbackManager;
   let todoContinuationHook: ReturnType<typeof createTodoContinuationHook>;
   let deepworkCommandHook: ReturnType<typeof createDeepworkCommandHook>;
-  let goalHook: ReturnType<typeof createGoalHook>;
   let taskSessionManagerHook: ReturnType<typeof createTaskSessionManagerHook>;
   let backgroundJobBoard: BackgroundJobBoard;
   let interviewManager: ReturnType<typeof createInterviewManager>;
@@ -313,9 +311,6 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
       backgroundJobBoard,
     });
     deepworkCommandHook = createDeepworkCommandHook();
-    goalHook = createGoalHook(ctx, config, {
-      getAgentName: (sessionID) => sessionAgentMap.get(sessionID),
-    });
     taskSessionManagerHook = createTaskSessionManagerHook(ctx, {
       maxSessionsPerAgent: config.sessionManager?.maxSessionsPerAgent ?? 2,
       readContextMinLines: config.sessionManager?.readContextMinLines ?? 10,
@@ -744,7 +739,6 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
       }
 
       interviewManager.registerCommand(opencodeConfig);
-      goalHook.registerCommand(opencodeConfig);
       deepworkCommandHook.registerCommand(opencodeConfig);
       presetManager.registerCommand(opencodeConfig);
     },
@@ -807,12 +801,6 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
       // Todo-continuation: auto-continue orchestrator on incomplete todos
       await todoContinuationHook.handleEvent(input);
 
-      goalHook.handleEvent(
-        input as {
-          event: { type: string; properties?: Record<string, unknown> };
-        },
-      );
-
       // Handle auto-update checking
       await autoUpdateChecker.event(input);
 
@@ -961,15 +949,6 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
         output as { parts: Array<{ type: string; text?: string }> },
       );
 
-      await goalHook.handleCommandExecuteBefore(
-        input as {
-          command: string;
-          sessionID: string;
-          arguments: string;
-        },
-        output as { parts: Array<{ type: string; text?: string }> },
-      );
-
       await deepworkCommandHook.handleCommandExecuteBefore(
         input as {
           command: string;
@@ -1050,8 +1029,6 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
         }
       }
 
-      goalHook.handleSystemTransform(input, output);
-
       // Collapse to single system message for provider compatibility.
       // Some providers (e.g. Qwen via VLLM/DashScope) reject multiple
       // system messages. Sub-hooks above may push additional entries; join