@triflux/core 10.0.1 → 10.0.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.
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
// CLAUDE_PLUGIN_ROOT — ${PLUGIN_ROOT} 치환용
|
|
20
20
|
// HOME / USERPROFILE — ${HOME} 치환용
|
|
21
21
|
|
|
22
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
22
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
|
23
23
|
import { join, dirname } from "node:path";
|
|
24
24
|
import { fileURLToPath } from "node:url";
|
|
25
25
|
import { execFileSync, execFile } from "node:child_process";
|
|
@@ -210,8 +210,55 @@ function mergeOutputs(accumulated, newOutput) {
|
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
// ── 라우팅 가중치 기록 ────────────────────────────────────────
|
|
214
|
+
function recordRouteOutcome(slug, mode, outcome) {
|
|
215
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
216
|
+
const projDir = join(home, ".gstack", "projects", slug);
|
|
217
|
+
const weightsPath = join(projDir, "routing-weights.json");
|
|
218
|
+
|
|
219
|
+
mkdirSync(projDir, { recursive: true });
|
|
220
|
+
|
|
221
|
+
const weights = existsSync(weightsPath)
|
|
222
|
+
? JSON.parse(readFileSync(weightsPath, "utf8"))
|
|
223
|
+
: { updated_at: null, total_routes: 0, overrides: 0, weights: { mode_bias: {}, profile_bias: {}, depth_bias: {} } };
|
|
224
|
+
|
|
225
|
+
weights.total_routes++;
|
|
226
|
+
weights.updated_at = new Date().toISOString();
|
|
227
|
+
|
|
228
|
+
const bias = weights.weights.mode_bias;
|
|
229
|
+
const current = bias[mode] || 0;
|
|
230
|
+
|
|
231
|
+
if (outcome === "override") {
|
|
232
|
+
bias[mode] = Math.max(0, current - 0.1);
|
|
233
|
+
weights.overrides++;
|
|
234
|
+
} else if (outcome === "completion") {
|
|
235
|
+
bias[mode] = Math.min(1, current + 0.05);
|
|
236
|
+
} else if (outcome === "abort") {
|
|
237
|
+
bias[mode] = Math.max(0, current - 0.1);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const total = Object.values(bias).reduce((s, v) => s + v, 0);
|
|
241
|
+
if (total > 0) {
|
|
242
|
+
for (const key of Object.keys(bias)) {
|
|
243
|
+
bias[key] = +(bias[key] / total).toFixed(3);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
writeFileSync(weightsPath, JSON.stringify(weights, null, 2), "utf8");
|
|
248
|
+
}
|
|
249
|
+
|
|
213
250
|
// ── 메인 ────────────────────────────────────────────────────
|
|
214
251
|
async function main() {
|
|
252
|
+
// CLI: --record-route <slug> <mode> <outcome>
|
|
253
|
+
const rrIdx = process.argv.indexOf("--record-route");
|
|
254
|
+
if (rrIdx !== -1) {
|
|
255
|
+
const slug = process.argv[rrIdx + 1];
|
|
256
|
+
const mode = process.argv[rrIdx + 2];
|
|
257
|
+
const outcome = process.argv[rrIdx + 3];
|
|
258
|
+
if (slug && mode && outcome) recordRouteOutcome(slug, mode, outcome);
|
|
259
|
+
process.exit(0);
|
|
260
|
+
}
|
|
261
|
+
|
|
215
262
|
const stdinRaw = readStdin();
|
|
216
263
|
const registry = loadRegistry();
|
|
217
264
|
|
package/hooks/keyword-rules.json
CHANGED
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
"priority": 0,
|
|
14
14
|
"supersedes": [
|
|
15
15
|
"tfx-multi",
|
|
16
|
-
"tfx-
|
|
17
|
-
"tfx-auto-codex",
|
|
16
|
+
"tfx-unified",
|
|
18
17
|
"tfx-codex",
|
|
19
18
|
"tfx-gemini"
|
|
20
19
|
],
|
|
@@ -71,34 +70,21 @@
|
|
|
71
70
|
"mcp_route": null
|
|
72
71
|
},
|
|
73
72
|
{
|
|
74
|
-
"id": "tfx-
|
|
73
|
+
"id": "tfx-unified",
|
|
75
74
|
"patterns": [
|
|
76
|
-
{
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
"
|
|
85
|
-
"tfx-codex"
|
|
86
|
-
],
|
|
87
|
-
"exclusive": false,
|
|
88
|
-
"state": null,
|
|
89
|
-
"mcp_route": null
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
"id": "tfx-auto",
|
|
93
|
-
"patterns": [
|
|
94
|
-
{
|
|
95
|
-
"source": "\\btfx[\\s-]?auto\\b",
|
|
96
|
-
"flags": "i"
|
|
97
|
-
}
|
|
75
|
+
{ "source": "\\btfx[\\s-]?auto\\b", "flags": "i" },
|
|
76
|
+
{ "source": "\\btfx[\\s-]?auto[\\s-]?codex\\b", "flags": "i" },
|
|
77
|
+
{ "source": "(?:만들어|고쳐|구현해|짜줘|수정해|바꿔)", "flags": "i" },
|
|
78
|
+
{ "source": "(?:리뷰해|검토해|봐줘|괜찮아)", "flags": "i" },
|
|
79
|
+
{ "source": "(?:테스트|검증|돌려봐|QA)", "flags": "i" },
|
|
80
|
+
{ "source": "(?:분석해|계획|설계해)", "flags": "i" },
|
|
81
|
+
{ "source": "(?:찾아봐|조사해|검색해)", "flags": "i" },
|
|
82
|
+
{ "source": "(?:정리해|슬롭|클린업)", "flags": "i" },
|
|
83
|
+
{ "source": "\\b(?:implement|build|fix|review|test|plan|analyze)\\b", "flags": "i" }
|
|
98
84
|
],
|
|
99
85
|
"skill": "tfx-auto",
|
|
100
86
|
"priority": 2,
|
|
101
|
-
"supersedes": [],
|
|
87
|
+
"supersedes": ["tfx-auto-codex"],
|
|
102
88
|
"exclusive": false,
|
|
103
89
|
"state": null,
|
|
104
90
|
"mcp_route": null
|
package/hub/cli-adapter-base.mjs
CHANGED
|
@@ -38,15 +38,16 @@ export function gte(minMinor) {
|
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* Codex CLI 기능별 분기 객체.
|
|
41
|
-
*
|
|
41
|
+
* 실측 기반 임계값: 0.114.0에서 exec/skip-git-repo-check/color 확인됨.
|
|
42
|
+
* --output-last-message는 0.114.0에 없음 (0.117+ 추정).
|
|
42
43
|
*/
|
|
43
44
|
export const FEATURES = {
|
|
44
|
-
/** exec 서브커맨드 사용 가능 여부 */
|
|
45
|
-
get execSubcommand() { return gte(
|
|
46
|
-
/** --output-last-message 플래그 지원 여부 */
|
|
45
|
+
/** exec 서브커맨드 사용 가능 여부 (0.110+ 이전부터 존재) */
|
|
46
|
+
get execSubcommand() { return gte(110); },
|
|
47
|
+
/** --output-last-message 플래그 지원 여부 (0.117+) */
|
|
47
48
|
get outputLastMessage() { return gte(117); },
|
|
48
|
-
/** --color
|
|
49
|
-
get colorNever() { return gte(
|
|
49
|
+
/** --color <COLOR> 플래그 지원 여부 (exec와 동시 도입) */
|
|
50
|
+
get colorNever() { return gte(110); },
|
|
50
51
|
/** 플러그인 시스템 지원 여부 (향후 확장용) */
|
|
51
52
|
get pluginSystem() { return gte(120); },
|
|
52
53
|
};
|
package/package.json
CHANGED
|
@@ -317,6 +317,30 @@ export function loadTemplatePartials(partialsDir) {
|
|
|
317
317
|
return partials;
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
+
export function loadSkillManifest(skillDir) {
|
|
321
|
+
const manifestPath = join(skillDir, "skill.json");
|
|
322
|
+
if (!existsSync(manifestPath)) return null;
|
|
323
|
+
|
|
324
|
+
const raw = readFileSync(manifestPath, "utf8");
|
|
325
|
+
return JSON.parse(raw);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export function parseFrontmatterWithManifest(source, skillDir) {
|
|
329
|
+
const manifest = skillDir ? loadSkillManifest(skillDir) : null;
|
|
330
|
+
const { data: yamlData, body } = parseFrontmatter(source);
|
|
331
|
+
|
|
332
|
+
if (!manifest) return { data: yamlData, body };
|
|
333
|
+
|
|
334
|
+
const merged = { ...yamlData };
|
|
335
|
+
if (manifest.name) merged.name = manifest.name;
|
|
336
|
+
if (manifest.description) merged.description = manifest.description;
|
|
337
|
+
if (manifest.triggers) merged.triggers = manifest.triggers;
|
|
338
|
+
if (manifest.argument_hint) merged["argument-hint"] = manifest.argument_hint;
|
|
339
|
+
if (manifest.internal != null) merged.internal = manifest.internal;
|
|
340
|
+
|
|
341
|
+
return { data: merged, body };
|
|
342
|
+
}
|
|
343
|
+
|
|
320
344
|
export function renderSkillTemplate(template, context = {}, options = {}) {
|
|
321
345
|
const { partials = {}, includes = {}, includeBaseDir = "" } = options;
|
|
322
346
|
return renderWithContext(template, context, partials, {
|