File size: 3,475 Bytes
837e3ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import type {
  ChatProvider,
  ParsedCommand,
  ResolvedChatContext,
  WorkPackage,
} from "./work-package-types";

const SLASH_COMMAND = /^\/(ask|plan|change|execute)\b\s*/i;

export function resolveChatRequestContext(args: {
  parsed: ParsedCommand;
  detailOpen: boolean;
  selectedWorkPackage?: Pick<WorkPackage, "id" | "title" | "shortName">;
}) {
  const { parsed, detailOpen, selectedWorkPackage } = args;
  const hasExplicitCommand =
    Boolean(parsed.mode) || Boolean(parsed.referencedPackageName);

  if (hasExplicitCommand) {
    return {
      parsedCommand: parsed,
      shouldAutomateBoard: false,
      source: "explicit-command" as const,
    };
  }

  const slashMatch = parsed.instruction.match(SLASH_COMMAND);
  if (slashMatch && selectedWorkPackage) {
    return {
      parsedCommand: {
        referencedPackageName: selectedWorkPackage.title,
        mode: slashMatch[1].toLowerCase() as "ask" | "plan" | "change" | "execute",
        instruction: parsed.instruction.replace(SLASH_COMMAND, "").trim(),
      },
      shouldAutomateBoard: false,
      source: "slash-command" as const,
    };
  }

  if (detailOpen && selectedWorkPackage && parsed.instruction.trim()) {
    return {
      parsedCommand: {
        referencedPackageName: selectedWorkPackage.title,
        mode: "ask" as const,
        instruction: parsed.instruction,
      },
      shouldAutomateBoard: false,
      source: "selected-package-context" as const,
    };
  }

  return {
    parsedCommand: parsed,
    shouldAutomateBoard: true,
    source: "board-automation" as const,
  };
}

export function buildResolvedContext(args: {
  parsedCommand?: ParsedCommand;
  shouldAutomateBoard?: boolean;
  selectedWorkPackage?: Pick<WorkPackage, "id" | "title" | "shortName"> | null;
  provider: ChatProvider;
}): ResolvedChatContext {
  const { parsedCommand, shouldAutomateBoard, selectedWorkPackage, provider } = args;

  if (shouldAutomateBoard || !parsedCommand?.mode) {
    return {
      scope: "global",
      workPackageId: null,
      workPackageTitle: null,
      mode: "board_automation",
      provider,
      boardMutationPolicy: "replace_all",
    };
  }

  return {
    scope: "package",
    workPackageId: selectedWorkPackage?.id ?? null,
    workPackageTitle:
      selectedWorkPackage?.title ?? parsedCommand.referencedPackageName ?? null,
    mode: parsedCommand.mode,
    provider,
    boardMutationPolicy:
      parsedCommand.mode === "ask" ? "none" : "selected_package_only",
  };
}

export function buildThinkingSummary(args: {
  context: ResolvedChatContext;
  model?: string;
  note?: string;
}) {
  const { context, model, note } = args;
  const lines: string[] = [];

  if (context.scope === "package" && context.workPackageTitle) {
    lines.push(`Resolved scope to ${context.workPackageTitle}.`);
  } else {
    lines.push("Resolved scope to board-wide planning.");
  }

  if (context.mode === "board_automation") {
    lines.push("Mode: board_automation. Board hydration is allowed.");
  } else if (context.boardMutationPolicy === "none") {
    lines.push(`Mode: ${context.mode}. Board changes are disabled for this request.`);
  } else {
    lines.push(
      `Mode: ${context.mode}. Board changes are limited to the selected work package.`,
    );
  }

  lines.push(
    context.provider === "live"
      ? `Using live model ${model ?? "configured model"}.`
      : "Using mock mode.",
  );

  if (note) {
    lines.push(note);
  }

  return lines;
}