From 509525dd051a59d6bd0ec40b0eedfe420db9c240 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Tue, 3 Nov 2026 09:45:46 +0530 Subject: [PATCH] fix(server): preserve Claude goals that mention skills --- .../src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts | 7 +++++++ .../src/orchestration-v2/Adapters/ClaudeAdapterV2.ts | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts index d7d03c779b4bb..ea1053bdb64aa 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts @@ -18765,6 +18765,13 @@ describe("ClaudeAdapterV2 background wake turns", () => { ); describe("native goals", () => { + it("preserves a multiline /goal as a native command when its objective mentions a skill", () => { + const skills = new Map([["review", { path: "/home/me/.claude/skills/review/SKILL.md" }]]); + const text = "/goal fix all the gaps.\ndecompile target.ipa into ./target/\n$review"; + const message = ClaudeAdapterV2.makeClaudeUserMessage({ text, skills }); + assert.equal(message.message.content, text); + }); + const syntheticFrame = (uuid: string, text: string) => claudeSdkFrame({ type: "assistant", diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts index df80f1316fb77..1f2c0ce7d491d 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts @@ -96,6 +96,7 @@ import { TrimmedNonEmptyString, defaultInstanceIdForDriver, isCompactionCommandMessage, + parseGoalCommand, type ModelSelection, type OrchestrationV2ConversationMessage, type OrchestrationV2ExecutionNode, @@ -2383,8 +2384,11 @@ export function makeClaudeUserMessage(input: { }): SDKUserMessage { // Claude expands a skill only when `/name` starts the last text block. // Keep the whole request before that block; see ClaudeSkillDispatch. + // Keep /goal on the native command path instead of dispatching a skill in its objective. const dispatch = - input.skills === undefined ? undefined : planClaudeSkillDispatch(input.text, input.skills); + input.skills === undefined || parseGoalCommand(input) !== null + ? undefined + : planClaudeSkillDispatch(input.text, input.skills); return { type: "user", message: {