triflux 3.3.0-dev.8 → 4.0.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 (91) hide show
  1. package/README.ko.md +108 -199
  2. package/README.md +108 -199
  3. package/bin/triflux.mjs +2415 -1762
  4. package/hooks/keyword-rules.json +361 -354
  5. package/hooks/pipeline-stop.mjs +5 -2
  6. package/hub/assign-callbacks.mjs +136 -136
  7. package/hub/bridge.mjs +734 -684
  8. package/hub/delegator/contracts.mjs +38 -38
  9. package/hub/delegator/index.mjs +14 -14
  10. package/hub/delegator/schema/delegator-tools.schema.json +250 -250
  11. package/hub/delegator/service.mjs +302 -118
  12. package/hub/delegator/tool-definitions.mjs +35 -35
  13. package/hub/hitl.mjs +67 -67
  14. package/hub/paths.mjs +28 -0
  15. package/hub/pipe.mjs +589 -561
  16. package/hub/pipeline/state.mjs +23 -0
  17. package/hub/public/dashboard.html +349 -0
  18. package/hub/public/tray-icon.ico +0 -0
  19. package/hub/public/tray-icon.png +0 -0
  20. package/hub/router.mjs +782 -782
  21. package/hub/schema.sql +40 -40
  22. package/hub/server.mjs +810 -637
  23. package/hub/store.mjs +706 -706
  24. package/hub/team/cli/commands/attach.mjs +37 -0
  25. package/hub/team/cli/commands/control.mjs +43 -0
  26. package/hub/team/cli/commands/debug.mjs +74 -0
  27. package/hub/team/cli/commands/focus.mjs +53 -0
  28. package/hub/team/cli/commands/interrupt.mjs +36 -0
  29. package/hub/team/cli/commands/kill.mjs +37 -0
  30. package/hub/team/cli/commands/list.mjs +24 -0
  31. package/hub/team/cli/commands/send.mjs +37 -0
  32. package/hub/team/cli/commands/start/index.mjs +87 -0
  33. package/hub/team/cli/commands/start/parse-args.mjs +32 -0
  34. package/hub/team/cli/commands/start/start-in-process.mjs +40 -0
  35. package/hub/team/cli/commands/start/start-mux.mjs +73 -0
  36. package/hub/team/cli/commands/start/start-wt.mjs +69 -0
  37. package/hub/team/cli/commands/status.mjs +87 -0
  38. package/hub/team/cli/commands/stop.mjs +31 -0
  39. package/hub/team/cli/commands/task.mjs +30 -0
  40. package/hub/team/cli/commands/tasks.mjs +13 -0
  41. package/hub/team/{cli.mjs → cli/help.mjs} +38 -99
  42. package/hub/team/cli/index.mjs +39 -0
  43. package/hub/team/cli/manifest.mjs +28 -0
  44. package/hub/team/cli/render.mjs +30 -0
  45. package/hub/team/cli/services/attach-fallback.mjs +54 -0
  46. package/hub/team/cli/services/hub-client.mjs +171 -0
  47. package/hub/team/cli/services/member-selector.mjs +30 -0
  48. package/hub/team/cli/services/native-control.mjs +115 -0
  49. package/hub/team/cli/services/runtime-mode.mjs +60 -0
  50. package/hub/team/cli/services/state-store.mjs +34 -0
  51. package/hub/team/cli/services/task-model.mjs +30 -0
  52. package/hub/team/native-supervisor.mjs +69 -63
  53. package/hub/team/native.mjs +367 -367
  54. package/hub/team/nativeProxy.mjs +217 -173
  55. package/hub/team/pane.mjs +149 -149
  56. package/hub/team/psmux.mjs +946 -946
  57. package/hub/team/session.mjs +608 -608
  58. package/hub/team/staleState.mjs +369 -299
  59. package/hub/tools.mjs +107 -107
  60. package/hub/tray.mjs +332 -0
  61. package/hub/workers/claude-worker.mjs +446 -446
  62. package/hub/workers/codex-mcp.mjs +414 -414
  63. package/hub/workers/delegator-mcp.mjs +1045 -1045
  64. package/hub/workers/factory.mjs +21 -21
  65. package/hub/workers/gemini-worker.mjs +349 -349
  66. package/hub/workers/interface.mjs +41 -41
  67. package/package.json +61 -60
  68. package/scripts/__tests__/keyword-detector.test.mjs +234 -234
  69. package/scripts/hub-ensure.mjs +102 -101
  70. package/scripts/keyword-detector.mjs +272 -272
  71. package/scripts/keyword-rules-expander.mjs +521 -521
  72. package/scripts/lib/keyword-rules.mjs +168 -168
  73. package/scripts/lib/mcp-filter.mjs +642 -642
  74. package/scripts/lib/mcp-server-catalog.mjs +118 -118
  75. package/scripts/mcp-check.mjs +126 -126
  76. package/scripts/preflight-cache.mjs +19 -0
  77. package/scripts/run.cjs +62 -62
  78. package/scripts/setup.mjs +68 -31
  79. package/scripts/test-tfx-route-no-claude-native.mjs +57 -57
  80. package/scripts/tfx-route-worker.mjs +161 -161
  81. package/scripts/tfx-route.sh +1360 -1326
  82. package/skills/tfx-auto/SKILL.md +196 -196
  83. package/skills/tfx-auto-codex/SKILL.md +77 -77
  84. package/skills/tfx-multi/SKILL.md +378 -378
  85. package/hub/team/cli-team-common.mjs +0 -348
  86. package/hub/team/cli-team-control.mjs +0 -393
  87. package/hub/team/cli-team-start.mjs +0 -516
  88. package/hub/team/cli-team-status.mjs +0 -283
  89. package/skills/auto-verify/SKILL.md +0 -145
  90. package/skills/manage-skills/SKILL.md +0 -192
  91. package/skills/verify-implementation/SKILL.md +0 -138
@@ -1,101 +1,102 @@
1
- #!/usr/bin/env node
2
- // SessionStart 훅에서 호출되는 Hub 보장 스크립트.
3
- // - /status 기반 헬스체크
4
- // - 비정상 시 Hub를 detached로 기동
5
-
6
- import { existsSync, readFileSync } from "fs";
7
- import { join, dirname } from "path";
8
- import { homedir } from "os";
9
- import { spawn } from "child_process";
10
- import { fileURLToPath } from "url";
11
-
12
- const LOOPBACK_HOSTS = new Set(["127.0.0.1", "localhost", "::1"]);
13
- const PLUGIN_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
14
- const HUB_PID_FILE = join(homedir(), ".claude", "cache", "tfx-hub", "hub.pid");
15
-
16
- function formatHostForUrl(host) {
17
- return host.includes(":") ? `[${host}]` : host;
18
- }
19
-
20
- function buildHubBaseUrl(host, port) {
21
- return `http://${formatHostForUrl(host)}:${port}`;
22
- }
23
-
24
- function resolveHubTarget() {
25
- const envPortRaw = Number(process.env.TFX_HUB_PORT || "");
26
- const envPort = Number.isFinite(envPortRaw) && envPortRaw > 0 ? envPortRaw : null;
27
- const target = {
28
- host: "127.0.0.1",
29
- port: envPort || 27888,
30
- };
31
-
32
- if (existsSync(HUB_PID_FILE)) {
33
- try {
34
- const info = JSON.parse(readFileSync(HUB_PID_FILE, "utf8"));
35
- if (!envPort) {
36
- const pidPort = Number(info?.port);
37
- if (Number.isFinite(pidPort) && pidPort > 0) target.port = pidPort;
38
- }
39
- if (typeof info?.host === "string") {
40
- const host = info.host.trim();
41
- if (LOOPBACK_HOSTS.has(host)) target.host = host;
42
- }
43
- } catch {
44
- // ignore parse errors and use env/default
45
- }
46
- }
47
-
48
- return target;
49
- }
50
-
51
- async function isHubHealthy(host, port) {
52
- try {
53
- const res = await fetch(`${buildHubBaseUrl(host, port)}/status`, {
54
- signal: AbortSignal.timeout(1000),
55
- });
56
- if (!res.ok) return false;
57
- const data = await res.json();
58
- return data?.hub?.state === "healthy";
59
- } catch {
60
- return false;
61
- }
62
- }
63
-
64
- function startHubDetached(port) {
65
- const serverPath = join(PLUGIN_ROOT, "hub", "server.mjs");
66
- if (!existsSync(serverPath)) return false;
67
-
68
- try {
69
- const child = spawn(process.execPath, [serverPath], {
70
- env: { ...process.env, TFX_HUB_PORT: String(port) },
71
- detached: true,
72
- stdio: "ignore",
73
- });
74
- child.unref();
75
- return true;
76
- } catch {
77
- return false;
78
- }
79
- }
80
-
81
- /** Hub 기동 후 ready 상태까지 대기 (최대 maxWaitMs) */
82
- async function waitForHubReady(host, port, maxWaitMs = 5000) {
83
- const interval = 250;
84
- const deadline = Date.now() + maxWaitMs;
85
- while (Date.now() < deadline) {
86
- if (await isHubHealthy(host, port)) return true;
87
- await new Promise((r) => setTimeout(r, interval));
88
- }
89
- return false;
90
- }
91
-
92
- const { host, port } = resolveHubTarget();
93
- if (!(await isHubHealthy(host, port))) {
94
- const started = startHubDetached(port);
95
- if (started) {
96
- const ready = await waitForHubReady(host, port);
97
- if (!ready) {
98
- console.error("[tfx-hub-ensure] Hub 시작했으나 ready 대기 초과 — MCP 연결 실패 가능");
99
- }
100
- }
101
- }
1
+ #!/usr/bin/env node
2
+ // SessionStart 훅에서 호출되는 Hub 보장 스크립트.
3
+ // - /status 기반 헬스체크
4
+ // - 비정상 시 Hub를 detached로 기동
5
+
6
+ import { existsSync, readFileSync } from "fs";
7
+ import { join, dirname } from "path";
8
+ import { homedir } from "os";
9
+ import { spawn } from "child_process";
10
+ import { fileURLToPath } from "url";
11
+
12
+ const LOOPBACK_HOSTS = new Set(["127.0.0.1", "localhost", "::1"]);
13
+ const PLUGIN_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
14
+ const HUB_PID_FILE = join(homedir(), ".claude", "cache", "tfx-hub", "hub.pid");
15
+
16
+ function formatHostForUrl(host) {
17
+ return host.includes(":") ? `[${host}]` : host;
18
+ }
19
+
20
+ function buildHubBaseUrl(host, port) {
21
+ return `http://${formatHostForUrl(host)}:${port}`;
22
+ }
23
+
24
+ function resolveHubTarget() {
25
+ const envPortRaw = Number(process.env.TFX_HUB_PORT || "");
26
+ const envPort = Number.isFinite(envPortRaw) && envPortRaw > 0 ? envPortRaw : null;
27
+ const target = {
28
+ host: "127.0.0.1",
29
+ port: envPort || 27888,
30
+ };
31
+
32
+ if (existsSync(HUB_PID_FILE)) {
33
+ try {
34
+ const info = JSON.parse(readFileSync(HUB_PID_FILE, "utf8"));
35
+ if (!envPort) {
36
+ const pidPort = Number(info?.port);
37
+ if (Number.isFinite(pidPort) && pidPort > 0) target.port = pidPort;
38
+ }
39
+ if (typeof info?.host === "string") {
40
+ const host = info.host.trim();
41
+ if (LOOPBACK_HOSTS.has(host)) target.host = host;
42
+ }
43
+ } catch {
44
+ // ignore parse errors and use env/default
45
+ }
46
+ }
47
+
48
+ return target;
49
+ }
50
+
51
+ async function isHubHealthy(host, port) {
52
+ try {
53
+ const res = await fetch(`${buildHubBaseUrl(host, port)}/status`, {
54
+ signal: AbortSignal.timeout(1000),
55
+ });
56
+ if (!res.ok) return false;
57
+ const data = await res.json();
58
+ return data?.hub?.state === "healthy";
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
63
+
64
+ function startHubDetached(port) {
65
+ const serverPath = join(PLUGIN_ROOT, "hub", "server.mjs");
66
+ if (!existsSync(serverPath)) return false;
67
+
68
+ try {
69
+ const child = spawn(process.execPath, [serverPath], {
70
+ env: { ...process.env, TFX_HUB_PORT: String(port) },
71
+ detached: true,
72
+ stdio: "ignore",
73
+ windowsHide: true,
74
+ });
75
+ child.unref();
76
+ return true;
77
+ } catch {
78
+ return false;
79
+ }
80
+ }
81
+
82
+ /** Hub 기동 ready 상태까지 대기 (최대 maxWaitMs) */
83
+ async function waitForHubReady(host, port, maxWaitMs = 5000) {
84
+ const interval = 250;
85
+ const deadline = Date.now() + maxWaitMs;
86
+ while (Date.now() < deadline) {
87
+ if (await isHubHealthy(host, port)) return true;
88
+ await new Promise((r) => setTimeout(r, interval));
89
+ }
90
+ return false;
91
+ }
92
+
93
+ const { host, port } = resolveHubTarget();
94
+ if (!(await isHubHealthy(host, port))) {
95
+ const started = startHubDetached(port);
96
+ if (started) {
97
+ const ready = await waitForHubReady(host, port);
98
+ if (!ready) {
99
+ console.error("[tfx-hub-ensure] Hub 시작했으나 ready 대기 초과 — MCP 연결 실패 가능");
100
+ }
101
+ }
102
+ }