triflux 10.2.0 → 10.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,105 @@
1
+ // hub/team/execution-mode.mjs — headless vs interactive execution mode selection
2
+
3
+ export const MODES = Object.freeze({
4
+ HEADLESS: "headless",
5
+ INTERACTIVE: "interactive",
6
+ AUTO: "auto",
7
+ });
8
+
9
+ function quotePrompt(prompt) {
10
+ return JSON.stringify(typeof prompt === "string" ? prompt : "");
11
+ }
12
+
13
+ /**
14
+ * @param {{
15
+ * cli: "codex"|"gemini"|"claude",
16
+ * taskType?: "implement"|"review"|"research"|"test"|"analyze",
17
+ * needsInput?: boolean,
18
+ * estimatedDuration?: number,
19
+ * hasHub?: boolean,
20
+ * }} opts
21
+ * @returns {{ mode: string, reason: string }}
22
+ */
23
+ export function selectExecutionMode(opts = {}) {
24
+ const {
25
+ cli = "codex",
26
+ taskType = "research",
27
+ needsInput = false,
28
+ estimatedDuration = 0,
29
+ hasHub = false,
30
+ } = opts;
31
+
32
+ if (!hasHub) {
33
+ return {
34
+ mode: MODES.HEADLESS,
35
+ reason: "interactive mode requires hub; falling back to headless",
36
+ };
37
+ }
38
+
39
+ if (cli === "gemini") {
40
+ return {
41
+ mode: MODES.HEADLESS,
42
+ reason: "gemini CLI only supports headless prompt mode",
43
+ };
44
+ }
45
+
46
+ if (taskType === "implement" && !needsInput) {
47
+ return {
48
+ mode: MODES.HEADLESS,
49
+ reason: "implementation without expected input fits headless execution",
50
+ };
51
+ }
52
+
53
+ if (taskType === "review" || taskType === "analyze") {
54
+ return {
55
+ mode: MODES.HEADLESS,
56
+ reason: "review and analyze tasks default to headless execution",
57
+ };
58
+ }
59
+
60
+ if (needsInput === true) {
61
+ return {
62
+ mode: MODES.INTERACTIVE,
63
+ reason: "task may require operator input during execution",
64
+ };
65
+ }
66
+
67
+ if (estimatedDuration > 300) {
68
+ return {
69
+ mode: MODES.INTERACTIVE,
70
+ reason: "long-running work benefits from monitored interactive mode",
71
+ };
72
+ }
73
+
74
+ return {
75
+ mode: MODES.HEADLESS,
76
+ reason: "defaulting to headless execution",
77
+ };
78
+ }
79
+
80
+ /**
81
+ * @param {string} mode
82
+ * @param {{ cli?: "codex"|"gemini"|"claude", prompt?: string }} opts
83
+ * @returns {{ command: string, useExec: boolean }}
84
+ */
85
+ export function buildCommandForMode(mode, opts = {}) {
86
+ const cli = opts.cli || "codex";
87
+ const prompt = quotePrompt(opts.prompt);
88
+
89
+ if (cli === "gemini") {
90
+ return { command: `gemini -p ${prompt}`, useExec: true };
91
+ }
92
+
93
+ if (mode === MODES.INTERACTIVE || mode === MODES.AUTO) {
94
+ return { command: cli === "claude" ? "claude" : "codex", useExec: false };
95
+ }
96
+
97
+ if (cli === "claude") {
98
+ return { command: `claude --print ${prompt}`, useExec: true };
99
+ }
100
+
101
+ return {
102
+ command: `codex exec ${prompt} -s danger-full-access --dangerously-bypass-approvals-and-sandbox`,
103
+ useExec: true,
104
+ };
105
+ }