triflux 10.3.4 → 10.7.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.
Files changed (329) hide show
  1. package/LICENSE +21 -21
  2. package/bin/tfx-doctor-tui.mjs +1 -1
  3. package/bin/tfx-doctor.mjs +6 -1
  4. package/bin/tfx-profile.mjs +1 -1
  5. package/bin/tfx-setup-tui.mjs +1 -1
  6. package/bin/tfx-setup.mjs +6 -1
  7. package/bin/triflux.mjs +2396 -1140
  8. package/hooks/agent-route-guard.mjs +12 -8
  9. package/hooks/cross-review-tracker.mjs +21 -8
  10. package/hooks/error-context.mjs +19 -7
  11. package/hooks/hook-adaptive-collector.mjs +18 -16
  12. package/hooks/hook-manager.mjs +93 -32
  13. package/hooks/hook-orchestrator.mjs +108 -24
  14. package/hooks/hook-registry.json +11 -0
  15. package/hooks/keyword-rules.json +6 -10
  16. package/hooks/lib/resolve-root.mjs +1 -1
  17. package/hooks/mcp-config-watcher.mjs +6 -2
  18. package/hooks/pipeline-stop.mjs +3 -6
  19. package/hooks/safety-guard.mjs +99 -28
  20. package/hooks/session-start-fast.mjs +143 -0
  21. package/hooks/subagent-verifier.mjs +5 -4
  22. package/hub/account-broker.mjs +256 -60
  23. package/hub/adaptive-diagnostic.mjs +75 -48
  24. package/hub/adaptive-inject.mjs +95 -57
  25. package/hub/adaptive-memory.mjs +156 -42
  26. package/hub/adaptive.mjs +60 -31
  27. package/hub/assign-callbacks.mjs +67 -30
  28. package/hub/bridge.mjs +0 -1
  29. package/hub/cli-adapter-base.mjs +200 -48
  30. package/hub/codex-adapter.mjs +76 -96
  31. package/hub/codex-compat.mjs +3 -3
  32. package/hub/codex-preflight.mjs +63 -37
  33. package/hub/delegator/contracts.mjs +19 -23
  34. package/hub/delegator/index.mjs +3 -3
  35. package/hub/delegator/service.mjs +88 -64
  36. package/hub/delegator/tool-definitions.mjs +5 -5
  37. package/hub/fullcycle.mjs +33 -17
  38. package/hub/gemini-adapter.mjs +69 -94
  39. package/hub/hitl.mjs +89 -30
  40. package/hub/intent.mjs +161 -38
  41. package/hub/lib/cache-guard.mjs +43 -17
  42. package/hub/lib/mcp-response-cache.mjs +66 -32
  43. package/hub/lib/memory-store.mjs +285 -111
  44. package/hub/lib/path-utils.mjs +35 -37
  45. package/hub/lib/process-utils.mjs +106 -37
  46. package/hub/lib/spawn-trace.mjs +527 -0
  47. package/hub/lib/ssh-command.mjs +34 -4
  48. package/hub/lib/ssh-retry.mjs +5 -1
  49. package/hub/lib/uuidv7.mjs +4 -3
  50. package/hub/memory-doctor.mjs +266 -106
  51. package/hub/middleware/request-logger.mjs +61 -34
  52. package/hub/paths.mjs +9 -9
  53. package/hub/pipeline/gates/confidence.mjs +34 -15
  54. package/hub/pipeline/gates/consensus.mjs +27 -15
  55. package/hub/pipeline/gates/index.mjs +7 -3
  56. package/hub/pipeline/gates/selfcheck.mjs +57 -19
  57. package/hub/pipeline/index.mjs +77 -42
  58. package/hub/pipeline/state.mjs +10 -10
  59. package/hub/pipeline/transitions.mjs +40 -23
  60. package/hub/platform.mjs +57 -48
  61. package/hub/promote-penalties.mjs +25 -7
  62. package/hub/quality/deslop.mjs +70 -49
  63. package/hub/research.mjs +32 -25
  64. package/hub/router.mjs +240 -107
  65. package/hub/routing/complexity.mjs +132 -29
  66. package/hub/routing/index.mjs +17 -12
  67. package/hub/routing/q-learning.mjs +76 -28
  68. package/hub/server.mjs +4 -4
  69. package/hub/session-fingerprint.mjs +126 -60
  70. package/hub/state.mjs +84 -43
  71. package/hub/store-adapter.mjs +59 -26
  72. package/hub/store.mjs +356 -153
  73. package/hub/team/agent-map.json +22 -7
  74. package/hub/team/ansi.mjs +186 -122
  75. package/hub/team/backend.mjs +28 -10
  76. package/hub/team/cli/commands/attach.mjs +29 -9
  77. package/hub/team/cli/commands/control.mjs +29 -8
  78. package/hub/team/cli/commands/debug.mjs +32 -11
  79. package/hub/team/cli/commands/focus.mjs +38 -11
  80. package/hub/team/cli/commands/interrupt.mjs +18 -6
  81. package/hub/team/cli/commands/kill.mjs +16 -5
  82. package/hub/team/cli/commands/list.mjs +11 -4
  83. package/hub/team/cli/commands/send.mjs +19 -6
  84. package/hub/team/cli/commands/start/index.mjs +154 -31
  85. package/hub/team/cli/commands/start/parse-args.mjs +38 -11
  86. package/hub/team/cli/commands/start/start-headless.mjs +112 -36
  87. package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
  88. package/hub/team/cli/commands/start/start-mux.mjs +70 -21
  89. package/hub/team/cli/commands/start/start-wt.mjs +29 -12
  90. package/hub/team/cli/commands/status.mjs +43 -14
  91. package/hub/team/cli/commands/stop.mjs +11 -4
  92. package/hub/team/cli/commands/task.mjs +8 -3
  93. package/hub/team/cli/commands/tasks.mjs +1 -1
  94. package/hub/team/cli/index.mjs +2 -2
  95. package/hub/team/cli/manifest.mjs +38 -8
  96. package/hub/team/cli/render.mjs +30 -8
  97. package/hub/team/cli/services/attach-fallback.mjs +31 -11
  98. package/hub/team/cli/services/hub-client.mjs +42 -14
  99. package/hub/team/cli/services/member-selector.mjs +11 -4
  100. package/hub/team/cli/services/native-control.mjs +48 -21
  101. package/hub/team/cli/services/runtime-mode.mjs +2 -1
  102. package/hub/team/cli/services/state-store.mjs +25 -8
  103. package/hub/team/cli/services/task-model.mjs +16 -6
  104. package/hub/team/conductor-mesh-bridge.mjs +24 -23
  105. package/hub/team/conductor.mjs +8 -4
  106. package/hub/team/dashboard-anchor.mjs +4 -5
  107. package/hub/team/dashboard-layout.mjs +3 -1
  108. package/hub/team/dashboard-open.mjs +41 -21
  109. package/hub/team/dashboard.mjs +76 -28
  110. package/hub/team/event-log.mjs +18 -10
  111. package/hub/team/handoff.mjs +31 -15
  112. package/hub/team/headless.mjs +2 -1
  113. package/hub/team/health-probe.mjs +69 -54
  114. package/hub/team/launcher-template.mjs +16 -13
  115. package/hub/team/native-supervisor.mjs +65 -21
  116. package/hub/team/native.mjs +74 -35
  117. package/hub/team/nativeProxy.mjs +184 -113
  118. package/hub/team/notify.mjs +119 -76
  119. package/hub/team/orchestrator.mjs +9 -4
  120. package/hub/team/pane.mjs +12 -7
  121. package/hub/team/process-cleanup.mjs +25 -16
  122. package/hub/team/psmux.mjs +491 -201
  123. package/hub/team/remote-probe.mjs +68 -52
  124. package/hub/team/remote-session.mjs +117 -59
  125. package/hub/team/remote-watcher.mjs +61 -33
  126. package/hub/team/routing.mjs +51 -25
  127. package/hub/team/runtime-strategy.mjs +3 -1
  128. package/hub/team/session.mjs +98 -34
  129. package/hub/team/staleState.mjs +72 -30
  130. package/hub/team/swarm-locks.mjs +15 -13
  131. package/hub/team/swarm-planner.mjs +32 -21
  132. package/hub/team/swarm-reconciler.mjs +48 -23
  133. package/hub/team/tui-lite.mjs +266 -68
  134. package/hub/team/tui-remote-adapter.mjs +14 -10
  135. package/hub/team/tui-viewer.mjs +99 -43
  136. package/hub/team/tui.mjs +708 -271
  137. package/hub/team/worktree-lifecycle.mjs +152 -58
  138. package/hub/team/wt-manager.mjs +24 -14
  139. package/hub/token-mode.mjs +71 -71
  140. package/hub/tray.mjs +66 -23
  141. package/hub/workers/claude-worker.mjs +162 -118
  142. package/hub/workers/codex-mcp.mjs +192 -141
  143. package/hub/workers/delegator-mcp.mjs +507 -333
  144. package/hub/workers/factory.mjs +8 -8
  145. package/hub/workers/gemini-worker.mjs +115 -84
  146. package/hub/workers/interface.mjs +6 -1
  147. package/hub/workers/worker-utils.mjs +21 -14
  148. package/hud/colors.mjs +27 -9
  149. package/hud/constants.mjs +162 -26
  150. package/hud/context-monitor.mjs +82 -41
  151. package/hud/hud-qos-status.mjs +129 -49
  152. package/hud/mission-board.mjs +6 -3
  153. package/hud/providers/claude.mjs +226 -115
  154. package/hud/providers/codex.mjs +62 -22
  155. package/hud/providers/gemini.mjs +168 -56
  156. package/hud/renderers.mjs +384 -119
  157. package/hud/terminal.mjs +101 -31
  158. package/hud/utils.mjs +78 -38
  159. package/mesh/index.mjs +11 -5
  160. package/mesh/mesh-budget.mjs +18 -9
  161. package/mesh/mesh-heartbeat.mjs +1 -1
  162. package/mesh/mesh-queue.mjs +3 -5
  163. package/mesh/mesh-router.mjs +5 -4
  164. package/package.json +2 -1
  165. package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
  166. package/scripts/__tests__/keyword-detector.test.mjs +77 -28
  167. package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
  168. package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
  169. package/scripts/__tests__/remote-spawn.test.mjs +10 -4
  170. package/scripts/__tests__/session-start-fast.test.mjs +36 -0
  171. package/scripts/__tests__/skill-template.test.mjs +98 -50
  172. package/scripts/__tests__/smoke.test.mjs +1 -1
  173. package/scripts/__tests__/spawn-trace.test.mjs +102 -0
  174. package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
  175. package/scripts/cache-doctor.mjs +11 -4
  176. package/scripts/cache-warmup.mjs +96 -37
  177. package/scripts/claudemd-sync.mjs +27 -17
  178. package/scripts/codex-gateway-preflight.mjs +52 -37
  179. package/scripts/codex-mcp-gateway-sync.mjs +59 -39
  180. package/scripts/completions/tfx.bash +47 -47
  181. package/scripts/completions/tfx.fish +44 -44
  182. package/scripts/completions/tfx.zsh +83 -83
  183. package/scripts/config-audit.mjs +232 -0
  184. package/scripts/convert-to-tmpl.mjs +54 -0
  185. package/scripts/cross-review-gate.mjs +35 -12
  186. package/scripts/cross-review-tracker.mjs +21 -8
  187. package/scripts/demo.mjs +35 -17
  188. package/scripts/doctor-diagnose.mjs +284 -0
  189. package/scripts/gen-skill-docs.mjs +7 -2
  190. package/scripts/gen-skill-manifest.mjs +2 -1
  191. package/scripts/headless-guard.mjs +86 -48
  192. package/scripts/hub-ensure.mjs +45 -26
  193. package/scripts/keyword-detector.mjs +41 -20
  194. package/scripts/keyword-rules-expander.mjs +47 -30
  195. package/scripts/lib/claudemd-scanner.mjs +6 -1
  196. package/scripts/lib/context.mjs +3 -3
  197. package/scripts/lib/cross-review-utils.mjs +6 -3
  198. package/scripts/lib/env-probe.mjs +47 -28
  199. package/scripts/lib/gemini-profiles.mjs +44 -10
  200. package/scripts/lib/handoff.mjs +33 -17
  201. package/scripts/lib/hook-utils.mjs +8 -6
  202. package/scripts/lib/keyword-rules.mjs +43 -19
  203. package/scripts/lib/logger.mjs +24 -24
  204. package/scripts/lib/mcp-filter.mjs +377 -239
  205. package/scripts/lib/mcp-guard-engine.mjs +194 -79
  206. package/scripts/lib/mcp-manifest.mjs +23 -13
  207. package/scripts/lib/mcp-server-catalog.mjs +300 -63
  208. package/scripts/lib/psmux-info.mjs +11 -6
  209. package/scripts/lib/remote-spawn-transfer.mjs +44 -14
  210. package/scripts/lib/skill-template.mjs +30 -7
  211. package/scripts/mcp-check.mjs +58 -39
  212. package/scripts/mcp-gateway-config.mjs +83 -39
  213. package/scripts/mcp-gateway-ensure.mjs +43 -35
  214. package/scripts/mcp-gateway-integration-test.mjs +70 -58
  215. package/scripts/mcp-gateway-start.mjs +126 -60
  216. package/scripts/mcp-gateway-verify.mjs +24 -22
  217. package/scripts/mcp-safety-guard.mjs +44 -11
  218. package/scripts/notion-read.mjs +199 -84
  219. package/scripts/pack.mjs +94 -89
  220. package/scripts/preflight-cache.mjs +27 -10
  221. package/scripts/preinstall.mjs +42 -13
  222. package/scripts/remote-spawn.mjs +309 -94
  223. package/scripts/run.cjs +8 -5
  224. package/scripts/session-spawn-helper.mjs +130 -39
  225. package/scripts/session-stale-cleanup.mjs +123 -0
  226. package/scripts/setup.mjs +941 -492
  227. package/scripts/test-lock.mjs +20 -7
  228. package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
  229. package/scripts/tfx-batch-stats.mjs +32 -11
  230. package/scripts/tfx-gate-activate.mjs +11 -4
  231. package/scripts/tfx-route-post.mjs +87 -20
  232. package/scripts/tfx-route-worker.mjs +57 -51
  233. package/scripts/tfx-route.sh +41 -124
  234. package/scripts/tmp-cleanup.mjs +21 -7
  235. package/scripts/token-snapshot.mjs +204 -85
  236. package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
  237. package/skills/.omc/state/idle-notif-cooldown.json +3 -0
  238. package/skills/.omc/state/last-tool-error.json +7 -0
  239. package/skills/.omc/state/subagent-tracking.json +7 -0
  240. package/skills/_templates/base.md +1 -6
  241. package/skills/merge-worktree/SKILL.md.tmpl +144 -0
  242. package/skills/shared/telemetry-segment.md +6 -0
  243. package/skills/star-prompt/SKILL.md.tmpl +222 -0
  244. package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
  245. package/skills/tfx-analysis/skill.json +1 -6
  246. package/skills/tfx-auto/SKILL.md +1 -0
  247. package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
  248. package/skills/tfx-auto-codex/skill.json +1 -3
  249. package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
  250. package/skills/tfx-autopilot/skill.json +1 -5
  251. package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
  252. package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
  253. package/skills/tfx-autoroute/skill.json +1 -7
  254. package/skills/tfx-codex/SKILL.md +1 -0
  255. package/skills/tfx-codex/skill.json +1 -3
  256. package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
  257. package/skills/tfx-codex-swarm/evals/evals.json +1 -1
  258. package/skills/tfx-codex-swarm/skill.json +1 -4
  259. package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
  260. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
  261. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
  262. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
  263. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
  264. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
  265. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
  266. package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
  267. package/skills/tfx-debate/SKILL.md.tmpl +192 -0
  268. package/skills/tfx-debate/skill.json +1 -7
  269. package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
  270. package/skills/tfx-deep-analysis/skill.json +1 -5
  271. package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
  272. package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
  273. package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
  274. package/skills/tfx-deep-qa/skill.json +1 -6
  275. package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
  276. package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
  277. package/skills/tfx-doctor/SKILL.md +21 -0
  278. package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
  279. package/skills/tfx-doctor/skill.json +1 -3
  280. package/skills/tfx-find/SKILL.md +1 -0
  281. package/skills/tfx-forge/SKILL.md.tmpl +187 -0
  282. package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
  283. package/skills/tfx-fullcycle/skill.json +1 -6
  284. package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
  285. package/skills/tfx-gemini/skill.json +1 -3
  286. package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
  287. package/skills/tfx-hooks/skill.json +1 -3
  288. package/skills/tfx-hub/SKILL.md.tmpl +212 -0
  289. package/skills/tfx-hub/skill.json +1 -3
  290. package/skills/tfx-index/SKILL.md +1 -0
  291. package/skills/tfx-index/skill.json +1 -6
  292. package/skills/tfx-interview/SKILL.md.tmpl +285 -0
  293. package/skills/tfx-multi/SKILL.md.tmpl +183 -0
  294. package/skills/tfx-multi/skill.json +1 -3
  295. package/skills/tfx-panel/SKILL.md.tmpl +189 -0
  296. package/skills/tfx-panel/skill.json +1 -7
  297. package/skills/tfx-persist/SKILL.md.tmpl +270 -0
  298. package/skills/tfx-persist/skill.json +1 -7
  299. package/skills/tfx-plan/SKILL.md +1 -0
  300. package/skills/tfx-plan/skill.json +1 -6
  301. package/skills/tfx-profile/SKILL.md.tmpl +239 -0
  302. package/skills/tfx-profile/skill.json +1 -3
  303. package/skills/tfx-prune/SKILL.md.tmpl +200 -0
  304. package/skills/tfx-prune/skill.json +1 -7
  305. package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
  306. package/skills/tfx-psmux-rules/skill.json +1 -4
  307. package/skills/tfx-qa/SKILL.md +1 -0
  308. package/skills/tfx-qa/skill.json +1 -6
  309. package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
  310. package/skills/tfx-ralph/skill.json +1 -4
  311. package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
  312. package/skills/tfx-remote-setup/skill.json +1 -3
  313. package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
  314. package/skills/tfx-remote-spawn/references/hosts.json +16 -0
  315. package/skills/tfx-remote-spawn/skill.json +1 -4
  316. package/skills/tfx-research/SKILL.md +1 -0
  317. package/skills/tfx-review/SKILL.md +1 -0
  318. package/skills/tfx-review/skill.json +1 -6
  319. package/skills/tfx-setup/SKILL.md.tmpl +504 -0
  320. package/skills/tfx-setup/skill.json +1 -3
  321. package/skills/tfx-swarm/SKILL.md +22 -0
  322. package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
  323. package/tui/codex-profile.mjs +88 -33
  324. package/tui/core.mjs +45 -15
  325. package/tui/doctor.mjs +75 -28
  326. package/tui/gemini-profile.mjs +74 -29
  327. package/tui/monitor-data.mjs +8 -4
  328. package/tui/monitor.mjs +71 -27
  329. package/tui/setup.mjs +133 -42
@@ -1,52 +1,59 @@
1
1
  // hub/team/notify.mjs — team notifier (bell / Windows toast / webhook)
2
2
 
3
- import { execFile } from 'node:child_process';
4
- import os from 'node:os';
3
+ import { execFile } from "node:child_process";
4
+ import os from "node:os";
5
5
 
6
- export const NOTIFY_EVENT_TYPES = Object.freeze(['completed', 'failed', 'inputWait']);
7
- export const NOTIFY_CHANNELS = Object.freeze(['bell', 'toast', 'webhook']);
6
+ export const NOTIFY_EVENT_TYPES = Object.freeze([
7
+ "completed",
8
+ "failed",
9
+ "inputWait",
10
+ ]);
11
+ export const NOTIFY_CHANNELS = Object.freeze(["bell", "toast", "webhook"]);
8
12
 
9
13
  function freezeRecord(record) {
10
14
  return Object.freeze({ ...record });
11
15
  }
12
16
 
13
17
  function escapePowerShellSingleQuoted(value) {
14
- return String(value ?? '').replaceAll("'", "''");
18
+ return String(value ?? "").replaceAll("'", "''");
15
19
  }
16
20
 
17
21
  function normalizeTimestamp(value) {
18
22
  if (value instanceof Date) return value.toISOString();
19
- if (value == null || value === '') return new Date().toISOString();
23
+ if (value == null || value === "") return new Date().toISOString();
20
24
  return String(value);
21
25
  }
22
26
 
23
27
  function normalizeEvent(event, defaults = {}) {
24
- if (!event || typeof event !== 'object') {
25
- throw new TypeError('notify(event) requires an event object');
28
+ if (!event || typeof event !== "object") {
29
+ throw new TypeError("notify(event) requires an event object");
26
30
  }
27
31
 
28
- const type = String(event.type || '').trim();
32
+ const type = String(event.type || "").trim();
29
33
  if (!NOTIFY_EVENT_TYPES.includes(type)) {
30
- throw new TypeError(`Unsupported notify event type: ${type || '<empty>'}`);
34
+ throw new TypeError(`Unsupported notify event type: ${type || "<empty>"}`);
31
35
  }
32
36
 
33
37
  return freezeRecord({
34
38
  type,
35
- sessionId: event.sessionId == null ? '' : String(event.sessionId),
36
- host: event.host == null || event.host === '' ? String(defaults.host || os.hostname()) : String(event.host),
37
- summary: event.summary == null ? '' : String(event.summary),
39
+ sessionId: event.sessionId == null ? "" : String(event.sessionId),
40
+ host:
41
+ event.host == null || event.host === ""
42
+ ? String(defaults.host || os.hostname())
43
+ : String(event.host),
44
+ summary: event.summary == null ? "" : String(event.summary),
38
45
  timestamp: normalizeTimestamp(event.timestamp),
39
46
  });
40
47
  }
41
48
 
42
49
  function defaultChannelConfig(name, env) {
43
50
  switch (name) {
44
- case 'bell':
51
+ case "bell":
45
52
  return { enabled: true };
46
- case 'toast':
53
+ case "toast":
47
54
  return { enabled: true };
48
- case 'webhook': {
49
- const url = String(env?.TRIFLUX_NOTIFY_WEBHOOK || '');
55
+ case "webhook": {
56
+ const url = String(env?.TRIFLUX_NOTIFY_WEBHOOK || "");
50
57
  return { enabled: Boolean(url), url };
51
58
  }
52
59
  default:
@@ -56,33 +63,40 @@ function defaultChannelConfig(name, env) {
56
63
 
57
64
  function normalizeChannelConfig(name, value, env) {
58
65
  const base = defaultChannelConfig(name, env);
59
- const patch = typeof value === 'boolean'
60
- ? { enabled: value }
61
- : (value && typeof value === 'object' ? value : {});
66
+ const patch =
67
+ typeof value === "boolean"
68
+ ? { enabled: value }
69
+ : value && typeof value === "object"
70
+ ? value
71
+ : {};
62
72
 
63
73
  const next = {
64
74
  ...base,
65
75
  ...patch,
66
76
  };
67
77
 
68
- if ('enabled' in next) {
78
+ if ("enabled" in next) {
69
79
  next.enabled = Boolean(next.enabled);
70
80
  }
71
81
 
72
- if (name === 'webhook') {
73
- next.url = String(next.url || env?.TRIFLUX_NOTIFY_WEBHOOK || '');
82
+ if (name === "webhook") {
83
+ next.url = String(next.url || env?.TRIFLUX_NOTIFY_WEBHOOK || "");
74
84
  }
75
85
 
76
- if (name === 'toast') {
86
+ if (name === "toast") {
77
87
  if (next.command != null) next.command = String(next.command);
78
- if (next.timeoutMs != null) next.timeoutMs = Math.max(1, Number.parseInt(String(next.timeoutMs), 10) || 5000);
88
+ if (next.timeoutMs != null)
89
+ next.timeoutMs = Math.max(
90
+ 1,
91
+ Number.parseInt(String(next.timeoutMs), 10) || 5000,
92
+ );
79
93
  }
80
94
 
81
95
  return freezeRecord(next);
82
96
  }
83
97
 
84
98
  function normalizeChannels(channels, env) {
85
- const source = channels && typeof channels === 'object' ? channels : {};
99
+ const source = channels && typeof channels === "object" ? channels : {};
86
100
  const normalized = {};
87
101
  for (const name of NOTIFY_CHANNELS) {
88
102
  normalized[name] = normalizeChannelConfig(name, source[name], env);
@@ -97,20 +111,27 @@ function updateChannelConfig(channels, channel, config, env) {
97
111
 
98
112
  return Object.freeze({
99
113
  ...channels,
100
- [channel]: normalizeChannelConfig(channel, { ...channels[channel], ...(typeof config === 'boolean' ? { enabled: config } : (config || {})) }, env),
114
+ [channel]: normalizeChannelConfig(
115
+ channel,
116
+ {
117
+ ...channels[channel],
118
+ ...(typeof config === "boolean" ? { enabled: config } : config || {}),
119
+ },
120
+ env,
121
+ ),
101
122
  });
102
123
  }
103
124
 
104
125
  function formatEventTitle(event) {
105
126
  switch (event.type) {
106
- case 'completed':
107
- return 'Triflux completed';
108
- case 'failed':
109
- return 'Triflux failed';
110
- case 'inputWait':
111
- return 'Triflux waiting for input';
127
+ case "completed":
128
+ return "Triflux completed";
129
+ case "failed":
130
+ return "Triflux failed";
131
+ case "inputWait":
132
+ return "Triflux waiting for input";
112
133
  default:
113
- return 'Triflux notification';
134
+ return "Triflux notification";
114
135
  }
115
136
  }
116
137
 
@@ -118,12 +139,15 @@ function formatEventBody(event) {
118
139
  const parts = [];
119
140
  if (event.summary) parts.push(event.summary);
120
141
 
121
- const meta = [event.sessionId ? `session ${event.sessionId}` : '', event.host ? `host ${event.host}` : '']
142
+ const meta = [
143
+ event.sessionId ? `session ${event.sessionId}` : "",
144
+ event.host ? `host ${event.host}` : "",
145
+ ]
122
146
  .filter(Boolean)
123
- .join(' · ');
147
+ .join(" · ");
124
148
  if (meta) parts.push(meta);
125
149
 
126
- return parts.join('\n') || event.timestamp;
150
+ return parts.join("\n") || event.timestamp;
127
151
  }
128
152
 
129
153
  function createResult(channel, status, extra = {}) {
@@ -145,17 +169,18 @@ function execFileAsync(command, args, options, execFileFn) {
145
169
  }
146
170
 
147
171
  async function sendBell(config, deps) {
148
- if (!config.enabled) return createResult('bell', 'skipped', { reason: 'disabled' });
172
+ if (!config.enabled)
173
+ return createResult("bell", "skipped", { reason: "disabled" });
149
174
  const stream = deps.stdout;
150
- if (!stream || typeof stream.write !== 'function') {
151
- return createResult('bell', 'skipped', { reason: 'stdout-unavailable' });
175
+ if (!stream || typeof stream.write !== "function") {
176
+ return createResult("bell", "skipped", { reason: "stdout-unavailable" });
152
177
  }
153
178
 
154
179
  try {
155
- stream.write('\u0007');
156
- return createResult('bell', 'sent');
180
+ stream.write("\u0007");
181
+ return createResult("bell", "sent");
157
182
  } catch (error) {
158
- return createResult('bell', 'failed', { error: error.message });
183
+ return createResult("bell", "failed", { error: error.message });
159
184
  }
160
185
  }
161
186
 
@@ -167,32 +192,34 @@ function buildToastScript(title, body) {
167
192
  `$Title = '${safeTitle}'`,
168
193
  `$Body = '${safeBody}'`,
169
194
  "if (Get-Command New-BurntToastNotification -ErrorAction SilentlyContinue) {",
170
- ' New-BurntToastNotification -Text @($Title, $Body) | Out-Null',
171
- ' return',
172
- '}',
173
- '[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > $null',
174
- '[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] > $null',
175
- '$escapedTitle = [System.Security.SecurityElement]::Escape($Title)',
176
- '$escapedBody = [System.Security.SecurityElement]::Escape($Body)',
177
- '$xml = New-Object Windows.Data.Xml.Dom.XmlDocument',
178
- '$xml.LoadXml("<toast><visual><binding template=\'ToastGeneric\'><text>$escapedTitle</text><text>$escapedBody</text></binding></visual></toast>")',
179
- '$toast = [Windows.UI.Notifications.ToastNotification]::new($xml)',
195
+ " New-BurntToastNotification -Text @($Title, $Body) | Out-Null",
196
+ " return",
197
+ "}",
198
+ "[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > $null",
199
+ "[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] > $null",
200
+ "$escapedTitle = [System.Security.SecurityElement]::Escape($Title)",
201
+ "$escapedBody = [System.Security.SecurityElement]::Escape($Body)",
202
+ "$xml = New-Object Windows.Data.Xml.Dom.XmlDocument",
203
+ "$xml.LoadXml(\"<toast><visual><binding template='ToastGeneric'><text>$escapedTitle</text><text>$escapedBody</text></binding></visual></toast>\")",
204
+ "$toast = [Windows.UI.Notifications.ToastNotification]::new($xml)",
180
205
  "[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('Triflux').Show($toast)",
181
- ].join('; ');
206
+ ].join("; ");
182
207
  }
183
208
 
184
209
  async function sendToast(event, config, deps) {
185
- if (!config.enabled) return createResult('toast', 'skipped', { reason: 'disabled' });
186
- if ((deps.platform || process.platform) !== 'win32') {
187
- return createResult('toast', 'skipped', { reason: 'unsupported-platform' });
210
+ if (!config.enabled)
211
+ return createResult("toast", "skipped", { reason: "disabled" });
212
+ if ((deps.platform || process.platform) !== "win32") {
213
+ return createResult("toast", "skipped", { reason: "unsupported-platform" });
188
214
  }
189
215
 
190
216
  const execFileFn = deps.execFile || execFile;
191
217
  const candidates = config.command
192
218
  ? [config.command]
193
- : Array.isArray(deps.powerShellCandidates) && deps.powerShellCandidates.length > 0
219
+ : Array.isArray(deps.powerShellCandidates) &&
220
+ deps.powerShellCandidates.length > 0
194
221
  ? deps.powerShellCandidates
195
- : ['pwsh', 'powershell.exe'];
222
+ : ["pwsh", "powershell.exe"];
196
223
 
197
224
  const title = formatEventTitle(event);
198
225
  const body = formatEventBody(event);
@@ -201,40 +228,51 @@ async function sendToast(event, config, deps) {
201
228
 
202
229
  for (const command of candidates) {
203
230
  try {
204
- await execFileAsync(command, ['-NoLogo', '-NoProfile', '-Command', script], {
205
- windowsHide: true,
206
- timeout: config.timeoutMs || 5000,
207
- }, execFileFn);
208
- return createResult('toast', 'sent', { command });
231
+ await execFileAsync(
232
+ command,
233
+ ["-NoLogo", "-NoProfile", "-Command", script],
234
+ {
235
+ windowsHide: true,
236
+ timeout: config.timeoutMs || 5000,
237
+ },
238
+ execFileFn,
239
+ );
240
+ return createResult("toast", "sent", { command });
209
241
  } catch (error) {
210
242
  failures.push(`${command}: ${error.message}`);
211
243
  }
212
244
  }
213
245
 
214
- return createResult('toast', 'failed', { error: failures.join(' | ') || 'toast-send-failed' });
246
+ return createResult("toast", "failed", {
247
+ error: failures.join(" | ") || "toast-send-failed",
248
+ });
215
249
  }
216
250
 
217
251
  async function sendWebhook(event, config, deps) {
218
- if (!config.enabled) return createResult('webhook', 'skipped', { reason: 'disabled' });
219
- if (!config.url) return createResult('webhook', 'skipped', { reason: 'missing-url' });
220
- if (typeof deps.fetch !== 'function') {
221
- return createResult('webhook', 'failed', { error: 'fetch-unavailable' });
252
+ if (!config.enabled)
253
+ return createResult("webhook", "skipped", { reason: "disabled" });
254
+ if (!config.url)
255
+ return createResult("webhook", "skipped", { reason: "missing-url" });
256
+ if (typeof deps.fetch !== "function") {
257
+ return createResult("webhook", "failed", { error: "fetch-unavailable" });
222
258
  }
223
259
 
224
260
  try {
225
261
  const response = await deps.fetch(config.url, {
226
- method: 'POST',
227
- headers: { 'content-type': 'application/json' },
262
+ method: "POST",
263
+ headers: { "content-type": "application/json" },
228
264
  body: JSON.stringify(event),
229
265
  });
230
266
 
231
267
  if (!response?.ok) {
232
- return createResult('webhook', 'failed', { error: `HTTP ${response?.status ?? 'unknown'}` });
268
+ return createResult("webhook", "failed", {
269
+ error: `HTTP ${response?.status ?? "unknown"}`,
270
+ });
233
271
  }
234
272
 
235
- return createResult('webhook', 'sent', { statusCode: response.status });
273
+ return createResult("webhook", "sent", { statusCode: response.status });
236
274
  } catch (error) {
237
- return createResult('webhook', 'failed', { error: error.message });
275
+ return createResult("webhook", "failed", { error: error.message });
238
276
  }
239
277
  }
240
278
 
@@ -254,7 +292,10 @@ function createNotifierInstance(channels, deps) {
254
292
  }
255
293
 
256
294
  function setChannel(channel, config) {
257
- return createNotifierInstance(updateChannelConfig(channels, channel, config, deps.env), deps);
295
+ return createNotifierInstance(
296
+ updateChannelConfig(channels, channel, config, deps.env),
297
+ deps,
298
+ );
258
299
  }
259
300
 
260
301
  return Object.freeze({ notify, setChannel });
@@ -286,7 +327,9 @@ export function createNotifier(opts = {}) {
286
327
  fetch: opts.deps?.fetch || globalThis.fetch?.bind(globalThis),
287
328
  platform: opts.platform || process.platform,
288
329
  hostname: opts.hostname || os.hostname(),
289
- powerShellCandidates: Object.freeze(opts.deps?.powerShellCandidates || ['pwsh', 'powershell.exe']),
330
+ powerShellCandidates: Object.freeze(
331
+ opts.deps?.powerShellCandidates || ["pwsh", "powershell.exe"],
332
+ ),
290
333
  });
291
334
 
292
335
  return createNotifierInstance(normalizeChannels(opts.channels, env), deps);
@@ -52,9 +52,10 @@ export function decomposeTask(taskDescription, agentCount) {
52
52
  export function buildLeadPrompt(taskDescription, config) {
53
53
  const { agentId, teammateMode = "tmux", workers = [] } = config;
54
54
 
55
- const roster = workers
56
- .map((w, i) => `${i + 1}. ${w.agentId} (${w.cli}) — ${w.subtask}`)
57
- .join("\n") || "- (워커 없음)";
55
+ const roster =
56
+ workers
57
+ .map((w, i) => `${i + 1}. ${w.agentId} (${w.cli}) — ${w.subtask}`)
58
+ .join("\n") || "- (워커 없음)";
58
59
 
59
60
  const workerIds = workers.map((w) => w.agentId).join(", ");
60
61
 
@@ -142,7 +143,11 @@ export async function orchestrate(sessionName, assignments, opts = {}) {
142
143
  agentId: leadAgentId,
143
144
  hubUrl,
144
145
  teammateMode,
145
- workers: workers.map((w) => ({ agentId: w.agentId, cli: w.cli, subtask: w.subtask })),
146
+ workers: workers.map((w) => ({
147
+ agentId: w.agentId,
148
+ cli: w.cli,
149
+ subtask: w.subtask,
150
+ })),
146
151
  });
147
152
  injectPrompt(lead.target, leadPrompt, { useFileRef: true });
148
153
  await new Promise((r) => setTimeout(r, 100));
package/hub/team/pane.mjs CHANGED
@@ -1,12 +1,11 @@
1
1
  // hub/team/pane.mjs — pane별 CLI 실행 + stdin 주입
2
2
  // 의존성: child_process, fs, os, path (Node.js 내장)만 사용
3
- import { writeFileSync, unlinkSync, mkdirSync } from "node:fs";
4
- import { join } from "node:path";
3
+ import { mkdirSync, unlinkSync, writeFileSync } from "node:fs";
5
4
  import { tmpdir } from "node:os";
6
- import { detectMultiplexer, tmuxExec } from "./session.mjs";
7
- import { psmuxExec } from "./psmux.mjs";
8
-
5
+ import { join } from "node:path";
9
6
  import { buildExecArgs } from "../codex-adapter.mjs";
7
+ import { psmuxExec } from "./psmux.mjs";
8
+ import { detectMultiplexer, tmuxExec } from "./session.mjs";
10
9
 
11
10
  function quoteArg(value) {
12
11
  return `"${String(value).replace(/"/g, '\\"')}"`;
@@ -118,7 +117,11 @@ export function injectPrompt(target, prompt, { useFileRef = false } = {}) {
118
117
  psmuxExec(["send-keys", "-t", target, "-l", `@${filePath}`]);
119
118
  psmuxExec(["send-keys", "-t", target, "Enter"]);
120
119
  // TUI가 파일을 읽을 시간을 주고 정리
121
- setTimeout(() => { try { unlinkSync(tmpFile); } catch {} }, 10000);
120
+ setTimeout(() => {
121
+ try {
122
+ unlinkSync(tmpFile);
123
+ } catch {}
124
+ }, 10000);
122
125
  return;
123
126
  }
124
127
 
@@ -139,7 +142,9 @@ export function injectPrompt(target, prompt, { useFileRef = false } = {}) {
139
142
  muxExec(`paste-buffer -t ${target}`);
140
143
  muxExec(`send-keys -t ${target} Enter`);
141
144
  } finally {
142
- try { unlinkSync(tmpFile); } catch {}
145
+ try {
146
+ unlinkSync(tmpFile);
147
+ } catch {}
143
148
  }
144
149
  }
145
150
 
@@ -13,10 +13,7 @@ const TARGET_PROCESS_NAMES = ["node", "python", "python3"];
13
13
  const SIGTERM_GRACE_MS = 5000;
14
14
 
15
15
  // cmdLine 패턴 기반 화이트리스트 (고아 후보에서 제외)
16
- const WHITELIST_CMDLINE = [
17
- /oh-my-claudecode/i,
18
- /triflux[\\/]hub[\\/]s/i,
19
- ];
16
+ const WHITELIST_CMDLINE = [/oh-my-claudecode/i, /triflux[\\/]hub[\\/]s/i];
20
17
 
21
18
  // 프로세스명 기반 화이트리스트
22
19
  const WHITELIST_NAMES = ["claude", "CCXProcess"];
@@ -44,15 +41,13 @@ async function queryWindowsProcesses(execFileFn) {
44
41
  const raw = JSON.parse(stdout.trim());
45
42
  const items = Array.isArray(raw) ? raw : [raw];
46
43
 
47
- return items
48
- .filter(Boolean)
49
- .map((p) => ({
50
- pid: Number(p.ProcessId),
51
- name: String(p.Name || "").replace(/\.exe$/i, ""),
52
- parentPid: Number(p.ParentProcessId) || 0,
53
- cmdLine: String(p.CommandLine || ""),
54
- ramMB: Math.round((Number(p.WorkingSetSize) || 0) / 1024 / 1024),
55
- }));
44
+ return items.filter(Boolean).map((p) => ({
45
+ pid: Number(p.ProcessId),
46
+ name: String(p.Name || "").replace(/\.exe$/i, ""),
47
+ parentPid: Number(p.ParentProcessId) || 0,
48
+ cmdLine: String(p.CommandLine || ""),
49
+ ramMB: Math.round((Number(p.WorkingSetSize) || 0) / 1024 / 1024),
50
+ }));
56
51
  }
57
52
 
58
53
  /**
@@ -225,7 +220,11 @@ export async function findOrphanProcesses(opts = {}) {
225
220
  const nameLower = p.name.toLowerCase();
226
221
 
227
222
  // 대상 프로세스만
228
- if (!TARGET_PROCESS_NAMES.some((t) => nameLower === t || nameLower === `${t}.exe`)) {
223
+ if (
224
+ !TARGET_PROCESS_NAMES.some(
225
+ (t) => nameLower === t || nameLower === `${t}.exe`,
226
+ )
227
+ ) {
229
228
  return false;
230
229
  }
231
230
 
@@ -299,7 +298,12 @@ export function createProcessCleanup(opts = {}) {
299
298
  */
300
299
  async function kill() {
301
300
  if (dryRun) {
302
- return lastOrphans.map((p) => ({ pid: p.pid, name: p.name, killed: false, dryRun: true }));
301
+ return lastOrphans.map((p) => ({
302
+ pid: p.pid,
303
+ name: p.name,
304
+ killed: false,
305
+ dryRun: true,
306
+ }));
303
307
  }
304
308
 
305
309
  const results = await Promise.all(
@@ -322,7 +326,12 @@ export function createProcessCleanup(opts = {}) {
322
326
 
323
327
  return { pid: p.pid, name: p.name, killed: true };
324
328
  } catch (err) {
325
- return { pid: p.pid, name: p.name, killed: false, error: String(err.message || err) };
329
+ return {
330
+ pid: p.pid,
331
+ name: p.name,
332
+ killed: false,
333
+ error: String(err.message || err),
334
+ };
326
335
  }
327
336
  }),
328
337
  );