triflux 3.2.0-dev.8 → 3.3.0-dev.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/bin/triflux.mjs +1296 -1055
- package/hooks/hooks.json +17 -0
- package/hooks/keyword-rules.json +20 -4
- package/hooks/pipeline-stop.mjs +54 -0
- package/hub/bridge.mjs +517 -318
- package/hub/hitl.mjs +45 -31
- package/hub/pipe.mjs +457 -0
- package/hub/pipeline/index.mjs +121 -0
- package/hub/pipeline/state.mjs +164 -0
- package/hub/pipeline/transitions.mjs +114 -0
- package/hub/router.mjs +422 -161
- package/hub/schema.sql +14 -0
- package/hub/server.mjs +499 -424
- package/hub/store.mjs +388 -314
- package/hub/team/cli-team-common.mjs +348 -0
- package/hub/team/cli-team-control.mjs +393 -0
- package/hub/team/cli-team-start.mjs +516 -0
- package/hub/team/cli-team-status.mjs +269 -0
- package/hub/team/cli.mjs +75 -1475
- package/hub/team/dashboard.mjs +1 -9
- package/hub/team/native.mjs +190 -130
- package/hub/team/nativeProxy.mjs +165 -78
- package/hub/team/orchestrator.mjs +15 -20
- package/hub/team/pane.mjs +137 -103
- package/hub/team/psmux.mjs +506 -0
- package/hub/team/session.mjs +393 -330
- package/hub/team/shared.mjs +13 -0
- package/hub/team/staleState.mjs +299 -0
- package/hub/tools.mjs +105 -31
- package/hub/workers/claude-worker.mjs +446 -0
- package/hub/workers/codex-mcp.mjs +414 -0
- package/hub/workers/factory.mjs +18 -0
- package/hub/workers/gemini-worker.mjs +349 -0
- package/hub/workers/interface.mjs +41 -0
- package/hud/hud-qos-status.mjs +1790 -1788
- package/package.json +4 -1
- package/scripts/__tests__/keyword-detector.test.mjs +8 -8
- package/scripts/keyword-detector.mjs +15 -0
- package/scripts/lib/keyword-rules.mjs +4 -1
- package/scripts/preflight-cache.mjs +72 -0
- package/scripts/psmux-steering-prototype.sh +368 -0
- package/scripts/setup.mjs +136 -71
- package/scripts/tfx-route-worker.mjs +161 -0
- package/scripts/tfx-route.sh +485 -91
- package/skills/tfx-auto/SKILL.md +90 -564
- package/skills/tfx-auto-codex/SKILL.md +1 -3
- package/skills/tfx-codex/SKILL.md +1 -4
- package/skills/tfx-doctor/SKILL.md +1 -0
- package/skills/tfx-gemini/SKILL.md +1 -4
- package/skills/tfx-multi/SKILL.md +378 -0
- package/skills/tfx-setup/SKILL.md +1 -4
- package/skills/tfx-team/SKILL.md +0 -304
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// hub/pipeline/state.mjs — Hub SQLite 파이프라인 상태 저장/로드
|
|
2
|
+
//
|
|
3
|
+
// store.mjs의 기존 SQLite 연결(db)을 활용한다.
|
|
4
|
+
// pipeline_state 테이블은 schema.sql에 정의.
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* pipeline_state 테이블 초기화 (store.db에 없으면 생성)
|
|
8
|
+
* @param {object} db - better-sqlite3 인스턴스
|
|
9
|
+
*/
|
|
10
|
+
export function ensurePipelineTable(db) {
|
|
11
|
+
db.exec(`
|
|
12
|
+
CREATE TABLE IF NOT EXISTS pipeline_state (
|
|
13
|
+
team_name TEXT PRIMARY KEY,
|
|
14
|
+
phase TEXT NOT NULL DEFAULT 'plan',
|
|
15
|
+
fix_attempt INTEGER DEFAULT 0,
|
|
16
|
+
fix_max INTEGER DEFAULT 3,
|
|
17
|
+
ralph_iteration INTEGER DEFAULT 0,
|
|
18
|
+
ralph_max INTEGER DEFAULT 10,
|
|
19
|
+
artifacts TEXT DEFAULT '{}',
|
|
20
|
+
phase_history TEXT DEFAULT '[]',
|
|
21
|
+
created_at INTEGER,
|
|
22
|
+
updated_at INTEGER
|
|
23
|
+
)
|
|
24
|
+
`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const STATEMENTS = new WeakMap();
|
|
28
|
+
|
|
29
|
+
function getStatements(db) {
|
|
30
|
+
let s = STATEMENTS.get(db);
|
|
31
|
+
if (s) return s;
|
|
32
|
+
|
|
33
|
+
s = {
|
|
34
|
+
get: db.prepare('SELECT * FROM pipeline_state WHERE team_name = ?'),
|
|
35
|
+
insert: db.prepare(`
|
|
36
|
+
INSERT INTO pipeline_state (team_name, phase, fix_attempt, fix_max, ralph_iteration, ralph_max, artifacts, phase_history, created_at, updated_at)
|
|
37
|
+
VALUES (@team_name, @phase, @fix_attempt, @fix_max, @ralph_iteration, @ralph_max, @artifacts, @phase_history, @created_at, @updated_at)
|
|
38
|
+
`),
|
|
39
|
+
update: db.prepare(`
|
|
40
|
+
UPDATE pipeline_state SET
|
|
41
|
+
phase = @phase,
|
|
42
|
+
fix_attempt = @fix_attempt,
|
|
43
|
+
fix_max = @fix_max,
|
|
44
|
+
ralph_iteration = @ralph_iteration,
|
|
45
|
+
ralph_max = @ralph_max,
|
|
46
|
+
artifacts = @artifacts,
|
|
47
|
+
phase_history = @phase_history,
|
|
48
|
+
updated_at = @updated_at
|
|
49
|
+
WHERE team_name = @team_name
|
|
50
|
+
`),
|
|
51
|
+
remove: db.prepare('DELETE FROM pipeline_state WHERE team_name = ?'),
|
|
52
|
+
list: db.prepare('SELECT * FROM pipeline_state ORDER BY updated_at DESC'),
|
|
53
|
+
};
|
|
54
|
+
STATEMENTS.set(db, s);
|
|
55
|
+
return s;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function parseRow(row) {
|
|
59
|
+
if (!row) return null;
|
|
60
|
+
return {
|
|
61
|
+
...row,
|
|
62
|
+
artifacts: JSON.parse(row.artifacts || '{}'),
|
|
63
|
+
phase_history: JSON.parse(row.phase_history || '[]'),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function serializeState(state) {
|
|
68
|
+
return {
|
|
69
|
+
team_name: state.team_name,
|
|
70
|
+
phase: state.phase || 'plan',
|
|
71
|
+
fix_attempt: state.fix_attempt ?? 0,
|
|
72
|
+
fix_max: state.fix_max ?? 3,
|
|
73
|
+
ralph_iteration: state.ralph_iteration ?? 0,
|
|
74
|
+
ralph_max: state.ralph_max ?? 10,
|
|
75
|
+
artifacts: JSON.stringify(state.artifacts || {}),
|
|
76
|
+
phase_history: JSON.stringify(state.phase_history || []),
|
|
77
|
+
created_at: state.created_at ?? Date.now(),
|
|
78
|
+
updated_at: state.updated_at ?? Date.now(),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 파이프라인 상태 초기화 (새 파이프라인)
|
|
84
|
+
* @param {object} db - better-sqlite3 인스턴스
|
|
85
|
+
* @param {string} teamName
|
|
86
|
+
* @param {object} opts - { fix_max?, ralph_max? }
|
|
87
|
+
* @returns {object} 초기 상태
|
|
88
|
+
*/
|
|
89
|
+
export function initPipelineState(db, teamName, opts = {}) {
|
|
90
|
+
const S = getStatements(db);
|
|
91
|
+
const now = Date.now();
|
|
92
|
+
const state = {
|
|
93
|
+
team_name: teamName,
|
|
94
|
+
phase: 'plan',
|
|
95
|
+
fix_attempt: 0,
|
|
96
|
+
fix_max: opts.fix_max ?? 3,
|
|
97
|
+
ralph_iteration: 0,
|
|
98
|
+
ralph_max: opts.ralph_max ?? 10,
|
|
99
|
+
artifacts: {},
|
|
100
|
+
phase_history: [],
|
|
101
|
+
created_at: now,
|
|
102
|
+
updated_at: now,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// 기존 상태가 있으면 삭제 후 재생성
|
|
106
|
+
S.remove.run(teamName);
|
|
107
|
+
S.insert.run(serializeState(state));
|
|
108
|
+
return state;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* 파이프라인 상태 조회
|
|
113
|
+
* @param {object} db - better-sqlite3 인스턴스
|
|
114
|
+
* @param {string} teamName
|
|
115
|
+
* @returns {object|null}
|
|
116
|
+
*/
|
|
117
|
+
export function readPipelineState(db, teamName) {
|
|
118
|
+
const S = getStatements(db);
|
|
119
|
+
return parseRow(S.get.get(teamName));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 파이프라인 상태 업데이트 (부분 패치)
|
|
124
|
+
* @param {object} db - better-sqlite3 인스턴스
|
|
125
|
+
* @param {string} teamName
|
|
126
|
+
* @param {object} patch - 업데이트할 필드
|
|
127
|
+
* @returns {object|null} 업데이트된 상태
|
|
128
|
+
*/
|
|
129
|
+
export function updatePipelineState(db, teamName, patch) {
|
|
130
|
+
const S = getStatements(db);
|
|
131
|
+
const current = parseRow(S.get.get(teamName));
|
|
132
|
+
if (!current) return null;
|
|
133
|
+
|
|
134
|
+
const merged = {
|
|
135
|
+
...current,
|
|
136
|
+
...patch,
|
|
137
|
+
team_name: teamName, // team_name 변경 불가
|
|
138
|
+
updated_at: Date.now(),
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
S.update.run(serializeState(merged));
|
|
142
|
+
return merged;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 파이프라인 상태 삭제
|
|
147
|
+
* @param {object} db - better-sqlite3 인스턴스
|
|
148
|
+
* @param {string} teamName
|
|
149
|
+
* @returns {boolean}
|
|
150
|
+
*/
|
|
151
|
+
export function removePipelineState(db, teamName) {
|
|
152
|
+
const S = getStatements(db);
|
|
153
|
+
return S.remove.run(teamName).changes > 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 활성 파이프라인 목록
|
|
158
|
+
* @param {object} db - better-sqlite3 인스턴스
|
|
159
|
+
* @returns {object[]}
|
|
160
|
+
*/
|
|
161
|
+
export function listPipelineStates(db) {
|
|
162
|
+
const S = getStatements(db);
|
|
163
|
+
return S.list.all().map(parseRow);
|
|
164
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// hub/pipeline/transitions.mjs — 파이프라인 단계 전이 규칙
|
|
2
|
+
//
|
|
3
|
+
// plan → prd → exec → verify → complete/fix
|
|
4
|
+
// fix → exec/verify/complete/failed
|
|
5
|
+
// complete, failed = 터미널 상태
|
|
6
|
+
|
|
7
|
+
export const PHASES = ['plan', 'prd', 'exec', 'verify', 'fix', 'complete', 'failed'];
|
|
8
|
+
|
|
9
|
+
export const TERMINAL = new Set(['complete', 'failed']);
|
|
10
|
+
|
|
11
|
+
export const ALLOWED = {
|
|
12
|
+
'plan': ['prd'],
|
|
13
|
+
'prd': ['exec'],
|
|
14
|
+
'exec': ['verify'],
|
|
15
|
+
'verify': ['fix', 'complete', 'failed'],
|
|
16
|
+
'fix': ['exec', 'verify', 'complete', 'failed'],
|
|
17
|
+
'complete': [],
|
|
18
|
+
'failed': [],
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 전이 가능 여부 확인
|
|
23
|
+
* @param {string} from - 현재 단계
|
|
24
|
+
* @param {string} to - 다음 단계
|
|
25
|
+
* @returns {boolean}
|
|
26
|
+
*/
|
|
27
|
+
export function canTransition(from, to) {
|
|
28
|
+
const targets = ALLOWED[from];
|
|
29
|
+
if (!targets) return false;
|
|
30
|
+
return targets.includes(to);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 상태 전이 실행 — fix loop 바운딩 포함
|
|
35
|
+
* @param {object} state - 파이프라인 상태 객체
|
|
36
|
+
* @param {string} nextPhase - 다음 단계
|
|
37
|
+
* @returns {{ ok: boolean, state?: object, error?: string }}
|
|
38
|
+
*/
|
|
39
|
+
export function transitionPhase(state, nextPhase) {
|
|
40
|
+
const current = state.phase;
|
|
41
|
+
|
|
42
|
+
if (!canTransition(current, nextPhase)) {
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
error: `전이 불가: ${current} → ${nextPhase}. 허용: [${(ALLOWED[current] || []).join(', ')}]`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const next = { ...state, phase: nextPhase, updated_at: Date.now() };
|
|
50
|
+
|
|
51
|
+
// fix 단계 진입 시 attempt 증가 + 바운딩
|
|
52
|
+
if (nextPhase === 'fix') {
|
|
53
|
+
next.fix_attempt = (state.fix_attempt || 0) + 1;
|
|
54
|
+
if (next.fix_attempt > (state.fix_max || 3)) {
|
|
55
|
+
return {
|
|
56
|
+
ok: false,
|
|
57
|
+
error: `fix loop 초과: ${next.fix_attempt}/${state.fix_max || 3}회. ralph loop로 승격 필요.`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// fix → exec 재진입 시 (fix 후 재실행)
|
|
63
|
+
if (current === 'fix' && nextPhase === 'exec') {
|
|
64
|
+
// fix_attempt 유지 (이미 fix 진입 시 증가됨)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// verify → fix → ... → verify 반복 후 fix_max 초과 시 ralph loop
|
|
68
|
+
if (nextPhase === 'failed' && current === 'fix') {
|
|
69
|
+
// ralph loop 반복 증가
|
|
70
|
+
next.ralph_iteration = (state.ralph_iteration || 0) + 1;
|
|
71
|
+
if (next.ralph_iteration > (state.ralph_max || 10)) {
|
|
72
|
+
// 최종 실패 — ralph loop도 초과
|
|
73
|
+
next.phase = 'failed';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// phase_history 기록
|
|
78
|
+
const history = Array.isArray(state.phase_history) ? [...state.phase_history] : [];
|
|
79
|
+
history.push({ from: current, to: nextPhase, at: Date.now() });
|
|
80
|
+
next.phase_history = history;
|
|
81
|
+
|
|
82
|
+
return { ok: true, state: next };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* ralph loop 재시작 전이
|
|
87
|
+
* fix_max 초과 시 plan으로 돌아가며 ralph_iteration 증가
|
|
88
|
+
* @param {object} state - 현재 상태
|
|
89
|
+
* @returns {{ ok: boolean, state?: object, error?: string }}
|
|
90
|
+
*/
|
|
91
|
+
export function ralphRestart(state) {
|
|
92
|
+
const iteration = (state.ralph_iteration || 0) + 1;
|
|
93
|
+
if (iteration > (state.ralph_max || 10)) {
|
|
94
|
+
return {
|
|
95
|
+
ok: false,
|
|
96
|
+
error: `ralph loop 초과: ${iteration}/${state.ralph_max || 10}회. 최종 실패.`,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const history = Array.isArray(state.phase_history) ? [...state.phase_history] : [];
|
|
101
|
+
history.push({ from: state.phase, to: 'plan', at: Date.now(), ralph_restart: true });
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
ok: true,
|
|
105
|
+
state: {
|
|
106
|
+
...state,
|
|
107
|
+
phase: 'plan',
|
|
108
|
+
fix_attempt: 0,
|
|
109
|
+
ralph_iteration: iteration,
|
|
110
|
+
phase_history: history,
|
|
111
|
+
updated_at: Date.now(),
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|