@triflux/core 10.48.1 → 10.50.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 (46) hide show
  1. package/hooks/agy-session-hook.mjs +1 -1
  2. package/hooks/claude-cwd-projection-refresh.mjs +7 -79
  3. package/hooks/codex-session-hook.mjs +3 -8
  4. package/hooks/hook-orchestrator.mjs +8 -648
  5. package/hooks/lib/resolve-root.mjs +1 -1
  6. package/hub/bridge.mjs +290 -51
  7. package/hub/team/claude-daemon-control.mjs +54 -6
  8. package/hub/team/claude-transcript.mjs +240 -0
  9. package/hub/team/session-context.mjs +203 -0
  10. package/hud/constants.mjs +10 -0
  11. package/hud/hud-qos-status.mjs +23 -4
  12. package/hud/providers/gemini.mjs +4 -1
  13. package/hud/renderers.mjs +10 -6
  14. package/package.json +2 -2
  15. package/scripts/lib/doctor-env-checks.mjs +0 -85
  16. package/scripts/lib/legacy-hook-cleanup.mjs +230 -0
  17. package/scripts/lib/legacy-mcp-cleanup.mjs +772 -0
  18. package/scripts/lib/mcp-guard-engine.mjs +44 -271
  19. package/scripts/lib/mcp-manifest.mjs +2 -2
  20. package/scripts/lib/session-presence.mjs +249 -0
  21. package/hooks/agent-route-guard.mjs +0 -113
  22. package/hooks/cross-review-tracker.mjs +0 -135
  23. package/hooks/cto-north-star-brief.mjs +0 -133
  24. package/hooks/error-context.mjs +0 -185
  25. package/hooks/hook-adaptive-collector.mjs +0 -88
  26. package/hooks/hook-manager.mjs +0 -426
  27. package/hooks/hook-registry.json +0 -367
  28. package/hooks/hooks.json +0 -149
  29. package/hooks/keyword-rules.json +0 -878
  30. package/hooks/mcp-config-watcher.mjs +0 -84
  31. package/hooks/permission-safe-allow.mjs +0 -267
  32. package/hooks/pipeline-stop.mjs +0 -435
  33. package/hooks/post-tool-tips.mjs +0 -306
  34. package/hooks/pre-compact-snapshot.mjs +0 -181
  35. package/hooks/safety-guard.mjs +0 -466
  36. package/hooks/session-end-cleanup.mjs +0 -360
  37. package/hooks/session-start-fast.mjs +0 -579
  38. package/hooks/session-start-lake.mjs +0 -81
  39. package/hooks/subagent-tracker.mjs +0 -281
  40. package/hooks/subagent-verifier.mjs +0 -85
  41. package/scripts/lib/cross-review-utils.mjs +0 -59
  42. package/scripts/lib/hook-utils.mjs +0 -16
  43. package/scripts/lib/keyword-rules.mjs +0 -219
  44. package/scripts/lib/mcp-gateway-health-check.mjs +0 -224
  45. package/scripts/lib/mcp-gateway-servers.mjs +0 -70
  46. package/scripts/lib/mcp-gateway-wrapper-check.mjs +0 -35
@@ -1,224 +0,0 @@
1
- // scripts/lib/mcp-gateway-health-check.mjs
2
- //
3
- // `~/.local/state/triflux/mcp-gateway.out.log` 파싱 헬퍼.
4
- // install-mcp-gateway-startup.mjs 가 LaunchAgent/systemd 로 띄운 gateway daemon
5
- // (mcp-gateway-start.mjs) 의 stdout 로그에서 다음 신호를 추출한다.
6
- //
7
- // [WARN] <server> skipped — missing env: <KEY> → missing-env finding
8
- // [START] <server> on :<port> → started
9
- // [SKIP] <server> already running on :<port> → started (preExisting)
10
- // [SKIP] <server> — manifest에서 비활성 → manifest-disabled skip
11
- //
12
- // 호출자는 `findings` 만 보고 doctor 진단을 출력할 수 있다.
13
- // log 파일이 없으면 `available: false` 로 반환하므로, gateway 미설치 환경에서는
14
- // 침묵한다 (false-positive 방지).
15
-
16
- import { readFileSync, statSync } from "node:fs";
17
- import { createConnection } from "node:net";
18
- import { homedir } from "node:os";
19
- import { join } from "node:path";
20
-
21
- const DEFAULT_LOG_PATH = join(
22
- homedir(),
23
- ".local",
24
- "state",
25
- "triflux",
26
- "mcp-gateway.out.log",
27
- );
28
-
29
- // producer (scripts/mcp-gateway-start.mjs:146) 가 missing env 를 `missing.join(", ")`
30
- // 로 출력하므로 detail 은 공백 포함 다중 키 (예: "JIRA_API_TOKEN, JIRA_EMAIL, JIRA_INSTANCE_URL")
31
- // 일 수 있다. line 끝까지 캡처한다.
32
- const MISSING_ENV_RE = /^\[WARN\]\s+(\S+)\s+skipped\s+—\s+missing env:\s+(.+)$/;
33
- const START_RE = /^\[START\]\s+(\S+)\s+on\s+:(\d+)$/;
34
- const SKIP_RUNNING_RE = /^\[SKIP\]\s+(\S+)\s+already running on\s+:(\d+)$/;
35
- const SKIP_DISABLED_RE = /^\[SKIP\]\s+(\S+)\s+—\s+manifest에서 비활성$/;
36
-
37
- function probePortListening(
38
- port,
39
- { host = "127.0.0.1", timeoutMs = 800 } = {},
40
- ) {
41
- return new Promise((resolve) => {
42
- const socket = createConnection({ host, port });
43
- let settled = false;
44
-
45
- function finish(value) {
46
- if (settled) return;
47
- settled = true;
48
- socket.destroy();
49
- resolve(value);
50
- }
51
-
52
- socket.once("connect", () => finish(true));
53
- socket.once("error", () => finish(false));
54
- socket.setTimeout(timeoutMs, () => finish(false));
55
- });
56
- }
57
-
58
- export function checkMcpGatewayHealth({
59
- fs = { readFileSync, statSync },
60
- logPath = DEFAULT_LOG_PATH,
61
- } = {}) {
62
- try {
63
- fs.statSync(logPath);
64
- } catch {
65
- return {
66
- available: false,
67
- logPath,
68
- findings: [],
69
- started: [],
70
- skipped: [],
71
- };
72
- }
73
-
74
- let text;
75
- try {
76
- text = fs.readFileSync(logPath, "utf8");
77
- } catch (error) {
78
- return {
79
- available: true,
80
- logPath,
81
- findings: [
82
- {
83
- server: null,
84
- reason: "log-read-error",
85
- detail: error?.message || String(error),
86
- },
87
- ],
88
- started: [],
89
- skipped: [],
90
- };
91
- }
92
-
93
- // log 는 append-only 라 옛 [WARN] 라인 뒤에 새 [START] 라인이 올 수 있다.
94
- // server 별 마지막 이벤트가 현재 상태이므로 그것만 finding 으로 환원한다.
95
- // (예: BRAVE_API_KEY missing 으로 한 번 skip → secrets 추가 후 restart 로 start)
96
- const lastEvent = new Map();
97
-
98
- for (const rawLine of text.split("\n")) {
99
- const line = rawLine.trim();
100
- if (!line) continue;
101
-
102
- let m;
103
- if ((m = MISSING_ENV_RE.exec(line))) {
104
- lastEvent.set(m[1], {
105
- type: "missing-env",
106
- detail: m[2],
107
- });
108
- continue;
109
- }
110
- if ((m = START_RE.exec(line))) {
111
- lastEvent.set(m[1], {
112
- type: "started",
113
- port: Number(m[2]),
114
- preExisting: false,
115
- });
116
- continue;
117
- }
118
- if ((m = SKIP_RUNNING_RE.exec(line))) {
119
- lastEvent.set(m[1], {
120
- type: "started",
121
- port: Number(m[2]),
122
- preExisting: true,
123
- });
124
- continue;
125
- }
126
- if ((m = SKIP_DISABLED_RE.exec(line))) {
127
- lastEvent.set(m[1], {
128
- type: "manifest-disabled",
129
- });
130
- }
131
- }
132
-
133
- const findings = [];
134
- const started = [];
135
- const skipped = [];
136
- for (const [server, ev] of lastEvent) {
137
- if (ev.type === "missing-env") {
138
- findings.push({ server, reason: "missing-env", detail: ev.detail });
139
- } else if (ev.type === "started") {
140
- started.push({ server, port: ev.port, preExisting: ev.preExisting });
141
- } else if (ev.type === "manifest-disabled") {
142
- skipped.push({ server, reason: "manifest-disabled" });
143
- }
144
- }
145
-
146
- return {
147
- available: true,
148
- logPath,
149
- findings,
150
- started,
151
- skipped,
152
- };
153
- }
154
-
155
- export async function checkMcpGatewayHealthLive({
156
- probePort = probePortListening,
157
- ...options
158
- } = {}) {
159
- const result = checkMcpGatewayHealth(options);
160
- if (!result.available || result.started.length === 0) {
161
- return {
162
- ...result,
163
- live: [],
164
- };
165
- }
166
-
167
- const live = [];
168
- const findings = [...result.findings];
169
- for (const entry of result.started) {
170
- const listening = await probePort(entry.port);
171
- live.push({ ...entry, listening });
172
- if (!listening) {
173
- findings.push({
174
- server: entry.server,
175
- reason: "port-down",
176
- detail: `expected listener on :${entry.port}`,
177
- port: entry.port,
178
- });
179
- }
180
- }
181
-
182
- return {
183
- ...result,
184
- findings,
185
- live,
186
- };
187
- }
188
-
189
- export function summarizeMcpGatewayHealth(result) {
190
- if (!result?.available) {
191
- return {
192
- level: "skip",
193
- message: "mcp-gateway log 없음 (gateway 미설치 또는 미실행)",
194
- };
195
- }
196
- if (result.findings.length === 0) {
197
- return {
198
- level: "ok",
199
- message: `mcp-gateway healthy (${result.started.length}개 server up)`,
200
- };
201
- }
202
- const missingEnv = result.findings.filter((f) => f.reason === "missing-env");
203
- if (missingEnv.length > 0) {
204
- const list = missingEnv.map((f) => `${f.server} (${f.detail})`).join(", ");
205
- return {
206
- level: "warn",
207
- message: `${missingEnv.length}개 MCP 서버가 missing env 로 skip: ${list}`,
208
- fix: "secrets 를 ~/.config/triflux/secrets.env 에 추가하고 `launchctl kickstart -k gui/$(id -u)/com.tellang.mcp-gateway` (macOS) 또는 `systemctl --user restart mcp-gateway` (linux)",
209
- };
210
- }
211
- const portDown = result.findings.filter((f) => f.reason === "port-down");
212
- if (portDown.length > 0) {
213
- const list = portDown.map((f) => `${f.server} (:${f.port})`).join(", ");
214
- return {
215
- level: "warn",
216
- message: `${portDown.length}개 MCP gateway port down: ${list}`,
217
- fix: "gateway 를 재기동하세요: `node scripts/mcp-gateway-start.mjs` 또는 macOS `launchctl kickstart -k gui/$(id -u)/com.tellang.mcp-gateway`",
218
- };
219
- }
220
- return {
221
- level: "warn",
222
- message: `${result.findings.length}개 gateway 이슈`,
223
- };
224
- }
@@ -1,70 +0,0 @@
1
- const SERVER_DEFINITIONS = Object.freeze([
2
- {
3
- name: "context7",
4
- port: 8100,
5
- stdioCmd: "npx -y @upstash/context7-mcp@latest",
6
- envVars: [],
7
- },
8
- {
9
- name: "brave-search",
10
- port: 8101,
11
- stdioCmd: "npx -y @brave/brave-search-mcp-server",
12
- envVars: ["BRAVE_API_KEY"],
13
- },
14
- {
15
- name: "exa",
16
- port: 8102,
17
- stdioCmd: "npx -y exa-mcp-server",
18
- envVars: ["EXA_API_KEY"],
19
- },
20
- {
21
- name: "tavily",
22
- port: 8103,
23
- stdioCmd: "npx -y tavily-mcp@latest",
24
- envVars: ["TAVILY_API_KEY"],
25
- },
26
- {
27
- name: "jira",
28
- port: 8104,
29
- stdioCmd: "npx -y mcp-jira-cloud@latest",
30
- envVars: ["JIRA_API_TOKEN", "JIRA_EMAIL", "JIRA_INSTANCE_URL"],
31
- },
32
- {
33
- name: "serena",
34
- port: 8105,
35
- stdioCmd:
36
- "uvx --from git+https://github.com/oraios/serena serena start-mcp-server",
37
- envVars: [],
38
- },
39
- {
40
- name: "notion",
41
- port: 8106,
42
- stdioCmd: "npx -y @notionhq/notion-mcp-server",
43
- envVars: ["NOTION_TOKEN"],
44
- },
45
- {
46
- name: "notion-guest",
47
- port: 8107,
48
- stdioCmd: "npx -y @notionhq/notion-mcp-server",
49
- envVars: ["NOTION_TOKEN"],
50
- },
51
- ]);
52
-
53
- export const SERVERS = SERVER_DEFINITIONS.map((server) => ({
54
- name: server.name,
55
- port: server.port,
56
- cmd: server.stdioCmd,
57
- envVars: [...server.envVars],
58
- }));
59
-
60
- export const GATEWAY_SERVERS = SERVER_DEFINITIONS.map((server) => ({
61
- name: server.name,
62
- port: server.port,
63
- stdioCmd: server.stdioCmd,
64
- }));
65
-
66
- export function serversWithSatisfiedEnv(env = process.env) {
67
- return SERVERS.filter((server) =>
68
- server.envVars.every((envName) => Boolean(env[envName])),
69
- );
70
- }
@@ -1,35 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import os from "node:os";
3
- import path from "node:path";
4
-
5
- export async function checkWrapperSourcing({
6
- wrapperPath = path.join(
7
- os.homedir(),
8
- ".local",
9
- "bin",
10
- "mcp-gateway-wrapper.sh",
11
- ),
12
- marker = "secrets.env",
13
- } = {}) {
14
- let text;
15
- try {
16
- text = await readFile(wrapperPath, "utf8");
17
- } catch (error) {
18
- const result = { status: "missing", wrapperPath };
19
- if (error?.code !== "ENOENT") {
20
- result.message = `wrapper read failed: ${error?.message || String(error)}`;
21
- }
22
- return result;
23
- }
24
-
25
- if (text.includes(marker)) {
26
- return { status: "ok", wrapperPath };
27
- }
28
-
29
- return {
30
- status: "warn",
31
- wrapperPath,
32
- message: "wrapper exists but does not source secrets.env",
33
- suggestedFix: "Re-run: node scripts/install-mcp-gateway-startup.mjs",
34
- };
35
- }