triflux 4.2.10 → 5.0.1
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 +53 -43
- package/README.md +53 -43
- package/bin/tfx-doctor.mjs +1 -1
- package/bin/tfx-setup.mjs +1 -1
- package/bin/triflux.mjs +1 -1
- package/hub/pipeline/index.mjs +19 -0
- package/hub/team/native.mjs +82 -32
- package/hub/team/routing.mjs +154 -0
- package/hub/tools.mjs +66 -1
- package/hud/hud-qos-status.mjs +8 -8
- package/package.json +1 -1
- package/scripts/lib/mcp-filter.mjs +45 -3
- package/skills/tfx-auto/SKILL.md +72 -14
- package/skills/tfx-multi/SKILL.md +12 -6
- package/skills/tfx-multi/references/thorough-pipeline.md +57 -18
package/README.ko.md
CHANGED
|
@@ -40,17 +40,23 @@
|
|
|
40
40
|
|
|
41
41
|
---
|
|
42
42
|
|
|
43
|
-
##
|
|
44
|
-
|
|
45
|
-
**triflux
|
|
46
|
-
|
|
47
|
-
### 주요 특징
|
|
48
|
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
- **
|
|
43
|
+
## v5의 새로운 기능
|
|
44
|
+
|
|
45
|
+
**triflux v5**는 v4의 오케스트레이션 기반을 유지하면서 파이프라인을 더 똑똑하고, 더 phase-aware하게, 더 협업적으로 발전시켰습니다. 멀티태스크 오케스트레이션에서는 이제 `--thorough`가 기본 경로이므로, 계획, 승인, 검증, 복구가 기본값으로 활성화됩니다.
|
|
46
|
+
|
|
47
|
+
### 주요 특징
|
|
48
|
+
|
|
49
|
+
- **`--thorough` 기본화** — 멀티태스크 오케스트레이션은 기본적으로 `plan` → `prd` → `exec` → `verify` → `fix` 전체 파이프라인을 실행합니다. 경량 경로가 필요할 때만 `--quick`을 명시합니다.
|
|
50
|
+
- **Opus × Codex Scout 계획 협업** — `plan` 단계에서 Opus가 설계를 주도하고 Codex scout 워커가 코드베이스를 병렬 탐색한 뒤 최종 계획에 반영합니다.
|
|
51
|
+
- **DAG 기반 라우팅 휴리스틱** — `dag_width`와 `complexity`를 함께 반영해 `quick_single`, `thorough_single`, `quick_team`, `thorough_team`, `batch_single` 전략 중 하나를 선택합니다.
|
|
52
|
+
- **피드백 루프 복원** — 워커는 여러 차례 재실행될 수 있고, 최종 완료 전 리드의 피드백을 다시 받아 반영할 수 있습니다.
|
|
53
|
+
- **HITL 승인 게이트** — `pipeline_advance_gated`를 통해 단계 전이 전에 사람 승인 체크포인트를 삽입합니다.
|
|
54
|
+
- **Phase-aware MCP 필터링** — 파이프라인 단계에 따라 MCP 노출을 조정해 `plan`, `prd`, `verify`는 읽기 중심으로 유지하고 `exec`는 더 넓은 도구 세트를 사용합니다.
|
|
55
|
+
- **Plan 파일 영속화** — 최종 계획 Markdown을 `.tfx/plans/{team}-plan.md`에 저장하고 파이프라인 artifact로 추적합니다.
|
|
56
|
+
- **Hub IPC 아키텍처** — Named Pipe 및 HTTP MCP 브리지를 활용한 초고속 상주형 허브 서버.
|
|
57
|
+
- **위임(Delegator) MCP** — 에이전트와 유연하게 상호작용할 수 있는 전용 MCP 도구(`delegate`, `reply`, `status`).
|
|
58
|
+
- **psmux / Windows 네이티브** — `tmux` (WSL)와 `psmux` (Windows Terminal 네이티브)를 모두 지원하는 하이브리드 세션 관리.
|
|
59
|
+
- **QoS 대시보드** — AIMD 기반 동적 배치 사이징 및 실시간 상태 모니터링.
|
|
54
60
|
- **21종 이상의 전문 에이전트** — `scientist-deep`부터 `spark_fast`까지, 작업에 최적화된 에이전트 라인업.
|
|
55
61
|
|
|
56
62
|
---
|
|
@@ -95,35 +101,39 @@ npm install -g triflux
|
|
|
95
101
|
tfx setup
|
|
96
102
|
```
|
|
97
103
|
|
|
98
|
-
### 3. 사용 방법
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
# 자동 모드 — 허브를 통한
|
|
102
|
-
/tfx-auto "인증 리팩터링 + UI 업데이트 + 테스트 추가"
|
|
103
|
-
|
|
104
|
-
#
|
|
105
|
-
/tfx-auto --
|
|
106
|
-
|
|
107
|
-
# 직접 위임
|
|
108
|
-
/tfx-delegate "최신 React 패턴 조사" --provider gemini
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
|
120
|
-
|
|
121
|
-
| **
|
|
122
|
-
| **
|
|
123
|
-
| **
|
|
124
|
-
| **
|
|
125
|
-
|
|
126
|
-
|
|
104
|
+
### 3. 사용 방법
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# 자동 모드 — 허브를 통한 thorough 멀티태스크 오케스트레이션
|
|
108
|
+
/tfx-auto "인증 리팩터링 + UI 업데이트 + 테스트 추가"
|
|
109
|
+
|
|
110
|
+
# quick 모드 — 전체 계획/검증 루프 생략
|
|
111
|
+
/tfx-auto --quick "작은 회귀 버그 수정"
|
|
112
|
+
|
|
113
|
+
# 직접 위임
|
|
114
|
+
/tfx-delegate "최신 React 패턴 조사" --provider gemini
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
v5에서는 멀티태스크 오케스트레이션이 기본적으로 `--thorough`로 실행되며, 더 가벼운 경로가 필요할 때 `--quick`을 사용합니다.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 파이프라인: `--thorough` 모드
|
|
122
|
+
|
|
123
|
+
v5 파이프라인은 복잡한 엔지니어링 작업에서 기본이 되는 thorough 실행 루프입니다. Plan 산출물은 영속화되고, PRD 핸드오프에는 사람 승인 게이트를 둘 수 있으며, verify/fix 단계는 워커 피드백 루프를 복원합니다.
|
|
124
|
+
|
|
125
|
+
| 단계 | 설명 |
|
|
126
|
+
|------|------|
|
|
127
|
+
| **plan** | Opus가 설계를 주도하고 Codex scout가 병렬 탐색을 수행하며, 계획 산출물을 파일로 영속화합니다. |
|
|
128
|
+
| **prd** | 상세한 기술 명세서(Technical Spec / PRD)를 생성하고 승인 체크포인트를 준비합니다. |
|
|
129
|
+
| **exec** | 실제 코드 구현을 수행합니다. |
|
|
130
|
+
| **verify** | 테스트를 실행하고 구현 결과가 PRD와 일치하는지 검증합니다. |
|
|
131
|
+
| **fix** | (루프) 검증 단계에서 발견된 실패를 리드 피드백과 함께 재실행하여 수정합니다 (최대 3회). |
|
|
132
|
+
| **ralph** | (재시작) 수정 루프 실패 시, 새로운 통찰을 바탕으로 `plan`부터 다시 시작합니다 (최대 10회). |
|
|
133
|
+
|
|
134
|
+
Phase-aware MCP 필터링으로 `plan`, `prd`, `verify`는 읽기 중심으로 제한되며, `prd` → `exec` 전이는 `pipeline_advance_gated`로 승인 대기를 걸 수 있습니다.
|
|
135
|
+
|
|
136
|
+
---
|
|
127
137
|
|
|
128
138
|
## 위임(Delegator) MCP
|
|
129
139
|
|
|
@@ -155,9 +165,9 @@ MCP 도구를 통해 허브와 직접 상호작용하세요.
|
|
|
155
165
|
|
|
156
166
|
---
|
|
157
167
|
|
|
158
|
-
## 보안
|
|
159
|
-
|
|
160
|
-
triflux
|
|
168
|
+
## 보안
|
|
169
|
+
|
|
170
|
+
triflux v5는 안전한 전문 개발 환경을 위해 설계되었습니다.
|
|
161
171
|
|
|
162
172
|
- **허브 토큰 인증** — `TFX_HUB_TOKEN`을 이용한 보안 IPC (Bearer 인증).
|
|
163
173
|
- **Localhost 전용** — 허브가 기본적으로 `127.0.0.1`에만 바인딩되어 외부 접근을 차단합니다.
|
package/README.md
CHANGED
|
@@ -40,17 +40,23 @@
|
|
|
40
40
|
|
|
41
41
|
---
|
|
42
42
|
|
|
43
|
-
## What's New in
|
|
44
|
-
|
|
45
|
-
**triflux
|
|
46
|
-
|
|
47
|
-
### Key Features
|
|
48
|
-
|
|
49
|
-
-
|
|
50
|
-
- **
|
|
51
|
-
- **
|
|
52
|
-
- **
|
|
53
|
-
- **
|
|
43
|
+
## What's New in v5?
|
|
44
|
+
|
|
45
|
+
**triflux v5** keeps the v4 orchestration foundation intact while making the pipeline smarter, more phase-aware, and more collaborative. For multi-task orchestration, `--thorough` is now the default path, so planning, approval, verification, and recovery stay on by default instead of being bolted on later.
|
|
46
|
+
|
|
47
|
+
### Key Features
|
|
48
|
+
|
|
49
|
+
- **`--thorough` by Default** — Multi-task orchestration now defaults to the full `plan` → `prd` → `exec` → `verify` → `fix` pipeline. Reach for `--quick` only when you explicitly want the lighter path.
|
|
50
|
+
- **Opus × Codex Scout Planning** — In `plan`, Opus leads architecture decisions while Codex scout workers explore the codebase in parallel and feed findings back into the final plan.
|
|
51
|
+
- **DAG-based Routing Heuristics** — Routing now considers both `dag_width` and `complexity` to choose between `quick_single`, `thorough_single`, `quick_team`, `thorough_team`, and `batch_single`.
|
|
52
|
+
- **Restored Feedback Loop** — Workers can be re-run for multiple iterations and receive lead feedback before final completion.
|
|
53
|
+
- **HITL Approval Gate** — `pipeline_advance_gated` inserts a human approval checkpoint before gated phase transitions.
|
|
54
|
+
- **Phase-aware MCP Filtering** — MCP exposure changes by pipeline phase so `plan`, `prd`, and `verify` stay read-focused while `exec` keeps broader tooling.
|
|
55
|
+
- **Persistent Plan Files** — Final plan markdown is saved to `.tfx/plans/{team}-plan.md` and tracked as a pipeline artifact.
|
|
56
|
+
- **Hub IPC Architecture** — Lightning-fast resident Hub server with Named Pipe & HTTP MCP bridge.
|
|
57
|
+
- **Delegator MCP** — Native MCP tools (`delegate`, `reply`, `status`) for seamless agent interaction.
|
|
58
|
+
- **psmux / Windows Native** — Hybrid support for `tmux` (WSL) and `psmux` (Windows Terminal native).
|
|
59
|
+
- **QoS Dashboard** — Real-time health monitoring with AIMD-based dynamic batch sizing.
|
|
54
60
|
- **21+ specialized agents** — From `scientist-deep` to `spark_fast`, each optimized for specific tasks.
|
|
55
61
|
|
|
56
62
|
---
|
|
@@ -95,35 +101,39 @@ Synchronize scripts, register skills to Claude Code, and configure the HUD.
|
|
|
95
101
|
tfx setup
|
|
96
102
|
```
|
|
97
103
|
|
|
98
|
-
### 3. Usage
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
# Auto mode —
|
|
102
|
-
/tfx-auto "refactor auth + update UI + add tests"
|
|
103
|
-
|
|
104
|
-
#
|
|
105
|
-
/tfx-auto --
|
|
106
|
-
|
|
107
|
-
# Direct Delegation
|
|
108
|
-
/tfx-delegate "research latest React patterns" --provider gemini
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
|
120
|
-
|
|
121
|
-
| **
|
|
122
|
-
| **
|
|
123
|
-
| **
|
|
124
|
-
| **
|
|
125
|
-
|
|
126
|
-
|
|
104
|
+
### 3. Usage
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Auto mode — Thorough multi-task orchestration via Hub
|
|
108
|
+
/tfx-auto "refactor auth + update UI + add tests"
|
|
109
|
+
|
|
110
|
+
# Quick mode — Skip the full planning/verification loop
|
|
111
|
+
/tfx-auto --quick "fix a small regression"
|
|
112
|
+
|
|
113
|
+
# Direct Delegation
|
|
114
|
+
/tfx-delegate "research latest React patterns" --provider gemini
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
In v5, multi-task orchestration defaults to `--thorough`; use `--quick` when you explicitly want the lighter path.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Pipeline: `--thorough` Mode
|
|
122
|
+
|
|
123
|
+
The v5 pipeline is the default thorough execution loop for complex engineering work. Plan artifacts are persisted, PRD handoff can be gated by human approval, and verify/fix restores the worker feedback loop.
|
|
124
|
+
|
|
125
|
+
| Phase | Description |
|
|
126
|
+
|-------|-------------|
|
|
127
|
+
| **plan** | Opus-led solution design with parallel Codex scout exploration and a persisted plan artifact. |
|
|
128
|
+
| **prd** | Generate a detailed Technical Specification / PRD and prepare the approval checkpoint. |
|
|
129
|
+
| **exec** | Perform the actual code implementation. |
|
|
130
|
+
| **verify** | Run tests and verify the implementation against the PRD. |
|
|
131
|
+
| **fix** | (Loop) Re-run workers with lead feedback to fix failures identified in the verify phase (Max 3). |
|
|
132
|
+
| **ralph** | (Reset) If the fix loop fails, restart from `plan` with new insights (Max 10). |
|
|
133
|
+
|
|
134
|
+
Phase-aware MCP filtering keeps `plan`, `prd`, and `verify` read-heavy, while the `prd` → `exec` handoff can be gated through `pipeline_advance_gated`.
|
|
135
|
+
|
|
136
|
+
---
|
|
127
137
|
|
|
128
138
|
## Delegator MCP
|
|
129
139
|
|
|
@@ -155,9 +165,9 @@ Interact with the Hub directly through MCP tools.
|
|
|
155
165
|
|
|
156
166
|
---
|
|
157
167
|
|
|
158
|
-
## Security
|
|
159
|
-
|
|
160
|
-
triflux
|
|
168
|
+
## Security
|
|
169
|
+
|
|
170
|
+
triflux v5 is designed for secure, professional environments:
|
|
161
171
|
|
|
162
172
|
- **Hub Token Auth** — Secure IPC using `TFX_HUB_TOKEN` (Bearer Auth).
|
|
163
173
|
- **Localhost Only** — Default Hub binding to `127.0.0.1` prevents external access.
|
package/bin/tfx-doctor.mjs
CHANGED
package/bin/tfx-setup.mjs
CHANGED
package/bin/triflux.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
// triflux CLI — setup, doctor, version
|
|
3
3
|
import { copyFileSync, existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync, readdirSync, unlinkSync, rmSync, statSync, openSync, closeSync } from "fs";
|
|
4
4
|
import { join, dirname } from "path";
|
package/hub/pipeline/index.mjs
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
//
|
|
3
3
|
// 상태(state.mjs) + 전이(transitions.mjs) 통합 인터페이스
|
|
4
4
|
|
|
5
|
+
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
6
|
+
import { join, resolve } from 'node:path';
|
|
7
|
+
|
|
5
8
|
import { canTransition, transitionPhase, ralphRestart, TERMINAL } from './transitions.mjs';
|
|
6
9
|
import {
|
|
7
10
|
ensurePipelineTable,
|
|
@@ -92,6 +95,22 @@ export function createPipeline(db, teamName, opts = {}) {
|
|
|
92
95
|
state = updatePipelineState(db, teamName, { artifacts });
|
|
93
96
|
},
|
|
94
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Plan 파일을 .tfx/plans/{teamName}-plan.md 에 기록하고
|
|
100
|
+
* artifact('plan_path')에 절대 경로를 저장한다.
|
|
101
|
+
* @param {string} content - Plan markdown 내용
|
|
102
|
+
* @returns {string} 절대 경로
|
|
103
|
+
*/
|
|
104
|
+
writePlanFile(content) {
|
|
105
|
+
const safeName = teamName.replace(/[<>:"/\\|?*\x00-\x1f]/g, '_');
|
|
106
|
+
const planDir = resolve(process.cwd(), '.tfx', 'plans');
|
|
107
|
+
mkdirSync(planDir, { recursive: true });
|
|
108
|
+
const planPath = join(planDir, `${safeName}-plan.md`);
|
|
109
|
+
writeFileSync(planPath, content, 'utf8');
|
|
110
|
+
this.setArtifact('plan_path', planPath);
|
|
111
|
+
return planPath;
|
|
112
|
+
},
|
|
113
|
+
|
|
95
114
|
/**
|
|
96
115
|
* 터미널 상태 여부
|
|
97
116
|
*/
|
package/hub/team/native.mjs
CHANGED
|
@@ -10,9 +10,17 @@ import * as fs from "node:fs/promises";
|
|
|
10
10
|
import os from "node:os";
|
|
11
11
|
import path from "node:path";
|
|
12
12
|
|
|
13
|
-
const ROUTE_SCRIPT = "~/.claude/scripts/tfx-route.sh";
|
|
14
|
-
export const SLIM_WRAPPER_SUBAGENT_TYPE = "slim-wrapper";
|
|
15
|
-
|
|
13
|
+
const ROUTE_SCRIPT = "~/.claude/scripts/tfx-route.sh";
|
|
14
|
+
export const SLIM_WRAPPER_SUBAGENT_TYPE = "slim-wrapper";
|
|
15
|
+
/** scout 역할 기본 설정 — read-only 탐색 전용 */
|
|
16
|
+
export const SCOUT_ROLE_CONFIG = {
|
|
17
|
+
cli: "codex",
|
|
18
|
+
role: "scientist",
|
|
19
|
+
mcp_profile: "analyze",
|
|
20
|
+
maxIterations: 2,
|
|
21
|
+
readOnly: true,
|
|
22
|
+
};
|
|
23
|
+
const ROUTE_LOG_RE = /\[tfx-route\]/i;
|
|
16
24
|
const ROUTE_COMMAND_RE = /(?:^|[\s"'`])(?:bash\s+)?(?:[^"'`\s]*\/)?tfx-route\.sh\b/i;
|
|
17
25
|
const ROUTE_PROMPT_RE = /tfx-route\.sh/i;
|
|
18
26
|
const DIRECT_TOOL_BYPASS_RE = /\b(?:Read|Edit|Write)\s*\(/;
|
|
@@ -143,9 +151,10 @@ function getRouteTimeout(role, _mcpProfile) {
|
|
|
143
151
|
* @param {number} [opts.workerIndex] — 검색 힌트 회전에 사용할 워커 인덱스(1-based)
|
|
144
152
|
* @param {string} [opts.searchTool] — 전용 검색 도구 힌트(brave-search|tavily|exa)
|
|
145
153
|
* @param {number} [opts.bashTimeout] — (deprecated, async에서는 무시됨)
|
|
154
|
+
* @param {number} [opts.maxIterations=3] — 피드백 루프 최대 반복 횟수
|
|
146
155
|
* @returns {string} 슬림 래퍼 프롬프트
|
|
147
156
|
*/
|
|
148
|
-
export function buildSlimWrapperPrompt(cli, opts = {}) {
|
|
157
|
+
export function buildSlimWrapperPrompt(cli, opts = {}) {
|
|
149
158
|
const {
|
|
150
159
|
subtask,
|
|
151
160
|
role = "executor",
|
|
@@ -157,30 +166,36 @@ export function buildSlimWrapperPrompt(cli, opts = {}) {
|
|
|
157
166
|
workerIndex,
|
|
158
167
|
searchTool = "",
|
|
159
168
|
pipelinePhase = "",
|
|
169
|
+
maxIterations = 3,
|
|
160
170
|
} = opts;
|
|
161
171
|
|
|
162
172
|
const routeTimeoutSec = getRouteTimeout(role, mcp_profile);
|
|
163
173
|
const escaped = subtask.replace(/'/g, "'\\''");
|
|
164
|
-
const pipelineHint = pipelinePhase
|
|
165
|
-
? `\n파이프라인 단계: ${pipelinePhase}`
|
|
166
|
-
: '';
|
|
167
|
-
const routeEnvPrefix = buildRouteEnvPrefix(agentName, workerIndex, searchTool);
|
|
174
|
+
const pipelineHint = pipelinePhase
|
|
175
|
+
? `\n파이프라인 단계: ${pipelinePhase}`
|
|
176
|
+
: '';
|
|
177
|
+
const routeEnvPrefix = buildRouteEnvPrefix(agentName, workerIndex, searchTool);
|
|
178
|
+
const scoutConstraint = (role === "scout" || role === "scientist")
|
|
179
|
+
? "\n이 워커는 scout(탐색 전용)이다. 코드를 수정하거나 파일을 생성하지 마라. 기존 코드를 읽고 분석하여 보고만 하라."
|
|
180
|
+
: "";
|
|
168
181
|
|
|
169
182
|
// Bash 도구 timeout (모두 600초 이내)
|
|
170
183
|
const launchTimeoutMs = 15000; // Step 1: fork + job_id 반환
|
|
171
184
|
const waitTimeoutMs = 570000; // Step 2: 내부 폴링 (540초 대기 + 여유)
|
|
172
185
|
const resultTimeoutMs = 30000; // Step 3: 결과 읽기
|
|
173
186
|
|
|
174
|
-
return `실행 프로토콜 (subagent_type="${SLIM_WRAPPER_SUBAGENT_TYPE}", async):
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
3. "still_running"이면 Step 2 반복, "done"이면 --job-result로 결과 수집
|
|
178
|
-
4. TaskUpdate + SendMessage → 종료${pipelineHint}
|
|
187
|
+
return `실행 프로토콜 (subagent_type="${SLIM_WRAPPER_SUBAGENT_TYPE}", async + feedback):
|
|
188
|
+
MAX_ITERATIONS = ${maxIterations}
|
|
189
|
+
ITERATION = 0${pipelineHint}
|
|
179
190
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
191
|
+
Step 0 — 시작 보고 (턴 경계 생성):
|
|
192
|
+
TaskUpdate(taskId: "${taskId}", status: "in_progress")
|
|
193
|
+
SendMessage(type: "message", recipient: "${leadName}", content: "작업 시작: ${agentName}", summary: "task ${taskId} started")
|
|
194
|
+
|
|
195
|
+
[HARD CONSTRAINT] 허용 도구: Bash, TaskUpdate, TaskGet, TaskList, SendMessage만 사용한다.
|
|
196
|
+
Read, Edit, Write, Grep, Glob, Agent, WebSearch, WebFetch 등 다른 모든 도구 사용을 금지한다.
|
|
197
|
+
코드를 직접 읽거나 수정하면 안 된다. 반드시 아래 Bash 명령(tfx-route.sh)을 통해 Codex/Gemini에 위임하라.
|
|
198
|
+
이 규칙을 위반하면 작업 실패로 간주한다.${scoutConstraint}
|
|
184
199
|
|
|
185
200
|
gemini/codex를 직접 호출하지 마라. 반드시 tfx-route.sh를 거쳐야 한다.
|
|
186
201
|
프롬프트를 파일로 저장하지 마라. tfx-route.sh가 인자로 받는다.
|
|
@@ -193,29 +208,64 @@ Step 2 — 완료 대기 (내부 폴링, 최대 540초):
|
|
|
193
208
|
Bash(command: 'bash ${ROUTE_SCRIPT} --job-wait JOB_ID 540', timeout: ${waitTimeoutMs})
|
|
194
209
|
→ 주기적 "waiting elapsed=Ns progress=NB" 출력 후 최종 상태:
|
|
195
210
|
"done" → Step 3으로
|
|
196
|
-
"timeout" 또는 "failed ..." → Step 4로 (실패
|
|
211
|
+
"timeout" 또는 "failed ..." → Step 4로 (실패 상태로)
|
|
197
212
|
"still_running ..." → Step 2 반복 (같은 명령 재실행)
|
|
198
213
|
|
|
199
214
|
Step 3 — 결과 수집:
|
|
200
215
|
Bash(command: 'bash ${ROUTE_SCRIPT} --job-result JOB_ID', timeout: ${resultTimeoutMs})
|
|
201
|
-
→
|
|
216
|
+
→ RESULT에 저장.
|
|
202
217
|
|
|
203
|
-
Step 4 —
|
|
218
|
+
Step 4 — 결과 보고 (턴 경계 생성, TaskUpdate 하지 않음):
|
|
204
219
|
"done"이면:
|
|
205
|
-
|
|
206
|
-
SendMessage(type: "message", recipient: "${leadName}", content: "완료: ${agentName}", summary: "task ${taskId} success")
|
|
220
|
+
SendMessage(type: "message", recipient: "${leadName}", content: "결과 (iteration ITERATION): ${agentName} 성공\\n{결과 요약}", summary: "task ${taskId} iteration ITERATION done")
|
|
207
221
|
"timeout" 또는 "failed"이면:
|
|
208
|
-
|
|
209
|
-
SendMessage(type: "message", recipient: "${leadName}", content: "실패: ${agentName} (상태)", summary: "task ${taskId} failed")
|
|
222
|
+
SendMessage(type: "message", recipient: "${leadName}", content: "결과 (iteration ITERATION): ${agentName} 실패\\n{에러 요약}", summary: "task ${taskId} iteration ITERATION failed")
|
|
210
223
|
TFX_NEEDS_FALLBACK 출력 감지 시:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
224
|
+
→ Step 6으로 즉시 이동 (fallback은 재실행 불가)
|
|
225
|
+
|
|
226
|
+
Step 5 — 피드백 대기:
|
|
227
|
+
SendMessage 후 너는 IDLE 상태가 된다. 리드의 응답을 기다려라.
|
|
228
|
+
수신 메시지에 따라:
|
|
229
|
+
- "재실행:" 포함 → ITERATION++ → ITERATION < MAX_ITERATIONS이면 메시지의 지시를 반영하여 Step 1로. ITERATION >= MAX_ITERATIONS이면 Step 6으로 (반복 한도 초과)
|
|
230
|
+
- "승인" 또는 기타 → Step 6으로
|
|
231
|
+
- 메시지 없이 팀이 삭제되면 자동 종료 (처리 불필요)
|
|
232
|
+
|
|
233
|
+
Step 6 — 최종 종료 (반드시 실행):
|
|
234
|
+
TaskUpdate(taskId: "${taskId}", status: "completed", metadata: {result: "success"|"failed"|"fallback", iterations: ITERATION})
|
|
235
|
+
SendMessage(type: "message", recipient: "${leadName}", content: "최종 완료: ${agentName} (ITERATION회 실행)", summary: "task ${taskId} final")
|
|
236
|
+
→ 종료. 이후 추가 도구 호출 금지.`;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* scout 파견용 프롬프트 생성
|
|
241
|
+
* @param {object} opts
|
|
242
|
+
* @param {string} opts.question — 탐색 질문
|
|
243
|
+
* @param {string} [opts.scope] — 탐색 범위 힌트 (파일 패턴)
|
|
244
|
+
* @param {string} [opts.teamName] — 팀 이름
|
|
245
|
+
* @param {string} [opts.taskId] — 태스크 ID
|
|
246
|
+
* @param {string} [opts.agentName] — 에이전트 이름
|
|
247
|
+
* @param {string} [opts.leadName] — 리드 이름
|
|
248
|
+
* @returns {string} slim wrapper 프롬프트
|
|
249
|
+
*/
|
|
250
|
+
export function buildScoutDispatchPrompt(opts = {}) {
|
|
251
|
+
const { question, scope = "", teamName, taskId, agentName, leadName } = opts;
|
|
252
|
+
const subtask = scope
|
|
253
|
+
? `${question} 탐색 범위: ${scope}`
|
|
254
|
+
: question;
|
|
255
|
+
return buildSlimWrapperPrompt("codex", {
|
|
256
|
+
subtask,
|
|
257
|
+
role: "scientist",
|
|
258
|
+
teamName,
|
|
259
|
+
taskId,
|
|
260
|
+
agentName,
|
|
261
|
+
leadName,
|
|
262
|
+
mcp_profile: "analyze",
|
|
263
|
+
maxIterations: SCOUT_ROLE_CONFIG.maxIterations,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* v3 하이브리드 래퍼 프롬프트 생성
|
|
219
269
|
* psmux pane 기반 비동기 실행 + polling 패턴.
|
|
220
270
|
* Agent가 idle 상태를 유지하여 인터럽트 수신이 가능하다.
|
|
221
271
|
*
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 라우팅 결정 함수
|
|
3
|
+
* @param {object} opts
|
|
4
|
+
* @param {Array<{id:string, description?:string, agent?:string, depends_on?:string[], complexity?:string}>} opts.subtasks
|
|
5
|
+
* @param {string} opts.graph_type - "INDEPENDENT" | "SEQUENTIAL" | "DAG"
|
|
6
|
+
* @param {boolean} opts.thorough - thorough 모드 여부
|
|
7
|
+
* @returns {{
|
|
8
|
+
* strategy: "quick_single" | "thorough_single" | "quick_team" | "thorough_team" | "batch_single",
|
|
9
|
+
* reason: string,
|
|
10
|
+
* dag_width: number,
|
|
11
|
+
* max_complexity: string
|
|
12
|
+
* }}
|
|
13
|
+
*/
|
|
14
|
+
export function resolveRoutingStrategy({ subtasks, graph_type, thorough }) {
|
|
15
|
+
const N = subtasks.length;
|
|
16
|
+
if (N === 0) {
|
|
17
|
+
return { strategy: 'quick_single', reason: 'empty_subtasks', dag_width: 0, max_complexity: 'S' };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const dag_width = computeDagWidth(subtasks, graph_type);
|
|
21
|
+
const max_complexity = getMaxComplexity(subtasks);
|
|
22
|
+
const isHighComplexity = ['L', 'XL'].includes(max_complexity);
|
|
23
|
+
const allSameAgent = new Set(subtasks.map((s) => s.agent)).size === 1;
|
|
24
|
+
const allSmall = subtasks.every((s) => normalizeComplexity(s.complexity) === 'S');
|
|
25
|
+
|
|
26
|
+
// N==1: 단일 태스크
|
|
27
|
+
if (N === 1) {
|
|
28
|
+
if (thorough || isHighComplexity) {
|
|
29
|
+
return {
|
|
30
|
+
strategy: 'thorough_single',
|
|
31
|
+
reason: 'single_high_complexity',
|
|
32
|
+
dag_width,
|
|
33
|
+
max_complexity,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
strategy: 'quick_single',
|
|
38
|
+
reason: 'single_low_complexity',
|
|
39
|
+
dag_width,
|
|
40
|
+
max_complexity,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// dag_width==1: 사실상 순차 -> single
|
|
45
|
+
if (dag_width === 1) {
|
|
46
|
+
if (thorough || isHighComplexity) {
|
|
47
|
+
return {
|
|
48
|
+
strategy: 'thorough_single',
|
|
49
|
+
reason: 'sequential_chain',
|
|
50
|
+
dag_width,
|
|
51
|
+
max_complexity,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
strategy: 'quick_single',
|
|
56
|
+
reason: 'sequential_chain',
|
|
57
|
+
dag_width,
|
|
58
|
+
max_complexity,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 동일 에이전트 + 모두 S: 프롬프트 병합 -> batch single
|
|
63
|
+
if (allSameAgent && allSmall) {
|
|
64
|
+
return {
|
|
65
|
+
strategy: 'batch_single',
|
|
66
|
+
reason: 'same_agent_small_batch',
|
|
67
|
+
dag_width,
|
|
68
|
+
max_complexity,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// dag_width >= 2: 팀
|
|
73
|
+
if (thorough || isHighComplexity) {
|
|
74
|
+
return {
|
|
75
|
+
strategy: 'thorough_team',
|
|
76
|
+
reason: 'parallel_high_complexity',
|
|
77
|
+
dag_width,
|
|
78
|
+
max_complexity,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
strategy: 'quick_team',
|
|
83
|
+
reason: 'parallel_low_complexity',
|
|
84
|
+
dag_width,
|
|
85
|
+
max_complexity,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* DAG 폭 계산 - 레벨별 최대 병렬 태스크 수
|
|
91
|
+
* @param {Array<{id:string, depends_on?:string[]}>} subtasks
|
|
92
|
+
* @param {string} graph_type
|
|
93
|
+
* @returns {number}
|
|
94
|
+
*/
|
|
95
|
+
function computeDagWidth(subtasks, graph_type) {
|
|
96
|
+
if (graph_type === 'SEQUENTIAL') return 1;
|
|
97
|
+
if (graph_type === 'INDEPENDENT') return subtasks.length;
|
|
98
|
+
|
|
99
|
+
// DAG: 레벨별 계산 (순환 의존 방어)
|
|
100
|
+
const levels = {};
|
|
101
|
+
const visiting = new Set();
|
|
102
|
+
|
|
103
|
+
function getLevel(task) {
|
|
104
|
+
if (levels[task.id] !== undefined) return levels[task.id];
|
|
105
|
+
if (visiting.has(task.id)) {
|
|
106
|
+
levels[task.id] = 0; // 순환 끊기
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
if (!task.depends_on || task.depends_on.length === 0) {
|
|
110
|
+
levels[task.id] = 0;
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
visiting.add(task.id);
|
|
114
|
+
const depLevels = task.depends_on.map((depId) => {
|
|
115
|
+
const dep = subtasks.find((s) => s.id === depId);
|
|
116
|
+
return dep ? getLevel(dep) : 0;
|
|
117
|
+
});
|
|
118
|
+
visiting.delete(task.id);
|
|
119
|
+
levels[task.id] = Math.max(...depLevels) + 1;
|
|
120
|
+
return levels[task.id];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
subtasks.forEach(getLevel);
|
|
124
|
+
|
|
125
|
+
const levelCounts = {};
|
|
126
|
+
for (const level of Object.values(levels)) {
|
|
127
|
+
levelCounts[level] = (levelCounts[level] || 0) + 1;
|
|
128
|
+
}
|
|
129
|
+
return Math.max(...Object.values(levelCounts), 1);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 최대 복잡도 추출
|
|
134
|
+
* @param {Array<{complexity?:string}>} subtasks
|
|
135
|
+
* @returns {"S" | "M" | "L" | "XL"}
|
|
136
|
+
*/
|
|
137
|
+
function getMaxComplexity(subtasks) {
|
|
138
|
+
const order = { S: 0, M: 1, L: 2, XL: 3 };
|
|
139
|
+
let max = 'S';
|
|
140
|
+
for (const s of subtasks) {
|
|
141
|
+
const complexity = normalizeComplexity(s.complexity);
|
|
142
|
+
if (order[complexity] > order[max]) max = complexity;
|
|
143
|
+
}
|
|
144
|
+
return max;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* complexity 기본값 보정
|
|
149
|
+
* @param {string | undefined} complexity
|
|
150
|
+
* @returns {"S" | "M" | "L" | "XL"}
|
|
151
|
+
*/
|
|
152
|
+
function normalizeComplexity(complexity) {
|
|
153
|
+
return ['S', 'M', 'L', 'XL'].includes(complexity) ? complexity : 'M';
|
|
154
|
+
}
|
package/hub/tools.mjs
CHANGED
|
@@ -472,7 +472,72 @@ export function createTools(store, router, hitl, pipe = null) {
|
|
|
472
472
|
}),
|
|
473
473
|
},
|
|
474
474
|
|
|
475
|
-
// ── 19.
|
|
475
|
+
// ── 19. pipeline_advance_gated (HITL 승인 게이트) ──
|
|
476
|
+
{
|
|
477
|
+
name: 'pipeline_advance_gated',
|
|
478
|
+
description: 'HITL 승인 게이트가 포함된 파이프라인 전이. 지정 단계로의 전이 전 사용자 승인을 요청하고, 승인 후 전이를 실행합니다. deadline 초과 시 default_action에 따라 자동 처리됩니다.',
|
|
479
|
+
inputSchema: {
|
|
480
|
+
type: 'object',
|
|
481
|
+
required: ['team_name', 'phase'],
|
|
482
|
+
properties: {
|
|
483
|
+
team_name: { type: 'string', pattern: '^[a-z0-9][a-z0-9-]*$' },
|
|
484
|
+
phase: { type: 'string', enum: ['plan', 'prd', 'exec', 'verify', 'fix', 'complete', 'failed'] },
|
|
485
|
+
prompt: { type: 'string', description: '사용자에게 표시할 승인 요청 메시지' },
|
|
486
|
+
deadline_ms: { type: 'integer', minimum: 5000, maximum: 600000, default: 120000 },
|
|
487
|
+
default_action: { type: 'string', enum: ['timeout_continue', 'timeout_abort'], default: 'timeout_continue' },
|
|
488
|
+
requester_agent: { type: 'string', description: '요청자 에이전트 이름' },
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
handler: wrap('PIPELINE_ADVANCE_GATED_FAILED', (args) => {
|
|
492
|
+
ensurePipelineTable(store.db);
|
|
493
|
+
const pipeline = createPipeline(store.db, args.team_name);
|
|
494
|
+
|
|
495
|
+
// 전이 가능 여부 사전 확인
|
|
496
|
+
if (!pipeline.canAdvance(args.phase)) {
|
|
497
|
+
const current = pipeline.getState();
|
|
498
|
+
return {
|
|
499
|
+
ok: false,
|
|
500
|
+
error: {
|
|
501
|
+
code: 'TRANSITION_BLOCKED',
|
|
502
|
+
message: `전이 불가: ${current.phase} → ${args.phase}`,
|
|
503
|
+
},
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// HITL 승인 요청 생성
|
|
508
|
+
const approvalPrompt = args.prompt || `파이프라인 ${args.team_name}: ${pipeline.getState().phase} → ${args.phase} 전이를 승인하시겠습니까?`;
|
|
509
|
+
const deadlineMs = args.deadline_ms || 120000;
|
|
510
|
+
const now = Date.now();
|
|
511
|
+
|
|
512
|
+
const hitlResult = hitl.requestHumanInput({
|
|
513
|
+
requester_agent: args.requester_agent || `pipeline:${args.team_name}`,
|
|
514
|
+
kind: 'approval',
|
|
515
|
+
prompt: approvalPrompt,
|
|
516
|
+
deadline_ms: now + deadlineMs,
|
|
517
|
+
default_action: args.default_action || 'timeout_continue',
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
if (!hitlResult.ok) {
|
|
521
|
+
return hitlResult;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return {
|
|
525
|
+
ok: true,
|
|
526
|
+
data: {
|
|
527
|
+
pending: true,
|
|
528
|
+
request_id: hitlResult.data.request_id,
|
|
529
|
+
team_name: args.team_name,
|
|
530
|
+
target_phase: args.phase,
|
|
531
|
+
current_phase: pipeline.getState().phase,
|
|
532
|
+
deadline_ms: now + deadlineMs,
|
|
533
|
+
default_action: args.default_action || 'timeout_continue',
|
|
534
|
+
message: `승인 대기 중. ID: ${hitlResult.data.request_id}. ${Math.round(deadlineMs / 1000)}초 후 ${args.default_action || 'timeout_continue'} 자동 실행.`,
|
|
535
|
+
},
|
|
536
|
+
};
|
|
537
|
+
}),
|
|
538
|
+
},
|
|
539
|
+
|
|
540
|
+
// ── 20. pipeline_list ──
|
|
476
541
|
{
|
|
477
542
|
name: 'pipeline_list',
|
|
478
543
|
description: '활성 파이프라인 목록을 조회합니다',
|
package/hud/hud-qos-status.mjs
CHANGED
|
@@ -1046,15 +1046,15 @@ function expireStaleCodexBuckets(buckets) {
|
|
|
1046
1046
|
// ============================================================================
|
|
1047
1047
|
// Codex 세션 JSONL에서 실제 rate limits 추출
|
|
1048
1048
|
// 한계: rate_limits는 세션별 스냅샷이므로 여러 세션 간 토큰 합산은 불가.
|
|
1049
|
-
//
|
|
1050
|
-
// (
|
|
1049
|
+
// 최근 7일간 세션 파일을 스캔해 가장 최신 rate_limits 버킷을 수집한다.
|
|
1050
|
+
// 합성 버킷(token_count 기반)은 2일 이내 데이터만 허용하여 stale 방지.
|
|
1051
1051
|
// ============================================================================
|
|
1052
1052
|
function getCodexRateLimits() {
|
|
1053
1053
|
const now = new Date();
|
|
1054
|
-
let syntheticBucket = null; //
|
|
1054
|
+
let syntheticBucket = null; // 최근 token_count에서 합성 (행 활성화 + 토큰 데이터용)
|
|
1055
1055
|
|
|
1056
|
-
//
|
|
1057
|
-
for (let dayOffset = 0; dayOffset <=
|
|
1056
|
+
// 7일간 스캔: 실제 rate_limits 우선, 합성 버킷은 폴백
|
|
1057
|
+
for (let dayOffset = 0; dayOffset <= 6; dayOffset++) {
|
|
1058
1058
|
const d = new Date(now.getTime() - dayOffset * 86_400_000);
|
|
1059
1059
|
const sessDir = join(
|
|
1060
1060
|
homedir(), ".codex", "sessions",
|
|
@@ -1086,8 +1086,8 @@ function getCodexRateLimits() {
|
|
|
1086
1086
|
contextWindow: evt.payload?.info?.model_context_window,
|
|
1087
1087
|
timestamp: evt.timestamp,
|
|
1088
1088
|
};
|
|
1089
|
-
} else if (dayOffset
|
|
1090
|
-
//
|
|
1089
|
+
} else if (dayOffset <= 1 && !rl && evt?.payload?.info?.total_token_usage && !syntheticBucket) {
|
|
1090
|
+
// 2일 이내 token_count: 합성 버킷 (rate_limits가 null일 때 행 활성화용, stale 방지)
|
|
1091
1091
|
syntheticBucket = {
|
|
1092
1092
|
limitId: "codex", limitName: "codex-session",
|
|
1093
1093
|
primary: null, secondary: null,
|
|
@@ -1102,7 +1102,7 @@ function getCodexRateLimits() {
|
|
|
1102
1102
|
}
|
|
1103
1103
|
} catch { /* 파일 읽기 실패 무시 */ }
|
|
1104
1104
|
}
|
|
1105
|
-
// 실제 rate_limits 발견 →
|
|
1105
|
+
// 실제 rate_limits 발견 → 토큰 데이터 병합 후 즉시 반환
|
|
1106
1106
|
if (Object.keys(mergedBuckets).length > 0) {
|
|
1107
1107
|
if (syntheticBucket) {
|
|
1108
1108
|
const main = mergedBuckets.codex || mergedBuckets[Object.keys(mergedBuckets)[0]];
|
package/package.json
CHANGED
|
@@ -127,6 +127,31 @@ const PROFILE_DEFINITIONS = Object.freeze({
|
|
|
127
127
|
}),
|
|
128
128
|
});
|
|
129
129
|
|
|
130
|
+
/**
|
|
131
|
+
* 파이프라인 단계별 MCP 서버/도구 제한 (post-filter).
|
|
132
|
+
* role-based 프로필 위에 추가 적용. 빈 배열 = 전체 차단, 미정의 = 제한 없음.
|
|
133
|
+
*/
|
|
134
|
+
export const PHASE_OVERRIDES = Object.freeze({
|
|
135
|
+
plan: Object.freeze({
|
|
136
|
+
description: '계획 단계: 읽기 전용 탐색만 허용',
|
|
137
|
+
allowedServers: Object.freeze(['context7']),
|
|
138
|
+
blockedServers: Object.freeze(['playwright', 'tavily', 'exa']),
|
|
139
|
+
}),
|
|
140
|
+
prd: Object.freeze({
|
|
141
|
+
description: 'PRD 단계: 읽기 전용 탐색 + 문서 조회',
|
|
142
|
+
allowedServers: Object.freeze(['context7', 'brave-search']),
|
|
143
|
+
blockedServers: Object.freeze(['playwright']),
|
|
144
|
+
}),
|
|
145
|
+
exec: Object.freeze({
|
|
146
|
+
description: '실행 단계: 프로필 기반 전체 허용 (제한 없음)',
|
|
147
|
+
}),
|
|
148
|
+
verify: Object.freeze({
|
|
149
|
+
description: '검증 단계: 읽기 전용 + 분석 도구',
|
|
150
|
+
allowedServers: Object.freeze(['context7', 'brave-search', 'exa']),
|
|
151
|
+
blockedServers: Object.freeze(['playwright']),
|
|
152
|
+
}),
|
|
153
|
+
});
|
|
154
|
+
|
|
130
155
|
export const LEGACY_PROFILE_ALIASES = Object.freeze({
|
|
131
156
|
implement: 'executor',
|
|
132
157
|
analyze: 'analyze',
|
|
@@ -553,13 +578,23 @@ export function buildMcpPolicy(options = {}) {
|
|
|
553
578
|
const inventoryIndex = buildInventoryIndex(inventory);
|
|
554
579
|
const resolvedOptions = { ...options, inventory, inventoryIndex };
|
|
555
580
|
const resolvedProfile = resolveMcpProfile(options.agentType, options.requestedProfile);
|
|
556
|
-
|
|
581
|
+
let allowedServers = resolveAllowedServers(resolvedOptions);
|
|
557
582
|
const hint = buildPromptHint(resolvedOptions);
|
|
583
|
+
|
|
584
|
+
// Phase-aware post-filter: 파이프라인 단계별 서버 제한 적용
|
|
585
|
+
const phase = options.phase;
|
|
586
|
+
const phaseOverride = phase && PHASE_OVERRIDES[phase];
|
|
587
|
+
if (phaseOverride && phaseOverride.blockedServers) {
|
|
588
|
+
const blocked = new Set(phaseOverride.blockedServers);
|
|
589
|
+
allowedServers = allowedServers.filter((s) => !blocked.has(s));
|
|
590
|
+
}
|
|
591
|
+
|
|
558
592
|
return {
|
|
559
593
|
requestedProfile: typeof options.requestedProfile === 'string' && options.requestedProfile
|
|
560
594
|
? options.requestedProfile
|
|
561
595
|
: 'auto',
|
|
562
596
|
resolvedProfile,
|
|
597
|
+
resolvedPhase: phase || null,
|
|
563
598
|
allowedServers,
|
|
564
599
|
hint,
|
|
565
600
|
geminiAllowedServers: getGeminiAllowedServers(resolvedOptions),
|
|
@@ -577,14 +612,18 @@ function shellArray(name, values) {
|
|
|
577
612
|
}
|
|
578
613
|
|
|
579
614
|
export function toShellExports(policy) {
|
|
580
|
-
|
|
615
|
+
const lines = [
|
|
581
616
|
`MCP_PROFILE_REQUESTED=${shellEscape(policy.requestedProfile)}`,
|
|
582
617
|
`MCP_RESOLVED_PROFILE=${shellEscape(policy.resolvedProfile)}`,
|
|
583
618
|
`MCP_HINT=${shellEscape(policy.hint)}`,
|
|
584
619
|
shellArray('GEMINI_ALLOWED_SERVERS', policy.geminiAllowedServers),
|
|
585
620
|
shellArray('CODEX_CONFIG_FLAGS', policy.codexConfigOverrides.flatMap((override) => ['-c', override])),
|
|
586
621
|
`CODEX_CONFIG_JSON=${shellEscape(JSON.stringify(policy.codexConfig))}`,
|
|
587
|
-
]
|
|
622
|
+
];
|
|
623
|
+
if (policy.resolvedPhase) {
|
|
624
|
+
lines.push(`MCP_PIPELINE_PHASE=${shellEscape(policy.resolvedPhase)}`);
|
|
625
|
+
}
|
|
626
|
+
return lines.join('\n');
|
|
588
627
|
}
|
|
589
628
|
|
|
590
629
|
function parseCliArgs(argv) {
|
|
@@ -636,6 +675,9 @@ function parseCliArgs(argv) {
|
|
|
636
675
|
case '--worker-index':
|
|
637
676
|
args.workerIndex = Number.parseInt(next(), 10);
|
|
638
677
|
break;
|
|
678
|
+
case '--phase':
|
|
679
|
+
args.phase = next();
|
|
680
|
+
break;
|
|
639
681
|
default:
|
|
640
682
|
throw new Error(`알 수 없는 옵션: ${token}`);
|
|
641
683
|
}
|
package/skills/tfx-auto/SKILL.md
CHANGED
|
@@ -32,14 +32,20 @@ argument-hint: "<command|task> [args...]"
|
|
|
32
32
|
> 2. **비용**: Codex 우선 → Gemini → Claude 최후 수단. `claude` 선택 전 "Codex로 가능한가?" 재확인.
|
|
33
33
|
> 3. **DAG**: SEQUENTIAL/DAG이면 레벨 기반 순차 실행. `.omc/context/{sid}/` 생성, context_output 저장, 실패 시 후속 SKIP.
|
|
34
34
|
> 4. **트리아지**: Codex `--full-auto` 분류 + Opus 인라인 분해. Agent 스폰 금지.
|
|
35
|
+
> 5. **thorough**: `-t`/`--thorough` 시 파이프라인 init 필수. 커맨드 숏컷은 항상 quick.
|
|
35
36
|
|
|
36
37
|
## 모드
|
|
37
38
|
|
|
38
39
|
| 입력 형식 | 모드 | 트리아지 |
|
|
39
40
|
|-----------|------|----------|
|
|
40
|
-
| `/implement JWT 추가` | 커맨드 숏컷 | 없음 (즉시 실행) |
|
|
41
|
-
| `/tfx-auto "리팩터링 + UI"` | 자동 | Codex 분류 → Opus 분해 |
|
|
42
|
-
| `/tfx-auto
|
|
41
|
+
| `/implement JWT 추가` | 커맨드 숏컷 (quick) | 없음 (즉시 실행) |
|
|
42
|
+
| `/tfx-auto "리팩터링 + UI"` | 자동 (quick) | Codex 분류 → Opus 분해 |
|
|
43
|
+
| `/tfx-auto -t "리팩터링 + UI"` | 자동 (thorough) | Codex 분류 → Opus 분해 → Pipeline |
|
|
44
|
+
| `/tfx-auto --thorough "리팩터링"` | 자동 (thorough) | `-t` 동일 |
|
|
45
|
+
| `/tfx-auto 3:codex "리뷰"` | 수동 (quick) | Opus 분해만 |
|
|
46
|
+
|
|
47
|
+
> **tfx-auto는 `--quick`이 기본.** 커맨드 숏컷·단일 실행에서 plan/verify 오버헤드가 불필요하기 때문.
|
|
48
|
+
> 멀티 태스크 시 tfx-multi로 전환되면 tfx-multi의 기본값(`--thorough`)이 적용된다.
|
|
43
49
|
|
|
44
50
|
## 커맨드 숏컷
|
|
45
51
|
|
|
@@ -100,25 +106,77 @@ argument-hint: "<command|task> [args...]"
|
|
|
100
106
|
|
|
101
107
|
**수동 모드 (`N:agent_type`):** Codex 분류 건너뜀 → Opus가 N개 서브태스크 분해. N > 10 거부.
|
|
102
108
|
|
|
109
|
+
## --thorough 모드
|
|
110
|
+
|
|
111
|
+
`-t` 또는 `--thorough` 플래그 시 파이프라인 기반 실행. 커맨드 숏컷에서는 무시된다.
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
분기점은 "실행 전략"이지 "계획"이 아님:
|
|
115
|
+
|
|
116
|
+
TRIAGE
|
|
117
|
+
│
|
|
118
|
+
├─ [thorough] → PIPELINE INIT(plan) → PLAN → PRD → [APPROVAL]
|
|
119
|
+
│ │
|
|
120
|
+
│ ┌───────────────┤
|
|
121
|
+
│ │ │
|
|
122
|
+
│ [1 task] [2+ tasks]
|
|
123
|
+
│ │ │
|
|
124
|
+
│ AUTO 직접 실행 TEAM EXEC (multi Phase 3)
|
|
125
|
+
│ │ │
|
|
126
|
+
│ └───────┬───────┘
|
|
127
|
+
│ │
|
|
128
|
+
│ VERIFY → FIX loop → COMPLETE
|
|
129
|
+
│
|
|
130
|
+
└─ [quick] → [1 task] → fire-and-forget
|
|
131
|
+
[2+ tasks] → TEAM EXEC → COLLECT → CLEANUP
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 단일 태스크 thorough
|
|
135
|
+
|
|
136
|
+
1. `Bash("node hub/bridge.mjs pipeline-init --team ${sid}")` — 파이프라인 초기화 (phase: plan)
|
|
137
|
+
2. Plan: Codex architect → 결과를 `pipeline.writePlanFile()` 저장
|
|
138
|
+
3. PRD: Codex analyst → acceptance criteria 확정
|
|
139
|
+
4. `pipeline_advance_gated` → [Approval Gate] → 사용자 승인 대기
|
|
140
|
+
5. Exec: tfx-auto 직접 실행 (아래 "실행" 섹션)
|
|
141
|
+
6. Verify: Codex verifier → 검증
|
|
142
|
+
7. 실패 시 Fix loop (최대 3회) → Exec 재실행
|
|
143
|
+
8. Complete
|
|
144
|
+
|
|
145
|
+
### 멀티 태스크 thorough
|
|
146
|
+
|
|
147
|
+
Plan/PRD/Approval은 tfx-auto에서 실행, 그 후 tfx-multi Phase 3로 전환.
|
|
148
|
+
서브태스크 배열 + `thorough: true` 신호를 함께 전달하여 multi 측에서 verify/fix를 수행.
|
|
149
|
+
|
|
103
150
|
## 멀티 태스크 라우팅 (트리아지 후)
|
|
104
151
|
|
|
105
|
-
> **트리아지
|
|
152
|
+
> **트리아지 결과에 따라 실행 경로 결정.**
|
|
106
153
|
|
|
107
|
-
|
|
|
108
|
-
|
|
109
|
-
| 1개 | tfx-auto 직접 실행 (
|
|
110
|
-
|
|
|
154
|
+
| 조건 | 실행 경로 | 이유 |
|
|
155
|
+
|------|----------|------|
|
|
156
|
+
| 1개 + quick | tfx-auto 직접 실행 (fire-and-forget) | 팀 오버헤드 불필요 |
|
|
157
|
+
| 1개 + thorough | tfx-auto 직접 실행 + verify/fix loop | plan→exec→verify 단일 경로 |
|
|
158
|
+
| 2개+ + quick | tfx-multi Phase 3 (TeamCreate 직행) | Shift+Down, 상태 추적 |
|
|
159
|
+
| 2개+ + thorough | Plan/PRD/Approval 후 → tfx-multi Phase 3 + verify/fix | 전체 파이프라인 |
|
|
111
160
|
|
|
112
|
-
**전환 방법:** 트리아지 완료 후 서브태스크
|
|
113
|
-
tfx-multi의 Phase 2(트리아지)는
|
|
161
|
+
**전환 방법:** 트리아지 완료 후 서브태스크 배열 + thorough 플래그를 tfx-multi Phase 3에 전달.
|
|
162
|
+
tfx-multi의 Phase 2(트리아지)는 건너뛰고, thorough 시 Phase 2.5-2.6도 건너뛴다 (auto에서 이미 수행).
|
|
114
163
|
|
|
115
164
|
```
|
|
165
|
+
thorough = args에 -t 또는 --thorough 포함
|
|
166
|
+
|
|
116
167
|
if subtasks.length >= 2:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
168
|
+
if thorough:
|
|
169
|
+
→ Pipeline init → Plan → PRD → Approval (tfx-auto에서 수행)
|
|
170
|
+
→ tfx-multi Phase 3 실행 ({ subtasks, thorough: true })
|
|
171
|
+
→ Phase 3.5 verify → Phase 3.6 fix loop → Phase 5 정리
|
|
172
|
+
else:
|
|
173
|
+
→ tfx-multi Phase 3 실행 ({ subtasks, thorough: false })
|
|
174
|
+
→ Phase 4 결과 수집 → Phase 5 정리
|
|
120
175
|
else:
|
|
121
|
-
|
|
176
|
+
if thorough:
|
|
177
|
+
→ Pipeline init → Plan → PRD → Approval → 직접 실행 → Verify → Fix loop
|
|
178
|
+
else:
|
|
179
|
+
→ tfx-auto 직접 실행 (아래)
|
|
122
180
|
```
|
|
123
181
|
|
|
124
182
|
## 실행
|
|
@@ -11,7 +11,7 @@ argument-hint: '"작업 설명" | --agents codex,gemini "작업" | --tmux "작
|
|
|
11
11
|
> Claude Code Native Teams의 Shift+Down 네비게이션을 복원한다.
|
|
12
12
|
> Codex/Gemini 워커마다 최소 프롬프트(~100 토큰)의 슬림 Agent 래퍼를 spawn하여 네비게이션에 등록하고,
|
|
13
13
|
> 실제 작업은 `tfx-route.sh`가 수행한다. task 상태는 `team_task_list`를 truth source로 검증한다.
|
|
14
|
-
> v3 — `--
|
|
14
|
+
> v3 — `--thorough`(기본: plan→prd→exec→verify→fix loop) + `--quick`(경량 모드).
|
|
15
15
|
|
|
16
16
|
> **Lead 고토큰 MCP 직접 사용 금지**
|
|
17
17
|
> Lead(Claude Opus)는 웹 서치(brave-search, exa, tavily), 외부 서비스(Notion, Jira/Confluence, Calendar, Gmail),
|
|
@@ -21,8 +21,8 @@ argument-hint: '"작업 설명" | --agents codex,gemini "작업" | --tmux "작
|
|
|
21
21
|
## 사용법
|
|
22
22
|
|
|
23
23
|
```
|
|
24
|
-
/tfx-multi "인증 리팩터링 + UI 개선 + 보안 리뷰" # --
|
|
25
|
-
/tfx-multi --
|
|
24
|
+
/tfx-multi "인증 리팩터링 + UI 개선 + 보안 리뷰" # --thorough (기본)
|
|
25
|
+
/tfx-multi --quick "인증 리팩터링 + UI 개선 + 보안 리뷰" # 경량 모드 (plan/verify 생략)
|
|
26
26
|
/tfx-multi --agents codex,gemini "프론트+백엔드"
|
|
27
27
|
/tfx-multi --tmux "작업" # 레거시 tmux 모드
|
|
28
28
|
/tfx-multi status
|
|
@@ -48,11 +48,17 @@ preflight와 Agent 생성을 병렬로 실행하여 사용자 체감 지연을
|
|
|
48
48
|
```
|
|
49
49
|
""(빈 문자열) → 사용자에게 작업 입력 요청
|
|
50
50
|
"3:codex 리뷰" → 수동 모드: N=3, agent=codex
|
|
51
|
-
"인증 + UI + 테스트" → 자동
|
|
51
|
+
"인증 + UI + 테스트" → 자동 모드 (--thorough 기본): Codex 분류 → Opus 분해 → Pipeline
|
|
52
|
+
"--quick 인증 + UI" → 경량 모드: plan/verify 생략, 즉시 실행
|
|
52
53
|
"--tmux 인증 + UI" → Phase 3-mux 분기
|
|
53
54
|
"status" / "stop" → Bash("node bin/triflux.mjs multi {cmd}") 직행
|
|
54
55
|
```
|
|
55
56
|
|
|
57
|
+
**모드 결정:**
|
|
58
|
+
- `--quick` 명시 → quick 모드 (Phase 2.5-2.6, 3.5-3.7 생략)
|
|
59
|
+
- `--thorough` 명시 또는 플래그 없음 → thorough 모드 (기본, 전체 파이프라인)
|
|
60
|
+
- 커맨드 숏컷 (tfx-auto 경유 단일 실행) → quick 유지 (오버헤드 불필요)
|
|
61
|
+
|
|
56
62
|
### Phase 2: 트리아지 (tfx-auto와 동일)
|
|
57
63
|
|
|
58
64
|
**자동 모드:**
|
|
@@ -62,9 +68,9 @@ preflight와 Agent 생성을 병렬로 실행하여 사용자 체감 지연을
|
|
|
62
68
|
|
|
63
69
|
**수동 모드:** Codex 분류 건너뜀 → Opus가 직접 N개 서브태스크 분해.
|
|
64
70
|
|
|
65
|
-
### Phase 2.5–2.6 + 3.5–3.7:
|
|
71
|
+
### Phase 2.5–2.6 + 3.5–3.7: 파이프라인 (기본)
|
|
66
72
|
|
|
67
|
-
> `--
|
|
73
|
+
> `--thorough`(기본) 모드에서 실행된다. `--quick` 플래그 시 건너뛴다.
|
|
68
74
|
> 상세는 → [`references/thorough-pipeline.md`](references/thorough-pipeline.md) 참조.
|
|
69
75
|
|
|
70
76
|
### Phase 3: Native Teams 실행
|
|
@@ -1,24 +1,63 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 파이프라인 상세 (thorough 기본)
|
|
2
2
|
|
|
3
|
-
> `--
|
|
4
|
-
> `--
|
|
3
|
+
> `--thorough`(기본) 모드에서 Phase 2.5-2.6과 Phase 3.5-3.7이 실행된다.
|
|
4
|
+
> `--quick` 플래그 시 이 파일의 내용은 적용되지 않는다.
|
|
5
|
+
> tfx-auto 경유 시: `-t`/`--thorough` 플래그가 있을 때만 파이프라인이 활성화된다.
|
|
5
6
|
|
|
6
|
-
## Phase 2.5: Plan (Codex
|
|
7
|
+
## Phase 2.5: Plan (Opus Lead + Codex Scout 협업)
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
9
|
+
> 기존: Codex architect(단독, one-shot)
|
|
10
|
+
> 변경: Opus 설계 주도 + Codex scout(병렬 탐색) → 합산 판단 → 최종 계획
|
|
11
|
+
|
|
12
|
+
### Step 2.5.1: Opus 과제 분석 → 탐색 목록 생성
|
|
13
|
+
|
|
14
|
+
Lead(Opus)가 과제를 분석하고, 설계에 필요한 정보 탐색 목록을 인라인 생성한다:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"questions": [
|
|
19
|
+
{ "id": "q1", "question": "현재 인증 미들웨어 구조와 세션 관리 방식", "scope": "src/middleware/auth*" },
|
|
20
|
+
{ "id": "q2", "question": "DB 스키마와 마이그레이션 현황", "scope": "db/migrations/" }
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
탐색 목록은 3-7개 항목 권장. 과도하면 Codex 비용이 아닌 Lead 컨텍스트가 팽창.
|
|
26
|
+
|
|
27
|
+
### Step 2.5.2: Codex Scout 병렬 파견
|
|
28
|
+
|
|
29
|
+
각 탐색 항목마다 Codex scout를 파견하여 코드베이스 탐색:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# 각 question마다 병렬 실행
|
|
33
|
+
for each question:
|
|
34
|
+
bash ~/.claude/scripts/tfx-route.sh scientist "${question.question}. 탐색 범위: ${question.scope}" analyze
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**scout 실행 규칙:**
|
|
38
|
+
- scout는 **read-only** — 코드 수정 금지, 탐색+보고만
|
|
39
|
+
- 병렬 실행 (run_in_background=true)
|
|
40
|
+
- scope 힌트로 탐색 범위를 제한하여 정확도 향상
|
|
41
|
+
- MCP 프로필: `analyze` (읽기 전용 도구만)
|
|
42
|
+
- 팀 모드 시: slim wrapper Agent로 spawn (Shift+Down 네비게이션)
|
|
43
|
+
- 단일 모드 시: tfx-route.sh 직접 호출
|
|
44
|
+
|
|
45
|
+
### Step 2.5.3: Opus 종합 판단 → 최종 계획 작성
|
|
46
|
+
|
|
47
|
+
Lead(Opus)가 scout 보고를 종합하고, 전략적 설계 결정을 내린다:
|
|
48
|
+
|
|
49
|
+
1. scout 결과 수집 (모든 scout 완료 대기)
|
|
50
|
+
2. 아키텍처 선택, 트레이드오프 판단, 리스크 평가
|
|
51
|
+
3. 최종 계획 작성
|
|
52
|
+
4. `pipeline.writePlanFile(planContent)` 저장
|
|
53
|
+
5. pipeline advance: plan → prd
|
|
54
|
+
|
|
55
|
+
### Step 2.5.4: 추가 질의 루프 (선택)
|
|
56
|
+
|
|
57
|
+
계획 작성 중 추가 정보 필요 시:
|
|
58
|
+
- 팀 모드: 피드백 루프(Phase 0 구현)를 활용하여 scout에 "재실행:" 메시지 전송
|
|
59
|
+
- 단일 모드: 추가 tfx-route.sh 호출
|
|
60
|
+
- maxIterations 내에서 반복 가능 (기본 2회)
|
|
22
61
|
|
|
23
62
|
## Phase 2.6: PRD (Codex analyst)
|
|
24
63
|
|