@triflux/core 10.69.0 → 10.70.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.
package/hub/team/agent-map.json
CHANGED
package/hub/team/pane.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// hub/team/pane.mjs — pane별 CLI 실행 + stdin 주입
|
|
2
2
|
// 의존성: child_process, fs, os, path (Node.js 내장)만 사용
|
|
3
|
-
import { unlinkSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { existsSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
|
-
import { join } from "node:path";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
6
7
|
import { privateTmpDir } from "../lib/private-tmp.mjs";
|
|
7
8
|
import { psmuxExec } from "./psmux.mjs";
|
|
8
9
|
import { detectMultiplexer, tmuxExec } from "./session.mjs";
|
|
@@ -56,7 +57,32 @@ export function resolveCli(cli) {
|
|
|
56
57
|
* @param {string} cli: CLI 이름, 별칭(agy) 또는 역할명
|
|
57
58
|
* @returns {string} 실행할 셸 커맨드
|
|
58
59
|
*/
|
|
59
|
-
export function buildCliCommand(cli) {
|
|
60
|
+
export function buildCliCommand(cli, prompt) {
|
|
61
|
+
if (["code-reviewer", "quality-reviewer"].includes(cli)) {
|
|
62
|
+
// 작성 벤더 선택은 라우터에 맡기고 프롬프트가 준비된 뒤 실행한다.
|
|
63
|
+
if (prompt === undefined) return "";
|
|
64
|
+
const routeScript = [
|
|
65
|
+
process.env.TFX_ROUTE_SCRIPT,
|
|
66
|
+
process.env.TFX_DELEGATOR_ROUTE_SCRIPT,
|
|
67
|
+
join(
|
|
68
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
69
|
+
"../../scripts/tfx-route.sh",
|
|
70
|
+
),
|
|
71
|
+
join(process.env.HOME || "", ".claude/scripts/tfx-route.sh"),
|
|
72
|
+
].find((path) => path && existsSync(path));
|
|
73
|
+
if (!routeScript) throw new Error("tfx-route.sh 를 찾지 못했다.");
|
|
74
|
+
const quote = (value) => `'${String(value).replace(/'/g, "'\\''")}'`;
|
|
75
|
+
const reviewEnv = ["TFX_REVIEW_AUTHOR", "TFX_CLI_MODE"]
|
|
76
|
+
.filter((key) => process.env[key] !== undefined)
|
|
77
|
+
.map((key) => `${key}=${quote(process.env[key])} `)
|
|
78
|
+
.join("");
|
|
79
|
+
const script = `${reviewEnv}bash ${quote(routeScript.replace(/\\/g, "/"))} ${quote(cli)} ${quote(prompt)} review < /dev/null`;
|
|
80
|
+
const quotedScript =
|
|
81
|
+
process.platform === "win32"
|
|
82
|
+
? `'${script.replace(/'/g, "''")}'`
|
|
83
|
+
: quote(script);
|
|
84
|
+
return `bash -lc ${quotedScript}`;
|
|
85
|
+
}
|
|
60
86
|
switch (resolveCli(cli)) {
|
|
61
87
|
case "codex":
|
|
62
88
|
return "codex --dangerously-bypass-approvals-and-sandbox";
|
|
@@ -78,7 +104,7 @@ export function buildCliCommand(cli) {
|
|
|
78
104
|
*/
|
|
79
105
|
export function startCliInPane(target, command) {
|
|
80
106
|
// CLI 시작도 buffer paste를 재사용해 셸/플랫폼별 quoting 차이를 제거한다.
|
|
81
|
-
injectPrompt(target, command);
|
|
107
|
+
if (command) injectPrompt(target, command);
|
|
82
108
|
}
|
|
83
109
|
|
|
84
110
|
/**
|
|
@@ -247,6 +273,11 @@ export function injectPrompt(
|
|
|
247
273
|
prompt,
|
|
248
274
|
{ useFileRef = false, cli = null } = {},
|
|
249
275
|
) {
|
|
276
|
+
if (["code-reviewer", "quality-reviewer"].includes(cli)) {
|
|
277
|
+
prompt = buildCliCommand(cli, prompt);
|
|
278
|
+
cli = null;
|
|
279
|
+
useFileRef = false;
|
|
280
|
+
}
|
|
250
281
|
const tmpDir = privateTmpDir("tfx-multi");
|
|
251
282
|
|
|
252
283
|
const safeTarget = target.replace(/[:.]/g, "-");
|
package/package.json
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Canonical Codex role policy. Concrete model IDs stay in profile config files.
|
|
2
2
|
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
4
6
|
import { pathToFileURL } from "node:url";
|
|
5
7
|
import {
|
|
6
8
|
normalizeCodexProfileName,
|
|
@@ -298,6 +300,7 @@ const NATIVE_CODEX_AGENTS = new Set([
|
|
|
298
300
|
"test-engineer",
|
|
299
301
|
"qa-tester",
|
|
300
302
|
]);
|
|
303
|
+
const CLAUDE_REVIEW_USAGE_THRESHOLD = 80;
|
|
301
304
|
|
|
302
305
|
function resolveDirectCodexRoutePolicy(agent) {
|
|
303
306
|
if (agent === "explore") {
|
|
@@ -315,6 +318,7 @@ export function resolveAgentRoute(
|
|
|
315
318
|
profileOverride = "auto",
|
|
316
319
|
nested = false,
|
|
317
320
|
geminiProfileResolver = resolveGeminiProfileForPurpose,
|
|
321
|
+
{ reviewAuthor = "codex", weeklyPercent = 0 } = {},
|
|
318
322
|
) {
|
|
319
323
|
if (!/^[A-Za-z0-9_.-]+$/.test(agent)) {
|
|
320
324
|
throw new Error(`invalid agent: ${agent}`);
|
|
@@ -324,10 +328,27 @@ export function resolveAgentRoute(
|
|
|
324
328
|
`알 수 없는 에이전트 타입: ${agent}; available: ${Object.keys(map).join(", ")}`,
|
|
325
329
|
);
|
|
326
330
|
}
|
|
327
|
-
|
|
331
|
+
let provider = map[agent];
|
|
328
332
|
if (!["codex", "antigravity", "claude"].includes(provider)) {
|
|
329
333
|
throw new Error(`invalid provider for ${agent}: ${provider}`);
|
|
330
334
|
}
|
|
335
|
+
const byAuthor = agent === "code-reviewer" || agent === "quality-reviewer";
|
|
336
|
+
let usageSwitch = false;
|
|
337
|
+
if (byAuthor) {
|
|
338
|
+
if (!["codex", "claude", "antigravity"].includes(reviewAuthor)) {
|
|
339
|
+
throw new Error(
|
|
340
|
+
`TFX_REVIEW_AUTHOR 값이 유효하지 않습니다: ${reviewAuthor}`,
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
usageSwitch =
|
|
344
|
+
reviewAuthor !== "claude" &&
|
|
345
|
+
Number.isFinite(weeklyPercent) &&
|
|
346
|
+
weeklyPercent >= CLAUDE_REVIEW_USAGE_THRESHOLD;
|
|
347
|
+
if (reviewAuthor === "claude") provider = "codex";
|
|
348
|
+
else if (usageSwitch)
|
|
349
|
+
provider = reviewAuthor === "codex" ? "antigravity" : "codex";
|
|
350
|
+
else provider = "claude";
|
|
351
|
+
}
|
|
331
352
|
const policy = resolveDirectCodexRoutePolicy(agent);
|
|
332
353
|
const normalizedOverride = normalizeCodexProfileName(profileOverride);
|
|
333
354
|
let resolvedOverride = policy.profile;
|
|
@@ -354,6 +375,8 @@ export function resolveAgentRoute(
|
|
|
354
375
|
geminiProfileResolver(agent),
|
|
355
376
|
EXPECTED_DURATION_BY_AGENT[agent] ?? 30,
|
|
356
377
|
NATIVE_CODEX_AGENTS.has(agent),
|
|
378
|
+
byAuthor ? reviewAuthor : "",
|
|
379
|
+
usageSwitch,
|
|
357
380
|
];
|
|
358
381
|
}
|
|
359
382
|
|
|
@@ -402,6 +425,23 @@ async function runCli() {
|
|
|
402
425
|
if (format === "route") {
|
|
403
426
|
if (!mapFile) throw new Error("--map-file is required for route format");
|
|
404
427
|
const map = JSON.parse(readFileSync(mapFile, "utf8"));
|
|
428
|
+
let weeklyPercent = 0;
|
|
429
|
+
if (agent === "code-reviewer" || agent === "quality-reviewer") {
|
|
430
|
+
try {
|
|
431
|
+
const data = JSON.parse(
|
|
432
|
+
readFileSync(
|
|
433
|
+
join(homedir(), ".claude/cache/claude-usage-cache.json"),
|
|
434
|
+
"utf8",
|
|
435
|
+
),
|
|
436
|
+
).data;
|
|
437
|
+
weeklyPercent =
|
|
438
|
+
Date.parse(data?.weeklyResetsAt) <= Date.now()
|
|
439
|
+
? 0
|
|
440
|
+
: data?.weeklyPercent;
|
|
441
|
+
} catch {
|
|
442
|
+
// 사용량을 읽지 못하면 한도 미만으로 취급한다.
|
|
443
|
+
}
|
|
444
|
+
}
|
|
405
445
|
let resolver = resolveGeminiProfileForPurpose;
|
|
406
446
|
if (geminiProfilesModule) {
|
|
407
447
|
try {
|
|
@@ -418,9 +458,10 @@ async function runCli() {
|
|
|
418
458
|
}
|
|
419
459
|
}
|
|
420
460
|
process.stdout.write(
|
|
421
|
-
resolveAgentRoute(agent, map, profileOverride, nested, resolver
|
|
422
|
-
"
|
|
423
|
-
|
|
461
|
+
resolveAgentRoute(agent, map, profileOverride, nested, resolver, {
|
|
462
|
+
reviewAuthor: process.env.TFX_REVIEW_AUTHOR ?? "codex",
|
|
463
|
+
weeklyPercent,
|
|
464
|
+
}).join("\x1e"),
|
|
424
465
|
);
|
|
425
466
|
return;
|
|
426
467
|
}
|