triflux 10.53.0 → 10.55.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/README.ko.md +10 -16
- package/README.md +13 -19
- package/adapters/codex/skills/tfx-live/SKILL.md +3 -3
- package/bin/tfx-live.mjs +10 -6
- package/bin/triflux.mjs +174 -127
- package/config/mcp-registry.json +1 -1
- package/hooks/codex-session-hook.mjs +1 -17
- package/hub/cli-adapter-base.mjs +3 -424
- package/hub/lib/hosts-compat.mjs +18 -64
- package/hub/lib/private-tmp.mjs +35 -0
- package/hub/lib/process-utils.mjs +19 -220
- package/hub/lib/prompt-tmp.mjs +6 -24
- package/hub/lib/spawn-trace.mjs +112 -5
- package/hub/lib/timeout-defaults.mjs +34 -6
- package/hub/lib/worker-lifecycle.mjs +3 -0
- package/hub/platform.mjs +3 -1
- package/hub/team/backend.mjs +10 -3
- package/hub/team/cli/commands/start/index.mjs +0 -2
- package/hub/team/codex-queue.mjs +49 -2
- package/hub/team/handoff.mjs +1 -1
- package/hub/team/headless.mjs +41 -57
- package/hub/team/intervention.mjs +6 -4
- package/hub/team/nativeProxy.mjs +2 -48
- package/hub/team/pane.mjs +31 -18
- package/hub/team/psmux.mjs +36 -20
- package/hub/team/remote-session.mjs +66 -56
- package/hub/team/retry-state-machine.mjs +1 -1
- package/hub/team/session.mjs +80 -59
- package/hub/team/terminal-opener.mjs +5 -19
- package/hub/team/tui-viewer.mjs +2 -2
- package/hub/team/tui.mjs +0 -6
- package/hub/workers/factory.mjs +1 -10
- package/hub/workers/interface.mjs +0 -7
- package/hud/constants.mjs +0 -3
- package/hud/providers/claude.mjs +35 -15
- package/hud/providers/codex.mjs +169 -42
- package/hud/utils.mjs +0 -23
- package/package.json +4 -11
- package/scripts/check-codex-config-stable.mjs +2 -11
- package/scripts/config-audit.mjs +0 -2
- package/scripts/doctor-diagnose.mjs +25 -20
- package/scripts/ensure-codex-hooks.mjs +70 -24
- package/scripts/lib/backup-rotation.mjs +60 -0
- package/scripts/lib/codex-profile-config.mjs +20 -7
- package/scripts/lib/codex-recovery.sh +2 -3
- package/scripts/lib/install-blob-history.json +3196 -0
- package/scripts/lib/install-retire.mjs +203 -0
- package/scripts/lib/legacy-hook-cleanup.mjs +148 -13
- package/scripts/lib/legacy-mcp-cleanup.mjs +405 -104
- package/scripts/lib/machine-profile.mjs +2 -2
- package/scripts/lib/mcp-filter.mjs +1 -42
- package/scripts/lib/mcp-guard-engine.mjs +198 -19
- package/scripts/lib/mcp-manifest.mjs +0 -40
- package/scripts/lib/process-utils.mjs +19 -0
- package/scripts/lib/stable-node.mjs +35 -0
- package/scripts/lib/test-env.mjs +20 -0
- package/scripts/lib/tmr-install.mjs +183 -0
- package/scripts/lint-private-info.mjs +137 -0
- package/scripts/mcp-check.mjs +24 -18
- package/scripts/pack.mjs +29 -42
- package/scripts/release/check-packages-mirror.mjs +77 -125
- package/scripts/release/lib.mjs +1 -1
- package/scripts/release/prepare.mjs +2 -0
- package/scripts/release/resolve-auto-release.mjs +11 -0
- package/scripts/release/version-manifest.json +7 -1
- package/scripts/remote-spawn.mjs +226 -116
- package/scripts/session-stale-cleanup.mjs +124 -75
- package/scripts/setup.mjs +414 -377
- package/scripts/test-lock.mjs +62 -8
- package/scripts/tfx-route-post.mjs +30 -4
- package/scripts/tfx-route.sh +73 -25
- package/scripts/tmp-cleanup.mjs +16 -3
- package/skills/tfx-auto/SKILL.md +12 -5
- package/skills/tfx-lead/SKILL.md +6 -6
- package/skills/tfx-live/SKILL.md +3 -3
- package/skills/tfx-remote/SKILL.md +2 -2
- package/skills/tfx-research/SKILL.md +10 -7
- package/skills/tfx-review/SKILL.md +14 -3
- package/skills/tfx-ship/SKILL.md +1 -1
- package/CLAUDE.md +0 -194
- package/cto/brief.mjs +0 -73
- package/cto/collect.mjs +0 -490
- package/cto/current.schema.json +0 -148
- package/cto/events.mjs +0 -208
- package/cto/hygiene.mjs +0 -320
- package/cto/index.mjs +0 -46
- package/cto/lake-root.mjs +0 -127
- package/cto/status.mjs +0 -124
- package/docs/assets/demo-multi.gif +0 -0
- package/hooks/agy-session-hook.mjs +0 -155
- package/hooks/claude-cwd-projection-refresh.mjs +0 -11
- package/hooks/hook-orchestrator.mjs +0 -11
- package/hooks/lib/resolve-root.mjs +0 -62
- package/hub/codex-adapter.mjs +0 -193
- package/hub/codex-preflight.mjs +0 -219
- package/hub/lib/cto-env.mjs +0 -70
- package/hub/team/notify.mjs +0 -385
- package/hub/team/process-cleanup.mjs +0 -386
- package/hub/team/routing.mjs +0 -249
- package/hub/team/staleState.mjs +0 -403
- package/hub/team/worker-signal.mjs +0 -263
- package/hub/team/worker-signal.types.d.ts +0 -52
- package/hub/workers/codex-mcp.mjs +0 -853
- package/references/codex-plugin-cc-analysis.md +0 -701
- package/references/codex-plugin-cc-code-patterns.md +0 -468
- package/scripts/__tests__/ensure-codex-hooks.test.mjs +0 -183
- package/scripts/__tests__/lint-skills.test.mjs +0 -169
- package/scripts/__tests__/mcp-guard-engine-http-headers.test.mjs +0 -223
- package/scripts/__tests__/mcp-guard-engine-policy-sync.test.mjs +0 -185
- package/scripts/__tests__/mcp-guard-engine-proactive-sync.test.mjs +0 -206
- package/scripts/__tests__/mcp-guard-engine-stdio-sync.test.mjs +0 -186
- package/scripts/__tests__/mcp-guard-engine-sync-http-headers.test.mjs +0 -270
- package/scripts/__tests__/mcp-guard-engine.test.mjs +0 -376
- package/scripts/__tests__/release-governance.test.mjs +0 -732
- package/scripts/__tests__/release-publish-ancestor.test.mjs +0 -67
- package/scripts/__tests__/release-verify-wait.test.mjs +0 -169
- package/scripts/__tests__/remote-spawn-transfer.test.mjs +0 -128
- package/scripts/__tests__/remote-spawn.test.mjs +0 -98
- package/scripts/__tests__/resolve-auto-release.test.mjs +0 -216
- package/scripts/__tests__/setup-cleanup-stale-skills.test.mjs +0 -86
- package/scripts/__tests__/spawn-trace.test.mjs +0 -269
- package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +0 -58
- package/scripts/claudemd-sync.mjs +0 -157
- package/scripts/ensure-agy-hooks.mjs +0 -163
- package/scripts/lib/remote-spawn-transfer.mjs +0 -226
- package/scripts/lib/session-presence.mjs +0 -151
- package/scripts/mcp-cleanup.ps1 +0 -26
package/bin/triflux.mjs
CHANGED
|
@@ -36,11 +36,15 @@ import {
|
|
|
36
36
|
inspectMacTimeoutDependency,
|
|
37
37
|
} from "../scripts/lib/doctor-env-checks.mjs";
|
|
38
38
|
import { ensureGeminiProfiles } from "../scripts/lib/gemini-profiles.mjs";
|
|
39
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
cleanupAgyHooks,
|
|
41
|
+
cleanupLegacyHooks,
|
|
42
|
+
} from "../scripts/lib/legacy-hook-cleanup.mjs";
|
|
40
43
|
import {
|
|
41
44
|
cleanupLegacyMcp,
|
|
42
45
|
cleanupTfxHub,
|
|
43
46
|
findProjectHubEntries,
|
|
47
|
+
pinRegistryMcpPackages,
|
|
44
48
|
} from "../scripts/lib/legacy-mcp-cleanup.mjs";
|
|
45
49
|
import {
|
|
46
50
|
addRegistryServer,
|
|
@@ -58,19 +62,21 @@ import {
|
|
|
58
62
|
formatPsmuxUpdateGuidance,
|
|
59
63
|
probePsmuxSupport,
|
|
60
64
|
} from "../scripts/lib/psmux-info.mjs";
|
|
65
|
+
import { writesRealHomeInTest } from "../scripts/lib/test-env.mjs";
|
|
61
66
|
import {
|
|
62
67
|
applyStatusLine,
|
|
68
|
+
cleanupStaleSkills,
|
|
63
69
|
ensureCodexProfiles,
|
|
64
|
-
ensureTrifluxMods,
|
|
65
70
|
getVersion,
|
|
66
|
-
|
|
71
|
+
inspectTrifluxMods,
|
|
67
72
|
isSkillSupportedOnPlatform,
|
|
68
73
|
LEGACY_CODEX_MODELS,
|
|
69
74
|
listInlineProfileNames,
|
|
75
|
+
MODS_UPDATE_COMMAND,
|
|
70
76
|
persistSettings,
|
|
71
|
-
REMOVED_SKILL_NAMES,
|
|
72
77
|
REQUIRED_CODEX_PROFILES,
|
|
73
|
-
|
|
78
|
+
retireOldInstallFiles,
|
|
79
|
+
runConsentSteps,
|
|
74
80
|
SYNC_MAP,
|
|
75
81
|
syncSkills,
|
|
76
82
|
} from "../scripts/setup.mjs";
|
|
@@ -265,16 +271,6 @@ const CLI_COMMAND_SCHEMAS = Object.freeze({
|
|
|
265
271
|
},
|
|
266
272
|
},
|
|
267
273
|
},
|
|
268
|
-
cto: {
|
|
269
|
-
usage: "tfx cto <collect|status|hygiene> [options]",
|
|
270
|
-
description: "repo-local authority layer console",
|
|
271
|
-
subcommands: {
|
|
272
|
-
collect: "refresh .triflux/lake/current.json from authority sources",
|
|
273
|
-
status: "print the current authority summary",
|
|
274
|
-
hygiene:
|
|
275
|
-
"project CTO hygiene counts and actionable dry-run rows (--dry-run)",
|
|
276
|
-
},
|
|
277
|
-
},
|
|
278
274
|
multi: {
|
|
279
275
|
usage:
|
|
280
276
|
"tfx multi [--dashboard-layout lite|single|split-2col|split-3col|auto] <subcommand|task>",
|
|
@@ -311,15 +307,10 @@ const CLI_COMMAND_SCHEMAS = Object.freeze({
|
|
|
311
307
|
},
|
|
312
308
|
},
|
|
313
309
|
update: {
|
|
314
|
-
usage: "tfx update
|
|
310
|
+
usage: "tfx update",
|
|
315
311
|
description:
|
|
316
312
|
"설치 방식(plugin/npm/git)을 감지해 triflux를 업데이트하고 setup/cache를 재동기화",
|
|
317
313
|
options: [
|
|
318
|
-
{
|
|
319
|
-
name: "--dev / dev",
|
|
320
|
-
type: "boolean",
|
|
321
|
-
description: "npm 설치 모드에서 dev tag로 업데이트",
|
|
322
|
-
},
|
|
323
314
|
{
|
|
324
315
|
name: "--help",
|
|
325
316
|
type: "boolean",
|
|
@@ -550,12 +541,6 @@ function whichInShell(cmd, shell) {
|
|
|
550
541
|
}
|
|
551
542
|
}
|
|
552
543
|
|
|
553
|
-
function isDevUpdateRequested(argv = process.argv) {
|
|
554
|
-
return (
|
|
555
|
-
argv.includes("--dev") || argv.includes("@dev") || argv.includes("dev")
|
|
556
|
-
);
|
|
557
|
-
}
|
|
558
|
-
|
|
559
544
|
function checkShellAvailable(shell) {
|
|
560
545
|
const cmds = {
|
|
561
546
|
bash: "bash --version",
|
|
@@ -703,7 +688,7 @@ function sumMemoryEstimateMb(panes, { execFile = execFileSync } = {}) {
|
|
|
703
688
|
return seen ? total : null;
|
|
704
689
|
}
|
|
705
690
|
|
|
706
|
-
|
|
691
|
+
function inspectDetachedTmuxSessions({
|
|
707
692
|
prefix = DEFAULT_TMUX_CLEANUP_PREFIX,
|
|
708
693
|
ageMin = DEFAULT_TMUX_CLEANUP_AGE_MIN,
|
|
709
694
|
platform = process.platform,
|
|
@@ -782,7 +767,7 @@ export function inspectDetachedTmuxSessions({
|
|
|
782
767
|
};
|
|
783
768
|
}
|
|
784
769
|
|
|
785
|
-
|
|
770
|
+
async function cleanupDetachedTmuxSessions({
|
|
786
771
|
sessions,
|
|
787
772
|
dryRun = true,
|
|
788
773
|
apply = false,
|
|
@@ -1129,12 +1114,6 @@ function listSkillSyncActions() {
|
|
|
1129
1114
|
if (!isSkillSupportedOnPlatform(join(skillsSrc, name))) continue;
|
|
1130
1115
|
actions.push(describeSyncAction(src, dst, `skill:${name}`));
|
|
1131
1116
|
}
|
|
1132
|
-
for (const { alias, source } of SKILL_ALIASES) {
|
|
1133
|
-
const src = join(skillsSrc, source, "SKILL.md");
|
|
1134
|
-
const dst = join(CLAUDE_DIR, "skills", alias, "SKILL.md");
|
|
1135
|
-
if (!existsSync(src)) continue;
|
|
1136
|
-
actions.push(describeSyncAction(src, dst, `skill-alias:${alias}`));
|
|
1137
|
-
}
|
|
1138
1117
|
return actions;
|
|
1139
1118
|
}
|
|
1140
1119
|
|
|
@@ -1242,7 +1221,7 @@ function reportSkillSync() {
|
|
|
1242
1221
|
ok(`스킬: ${result.total}개 확인, 파일 ${result.changed}개 반영`);
|
|
1243
1222
|
}
|
|
1244
1223
|
|
|
1245
|
-
function cmdSetup(options = {}) {
|
|
1224
|
+
async function cmdSetup(options = {}) {
|
|
1246
1225
|
const {
|
|
1247
1226
|
dryRun = false,
|
|
1248
1227
|
fromUpdate = false,
|
|
@@ -1253,6 +1232,10 @@ function cmdSetup(options = {}) {
|
|
|
1253
1232
|
printJson(buildSetupDryRunPlan());
|
|
1254
1233
|
return;
|
|
1255
1234
|
}
|
|
1235
|
+
if (writesRealHomeInTest()) {
|
|
1236
|
+
info("setup: skip (테스트가 홈을 격리하지 않음)");
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1256
1239
|
|
|
1257
1240
|
const cleanup = cleanupLegacyHooks({
|
|
1258
1241
|
settingsPath: join(CLAUDE_DIR, "settings.json"),
|
|
@@ -1264,13 +1247,24 @@ function cmdSetup(options = {}) {
|
|
|
1264
1247
|
fix: `${join(CLAUDE_DIR, "settings.json")}의 JSON 문법과 쓰기 권한을 확인하세요.`,
|
|
1265
1248
|
});
|
|
1266
1249
|
}
|
|
1250
|
+
// agy 는 hooks.json 의 훅을 실행하므로 지워진 훅 스크립트를 가리키는 항목을 남기지 않는다.
|
|
1251
|
+
const agyCleanup = cleanupAgyHooks();
|
|
1252
|
+
if (agyCleanup.changed) ok("agy 옛 triflux-session 훅 정리됨");
|
|
1253
|
+
else if (!agyCleanup.ok) warn(`agy 옛 훅 정리 실패: ${agyCleanup.error}`);
|
|
1267
1254
|
// 이주가 막혀도 setup 은 계속한다. 남은 항목은 경고로 알린다.
|
|
1268
|
-
|
|
1255
|
+
const mcpBackups = new Map();
|
|
1256
|
+
for (const warning of cleanupLegacyMcp({ backups: mcpBackups }).warnings)
|
|
1257
|
+
warn(warning);
|
|
1269
1258
|
const hubCleanup = cleanupTfxHub({
|
|
1270
1259
|
pluginRoot: existsSync(join(PKG_ROOT, ".git")) ? undefined : PKG_ROOT,
|
|
1260
|
+
log: info,
|
|
1261
|
+
backups: mcpBackups,
|
|
1271
1262
|
});
|
|
1272
1263
|
for (const warning of hubCleanup.warnings) warn(warning);
|
|
1273
1264
|
if (hubCleanup.changed) ok("제거된 허브의 설정과 실행 흔적 정리");
|
|
1265
|
+
const mcpPins = pinRegistryMcpPackages({ backups: mcpBackups });
|
|
1266
|
+
for (const warning of mcpPins.warnings) warn(warning);
|
|
1267
|
+
if (mcpPins.pinned) ok(`MCP 패키지 고정 버전 반영: ${mcpPins.pinned}개 항목`);
|
|
1274
1268
|
if (fromUpdate) refreshSetupCaches();
|
|
1275
1269
|
|
|
1276
1270
|
console.log(`\n${BOLD}triflux setup${RESET}\n`);
|
|
@@ -1278,34 +1272,8 @@ function cmdSetup(options = {}) {
|
|
|
1278
1272
|
for (const target of SYNC_MAP) {
|
|
1279
1273
|
syncFile(target.src, target.dst, target.label);
|
|
1280
1274
|
}
|
|
1275
|
+
retireOldInstallFiles(info);
|
|
1281
1276
|
reportSkillSync();
|
|
1282
|
-
ensureTrifluxMods({ install: mods, log: console.log, warn });
|
|
1283
|
-
|
|
1284
|
-
// ── psmux 기본 셸 자동 수정 (cmd.exe → PowerShell) ──
|
|
1285
|
-
if (process.platform === "win32" && which("psmux")) {
|
|
1286
|
-
try {
|
|
1287
|
-
const shellOut = execSync("psmux show-options -g default-shell 2>NUL", {
|
|
1288
|
-
encoding: "utf8",
|
|
1289
|
-
timeout: 3000,
|
|
1290
|
-
}).trim();
|
|
1291
|
-
if (!/powershell|pwsh/i.test(shellOut)) {
|
|
1292
|
-
const pwsh = which("pwsh")
|
|
1293
|
-
? "pwsh"
|
|
1294
|
-
: which("powershell.exe")
|
|
1295
|
-
? "powershell.exe"
|
|
1296
|
-
: "";
|
|
1297
|
-
if (pwsh) {
|
|
1298
|
-
execSync(`psmux set-option -g default-shell "${pwsh}"`, {
|
|
1299
|
-
timeout: 3000,
|
|
1300
|
-
stdio: "pipe",
|
|
1301
|
-
});
|
|
1302
|
-
ok(`psmux 기본 셸 → ${pwsh}`);
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
} catch {
|
|
1306
|
-
/* psmux 서버 미실행 — 무시 */
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
1277
|
|
|
1310
1278
|
// ── 결과 추적 ──
|
|
1311
1279
|
const summary = [];
|
|
@@ -1402,6 +1370,35 @@ function cmdSetup(options = {}) {
|
|
|
1402
1370
|
});
|
|
1403
1371
|
}
|
|
1404
1372
|
|
|
1373
|
+
// Codex 프로필 정리가 끝난 뒤에 훅을 다룬다.
|
|
1374
|
+
await runConsentSteps({ modsInstall: mods, log: info, warn });
|
|
1375
|
+
|
|
1376
|
+
// ── psmux 기본 셸 자동 수정 (cmd.exe → PowerShell) ──
|
|
1377
|
+
if (process.platform === "win32" && which("psmux")) {
|
|
1378
|
+
try {
|
|
1379
|
+
const shellOut = execSync("psmux show-options -g default-shell 2>NUL", {
|
|
1380
|
+
encoding: "utf8",
|
|
1381
|
+
timeout: 3000,
|
|
1382
|
+
}).trim();
|
|
1383
|
+
if (!/powershell|pwsh/i.test(shellOut)) {
|
|
1384
|
+
const pwsh = which("pwsh")
|
|
1385
|
+
? "pwsh"
|
|
1386
|
+
: which("powershell.exe")
|
|
1387
|
+
? "powershell.exe"
|
|
1388
|
+
: "";
|
|
1389
|
+
if (pwsh) {
|
|
1390
|
+
execSync(`psmux set-option -g default-shell "${pwsh}"`, {
|
|
1391
|
+
timeout: 3000,
|
|
1392
|
+
stdio: "pipe",
|
|
1393
|
+
});
|
|
1394
|
+
ok(`psmux 기본 셸 → ${pwsh}`);
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
} catch {
|
|
1398
|
+
/* psmux 서버 미실행이면 무시 */
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1405
1402
|
// CLI 존재 확인
|
|
1406
1403
|
const cliChecks = [
|
|
1407
1404
|
{ name: "codex", install: "npm i -g @openai/codex" },
|
|
@@ -1510,6 +1507,16 @@ function statusBadge(status) {
|
|
|
1510
1507
|
}
|
|
1511
1508
|
}
|
|
1512
1509
|
|
|
1510
|
+
// URL 은 같고 헤더만 다르면 그렇게 보여 준다. URL 만 찍으면 같은 값끼리 불일치로 보인다.
|
|
1511
|
+
function isHeaderOnlyMismatch(row) {
|
|
1512
|
+
return Boolean(row.expectedUrl) && row.expectedUrl === row.actualUrl;
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
// stdio 항목은 명령이 같고 인자만 다를 수 있어 인자까지 보여 준다.
|
|
1516
|
+
function mcpTarget(url, command, args = []) {
|
|
1517
|
+
return url || [command, ...args].filter(Boolean).join(" ");
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1513
1520
|
function buildMcpStatusRows(statusInfo) {
|
|
1514
1521
|
const registryRows = statusInfo.rows
|
|
1515
1522
|
.filter((row) => row.type === "registry")
|
|
@@ -1520,7 +1527,9 @@ function buildMcpStatusRows(statusInfo) {
|
|
|
1520
1527
|
else if (row.status === "missing") detail = "registry only";
|
|
1521
1528
|
else if (row.status === "missing-file") detail = "config missing";
|
|
1522
1529
|
else if (row.status === "mismatch")
|
|
1523
|
-
detail =
|
|
1530
|
+
detail = isHeaderOnlyMismatch(row)
|
|
1531
|
+
? `header ${row.headerStatus}: ${row.expectedHeaderNames.join(", ")}`
|
|
1532
|
+
: `expected ${mcpTarget(row.expectedUrl, row.expectedCommand, row.expectedArgs)}`;
|
|
1524
1533
|
else if (row.status === "invalid-config") detail = "parse error";
|
|
1525
1534
|
else if (row.status === "skipped") detail = row.message || "skipped";
|
|
1526
1535
|
else if (row.status === "stdio") detail = "configured as stdio";
|
|
@@ -1855,6 +1864,9 @@ async function cmdDoctor(options = {}) {
|
|
|
1855
1864
|
status: invalidCount > 0 ? "issues" : "ok",
|
|
1856
1865
|
actions: mcpSync.actions,
|
|
1857
1866
|
});
|
|
1867
|
+
for (const action of mcpSync.actions)
|
|
1868
|
+
if (action.status === "warning" && action.message)
|
|
1869
|
+
warn(`${action.label}: ${action.message}`);
|
|
1858
1870
|
if (updatedCount > 0)
|
|
1859
1871
|
ok(`MCP registry 동기화: ${updatedCount}개 설정 반영됨`);
|
|
1860
1872
|
else info("MCP registry: 이미 최신 상태");
|
|
@@ -1959,6 +1971,21 @@ async function cmdDoctor(options = {}) {
|
|
|
1959
1971
|
warn(`미설치 ${GRAY}(선택사항)${RESET}`);
|
|
1960
1972
|
}
|
|
1961
1973
|
|
|
1974
|
+
// mods 가 없으면 effort 없이 부른 서브에이전트가 리드 effort 를 물려받는다.
|
|
1975
|
+
section("Claude mods");
|
|
1976
|
+
const mods = inspectTrifluxMods({ pkgVersion: PKG.version });
|
|
1977
|
+
addDoctorCheck(report, { name: "triflux-mods", ...mods });
|
|
1978
|
+
if (mods.status === "current")
|
|
1979
|
+
ok(`설치됨 ${DIM}v${mods.installedVersion}${RESET}`);
|
|
1980
|
+
else if (mods.status === "missing") {
|
|
1981
|
+
warn("미설치");
|
|
1982
|
+
info("수정: tfx setup --mods");
|
|
1983
|
+
} else if (mods.status === "outdated") {
|
|
1984
|
+
warn(`버전 차이: 설치 ${mods.installedVersion}, 패키지 ${PKG.version}`);
|
|
1985
|
+
info(`수정: ${MODS_UPDATE_COMMAND}`);
|
|
1986
|
+
} else if (mods.status === "failed") warn(`확인 실패: ${mods.error}`);
|
|
1987
|
+
else info(`건너뜀 (${mods.reason})`);
|
|
1988
|
+
|
|
1962
1989
|
// Codex CLI
|
|
1963
1990
|
section(`Codex CLI ${WHITE_BRIGHT}●${RESET}`);
|
|
1964
1991
|
const codexCli = checkCliCrossShell(
|
|
@@ -2260,38 +2287,29 @@ async function cmdDoctor(options = {}) {
|
|
|
2260
2287
|
});
|
|
2261
2288
|
}
|
|
2262
2289
|
|
|
2263
|
-
// Stale 스킬
|
|
2264
|
-
const staleSkills =
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
if (existsSync(pkgSkillsDir)) {
|
|
2270
|
-
for (const n of readdirSync(pkgSkillsDir)) {
|
|
2271
|
-
if (isSkillSupportedOnPlatform(join(pkgSkillsDir, n)))
|
|
2272
|
-
pkgSkills.add(n);
|
|
2273
|
-
}
|
|
2274
|
-
}
|
|
2275
|
-
for (const { alias } of SKILL_ALIASES) pkgSkills.add(alias);
|
|
2276
|
-
|
|
2277
|
-
for (const n of readdirSync(userSkillsDir)) {
|
|
2278
|
-
if (
|
|
2279
|
-
!REMOVED_SKILL_NAMES.includes(n) &&
|
|
2280
|
-
!existsSync(join(pkgSkillsDir, n))
|
|
2281
|
-
)
|
|
2282
|
-
continue;
|
|
2283
|
-
if (isLocalDevSkillDir(join(userSkillsDir, n))) continue;
|
|
2284
|
-
if (!pkgSkills.has(n)) staleSkills.push(n);
|
|
2285
|
-
}
|
|
2286
|
-
}
|
|
2290
|
+
// Stale 스킬 체크: setup 과 같은 판정으로 지울 관리 사본과 보존할 사용자 사본을 나눈다.
|
|
2291
|
+
const { removed: staleSkills, preserved: keptSkills } = cleanupStaleSkills(
|
|
2292
|
+
join(CLAUDE_DIR, "skills"),
|
|
2293
|
+
join(PKG_ROOT, "skills"),
|
|
2294
|
+
{ dryRun: true },
|
|
2295
|
+
);
|
|
2287
2296
|
if (staleSkills.length > 0) {
|
|
2288
2297
|
warn(`구형 스킬 ${staleSkills.length}개 감지: ${staleSkills.join(", ")}`);
|
|
2289
2298
|
info("관리 사본 정리: tfx setup 또는 tfx update");
|
|
2299
|
+
}
|
|
2300
|
+
if (keptSkills.length > 0) {
|
|
2301
|
+
warn(
|
|
2302
|
+
`구형 스킬 사용자 사본 ${keptSkills.length}개: ${keptSkills.join(", ")}`,
|
|
2303
|
+
);
|
|
2304
|
+
info("setup 은 고친 사본을 보존한다. 필요 없으면 직접 지우세요.");
|
|
2305
|
+
}
|
|
2306
|
+
if (staleSkills.length + keptSkills.length > 0) {
|
|
2290
2307
|
addDoctorCheck(report, {
|
|
2291
2308
|
name: "stale-skills",
|
|
2292
2309
|
status: "issues",
|
|
2293
2310
|
skills: staleSkills,
|
|
2294
|
-
|
|
2311
|
+
preserved: keptSkills,
|
|
2312
|
+
...(staleSkills.length > 0 ? { fix: "tfx setup" } : {}),
|
|
2295
2313
|
});
|
|
2296
2314
|
issues++;
|
|
2297
2315
|
} else {
|
|
@@ -2357,7 +2375,6 @@ async function cmdDoctor(options = {}) {
|
|
|
2357
2375
|
// --fix 모드: npm install 실행 (Windows 호환 shell: true)
|
|
2358
2376
|
info(`npm install 실행 중 (${PKG_ROOT})...`);
|
|
2359
2377
|
try {
|
|
2360
|
-
const { execFileSync } = await import("node:child_process");
|
|
2361
2378
|
execFileSync("npm", ["install", "--no-audit", "--no-fund"], {
|
|
2362
2379
|
cwd: PKG_ROOT,
|
|
2363
2380
|
stdio: "inherit",
|
|
@@ -2728,10 +2745,7 @@ async function cmdDoctor(options = {}) {
|
|
|
2728
2745
|
}
|
|
2729
2746
|
} else {
|
|
2730
2747
|
// --fix 없이는 개수만 보고
|
|
2731
|
-
const
|
|
2732
|
-
"node:child_process"
|
|
2733
|
-
);
|
|
2734
|
-
const countStr = execSyncDoctor(
|
|
2748
|
+
const countStr = execSync(
|
|
2735
2749
|
`powershell -NoProfile -WindowStyle Hidden -Command "(Get-Process node -ErrorAction SilentlyContinue).Count"`,
|
|
2736
2750
|
{ encoding: "utf8", timeout: 5000 },
|
|
2737
2751
|
).trim();
|
|
@@ -2875,9 +2889,22 @@ async function cmdDoctor(options = {}) {
|
|
|
2875
2889
|
}
|
|
2876
2890
|
|
|
2877
2891
|
for (const row of mismatchRows) {
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2892
|
+
if (isHeaderOnlyMismatch(row)) {
|
|
2893
|
+
warn(`${row.label}: ${row.name} 헤더 불일치 (${row.headerStatus})`);
|
|
2894
|
+
info(`expected ${row.expectedHeaderNames.join(", ") || "없음"}`);
|
|
2895
|
+
info(`actual ${row.actualHeaderNames.join(", ") || "없음"}`);
|
|
2896
|
+
continue;
|
|
2897
|
+
}
|
|
2898
|
+
warn(`${row.label}: ${row.name} 설정 불일치`);
|
|
2899
|
+
info(
|
|
2900
|
+
`expected ${mcpTarget(row.expectedUrl, row.expectedCommand, row.expectedArgs)}`,
|
|
2901
|
+
);
|
|
2902
|
+
const actualTarget = mcpTarget(
|
|
2903
|
+
row.actualUrl,
|
|
2904
|
+
row.actualCommand,
|
|
2905
|
+
row.actualArgs,
|
|
2906
|
+
);
|
|
2907
|
+
if (actualTarget) info(`actual ${actualTarget}`);
|
|
2881
2908
|
}
|
|
2882
2909
|
|
|
2883
2910
|
for (const row of missingFileRows) {
|
|
@@ -3070,6 +3097,44 @@ async function cmdDoctor(options = {}) {
|
|
|
3070
3097
|
: "남은 triflux command hook 없음",
|
|
3071
3098
|
);
|
|
3072
3099
|
}
|
|
3100
|
+
|
|
3101
|
+
// 옛 setup 이 agy hooks.json 에 넣은 triflux-session 훅. 스크립트가 사라져 실행되면 실패한다.
|
|
3102
|
+
// 옛 설치기 모양과 같은 것만 지우고, 다른 모양은 사용자가 정리하도록 경고만 한다.
|
|
3103
|
+
const agyHooks = cleanupAgyHooks({ dryRun: !fix });
|
|
3104
|
+
const agyRemaining = agyHooks.ok && fix ? 0 : agyHooks.removed;
|
|
3105
|
+
const agyLeftover = agyHooks.leftover;
|
|
3106
|
+
addDoctorCheck(report, {
|
|
3107
|
+
name: "legacy-agy-hooks",
|
|
3108
|
+
status: !agyHooks.ok
|
|
3109
|
+
? "error"
|
|
3110
|
+
: agyRemaining + agyLeftover.length > 0
|
|
3111
|
+
? "issues"
|
|
3112
|
+
: "ok",
|
|
3113
|
+
remaining: agyRemaining,
|
|
3114
|
+
leftover: agyLeftover,
|
|
3115
|
+
path: agyHooks.hooksPath,
|
|
3116
|
+
...(agyHooks.ok ? {} : { error: agyHooks.error }),
|
|
3117
|
+
...(agyRemaining > 0 ? { fix: "tfx doctor --fix" } : {}),
|
|
3118
|
+
});
|
|
3119
|
+
if (!agyHooks.ok) {
|
|
3120
|
+
fail(`agy 훅 점검 실패: ${agyHooks.error}`);
|
|
3121
|
+
issues++;
|
|
3122
|
+
} else {
|
|
3123
|
+
if (agyRemaining > 0) {
|
|
3124
|
+
warn(
|
|
3125
|
+
"agy hooks.json 에 옛 triflux-session 훅이 남음: tfx doctor --fix",
|
|
3126
|
+
);
|
|
3127
|
+
issues += agyRemaining;
|
|
3128
|
+
} else if (fix && agyHooks.changed) {
|
|
3129
|
+
ok("agy 옛 triflux-session 훅 정리됨");
|
|
3130
|
+
}
|
|
3131
|
+
if (agyLeftover.length > 0) {
|
|
3132
|
+
warn(
|
|
3133
|
+
`agy 훅 ${agyLeftover.join(", ")} 이 지워진 agy-session-hook.mjs 를 가리킴. 직접 정리: ${agyHooks.hooksPath}`,
|
|
3134
|
+
);
|
|
3135
|
+
issues += agyLeftover.length;
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3073
3138
|
if (fix) {
|
|
3074
3139
|
try {
|
|
3075
3140
|
execFileSync(
|
|
@@ -3159,9 +3224,7 @@ async function cmdUpdate(args = []) {
|
|
|
3159
3224
|
return;
|
|
3160
3225
|
}
|
|
3161
3226
|
|
|
3162
|
-
|
|
3163
|
-
const tagLabel = isDev ? ` ${YELLOW}--dev${RESET}` : "";
|
|
3164
|
-
console.log(`\n${BOLD}triflux update${RESET}${tagLabel}\n`);
|
|
3227
|
+
console.log(`\n${BOLD}triflux update${RESET}\n`);
|
|
3165
3228
|
|
|
3166
3229
|
// 1. 설치 방식 감지
|
|
3167
3230
|
const pluginsFile = join(CLAUDE_DIR, "plugins", "installed_plugins.json");
|
|
@@ -3259,9 +3322,7 @@ async function cmdUpdate(args = []) {
|
|
|
3259
3322
|
break;
|
|
3260
3323
|
}
|
|
3261
3324
|
case "npm-global": {
|
|
3262
|
-
const npmCmd =
|
|
3263
|
-
? "npm install -g triflux@dev"
|
|
3264
|
-
: "npm install -g triflux@latest";
|
|
3325
|
+
const npmCmd = "npm install -g triflux@latest";
|
|
3265
3326
|
let result;
|
|
3266
3327
|
try {
|
|
3267
3328
|
result = execSync(npmCmd, {
|
|
@@ -3284,14 +3345,12 @@ async function cmdUpdate(args = []) {
|
|
|
3284
3345
|
.trim()
|
|
3285
3346
|
.split(/\r?\n/)[0];
|
|
3286
3347
|
}
|
|
3287
|
-
ok(`${npmCmd}
|
|
3348
|
+
ok(`${npmCmd}: ${result || "완료"}`);
|
|
3288
3349
|
updated = true;
|
|
3289
3350
|
break;
|
|
3290
3351
|
}
|
|
3291
3352
|
case "npm-local": {
|
|
3292
|
-
const npmLocalCmd =
|
|
3293
|
-
? "npm install triflux@dev"
|
|
3294
|
-
: "npm update triflux";
|
|
3353
|
+
const npmLocalCmd = "npm update triflux";
|
|
3295
3354
|
const result = execSync(npmLocalCmd, {
|
|
3296
3355
|
encoding: "utf8",
|
|
3297
3356
|
timeout: 60000,
|
|
@@ -3301,9 +3360,7 @@ async function cmdUpdate(args = []) {
|
|
|
3301
3360
|
})
|
|
3302
3361
|
.trim()
|
|
3303
3362
|
.split(/\r?\n/)[0];
|
|
3304
|
-
ok(
|
|
3305
|
-
`${isDev ? "npm install triflux@dev" : "npm update triflux"} — ${result || "완료"}`,
|
|
3306
|
-
);
|
|
3363
|
+
ok(`${npmLocalCmd}: ${result || "완료"}`);
|
|
3307
3364
|
updated = true;
|
|
3308
3365
|
break;
|
|
3309
3366
|
}
|
|
@@ -3572,7 +3629,10 @@ function cmdMcp(args = [], options = {}) {
|
|
|
3572
3629
|
}
|
|
3573
3630
|
const label = `${action.label} ${DIM}(${formatPathForDisplay(action.filePath)})${RESET}`;
|
|
3574
3631
|
if (action.status === "updated") ok(`${label} → updated`);
|
|
3575
|
-
else if (action.status === "warning")
|
|
3632
|
+
else if (action.status === "warning")
|
|
3633
|
+
warn(
|
|
3634
|
+
`${label} → warning${action.message ? `: ${action.message}` : ""}`,
|
|
3635
|
+
);
|
|
3576
3636
|
else if (action.status === "invalid-config")
|
|
3577
3637
|
fail(`${label} → invalid-config`);
|
|
3578
3638
|
else info(`${stripAnsi(label)} → ${action.status}`);
|
|
@@ -3702,10 +3762,8 @@ function cmdHelp() {
|
|
|
3702
3762
|
${DIM} --help${RESET} ${GRAY}--audit, --diagnose 등 전체 옵션${RESET}
|
|
3703
3763
|
${WHITE_BRIGHT}tfx mcp${RESET} ${GRAY}MCP registry 관리 (list/sync/add/remove)${RESET}
|
|
3704
3764
|
${WHITE_BRIGHT}tfx update${RESET} ${GRAY}최신 안정 버전으로 업데이트${RESET}
|
|
3705
|
-
${DIM} --dev / dev${RESET} ${GRAY}dev 태그로 업데이트${RESET}
|
|
3706
3765
|
${WHITE_BRIGHT}tfx list${RESET} ${GRAY}설치된 스킬 목록${RESET}
|
|
3707
3766
|
${WHITE_BRIGHT}tfx multi${RESET} ${GRAY}멀티-CLI 팀 모드 (tmux)${RESET}
|
|
3708
|
-
${WHITE_BRIGHT}tfx cto${RESET} ${GRAY}저장소 스냅숏 수집과 조회 (collect/status/hygiene)${RESET}
|
|
3709
3767
|
${WHITE_BRIGHT}tfx version${RESET} ${GRAY}버전 표시${RESET}
|
|
3710
3768
|
${WHITE_BRIGHT}tfx-live${RESET} ${GRAY}Claude·Codex 라이브 세션 (tfx-live --help)${RESET}
|
|
3711
3769
|
|
|
@@ -3761,7 +3819,7 @@ async function main() {
|
|
|
3761
3819
|
printCommandHelp("setup");
|
|
3762
3820
|
return;
|
|
3763
3821
|
}
|
|
3764
|
-
cmdSetup({
|
|
3822
|
+
await cmdSetup({
|
|
3765
3823
|
dryRun: cmdArgs.includes("--dry-run"),
|
|
3766
3824
|
fromUpdate: cmdArgs.includes("--from-update"),
|
|
3767
3825
|
mods: cmdArgs.includes("--mods"),
|
|
@@ -3789,6 +3847,7 @@ async function main() {
|
|
|
3789
3847
|
} catch (e) {
|
|
3790
3848
|
process.stdout.write(e.stdout || "");
|
|
3791
3849
|
if (e.stderr) process.stderr.write(e.stderr);
|
|
3850
|
+
process.exitCode = Number.isInteger(e.status) ? e.status : EXIT_ERROR;
|
|
3792
3851
|
}
|
|
3793
3852
|
return;
|
|
3794
3853
|
}
|
|
@@ -3857,18 +3916,6 @@ async function main() {
|
|
|
3857
3916
|
}
|
|
3858
3917
|
cmdList({ json: JSON_OUTPUT });
|
|
3859
3918
|
return;
|
|
3860
|
-
case "cto": {
|
|
3861
|
-
if (cmdArgs.some(isHelpArg)) {
|
|
3862
|
-
printCommandHelp("cto");
|
|
3863
|
-
return;
|
|
3864
|
-
}
|
|
3865
|
-
const { pathToFileURL } = await import("node:url");
|
|
3866
|
-
const { cmdCto } = await import(
|
|
3867
|
-
pathToFileURL(join(PKG_ROOT, "cto", "index.mjs")).href
|
|
3868
|
-
);
|
|
3869
|
-
await cmdCto(cmdArgs, { json: JSON_OUTPUT });
|
|
3870
|
-
return;
|
|
3871
|
-
}
|
|
3872
3919
|
case "multi": {
|
|
3873
3920
|
if (cmdArgs.some(isHelpArg)) {
|
|
3874
3921
|
const { pathToFileURL } = await import("node:url");
|
package/config/mcp-registry.json
CHANGED
|
@@ -3,10 +3,6 @@
|
|
|
3
3
|
import { argv, exit, stdin, stdout } from "node:process";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { writeCodexSessionRecord } from "../hub/lib/codex-session-registry.mjs";
|
|
6
|
-
import {
|
|
7
|
-
emitParticipantSessionStarted,
|
|
8
|
-
shouldSkipInteractiveRegistration,
|
|
9
|
-
} from "../scripts/lib/session-presence.mjs";
|
|
10
6
|
|
|
11
7
|
function parsePayload(stdinData) {
|
|
12
8
|
try {
|
|
@@ -72,23 +68,11 @@ export async function runCodexSessionHook(stdinData, opts = {}) {
|
|
|
72
68
|
? normalizeMode(opts.argvMode ?? argv[2], parsed.payload)
|
|
73
69
|
: "";
|
|
74
70
|
const writeSessionRecord = opts.writeSessionRecord || writeCodexSessionRecord;
|
|
75
|
-
const emitSessionStarted =
|
|
76
|
-
opts.emitSessionStarted || emitParticipantSessionStarted;
|
|
77
71
|
|
|
78
72
|
try {
|
|
79
73
|
await runHookSideEffectsWithStdoutSuppressed(async () => {
|
|
80
74
|
if (mode === "register" || mode === "heartbeat") {
|
|
81
|
-
|
|
82
|
-
writeSessionRecord(parsed.payload);
|
|
83
|
-
} catch {}
|
|
84
|
-
}
|
|
85
|
-
if (
|
|
86
|
-
mode === "register" &&
|
|
87
|
-
!shouldSkipInteractiveRegistration(parsed.payload, opts)
|
|
88
|
-
) {
|
|
89
|
-
try {
|
|
90
|
-
await emitSessionStarted(stdinData);
|
|
91
|
-
} catch {}
|
|
75
|
+
writeSessionRecord(parsed.payload);
|
|
92
76
|
}
|
|
93
77
|
});
|
|
94
78
|
} catch {
|