triflux 7.1.4 → 7.2.2
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/.claude-plugin/marketplace.json +31 -31
- package/.claude-plugin/plugin.json +22 -23
- package/bin/triflux.mjs +18 -5
- package/hooks/keyword-rules.json +393 -361
- package/hub/bridge.mjs +799 -786
- package/hub/delegator/contracts.mjs +37 -38
- package/hub/delegator/schema/delegator-tools.schema.json +250 -250
- package/hub/delegator/service.mjs +307 -302
- package/hub/intent.mjs +108 -11
- package/hub/lib/process-utils.mjs +20 -0
- package/hub/pipe.mjs +589 -589
- package/hub/pipeline/gates/confidence.mjs +1 -1
- package/hub/pipeline/gates/selfcheck.mjs +2 -4
- package/hub/pipeline/state.mjs +191 -187
- package/hub/pipeline/transitions.mjs +124 -120
- package/hub/public/dashboard.html +355 -349
- package/hub/quality/deslop.mjs +5 -3
- package/hub/reflexion.mjs +5 -1
- package/hub/research.mjs +6 -1
- package/hub/router.mjs +791 -782
- package/hub/server.mjs +893 -822
- package/hub/store.mjs +807 -778
- package/hub/team/agent-map.json +10 -0
- package/hub/team/ansi.mjs +3 -4
- package/hub/team/cli/commands/control.mjs +43 -43
- package/hub/team/cli/commands/interrupt.mjs +36 -36
- package/hub/team/cli/commands/kill.mjs +3 -3
- package/hub/team/cli/commands/send.mjs +37 -37
- package/hub/team/cli/commands/start/index.mjs +18 -8
- package/hub/team/cli/commands/start/parse-args.mjs +3 -1
- package/hub/team/cli/commands/start/start-headless.mjs +4 -1
- package/hub/team/cli/commands/status.mjs +87 -87
- package/hub/team/cli/commands/stop.mjs +1 -1
- package/hub/team/cli/commands/task.mjs +1 -1
- package/hub/team/cli/index.mjs +41 -39
- package/hub/team/cli/manifest.mjs +29 -28
- package/hub/team/cli/services/hub-client.mjs +37 -0
- package/hub/team/cli/services/state-store.mjs +26 -12
- package/hub/team/dashboard.mjs +11 -4
- package/hub/team/handoff.mjs +12 -0
- package/hub/team/headless.mjs +202 -200
- package/hub/team/native-supervisor.mjs +386 -346
- package/hub/team/nativeProxy.mjs +680 -692
- package/hub/team/staleState.mjs +361 -369
- package/hub/team/tui-viewer.mjs +27 -3
- package/hub/team/tui.mjs +1 -0
- package/hub/token-mode.mjs +114 -24
- package/hub/workers/delegator-mcp.mjs +1059 -1057
- package/hud/colors.mjs +88 -0
- package/hud/constants.mjs +78 -0
- package/hud/hud-qos-status.mjs +206 -1872
- package/hud/providers/claude.mjs +309 -0
- package/hud/providers/codex.mjs +151 -0
- package/hud/providers/gemini.mjs +320 -0
- package/hud/renderers.mjs +424 -0
- package/hud/terminal.mjs +140 -0
- package/hud/utils.mjs +271 -0
- package/package.json +1 -2
- package/scripts/__tests__/keyword-detector.test.mjs +234 -234
- package/scripts/headless-guard-fast.sh +21 -0
- package/scripts/headless-guard.mjs +26 -6
- package/scripts/lib/keyword-rules.mjs +166 -168
- package/scripts/setup.mjs +725 -690
- package/scripts/tfx-route-post.mjs +424 -424
- package/scripts/tfx-route.sh +1671 -1650
- package/scripts/tmp-cleanup.mjs +74 -0
- package/skills/tfx-auto/SKILL.md +279 -278
- package/skills/tfx-auto-codex/SKILL.md +98 -77
- package/skills/tfx-codex/SKILL.md +65 -65
- package/skills/tfx-gemini/SKILL.md +83 -82
- package/skills/tfx-hub/SKILL.md +205 -136
- package/skills/tfx-multi/SKILL.md +11 -5
- package/.mcp.json +0 -8
|
@@ -1,168 +1,166 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
|
|
3
|
-
const VALID_MCP_ROUTES = new Set(["codex", "gemini", "claude"]);
|
|
4
|
-
|
|
5
|
-
function logRuleError(message, error) {
|
|
6
|
-
if (error) {
|
|
7
|
-
console.error(`[triflux-keyword-rules] ${message}: ${error.message}`);
|
|
8
|
-
return;
|
|
9
|
-
}
|
|
10
|
-
console.error(`[triflux-keyword-rules] ${message}`);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function normalizePattern(pattern) {
|
|
14
|
-
if (!pattern || typeof pattern.source !== "string") return null;
|
|
15
|
-
if (typeof pattern.flags !== "string") return null;
|
|
16
|
-
return { source: pattern.source, flags: pattern.flags };
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function normalizeState(state) {
|
|
20
|
-
if (state == null) return null;
|
|
21
|
-
if (typeof state !== "object") return null;
|
|
22
|
-
if (typeof state.activate !== "boolean") return null;
|
|
23
|
-
if (typeof state.name !== "string" || !state.name.trim()) return null;
|
|
24
|
-
return { activate: state.activate, name: state.name.trim() };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function normalizeRule(rule) {
|
|
28
|
-
if (!rule || typeof rule !== "object") return null;
|
|
29
|
-
if (typeof rule.id !== "string" || !rule.id.trim()) return null;
|
|
30
|
-
if (!Array.isArray(rule.patterns) || rule.patterns.length === 0) return null;
|
|
31
|
-
if (typeof rule.priority !== "number" || !Number.isFinite(rule.priority)) return null;
|
|
32
|
-
|
|
33
|
-
const patterns = rule.patterns.map(normalizePattern).filter(Boolean);
|
|
34
|
-
if (patterns.length === 0) return null;
|
|
35
|
-
|
|
36
|
-
const skill = typeof rule.skill === "string" && rule.skill.trim() ? rule.skill.trim() : null;
|
|
37
|
-
const action = typeof rule.action === "string" && rule.action.trim() ? rule.action.trim() : null;
|
|
38
|
-
const mcpRoute = typeof rule.mcp_route === "string" && VALID_MCP_ROUTES.has(rule.mcp_route)
|
|
39
|
-
? rule.mcp_route
|
|
40
|
-
: null;
|
|
41
|
-
|
|
42
|
-
if (!skill && !mcpRoute && !action) return null;
|
|
43
|
-
|
|
44
|
-
const supersedes = Array.isArray(rule.supersedes)
|
|
45
|
-
? rule.supersedes.filter((id) => typeof id === "string" && id.trim()).map((id) => id.trim())
|
|
46
|
-
: [];
|
|
47
|
-
|
|
48
|
-
const state = normalizeState(rule.state);
|
|
49
|
-
if (rule.state != null && state == null) return null;
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
id: rule.id.trim(),
|
|
53
|
-
patterns,
|
|
54
|
-
skill,
|
|
55
|
-
action
|
|
56
|
-
priority: rule.priority,
|
|
57
|
-
supersedes,
|
|
58
|
-
exclusive: rule.exclusive === true,
|
|
59
|
-
state,
|
|
60
|
-
mcp_route: mcpRoute
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// 외부 JSON 규칙 로드 + 스키마 검증
|
|
65
|
-
export function loadRules(rulesPath) {
|
|
66
|
-
try {
|
|
67
|
-
const raw = readFileSync(rulesPath, "utf8");
|
|
68
|
-
const parsed = JSON.parse(raw);
|
|
69
|
-
if (!parsed || !Array.isArray(parsed.rules)) {
|
|
70
|
-
logRuleError(`규칙 형식이 올바르지 않습니다: ${rulesPath}`);
|
|
71
|
-
return [];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const normalized = parsed.rules.map(normalizeRule).filter(Boolean);
|
|
75
|
-
return normalized;
|
|
76
|
-
} catch (error) {
|
|
77
|
-
logRuleError(`규칙 파일을 읽을 수 없습니다: ${rulesPath}`, error);
|
|
78
|
-
return [];
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// pattern.source / flags를 RegExp로 컴파일
|
|
83
|
-
export function compileRules(rules) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
...rule,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
}
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
const VALID_MCP_ROUTES = new Set(["codex", "gemini", "claude"]);
|
|
4
|
+
|
|
5
|
+
function logRuleError(message, error) {
|
|
6
|
+
if (error) {
|
|
7
|
+
console.error(`[triflux-keyword-rules] ${message}: ${error.message}`);
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
console.error(`[triflux-keyword-rules] ${message}`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normalizePattern(pattern) {
|
|
14
|
+
if (!pattern || typeof pattern.source !== "string") return null;
|
|
15
|
+
if (typeof pattern.flags !== "string") return null;
|
|
16
|
+
return { source: pattern.source, flags: pattern.flags };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function normalizeState(state) {
|
|
20
|
+
if (state == null) return null;
|
|
21
|
+
if (typeof state !== "object") return null;
|
|
22
|
+
if (typeof state.activate !== "boolean") return null;
|
|
23
|
+
if (typeof state.name !== "string" || !state.name.trim()) return null;
|
|
24
|
+
return { activate: state.activate, name: state.name.trim() };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function normalizeRule(rule) {
|
|
28
|
+
if (!rule || typeof rule !== "object") return null;
|
|
29
|
+
if (typeof rule.id !== "string" || !rule.id.trim()) return null;
|
|
30
|
+
if (!Array.isArray(rule.patterns) || rule.patterns.length === 0) return null;
|
|
31
|
+
if (typeof rule.priority !== "number" || !Number.isFinite(rule.priority)) return null;
|
|
32
|
+
|
|
33
|
+
const patterns = rule.patterns.map(normalizePattern).filter(Boolean);
|
|
34
|
+
if (patterns.length === 0) return null;
|
|
35
|
+
|
|
36
|
+
const skill = typeof rule.skill === "string" && rule.skill.trim() ? rule.skill.trim() : null;
|
|
37
|
+
const action = typeof rule.action === "string" && rule.action.trim() ? rule.action.trim() : null;
|
|
38
|
+
const mcpRoute = typeof rule.mcp_route === "string" && VALID_MCP_ROUTES.has(rule.mcp_route)
|
|
39
|
+
? rule.mcp_route
|
|
40
|
+
: null;
|
|
41
|
+
|
|
42
|
+
if (!skill && !mcpRoute && !action) return null;
|
|
43
|
+
|
|
44
|
+
const supersedes = Array.isArray(rule.supersedes)
|
|
45
|
+
? rule.supersedes.filter((id) => typeof id === "string" && id.trim()).map((id) => id.trim())
|
|
46
|
+
: [];
|
|
47
|
+
|
|
48
|
+
const state = normalizeState(rule.state);
|
|
49
|
+
if (rule.state != null && state == null) return null;
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
id: rule.id.trim(),
|
|
53
|
+
patterns,
|
|
54
|
+
skill,
|
|
55
|
+
action,
|
|
56
|
+
priority: rule.priority,
|
|
57
|
+
supersedes,
|
|
58
|
+
exclusive: rule.exclusive === true,
|
|
59
|
+
state,
|
|
60
|
+
mcp_route: mcpRoute
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 외부 JSON 규칙 로드 + 스키마 검증
|
|
65
|
+
export function loadRules(rulesPath) {
|
|
66
|
+
try {
|
|
67
|
+
const raw = readFileSync(rulesPath, "utf8");
|
|
68
|
+
const parsed = JSON.parse(raw);
|
|
69
|
+
if (!parsed || !Array.isArray(parsed.rules)) {
|
|
70
|
+
logRuleError(`규칙 형식이 올바르지 않습니다: ${rulesPath}`);
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const normalized = parsed.rules.map(normalizeRule).filter(Boolean);
|
|
75
|
+
return normalized;
|
|
76
|
+
} catch (error) {
|
|
77
|
+
logRuleError(`규칙 파일을 읽을 수 없습니다: ${rulesPath}`, error);
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// pattern.source / flags를 RegExp로 컴파일
|
|
83
|
+
export function compileRules(rules) {
|
|
84
|
+
return rules.map((rule) => {
|
|
85
|
+
try {
|
|
86
|
+
return { ...rule, compiledPatterns: rule.patterns.map((p) => new RegExp(p.source, p.flags)) };
|
|
87
|
+
} catch (error) {
|
|
88
|
+
logRuleError(`정규식 컴파일 실패: ${rule.id}`, error);
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}).filter(Boolean);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 입력 텍스트에서 매칭된 규칙 목록 반환
|
|
95
|
+
export function matchRules(compiledRules, cleanText) {
|
|
96
|
+
if (!Array.isArray(compiledRules) || typeof cleanText !== "string" || !cleanText) {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const matches = [];
|
|
101
|
+
|
|
102
|
+
for (const rule of compiledRules) {
|
|
103
|
+
if (!Array.isArray(rule.compiledPatterns) || rule.compiledPatterns.length === 0) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const matched = rule.compiledPatterns.some((pattern) => {
|
|
108
|
+
pattern.lastIndex = 0;
|
|
109
|
+
return pattern.test(cleanText);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if (!matched) continue;
|
|
113
|
+
|
|
114
|
+
matches.push({
|
|
115
|
+
id: rule.id,
|
|
116
|
+
skill: rule.skill,
|
|
117
|
+
action: rule.action,
|
|
118
|
+
priority: rule.priority,
|
|
119
|
+
supersedes: rule.supersedes || [],
|
|
120
|
+
exclusive: rule.exclusive === true,
|
|
121
|
+
state: rule.state || null,
|
|
122
|
+
mcp_route: rule.mcp_route || null
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return matches;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// priority 정렬 + supersedes + exclusive 처리
|
|
130
|
+
export function resolveConflicts(matches) {
|
|
131
|
+
try {
|
|
132
|
+
if (!Array.isArray(matches) || matches.length === 0) return [];
|
|
133
|
+
|
|
134
|
+
const sorted = [...matches].sort((a, b) => {
|
|
135
|
+
if (a.priority !== b.priority) return a.priority - b.priority;
|
|
136
|
+
return String(a.id).localeCompare(String(b.id));
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const deduped = [];
|
|
140
|
+
const seen = new Set();
|
|
141
|
+
for (const match of sorted) {
|
|
142
|
+
if (seen.has(match.id)) continue;
|
|
143
|
+
deduped.push(match);
|
|
144
|
+
seen.add(match.id);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const superseded = new Set();
|
|
148
|
+
const resolved = [];
|
|
149
|
+
|
|
150
|
+
for (const match of deduped) {
|
|
151
|
+
if (superseded.has(match.id)) continue;
|
|
152
|
+
resolved.push(match);
|
|
153
|
+
for (const targetId of match.supersedes || []) {
|
|
154
|
+
superseded.add(targetId);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const exclusiveMatch = resolved.find((match) => match.exclusive === true);
|
|
159
|
+
if (exclusiveMatch) return [exclusiveMatch];
|
|
160
|
+
|
|
161
|
+
return resolved;
|
|
162
|
+
} catch (error) {
|
|
163
|
+
logRuleError("규칙 충돌 해결 실패", error);
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
}
|