triflux 9.0.0 → 9.2.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.
- package/README.ko.md +18 -13
- package/README.md +17 -12
- package/bin/triflux.mjs +126 -7
- package/package.json +1 -1
- package/scripts/setup.mjs +57 -0
- package/skills/star-prompt/SKILL.md +122 -0
- package/skills/tfx-codex-swarm/SKILL.md +104 -30
- package/skills/tfx-psmux-rules/SKILL.md +208 -0
- package/tui/setup.mjs +51 -0
package/README.ko.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
12
|
<strong>Consensus Intelligence 기반 Tri-CLI 오케스트레이션</strong><br>
|
|
13
|
-
<em>Claude + Codex + Gemini —
|
|
13
|
+
<em>Claude + Codex + Gemini — 자연어 라우팅, 교차 모델 리뷰, Deep/Light 변형을 갖춘 38개 스킬.</em>
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
16
|
<p align="center">
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
<p align="center">
|
|
28
28
|
<a href="#빠른-시작">빠른 시작</a> ·
|
|
29
29
|
<a href="#tri-cli-합의-엔진">Tri-CLI 합의 엔진</a> ·
|
|
30
|
-
<a href="#
|
|
30
|
+
<a href="#38개-스킬">38개 스킬</a> ·
|
|
31
31
|
<a href="#아키텍처">아키텍처</a> ·
|
|
32
32
|
<a href="#deep-vs-light">Deep vs Light</a> ·
|
|
33
33
|
<a href="#보안">보안</a>
|
|
@@ -80,21 +80,26 @@ tfx setup
|
|
|
80
80
|
|
|
81
81
|
---
|
|
82
82
|
|
|
83
|
-
##
|
|
83
|
+
## v9의 새로운 기능
|
|
84
84
|
|
|
85
|
-
**triflux
|
|
85
|
+
**triflux v9**은 **하네스 네이티브 인텔리전스**를 도입합니다. 자연어로 말하면 적절한 스킬로 자동 라우팅되고, 교차 모델 리뷰로 동일 모델의 self-approve를 차단합니다.
|
|
86
86
|
|
|
87
|
-
### 주요 특징
|
|
87
|
+
### v9 주요 특징
|
|
88
|
+
|
|
89
|
+
- **자연어 라우팅** — "리뷰해줘"라고 말하면 `/tfx-review`가 자동 호출. "제대로/꼼꼼히" 수정자로 Deep 변형 자동 에스컬레이션
|
|
90
|
+
- **교차 모델 리뷰** — Claude가 작성하면 Codex가 리뷰, Codex가 작성하면 Claude가 리뷰. 동일 모델 self-approve 차단. 커밋 전 미검증 파일 nudge
|
|
91
|
+
- **맥락 격리** — 현재 맥락과 무관한 요청을 감지하면 별도 psmux 세션으로 분리 제안
|
|
92
|
+
- **38개 스킬** — Light 14개 + Deep 10개 + Infrastructure 14개, 10개 도메인으로 구성
|
|
93
|
+
- **Codex Swarm 강화** — PowerShell `.ps1` 런처, 프로파일 기반 실행, `/merge-worktree`로 결과 자동 수집
|
|
94
|
+
- **스킬 메타데이터** — 모든 스킬에 래퍼/인프라/Light-Deep 관계 표기. 트리거 충돌 해소
|
|
95
|
+
|
|
96
|
+
### v8 기반 (계속 유지)
|
|
88
97
|
|
|
89
|
-
- **35개 스킬** — Light 11개 + Deep 10개 + Infrastructure 12개, 9개 도메인으로 구성
|
|
90
98
|
- **Tri-Debate Engine** — 3개 CLI가 독립 분석 후 Anti-Herding, 교차 검증, 합의 점수 산출
|
|
91
99
|
- **Deep/Light 변형** — 모든 기능에 토큰 효율적인 Light 모드와 정밀한 Deep 모드를 제공
|
|
92
|
-
- **Consensus Gate** — Deep 스킬은 3개 CLI 중 2개 이상의
|
|
93
|
-
- **
|
|
94
|
-
- **
|
|
95
|
-
- **94% 토큰 절감** — `tfx-index`가 58K 토큰 분량의 파일 읽기를 3KB 프로젝트 맵으로 대체
|
|
96
|
-
- **Persistence Loop** — `tfx-persist`(정식 이름, 3자 검증), `/tfx-ralph`(호환 별칭), `tfx-sisyphus`(자동 라우팅)가 검증 완료까지 반복 실행
|
|
97
|
-
- **Hub IPC** — Named Pipe 및 HTTP MCP 브리지를 활용한 초고속 상주형 Hub 서버
|
|
100
|
+
- **Consensus Gate** — Deep 스킬은 3개 CLI 중 2개 이상의 동의 요구
|
|
101
|
+
- **Expert Panel** — `tfx-panel`을 통한 가상 전문가 시뮬레이션
|
|
102
|
+
- **Hub IPC** — Named Pipe 및 HTTP MCP 브리지를 활용한 상주형 Hub 서버
|
|
98
103
|
- **psmux / Windows 네이티브** — `tmux`(WSL)와 `psmux`(Windows Terminal) 하이브리드 지원
|
|
99
104
|
|
|
100
105
|
---
|
|
@@ -128,7 +133,7 @@ Phase 3: Resolution (합의율 < 70%일 경우)
|
|
|
128
133
|
|
|
129
134
|
---
|
|
130
135
|
|
|
131
|
-
##
|
|
136
|
+
## 38개 스킬
|
|
132
137
|
|
|
133
138
|
### 리서치
|
|
134
139
|
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
12
|
<strong>Tri-CLI Orchestration with Consensus Intelligence</strong><br>
|
|
13
|
-
<em>Claude + Codex + Gemini —
|
|
13
|
+
<em>Claude + Codex + Gemini — natural language routing, cross-model review, 38 skills with Deep/Light variants.</em>
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
16
|
<p align="center">
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
<p align="center">
|
|
28
28
|
<a href="#quick-start">Quick Start</a> ·
|
|
29
29
|
<a href="#tri-cli-consensus">Tri-CLI Consensus</a> ·
|
|
30
|
-
<a href="#
|
|
30
|
+
<a href="#38-skills">38 Skills</a> ·
|
|
31
31
|
<a href="#architecture">Architecture</a> ·
|
|
32
32
|
<a href="#deep-vs-light">Deep vs Light</a> ·
|
|
33
33
|
<a href="#security">Security</a>
|
|
@@ -80,21 +80,26 @@ tfx setup
|
|
|
80
80
|
|
|
81
81
|
---
|
|
82
82
|
|
|
83
|
-
## What's New in
|
|
83
|
+
## What's New in v9
|
|
84
84
|
|
|
85
|
-
**triflux
|
|
85
|
+
**triflux v9** introduces **Harness-Native Intelligence** — speak naturally, and triflux routes to the right skill automatically. Cross-model review ensures no model approves its own work.
|
|
86
86
|
|
|
87
|
-
### Highlights
|
|
87
|
+
### v9 Highlights
|
|
88
|
+
|
|
89
|
+
- **Natural Language Routing** — Say "review this" or "리뷰해줘" instead of memorizing `/tfx-review`. Depth modifiers ("thoroughly", "제대로") auto-escalate to Deep variants
|
|
90
|
+
- **Cross-Model Review** — Claude writes → Codex reviews. Codex writes → Claude reviews. Same-model self-approve is blocked. Pre-commit nudge for unreviewed files
|
|
91
|
+
- **Context Isolation** — Off-topic requests auto-detected; spawns a clean psmux session so your main context stays focused
|
|
92
|
+
- **38 Skills** — 14 Light + 10 Deep + 14 Infrastructure, organized across 10 domains
|
|
93
|
+
- **Codex Swarm Hardened** — PowerShell `.ps1` launchers, profile-based execution (no `--dangerously` flag), `/merge-worktree` auto-invocation for result collection
|
|
94
|
+
- **Skill Metadata** — Every skill labeled: wrapper/infrastructure/Light-Deep pairs. Trigger conflicts resolved
|
|
95
|
+
|
|
96
|
+
### v8 Foundations (carried forward)
|
|
88
97
|
|
|
89
|
-
- **35 Skills** — 11 Light + 10 Deep + 12 Infrastructure, organized across 9 domains
|
|
90
98
|
- **Tri-Debate Engine** — 3-CLI independent analysis with anti-herding, cross-validation, and consensus scoring
|
|
91
99
|
- **Deep/Light Variants** — Every capability has a token-efficient Light mode and a thorough Deep mode
|
|
92
100
|
- **Consensus Gate** — Deep skills require 2/3+ CLI agreement; learned weights track CLI reliability over time
|
|
93
|
-
- **
|
|
94
|
-
- **
|
|
95
|
-
- **94% Token Reduction** — `tfx-index` creates a 3KB project map replacing 58K tokens of file reads
|
|
96
|
-
- **Persistence Loops** — `tfx-persist` (canonical, 3-party verified), `/tfx-ralph` (compat alias), and `tfx-sisyphus` (auto-routing) run until verified complete
|
|
97
|
-
- **Hub IPC** — Lightning-fast resident Hub server with Named Pipe & HTTP MCP bridge
|
|
101
|
+
- **Expert Panel** — Virtual expert simulation via `tfx-panel`
|
|
102
|
+
- **Hub IPC** — Resident Hub server with Named Pipe & HTTP MCP bridge
|
|
98
103
|
- **psmux / Windows Native** — Hybrid support for `tmux` (WSL) and `psmux` (Windows Terminal)
|
|
99
104
|
|
|
100
105
|
---
|
|
@@ -128,7 +133,7 @@ Phase 3: Resolution (if consensus < 70%)
|
|
|
128
133
|
|
|
129
134
|
---
|
|
130
135
|
|
|
131
|
-
##
|
|
136
|
+
## 38 Skills
|
|
132
137
|
|
|
133
138
|
### Research
|
|
134
139
|
|
package/bin/triflux.mjs
CHANGED
|
@@ -16,8 +16,24 @@ const PKG_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
|
16
16
|
const CLAUDE_DIR = join(homedir(), ".claude");
|
|
17
17
|
const CODEX_DIR = join(homedir(), ".codex");
|
|
18
18
|
const CODEX_CONFIG_PATH = join(CODEX_DIR, "config.toml");
|
|
19
|
+
const GEMINI_DIR = join(homedir(), ".gemini");
|
|
20
|
+
const GEMINI_PROFILES_PATH = join(GEMINI_DIR, "triflux-profiles.json");
|
|
19
21
|
const PKG = JSON.parse(readFileSync(join(PKG_ROOT, "package.json"), "utf8"));
|
|
20
22
|
|
|
23
|
+
// 이 배열에 포함된 버전에서만 star prompt를 표시한다 (빈 배열 = 모든 버전에서 표시)
|
|
24
|
+
const STAR_PROMPT_VERSIONS = ["9.2.0"];
|
|
25
|
+
|
|
26
|
+
const DEFAULT_GEMINI_PROFILES = {
|
|
27
|
+
model: "gemini-3.1-pro-preview",
|
|
28
|
+
profiles: {
|
|
29
|
+
pro31: { model: "gemini-3.1-pro-preview", hint: "3.1 Pro — 플래그십 (1M ctx, 멀티모달)" },
|
|
30
|
+
flash3: { model: "gemini-3-flash-preview", hint: "3.0 Flash — 빠른 응답, 비용 효율" },
|
|
31
|
+
pro25: { model: "gemini-2.5-pro", hint: "2.5 Pro — 안정 (추론 강화)" },
|
|
32
|
+
flash25: { model: "gemini-2.5-flash", hint: "2.5 Flash — 경량 범용" },
|
|
33
|
+
lite25: { model: "gemini-2.5-flash-lite", hint: "2.5 Flash Lite — 최경량" },
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
21
37
|
const REQUIRED_CODEX_PROFILES = [
|
|
22
38
|
{
|
|
23
39
|
name: "codex53_high",
|
|
@@ -496,6 +512,44 @@ function previewCodexProfiles() {
|
|
|
496
512
|
};
|
|
497
513
|
}
|
|
498
514
|
|
|
515
|
+
function ensureGeminiProfiles() {
|
|
516
|
+
try {
|
|
517
|
+
if (!existsSync(GEMINI_DIR)) mkdirSync(GEMINI_DIR, { recursive: true });
|
|
518
|
+
|
|
519
|
+
if (!existsSync(GEMINI_PROFILES_PATH)) {
|
|
520
|
+
writeFileSync(GEMINI_PROFILES_PATH, JSON.stringify(DEFAULT_GEMINI_PROFILES, null, 2) + "\n", "utf8");
|
|
521
|
+
return { ok: true, created: true, count: Object.keys(DEFAULT_GEMINI_PROFILES.profiles).length };
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
let cfg;
|
|
525
|
+
try {
|
|
526
|
+
cfg = JSON.parse(readFileSync(GEMINI_PROFILES_PATH, "utf8"));
|
|
527
|
+
} catch {
|
|
528
|
+
// 파싱 실패 → 재생성
|
|
529
|
+
writeFileSync(GEMINI_PROFILES_PATH, JSON.stringify(DEFAULT_GEMINI_PROFILES, null, 2) + "\n", "utf8");
|
|
530
|
+
return { ok: true, created: true, count: Object.keys(DEFAULT_GEMINI_PROFILES.profiles).length };
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (!cfg.profiles) cfg.profiles = {};
|
|
534
|
+
let added = 0;
|
|
535
|
+
for (const [name, value] of Object.entries(DEFAULT_GEMINI_PROFILES.profiles)) {
|
|
536
|
+
if (!cfg.profiles[name]) {
|
|
537
|
+
cfg.profiles[name] = value;
|
|
538
|
+
added++;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
if (!cfg.model) cfg.model = DEFAULT_GEMINI_PROFILES.model;
|
|
542
|
+
|
|
543
|
+
if (added > 0) {
|
|
544
|
+
writeFileSync(GEMINI_PROFILES_PATH, JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
return { ok: true, created: false, added, count: Object.keys(cfg.profiles).length };
|
|
548
|
+
} catch (e) {
|
|
549
|
+
return { ok: false, message: e.message };
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
499
553
|
function syncFile(src, dst, label) {
|
|
500
554
|
const dstDir = dirname(dst);
|
|
501
555
|
if (!existsSync(dstDir)) mkdirSync(dstDir, { recursive: true });
|
|
@@ -869,19 +923,42 @@ function cmdSetup(options = {}) {
|
|
|
869
923
|
}
|
|
870
924
|
}
|
|
871
925
|
|
|
926
|
+
// ── 결과 추적 ──
|
|
927
|
+
const summary = [];
|
|
928
|
+
|
|
872
929
|
const codexProfileResult = ensureCodexProfiles();
|
|
873
930
|
if (!codexProfileResult.ok) {
|
|
874
931
|
warn(`Codex profiles 설정 실패: ${codexProfileResult.message}`);
|
|
932
|
+
summary.push({ item: "Codex profiles", status: "⚠️", detail: codexProfileResult.message });
|
|
875
933
|
} else if (codexProfileResult.added > 0) {
|
|
876
934
|
ok(`Codex profiles: ${codexProfileResult.added}개 추가됨 (~/.codex/config.toml)`);
|
|
935
|
+
summary.push({ item: "Codex profiles", status: "✅", detail: `${codexProfileResult.added}개 추가됨` });
|
|
877
936
|
} else {
|
|
878
937
|
ok("Codex profiles: 이미 준비됨");
|
|
938
|
+
summary.push({ item: "Codex profiles", status: "✅", detail: "이미 준비됨" });
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// Gemini 프로필
|
|
942
|
+
const geminiResult = ensureGeminiProfiles();
|
|
943
|
+
if (!geminiResult.ok) {
|
|
944
|
+
warn(`Gemini profiles 설정 실패: ${geminiResult.message}`);
|
|
945
|
+
summary.push({ item: "Gemini profiles", status: "⚠️", detail: geminiResult.message });
|
|
946
|
+
} else if (geminiResult.created) {
|
|
947
|
+
ok(`Gemini profiles: ${geminiResult.count}개 생성됨 (~/.gemini/triflux-profiles.json)`);
|
|
948
|
+
summary.push({ item: "Gemini profiles", status: "✅", detail: `${geminiResult.count}개 생성됨` });
|
|
949
|
+
} else if (geminiResult.added > 0) {
|
|
950
|
+
ok(`Gemini profiles: ${geminiResult.added}개 추가됨`);
|
|
951
|
+
summary.push({ item: "Gemini profiles", status: "✅", detail: `${geminiResult.added}개 추가됨 (총 ${geminiResult.count}개)` });
|
|
952
|
+
} else {
|
|
953
|
+
ok(`Gemini profiles: ${geminiResult.count}개 준비됨`);
|
|
954
|
+
summary.push({ item: "Gemini profiles", status: "✅", detail: `${geminiResult.count}개 준비됨` });
|
|
879
955
|
}
|
|
880
956
|
|
|
881
957
|
// hub MCP 사전 등록 (서버 미실행이어도 설정만 등록 — hub start 시 즉시 사용 가능)
|
|
882
958
|
if (existsSync(join(PKG_ROOT, "hub", "server.mjs"))) {
|
|
883
959
|
const defaultHubUrl = `http://127.0.0.1:${process.env.TFX_HUB_PORT || "27888"}/mcp`;
|
|
884
960
|
autoRegisterMcp(defaultHubUrl);
|
|
961
|
+
summary.push({ item: "Hub MCP", status: "✅", detail: "등록됨" });
|
|
885
962
|
console.log("");
|
|
886
963
|
}
|
|
887
964
|
|
|
@@ -900,6 +977,7 @@ function cmdSetup(options = {}) {
|
|
|
900
977
|
const currentCmd = settings.statusLine?.command || "";
|
|
901
978
|
if (currentCmd.includes("hud-qos-status.mjs")) {
|
|
902
979
|
ok("statusLine 이미 설정됨");
|
|
980
|
+
summary.push({ item: "HUD statusLine", status: "✅", detail: "이미 설정됨" });
|
|
903
981
|
} else {
|
|
904
982
|
const nodePath = process.execPath.replace(/\\/g, "/");
|
|
905
983
|
const hudForward = hudPath.replace(/\\/g, "/");
|
|
@@ -917,6 +995,7 @@ function cmdSetup(options = {}) {
|
|
|
917
995
|
|
|
918
996
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
919
997
|
ok("statusLine 설정 완료 — 세션 재시작 후 HUD 표시");
|
|
998
|
+
summary.push({ item: "HUD statusLine", status: "✅", detail: "설정 완료" });
|
|
920
999
|
}
|
|
921
1000
|
} catch (e) {
|
|
922
1001
|
throw createCliError(`settings.json 처리 실패: ${e.message}`, {
|
|
@@ -928,9 +1007,50 @@ function cmdSetup(options = {}) {
|
|
|
928
1007
|
}
|
|
929
1008
|
} else {
|
|
930
1009
|
warn("HUD 파일 없음 — 먼저 파일 동기화 필요");
|
|
1010
|
+
summary.push({ item: "HUD statusLine", status: "⚠️", detail: "HUD 파일 없음" });
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// CLI 존재 확인
|
|
1014
|
+
const cliChecks = [
|
|
1015
|
+
{ name: "codex", install: "npm i -g @openai/codex" },
|
|
1016
|
+
{ name: "gemini", install: "npm i -g @google/gemini-cli" },
|
|
1017
|
+
];
|
|
1018
|
+
for (const { name, install } of cliChecks) {
|
|
1019
|
+
if (which(name)) {
|
|
1020
|
+
summary.push({ item: `${name} CLI`, status: "✅", detail: "설치됨" });
|
|
1021
|
+
} else {
|
|
1022
|
+
summary.push({ item: `${name} CLI`, status: "⏭️", detail: `미설치 (${install})` });
|
|
1023
|
+
}
|
|
931
1024
|
}
|
|
932
1025
|
|
|
933
|
-
|
|
1026
|
+
// Star request (버전 게이팅)
|
|
1027
|
+
const showStar = STAR_PROMPT_VERSIONS.length === 0 || STAR_PROMPT_VERSIONS.includes(PKG.version);
|
|
1028
|
+
if (showStar) {
|
|
1029
|
+
try {
|
|
1030
|
+
execFileSync("gh", ["auth", "status"], { timeout: 5000, stdio: ["pipe", "pipe", "pipe"] });
|
|
1031
|
+
try {
|
|
1032
|
+
execFileSync("gh", ["api", "user/starred/tellang/triflux"], { timeout: 5000, stdio: ["pipe", "pipe", "pipe"] });
|
|
1033
|
+
console.log();
|
|
1034
|
+
ok(`이미 함께하고 계시군요. ${AMBER}⭐${RESET}`);
|
|
1035
|
+
} catch {
|
|
1036
|
+
console.log();
|
|
1037
|
+
info(`${AMBER}⭐${RESET} 하나가 큰 차이를 만듭니다. ${CYAN}https://github.com/tellang/triflux${RESET}`);
|
|
1038
|
+
}
|
|
1039
|
+
} catch {
|
|
1040
|
+
console.log();
|
|
1041
|
+
info(`${AMBER}⭐${RESET} 하나가 큰 차이를 만듭니다. ${CYAN}https://github.com/tellang/triflux${RESET}`);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
// ── 결과 요약 테이블 ──
|
|
1046
|
+
console.log(`\n${BOLD}── 설정 요약 ──${RESET}`);
|
|
1047
|
+
const maxItem = Math.max(...summary.map((s) => s.item.length));
|
|
1048
|
+
for (const { item, status, detail } of summary) {
|
|
1049
|
+
console.log(` ${status} ${item.padEnd(maxItem)} ${DIM}${detail}${RESET}`);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
console.log(`\n${DIM}설치 위치: ${CLAUDE_DIR}${RESET}`);
|
|
1053
|
+
console.log(`${DIM}버전: v${PKG.version}${RESET}\n`);
|
|
934
1054
|
}
|
|
935
1055
|
|
|
936
1056
|
function addDoctorCheck(report, entry) {
|
|
@@ -1849,7 +1969,7 @@ function cmdUpdate() {
|
|
|
1849
1969
|
return;
|
|
1850
1970
|
}
|
|
1851
1971
|
|
|
1852
|
-
// 3. setup 재실행 (
|
|
1972
|
+
// 3. setup 재실행 (파일 동기화, 프로파일, HUD, CLI 확인)
|
|
1853
1973
|
if (updated) {
|
|
1854
1974
|
console.log("");
|
|
1855
1975
|
// 업데이트 후 새 버전 읽기
|
|
@@ -1865,18 +1985,17 @@ function cmdUpdate() {
|
|
|
1865
1985
|
ok(`버전: v${oldVer} (이미 최신)`);
|
|
1866
1986
|
}
|
|
1867
1987
|
|
|
1868
|
-
// setup 재실행
|
|
1869
|
-
console.log(
|
|
1870
|
-
info("setup 재실행 중...");
|
|
1988
|
+
// setup 재실행 — 개선된 cmdSetup()이 Gemini 프로필, CLI 확인, 요약 테이블 포함
|
|
1989
|
+
console.log(`\n${CYAN}── 설정 동기화 ──${RESET}`);
|
|
1871
1990
|
cmdSetup();
|
|
1872
1991
|
|
|
1873
1992
|
if (stoppedHubInfo) {
|
|
1874
|
-
if (startHubAfterUpdate(stoppedHubInfo))
|
|
1993
|
+
if (startHubAfterUpdate(stoppedHubInfo)) ok("hub 재기동 완료");
|
|
1875
1994
|
else warn("hub 재기동 실패 — `tfx hub start`로 수동 시작 필요");
|
|
1876
1995
|
}
|
|
1877
1996
|
}
|
|
1878
1997
|
|
|
1879
|
-
console.log(`${GREEN}${BOLD}업데이트 완료${RESET}\n`);
|
|
1998
|
+
console.log(`${GREEN}${BOLD}✓ 업데이트 완료${RESET}\n`);
|
|
1880
1999
|
}
|
|
1881
2000
|
|
|
1882
2001
|
function cmdList(options = {}) {
|
package/package.json
CHANGED
package/scripts/setup.mjs
CHANGED
|
@@ -331,6 +331,56 @@ function ensureCodexProfiles() {
|
|
|
331
331
|
}
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
+
const GEMINI_DIR = join(homedir(), ".gemini");
|
|
335
|
+
const GEMINI_PROFILES_PATH = join(GEMINI_DIR, "triflux-profiles.json");
|
|
336
|
+
|
|
337
|
+
const DEFAULT_GEMINI_PROFILES = {
|
|
338
|
+
model: "gemini-3.1-pro-preview",
|
|
339
|
+
profiles: {
|
|
340
|
+
pro31: { model: "gemini-3.1-pro-preview", hint: "3.1 Pro — 플래그십 (1M ctx, 멀티모달)" },
|
|
341
|
+
flash3: { model: "gemini-3-flash-preview", hint: "3.0 Flash — 빠른 응답, 비용 효율" },
|
|
342
|
+
pro25: { model: "gemini-2.5-pro", hint: "2.5 Pro — 안정 (추론 강화)" },
|
|
343
|
+
flash25: { model: "gemini-2.5-flash", hint: "2.5 Flash — 경량 범용" },
|
|
344
|
+
lite25: { model: "gemini-2.5-flash-lite", hint: "2.5 Flash Lite — 최경량" },
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
function ensureGeminiProfiles() {
|
|
349
|
+
try {
|
|
350
|
+
if (!existsSync(GEMINI_DIR)) mkdirSync(GEMINI_DIR, { recursive: true });
|
|
351
|
+
|
|
352
|
+
if (!existsSync(GEMINI_PROFILES_PATH)) {
|
|
353
|
+
writeFileSync(GEMINI_PROFILES_PATH, JSON.stringify(DEFAULT_GEMINI_PROFILES, null, 2) + "\n", "utf8");
|
|
354
|
+
return Object.keys(DEFAULT_GEMINI_PROFILES.profiles).length;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
let cfg;
|
|
358
|
+
try {
|
|
359
|
+
cfg = JSON.parse(readFileSync(GEMINI_PROFILES_PATH, "utf8"));
|
|
360
|
+
} catch {
|
|
361
|
+
writeFileSync(GEMINI_PROFILES_PATH, JSON.stringify(DEFAULT_GEMINI_PROFILES, null, 2) + "\n", "utf8");
|
|
362
|
+
return Object.keys(DEFAULT_GEMINI_PROFILES.profiles).length;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (!cfg.profiles) cfg.profiles = {};
|
|
366
|
+
let added = 0;
|
|
367
|
+
for (const [name, value] of Object.entries(DEFAULT_GEMINI_PROFILES.profiles)) {
|
|
368
|
+
if (!cfg.profiles[name]) {
|
|
369
|
+
cfg.profiles[name] = value;
|
|
370
|
+
added++;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (!cfg.model) cfg.model = DEFAULT_GEMINI_PROFILES.model;
|
|
374
|
+
|
|
375
|
+
if (added > 0) {
|
|
376
|
+
writeFileSync(GEMINI_PROFILES_PATH, JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
377
|
+
}
|
|
378
|
+
return added;
|
|
379
|
+
} catch {
|
|
380
|
+
return 0;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
334
384
|
export { replaceProfileSection, hasProfileSection, detectDevMode, SYNC_MAP, BREADCRUMB_PATH, PLUGIN_ROOT, CLAUDE_DIR, SKILL_ALIASES };
|
|
335
385
|
|
|
336
386
|
async function main() {
|
|
@@ -813,6 +863,13 @@ if (codexProfilesAdded > 0) {
|
|
|
813
863
|
synced++;
|
|
814
864
|
}
|
|
815
865
|
|
|
866
|
+
// ── Gemini 프로필 자동 보정 ──
|
|
867
|
+
|
|
868
|
+
const geminiProfilesAdded = ensureGeminiProfiles();
|
|
869
|
+
if (geminiProfilesAdded > 0) {
|
|
870
|
+
synced++;
|
|
871
|
+
}
|
|
872
|
+
|
|
816
873
|
// ── MCP 인벤토리 백그라운드 갱신 ──
|
|
817
874
|
|
|
818
875
|
const mcpCheck = join(PLUGIN_ROOT, "scripts", "mcp-check.mjs");
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: star-prompt
|
|
3
|
+
description: >-
|
|
4
|
+
CLI 프로젝트의 setup/postinstall 흐름에 GitHub 스타 요청 프롬프트를 추가한다.
|
|
5
|
+
gh CLI 인증 확인 → 이미 스타 여부 감지 → 인터랙티브 confirm → gh API로 자동 스타.
|
|
6
|
+
Apple 스타일 UX 카피 포함. 'star prompt', '스타 요청', '리포 스타', 'star request',
|
|
7
|
+
'깃헙 스타 넣어줘', 'star 눌러달라고', '응원 요청' 같은 요청에 사용한다.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# tfx-star-prompt — GitHub Star Request Prompt
|
|
11
|
+
|
|
12
|
+
CLI 도구의 setup 완료 시점에 GitHub 리포 스타를 요청하는 프롬프트를 추가한다.
|
|
13
|
+
사용자 경험을 해치지 않으면서, 이미 스타한 사용자에겐 감사를 표하고, 아직 안 한 사용자에겐 부담 없이 한 번 물어본다.
|
|
14
|
+
|
|
15
|
+
## 동작 흐름
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
gh auth status ─── 실패 → URL만 표시 (비인터랙티브 폴백)
|
|
19
|
+
│
|
|
20
|
+
✓ 인증됨
|
|
21
|
+
│
|
|
22
|
+
gh api user/starred/{owner}/{repo} ─── 성공 → "이미 함께하고 계시군요. ⭐"
|
|
23
|
+
│
|
|
24
|
+
✗ 미스타
|
|
25
|
+
│
|
|
26
|
+
confirm("⭐ 하나가 큰 차이를 만듭니다.") ─── N → URL 조용히 표시
|
|
27
|
+
│
|
|
28
|
+
Y
|
|
29
|
+
│
|
|
30
|
+
gh api -X PUT /user/starred/{owner}/{repo} → "함께해 주셔서 감사합니다. ⭐"
|
|
31
|
+
│
|
|
32
|
+
실패 → URL 폴백
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 구현 패턴
|
|
36
|
+
|
|
37
|
+
### 인터랙티브 (TUI / readline confirm)
|
|
38
|
+
|
|
39
|
+
setup 위저드 완료 후 호출. `confirm()` 으로 Y/n 입력 받고 `gh api`로 자동 스타.
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
async function starRequest() {
|
|
43
|
+
let ghOk = false;
|
|
44
|
+
try {
|
|
45
|
+
execFileSync("gh", ["auth", "status"], {
|
|
46
|
+
timeout: 5000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"],
|
|
47
|
+
});
|
|
48
|
+
ghOk = true;
|
|
49
|
+
} catch {}
|
|
50
|
+
|
|
51
|
+
if (!ghOk) {
|
|
52
|
+
// gh 미설치/미인증 — URL만 표시
|
|
53
|
+
info(`⭐ 하나가 큰 차이를 만듭니다. https://github.com/{owner}/{repo}`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let alreadyStarred = false;
|
|
58
|
+
try {
|
|
59
|
+
execFileSync("gh", ["api", "user/starred/{owner}/{repo}"], {
|
|
60
|
+
timeout: 5000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"],
|
|
61
|
+
});
|
|
62
|
+
alreadyStarred = true;
|
|
63
|
+
} catch {}
|
|
64
|
+
|
|
65
|
+
if (alreadyStarred) {
|
|
66
|
+
ok(`이미 함께하고 계시군요. ⭐`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (await confirm(`⭐ 하나가 큰 차이를 만듭니다.`, true)) {
|
|
71
|
+
try {
|
|
72
|
+
execFileSync("gh", ["api", "-X", "PUT", "/user/starred/{owner}/{repo}"], {
|
|
73
|
+
timeout: 5000, stdio: ["pipe", "pipe", "pipe"],
|
|
74
|
+
});
|
|
75
|
+
ok(`함께해 주셔서 감사합니다. ⭐`);
|
|
76
|
+
} catch {
|
|
77
|
+
info(`https://github.com/{owner}/{repo}`);
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
// 거절 시 URL만 조용히
|
|
81
|
+
console.log(` https://github.com/{owner}/{repo}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 비인터랙티브 (postinstall / CLI banner)
|
|
87
|
+
|
|
88
|
+
confirm 불가한 환경. 이미 스타 여부만 감지하고 메시지 분기.
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
try {
|
|
92
|
+
execFileSync("gh", ["auth", "status"], { timeout: 5000, stdio: ["pipe","pipe","pipe"] });
|
|
93
|
+
try {
|
|
94
|
+
execFileSync("gh", ["api", "user/starred/{owner}/{repo}"], { timeout: 5000, stdio: ["pipe","pipe","pipe"] });
|
|
95
|
+
ok(`이미 함께하고 계시군요. ⭐`);
|
|
96
|
+
} catch {
|
|
97
|
+
info(`⭐ 하나가 큰 차이를 만듭니다. https://github.com/{owner}/{repo}`);
|
|
98
|
+
}
|
|
99
|
+
} catch {
|
|
100
|
+
info(`⭐ 하나가 큰 차이를 만듭니다. https://github.com/{owner}/{repo}`);
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## 적용 시 규칙
|
|
105
|
+
|
|
106
|
+
1. `{owner}/{repo}`를 대상 리포로 치환한다
|
|
107
|
+
2. 프로젝트의 기존 ANSI 컬러 상수(AMBER, CYAN, RESET 등)를 사용한다
|
|
108
|
+
3. setup 완료 직후, 최종 요약 다음에 호출한다 — 핵심 설정 흐름을 방해하지 않는다
|
|
109
|
+
4. 모든 gh 호출은 `timeout: 5000`, `stdio: ["pipe","pipe","pipe"]`로 감싸서 실패해도 setup을 블로킹하지 않는다
|
|
110
|
+
5. 거절 시 죄책감을 주지 않는다 — URL만 조용히 남긴다
|
|
111
|
+
|
|
112
|
+
## UX 카피 톤
|
|
113
|
+
|
|
114
|
+
Apple 스타일: 짧고, 자신감 있고, 부담 없이.
|
|
115
|
+
|
|
116
|
+
| 상황 | 멘트 |
|
|
117
|
+
|------|------|
|
|
118
|
+
| 이미 스타 | `이미 함께하고 계시군요. ⭐` |
|
|
119
|
+
| 요청 | `⭐ 하나가 큰 차이를 만듭니다.` |
|
|
120
|
+
| 수락 후 | `함께해 주셔서 감사합니다. ⭐` |
|
|
121
|
+
| 거절 | URL만 |
|
|
122
|
+
| gh 없음 | `⭐ 하나가 큰 차이를 만듭니다.` + URL |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tfx-codex-swarm
|
|
3
|
-
description: OMX 스킬을 활용하는 Codex 다중 세션 스폰 오케스트레이터. PRD/태스크 파일을 스캔하여 각각 독립 git worktree + psmux 세션���로 Codex를 full-auto 실행한다. 'codex swarm', 'codex 다중', '코덱스 스웜', '코덱스 다중 실행', 'PRD 일괄 실행', 'codex spawn', '코덱스 스폰', '다중 코덱스', 'swarm codex', 'omx swarm' 같은 요청에 반드시 사용. PRD가 여러 개이거나 병렬 Codex 실행이 필요한 모든 상황에 적극 활용.
|
|
3
|
+
description: OMX 스킬을 활용하는 Codex 다중 세션 스폰 오케스트레이터. PRD/태스크 파일을 스캔하여 각각 독립 git worktree + psmux 세션���로 Codex를 full-auto 실행한다. 'codex swarm', 'codex 다중', '코덱스 스웜', '코덱스 다중 실행', 'PRD 일괄 실행', 'codex spawn', '코덱스 스폰', '다중 코덱스', 'swarm codex', 'omx swarm', '새 창에서 진행', '새 창에서 prd', 'prd 부터', 'prd 실행', 'prd 돌려', '병렬로 돌려', 'worktree로 실행' 같은 요청에 반드시 사용. PRD가 여러 개이거나 병렬 Codex 실행이 필요한 모든 상황에 적극 활용.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# tfx-codex-swarm — Codex 다중 세션 스폰 오케스트레이터
|
|
@@ -10,10 +10,11 @@ description: OMX 스킬을 활용하는 Codex 다중 세션 스폰 오케스트
|
|
|
10
10
|
|
|
11
11
|
## 전제 조건
|
|
12
12
|
|
|
13
|
-
- `
|
|
13
|
+
- **`tfx-psmux-rules` 스킬 필수** — psmux 명령/런처 생성 시 이 스킬의 규칙을 반드시 준수
|
|
14
|
+
- `codex` CLI 설치됨 (0.117.0+)
|
|
14
15
|
- `psmux` 설치됨 (세션 관리)
|
|
15
16
|
- `git` (worktree 생성)
|
|
16
|
-
- Windows Terminal (`wt.exe`
|
|
17
|
+
- Windows Terminal (`wt.exe` 패인 기반 attach)
|
|
17
18
|
- MCP 싱글톤 데몬 (`supergateway` + `mcp-remote`) — 선택적이나 스웜 시 강력 권장
|
|
18
19
|
|
|
19
20
|
## 설정
|
|
@@ -176,18 +177,25 @@ PRD 파일을 읽고 아래 시그널에서 규모를 판단한다:
|
|
|
176
177
|
|-------------|-----|-----|-----|-----|
|
|
177
178
|
| **구현** | `codex53_xhigh` | `codex53_high` | `codex53_med` | `codex53_low` |
|
|
178
179
|
| **조사** | `gpt54_high` | `gpt54_high` | `gpt54_low` | `mini54_med` |
|
|
179
|
-
| **리팩터링** | `codex53_xhigh` | `codex53_high` | `codex53_med` | `
|
|
180
|
-
| **린트/포맷** | — | — | `
|
|
180
|
+
| **리팩터링** | `codex53_xhigh` | `codex53_high` | `codex53_med` | `codex53_low` |
|
|
181
|
+
| **린트/포맷** | — | — | `codex53_low` | `codex53_low` |
|
|
181
182
|
|
|
182
|
-
프로파일은
|
|
183
|
-
|
|
183
|
+
> **NOTE**: `spark53_*` 프로파일은 Pro 구독 전용. 비-Pro 환경에서는 `codex53_low`로 폴백한다.
|
|
184
|
+
|
|
185
|
+
프로파일 이름(`codex53_high` 등)은 `~/.codex/config.toml`에 정의된 프로파일이다.
|
|
186
|
+
**모델·effort·실행모드는 인자로 하드코딩하지 않고 프로파일로 관리한다.**
|
|
187
|
+
(→ tfx-psmux-rules RULE 4)
|
|
184
188
|
|
|
185
189
|
```bash
|
|
186
|
-
#
|
|
187
|
-
codex
|
|
188
|
-
|
|
190
|
+
# 프로파일이 config.toml에 정의되어 있으면 깔끔하게 호출
|
|
191
|
+
codex < prompt.md
|
|
192
|
+
|
|
193
|
+
# 프로파일 관리: tfx-profile 스킬 또는 config.toml 직접 편집
|
|
189
194
|
```
|
|
190
195
|
|
|
196
|
+
> **config.toml 중복 금지**: 이미 설정된 값을 CLI 플래그로 다시 지정하면 에러.
|
|
197
|
+
> 런처 생성 전 config.toml 확인 필수.
|
|
198
|
+
|
|
191
199
|
#### 4-3. 라우팅 결과 표시
|
|
192
200
|
|
|
193
201
|
프로파일 결정 후 테이블로 표시한다:
|
|
@@ -221,7 +229,7 @@ options:
|
|
|
221
229
|
|
|
222
230
|
| 설정 | 기본값 | 설명 |
|
|
223
231
|
|------|--------|------|
|
|
224
|
-
| 실행 모드 | `--
|
|
232
|
+
| 실행 모드 | `--full-auto` (config.toml 미설정 시만) | config.toml에 `approval_mode`/`sandbox` 이미 있으면 **생략 필수** (중복 에러). `--skip-git-repo-check`는 `codex exec` 전용 |
|
|
225
233
|
| worktree | 활성화 | 세션당 독립 worktree |
|
|
226
234
|
| psmux | 활성화 | 세션 관리 |
|
|
227
235
|
|
|
@@ -287,41 +295,94 @@ cp {PRD_PATH} .codex-swarm/wt-issue-{N}/{PRD_PATH}
|
|
|
287
295
|
|
|
288
296
|
각 태스크에 대해 psmux 세션을 생성하고 Codex를 실행한다:
|
|
289
297
|
|
|
298
|
+
> **CRITICAL — psmux 셸 = PowerShell**
|
|
299
|
+
> psmux 세션의 기본 셸은 **PowerShell**이다. `cd '/c/...'`, `$(cat ...)`, `&&` 등
|
|
300
|
+
> bash 문법을 `send-keys`로 직접 전달하면 경로 오류 + 인자 파싱 실패가 발생한다.
|
|
301
|
+
> **반드시 `.sh` 런처 파일을 생성하고, PowerShell 구문으로 bash.exe 전체 경로를 호출해야 한다.**
|
|
302
|
+
|
|
303
|
+
**실행 방식 2가지 (택 1):**
|
|
304
|
+
|
|
305
|
+
**A) .sh 런처 + send-keys (권장)**
|
|
290
306
|
```bash
|
|
291
|
-
# 세션 생성 (-s: 세션명, -d: detached 백그라운드)
|
|
292
|
-
# psmux 기본 셸이 PowerShell이므로, bash 런처 스크립트를 PowerShell에서 호출
|
|
293
307
|
psmux new-session -s "codex-swarm-{id}" -d
|
|
294
308
|
|
|
295
|
-
#
|
|
309
|
+
# PowerShell 구문으로 bash.exe를 호출 — 경로는 반드시 Windows 형식
|
|
296
310
|
BASH_WIN='C:\\Program Files\\Git\\bin\\bash.exe'
|
|
297
311
|
LAUNCH_DIR='C:\\path\\to\\.codex-swarm'
|
|
298
312
|
psmux send-keys -t "codex-swarm-{id}" \
|
|
299
313
|
"& '$BASH_WIN' '$LAUNCH_DIR\\launch-{id}.sh'" Enter
|
|
314
|
+
```
|
|
300
315
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
#
|
|
306
|
-
|
|
307
|
-
#
|
|
308
|
-
#
|
|
309
|
-
# --skip-git-repo-check: codex exec 전용이므로 대화식 모드에서 사용 불가
|
|
316
|
+
launch-{id}.sh:
|
|
317
|
+
```bash
|
|
318
|
+
#!/bin/bash
|
|
319
|
+
cd /c/path/.codex-swarm/wt-issue-{N} || exit 1
|
|
320
|
+
# stdin으로 프롬프트 전달 (-- 접두사 텍스트 파싱 회피)
|
|
321
|
+
exec codex < /c/path/.codex-swarm/prompts/prompt-{id}.md
|
|
322
|
+
# config.toml에 model/approval_mode 미설정 시만 플래그 추가:
|
|
323
|
+
# exec codex -c 'model="gpt-5.3-codex"' -c 'model_reasoning_effort="high"' --full-auto < prompt.md
|
|
310
324
|
```
|
|
311
325
|
|
|
326
|
+
**B) .ps1 런처 + send-keys**
|
|
327
|
+
```bash
|
|
328
|
+
psmux new-session -s "codex-swarm-{id}" -d
|
|
329
|
+
psmux send-keys -t "codex-swarm-{id}" \
|
|
330
|
+
"& '.\\launch-{id}.ps1'" Enter
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
launch-{id}.ps1:
|
|
334
|
+
```powershell
|
|
335
|
+
Set-Location 'C:\path\.codex-swarm\wt-issue-{N}'
|
|
336
|
+
# stdin 파이프로 전달 (& ; | -- 등 특수문자 안전)
|
|
337
|
+
Get-Content 'C:\path\.codex-swarm\prompts\prompt-{id}.md' -Raw | codex
|
|
338
|
+
# config.toml에 model 미설정 시만:
|
|
339
|
+
# Get-Content 'prompt.md' -Raw | codex -c 'model="gpt-5.3-codex"' -c 'model_reasoning_effort="high"'
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
> **금지 패턴** (절대 사용하지 말 것):
|
|
343
|
+
> ```bash
|
|
344
|
+
> # WRONG — bash 문법을 PowerShell 세션에 직접 전달
|
|
345
|
+
> psmux send-keys -t "codex-swarm-{id}" \
|
|
346
|
+
> "cd '/c/path/...' && codex -p profile \"$(cat prompt.md)\"" Enter
|
|
347
|
+
> ```
|
|
348
|
+
|
|
349
|
+
{PROFILE}은 Step 4 라우팅 결과 → `-c` 쌍으로 변환 (4-2 매핑 테이블 참조).
|
|
350
|
+
실행 모드/모델 설정은 `~/.codex/config.toml` 우선. CLI 플래그는 미설정 항목만 추가.
|
|
351
|
+
|
|
312
352
|
### Step 8: WT attach
|
|
313
353
|
|
|
314
354
|
> WT `triflux` 프로파일 사용 필수 (`commandline: "psmux"`, One Half Dark, acrylic).
|
|
315
|
-
>
|
|
355
|
+
> **새 탭(nt) 금지** — split + dashboard가 기본.
|
|
356
|
+
|
|
357
|
+
**WT 레이아웃은 반드시 AskUserQuestion으로 먼저 확인한다:**
|
|
358
|
+
|
|
359
|
+
```
|
|
360
|
+
question: "WT 레이아웃을 선택하세요"
|
|
361
|
+
header: "WT 배치"
|
|
362
|
+
options:
|
|
363
|
+
- label: "새 창에서 스플릿"
|
|
364
|
+
description: "새 WT 윈도우를 열고 세션을 split-pane으로 배치"
|
|
365
|
+
- label: "현재 창에서 스플릿"
|
|
366
|
+
description: "현재 WT 윈도우(-w 0)에 split-pane 추가"
|
|
367
|
+
- label: "dashboard 모드"
|
|
368
|
+
description: "모니터링 전용 단일 패인 (10초 폴링)"
|
|
369
|
+
- label: "attach 안 함"
|
|
370
|
+
description: "psmux 세션만 생성, WT attach 생략"
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
선택에 따른 wt.exe 명령:
|
|
316
374
|
|
|
317
375
|
```bash
|
|
376
|
+
# [새 창에서 스플릿] — wt -w new
|
|
377
|
+
# [현재 창에서 스플릿] — wt -w 0
|
|
378
|
+
|
|
318
379
|
# 2개: 상하 분할
|
|
319
|
-
wt.exe -w
|
|
380
|
+
wt.exe -w {WINDOW} \
|
|
320
381
|
sp -H -p triflux --title "{t1}" psmux attach-session -t {id1} \; \
|
|
321
382
|
sp -V -p triflux --title "{t2}" psmux attach-session -t {id2}
|
|
322
383
|
|
|
323
384
|
# 4개: 2x2 그리드
|
|
324
|
-
wt.exe -w
|
|
385
|
+
wt.exe -w {WINDOW} \
|
|
325
386
|
sp -H -p triflux --title "{t1}" psmux attach-session -t {id1} \; \
|
|
326
387
|
sp -V -p triflux --title "{t2}" psmux attach-session -t {id2} \; \
|
|
327
388
|
move-focus up \; \
|
|
@@ -330,7 +391,7 @@ wt.exe -w 0 \
|
|
|
330
391
|
sp -V -p triflux --title "{t4}" psmux attach-session -t {id4}
|
|
331
392
|
```
|
|
332
393
|
|
|
333
|
-
5개 이상이면
|
|
394
|
+
5개 이상이면 dashboard 모드를 추천:
|
|
334
395
|
```bash
|
|
335
396
|
# dashboard 모드 (모니터링 전용)
|
|
336
397
|
wt.exe -w 0 -p triflux --title "swarm-dashboard" bash -c '
|
|
@@ -481,13 +542,26 @@ fi
|
|
|
481
542
|
## 정리
|
|
482
543
|
|
|
483
544
|
전체 스웜 종료 시:
|
|
545
|
+
|
|
546
|
+
> **WT 프리징 방지**: `kill-session` 전에 반드시 각 세션에 `exit`을 보내
|
|
547
|
+
> 프로세스를 정상 종료시킨다. WT 패인이 attach 중인 상태에서 세션을
|
|
548
|
+
> 강제 kill하면 WT가 응답 없음 상태로 전환될 수 있다.
|
|
549
|
+
|
|
484
550
|
```bash
|
|
485
|
-
#
|
|
551
|
+
# 1) graceful exit — 각 세션에 exit 전송 (WT 패인 자연 종료 유도)
|
|
552
|
+
for s in $(psmux list-sessions -F '#{session_name}' 2>/dev/null | grep codex-swarm); do
|
|
553
|
+
psmux send-keys -t "$s" "exit" Enter 2>/dev/null || true
|
|
554
|
+
done
|
|
555
|
+
|
|
556
|
+
# 2) 프로세스 종료 대기 (WT 패인이 닫힐 시간 확보)
|
|
557
|
+
sleep 2
|
|
558
|
+
|
|
559
|
+
# 3) 잔여 세션 강제 종료 (exit이 먹히지 않은 경우)
|
|
486
560
|
for s in $(psmux list-sessions -F '#{session_name}' 2>/dev/null | grep codex-swarm); do
|
|
487
|
-
psmux kill-session -t "$s"
|
|
561
|
+
psmux kill-session -t "$s" 2>/dev/null || true
|
|
488
562
|
done
|
|
489
563
|
|
|
490
|
-
# worktree 정리 (머지 완료된 것만)
|
|
564
|
+
# 4) worktree 정리 (머지 완료된 것만)
|
|
491
565
|
git worktree prune
|
|
492
566
|
rm -rf .codex-swarm/
|
|
493
567
|
```
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tfx-psmux-rules
|
|
3
|
+
description: >
|
|
4
|
+
psmux + Codex CLI 명령 생성 시 반드시 적용되는 강제 규칙. PowerShell/bash 구분,
|
|
5
|
+
경로 형식, 인자 이스케이프, WT 정리, 프로파일 지정 방식 등을 검증한다.
|
|
6
|
+
이 스킬은 psmux, send-keys, codex exec, codex spawn, 세션 생성, worktree 실행,
|
|
7
|
+
launch 스크립트 생성, WT attach, 패널 스플릿, 스웜 정리 시 자동 트리거된다.
|
|
8
|
+
다른 스킬(tfx-codex-swarm, tfx-multi, tfx-remote-spawn 등)이 psmux 명령을
|
|
9
|
+
생성할 때 이 스킬의 규칙을 위반하면 생성을 중단하고 수정해야 한다.
|
|
10
|
+
triggers:
|
|
11
|
+
- tfx-psmux-rules
|
|
12
|
+
- psmux-rules
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# tfx-psmux-rules — psmux + Codex CLI 강제 규칙
|
|
16
|
+
|
|
17
|
+
> **이 스킬은 참고 문서가 아니다. 강제 규칙이다.**
|
|
18
|
+
> psmux 명령, launch 스크립트, Codex CLI 호출을 생성하는 모든 스킬은
|
|
19
|
+
> 아래 규칙을 **반드시** 준수해야 한다. 위반 시 생성을 중단하고 수정한다.
|
|
20
|
+
|
|
21
|
+
## 적용 시점
|
|
22
|
+
|
|
23
|
+
다음 행위 중 하나라도 수행할 때 이 규칙이 자동 적용된다:
|
|
24
|
+
- `psmux send-keys` 명령 생성
|
|
25
|
+
- `launch-*.sh` 또는 `launch-*.ps1` 스크립트 생성
|
|
26
|
+
- `codex` CLI 호출 인자 조합
|
|
27
|
+
- `wt.exe` 탭/패인 명령 생성
|
|
28
|
+
- 스웜 세션 정리
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## RULE 1: psmux 기본 셸 = PowerShell
|
|
33
|
+
|
|
34
|
+
psmux 세션의 기본 셸은 **PowerShell**이다.
|
|
35
|
+
|
|
36
|
+
### MUST NOT (금지)
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# WRONG — bash 문법을 PowerShell 세션에 직접 전달
|
|
40
|
+
psmux send-keys -t session "cd '/c/Users/...' && codex ..." Enter
|
|
41
|
+
psmux send-keys -t session "prompt=\$(cat file.md)" Enter
|
|
42
|
+
psmux send-keys -t session "export FOO=bar" Enter
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### MUST (필수)
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# CORRECT — PowerShell 구문으로 bash.exe 전체 경로 호출
|
|
49
|
+
BASH_WIN='C:\\Program Files\\Git\\bin\\bash.exe'
|
|
50
|
+
psmux send-keys -t session "& '$BASH_WIN' './launch.sh'" Enter
|
|
51
|
+
|
|
52
|
+
# CORRECT — PowerShell 네이티브 명령 사용
|
|
53
|
+
psmux send-keys -t session "Set-Location 'C:\\path'" Enter
|
|
54
|
+
psmux send-keys -t session "\$p = Get-Content 'file.md' -Raw" Enter
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 금지 패턴 체크리스트
|
|
58
|
+
|
|
59
|
+
| 패턴 | 문제 | 대체 |
|
|
60
|
+
|------|------|------|
|
|
61
|
+
| `cd '/c/...'` | PS가 `/c/`를 상대경로로 해석 → `C:\c\...` | `Set-Location 'C:\...'` |
|
|
62
|
+
| `$(cat file)` | bash 명령 치환, PS에서 `Get-Content` 호출됨 | `$p = Get-Content file -Raw` |
|
|
63
|
+
| `&&` | PS7에서 작동하지만 앞 명령 실패 시 의미 다름 | `;` 또는 별도 send-keys |
|
|
64
|
+
| `export VAR=val` | bash 전용 | `$env:VAR = 'val'` |
|
|
65
|
+
| `grep`, `awk`, `sed` | bash 유틸리티 | PS cmdlet 또는 bash.exe 경유 |
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## RULE 2: 경로는 Windows 형식
|
|
70
|
+
|
|
71
|
+
psmux send-keys로 전달하는 경로는 반드시 **Windows 형식**이다.
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
WRONG: /c/Users/SSAFY/Desktop/Projects/...
|
|
75
|
+
RIGHT: C:\Users\SSAFY\Desktop\Projects\...
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`.sh` 런처 내부에서만 Unix 경로(`/c/...`) 사용 가능.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## RULE 3: 프롬프트 인자 인용 필수
|
|
83
|
+
|
|
84
|
+
PRD/프롬프트 내용을 CLI 인자로 전달할 때 **반드시 인용**한다.
|
|
85
|
+
|
|
86
|
+
### PowerShell (.ps1)
|
|
87
|
+
|
|
88
|
+
```powershell
|
|
89
|
+
$p = (Get-Content 'prompt.md' -Raw) -replace "`r`n"," " -replace "`n"," "
|
|
90
|
+
|
|
91
|
+
# MUST — 인용 필수
|
|
92
|
+
codex -c 'model="gpt-5.3-codex"' "$p"
|
|
93
|
+
|
|
94
|
+
# MUST NOT — 미인용 시 & ; | 등이 PS 연산자로 해석됨
|
|
95
|
+
codex -c 'model="gpt-5.3-codex"' $p
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Bash (.sh)
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
prompt=$(cat prompt.md)
|
|
102
|
+
|
|
103
|
+
# MUST — 더블쿼트 필수
|
|
104
|
+
exec codex "$prompt"
|
|
105
|
+
|
|
106
|
+
# MUST NOT — 워드 스플리팅 + 글로빙 발생
|
|
107
|
+
exec codex $prompt
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## RULE 4: 프로파일 사용, 인자 하드코딩 금지
|
|
113
|
+
|
|
114
|
+
Codex든 Gemini든 **모델·effort·실행모드는 프로파일(config)로 관리**한다.
|
|
115
|
+
CLI 인자로 하드코딩하지 않는다.
|
|
116
|
+
|
|
117
|
+
### 4-1. 프로파일 우선
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# CORRECT — 프로파일에 model/effort/approval_mode 모두 정의해두고 호출
|
|
121
|
+
codex < prompt.md
|
|
122
|
+
codex --full-auto < prompt.md # config.toml에 approval_mode 미설정 시만
|
|
123
|
+
|
|
124
|
+
# WRONG — 인자로 모델·effort 하드코딩
|
|
125
|
+
codex -c 'model="gpt-5.3-codex"' -c 'model_reasoning_effort="high"' "prompt"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
프로파일 관리는 `tfx-profile` 스킬 또는 `~/.codex/config.toml` 직접 편집.
|
|
129
|
+
|
|
130
|
+
### 4-2. config.toml 중복 플래그 금지
|
|
131
|
+
|
|
132
|
+
config.toml에 이미 설정된 값을 CLI 플래그로 다시 지정하면 **에러**:
|
|
133
|
+
```
|
|
134
|
+
error: the argument '--dangerously-bypass-approvals-and-sandbox' cannot be used multiple times
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**규칙**: 런처 생성 전 config.toml을 확인하고, 이미 있는 항목은 CLI에서 생략.
|
|
138
|
+
|
|
139
|
+
### 4-3. 프롬프트는 stdin으로 전달
|
|
140
|
+
|
|
141
|
+
프롬프트를 CLI 인자로 넘기면 `--` 접두사 텍스트가 플래그로 파싱되는 문제 발생.
|
|
142
|
+
**항상 stdin(파이프 또는 리다이렉션)으로 전달한다.**
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# bash 런처
|
|
146
|
+
exec codex < /c/path/prompts/prompt.md
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
```powershell
|
|
150
|
+
# PS1 런처
|
|
151
|
+
Get-Content 'C:\path\prompts\prompt.md' -Raw | codex
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## RULE 5: WT 패인 정리 (프리징 방지)
|
|
157
|
+
|
|
158
|
+
WT 패인이 psmux 세션에 attach된 상태에서 `kill-session`하면 WT가 프리징된다.
|
|
159
|
+
|
|
160
|
+
### MUST (3단계 정리)
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# 1) graceful exit
|
|
164
|
+
for s in $(psmux list-sessions -F '#{session_name}' 2>/dev/null | grep codex-swarm); do
|
|
165
|
+
psmux send-keys -t "$s" "exit" Enter 2>/dev/null || true
|
|
166
|
+
done
|
|
167
|
+
|
|
168
|
+
# 2) WT 패인 종료 대기
|
|
169
|
+
sleep 2
|
|
170
|
+
|
|
171
|
+
# 3) 잔여 세션 강제 종료
|
|
172
|
+
for s in $(psmux list-sessions -F '#{session_name}' 2>/dev/null | grep codex-swarm); do
|
|
173
|
+
psmux kill-session -t "$s" 2>/dev/null || true
|
|
174
|
+
done
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### MUST NOT
|
|
178
|
+
|
|
179
|
+
```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
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## RULE 6: spark53 프로파일은 Pro 전용
|
|
189
|
+
|
|
190
|
+
`spark53_med`, `spark53_low` 등 spark 모델 프로파일은 **Codex Pro 구독 전용**이다.
|
|
191
|
+
비-Pro 환경에서는 `codex53_low`로 폴백한다.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## RULE 7: WT 레이아웃 선택 필수
|
|
196
|
+
|
|
197
|
+
WT에 패인을 배치하기 전에 **반드시** 사용자에게 레이아웃을 확인한다.
|
|
198
|
+
새 탭(`nt`)은 금지 — split + dashboard가 기본.
|
|
199
|
+
|
|
200
|
+
선택지: 새 창에서 스플릿 / 현재 창에서 스플릿 / dashboard / attach 안 함
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 위반 감지 시 행동
|
|
205
|
+
|
|
206
|
+
1. 생성한 명령/스크립트가 위 규칙을 위반하면 **즉시 수정**한다
|
|
207
|
+
2. 수정 불가능하면 **생성을 중단**하고 사용자에게 알린다
|
|
208
|
+
3. 다른 스킬이 이 규칙을 무시하고 명령을 생성하면 **경고를 출력**한다
|
package/tui/setup.mjs
CHANGED
|
@@ -326,6 +326,56 @@ function showSummary(results) {
|
|
|
326
326
|
}
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
+
// ── Star Request ──
|
|
330
|
+
|
|
331
|
+
async function starRequest() {
|
|
332
|
+
let ghOk = false;
|
|
333
|
+
try {
|
|
334
|
+
execFileSync("gh", ["auth", "status"], {
|
|
335
|
+
timeout: 5000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"],
|
|
336
|
+
});
|
|
337
|
+
ghOk = true;
|
|
338
|
+
} catch {}
|
|
339
|
+
|
|
340
|
+
if (!ghOk) {
|
|
341
|
+
console.log();
|
|
342
|
+
info(`${AMBER}⭐${RESET} 하나가 큰 차이를 만듭니다. ${CYAN}https://github.com/tellang/triflux${RESET}`);
|
|
343
|
+
console.log();
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// gh 인증됨 — 스타 여부 확인
|
|
348
|
+
let alreadyStarred = false;
|
|
349
|
+
try {
|
|
350
|
+
execFileSync("gh", ["api", "user/starred/tellang/triflux"], {
|
|
351
|
+
timeout: 5000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"],
|
|
352
|
+
});
|
|
353
|
+
alreadyStarred = true;
|
|
354
|
+
} catch {}
|
|
355
|
+
|
|
356
|
+
console.log();
|
|
357
|
+
|
|
358
|
+
if (alreadyStarred) {
|
|
359
|
+
ok(`이미 함께하고 계시군요. ${AMBER}⭐${RESET}`);
|
|
360
|
+
console.log();
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (await confirm(`${AMBER}⭐${RESET} 하나가 큰 차이를 만듭니다.`, true)) {
|
|
365
|
+
try {
|
|
366
|
+
execFileSync("gh", ["api", "-X", "PUT", "/user/starred/tellang/triflux"], {
|
|
367
|
+
timeout: 5000, stdio: ["pipe", "pipe", "pipe"],
|
|
368
|
+
});
|
|
369
|
+
ok(`함께해 주셔서 감사합니다. ${AMBER}⭐${RESET}`);
|
|
370
|
+
} catch {
|
|
371
|
+
info(`${CYAN}https://github.com/tellang/triflux${RESET}`);
|
|
372
|
+
}
|
|
373
|
+
} else {
|
|
374
|
+
console.log(` ${DIM}${CYAN}https://github.com/tellang/triflux${RESET}`);
|
|
375
|
+
}
|
|
376
|
+
console.log();
|
|
377
|
+
}
|
|
378
|
+
|
|
329
379
|
// ── Main Menu ──
|
|
330
380
|
|
|
331
381
|
const MENU = [
|
|
@@ -357,6 +407,7 @@ async function main() {
|
|
|
357
407
|
case 0: {
|
|
358
408
|
const results = await runWizard();
|
|
359
409
|
showSummary(results);
|
|
410
|
+
await starRequest();
|
|
360
411
|
break;
|
|
361
412
|
}
|
|
362
413
|
|