triflux 10.9.28 → 10.9.30

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 (117) hide show
  1. package/.claude-plugin/marketplace.json +34 -0
  2. package/.claude-plugin/plugin.json +22 -0
  3. package/bin/triflux.mjs +80 -1
  4. package/config/mcp-registry.json +29 -0
  5. package/hooks/error-context.mjs +1 -1
  6. package/hooks/safety-guard.mjs +42 -4
  7. package/hub/account-broker.mjs +405 -28
  8. package/hub/bridge.mjs +8 -2
  9. package/hub/cli-adapter-base.mjs +4 -1
  10. package/hub/codex-adapter.mjs +3 -1
  11. package/hub/lib/env-detect.mjs +7 -1
  12. package/hub/platform.mjs +6 -0
  13. package/hub/server.mjs +438 -194
  14. package/hub/team/backend.mjs +5 -1
  15. package/hub/team/conductor.mjs +82 -4
  16. package/hub/team/dashboard-open.mjs +41 -10
  17. package/hub/team/headless.mjs +12 -14
  18. package/hub/team/launcher-template.mjs +2 -0
  19. package/hub/team/notify.mjs +21 -2
  20. package/hub/team/process-cleanup.mjs +1 -1
  21. package/hub/team/psmux.mjs +432 -63
  22. package/hub/team/runtime-strategy.mjs +70 -0
  23. package/hub/team/swarm-cli.mjs +188 -0
  24. package/hub/team/swarm-hypervisor.mjs +67 -14
  25. package/hub/team/synapse-registry.mjs +1 -1
  26. package/hub/team/worktree-lifecycle.mjs +103 -36
  27. package/hub/workers/codex-app-server-worker.mjs +1019 -0
  28. package/hub/workers/delegator-mcp.mjs +4 -1
  29. package/hub/workers/factory.mjs +78 -1
  30. package/hub/workers/gemini-worker.mjs +3 -0
  31. package/hub/workers/interface.mjs +17 -1
  32. package/hub/workers/lib/jsonrpc-stdio.mjs +464 -0
  33. package/package.json +62 -21
  34. package/scripts/headless-guard.mjs +3 -1
  35. package/scripts/hub-watchdog.mjs +86 -0
  36. package/scripts/lib/mcp-filter.mjs +17 -0
  37. package/scripts/preinstall.mjs +4 -1
  38. package/scripts/sync-codex-auth.mjs +71 -0
  39. package/scripts/sync-hub-mcp-settings.mjs +185 -0
  40. package/scripts/tfx-route.sh +24 -6
  41. package/skills/tfx-auto/SKILL.md +45 -20
  42. package/skills/tfx-panel/SKILL.md +1 -1
  43. package/skills/tfx-panel/SKILL.md.tmpl +1 -1
  44. package/skills/tfx-prune/SKILL.md +1 -1
  45. package/skills/tfx-prune/SKILL.md.tmpl +1 -1
  46. package/tui/codex-profile.mjs +457 -0
  47. package/tui/core.mjs +266 -0
  48. package/tui/doctor.mjs +375 -0
  49. package/tui/gemini-profile.mjs +299 -0
  50. package/tui/monitor-data.mjs +152 -0
  51. package/tui/monitor.mjs +330 -0
  52. package/tui/setup.mjs +598 -0
  53. package/CLAUDE.md +0 -212
  54. package/references/hosts.json +0 -46
  55. package/skills/tfx-workspace/async-tests/run-tests.sh +0 -203
  56. package/skills/tfx-workspace/evals/evals.json +0 -79
  57. package/skills/tfx-workspace/iteration-1/benchmark.json +0 -524
  58. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +0 -11
  59. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +0 -25
  60. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +0 -154
  61. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +0 -5
  62. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +0 -25
  63. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +0 -126
  64. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +0 -5
  65. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +0 -11
  66. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +0 -25
  67. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +0 -119
  68. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +0 -5
  69. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +0 -25
  70. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +0 -115
  71. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +0 -5
  72. package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +0 -10
  73. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +0 -20
  74. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +0 -86
  75. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +0 -5
  76. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +0 -20
  77. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +0 -81
  78. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +0 -5
  79. package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +0 -12
  80. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +0 -30
  81. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +0 -316
  82. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +0 -5
  83. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +0 -30
  84. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +0 -352
  85. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +0 -5
  86. package/skills/tfx-workspace/iteration-1/review.html +0 -1325
  87. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +0 -12
  88. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +0 -30
  89. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +0 -97
  90. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +0 -5
  91. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +0 -30
  92. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +0 -94
  93. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +0 -5
  94. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +0 -12
  95. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +0 -30
  96. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +0 -209
  97. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +0 -5
  98. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +0 -30
  99. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +0 -193
  100. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +0 -5
  101. package/skills/tfx-workspace/iteration-2/benchmark.json +0 -144
  102. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +0 -13
  103. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +0 -35
  104. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +0 -382
  105. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +0 -5
  106. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +0 -35
  107. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +0 -333
  108. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +0 -5
  109. package/skills/tfx-workspace/iteration-2/review.html +0 -1325
  110. package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +0 -217
  111. package/skills/tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md +0 -77
  112. package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +0 -65
  113. package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +0 -94
  114. package/skills/tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md +0 -82
  115. package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +0 -133
  116. package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +0 -426
  117. package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +0 -101
@@ -0,0 +1,34 @@
1
+ {
2
+ "$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
3
+ "name": "triflux",
4
+ "description": "CLI-first multi-model orchestrator — Codex/Gemini/Claude routing with DAG execution, auto-triage, and cost optimization",
5
+ "owner": {
6
+ "name": "tellang"
7
+ },
8
+ "plugins": [
9
+ {
10
+ "name": "triflux",
11
+ "description": "Tri-CLI orchestrator for Claude Code. Routes tasks across Claude + Codex + Gemini with consensus intelligence, natural language routing, 42 skills, and cross-model review.",
12
+ "version": "10.9.30",
13
+ "author": {
14
+ "name": "tellang"
15
+ },
16
+ "source": {
17
+ "source": "npm",
18
+ "package": "triflux"
19
+ },
20
+ "category": "productivity",
21
+ "homepage": "https://github.com/tellang/triflux",
22
+ "tags": [
23
+ "multi-model",
24
+ "codex",
25
+ "gemini",
26
+ "cli-routing",
27
+ "orchestration",
28
+ "cost-optimization",
29
+ "dag-execution"
30
+ ]
31
+ }
32
+ ],
33
+ "version": "10.9.30"
34
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "triflux",
3
+ "version": "10.9.30",
4
+ "description": "CLI-first multi-model orchestrator for Claude Code — route tasks to Codex, Gemini, and Claude",
5
+ "author": {
6
+ "name": "tellang"
7
+ },
8
+ "repository": "https://github.com/tellang/triflux",
9
+ "homepage": "https://github.com/tellang/triflux",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "claude-code",
13
+ "plugin",
14
+ "codex",
15
+ "gemini",
16
+ "cli-routing",
17
+ "orchestration",
18
+ "multi-model"
19
+ ],
20
+ "skills": "./skills/",
21
+ "hooks": "./hooks/hooks.json"
22
+ }
package/bin/triflux.mjs CHANGED
@@ -308,6 +308,42 @@ const CLI_COMMAND_SCHEMAS = Object.freeze({
308
308
  },
309
309
  ],
310
310
  },
311
+ swarm: {
312
+ usage: "tfx swarm <prd-path> [--dry-run|--json|--filter <shard>]",
313
+ description: "PRD 기반 멀티모델 x 멀티기기 스웜 실행 (#93)",
314
+ subcommands: {
315
+ plan: "tfx swarm plan <prd-path> [--json] — 실행 없이 계획만 출력",
316
+ list: "tfx swarm list [--json] — 활성 스웜 세션 조회 (synapse status)",
317
+ status: "tfx swarm status [--json] — list alias",
318
+ },
319
+ options: [
320
+ {
321
+ name: "--dry-run",
322
+ type: "boolean",
323
+ description: "PRD 분석만 수행, shard를 실행하지 않음",
324
+ },
325
+ {
326
+ name: "--json",
327
+ type: "boolean",
328
+ description: "구조화된 JSON 출력",
329
+ },
330
+ {
331
+ name: "--filter",
332
+ type: "string",
333
+ description: "특정 shard만 실행 (이름 매칭)",
334
+ },
335
+ {
336
+ name: "--max-restarts",
337
+ type: "number",
338
+ description: "shard별 최대 재시작 횟수 (기본 2)",
339
+ },
340
+ {
341
+ name: "--logs-dir",
342
+ type: "string",
343
+ description: "이벤트 로그 출력 디렉토리 오버라이드",
344
+ },
345
+ ],
346
+ },
311
347
  why: {
312
348
  usage: "tfx why <path> [--json]",
313
349
  description: "해당 경로의 마지막 커밋에서 X-Intent 트레일러 추출",
@@ -525,6 +561,10 @@ function whichInShell(cmd, shell) {
525
561
  ["-c", `source ~/.bashrc 2>/dev/null && command -v "${cmd}" 2>/dev/null`],
526
562
  ],
527
563
  cmd: ["cmd", ["/c", "where", cmd]],
564
+ zsh: [
565
+ "zsh",
566
+ ["-c", `source ~/.zshrc 2>/dev/null && command -v "${cmd}" 2>/dev/null`],
567
+ ],
528
568
  pwsh: [
529
569
  "pwsh",
530
570
  [
@@ -852,7 +892,7 @@ function getClaudeRoutingSyncSummary(results) {
852
892
 
853
893
  function checkCliCrossShell(cmd, installHint) {
854
894
  const shells =
855
- process.platform === "win32" ? ["bash", "cmd", "pwsh"] : ["bash"];
895
+ process.platform === "win32" ? ["bash", "cmd", "pwsh"] : ["bash", "zsh"];
856
896
  let anyFound = false;
857
897
  let bashMissing = false;
858
898
  const shellResults = [];
@@ -1186,6 +1226,21 @@ function cmdSetup(options = {}) {
1186
1226
  }
1187
1227
  }
1188
1228
 
1229
+ // ── tmux 기본 셸 확인 (macOS/Linux) ──
1230
+ if (process.platform !== "win32" && which("tmux")) {
1231
+ try {
1232
+ const shellOut = execSync("tmux show-options -g default-shell 2>/dev/null", {
1233
+ encoding: "utf8",
1234
+ timeout: 3000,
1235
+ }).trim();
1236
+ if (shellOut) {
1237
+ ok(`tmux 기본 셸: ${shellOut.split(/\s+/).pop() || "확인 완료"}`);
1238
+ }
1239
+ } catch {
1240
+ /* tmux 서버 미실행 — 무시 */
1241
+ }
1242
+ }
1243
+
1189
1244
  // ── 결과 추적 ──
1190
1245
  const summary = [];
1191
1246
 
@@ -5356,6 +5411,30 @@ async function main() {
5356
5411
  await cmdSynapseStatus(cmdArgs.slice(1), { json: JSON_OUTPUT });
5357
5412
  return;
5358
5413
  }
5414
+ case "swarm": {
5415
+ await checkHubRunning();
5416
+ const { cmdSwarmRun, cmdSwarmPlan, cmdSwarmList } = await import(
5417
+ "../hub/team/swarm-cli.mjs"
5418
+ );
5419
+ const sub = cmdArgs[0] || "";
5420
+ if (sub === "list" || sub === "status") {
5421
+ await cmdSwarmList(cmdArgs.slice(1), { json: JSON_OUTPUT });
5422
+ return;
5423
+ }
5424
+ if (sub === "plan") {
5425
+ await cmdSwarmPlan(cmdArgs.slice(1), { json: JSON_OUTPUT });
5426
+ return;
5427
+ }
5428
+ if (!sub || sub.startsWith("--")) {
5429
+ throw createCliError("PRD 경로가 필요합니다", {
5430
+ exitCode: EXIT_ARG_ERROR,
5431
+ reason: "argError",
5432
+ fix: "tfx swarm <prd-path> [--dry-run|--json|--filter <shard>]",
5433
+ });
5434
+ }
5435
+ await cmdSwarmRun(cmdArgs, { json: JSON_OUTPUT });
5436
+ return;
5437
+ }
5359
5438
  case "why": {
5360
5439
  const { cmdSynapseWhy } = await import("../hub/team/synapse-cli.mjs");
5361
5440
  await cmdSynapseWhy(cmdArgs, { json: JSON_OUTPUT });
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "mcp-registry-schema",
3
+ "version": 1,
4
+ "description": "MCP 서버 중앙 레지스트리 — 진실의 원천",
5
+ "defaults": {
6
+ "transport": "hub-url",
7
+ "hub_base": "http://127.0.0.1:27888"
8
+ },
9
+ "servers": {
10
+ "tfx-hub": {
11
+ "transport": "hub-url",
12
+ "url": "http://127.0.0.1:27888/mcp",
13
+ "safe": true,
14
+ "targets": ["claude", "gemini", "codex"],
15
+ "description": "triflux Hub MCP 서버"
16
+ }
17
+ },
18
+ "policies": {
19
+ "stdio_action": "replace-with-hub",
20
+ "unknown_server_action": "warn",
21
+ "watched_paths": [
22
+ "~/.gemini/settings.json",
23
+ "~/.codex/config.toml",
24
+ "~/.claude/settings.json",
25
+ "~/.claude/settings.local.json",
26
+ ".mcp.json"
27
+ ]
28
+ }
29
+ }
@@ -16,7 +16,7 @@ const ERROR_HINTS = [
16
16
  },
17
17
  {
18
18
  pattern: /EACCES.*permission denied/i,
19
- hint: "권한 부족. Windows에서는 관리자 권한, Unix에서는 chmod/sudo를 확인하세요.",
19
+ hint: "권한 부족. macOS/Linux에서는 chmod/sudo, Windows에서는 관리자 권한을 확인하세요.",
20
20
  },
21
21
  {
22
22
  pattern: /EADDRINUSE/i,
@@ -11,6 +11,22 @@
11
11
  import { existsSync, readFileSync } from "node:fs";
12
12
  import { join } from "node:path";
13
13
 
14
+ // ── 로컬 우회 플래그 ──────────────────────────────────────────
15
+ // LOCAL ONLY — Issue #89 대안 API 구현 전까지 psmux kill 시리즈 우회용.
16
+ // 활성 조건 (OR):
17
+ // 1) env: TFX_CLEANUP_BYPASS=1
18
+ // 2) 파일: .claude/cleanup-bypass 존재 (repo root)
19
+ // 파일 방식은 Claude Code Bash 도구가 훅에 env 전달 못하는 경우에도 동작.
20
+ // 정식 해결: hub/team/psmux.mjs 에 listSessions/killSessionByTitle/pruneStale 노출.
21
+ const CLEANUP_BYPASS = (() => {
22
+ if (process.env.TFX_CLEANUP_BYPASS === "1") return true;
23
+ try {
24
+ return existsSync(join(process.cwd(), ".claude", "cleanup-bypass"));
25
+ } catch {
26
+ return false;
27
+ }
28
+ })();
29
+
14
30
  // ── 차단 규칙 ──────────────────────────────────────────────
15
31
  const BLOCK_RULES = [
16
32
  {
@@ -44,14 +60,16 @@ const BLOCK_RULES = [
44
60
  {
45
61
  pattern: /\bpsmux\s+kill-session\b/i,
46
62
  reason:
47
- "raw psmux kill-session 차단 — WT ConPTY 프리징 위험. 안전 경로: node hub/team/psmux.mjs kill --session <name>",
63
+ "raw psmux kill-session 차단 — WT ConPTY 프리징 위험. 대안: listSessions()/killSessionByTitle()/pruneStale() 또는 node hub/team/psmux.mjs --internal kill-by-title <prefix|/regex/> (또는 TFX_CLEANUP_BYPASS=1/.claude/cleanup-bypass)",
48
64
  skipIfGit: true,
65
+ cleanupBypass: true,
49
66
  },
50
67
  {
51
68
  pattern: /\bpsmux\s+kill-server\b/i,
52
69
  reason:
53
- "psmux kill-server 차단 — 모든 세션이 즉시 종료됩니다. node hub/team/psmux.mjs kill-swarm 사용",
70
+ "psmux kill-server 차단 — 모든 세션이 즉시 종료됩니다. node hub/team/psmux.mjs kill-swarm 사용 (또는 TFX_CLEANUP_BYPASS=1)",
54
71
  skipIfGit: true,
72
+ cleanupBypass: true,
55
73
  },
56
74
  ];
57
75
 
@@ -70,8 +88,11 @@ const WT_DIRECT_BLOCK_MESSAGE =
70
88
  " wt.createTab({ title, command, profile, cwd }) — 새 탭\n" +
71
89
  " wt.splitPane({ direction: 'H'|'V', title, command }) — 패인 분할\n" +
72
90
  " wt.applySplitLayout([{ title, command, direction }]) — 다중 배치\n" +
73
- '사용법: node -e "import(\'./hub/team/wt-manager.mjs\').then(m => { const wt = m.createWtManager(); wt.createTab({ title: \'제목\', command: \'pwsh\' }); })"';
91
+ "사용법: node -e \"import('./hub/team/wt-manager.mjs').then(m => { const wt = m.createWtManager(); wt.createTab({ title: '제목', command: 'pwsh' }); })\"";
74
92
 
93
+ const PSMUX_INTERNAL_WRAPPER_PATTERNS = [
94
+ /node(?:\.exe)?\s+.*hub[\\/]+team[\\/]+psmux\.mjs\s+--internal\s+(?:list|kill-by-title|prune-stale)\b/i,
95
+ ];
75
96
 
76
97
  // ── SSH+PowerShell bash 문법 차단 ────────────────────────────
77
98
  // 원격 기본 셸이 PowerShell인 호스트에 bash redirect/glob을 보내면 오동작
@@ -261,6 +282,10 @@ function main() {
261
282
  return hasSegmentInvocation(cmd, [/\bpsmux\s+kill-(session|server)\b/i]);
262
283
  }
263
284
 
285
+ function isAllowedPsmuxWrapperInvocation(cmd) {
286
+ return hasSegmentInvocation(cmd, PSMUX_INTERNAL_WRAPPER_PATTERNS);
287
+ }
288
+
264
289
  function isWtDirectInvocation(cmd) {
265
290
  return hasSegmentInvocation(cmd, WT_DIRECT_PATTERNS);
266
291
  }
@@ -269,6 +294,10 @@ function main() {
269
294
  blockCommand(WT_DIRECT_BLOCK_MESSAGE, command);
270
295
  }
271
296
 
297
+ if (isAllowedPsmuxWrapperInvocation(command)) {
298
+ process.exit(0);
299
+ }
300
+
272
301
  // 0.1. reflexion 적응형 패널티 — 이전 세션에서 차단된 패턴 사전 경고
273
302
  const penalties = loadReflexionPenalties();
274
303
  if (penalties.length > 0) {
@@ -299,7 +328,10 @@ function main() {
299
328
 
300
329
  // 0.5. SSH → Windows(PowerShell) 호스트에만 bash 문법 전달 차단
301
330
  // macOS/Linux 대상은 bash/zsh이므로 허용. hosts.json OS로 판별.
302
- if (hasSegmentInvocation(command, [/^\s*ssh\s+/i]) && isSshTargetWindows(command)) {
331
+ if (
332
+ hasSegmentInvocation(command, [/^\s*ssh\s+/i]) &&
333
+ isSshTargetWindows(command)
334
+ ) {
303
335
  const segments = command.split(/\s*(?:&&|;|\|\||\|)\s*/);
304
336
  for (const seg of segments) {
305
337
  const sshMatch = seg.trim().match(/^ssh\s+\S+\s+(.*)/s);
@@ -317,12 +349,18 @@ function main() {
317
349
 
318
350
  // 1. BLOCK 체크 — exit 2로 차단
319
351
  for (const rule of BLOCK_RULES) {
352
+ if (rule.cleanupBypass && CLEANUP_BYPASS) continue;
320
353
  if (rule.skipIfGit && !isPsmuxInvocation(command)) continue;
321
354
  if (rule.pattern.test(command)) {
322
355
  blockCommand(`[triflux safety-guard] BLOCKED: ${rule.reason}`, command);
323
356
  }
324
357
  }
325
358
 
359
+ // wt 정리 명령 우회 (TFX_CLEANUP_BYPASS=1 한정). new-tab/split-pane만 차단 유지하려면 아래 조건 세분화.
360
+ if (CLEANUP_BYPASS) {
361
+ // bypass 모드에서는 아래 wt 검사를 이미 통과한 상태. 추가 작업 없음.
362
+ }
363
+
326
364
  // 2. WARN 체크 — allow + additionalContext
327
365
  const warnings = [];
328
366
  for (const rule of WARN_RULES) {