diff --git a/src/hooks/start-work/parse-user-request.test.ts b/src/hooks/start-work/parse-user-request.test.ts
new file mode 100644
index 00000000..e5d61a4c
--- /dev/null
+++ b/src/hooks/start-work/parse-user-request.test.ts
@@ -0,0 +1,78 @@
+///
+
+import { describe, expect, test } from "bun:test"
+import { parseUserRequest } from "./parse-user-request"
+
+describe("parseUserRequest", () => {
+ describe("when no user-request tag", () => {
+ test("#given prompt without tag #when parsing #then returns nulls", () => {
+ const result = parseUserRequest("Just a regular message without any tags")
+ expect(result.planName).toBeNull()
+ expect(result.explicitWorktreePath).toBeNull()
+ })
+ })
+
+ describe("when user-request tag is empty", () => {
+ test("#given empty user-request tag #when parsing #then returns nulls", () => {
+ const result = parseUserRequest(" ")
+ expect(result.planName).toBeNull()
+ expect(result.explicitWorktreePath).toBeNull()
+ })
+ })
+
+ describe("when only plan name given", () => {
+ test("#given plan name without worktree flag #when parsing #then returns plan name with null worktree", () => {
+ const result = parseUserRequest("\nmy-plan\n")
+ expect(result.planName).toBe("my-plan")
+ expect(result.explicitWorktreePath).toBeNull()
+ })
+ })
+
+ describe("when only --worktree flag given", () => {
+ test("#given --worktree with path only #when parsing #then returns worktree path with null plan", () => {
+ const result = parseUserRequest("--worktree /home/user/repo-feat")
+ expect(result.planName).toBeNull()
+ expect(result.explicitWorktreePath).toBe("/home/user/repo-feat")
+ })
+ })
+
+ describe("when plan name and --worktree are both given", () => {
+ test("#given plan name before --worktree #when parsing #then returns both", () => {
+ const result = parseUserRequest("my-plan --worktree /path/to/worktree")
+ expect(result.planName).toBe("my-plan")
+ expect(result.explicitWorktreePath).toBe("/path/to/worktree")
+ })
+
+ test("#given --worktree before plan name #when parsing #then returns both", () => {
+ const result = parseUserRequest("--worktree /path/to/worktree my-plan")
+ expect(result.planName).toBe("my-plan")
+ expect(result.explicitWorktreePath).toBe("/path/to/worktree")
+ })
+ })
+
+ describe("when --worktree flag has no path", () => {
+ test("#given --worktree without path #when parsing #then worktree path is null", () => {
+ const result = parseUserRequest("--worktree")
+ expect(result.explicitWorktreePath).toBeNull()
+ })
+ })
+
+ describe("when ultrawork keywords are present", () => {
+ test("#given plan name with ultrawork keyword #when parsing #then strips keyword from plan name", () => {
+ const result = parseUserRequest("my-plan ultrawork")
+ expect(result.planName).toBe("my-plan")
+ })
+
+ test("#given plan name with ulw keyword and worktree #when parsing #then strips ulw, preserves worktree", () => {
+ const result = parseUserRequest("my-plan ulw --worktree /path/to/wt")
+ expect(result.planName).toBe("my-plan")
+ expect(result.explicitWorktreePath).toBe("/path/to/wt")
+ })
+
+ test("#given only ultrawork keyword with worktree #when parsing #then plan name is null, worktree preserved", () => {
+ const result = parseUserRequest("ultrawork --worktree /wt")
+ expect(result.planName).toBeNull()
+ expect(result.explicitWorktreePath).toBe("/wt")
+ })
+ })
+})
diff --git a/src/hooks/start-work/parse-user-request.ts b/src/hooks/start-work/parse-user-request.ts
new file mode 100644
index 00000000..627deb67
--- /dev/null
+++ b/src/hooks/start-work/parse-user-request.ts
@@ -0,0 +1,29 @@
+const KEYWORD_PATTERN = /\b(ultrawork|ulw)\b/gi
+const WORKTREE_FLAG_PATTERN = /--worktree(?:\s+(\S+))?/
+
+export interface ParsedUserRequest {
+ planName: string | null
+ explicitWorktreePath: string | null
+}
+
+export function parseUserRequest(promptText: string): ParsedUserRequest {
+ const match = promptText.match(/\s*([\s\S]*?)\s*<\/user-request>/i)
+ if (!match) return { planName: null, explicitWorktreePath: null }
+
+ let rawArg = match[1].trim()
+ if (!rawArg) return { planName: null, explicitWorktreePath: null }
+
+ const worktreeMatch = rawArg.match(WORKTREE_FLAG_PATTERN)
+ const explicitWorktreePath = worktreeMatch ? (worktreeMatch[1] ?? null) : null
+
+ if (worktreeMatch) {
+ rawArg = rawArg.replace(worktreeMatch[0], "").trim()
+ }
+
+ const cleanedArg = rawArg.replace(KEYWORD_PATTERN, "").trim()
+
+ return {
+ planName: cleanedArg || null,
+ explicitWorktreePath,
+ }
+}