triflux 3.2.0-dev.8 → 3.3.0-dev.1

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 (52) hide show
  1. package/bin/triflux.mjs +1296 -1055
  2. package/hooks/hooks.json +17 -0
  3. package/hooks/keyword-rules.json +20 -4
  4. package/hooks/pipeline-stop.mjs +54 -0
  5. package/hub/bridge.mjs +517 -318
  6. package/hub/hitl.mjs +45 -31
  7. package/hub/pipe.mjs +457 -0
  8. package/hub/pipeline/index.mjs +121 -0
  9. package/hub/pipeline/state.mjs +164 -0
  10. package/hub/pipeline/transitions.mjs +114 -0
  11. package/hub/router.mjs +422 -161
  12. package/hub/schema.sql +14 -0
  13. package/hub/server.mjs +499 -424
  14. package/hub/store.mjs +388 -314
  15. package/hub/team/cli-team-common.mjs +348 -0
  16. package/hub/team/cli-team-control.mjs +393 -0
  17. package/hub/team/cli-team-start.mjs +516 -0
  18. package/hub/team/cli-team-status.mjs +269 -0
  19. package/hub/team/cli.mjs +75 -1475
  20. package/hub/team/dashboard.mjs +1 -9
  21. package/hub/team/native.mjs +190 -130
  22. package/hub/team/nativeProxy.mjs +165 -78
  23. package/hub/team/orchestrator.mjs +15 -20
  24. package/hub/team/pane.mjs +137 -103
  25. package/hub/team/psmux.mjs +506 -0
  26. package/hub/team/session.mjs +393 -330
  27. package/hub/team/shared.mjs +13 -0
  28. package/hub/team/staleState.mjs +299 -0
  29. package/hub/tools.mjs +105 -31
  30. package/hub/workers/claude-worker.mjs +446 -0
  31. package/hub/workers/codex-mcp.mjs +414 -0
  32. package/hub/workers/factory.mjs +18 -0
  33. package/hub/workers/gemini-worker.mjs +349 -0
  34. package/hub/workers/interface.mjs +41 -0
  35. package/hud/hud-qos-status.mjs +1790 -1788
  36. package/package.json +4 -1
  37. package/scripts/__tests__/keyword-detector.test.mjs +8 -8
  38. package/scripts/keyword-detector.mjs +15 -0
  39. package/scripts/lib/keyword-rules.mjs +4 -1
  40. package/scripts/preflight-cache.mjs +72 -0
  41. package/scripts/psmux-steering-prototype.sh +368 -0
  42. package/scripts/setup.mjs +136 -71
  43. package/scripts/tfx-route-worker.mjs +161 -0
  44. package/scripts/tfx-route.sh +485 -91
  45. package/skills/tfx-auto/SKILL.md +90 -564
  46. package/skills/tfx-auto-codex/SKILL.md +1 -3
  47. package/skills/tfx-codex/SKILL.md +1 -4
  48. package/skills/tfx-doctor/SKILL.md +1 -0
  49. package/skills/tfx-gemini/SKILL.md +1 -4
  50. package/skills/tfx-multi/SKILL.md +378 -0
  51. package/skills/tfx-setup/SKILL.md +1 -4
  52. package/skills/tfx-team/SKILL.md +0 -304
package/hub/team/pane.mjs CHANGED
@@ -1,44 +1,58 @@
1
- // hub/team/pane.mjs — pane별 CLI 실행 + stdin 주입
2
- // 의존성: child_process, fs, os, path (Node.js 내장)만 사용
3
- import { writeFileSync, unlinkSync, mkdirSync } from "node:fs";
4
- import { join } from "node:path";
5
- import { tmpdir } from "node:os";
6
- import { detectMultiplexer, tmuxExec } from "./session.mjs";
7
-
8
- /** Windows 경로를 MSYS2/Git Bash tmux용 POSIX 경로로 변환 */
9
- function toTmuxPath(p) {
10
- if (process.platform !== "win32") return p;
11
-
12
- const normalized = p.replace(/\\/g, "/");
13
- const m = normalized.match(/^([A-Za-z]):\/(.*)$/);
14
- if (!m) return normalized;
15
-
16
- const drive = m[1].toLowerCase();
17
- const rest = m[2];
18
- const mux = detectMultiplexer();
19
-
20
- // wsl tmux는 /mnt/c/... 경로를 사용
21
- if (mux === "wsl-tmux") {
22
- return `/mnt/${drive}/${rest}`;
23
- }
24
-
25
- // Git Bash/MSYS tmux는 /c/... 경로를 사용
26
- return `/${drive}/${rest}`;
27
- }
28
-
29
- /** tmux 커맨드 실행 (session.mjs와 동일 패턴) */
30
- function tmux(args, opts = {}) {
31
- return tmuxExec(args, {
32
- encoding: "utf8",
33
- timeout: 10000,
34
- stdio: ["pipe", "pipe", "pipe"],
35
- ...opts,
36
- });
37
- }
38
-
39
- /**
40
- * CLI 에이전트 시작 커맨드 생성
41
- * @param {'codex'|'gemini'|'claude'} cli
1
+ // hub/team/pane.mjs — pane별 CLI 실행 + stdin 주입
2
+ // 의존성: child_process, fs, os, path (Node.js 내장)만 사용
3
+ import { writeFileSync, unlinkSync, mkdirSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { tmpdir } from "node:os";
6
+ import { detectMultiplexer, tmuxExec } from "./session.mjs";
7
+ import { psmuxExec } from "./psmux.mjs";
8
+
9
+ function quoteArg(value) {
10
+ return `"${String(value).replace(/"/g, '\\"')}"`;
11
+ }
12
+
13
+ function getPsmuxSessionName(target) {
14
+ return String(target).split(":")[0]?.trim() || "";
15
+ }
16
+
17
+ /** Windows 경로를 멀티플렉서용 경로로 변환 */
18
+ function toMuxPath(p) {
19
+ if (process.platform !== "win32") return p;
20
+
21
+ const mux = detectMultiplexer();
22
+
23
+ // psmux는 Windows 네이티브 경로 그대로 사용
24
+ if (mux === "psmux") return p;
25
+
26
+ const normalized = p.replace(/\\/g, "/");
27
+ const m = normalized.match(/^([A-Za-z]):\/(.*)$/);
28
+ if (!m) return normalized;
29
+
30
+ const drive = m[1].toLowerCase();
31
+ const rest = m[2];
32
+
33
+ // wsl tmux는 /mnt/c/... 경로를 사용
34
+ if (mux === "wsl-tmux") {
35
+ return `/mnt/${drive}/${rest}`;
36
+ }
37
+
38
+ // Git Bash/MSYS tmux는 /c/... 경로를 사용
39
+ return `/${drive}/${rest}`;
40
+ }
41
+
42
+ /** 멀티플렉서 커맨드 실행 (session.mjs와 동일 패턴) */
43
+ function muxExec(args, opts = {}) {
44
+ const exec = detectMultiplexer() === "psmux" ? psmuxExec : tmuxExec;
45
+ return exec(args, {
46
+ encoding: "utf8",
47
+ timeout: 10000,
48
+ stdio: ["pipe", "pipe", "pipe"],
49
+ ...opts,
50
+ });
51
+ }
52
+
53
+ /**
54
+ * CLI 에이전트 시작 커맨드 생성
55
+ * @param {'codex'|'gemini'|'claude'} cli
42
56
  * @param {{ trustMode?: boolean }} [options]
43
57
  * @returns {string} 실행할 셸 커맨드
44
58
  */
@@ -51,65 +65,85 @@ export function buildCliCommand(cli, options = {}) {
51
65
  return trustMode
52
66
  ? "codex --dangerously-bypass-approvals-and-sandbox --no-alt-screen"
53
67
  : "codex";
54
- case "gemini":
55
- // interactive 모드 — MCP는 ~/.gemini/settings.json에 사전 등록
56
- return "gemini";
57
- case "claude":
58
- // interactive 모드
59
- return "claude";
60
- default:
61
- return cli; // 커스텀 CLI 허용
62
- }
63
- }
64
-
65
- /**
66
- * tmux pane에 CLI 시작
67
- * @param {string} target — 예: tfx-team-abc:0.1
68
- * @param {string} command — 실행할 커맨드
69
- */
70
- export function startCliInPane(target, command) {
71
- // 특수문자 이스케이프: 작은따옴표 내부에서 안전하도록
72
- const escaped = command.replace(/'/g, "'\\''");
73
- tmux(`send-keys -t ${target} '${escaped}' Enter`);
74
- }
75
-
76
- /**
77
- * pane에 프롬프트 주입 (load-buffer + paste-buffer 방식)
78
- * 멀티라인 + 특수문자 안전, 크기 제한 없음
79
- * @param {string} target예: tfx-team-abc:0.1
80
- * @param {string} prompt — 주입할 텍스트
81
- */
82
- export function injectPrompt(target, prompt) {
83
- // 임시 파일에 프롬프트 저장
84
- const tmpDir = join(tmpdir(), "tfx-team");
85
- mkdirSync(tmpDir, { recursive: true });
86
-
87
- // pane ID를 파일명에 포함 (충돌 방지)
88
- const safeTarget = target.replace(/[:.]/g, "-");
89
- const tmpFile = join(tmpDir, `prompt-${safeTarget}-${Date.now()}.txt`);
90
-
91
- try {
92
- writeFileSync(tmpFile, prompt, "utf8");
93
-
94
- // tmux load-buffer → paste-buffer → Enter (Windows 경로 변환 필요)
95
- tmux(`load-buffer ${toTmuxPath(tmpFile)}`);
96
- tmux(`paste-buffer -t ${target}`);
97
- tmux(`send-keys -t ${target} Enter`);
98
- } finally {
99
- // 임시 파일 정리
100
- try {
101
- unlinkSync(tmpFile);
102
- } catch {
103
- // 정리 실패 무시
104
- }
105
- }
106
- }
107
-
108
- /**
109
- * pane에 키 입력 전송
110
- * @param {string} target — 예: tfx-team-abc:0.1
111
- * @param {string} keys — tmux 키 표현 (예: 'C-c', 'Enter')
112
- */
113
- export function sendKeys(target, keys) {
114
- tmux(`send-keys -t ${target} ${keys}`);
115
- }
68
+ case "gemini":
69
+ // interactive 모드 — MCP는 ~/.gemini/settings.json에 사전 등록
70
+ return "gemini";
71
+ case "claude":
72
+ // interactive 모드
73
+ return "claude";
74
+ default:
75
+ return cli; // 커스텀 CLI 허용
76
+ }
77
+ }
78
+
79
+ /**
80
+ * pane에 CLI 시작
81
+ * @param {string} target — 예: tfx-multi-abc:0.1
82
+ * @param {string} command — 실행할 커맨드
83
+ */
84
+ export function startCliInPane(target, command) {
85
+ // CLI 시작도 buffer paste를 재사용해 셸/플랫폼별 quoting 차이를 제거한다.
86
+ injectPrompt(target, command);
87
+ }
88
+
89
+ /**
90
+ * pane에 프롬프트 주입 (load-buffer + paste-buffer 방식)
91
+ * 멀티라인 + 특수문자 안전, 크기 제한 없음
92
+ * @param {string} target 예: tfx-multi-abc:0.1
93
+ * @param {string} prompt주입할 텍스트
94
+ */
95
+ /**
96
+ * pane에 프롬프트 주입
97
+ * @param {string} target — 예: tfx-multi-abc:0.1
98
+ * @param {string} prompt — 주입할 텍스트
99
+ * @param {object} [opts]
100
+ * @param {boolean} [opts.useFileRef] — true면 TUI용 @file 참조 방식 (psmux 전용)
101
+ */
102
+ export function injectPrompt(target, prompt, { useFileRef = false } = {}) {
103
+ const tmpDir = join(tmpdir(), "tfx-multi");
104
+ mkdirSync(tmpDir, { recursive: true });
105
+
106
+ const safeTarget = target.replace(/[:.]/g, "-");
107
+ const tmpFile = join(tmpDir, `prompt-${safeTarget}-${Date.now()}.txt`);
108
+
109
+ // psmux + TUI 앱: @file 참조로 주입 (paste-buffer TUI와 호환 안 됨)
110
+ if (detectMultiplexer() === "psmux" && useFileRef) {
111
+ writeFileSync(tmpFile, prompt, "utf8");
112
+ const filePath = tmpFile.replace(/\\/g, "/");
113
+ psmuxExec(["select-pane", "-t", target]);
114
+ psmuxExec(["send-keys", "-t", target, "-l", `@${filePath}`]);
115
+ psmuxExec(["send-keys", "-t", target, "Enter"]);
116
+ // TUI가 파일을 읽을 시간을 주고 정리
117
+ setTimeout(() => { try { unlinkSync(tmpFile); } catch {} }, 10000);
118
+ return;
119
+ }
120
+
121
+ try {
122
+ writeFileSync(tmpFile, prompt, "utf8");
123
+
124
+ if (detectMultiplexer() === "psmux") {
125
+ const sessionName = getPsmuxSessionName(target);
126
+ psmuxExec(["load-buffer", "-t", sessionName, toMuxPath(tmpFile)]);
127
+ psmuxExec(["select-pane", "-t", target]);
128
+ psmuxExec(["paste-buffer", "-t", target]);
129
+ psmuxExec(["send-keys", "-t", target, "Enter"]);
130
+ return;
131
+ }
132
+
133
+ // tmux load-buffer → paste-buffer → Enter
134
+ muxExec(`load-buffer ${quoteArg(toMuxPath(tmpFile))}`);
135
+ muxExec(`paste-buffer -t ${target}`);
136
+ muxExec(`send-keys -t ${target} Enter`);
137
+ } finally {
138
+ try { unlinkSync(tmpFile); } catch {}
139
+ }
140
+ }
141
+
142
+ /**
143
+ * pane에 키 입력 전송
144
+ * @param {string} target — 예: tfx-multi-abc:0.1
145
+ * @param {string} keys — tmux 키 표현 (예: 'C-c', 'Enter')
146
+ */
147
+ export function sendKeys(target, keys) {
148
+ muxExec(`send-keys -t ${target} ${keys}`);
149
+ }