triflux 10.61.2 → 10.63.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/.claude/rules/tfx-routing.md +14 -9
- package/README.ko.md +11 -9
- package/README.md +11 -8
- package/adapters/codex/skills/tfx-live/SKILL.md +30 -5
- package/bin/tfx-live.mjs +591 -83
- package/bin/triflux.mjs +9 -2
- package/hub/team/claude-transcript.mjs +27 -0
- package/hub/team/retry-state-machine.mjs +16 -24
- package/hub/team/session-context.mjs +13 -2
- package/package.json +1 -1
- package/scripts/lib/agent-route-policy.mjs +40 -40
- package/scripts/lib/codex-profile-config.mjs +44 -33
- package/scripts/lib/gemini-profiles.mjs +112 -126
- package/scripts/lib/model-family-resolver.mjs +335 -0
- package/scripts/setup.mjs +21 -47
- package/scripts/tfx-route.sh +29 -98
- package/skills/tfx-auto/SKILL.md +1 -1
- package/skills/tfx-lead/SKILL.md +5 -5
- package/skills/tfx-live/SKILL.md +30 -5
- package/skills/tfx-setup/SKILL.md +1 -1
package/scripts/setup.mjs
CHANGED
|
@@ -36,6 +36,10 @@ import {
|
|
|
36
36
|
recordFile,
|
|
37
37
|
renderCliResult,
|
|
38
38
|
} from "./lib/cli-output.mjs";
|
|
39
|
+
import {
|
|
40
|
+
ensureManagedCodexProfiles,
|
|
41
|
+
normalizeCodexProfileName,
|
|
42
|
+
} from "./lib/codex-profile-config.mjs";
|
|
39
43
|
import { ensureGeminiProfiles } from "./lib/gemini-profiles.mjs";
|
|
40
44
|
import {
|
|
41
45
|
retireInstallLeftovers,
|
|
@@ -56,6 +60,7 @@ import {
|
|
|
56
60
|
resolveMachineProfilePath,
|
|
57
61
|
resolveTrifluxHome,
|
|
58
62
|
} from "./lib/machine-profile.mjs";
|
|
63
|
+
import { CODEX_FAMILY_PROFILES } from "./lib/model-family-resolver.mjs";
|
|
59
64
|
import { parseFrontmatter } from "./lib/skill-template.mjs";
|
|
60
65
|
import { resolveStableNodeBin } from "./lib/stable-node.mjs";
|
|
61
66
|
import { isTestRun, writesRealHomeInTest } from "./lib/test-env.mjs";
|
|
@@ -568,39 +573,12 @@ const BREADCRUMB_PATH = join(CLAUDE_DIR, "scripts", ".tfx-pkg-root");
|
|
|
568
573
|
const SETTINGS_PATH = join(CLAUDE_DIR, "settings.json");
|
|
569
574
|
const HUD_PATH = join(CLAUDE_DIR, "hud", "hud-qos-status.mjs");
|
|
570
575
|
|
|
571
|
-
const REQUIRED_CODEX_PROFILES =
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
{
|
|
578
|
-
name: "gpt6_astra_max",
|
|
579
|
-
lines: ['model = "gpt-6-astra"', 'model_reasoning_effort = "max"'],
|
|
580
|
-
},
|
|
581
|
-
{
|
|
582
|
-
name: "gpt6_astra_xhigh",
|
|
583
|
-
lines: ['model = "gpt-6-astra"', 'model_reasoning_effort = "xhigh"'],
|
|
584
|
-
},
|
|
585
|
-
// GPT-6.1 Sol: implementation, review, and verification role defaults.
|
|
586
|
-
{
|
|
587
|
-
name: "gpt61_sol_high",
|
|
588
|
-
lines: ['model = "gpt-6.1-sol"', 'model_reasoning_effort = "high"'],
|
|
589
|
-
},
|
|
590
|
-
{
|
|
591
|
-
name: "gpt61_sol_med",
|
|
592
|
-
lines: ['model = "gpt-6.1-sol"', 'model_reasoning_effort = "medium"'],
|
|
593
|
-
},
|
|
594
|
-
// GPT-6 Luna: high for focused lanes, low for latency-first lanes.
|
|
595
|
-
{
|
|
596
|
-
name: "gpt6_luna_high",
|
|
597
|
-
lines: ['model = "gpt-6-luna"', 'model_reasoning_effort = "high"'],
|
|
598
|
-
},
|
|
599
|
-
{
|
|
600
|
-
name: "gpt6_luna_low",
|
|
601
|
-
lines: ['model = "gpt-6-luna"', 'model_reasoning_effort = "low"'],
|
|
602
|
-
},
|
|
603
|
-
];
|
|
576
|
+
const REQUIRED_CODEX_PROFILES = Object.entries(CODEX_FAMILY_PROFILES).map(
|
|
577
|
+
([name, { model, effort }]) => ({
|
|
578
|
+
name,
|
|
579
|
+
lines: [`model = "${model}"`, `model_reasoning_effort = "${effort}"`],
|
|
580
|
+
}),
|
|
581
|
+
);
|
|
604
582
|
|
|
605
583
|
// Codex 0.134 마이그레이션: triflux 가 과거에 inline [profiles.*] 로 썼던 구형 프로필.
|
|
606
584
|
// config.toml 에서 제거 대상 (retired 모델 — 별도 파일로 만들지 않는다).
|
|
@@ -1586,19 +1564,12 @@ function ensureCodexProfiles() {
|
|
|
1586
1564
|
// 키)을 읽고, config.toml 의 inline [profiles.X] 테이블은 거부한다. 따라서
|
|
1587
1565
|
// (a) 각 프로필을 별도 파일로 쓰고 (b) config.toml 의 inline 테이블을 제거한다.
|
|
1588
1566
|
// ref: developers.openai.com/codex/config-advanced#profiles
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
? readFileSync(profilePath, "utf8")
|
|
1594
|
-
: null;
|
|
1595
|
-
if (existingContent !== desiredContent) {
|
|
1596
|
-
writeFileSync(profilePath, desiredContent, CODEX_FILE_WRITE);
|
|
1597
|
-
changed++;
|
|
1598
|
-
}
|
|
1599
|
-
}
|
|
1567
|
+
changed += ensureManagedCodexProfiles({
|
|
1568
|
+
home: _TFX_HOME,
|
|
1569
|
+
codexHome: CODEX_DIR,
|
|
1570
|
+
}).changed;
|
|
1600
1571
|
// config.toml 의 모든 inline [profiles.*] 마이그레이션 (Codex 0.134 는 inline 거부).
|
|
1601
|
-
// - 관리
|
|
1572
|
+
// - 관리 가족 별칭과 옛 이름: 위에서 별도 파일을 썼으므로 inline 만 제거.
|
|
1602
1573
|
// - 구형 retired (codex53/spark53/gpt54/mini54): inline 제거, 재생성 안 함.
|
|
1603
1574
|
// - 그 외 사용자 커스텀: 본문을 보존해 별도 파일로 이관한 뒤 inline 제거.
|
|
1604
1575
|
// (별도 파일이 이미 있으면 덮어쓰지 않는다 — 사용자 수정 보존.)
|
|
@@ -1607,7 +1578,10 @@ function ensureCodexProfiles() {
|
|
|
1607
1578
|
);
|
|
1608
1579
|
const retiredProfileNames = new Set(LEGACY_CODEX_PROFILE_NAMES);
|
|
1609
1580
|
for (const name of listInlineProfileNames(updated)) {
|
|
1610
|
-
if (
|
|
1581
|
+
if (
|
|
1582
|
+
!managedProfileNames.has(normalizeCodexProfileName(name)) &&
|
|
1583
|
+
!retiredProfileNames.has(name)
|
|
1584
|
+
) {
|
|
1611
1585
|
const customPath = join(CODEX_DIR, `${name}.config.toml`);
|
|
1612
1586
|
if (!existsSync(customPath)) {
|
|
1613
1587
|
const lines = extractProfileLines(updated, name);
|
|
@@ -2432,7 +2406,7 @@ async function runDeferredWithIo(stdinData, io) {
|
|
|
2432
2406
|
const geminiDir = join(_TFX_HOME, ".gemini");
|
|
2433
2407
|
if (!isProtectedSetupEnv() && existsSync(geminiDir)) {
|
|
2434
2408
|
geminiProfilesResult = ensureGeminiProfiles({ geminiDir });
|
|
2435
|
-
if (geminiProfilesResult.ok && geminiProfilesResult.
|
|
2409
|
+
if (geminiProfilesResult.ok && geminiProfilesResult.changed) synced++;
|
|
2436
2410
|
else if (!geminiProfilesResult.ok)
|
|
2437
2411
|
io.warn(
|
|
2438
2412
|
"gemini-profiles",
|
package/scripts/tfx-route.sh
CHANGED
|
@@ -903,96 +903,26 @@ capture_workspace_signature() {
|
|
|
903
903
|
}
|
|
904
904
|
|
|
905
905
|
# ── Gemini 모델 프로필 해석 ──
|
|
906
|
-
_GEMINI_PROFILE_CACHE=""
|
|
907
906
|
resolve_gemini_profile() {
|
|
908
|
-
local profile="$1"
|
|
909
|
-
if [[ "$profile" == gemini-* ]]; then
|
|
910
|
-
echo "$profile"
|
|
911
|
-
return
|
|
912
|
-
fi
|
|
913
|
-
if [[ -z "$_GEMINI_PROFILE_CACHE" && -f "$GEMINI_PROFILES_PATH" ]]; then
|
|
914
|
-
_GEMINI_PROFILE_CACHE=$(cat "$GEMINI_PROFILES_PATH" 2>/dev/null || echo "{}")
|
|
915
|
-
fi
|
|
916
907
|
local result
|
|
917
|
-
result=$("$NODE_BIN" -e
|
|
918
|
-
|
|
919
|
-
const
|
|
920
|
-
const
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
};
|
|
925
|
-
|
|
926
|
-
if (typeof name === 'string' && name.startsWith('gemini-')) {
|
|
927
|
-
process.stdout.write(name);
|
|
928
|
-
process.exit(0);
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
const parseJson = (raw) => {
|
|
932
|
-
try {
|
|
933
|
-
const parsed = JSON.parse(raw);
|
|
934
|
-
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
935
|
-
} catch {
|
|
936
|
-
return {};
|
|
937
|
-
}
|
|
938
|
-
};
|
|
939
|
-
|
|
940
|
-
const getModelValue = (entry) => {
|
|
941
|
-
if (!entry) return '';
|
|
942
|
-
if (typeof entry === 'string') return entry;
|
|
943
|
-
if (typeof entry !== 'object') return '';
|
|
944
|
-
if (typeof entry.model === 'string') return entry.model;
|
|
945
|
-
if (typeof entry.name === 'string' && entry.name.startsWith('gemini-')) return entry.name;
|
|
946
|
-
if (entry.model && typeof entry.model.name === 'string') return entry.model.name;
|
|
947
|
-
return '';
|
|
948
|
-
};
|
|
949
|
-
|
|
950
|
-
const getProfileBuckets = (cfg) => {
|
|
951
|
-
const buckets = [];
|
|
952
|
-
if (cfg.profiles && typeof cfg.profiles === 'object') buckets.push(cfg.profiles);
|
|
953
|
-
if (cfg.model?.profiles && typeof cfg.model.profiles === 'object') buckets.push(cfg.model.profiles);
|
|
954
|
-
if (cfg.modelProfiles && typeof cfg.modelProfiles === 'object') buckets.push(cfg.modelProfiles);
|
|
955
|
-
if (cfg.models && typeof cfg.models === 'object') buckets.push(cfg.models);
|
|
956
|
-
return buckets;
|
|
957
|
-
};
|
|
958
|
-
|
|
959
|
-
const getDefaultModel = (cfg) => {
|
|
960
|
-
return (
|
|
961
|
-
(typeof cfg.defaultModel === 'string' && cfg.defaultModel) ||
|
|
962
|
-
(typeof cfg.default_profile === 'string' && cfg.default_profile) ||
|
|
963
|
-
(typeof cfg.defaultProfile === 'string' && cfg.defaultProfile) ||
|
|
964
|
-
(typeof cfg.model === 'string' && cfg.model) ||
|
|
965
|
-
(typeof cfg.model?.default === 'string' && cfg.model.default) ||
|
|
966
|
-
''
|
|
967
|
-
);
|
|
968
|
-
};
|
|
969
|
-
|
|
970
|
-
const sources = [parseJson(primaryRaw)];
|
|
971
|
-
for (const cfg of sources) {
|
|
972
|
-
for (const bucket of getProfileBuckets(cfg)) {
|
|
973
|
-
const value = getModelValue(bucket[name]);
|
|
974
|
-
if (value) {
|
|
975
|
-
process.stdout.write(value);
|
|
976
|
-
process.exit(0);
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
if (name === 'default') {
|
|
982
|
-
for (const cfg of sources) {
|
|
983
|
-
const value = getDefaultModel(cfg);
|
|
984
|
-
if (value) {
|
|
985
|
-
process.stdout.write(value);
|
|
986
|
-
process.exit(0);
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
process.stdout.write(defaults[name] || defaults[process.env.TFX_GEMINI_DEFAULT_PROFILE] || defaults.flash38);
|
|
992
|
-
" "$profile" "$_GEMINI_PROFILE_CACHE" 2>/dev/null)
|
|
908
|
+
result=$("$NODE_BIN" --input-type=module -e '
|
|
909
|
+
import { pathToFileURL } from "node:url";
|
|
910
|
+
const { ensureGeminiProfiles, resolveGeminiModel } = await import(pathToFileURL(process.argv[1]).href);
|
|
911
|
+
const opts = { profilesPath: process.argv[3], command: process.argv[4] };
|
|
912
|
+
ensureGeminiProfiles(opts);
|
|
913
|
+
process.stdout.write(resolveGeminiModel(process.argv[2], opts));
|
|
914
|
+
' "${CODEX_PROFILE_MODULE%/*}/gemini-profiles.mjs" "$1" "$GEMINI_PROFILES_PATH" "$CLI_CMD" 2>/dev/null)
|
|
993
915
|
echo "${result:-Gemini 3.8 Flash (Medium)}"
|
|
994
916
|
}
|
|
995
917
|
|
|
918
|
+
refresh_codex_profiles() {
|
|
919
|
+
"$NODE_BIN" --input-type=module -e '
|
|
920
|
+
import { pathToFileURL } from "node:url";
|
|
921
|
+
const { ensureManagedCodexProfiles } = await import(pathToFileURL(process.argv[1]).href);
|
|
922
|
+
ensureManagedCodexProfiles({ command: process.argv[2] });
|
|
923
|
+
' "$CODEX_PROFILE_MODULE" "$CLI_CMD" 2>/dev/null || true
|
|
924
|
+
}
|
|
925
|
+
|
|
996
926
|
# ── 라우팅 테이블 ──
|
|
997
927
|
# CLI_TYPE/CLI_CMD는 agent-map, 역할별 설정은 agent-route-policy에서 조회한다.
|
|
998
928
|
# 반환: CLI_TYPE, CLI_CMD, CLI_ARGS, CLI_EFFORT, DEFAULT_TIMEOUT
|
|
@@ -1258,11 +1188,11 @@ resolve_safe_retry_codex_profile() {
|
|
|
1258
1188
|
local profile
|
|
1259
1189
|
profile="$1"
|
|
1260
1190
|
case "$profile" in
|
|
1261
|
-
ultra|
|
|
1262
|
-
echo "
|
|
1191
|
+
ultra|astra_ultra|*_ultra)
|
|
1192
|
+
echo "astra_max"
|
|
1263
1193
|
return
|
|
1264
1194
|
;;
|
|
1265
|
-
|
|
1195
|
+
luna_low|luna_high|sol_med|sol_high|astra_xhigh|astra_max)
|
|
1266
1196
|
echo "$profile"
|
|
1267
1197
|
return
|
|
1268
1198
|
;;
|
|
@@ -1272,14 +1202,14 @@ resolve_safe_retry_codex_profile() {
|
|
|
1272
1202
|
codex_home="${CODEX_HOME:-${TFX_CODEX_HOME:-${HOME:-}/.codex}}"
|
|
1273
1203
|
profile_file="${codex_home}/${profile}.config.toml"
|
|
1274
1204
|
if [[ ! -f "$profile_file" ]]; then
|
|
1275
|
-
echo "
|
|
1205
|
+
echo "sol_high"
|
|
1276
1206
|
return 0
|
|
1277
1207
|
fi
|
|
1278
1208
|
local effort
|
|
1279
1209
|
effort="$(read_codex_profile_effort "$profile")"
|
|
1280
1210
|
case "$effort" in
|
|
1281
|
-
ultra) echo "
|
|
1282
|
-
"") echo "
|
|
1211
|
+
ultra) echo "astra_max" ;;
|
|
1212
|
+
"") echo "sol_high" ;;
|
|
1283
1213
|
*) echo "$profile" ;;
|
|
1284
1214
|
esac
|
|
1285
1215
|
}
|
|
@@ -1328,18 +1258,18 @@ apply_codex_concrete_effort_guard() {
|
|
|
1328
1258
|
[[ -n "$profile" ]] || return 0
|
|
1329
1259
|
|
|
1330
1260
|
local allow_ultra=0
|
|
1331
|
-
if [[ "$CODEX_OVERRIDE_PROFILE" == "
|
|
1261
|
+
if [[ "$CODEX_OVERRIDE_PROFILE" == "astra_ultra" && -z "${TFX_RETRY_SNAPSHOT:-${TFX_RETRY_SNAPSHOT_FILE:-}}" ]]; then
|
|
1332
1262
|
allow_ultra=1
|
|
1333
1263
|
fi
|
|
1334
1264
|
local effort target_profile=""
|
|
1335
1265
|
effort="$(read_codex_profile_effort "$profile")"
|
|
1336
1266
|
case "$profile" in
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
*) [[ "$effort" != "ultra" ]] || target_profile="
|
|
1267
|
+
astra_max) target_profile="astra_max" ;;
|
|
1268
|
+
astra_ultra) target_profile="astra_max" ;;
|
|
1269
|
+
*) [[ "$effort" != "ultra" ]] || target_profile="astra_max" ;;
|
|
1340
1270
|
esac
|
|
1341
1271
|
[[ -n "$target_profile" ]] || return 0
|
|
1342
|
-
[[ "$allow_ultra" -ne 1 ]] || target_profile="
|
|
1272
|
+
[[ "$allow_ultra" -ne 1 ]] || target_profile="astra_ultra"
|
|
1343
1273
|
for ((i = 0; i < ${#args[@]}; i++)); do
|
|
1344
1274
|
case "${args[$i]}" in
|
|
1345
1275
|
--profile) args[$((i + 1))]="$target_profile" ;;
|
|
@@ -1944,7 +1874,7 @@ run_antigravity_exec() {
|
|
|
1944
1874
|
# "${agy_args[@]}" expand 시 단일 인자로 보존된다. 모델 SSOT 는 프로필 설정이다.
|
|
1945
1875
|
if [[ -z "${TFX_GEMINI_NO_MODEL:-}" ]]; then
|
|
1946
1876
|
local _agy_model
|
|
1947
|
-
_agy_model="$(resolve_gemini_profile "${TFX_GEMINI_PROFILE:-${GEMINI_PROFILE:-
|
|
1877
|
+
_agy_model="$(resolve_gemini_profile "${TFX_GEMINI_PROFILE:-${GEMINI_PROFILE:-flash}}")"
|
|
1948
1878
|
if [[ -n "$_agy_model" ]]; then
|
|
1949
1879
|
agy_args+=("--model" "$_agy_model")
|
|
1950
1880
|
fi
|
|
@@ -2118,6 +2048,7 @@ _codex_stdout_has_meaningful_output() {
|
|
|
2118
2048
|
}
|
|
2119
2049
|
|
|
2120
2050
|
run_codex_exec() {
|
|
2051
|
+
refresh_codex_profiles
|
|
2121
2052
|
local prompt="$1"
|
|
2122
2053
|
local use_tee_flag="$2"
|
|
2123
2054
|
local exit_code_local=0
|
|
@@ -2152,7 +2083,7 @@ run_codex_exec() {
|
|
|
2152
2083
|
if [[ "$exit_code_local" -ne 0 ]] && grep -qE "is not supported when using Codex with a ChatGPT account" "$STDERR_LOG" 2>/dev/null; then
|
|
2153
2084
|
local fallback_profile=""
|
|
2154
2085
|
case "$CLI_EFFORT" in
|
|
2155
|
-
|
|
2086
|
+
astra_ultra|astra_max|astra_xhigh|sol_high|sol_med|luna_high) fallback_profile="luna_low" ;;
|
|
2156
2087
|
esac
|
|
2157
2088
|
if [[ -n "$fallback_profile" ]]; then
|
|
2158
2089
|
echo "[tfx-route] tier fallback: $CLI_EFFORT not supported on ChatGPT account → retry with $fallback_profile" >&2
|
package/skills/tfx-auto/SKILL.md
CHANGED
|
@@ -166,7 +166,7 @@ agy 레인은 `TFX_AGY_ANTI_OVERCLAIM`(기본 on) 으로 완료/grounding 규율
|
|
|
166
166
|
|
|
167
167
|
`DEFAULT_ESCALATION_CHAIN`의 프로필 설정은 `.claude/rules/tfx-escalation-chain.md`와 CLI 프로필 설정을 따른다.
|
|
168
168
|
|
|
169
|
-
1. Codex: `
|
|
169
|
+
1. Codex: `astra_max` 프로필
|
|
170
170
|
2. Claude: 최종 수단 프로필
|
|
171
171
|
|
|
172
172
|
체인 규칙:
|
package/skills/tfx-lead/SKILL.md
CHANGED
|
@@ -50,14 +50,14 @@ tmux ls # 없으면
|
|
|
50
50
|
| 역할 | 모델 | 비고 |
|
|
51
51
|
| --- | --- | --- |
|
|
52
52
|
| 리드 (Claude) | Claude 최상위 tier, effort high | 지휘와 판정만 맡는다 |
|
|
53
|
-
| 리드 (Codex) | `
|
|
53
|
+
| 리드 (Codex) | `astra_xhigh` | 지휘와 판정만 맡는다 |
|
|
54
54
|
| 판단 중심 긴 작업 | Claude `opus` | 설계 검토, 긴 구현 |
|
|
55
55
|
| 반복 검증, 정형 문서 | Claude `sonnet` | 테스트 재실행, 표 갱신 |
|
|
56
56
|
| `opus`로 안 풀리는 난제 | Claude `fable` | 최종 수단 |
|
|
57
|
-
| Codex 설계, 비평, 디버그 | `
|
|
58
|
-
| Codex 일반 구현, 리뷰 | `
|
|
59
|
-
| Codex 정리 | `
|
|
60
|
-
| Codex 반복 작업 | `
|
|
57
|
+
| Codex 설계, 비평, 디버그 | `astra_xhigh` | |
|
|
58
|
+
| Codex 일반 구현, 리뷰 | `sol_high` | |
|
|
59
|
+
| Codex 정리 | `sol_med` | |
|
|
60
|
+
| Codex 반복 작업 | `luna_high` | |
|
|
61
61
|
| Codex 시험용 | 역할 프로필에 effort low | 연결 확인용 |
|
|
62
62
|
|
|
63
63
|
- Codex 주간 한도가 낮으면 새 Codex 워커 대신 Claude 로 돌린다.
|
package/skills/tfx-live/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: tfx-live
|
|
|
3
3
|
description: >
|
|
4
4
|
Claude·Codex·agy 세션을 실시간으로 생성·질의·대기·압축·종료하거나,
|
|
5
5
|
tmux/daemon UDS 연결과 peer 중계를 운영할 때 사용한다.
|
|
6
|
-
argument-hint: "<start|ask|wait|queue|compact|stop|rename|interrupt|probe|list-sessions|peer|converse|goal-driven> [--cli codex|claude|agy] ..."
|
|
6
|
+
argument-hint: "<start|ask|wait|queue|goal|compact|stop|rename|interrupt|probe|list-sessions|peer|converse|goal-driven> [--cli codex|claude|agy] ..."
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# tfx-live
|
|
@@ -53,11 +53,13 @@ agy `ask`의 기본 `--if-busy queue`는 바쁜 턴에도 바로 전송한다. a
|
|
|
53
53
|
|
|
54
54
|
| 동작 | 적용 조건과 실행 |
|
|
55
55
|
| --- | --- |
|
|
56
|
-
| compact | 같은 목표와 담당이 이어지고 파일과 git에서 상태를 복구할 수 있을 때. Claude는 `tfx-live compact
|
|
56
|
+
| compact | 같은 목표와 담당이 이어지고 파일과 git에서 상태를 복구할 수 있을 때. Claude와 Codex는 `tfx-live compact --then "다음 지시" --no-wait`로 압축 제출을 확인하고 다음 지시를 미리 넣는다. 압축 뒤에 필요한 파일 경로와 검증 명령도 `--then`에 싣는다. agy는 수동 compact가 없어 handoff한다. |
|
|
57
57
|
| clear | 무관한 작업으로 전환하거나 같은 문제를 두 번 넘게 고쳐도 실패할 때, 스펙에서 구현으로 또는 작성자에서 리뷰어로 바뀔 때. 미완료 상태를 정리한 뒤 Claude는 `/rename`과 `/clear`, Codex는 작업 종료 뒤 `/new <이름>`, agy는 `/clear`를 쓴다. |
|
|
58
58
|
| handoff | 담당·CLI·모델·소유자가 바뀌거나 긴 작업이 창을 넘길 때. 인계 파일에 목표, 완료 조건, 체크아웃과 변경 상태, 확정 결정, 증거 경로, PASS/FAIL/SKIP, 미완료 작업과 프로세스, 다음 행동과 소유자, `predecessorSessionId`를 적는다. 승계 세션이 경로를 확인하면 이전 세션을 닫는다. |
|
|
59
59
|
| close | 결과와 인계를 확인한 즉시 닫는다. Claude 백그라운드는 `claude stop <id>`, tmux는 `tfx-live stop --session`, Claude 팀원은 shutdown, Codex 서브에이전트는 `close_agent`, agy 서브에이전트는 `/agents` 패널의 K를 쓴다. |
|
|
60
60
|
|
|
61
|
+
Claude TUI는 바쁠 때 들어온 입력을 대기열에 넣고 현재 턴(압축 포함)이 끝나면 순서대로 처리한다. transcript에는 `queue-operation`의 `enqueue`와 `dequeue`로 남는다. Codex app-server 큐도 바쁜 턴 뒤에 순서대로 처리한다. 리드는 compact를 보내고 `--then`으로 다음 지시를 넣는 것을 기본 절차로 쓴다.
|
|
62
|
+
|
|
61
63
|
두 번째 compact 전에는 handoff가 맞는지 점검한다. compact 횟수는 고정하지 않는다.
|
|
62
64
|
|
|
63
65
|
리드 역할 절차(역할별 모델, 지시서, 교차 리뷰, 릴리스 조율)는 `tfx-lead` 스킬을 따른다.
|
|
@@ -80,6 +82,23 @@ tfx-live queue --cli codex --thread <UUID> --delete <queuedMessageId>
|
|
|
80
82
|
|
|
81
83
|
`queue`는 TUI가 아직 가져가지 않은 항목을 보고 지운다. Codex app-server의 실험 API `thread/queue/list`·`thread/queue/delete`를 stdio로 호출하며 큐 저장소 파일은 건드리지 않는다. 목록 항목은 `id`(ask 결과의 `queuedMessageId`와 같다), `requestId`, `text`다. 삭제 결과의 `missing`은 이미 TUI가 가져갔거나 없는 항목이다. 유휴 TUI는 쌓인 항목을 곧 가져가므로 지울 수 있는 것은 사실상 바쁜 세션과 닫힌 세션의 항목이다. 가져간 뒤의 처리 여부는 `wait`로 확인한다.
|
|
82
84
|
|
|
85
|
+
### 네이티브 goal
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
tfx-live goal set --session cl1 --condition "관련 단위 테스트가 모두 통과한다"
|
|
89
|
+
tfx-live goal set --session cx1 --cli codex --condition "관련 단위 테스트가 모두 통과한다" --budget 100000
|
|
90
|
+
tfx-live goal status --session cx1
|
|
91
|
+
tfx-live goal clear --session cx1
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`--cli`를 생략하면 세션 프로세스에서 추정한다. 로컬 Claude와 Codex만 지원하며 `--remote`와 agy는 거부한다. `--budget`은 Codex set의 daemon 경로에서만 받고 Claude와 tmux 폴백에서는 오류로 거부한다.
|
|
95
|
+
|
|
96
|
+
Claude는 tmux로 `/goal <조건>`과 `/goal clear`를 보내고 transcript의 마지막 `goal_status`로 상태를 읽는다. 별도 평가자의 완료는 `complete`, 수동 해제는 `none`이며 `judgedBy: "evaluator"`와 평가 이유를 보고한다. transcript를 찾지 못하면 상태는 `null`이고 경고를 남긴다. set/clear 뒤 새 sentinel을 최대 15초 확인하며 없으면 `confirmed: false`와 경고를 반환한다. 이미 해제된 goal의 clear는 입력 없이 `alreadyCleared: true`로 답한다. set은 기본 `--if-busy wait`, clear는 미충족 goal이 턴을 계속 이어 가도 끊을 수 있게 기본 `--if-busy interrupt`다. 평가자가 해제하지 않고 멈춘 goal은 `active`로 남으므로 리드가 `goal clear`로 정리한다.
|
|
97
|
+
|
|
98
|
+
Codex는 공유 daemon UDS의 `thread/goal/*`를 우선 쓴다. `--codex-socket PATH|default`로 소켓을 고르고 기존 thread 해석 순서와 `--thread UUID`를 따른다. 새 goal은 `origin: "user"`로 clear 후 active set한다. 소켓이 없거나 thread가 미적재면 set/clear는 tmux 슬래시로, status는 `$CODEX_HOME/goals_1.sqlite`의 읽기 전용 조회로 폴백한다. `node:sqlite`를 지원하지 않는 Node에서는 상태를 `null`로 두고 경고한다. 결과에 `transport: "daemon"|"tmux"|"sqlite"`와 폴백 이유를 남긴다. tmux 폴백의 set/clear는 sqlite로 최대 15초 확인하고, 확인하지 못하면 재전송을 막으려고 오류 대신 `confirmed: false`와 경고를 반환한다. Codex의 `judgedBy: "model"`, `complete`는 모델 자기 판정이므로 리드의 교차 검증이 필요하다.
|
|
99
|
+
|
|
100
|
+
Claude와 Codex는 네이티브 goal을 우선 쓰고, goal-driven은 agy처럼 네이티브 goal이 없을 때 쓰는 방식이다. 현재 agy의 goal-driven 실행은 지원하지 않는다.
|
|
101
|
+
|
|
83
102
|
### 컨텍스트 판단
|
|
84
103
|
|
|
85
104
|
분모는 공식 모델 창이고 실행 한도가 더 작으면 그 한도 아래에서 운영한다. 아래 비율은 공식 권고가 아닌 **[추론] 운영값**이다. 모델 문서가 바뀌면 한도도 갱신한다.
|
|
@@ -99,13 +118,19 @@ Claude는 85% 도달 시 바로 handoff를 지시한다. agy 행은 운영 지
|
|
|
99
118
|
### 수동 compact와 종료
|
|
100
119
|
|
|
101
120
|
```bash
|
|
102
|
-
tfx-live compact --cli claude --session cl1 --instructions "변경 파일과 검증 명령을 보존하라" --
|
|
121
|
+
tfx-live compact --cli claude --session cl1 --instructions "변경 파일과 검증 명령을 보존하라" --then "인계 파일을 읽고 검증을 이어가라" --no-wait
|
|
122
|
+
tfx-live compact --cli codex --session cx1 --then "src/main.mjs를 확인하고 지정된 단위 테스트를 실행하라" --no-wait
|
|
123
|
+
tfx-live wait --cli codex --session cx1 --request-id <then.requestId> --timeout 120
|
|
103
124
|
tfx-live stop --cli claude --short <8hex>
|
|
104
125
|
tfx-live stop --cli claude --session-id <id>
|
|
105
126
|
tfx-live stop --session cl1
|
|
106
127
|
```
|
|
107
128
|
|
|
108
|
-
`compact`는 로컬
|
|
129
|
+
`compact --session S [--cli claude|codex] [--instructions TEXT] [--then PROMPT] [--no-wait]`는 로컬 tmux에서 `/compact`를 중계 표식 없이 보낸다. CLI 기본값은 Claude다. `--instructions`는 Claude에서만 받으며 Codex는 인자를 받지 않아 거부한다. 기본 `--if-busy fail`이며 바쁜 세션에는 compact를 전송하지 않는다. UDS compact는 지원하지 않는다.
|
|
130
|
+
|
|
131
|
+
`--no-wait`는 압축 진행 표시 또는 새 완료 기록으로 제출을 확인한 뒤 반환한다. Codex에서 확인되지 않으면 Enter를 한 번 더 보내고, 그래도 확인되지 않으면 `--then`을 보내지 않고 오류로 끝낸다. `--then`은 일반 텍스트 지시만 받으며 슬래시 명령은 거부한다. 제출 확인 직후 Claude에는 표식을 붙여 tmux로 보내고 화면의 `queued messages` 또는 transcript의 `enqueue`로 확인한다. Claude 압축이 먼저 끝나 지시가 이미 처리에 들어갔으면 `then.queued: false, delivered: true`로 보고한다. Codex에는 기존 queue 경로로만 보내며 tmux로 전환하지 않는다. 결과의 `then.requestId`로 `wait --request-id`에서 압축 뒤 응답을 받는다.
|
|
132
|
+
|
|
133
|
+
`--no-wait`가 없으면 Claude의 새 `compact_boundary`, Codex rollout의 새 `compacted`까지 기다린다. 결과에 `compact: {submitted: true, done}`과 `then: {requestId, queued, transport}`를 추가하고 기존 `compacted`, `preTokens`, `postTokens`를 유지한다. 토큰 수는 기록에 있는 값만 보고하며 Codex의 압축 후 token_count가 아직 없으면 `postTokens`는 `null`이다. 시간 안에 완료 기록이 없으면 `compacted: false, timedOut: true`다.
|
|
109
134
|
|
|
110
135
|
`stop --short`와 `stop --session-id`는 `claude stop <id>`를 호출하고 `stopped: true, conversationKept: true`를 반환한다. `claude attach`로 대화를 재개할 수 있다. `/exit`, `←`, Ctrl+Z는 백그라운드 세션의 연결만 끊는다. macOS의 `sysctl -n kern.memorystatus_vm_pressure_level`이 2 이상이면 새 세션을 띄우기 전 정리한다. 원격 배정 전에는 `tfx-remote probe <host>`의 `ready`와 `warnings`를 확인한다.
|
|
111
136
|
|
|
@@ -120,7 +145,7 @@ tfx-live ask --cli codex --session cx1 --prompt "진행 상황을 보고해줘"
|
|
|
120
145
|
tfx-live wait --cli codex --session cx1 --request-id <requestId> --timeout 120
|
|
121
146
|
```
|
|
122
147
|
|
|
123
|
-
`ask --no-wait`는 Claude와 tmux에서 `status: "submitted"`, Codex queue에서 `status: "queued"`와 함께 `inputSent: true`, `done: false`, `submittedAt`, `target`, `requestId`를 반환한다. `ask`, `peer`, `converse`, `goal-driven`은 `[tfx-live req=<requestId>]`를 프롬프트 앞에 붙인다. `--no-relay-tag`로 제거하면 `wait --request-id`가 해당 요청을 찾을 수 없다.
|
|
148
|
+
`ask --no-wait`는 Claude와 tmux에서 `status: "submitted"`, Codex queue에서 `status: "queued"`와 함께 `inputSent: true`, `done: false`, `submittedAt`, `target`, `requestId`를 반환한다. `ask`, `peer`, `converse`, `goal-driven`은 `[tfx-live req=<requestId>]`를 프롬프트 앞에 붙인다. `--no-relay-tag`로 제거하면 `wait --request-id`가 해당 요청을 찾을 수 없다. 슬래시 명령(`/rename`, `/clear` 등)은 CLI와 상관없이 표식 없이 보낸다. 응답 턴이 없는 Claude 내장 명령(`/rename`, `/clear`, `/compact`, `/model`, `/effort`, `/color`)은 기다리지 않고 `status: "submitted"`로 반환한다. 압축 완료까지 기다리려면 `compact` 명령을 쓴다.
|
|
124
149
|
|
|
125
150
|
`wait`는 Claude transcript에서 표식을 user 메시지, `queue-operation`의 `enqueue` content, queued command attachment에서 찾는다. 표식이 소비된 턴이 끝난 뒤 마지막 assistant 텍스트를 응답으로 쓴다. 표식 없는 `wait`는 최신 user turn을 본다. `isSidechain`과 합성 API 오류 메시지는 정상 응답에서 제외한다. 요청을 소비한 턴에서 합성 오류가 나타나면 오류 문구와 `status: "failed"`를 반환한다. daemon의 `idle`만으로 완료를 단정하지 않는다. timeout은 `status: "working"`, `timedOut: true`, `done: false`다.
|
|
126
151
|
|
|
@@ -18,7 +18,7 @@ HUD의 `statusLine`이 비어 있으면 triflux HUD를 등록한다. 이미 trif
|
|
|
18
18
|
|
|
19
19
|
Codex 프로필은 `~/.codex/<이름>.config.toml`로 관리한다. 기본 설정 키는 `approval_policy`, `sandbox_mode`이며 CLI에는 `--profile`만 전달한다. 프로젝트 지침은 [CLAUDE.md](../../CLAUDE.md)의 `<codex-config>`를 따른다.
|
|
20
20
|
|
|
21
|
-
Antigravity 프로필은 `~/.gemini/triflux-profiles.json`에서
|
|
21
|
+
Antigravity 프로필은 `~/.gemini/triflux-profiles.json`에서 `flash_low`, `flash`, `flash_high` 가족 프로필을 자동 갱신한다. `agy`와 `codex`가 설치되지 않았으면 선택적 CLI 누락으로 보고한다.
|
|
22
22
|
|
|
23
23
|
mods(사용량 band, 서브에이전트 effort 강제)는 `tfx setup --mods`로 설치한다. Claude Code 2.1.287 이상이 필요하다. 기본 setup은 marketplace 등록과 안내 한 줄만 한다.
|
|
24
24
|
|