triflux 9.2.4 → 9.4.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 (38) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/bin/triflux.mjs +48 -217
  3. package/hub/pipe.mjs +1 -8
  4. package/hub/team/psmux.mjs +21 -1
  5. package/hub/workers/claude-worker.mjs +1 -24
  6. package/hub/workers/codex-mcp.mjs +0 -4
  7. package/hub/workers/gemini-worker.mjs +108 -28
  8. package/hub/workers/interface.mjs +0 -1
  9. package/hub/workers/worker-utils.mjs +26 -0
  10. package/package.json +1 -1
  11. package/scripts/__tests__/remote-spawn.test.mjs +17 -3
  12. package/scripts/cross-review-gate.mjs +11 -65
  13. package/scripts/cross-review-tracker.mjs +10 -51
  14. package/scripts/headless-guard.mjs +5 -17
  15. package/scripts/lib/cross-review-utils.mjs +51 -0
  16. package/scripts/lib/hook-utils.mjs +14 -0
  17. package/scripts/lib/mcp-filter.mjs +10 -1
  18. package/scripts/psmux-safety-guard.mjs +64 -0
  19. package/scripts/remote-spawn.mjs +77 -23
  20. package/scripts/session-spawn-helper.mjs +2 -1
  21. package/scripts/setup.mjs +36 -6
  22. package/scripts/tfx-route.sh +93 -10
  23. package/skills/tfx-auto/SKILL.md +4 -1
  24. package/skills/tfx-auto-codex/SKILL.md +3 -1
  25. package/skills/tfx-autopilot/SKILL.md +1 -1
  26. package/skills/tfx-autoresearch/SKILL.md +2 -0
  27. package/skills/tfx-codex/SKILL.md +3 -1
  28. package/skills/tfx-consensus/SKILL.md +5 -3
  29. package/skills/tfx-fullcycle/SKILL.md +1 -1
  30. package/skills/tfx-gemini/SKILL.md +3 -1
  31. package/skills/tfx-hooks/SKILL.md +216 -0
  32. package/skills/tfx-multi/SKILL.md +4 -1
  33. package/skills/tfx-plan/SKILL.md +1 -1
  34. package/skills/tfx-psmux-rules/SKILL.md +100 -13
  35. package/skills/tfx-ralph/SKILL.md +11 -66
  36. package/skills/tfx-research/SKILL.md +1 -1
  37. package/skills/tfx-review/SKILL.md +1 -1
  38. package/skills/tfx-setup/SKILL.md +17 -1
@@ -617,12 +617,23 @@ codex_gte() {
617
617
  _GEMINI_PROFILE_CACHE=""
618
618
  resolve_gemini_profile() {
619
619
  local profile="$1"
620
+ if [[ "$profile" == gemini-* ]]; then
621
+ echo "$profile"
622
+ return
623
+ fi
620
624
  if [[ -z "$_GEMINI_PROFILE_CACHE" && -f "$GEMINI_PROFILES_PATH" ]]; then
621
625
  _GEMINI_PROFILE_CACHE=$(cat "$GEMINI_PROFILES_PATH" 2>/dev/null || echo "{}")
622
626
  fi
627
+ local settings_path="${HOME}/.gemini/settings.json"
628
+ local settings_cache="{}"
629
+ if [[ -f "$settings_path" ]]; then
630
+ settings_cache=$(cat "$settings_path" 2>/dev/null || echo "{}")
631
+ fi
623
632
  local result
624
633
  result=$("$NODE_BIN" -e "
625
634
  const name = process.argv[1];
635
+ const primaryRaw = process.argv[2] || '{}';
636
+ const settingsRaw = process.argv[3] || '{}';
626
637
  const defaults = {
627
638
  pro31: 'gemini-3.1-pro-preview',
628
639
  flash3: 'gemini-3-flash-preview',
@@ -630,13 +641,74 @@ resolve_gemini_profile() {
630
641
  flash25: 'gemini-2.5-flash',
631
642
  lite25: 'gemini-2.5-flash-lite'
632
643
  };
633
- try {
634
- const cfg = JSON.parse(process.argv[2] || '{}');
635
- const p = cfg.profiles?.[name];
636
- if (p?.model) { process.stdout.write(p.model); return; }
637
- } catch {}
638
- process.stdout.write(defaults[name] || 'gemini-3.1-pro-preview');
639
- " "$profile" "$_GEMINI_PROFILE_CACHE" 2>/dev/null)
644
+
645
+ if (typeof name === 'string' && name.startsWith('gemini-')) {
646
+ process.stdout.write(name);
647
+ process.exit(0);
648
+ }
649
+
650
+ const parseJson = (raw) => {
651
+ try {
652
+ const parsed = JSON.parse(raw);
653
+ return parsed && typeof parsed === 'object' ? parsed : {};
654
+ } catch {
655
+ return {};
656
+ }
657
+ };
658
+
659
+ const getModelValue = (entry) => {
660
+ if (!entry) return '';
661
+ if (typeof entry === 'string') return entry;
662
+ if (typeof entry !== 'object') return '';
663
+ if (typeof entry.model === 'string') return entry.model;
664
+ if (typeof entry.name === 'string' && entry.name.startsWith('gemini-')) return entry.name;
665
+ if (entry.model && typeof entry.model.name === 'string') return entry.model.name;
666
+ return '';
667
+ };
668
+
669
+ const getProfileBuckets = (cfg) => {
670
+ const buckets = [];
671
+ if (cfg.profiles && typeof cfg.profiles === 'object') buckets.push(cfg.profiles);
672
+ if (cfg.model?.profiles && typeof cfg.model.profiles === 'object') buckets.push(cfg.model.profiles);
673
+ if (cfg.modelProfiles && typeof cfg.modelProfiles === 'object') buckets.push(cfg.modelProfiles);
674
+ if (cfg.models && typeof cfg.models === 'object') buckets.push(cfg.models);
675
+ return buckets;
676
+ };
677
+
678
+ const getDefaultModel = (cfg) => {
679
+ return (
680
+ (typeof cfg.defaultModel === 'string' && cfg.defaultModel) ||
681
+ (typeof cfg.default_profile === 'string' && cfg.default_profile) ||
682
+ (typeof cfg.defaultProfile === 'string' && cfg.defaultProfile) ||
683
+ (typeof cfg.model === 'string' && cfg.model) ||
684
+ (typeof cfg.model?.default === 'string' && cfg.model.default) ||
685
+ ''
686
+ );
687
+ };
688
+
689
+ const sources = [parseJson(primaryRaw), parseJson(settingsRaw)];
690
+ for (const cfg of sources) {
691
+ for (const bucket of getProfileBuckets(cfg)) {
692
+ const value = getModelValue(bucket[name]);
693
+ if (value) {
694
+ process.stdout.write(value);
695
+ process.exit(0);
696
+ }
697
+ }
698
+ }
699
+
700
+ if (name === 'default') {
701
+ for (const cfg of sources) {
702
+ const value = getDefaultModel(cfg);
703
+ if (value) {
704
+ process.stdout.write(value);
705
+ process.exit(0);
706
+ }
707
+ }
708
+ }
709
+
710
+ process.stdout.write(defaults[name] || defaults.pro31);
711
+ " "$profile" "$_GEMINI_PROFILE_CACHE" "$settings_cache" 2>/dev/null)
640
712
  echo "${result:-gemini-3.1-pro-preview}"
641
713
  }
642
714
 
@@ -853,6 +925,7 @@ CODEX_MCP_TRANSPORT_EXIT_CODE=70
853
925
 
854
926
  apply_cli_mode() {
855
927
  local codex_base="--dangerously-bypass-approvals-and-sandbox --skip-git-repo-check"
928
+ local gemini_tier=""
856
929
 
857
930
  case "$TFX_CLI_MODE" in
858
931
  codex)
@@ -894,7 +967,12 @@ apply_cli_mode() {
894
967
  *)
895
968
  CLI_ARGS="-m $(resolve_gemini_profile flash3) -y --prompt"; CLI_EFFORT="flash3" ;;
896
969
  esac
897
- echo "[tfx-route] TFX_CLI_MODE=gemini: $AGENT_TYPE → gemini($CLI_EFFORT)로 리매핑" >&2
970
+ case "$CLI_EFFORT" in
971
+ pro*) gemini_tier="pro" ;;
972
+ flash*|lite*) gemini_tier="flash" ;;
973
+ *) gemini_tier="$CLI_EFFORT" ;;
974
+ esac
975
+ echo "[tfx-route] TFX_CLI_MODE=gemini: $AGENT_TYPE → gemini($gemini_tier)로 리매핑" >&2
898
976
  fi ;;
899
977
  auto)
900
978
  if [[ "$CLI_TYPE" == "codex" ]] && ! command -v "$CODEX_BIN" &>/dev/null; then
@@ -1226,7 +1304,7 @@ _gemini_run_once() {
1226
1304
  else
1227
1305
  "$TIMEOUT_BIN" "$TIMEOUT_SEC" "$CLI_CMD" "${g_args[@]}" "$prompt" >"$STDOUT_LOG" 2>"$STDERR_LOG" &
1228
1306
  fi
1229
- echo "$!"
1307
+ GEMINI_RUN_PID=$!
1230
1308
  }
1231
1309
 
1232
1310
  gemini_with_retry() {
@@ -1243,7 +1321,12 @@ gemini_with_retry() {
1243
1321
  while (( attempt < max_retries )); do
1244
1322
  exit_code_local=0
1245
1323
  local pid
1246
- pid=$(_gemini_run_once "$use_tee_flag" "$prompt" "${g_args[@]}")
1324
+ _gemini_run_once "$use_tee_flag" "$prompt" "${g_args[@]}"
1325
+ pid="${GEMINI_RUN_PID:-}"
1326
+ if [[ -z "$pid" ]]; then
1327
+ echo "[tfx-route] Gemini: worker pid 획득 실패" >&2
1328
+ return 1
1329
+ fi
1247
1330
 
1248
1331
  local health_ok=true
1249
1332
  local intervals=(1 2 3 5 8)
@@ -1,6 +1,9 @@
1
1
  ---
2
2
  name: tfx-auto
3
- description: 통합 CLI 오케스트레이터. 커맨드 숏컷(단일) + 자동 분류/분해(병렬) + 수동 병렬. tfx-route.sh 기반.
3
+ description: >
4
+ 통합 CLI 오케스트레이터. 커맨드 숏컷(단일) + 자동 분류/분해(병렬) + 수동 병렬. tfx-route.sh 기반.
5
+ '코드 짜줘', '구현해줘', '만들어줘', '수정해줘', '고쳐줘', 'implement', 'build', 'fix' 같은
6
+ 구현/수정 요청에 사용. CLI 라우팅이 필요한 모든 작업에 적극 활용.
4
7
  triggers:
5
8
  - tfx-auto
6
9
  - implement
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: tfx-auto-codex
3
- description: Codex 리드형 tfx-auto. Claude 네이티브 역할을 Codex로 치환하고 Gemini 사용은 유지합니다.
3
+ description: >
4
+ Codex 리드형 tfx-auto. Claude 네이티브 역할을 Codex로 치환하고 Gemini 사용은 유지합니다.
5
+ '코덱스 리드', '코덱스가 주도', 'codex lead' 같은 요청에 사용.
4
6
  triggers:
5
7
  - tfx-auto-codex
6
8
  argument-hint: "\"작업 설명\" | N:agent_type \"작업 설명\""
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: tfx-autopilot
3
- description: "간단한 작업을 자율적으로 구현해야 때 사용한다. 'autopilot', '자동으로', '알아서 해', '그냥 해줘' 같은 요청에 반드시 사용. 명확한 단일 작업을 빠르게 자동 구현+검증할 때 적극 활용."
3
+ description: "단일 파일/모듈 수준의 간단한 작업을 자율 구현할 때 사용한다. 구현+검증까지 5분 이내 완료 가능한 작업, '간단히', '빠르게', '하나만', '자동으로', '알아서 해', '그냥 해줘' 같은 수식어가 있을 사용. 복잡한 멀티파일 작업은 tfx-fullcycle을 사용하세요."
4
4
  triggers:
5
5
  - autopilot
6
6
  - 자동
@@ -3,6 +3,8 @@ name: tfx-autoresearch
3
3
  description: >
4
4
  자율 웹 리서치 → 실행 가능 계획 도출.
5
5
  주제를 받아 웹 검색 → 정보 수집 → 분석 → 구조화된 리서치 보고서 생성.
6
+ '알아봐', '조사해', '리서치해줘', '검색하고 정리해', 'research and plan' 같은 요청에 반드시 사용.
7
+ 웹 검색 후 실행 가능한 계획까지 도출이 필요할 때 적극 활용.
6
8
  triggers:
7
9
  - autoresearch
8
10
  - 자율 리서치
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: tfx-codex
3
- description: Codex-Only 오케스트레이터. tfx-auto 워크플로우를 Codex 전용으로 고정합니다.
3
+ description: >
4
+ Codex-Only 오케스트레이터. tfx-auto 워크플로우를 Codex 전용으로 고정합니다.
5
+ '코덱스로 해줘', '코덱스한테 시켜', 'codex로', 'Codex 전용' 같은 요청에 반드시 사용.
4
6
  triggers:
5
7
  - tfx-codex
6
8
  argument-hint: "\"작업 설명\" | N:codex \"작업 설명\""
@@ -1,8 +1,10 @@
1
1
  ---
2
2
  name: tfx-consensus
3
- description: 3자 합의 엔진 — 모든 Deep 스킬의 핵심 인프라. Claude/Codex/Gemini 독립 분석 결과를 교차검증하여 편향 없는 합의를 도출한다.
4
- triggers: []
5
- argument-hint: "(내부 전용 Deep 스킬이 자동 호출)"
3
+ description: >
4
+ 3자 합의 엔진 — 모든 Deep 스킬의 핵심 인프라. Claude/Codex/Gemini 독립 분석 결과를 교차검증하여 편향 없는 합의를 도출한다.
5
+ 독립 실행도 가능: '합의로 분석해', '3자 합의', 'consensus' 같은 요청 시 직접 호출 가능.
6
+ triggers: [consensus, 합의]
7
+ argument-hint: "<분석 주제 또는 컨텍스트> (Deep 스킬 내부 자동 호출 또는 직접 사용 가능)"
6
8
  ---
7
9
 
8
10
  # tfx-consensus — Tri-CLI Consensus Engine
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: tfx-fullcycle
3
- description: "복잡한 기능을 처음부터 끝까지 자율 개발해야 때 사용한다. 'deep autopilot', ' 오토', '처음부터 끝까지', 'full auto', '전체 파이프라인으로' 같은 요청에 사용. 설계→계획→구현→QA→검증 전체 사이클이 필요한 대규모 기능에 적극 활용."
3
+ description: "여러 파일/모듈에 걸친 복잡한 기능을 처음부터 끝까지 자율 개발할 때 사용한다. 설계→계획→구현→QA→검증 전체 파이프라인이 필요한 작업, '처음부터 끝까지', '전체', '완전히', ' 오토', 'full auto', '전체 파이프라인으로' 같은 수식어가 있을 사용. 단순 단일 작업은 tfx-autopilot이 빠릅니다."
4
4
  triggers:
5
5
  - deep autopilot
6
6
  - 풀 오토
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: tfx-gemini
3
- description: Gemini-Only 오케스트레이터. tfx-auto 워크플로우를 Gemini 전용으로 고정합니다.
3
+ description: >
4
+ Gemini-Only 오케스트레이터. tfx-auto 워크플로우를 Gemini 전용으로 고정합니다.
5
+ '제미나이로 해줘', '제미나이한테 시켜', 'gemini로', 'Gemini 전용' 같은 요청에 반드시 사용.
4
6
  triggers:
5
7
  - tfx-gemini
6
8
  argument-hint: "\"작업 설명\" | N:gemini \"작업 설명\""
@@ -0,0 +1,216 @@
1
+ ---
2
+ name: tfx-hooks
3
+ description: >
4
+ Claude Code 훅 우선순위 관리자. AskUserQuestion 기반 인터랙티브 UI로
5
+ 훅 스캔, 우선순위 조정, 오케스트레이터 적용/복원, 개별 훅 토글을 수행합니다.
6
+ 사용자가 훅, hooks, 훅 관리, hook priority, 훅 우선순위, 오케스트레이터,
7
+ orchestrator, 훅 설정, hook 설정, 훅 순서, hook order를 언급할 때마다
8
+ 반드시 이 스킬을 사용하세요.
9
+ Use when: hooks, 훅, hook priority, 훅 관리, orchestrator, 오케스트레이터, 훅 설정, 훅 순서
10
+ triggers:
11
+ - tfx-hooks
12
+ argument-hint: "[scan|apply|restore]"
13
+ ---
14
+
15
+ # tfx-hooks — 훅 우선순위 관리자
16
+
17
+ > Claude Code 훅의 실행 순서를 관리합니다. triflux 훅이 항상 최우선 실행되도록 보장합니다.
18
+
19
+ ## 핵심 개념
20
+
21
+ Claude Code는 같은 이벤트에 매칭된 훅을 **병렬 실행**합니다. 순서 보장이 불가능합니다.
22
+ triflux의 **hook-orchestrator**는 이벤트당 하나의 진입점을 두고, 내부에서 **우선순위대로 순차 실행**합니다.
23
+
24
+ ```
25
+ settings.json → hook-orchestrator.mjs (단일 진입점)
26
+ ↓ hook-registry.json 읽기
27
+ ↓ priority 순 정렬
28
+ 1. triflux 훅 (priority=0)
29
+ 2. OMC 훅 (priority=50)
30
+ 3. 외부 훅 (priority=100)
31
+ ```
32
+
33
+ ## 워크플로우
34
+
35
+ ### Step 1: 현재 상태 확인
36
+
37
+ ```bash
38
+ Bash("node hooks/hook-manager.mjs status")
39
+ ```
40
+
41
+ 결과 JSON에서 `orchestrated` 필드로 적용 여부를 판단한다.
42
+
43
+ ### Step 2: 메인 메뉴 (AskUserQuestion)
44
+
45
+ ```
46
+ question: "훅 관리 — 어떤 작업을 수행하시겠습니까?"
47
+ header: "Hook Manager"
48
+ options:
49
+ - label: "현재 상태 보기"
50
+ description: "settings.json 훅 스캔 + 오케스트레이터 적용 상태"
51
+ - label: "오케스트레이터 적용"
52
+ description: "모든 훅을 통합 — triflux 최우선 실행 보장"
53
+ - label: "변경점 미리보기 (diff)"
54
+ description: "적용 시 어떻게 바뀌는지 확인"
55
+ - label: "원래대로 복원"
56
+ description: "오케스트레이터 제거, 원래 settings.json 훅으로 복원"
57
+ - label: "개별 훅 관리"
58
+ description: "특정 훅 활성/비활성, 우선순위 변경"
59
+ ```
60
+
61
+ ### Step 3: 선택에 따른 분기
62
+
63
+ #### "현재 상태 보기"
64
+
65
+ ```bash
66
+ Bash("node hooks/hook-manager.mjs scan")
67
+ ```
68
+
69
+ 결과 JSON을 파싱하여 테이블로 표시:
70
+
71
+ ```markdown
72
+ ## 현재 훅 상태
73
+
74
+ | 이벤트 | 훅 수 | 소스 |
75
+ |--------|-------|------|
76
+ | SessionStart | 4 | triflux(3), session-vault(1) |
77
+ | PreToolUse | 3 | triflux(1), omc(2) |
78
+ | ... | ... | ... |
79
+
80
+ 오케스트레이터: ❌ 미적용 / ✅ 적용됨 (N개 이벤트)
81
+ ```
82
+
83
+ #### "오케스트레이터 적용"
84
+
85
+ 먼저 diff를 보여준 후 확인:
86
+
87
+ ```bash
88
+ Bash("node hooks/hook-manager.mjs diff")
89
+ ```
90
+
91
+ 결과를 표시한 뒤 AskUserQuestion:
92
+ ```
93
+ question: "위 변경을 적용하시겠습니까?"
94
+ header: "확인"
95
+ options:
96
+ - label: "적용"
97
+ description: "settings.json 백업 후 오케스트레이터 적용"
98
+ - label: "취소"
99
+ description: "변경 없이 돌아가기"
100
+ ```
101
+
102
+ "적용" 선택 시:
103
+ ```bash
104
+ Bash("node hooks/hook-manager.mjs apply")
105
+ ```
106
+
107
+ 적용 결과 표시:
108
+ ```
109
+ ✅ 오케스트레이터 적용 완료
110
+ N개 이벤트 → 1개 오케스트레이터로 통합
111
+ 실행 순서: triflux(0) → OMC(50) → external(100)
112
+ 복원: /tfx-hooks → 원래대로 복원
113
+ 또는: triflux hooks restore
114
+ ```
115
+
116
+ #### "변경점 미리보기 (diff)"
117
+
118
+ ```bash
119
+ Bash("node hooks/hook-manager.mjs diff")
120
+ ```
121
+
122
+ 결과를 테이블로 표시:
123
+ ```markdown
124
+ | 이벤트 | 현재 | 적용 후 | 변경 |
125
+ |--------|------|---------|------|
126
+ | PreToolUse | 2개 개별 | 1개 오케스트레이터 (내부 4개) | 교체 |
127
+ | PostToolUse | 없음 | 1개 오케스트레이터 (내부 1개) | 신규 |
128
+ | ... | ... | ... | ... |
129
+ ```
130
+
131
+ #### "원래대로 복원"
132
+
133
+ ```bash
134
+ Bash("node hooks/hook-manager.mjs restore")
135
+ ```
136
+
137
+ 결과에서 status를 확인:
138
+ - `"restored"` → "✅ 원래 훅 설정이 복원되었습니다."
139
+ - `"no_backup"` → "⚠️ 백업이 없습니다. 오케스트레이터를 적용한 적이 없습니다."
140
+
141
+ #### "개별 훅 관리"
142
+
143
+ 레지스트리에서 전체 훅 목록을 읽어 표시:
144
+
145
+ ```bash
146
+ Bash("node -e \"const r=JSON.parse(require('fs').readFileSync('hooks/hook-registry.json','utf8')); const all=[]; for(const [e,hs] of Object.entries(r.events)) hs.forEach(h=>all.push({event:e,...h})); console.log(JSON.stringify(all))\"")
147
+ ```
148
+
149
+ 결과를 테이블로 표시 후 AskUserQuestion:
150
+ ```
151
+ question: "어떤 훅을 관리하시겠습니까?"
152
+ header: "훅 선택"
153
+ options:
154
+ (레지스트리의 각 훅을 옵션으로 나열)
155
+ - label: "tfx-safety-guard"
156
+ description: "[PreToolUse:Bash] 위험 명령 차단 — priority:0, 활성"
157
+ - label: "tfx-agent-route-guard"
158
+ description: "[PreToolUse:Agent] 에이전트 라우팅 — priority:0, 활성"
159
+ - label: "omc-headless-guard"
160
+ description: "[PreToolUse:Bash|Agent] headless 가드 — priority:50, 활성"
161
+ ...
162
+ ```
163
+
164
+ 훅 선택 후 AskUserQuestion:
165
+ ```
166
+ question: "{hookId} — 어떤 조작을 하시겠습니까?"
167
+ header: "훅 조작"
168
+ options:
169
+ - label: "활성/비활성 토글"
170
+ description: "현재: 활성 → 비활성으로 전환"
171
+ - label: "우선순위 변경"
172
+ description: "현재: 0 — 숫자가 낮을수록 먼저 실행"
173
+ - label: "뒤로"
174
+ description: "훅 목록으로 돌아가기"
175
+ ```
176
+
177
+ "활성/비활성 토글":
178
+ ```bash
179
+ Bash("node hooks/hook-manager.mjs toggle {hookId}")
180
+ ```
181
+
182
+ "우선순위 변경":
183
+ AskUserQuestion으로 새 우선순위 입력:
184
+ ```
185
+ question: "새 우선순위를 입력하세요 (0=최우선, 50=중간, 100=후순위)"
186
+ header: "Priority"
187
+ ```
188
+ ```bash
189
+ Bash("node hooks/hook-manager.mjs set-priority {hookId} {newPriority}")
190
+ ```
191
+
192
+ 변경 후 오케스트레이터가 적용된 상태라면 재적용 안내:
193
+ ```
194
+ 💡 레지스트리가 변경되었습니다. 오케스트레이터는 실시간으로 레지스트리를 읽으므로 재적용 불필요합니다.
195
+ ```
196
+
197
+ ## CLI 대응
198
+
199
+ | 스킬 UI | CLI 명령 |
200
+ |---------|---------|
201
+ | 현재 상태 보기 | `triflux hooks scan` |
202
+ | 오케스트레이터 적용 | `triflux hooks apply` |
203
+ | 변경점 미리보기 | `triflux hooks diff` |
204
+ | 원래대로 복원 | `triflux hooks restore` |
205
+ | 상태 확인 | `triflux hooks status` |
206
+ | 훅 토글 | `triflux hooks toggle <hookId>` |
207
+ | 우선순위 변경 | `triflux hooks set-priority <hookId> <priority>` |
208
+
209
+ ## 에러 처리
210
+
211
+ | 상황 | 처리 |
212
+ |------|------|
213
+ | hook-registry.json 없음 | "레지스트리가 없습니다. triflux hooks 디렉토리를 확인하세요." |
214
+ | settings.json 파싱 실패 | "settings.json이 손상되었습니다. 수동 확인이 필요합니다." |
215
+ | 백업 없이 복원 시도 | "오케스트레이터를 적용한 적이 없어 복원할 수 없습니다." |
216
+ | hook-manager.mjs 실행 실패 | "hook-manager를 실행할 수 없습니다. node hooks/hook-manager.mjs를 직접 실행해 보세요." |
@@ -1,6 +1,9 @@
1
1
  ---
2
2
  name: tfx-multi
3
- description: 멀티-CLI 팀 모드. Claude Native Agent Teams + Codex/Gemini 멀티모델 오케스트레이션.
3
+ description: >
4
+ 멀티-CLI 팀 모드. Claude Native Agent Teams + Codex/Gemini 멀티모델 오케스트레이션.
5
+ '멀티로', '팀 모드', '다중 CLI', '3개 다 동원', 'multi' 같은 요청에 반드시 사용.
6
+ Claude+Codex+Gemini 협업이 필요한 모든 상황에 적극 활용.
4
7
  triggers:
5
8
  - tfx-multi
6
9
  argument-hint: '"작업 설명" | --agents codex,gemini "작업" | --tmux "작업" | status | stop'
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: tfx-plan
3
- description: "구현 계획이 필요할 때 사용한다. '계획 세워줘', 'plan', '플랜', '어떻게 구현하지', '태스크 분해', '작업 순서' 같은 요청에 반드시 사용. 기능 구현 전 영향 범위 파악과 태스크 분해가 필요할 때 적극 활용."
3
+ description: "구현 계획이 필요할 때 사용한다. '계획 세워줘', 'plan', '플랜', '어떻게 구현하지', '태스크 분해', '작업 순서' 같은 요청에 반드시 사용. 기능 구현 전 영향 범위 파악과 태스크 분해가 필요할 때 적극 활용. 작업 분해, 순서 정리, '어떤 순서로', '먼저 뭐 해야', 'breakdown', 'decompose', 'task list' 같은 요청에도 적극 활용. 단순 계획은 이 스킬, 3자 합의 계획은 tfx-deep-plan을 사용."
4
4
  triggers:
5
5
  - plan
6
6
  - 계획
@@ -153,34 +153,121 @@ Get-Content 'C:\path\prompts\prompt.md' -Raw | codex
153
153
 
154
154
  ---
155
155
 
156
- ## RULE 5: WT 패인 정리 (프리징 방지)
156
+ ## RULE 5: WT 세션 정리 — CRITICAL (프리징 치명 버그)
157
157
 
158
- WT 패인이 psmux 세션에 attach된 상태에서 `kill-session`하면 WT가 프리징된다.
158
+ > **이 규칙을 위반하면 Windows Terminal이 완전히 프리징된다.**
159
+ > 복구 방법은 작업 관리자에서 WindowsTerminal.exe 강제 종료뿐이다.
160
+ > **모든 열린 탭/세션이 유실된다.** 절대 위반하지 마라.
159
161
 
160
- ### MUST (3단계 정리)
162
+ ### 프리징 원인
163
+
164
+ WT 패인이 `psmux attach-session`으로 세션에 연결된 상태에서
165
+ `psmux kill-session`을 직접 호출하면 WT의 PTY 핸들이 dangling 상태가 되어
166
+ **전체 WT 프로세스가 응답 불능**이 된다.
167
+
168
+ ### MUST — 3단계 정리 (순서 반드시 준수)
161
169
 
162
170
  ```bash
163
- # 1) graceful exit
164
- for s in $(psmux list-sessions -F '#{session_name}' 2>/dev/null | grep codex-swarm); do
171
+ # 1) graceful exit — 각 세션 내 프로세스를 정상 종료
172
+ for s in $(psmux list-sessions -F '#{session_name}' 2>/dev/null | grep "$PREFIX"); do
165
173
  psmux send-keys -t "$s" "exit" Enter 2>/dev/null || true
166
174
  done
167
175
 
168
- # 2) WT 패인 종료 대기
176
+ # 2) WT 패인 분리 대기 — exit이 WT에 전파되는 시간 필요
169
177
  sleep 2
170
178
 
171
- # 3) 잔여 세션 강제 종료
172
- for s in $(psmux list-sessions -F '#{session_name}' 2>/dev/null | grep codex-swarm); do
179
+ # 3) 잔여 세션 강제 종료 — exit이 먹히지 않은 경우만
180
+ for s in $(psmux list-sessions -F '#{session_name}' 2>/dev/null | grep "$PREFIX"); do
173
181
  psmux kill-session -t "$s" 2>/dev/null || true
174
182
  done
175
183
  ```
176
184
 
177
- ### MUST NOT
185
+ ### MUST NOT — 아래 패턴은 전부 프리징 유발
178
186
 
179
187
  ```bash
180
- # WRONG — WT 프리징 발생
181
- for s in $(psmux list-sessions -F '#{session_name}' 2>/dev/null | grep codex-swarm); do
182
- psmux kill-session -t "$s"
183
- done
188
+ # WRONG 1 exit 없이 바로 kill
189
+ psmux kill-session -t "$session"
190
+
191
+ # WRONG 2 — sleep 없이 exit 직후 kill
192
+ psmux send-keys -t "$s" "exit" Enter
193
+ psmux kill-session -t "$s" # ← WT가 아직 PTY를 잡고 있음
194
+
195
+ # WRONG 3 — detach 직후 kill
196
+ psmux send-keys -t "$s" "psmux detach" Enter
197
+ psmux kill-session -t "$s" # ← detach 완료 전에 kill → 프리징
198
+ ```
199
+
200
+ ### WT 패인 직접 닫기 (세션이 아닌 WT 쪽에서 정리)
201
+
202
+ ```bash
203
+ # WT 패인을 닫는 것이 세션 kill보다 안전
204
+ wt.exe -w 0 close-pane # 현재 포커스된 패인 닫기
205
+ ```
206
+
207
+ ---
208
+
209
+ ## RULE 5-1: psmux 경로 탐색
210
+
211
+ psmux는 환경마다 설치 위치가 다르다. `hub/team/psmux.mjs`의 `PSMUX_BIN`이
212
+ 자동 탐색하지만, 스크립트에서 직접 psmux를 호출할 때도 경로를 고려해야 한다.
213
+
214
+ 탐색 우선순위:
215
+ 1. `$PSMUX_BIN` 환경변수 (설정 시 최우선)
216
+ 2. PATH의 `psmux`
217
+ 3. `%LOCALAPPDATA%\psmux\psmux.exe` (Windows 기본)
218
+ 4. `%APPDATA%\npm\psmux.cmd` (npm global)
219
+ 5. `~\scoop\shims\psmux.exe` (Scoop)
220
+
221
+ ---
222
+
223
+ ## RULE 5-2: WT 명령 치트시트
224
+
225
+ ### 패인 분할
226
+
227
+ ```bash
228
+ # 현재 창에서 수평 분할 (상/하)
229
+ wt.exe -w 0 sp -H -p triflux --title "worker" psmux attach-session -t SESSION
230
+
231
+ # 현재 창에서 수직 분할 (좌/우)
232
+ wt.exe -w 0 sp -V -p triflux --title "worker" psmux attach-session -t SESSION
233
+
234
+ # 2x2 그리드 (4 패인)
235
+ wt.exe -w 0 \
236
+ sp -H -p triflux --title "w1" psmux attach-session -t S1 \; \
237
+ sp -V -p triflux --title "w2" psmux attach-session -t S2 \; \
238
+ move-focus up \; \
239
+ sp -V -p triflux --title "w3" psmux attach-session -t S3
240
+ ```
241
+
242
+ ### 포커스 이동
243
+
244
+ ```bash
245
+ wt.exe -w 0 move-focus up|down|left|right
246
+ ```
247
+
248
+ ### 패인 닫기 (세션 kill보다 안전)
249
+
250
+ ```bash
251
+ wt.exe -w 0 close-pane
252
+ ```
253
+
254
+ ### 필수 옵션
255
+
256
+ | 옵션 | 의미 | 필수 여부 |
257
+ |------|------|----------|
258
+ | `-w 0` | 현재 WT 윈도우 | 필수 (없으면 새 창) |
259
+ | `-p triflux` | triflux WT 프로파일 | 필수 (테마/셸 일관성) |
260
+ | `--title "name"` | 패인 제목 | 권장 (식별용) |
261
+ | `sp -H` / `sp -V` | 분할 방향 | 필수 |
262
+
263
+ ### 새 탭 금지
264
+
265
+ ```bash
266
+ # WRONG — 새 탭 생성 (nt)
267
+ wt.exe -w 0 nt -p triflux ...
268
+
269
+ # RIGHT — split-pane (sp)
270
+ wt.exe -w 0 sp -V -p triflux ...
184
271
  ```
185
272
 
186
273
  ---