triflux 10.43.0 → 10.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +2 -0
  2. package/adapters/codex/skills/tfx-harness/SKILL.md +30 -0
  3. package/adapters/codex/skills/tfx-harness/skill.json +5 -0
  4. package/bin/triflux.mjs +14 -0
  5. package/hooks/keyword-rules.json +139 -19
  6. package/hub/cli-adapter-base.mjs +11 -2
  7. package/hub/codex-adapter.mjs +3 -0
  8. package/hub/intent.mjs +24 -32
  9. package/hub/router.mjs +272 -0
  10. package/hub/schema.sql +61 -2
  11. package/hub/server.mjs +42 -0
  12. package/hub/store.mjs +577 -5
  13. package/hub/team/conductor.mjs +23 -2
  14. package/hub/team/execution-mode.mjs +12 -2
  15. package/hub/team/headless.mjs +16 -0
  16. package/hub/team/retry-state-machine.mjs +8 -3
  17. package/hub/team/swarm-hypervisor.mjs +1 -0
  18. package/hub/team/swarm-preflight.mjs +47 -0
  19. package/hub/team/synapse-http.mjs +149 -4
  20. package/hub/team/worktree-lifecycle.mjs +56 -59
  21. package/hub/tools.mjs +11 -0
  22. package/hub/workers/codex-mcp.mjs +20 -4
  23. package/hub/workers/delegator-mcp.mjs +3 -49
  24. package/package.json +2 -1
  25. package/scripts/__tests__/lint-skills.test.mjs +68 -0
  26. package/scripts/__tests__/tfx-route-node-entry.test.mjs +36 -0
  27. package/scripts/keyword-detector.mjs +55 -8
  28. package/scripts/lib/agent-route-policy.mjs +360 -0
  29. package/scripts/lib/cli-codex.mjs +22 -160
  30. package/scripts/lib/codex-profile-config.mjs +29 -3
  31. package/scripts/lib/keyword-rules.mjs +12 -0
  32. package/scripts/lint-skills.mjs +65 -0
  33. package/scripts/pack.mjs +13 -0
  34. package/scripts/release/check-packages-mirror.mjs +5 -0
  35. package/scripts/setup.mjs +94 -2
  36. package/scripts/tfx-route.mjs +14 -1
  37. package/scripts/tfx-route.sh +233 -70
  38. package/skills/tfx-harness/SKILL.md +19 -76
  39. package/skills/tfx-hub/SKILL.md +1 -1
  40. package/skills/tfx-interview/SKILL.md +3 -1
  41. package/skills/tfx-interview/skill.json +1 -13
  42. package/skills/tfx-profile/SKILL.md +25 -15
  43. package/skills/tfx-prune/SKILL.md +3 -1
  44. package/skills/tfx-prune/skill.json +1 -8
  45. package/skills/tfx-setup/SKILL.md +1 -1
  46. package/tui/codex-profile.mjs +11 -2
  47. package/tui/setup.mjs +2 -0
package/README.md CHANGED
@@ -264,6 +264,8 @@ state without replaying the entire conversation.
264
264
 
265
265
  ## Architecture
266
266
 
267
+ > 상세 구조·패키지 레이아웃·실행 경로는 [ARCHITECTURE.md](ARCHITECTURE.md), 문서 전체 지도는 [docs/README.md](docs/README.md) 참조.
268
+
267
269
  <p align="center">
268
270
  <img src="docs/assets/architecture.svg" alt="triflux architecture" width="680">
269
271
  </p>
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: tfx-harness
3
+ description: >
4
+ Use for meta-routing questions. Read the canonical routing SSOT and return
5
+ exactly one branch and immediate owner; do not execute the requested work.
6
+ ---
7
+
8
+ # tfx-harness — Codex adapter
9
+
10
+ Read `.claude/rules/tfx-routing.md` D0–D11 before routing. This adapter only
11
+ translates host syntax: use `$skill` for an available owner. Do not copy the
12
+ routing tree, keyword rules, or an owner matrix here.
13
+
14
+ Return:
15
+
16
+ ```text
17
+ [tfx-harness]
18
+ branch: D<n>
19
+ owner: <exactly one immediate owner>
20
+ availability: available | unavailable | unknown
21
+ fallback_notice: <optional>
22
+ status: recommendation-only | dispatching | blocked
23
+ ```
24
+
25
+ - Never choose more than one branch or immediate owner.
26
+ - With unknown availability, do not infer a fallback. With measured
27
+ unavailability, state `owner unavailable → tfx-X fallback` before fallback.
28
+ - If the SSOT cannot be read, return `blocked: routing SSOT unavailable`.
29
+ - For a “which skill/path?” question, remain recommendation-only.
30
+ - Do not select downstream implementation agents or models.
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "tfx-harness",
3
+ "version": "1.0.0",
4
+ "host": "codex"
5
+ }
package/bin/triflux.mjs CHANGED
@@ -94,6 +94,7 @@ import {
94
94
  SKILL_ALIASES,
95
95
  SYNC_MAP,
96
96
  syncAliasedSkillDir,
97
+ syncCodexHarnessAdapter,
97
98
  } from "../scripts/setup.mjs";
98
99
  import { cleanupTmpFiles } from "../scripts/tmp-cleanup.mjs";
99
100
 
@@ -2203,6 +2204,19 @@ function cmdSetup(options = {}) {
2203
2204
  }
2204
2205
  }
2205
2206
 
2207
+ const codexHarnessSync = syncCodexHarnessAdapter();
2208
+ if (!codexHarnessSync.ok) {
2209
+ fail(`Codex tfx-harness: ${codexHarnessSync.reason}`);
2210
+ return;
2211
+ }
2212
+ if (codexHarnessSync.action === "synced") {
2213
+ ok("Codex tfx-harness adapter: 동기화됨");
2214
+ } else if (codexHarnessSync.action === "skipped") {
2215
+ warn(
2216
+ `Codex tfx-harness adapter: 사용자 스킬 보존 (backup: ${codexHarnessSync.backupDir})`,
2217
+ );
2218
+ }
2219
+
2206
2220
  // ── psmux 기본 셸 자동 수정 (cmd.exe → PowerShell) ──
2207
2221
  if (process.platform === "win32" && which("psmux")) {
2208
2222
  try {
@@ -96,6 +96,102 @@
96
96
  "state": null,
97
97
  "mcp_route": null
98
98
  },
99
+ {
100
+ "id": "tfx-review",
101
+ "patterns": [
102
+ {
103
+ "source": "\\btfx[\\s-]?review\\b",
104
+ "flags": "i"
105
+ }
106
+ ],
107
+ "skill": "tfx-review",
108
+ "priority": 1,
109
+ "supersedes": ["tfx-unified"],
110
+ "exclusive": false,
111
+ "state": null,
112
+ "mcp_route": null,
113
+ "explicit": true
114
+ },
115
+ {
116
+ "id": "tfx-analysis",
117
+ "patterns": [
118
+ {
119
+ "source": "\\btfx[\\s-]?analysis\\b",
120
+ "flags": "i"
121
+ }
122
+ ],
123
+ "skill": "tfx-analysis",
124
+ "priority": 1,
125
+ "supersedes": ["tfx-unified"],
126
+ "exclusive": false,
127
+ "state": null,
128
+ "mcp_route": null,
129
+ "explicit": true
130
+ },
131
+ {
132
+ "id": "tfx-plan",
133
+ "patterns": [
134
+ {
135
+ "source": "\\btfx[\\s-]?plan\\b",
136
+ "flags": "i"
137
+ }
138
+ ],
139
+ "skill": "tfx-plan",
140
+ "priority": 1,
141
+ "supersedes": ["tfx-unified"],
142
+ "exclusive": false,
143
+ "state": null,
144
+ "mcp_route": null,
145
+ "explicit": true
146
+ },
147
+ {
148
+ "id": "tfx-qa",
149
+ "patterns": [
150
+ {
151
+ "source": "\\btfx[\\s-]?qa\\b",
152
+ "flags": "i"
153
+ }
154
+ ],
155
+ "skill": "tfx-qa",
156
+ "priority": 1,
157
+ "supersedes": ["tfx-unified"],
158
+ "exclusive": false,
159
+ "state": null,
160
+ "mcp_route": null,
161
+ "explicit": true
162
+ },
163
+ {
164
+ "id": "tfx-research",
165
+ "patterns": [
166
+ {
167
+ "source": "\\btfx[\\s-]?research\\b",
168
+ "flags": "i"
169
+ }
170
+ ],
171
+ "skill": "tfx-research",
172
+ "priority": 1,
173
+ "supersedes": ["tfx-unified"],
174
+ "exclusive": false,
175
+ "state": null,
176
+ "mcp_route": null,
177
+ "explicit": true
178
+ },
179
+ {
180
+ "id": "tfx-find",
181
+ "patterns": [
182
+ {
183
+ "source": "\\btfx[\\s-]?find\\b",
184
+ "flags": "i"
185
+ }
186
+ ],
187
+ "skill": "tfx-find",
188
+ "priority": 1,
189
+ "supersedes": ["tfx-unified"],
190
+ "exclusive": false,
191
+ "state": null,
192
+ "mcp_route": null,
193
+ "explicit": true
194
+ },
99
195
  {
100
196
  "id": "tfx-unified",
101
197
  "patterns": [
@@ -127,10 +223,6 @@
127
223
  "source": "(?:찾아봐|조사해|검색해)",
128
224
  "flags": "i"
129
225
  },
130
- {
131
- "source": "(?:정리해|슬롭|클린업)",
132
- "flags": "i"
133
- },
134
226
  {
135
227
  "source": "(?:병렬|동시에|parallel|concurrent)",
136
228
  "flags": "i"
@@ -155,6 +247,41 @@
155
247
  "state": null,
156
248
  "mcp_route": null
157
249
  },
250
+ {
251
+ "id": "host-ai-slop-cleaner",
252
+ "patterns": [
253
+ {
254
+ "source": "(?:AI\\s*슬롭|ai\\s*slop|anti[\\s-]?slop|deslop)",
255
+ "flags": "i"
256
+ },
257
+ {
258
+ "source": "(?:슬롭|클린업)",
259
+ "flags": "i"
260
+ }
261
+ ],
262
+ "skill": "ai-slop-cleaner",
263
+ "priority": 1,
264
+ "supersedes": [],
265
+ "exclusive": false,
266
+ "state": null,
267
+ "mcp_route": null
268
+ },
269
+ {
270
+ "id": "tfx-prune",
271
+ "patterns": [
272
+ {
273
+ "source": "\\btfx[\\s-]?prune\\b",
274
+ "flags": "i"
275
+ }
276
+ ],
277
+ "skill": "tfx-prune",
278
+ "priority": 1,
279
+ "supersedes": ["tfx-unified"],
280
+ "exclusive": false,
281
+ "state": null,
282
+ "mcp_route": null,
283
+ "explicit": true
284
+ },
158
285
  {
159
286
  "id": "tfx-codex",
160
287
  "patterns": [
@@ -586,7 +713,6 @@
586
713
  },
587
714
  {
588
715
  "id": "gstack-ship",
589
- "disabled": true,
590
716
  "patterns": [
591
717
  {
592
718
  "source": "(?:배포해|PR\\s*만들|릴리스\\s*해|머지하고\\s*배포)",
@@ -598,7 +724,7 @@
598
724
  }
599
725
  ],
600
726
  "skill": "ship",
601
- "priority": 5,
727
+ "priority": 1,
602
728
  "supersedes": [],
603
729
  "exclusive": false,
604
730
  "state": null,
@@ -627,12 +753,12 @@
627
753
  "id": "gstack-cso",
628
754
  "patterns": [
629
755
  {
630
- "source": "(?:보안\\s*(?:감사|점검|리뷰|스캔)|\\bcso\\b|\\bOWASP\\b|\\bSTRIDE\\b)",
756
+ "source": "(?:보안\\s*(?:감사|점검|리뷰|검토|스캔)|\\bcso\\b|\\bOWASP\\b|\\bSTRIDE\\b)",
631
757
  "flags": "i"
632
758
  }
633
759
  ],
634
760
  "skill": "cso",
635
- "priority": 5,
761
+ "priority": 1,
636
762
  "supersedes": [],
637
763
  "exclusive": false,
638
764
  "state": null,
@@ -651,7 +777,7 @@
651
777
  }
652
778
  ],
653
779
  "skill": "qa",
654
- "priority": 5,
780
+ "priority": 1,
655
781
  "supersedes": [],
656
782
  "exclusive": false,
657
783
  "state": null,
@@ -722,14 +848,6 @@
722
848
  "source": "/ship\\b",
723
849
  "flags": "i"
724
850
  },
725
- {
726
- "source": "\\brelease\\b",
727
- "flags": "i"
728
- },
729
- {
730
- "source": "\\bpublish\\b",
731
- "flags": "i"
732
- },
733
851
  {
734
852
  "source": "배포(?!자|사|장|처)",
735
853
  "flags": ""
@@ -744,11 +862,13 @@
744
862
  }
745
863
  ],
746
864
  "skill": "tfx-ship",
747
- "priority": 3,
865
+ "priority": 1,
748
866
  "supersedes": ["gstack-ship"],
749
867
  "exclusive": false,
750
868
  "state": null,
751
- "mcp_route": null
869
+ "mcp_route": null,
870
+ "repo_scope": ["triflux"],
871
+ "explicit": true
752
872
  }
753
873
  ]
754
874
  }
@@ -167,7 +167,7 @@ export const CODEX_MCP_EXECUTION_EXIT_CODE = 1;
167
167
  *
168
168
  * @param {string} prompt
169
169
  * @param {string|null} resultFile — null이면 --output-last-message 생략
170
- * @param {{ profile?: string, skipGitRepoCheck?: boolean, sandboxBypass?: boolean, cwd?: string, mcpServers?: string[], stdinPrompt?: boolean }} [opts]
170
+ * @param {{ profile?: string, codexHome?: string, disallowUltra?: boolean, enforceCanonicalProfile?: boolean, skipGitRepoCheck?: boolean, sandboxBypass?: boolean, cwd?: string, mcpServers?: string[], stdinPrompt?: boolean }} [opts]
171
171
  * @returns {string} 실행할 셸 커맨드
172
172
  */
173
173
  export function buildExecCommand(prompt, resultFile = null, opts = {}) {
@@ -177,6 +177,9 @@ export function buildExecCommand(prompt, resultFile = null, opts = {}) {
177
177
  sandboxBypass = true,
178
178
  mcpServers,
179
179
  stdinPrompt,
180
+ codexHome,
181
+ disallowUltra,
182
+ enforceCanonicalProfile,
180
183
  } = opts;
181
184
 
182
185
  const parts = ["codex"];
@@ -185,7 +188,13 @@ export function buildExecCommand(prompt, resultFile = null, opts = {}) {
185
188
  // still contains an inline [profiles.X] table (and codex re-injects such
186
189
  // tables when it rewrites config.toml), so the `-c model=.. -c
187
190
  // model_reasoning_effort=..` form is immune and mutates no config.
188
- const profileOverrides = profile ? codexProfileConfigOverrides(profile) : [];
191
+ const profileOverrides = profile
192
+ ? codexProfileConfigOverrides(profile, {
193
+ codexHome,
194
+ disallowUltra,
195
+ enforceCanonicalProfile,
196
+ })
197
+ : [];
189
198
 
190
199
  if (FEATURES.execSubcommand) {
191
200
  parts.push("exec");
@@ -118,6 +118,9 @@ export function buildExecArgs(opts = {}) {
118
118
  const prompt = typeof opts.prompt === "string" ? opts.prompt : "";
119
119
  const command = buildExecCommand(prompt, opts.resultFile || null, {
120
120
  profile: opts.profile,
121
+ codexHome: opts.codexHome,
122
+ disallowUltra: opts.disallowUltra,
123
+ enforceCanonicalProfile: opts.enforceCanonicalProfile,
121
124
  skipGitRepoCheck: true,
122
125
  sandboxBypass: true,
123
126
  cwd: opts.cwd,
package/hub/intent.mjs CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  import { execFileSync } from "node:child_process";
5
5
  import crypto from "node:crypto";
6
+ import { resolveCodexAgentPolicy } from "../scripts/lib/agent-route-policy.mjs";
6
7
  import { whichCommand } from "./platform.mjs";
7
8
 
8
9
  /** 캐시 엔트리: { category, confidence, ts } */
@@ -61,31 +62,27 @@ function _tryCodexClassify(prompt) {
61
62
  * triflux 특화 의도 카테고리 (10개)
62
63
  */
63
64
  export const INTENT_CATEGORIES = {
64
- // 모델 정책 (2026-04-25):
65
- // - GPT-5.6 family = 기존 semantic tier를 Sol/Terra/Luna lane으로 매핑
66
- // - gpt-5.4-mini = 자잘/부가/가성비 (fast tier 가능)
67
- // - gpt-5.3-codex = escalation 가성비 중간 (fast 미지원)
68
- // - 기존 effort는 유지하고 모델만 GPT-5.6 family로 교체
69
- implement: {
70
- agent: "executor",
71
- mcp: "implement",
72
- effort: "gpt56_terra_high",
73
- },
74
- debug: { agent: "debugger", mcp: "implement", effort: "gpt56_sol_xhigh" },
75
- analyze: { agent: "analyst", mcp: "analyze", effort: "gpt56_sol_xhigh" },
76
- design: { agent: "architect", mcp: "analyze", effort: "gpt56_sol_xhigh" },
77
- review: { agent: "code-reviewer", mcp: "review", effort: "gpt56_terra_high" },
78
- document: { agent: "writer", mcp: "docs", effort: "pro" },
79
- research: { agent: "scientist", mcp: "analyze", effort: "gpt56_terra_high" },
80
- "quick-fix": {
81
- agent: "build-fixer",
82
- mcp: "implement",
83
- effort: "gpt56_luna_low",
84
- },
85
- explain: { agent: "writer", mcp: "docs", effort: "flash" },
86
- test: { agent: "test-engineer", mcp: null, effort: null },
65
+ implement: { agent: "executor", mcp: "implement" },
66
+ debug: { agent: "debugger", mcp: "implement" },
67
+ analyze: { agent: "analyst", mcp: "analyze" },
68
+ design: { agent: "architect", mcp: "analyze" },
69
+ review: { agent: "code-reviewer", mcp: "review" },
70
+ document: { agent: "writer", mcp: "docs" },
71
+ research: { agent: "scientist", mcp: "analyze" },
72
+ "quick-fix": { agent: "build-fixer", mcp: "implement" },
73
+ explain: { agent: "writer", mcp: "docs" },
74
+ test: { agent: "test-engineer", mcp: null },
87
75
  };
88
76
 
77
+ function routingFor(category) {
78
+ const route = INTENT_CATEGORIES[category] || INTENT_CATEGORIES.implement;
79
+ return {
80
+ agent: route.agent,
81
+ mcp: route.mcp,
82
+ effort: resolveCodexAgentPolicy(route.agent).profile,
83
+ };
84
+ }
85
+
89
86
  /** @internal 키워드 → 카테고리 매핑 패턴 */
90
87
  const KEYWORD_PATTERNS = [
91
88
  {
@@ -267,17 +264,12 @@ export function classifyIntent(prompt) {
267
264
  // 캐시 확인
268
265
  const cached = _getCached(hash);
269
266
  if (cached) {
270
- const routing =
271
- INTENT_CATEGORIES[cached.category] || INTENT_CATEGORIES.implement;
267
+ const routing = routingFor(cached.category);
272
268
  return {
273
269
  category: cached.category,
274
270
  confidence: cached.confidence,
275
271
  reasoning: `cache-hit: ${cached.category} (${cached.confidence.toFixed(2)})`,
276
- routing: {
277
- agent: routing.agent,
278
- mcp: routing.mcp,
279
- effort: routing.effort,
280
- },
272
+ routing,
281
273
  };
282
274
  }
283
275
 
@@ -308,12 +300,12 @@ export function classifyIntent(prompt) {
308
300
  // 캐시 저장
309
301
  _intentCache.set(hash, { category, confidence, ts: Date.now() });
310
302
 
311
- const routing = INTENT_CATEGORIES[category] || INTENT_CATEGORIES.implement;
303
+ const routing = routingFor(category);
312
304
  return {
313
305
  category,
314
306
  confidence,
315
307
  reasoning,
316
- routing: { agent: routing.agent, mcp: routing.mcp, effort: routing.effort },
308
+ routing,
317
309
  };
318
310
  }
319
311