triflux 10.53.0 → 10.54.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 +9 -15
- package/README.md +12 -18
- package/adapters/codex/skills/tfx-live/SKILL.md +1 -1
- package/bin/tfx-live.mjs +5 -5
- package/bin/triflux.mjs +146 -124
- 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 +1 -0
- 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 +275 -103
- package/scripts/lib/machine-profile.mjs +2 -2
- package/scripts/lib/mcp-filter.mjs +1 -42
- package/scripts/lib/mcp-guard-engine.mjs +34 -8
- 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 +288 -343
- 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 +1 -1
- 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/README.ko.md
CHANGED
|
@@ -18,10 +18,6 @@
|
|
|
18
18
|
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-374151?style=flat-square" alt="License: MIT"></a>
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
|
-
<p align="center">
|
|
22
|
-
<img alt="triflux demo" src="docs/assets/demo-multi.gif" width="680">
|
|
23
|
-
</p>
|
|
24
|
-
|
|
25
21
|
triflux는 코딩 작업을 Claude, Codex, Antigravity 사이에서 나눠 맡기는 Claude Code 플러그인이자
|
|
26
22
|
npm CLI다. `/tfx-auto`에 할 일을 한 번 적으면 triflux가 CLI 레인(기본은 Codex)을 고르고, 임의
|
|
27
23
|
셸 명령 대신 관리된 경로로 실행한다. 필요하면 로컬 병렬 워커, Claude↔Codex 라이브 세션, 원격 호스트로 작업을 나눈다.
|
|
@@ -135,7 +131,6 @@ Codex는 이름 붙은 프로필로 실행한다. 모델 ID는 `~/.codex/<프로
|
|
|
135
131
|
| `tfx setup` / `tfx doctor` | 파일·HUD·MCP·프로필 동기화 / 진단과 복구(`--fix`, `--json`) |
|
|
136
132
|
| `tfx multi` | tmux 기반 로컬 멀티 CLI 팀 |
|
|
137
133
|
| `tfx mcp` | 관리형 MCP 레지스트리: `list`, `sync`, `add`, `remove` |
|
|
138
|
-
| `tfx cto` | 저장소 단위 권위 콘솔: `collect`, `status`, `hygiene`(dry-run) |
|
|
139
134
|
| `bash ~/.claude/scripts/tfx-route.sh code-reviewer "<지시>"` | 정책에 따라 `codex exec review`로 리뷰 전달 |
|
|
140
135
|
| `tfx list`, `tfx update`, `tfx version` | 설치된 스킬, 업데이트, 버전 |
|
|
141
136
|
| `tfx-live` | 라이브 세션 브리지(`/tfx-live` 스킬과 같은 명령) |
|
|
@@ -169,11 +164,6 @@ Claude Code Bash 도구의 600초 제한에 걸리지 않는다. 이후 `--job-s
|
|
|
169
164
|
다른 경로로 넘어가지 않고 실패한다. 자세한 내용은
|
|
170
165
|
[`.claude/rules/tfx-machine-profile.md`](.claude/rules/tfx-machine-profile.md).
|
|
171
166
|
|
|
172
|
-
**CTO lake.** `tfx cto collect`로 `.triflux/lake/`의 저장소 스냅샷을 갱신하고,
|
|
173
|
-
`tfx cto status`로 생성 시각과 경과 시간을 확인한다. `tfx cto hygiene --dry-run`는 dry-run 결과를
|
|
174
|
-
보고한다. 트레이와 쓰이지 않는 CTO 운영 명령은 제거하였다
|
|
175
|
-
([ADR-0024](docs/adr/0024-cto-explicit-queries-only.md)). 자동 수집은 기본으로 꺼져 있고 `TFX_CTO_AUTO_COLLECT=1` 로 켠다([ADR-0018](docs/_archive/adr/0018-cto-auto-behaviors-opt-in.md)).
|
|
176
|
-
|
|
177
167
|
**원격 호스트.** `/tfx-remote`는 `~/.config/triflux/hosts.json`
|
|
178
168
|
(Windows는 `%APPDATA%\triflux\hosts.json`)에서 호스트를 읽는다. 세션 시작은
|
|
179
169
|
`remote-spawn.mjs`의 `--host <host> --prompt "<요청>"` 옵션을 사용한다.
|
|
@@ -184,18 +174,22 @@ Claude Code Bash 도구의 600초 제한에 걸리지 않는다. 이후 `--job-s
|
|
|
184
174
|
```mermaid
|
|
185
175
|
graph TD
|
|
186
176
|
User([Claude Code 프롬프트 / 셸]) --> Skills["/tfx-auto · /tfx-live · /tfx-remote"]
|
|
177
|
+
User --> Lead["/tfx-lead"]
|
|
187
178
|
User --> CLI[tfx CLI]
|
|
179
|
+
Lead -->|"지시서, 교차 리뷰, 머지"| Live
|
|
188
180
|
Skills --> Route[tfx-route.sh]
|
|
189
181
|
Skills --> Live[tfx-live]
|
|
190
182
|
CLI --> Team["tfx multi"]
|
|
191
183
|
Route --> Codex[Codex CLI]
|
|
192
184
|
Route --> Agy[Antigravity agy]
|
|
193
185
|
Route --> Claude[Claude Code]
|
|
194
|
-
Team
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
186
|
+
Team -->|headless 워커| Route
|
|
187
|
+
Team --> Index[("결과 색인: tfx-headless/*.results.json")]
|
|
188
|
+
Team --> Rows["claude agents 행"]
|
|
189
|
+
Rows -->|Enter| Room["워커 tmux 방"]
|
|
190
|
+
Live -->|"codex queue, tmux 폴백"| CodexTUI[Codex TUI 세션]
|
|
191
|
+
Live -->|"UDS 또는 tmux"| ClaudeTUI[Claude Code 세션]
|
|
192
|
+
Route --> HUD[HUD]
|
|
199
193
|
```
|
|
200
194
|
|
|
201
195
|
패키지 구성과 실행 경로는 [ARCHITECTURE.md](ARCHITECTURE.md), 문서 지도는
|
package/README.md
CHANGED
|
@@ -18,10 +18,6 @@
|
|
|
18
18
|
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-374151?style=flat-square" alt="License: MIT"></a>
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
|
-
<p align="center">
|
|
22
|
-
<img alt="triflux demo" src="docs/assets/demo-multi.gif" width="680">
|
|
23
|
-
</p>
|
|
24
|
-
|
|
25
21
|
triflux is a Claude Code plugin and npm CLI that routes coding work across Claude, Codex,
|
|
26
22
|
and Antigravity. You describe the task once with `/tfx-auto`; triflux picks the CLI lane
|
|
27
23
|
(Codex by default), runs it through managed routes instead of ad-hoc shell commands, and
|
|
@@ -137,7 +133,6 @@ escalation chain: [`.claude/rules/tfx-escalation-chain.md`](.claude/rules/tfx-es
|
|
|
137
133
|
| `tfx setup` / `tfx doctor` | Sync files, HUD, MCP, profiles / diagnose and repair (`--fix`, `--json`) |
|
|
138
134
|
| `tfx multi` | Local multi-CLI team in tmux |
|
|
139
135
|
| `tfx mcp` | Managed MCP registry: `list`, `sync`, `add`, `remove` |
|
|
140
|
-
| `tfx cto` | Repo-local authority console: `collect`, `status`, `hygiene` (dry-run) |
|
|
141
136
|
| `bash ~/.claude/scripts/tfx-route.sh code-reviewer "<instruction>"` | Send review to Codex (`codex exec review` via policy) |
|
|
142
137
|
| `tfx list`, `tfx update`, `tfx version` | Installed skills, update, version |
|
|
143
138
|
| `tfx-live` | Live session bridge (same as the `/tfx-live` skill) |
|
|
@@ -171,32 +166,31 @@ tmux pane the worker runs in.
|
|
|
171
166
|
removes a CLI from routing; if no allowed CLI is available the route fails instead of silently
|
|
172
167
|
falling back. Details: [`.claude/rules/tfx-machine-profile.md`](.claude/rules/tfx-machine-profile.md).
|
|
173
168
|
|
|
174
|
-
**
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
([ADR-0024](docs/adr/0024-cto-explicit-queries-only.md)). Automatic collection stays off unless `TFX_CTO_AUTO_COLLECT=1` is set ([ADR-0018](docs/_archive/adr/0018-cto-auto-behaviors-opt-in.md)).
|
|
178
|
-
|
|
179
|
-
**원격 호스트.** `/tfx-remote`는 `~/.config/triflux/hosts.json`에서 호스트를 읽는다
|
|
180
|
-
(Windows: `%APPDATA%\triflux\hosts.json`). 세션 시작은 `remote-spawn.mjs`의
|
|
181
|
-
`--host <host> --prompt "<요청>"` 옵션을 사용한다. [실행 옵션](skills/tfx-remote/SKILL.md)을 따른다.
|
|
169
|
+
**Remote hosts.** `/tfx-remote` reads hosts from `~/.config/triflux/hosts.json`
|
|
170
|
+
(Windows: `%APPDATA%\triflux\hosts.json`). Start a session with `remote-spawn.mjs`
|
|
171
|
+
`--host <host> --prompt "<request>"`. Options: [skills/tfx-remote/SKILL.md](skills/tfx-remote/SKILL.md).
|
|
182
172
|
|
|
183
173
|
## Architecture
|
|
184
174
|
|
|
185
175
|
```mermaid
|
|
186
176
|
graph TD
|
|
187
177
|
User([Claude Code prompt / shell]) --> Skills["/tfx-auto · /tfx-live · /tfx-remote"]
|
|
178
|
+
User --> Lead["/tfx-lead"]
|
|
188
179
|
User --> CLI[tfx CLI]
|
|
180
|
+
Lead -->|"briefs, cross review, merge"| Live
|
|
189
181
|
Skills --> Route[tfx-route.sh]
|
|
190
182
|
Skills --> Live[tfx-live]
|
|
191
183
|
CLI --> Team["tfx multi"]
|
|
192
184
|
Route --> Codex[Codex CLI]
|
|
193
185
|
Route --> Agy[Antigravity agy]
|
|
194
186
|
Route --> Claude[Claude Code]
|
|
195
|
-
Team
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
187
|
+
Team -->|headless workers| Route
|
|
188
|
+
Team --> Index[("results index: tfx-headless/*.results.json")]
|
|
189
|
+
Team --> Rows["claude agents rows"]
|
|
190
|
+
Rows -->|Enter| Room["worker tmux room"]
|
|
191
|
+
Live -->|"codex queue, tmux fallback"| CodexTUI[Codex TUI sessions]
|
|
192
|
+
Live -->|"UDS or tmux"| ClaudeTUI[Claude Code sessions]
|
|
193
|
+
Route --> HUD[HUD]
|
|
200
194
|
```
|
|
201
195
|
|
|
202
196
|
Package layout and execution paths: [ARCHITECTURE.md](ARCHITECTURE.md). Documentation map:
|
|
@@ -114,7 +114,7 @@ Codex `wait`는 `--session` 또는 `--thread UUID`의 rollout에서 표식이
|
|
|
114
114
|
|
|
115
115
|
명시한 `--config-dir`은 정확히 일치하는 daemon만 선택한다. `CLAUDE_CONFIG_DIR`도 해당 env 디렉터리로 제한한다. stale endpoint 복구는 같은 source configDir 안에서만 재시도한다. attach 전 예외는 `inputSent: false`이므로 `auto`의 tmux fallback이 가능하다. attach 뒤 전송 여부가 불명확하면 `status: "unknown"`으로 보고하고 재전송하지 않는다.
|
|
116
116
|
|
|
117
|
-
직접 `tmux capture-pane`과 `tmux send-keys`를 쓸 때는 전송·pane 진단에 한정한다. Claude pane에 `C-c`를 보내지 않는다. 자동 세션 탐색에는 `tmr ls --json
|
|
117
|
+
직접 `tmux capture-pane`과 `tmux send-keys`를 쓸 때는 전송·pane 진단에 한정한다. Claude pane에 `C-c`를 보내지 않는다. 자동 세션 탐색에는 `tmr ls --json`으로 pane 별 에이전트, 상태, 클라이언트 수, 마지막 활동을 본다. tmr 이 없으면 아래 `tfx-live list-sessions`와 `tmux ls -F '#{session_name} #{session_attached}'`를 쓴다. tmr 은 macOS(arm64) 에서 `tfx setup` 이 설치를 묻는다.
|
|
118
118
|
|
|
119
119
|
## 세션 탐색과 연결
|
|
120
120
|
|
package/bin/tfx-live.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
stat,
|
|
12
12
|
writeFile,
|
|
13
13
|
} from "node:fs/promises";
|
|
14
|
-
import { homedir
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
15
|
import { join as pathJoin, resolve as pathResolve } from "node:path";
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
17
17
|
import { promisify } from "node:util";
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
readCodexSessionRecords,
|
|
21
21
|
registryDir,
|
|
22
22
|
} from "../hub/lib/codex-session-registry.mjs";
|
|
23
|
+
import { privateTmpDir } from "../hub/lib/private-tmp.mjs";
|
|
23
24
|
import { resolveHardCeilingMs } from "../hub/lib/worker-lifecycle.mjs";
|
|
24
25
|
import { exposeLiveSession } from "../hub/team/agents-row.mjs";
|
|
25
26
|
import {
|
|
@@ -1932,8 +1933,9 @@ async function callBridgeVerb(bridgePath, verb, payload, timeoutMs) {
|
|
|
1932
1933
|
let payloadFile = null;
|
|
1933
1934
|
let args;
|
|
1934
1935
|
if (json.length > PAYLOAD_FILE_THRESHOLD) {
|
|
1936
|
+
// 페이로드에 프롬프트가 들어 있어 사용자별 0700 디렉터리에 쓴다.
|
|
1935
1937
|
payloadFile = pathJoin(
|
|
1936
|
-
|
|
1938
|
+
privateTmpDir("tfx-live"),
|
|
1937
1939
|
`codex-live-payload-${process.pid}-${Date.now()}.json`,
|
|
1938
1940
|
);
|
|
1939
1941
|
await writeFile(payloadFile, json, "utf8");
|
|
@@ -4098,13 +4100,11 @@ function peerArtifactPaths(sessionA, sessionB) {
|
|
|
4098
4100
|
const preferredDir =
|
|
4099
4101
|
process.env.TFX_LIVE_ARTIFACT_DIR ??
|
|
4100
4102
|
pathJoin(homedir(), ".claude", "cache", "triflux", "tfx-live", "peer-runs");
|
|
4101
|
-
const fallbackDir = pathJoin(tmpdir(), "triflux-live");
|
|
4102
4103
|
let artifactDir = preferredDir;
|
|
4103
4104
|
try {
|
|
4104
4105
|
mkdirSync(artifactDir, { recursive: true });
|
|
4105
4106
|
} catch {
|
|
4106
|
-
artifactDir =
|
|
4107
|
-
mkdirSync(artifactDir, { recursive: true });
|
|
4107
|
+
artifactDir = privateTmpDir("tfx-live");
|
|
4108
4108
|
}
|
|
4109
4109
|
const safe = (value) =>
|
|
4110
4110
|
String(value)
|
package/bin/triflux.mjs
CHANGED
|
@@ -36,7 +36,10 @@ 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,
|
|
@@ -58,19 +61,20 @@ import {
|
|
|
58
61
|
formatPsmuxUpdateGuidance,
|
|
59
62
|
probePsmuxSupport,
|
|
60
63
|
} from "../scripts/lib/psmux-info.mjs";
|
|
64
|
+
import { writesRealHomeInTest } from "../scripts/lib/test-env.mjs";
|
|
61
65
|
import {
|
|
62
66
|
applyStatusLine,
|
|
67
|
+
cleanupStaleSkills,
|
|
63
68
|
ensureCodexProfiles,
|
|
64
69
|
ensureTrifluxMods,
|
|
65
70
|
getVersion,
|
|
66
|
-
isLocalDevSkillDir,
|
|
67
71
|
isSkillSupportedOnPlatform,
|
|
68
72
|
LEGACY_CODEX_MODELS,
|
|
69
73
|
listInlineProfileNames,
|
|
70
74
|
persistSettings,
|
|
71
|
-
REMOVED_SKILL_NAMES,
|
|
72
75
|
REQUIRED_CODEX_PROFILES,
|
|
73
|
-
|
|
76
|
+
retireOldInstallFiles,
|
|
77
|
+
runConsentSteps,
|
|
74
78
|
SYNC_MAP,
|
|
75
79
|
syncSkills,
|
|
76
80
|
} from "../scripts/setup.mjs";
|
|
@@ -265,16 +269,6 @@ const CLI_COMMAND_SCHEMAS = Object.freeze({
|
|
|
265
269
|
},
|
|
266
270
|
},
|
|
267
271
|
},
|
|
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
272
|
multi: {
|
|
279
273
|
usage:
|
|
280
274
|
"tfx multi [--dashboard-layout lite|single|split-2col|split-3col|auto] <subcommand|task>",
|
|
@@ -311,15 +305,10 @@ const CLI_COMMAND_SCHEMAS = Object.freeze({
|
|
|
311
305
|
},
|
|
312
306
|
},
|
|
313
307
|
update: {
|
|
314
|
-
usage: "tfx update
|
|
308
|
+
usage: "tfx update",
|
|
315
309
|
description:
|
|
316
310
|
"설치 방식(plugin/npm/git)을 감지해 triflux를 업데이트하고 setup/cache를 재동기화",
|
|
317
311
|
options: [
|
|
318
|
-
{
|
|
319
|
-
name: "--dev / dev",
|
|
320
|
-
type: "boolean",
|
|
321
|
-
description: "npm 설치 모드에서 dev tag로 업데이트",
|
|
322
|
-
},
|
|
323
312
|
{
|
|
324
313
|
name: "--help",
|
|
325
314
|
type: "boolean",
|
|
@@ -550,12 +539,6 @@ function whichInShell(cmd, shell) {
|
|
|
550
539
|
}
|
|
551
540
|
}
|
|
552
541
|
|
|
553
|
-
function isDevUpdateRequested(argv = process.argv) {
|
|
554
|
-
return (
|
|
555
|
-
argv.includes("--dev") || argv.includes("@dev") || argv.includes("dev")
|
|
556
|
-
);
|
|
557
|
-
}
|
|
558
|
-
|
|
559
542
|
function checkShellAvailable(shell) {
|
|
560
543
|
const cmds = {
|
|
561
544
|
bash: "bash --version",
|
|
@@ -703,7 +686,7 @@ function sumMemoryEstimateMb(panes, { execFile = execFileSync } = {}) {
|
|
|
703
686
|
return seen ? total : null;
|
|
704
687
|
}
|
|
705
688
|
|
|
706
|
-
|
|
689
|
+
function inspectDetachedTmuxSessions({
|
|
707
690
|
prefix = DEFAULT_TMUX_CLEANUP_PREFIX,
|
|
708
691
|
ageMin = DEFAULT_TMUX_CLEANUP_AGE_MIN,
|
|
709
692
|
platform = process.platform,
|
|
@@ -782,7 +765,7 @@ export function inspectDetachedTmuxSessions({
|
|
|
782
765
|
};
|
|
783
766
|
}
|
|
784
767
|
|
|
785
|
-
|
|
768
|
+
async function cleanupDetachedTmuxSessions({
|
|
786
769
|
sessions,
|
|
787
770
|
dryRun = true,
|
|
788
771
|
apply = false,
|
|
@@ -1129,12 +1112,6 @@ function listSkillSyncActions() {
|
|
|
1129
1112
|
if (!isSkillSupportedOnPlatform(join(skillsSrc, name))) continue;
|
|
1130
1113
|
actions.push(describeSyncAction(src, dst, `skill:${name}`));
|
|
1131
1114
|
}
|
|
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
1115
|
return actions;
|
|
1139
1116
|
}
|
|
1140
1117
|
|
|
@@ -1242,7 +1219,7 @@ function reportSkillSync() {
|
|
|
1242
1219
|
ok(`스킬: ${result.total}개 확인, 파일 ${result.changed}개 반영`);
|
|
1243
1220
|
}
|
|
1244
1221
|
|
|
1245
|
-
function cmdSetup(options = {}) {
|
|
1222
|
+
async function cmdSetup(options = {}) {
|
|
1246
1223
|
const {
|
|
1247
1224
|
dryRun = false,
|
|
1248
1225
|
fromUpdate = false,
|
|
@@ -1253,6 +1230,10 @@ function cmdSetup(options = {}) {
|
|
|
1253
1230
|
printJson(buildSetupDryRunPlan());
|
|
1254
1231
|
return;
|
|
1255
1232
|
}
|
|
1233
|
+
if (writesRealHomeInTest()) {
|
|
1234
|
+
info("setup: skip (테스트가 홈을 격리하지 않음)");
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1256
1237
|
|
|
1257
1238
|
const cleanup = cleanupLegacyHooks({
|
|
1258
1239
|
settingsPath: join(CLAUDE_DIR, "settings.json"),
|
|
@@ -1264,10 +1245,18 @@ function cmdSetup(options = {}) {
|
|
|
1264
1245
|
fix: `${join(CLAUDE_DIR, "settings.json")}의 JSON 문법과 쓰기 권한을 확인하세요.`,
|
|
1265
1246
|
});
|
|
1266
1247
|
}
|
|
1248
|
+
// agy 는 hooks.json 의 훅을 실행하므로 지워진 훅 스크립트를 가리키는 항목을 남기지 않는다.
|
|
1249
|
+
const agyCleanup = cleanupAgyHooks();
|
|
1250
|
+
if (agyCleanup.changed) ok("agy 옛 triflux-session 훅 정리됨");
|
|
1251
|
+
else if (!agyCleanup.ok) warn(`agy 옛 훅 정리 실패: ${agyCleanup.error}`);
|
|
1267
1252
|
// 이주가 막혀도 setup 은 계속한다. 남은 항목은 경고로 알린다.
|
|
1268
|
-
|
|
1253
|
+
const mcpBackups = new Map();
|
|
1254
|
+
for (const warning of cleanupLegacyMcp({ backups: mcpBackups }).warnings)
|
|
1255
|
+
warn(warning);
|
|
1269
1256
|
const hubCleanup = cleanupTfxHub({
|
|
1270
1257
|
pluginRoot: existsSync(join(PKG_ROOT, ".git")) ? undefined : PKG_ROOT,
|
|
1258
|
+
log: info,
|
|
1259
|
+
backups: mcpBackups,
|
|
1271
1260
|
});
|
|
1272
1261
|
for (const warning of hubCleanup.warnings) warn(warning);
|
|
1273
1262
|
if (hubCleanup.changed) ok("제거된 허브의 설정과 실행 흔적 정리");
|
|
@@ -1278,35 +1267,10 @@ function cmdSetup(options = {}) {
|
|
|
1278
1267
|
for (const target of SYNC_MAP) {
|
|
1279
1268
|
syncFile(target.src, target.dst, target.label);
|
|
1280
1269
|
}
|
|
1270
|
+
retireOldInstallFiles(info);
|
|
1281
1271
|
reportSkillSync();
|
|
1282
1272
|
ensureTrifluxMods({ install: mods, log: console.log, warn });
|
|
1283
1273
|
|
|
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
|
-
|
|
1310
1274
|
// ── 결과 추적 ──
|
|
1311
1275
|
const summary = [];
|
|
1312
1276
|
|
|
@@ -1402,6 +1366,35 @@ function cmdSetup(options = {}) {
|
|
|
1402
1366
|
});
|
|
1403
1367
|
}
|
|
1404
1368
|
|
|
1369
|
+
// Codex 프로필 정리가 끝난 뒤에 훅을 다룬다.
|
|
1370
|
+
await runConsentSteps({ log: info, warn });
|
|
1371
|
+
|
|
1372
|
+
// ── psmux 기본 셸 자동 수정 (cmd.exe → PowerShell) ──
|
|
1373
|
+
if (process.platform === "win32" && which("psmux")) {
|
|
1374
|
+
try {
|
|
1375
|
+
const shellOut = execSync("psmux show-options -g default-shell 2>NUL", {
|
|
1376
|
+
encoding: "utf8",
|
|
1377
|
+
timeout: 3000,
|
|
1378
|
+
}).trim();
|
|
1379
|
+
if (!/powershell|pwsh/i.test(shellOut)) {
|
|
1380
|
+
const pwsh = which("pwsh")
|
|
1381
|
+
? "pwsh"
|
|
1382
|
+
: which("powershell.exe")
|
|
1383
|
+
? "powershell.exe"
|
|
1384
|
+
: "";
|
|
1385
|
+
if (pwsh) {
|
|
1386
|
+
execSync(`psmux set-option -g default-shell "${pwsh}"`, {
|
|
1387
|
+
timeout: 3000,
|
|
1388
|
+
stdio: "pipe",
|
|
1389
|
+
});
|
|
1390
|
+
ok(`psmux 기본 셸 → ${pwsh}`);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
} catch {
|
|
1394
|
+
/* psmux 서버 미실행이면 무시 */
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1405
1398
|
// CLI 존재 확인
|
|
1406
1399
|
const cliChecks = [
|
|
1407
1400
|
{ name: "codex", install: "npm i -g @openai/codex" },
|
|
@@ -1510,6 +1503,16 @@ function statusBadge(status) {
|
|
|
1510
1503
|
}
|
|
1511
1504
|
}
|
|
1512
1505
|
|
|
1506
|
+
// URL 은 같고 헤더만 다르면 그렇게 보여 준다. URL 만 찍으면 같은 값끼리 불일치로 보인다.
|
|
1507
|
+
function isHeaderOnlyMismatch(row) {
|
|
1508
|
+
return Boolean(row.expectedUrl) && row.expectedUrl === row.actualUrl;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
// stdio 항목은 명령이 같고 인자만 다를 수 있어 인자까지 보여 준다.
|
|
1512
|
+
function mcpTarget(url, command, args = []) {
|
|
1513
|
+
return url || [command, ...args].filter(Boolean).join(" ");
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1513
1516
|
function buildMcpStatusRows(statusInfo) {
|
|
1514
1517
|
const registryRows = statusInfo.rows
|
|
1515
1518
|
.filter((row) => row.type === "registry")
|
|
@@ -1520,7 +1523,9 @@ function buildMcpStatusRows(statusInfo) {
|
|
|
1520
1523
|
else if (row.status === "missing") detail = "registry only";
|
|
1521
1524
|
else if (row.status === "missing-file") detail = "config missing";
|
|
1522
1525
|
else if (row.status === "mismatch")
|
|
1523
|
-
detail =
|
|
1526
|
+
detail = isHeaderOnlyMismatch(row)
|
|
1527
|
+
? `header ${row.headerStatus}: ${row.expectedHeaderNames.join(", ")}`
|
|
1528
|
+
: `expected ${mcpTarget(row.expectedUrl, row.expectedCommand, row.expectedArgs)}`;
|
|
1524
1529
|
else if (row.status === "invalid-config") detail = "parse error";
|
|
1525
1530
|
else if (row.status === "skipped") detail = row.message || "skipped";
|
|
1526
1531
|
else if (row.status === "stdio") detail = "configured as stdio";
|
|
@@ -2260,38 +2265,29 @@ async function cmdDoctor(options = {}) {
|
|
|
2260
2265
|
});
|
|
2261
2266
|
}
|
|
2262
2267
|
|
|
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
|
-
}
|
|
2268
|
+
// Stale 스킬 체크: setup 과 같은 판정으로 지울 관리 사본과 보존할 사용자 사본을 나눈다.
|
|
2269
|
+
const { removed: staleSkills, preserved: keptSkills } = cleanupStaleSkills(
|
|
2270
|
+
join(CLAUDE_DIR, "skills"),
|
|
2271
|
+
join(PKG_ROOT, "skills"),
|
|
2272
|
+
{ dryRun: true },
|
|
2273
|
+
);
|
|
2287
2274
|
if (staleSkills.length > 0) {
|
|
2288
2275
|
warn(`구형 스킬 ${staleSkills.length}개 감지: ${staleSkills.join(", ")}`);
|
|
2289
2276
|
info("관리 사본 정리: tfx setup 또는 tfx update");
|
|
2277
|
+
}
|
|
2278
|
+
if (keptSkills.length > 0) {
|
|
2279
|
+
warn(
|
|
2280
|
+
`구형 스킬 사용자 사본 ${keptSkills.length}개: ${keptSkills.join(", ")}`,
|
|
2281
|
+
);
|
|
2282
|
+
info("setup 은 고친 사본을 보존한다. 필요 없으면 직접 지우세요.");
|
|
2283
|
+
}
|
|
2284
|
+
if (staleSkills.length + keptSkills.length > 0) {
|
|
2290
2285
|
addDoctorCheck(report, {
|
|
2291
2286
|
name: "stale-skills",
|
|
2292
2287
|
status: "issues",
|
|
2293
2288
|
skills: staleSkills,
|
|
2294
|
-
|
|
2289
|
+
preserved: keptSkills,
|
|
2290
|
+
...(staleSkills.length > 0 ? { fix: "tfx setup" } : {}),
|
|
2295
2291
|
});
|
|
2296
2292
|
issues++;
|
|
2297
2293
|
} else {
|
|
@@ -2357,7 +2353,6 @@ async function cmdDoctor(options = {}) {
|
|
|
2357
2353
|
// --fix 모드: npm install 실행 (Windows 호환 shell: true)
|
|
2358
2354
|
info(`npm install 실행 중 (${PKG_ROOT})...`);
|
|
2359
2355
|
try {
|
|
2360
|
-
const { execFileSync } = await import("node:child_process");
|
|
2361
2356
|
execFileSync("npm", ["install", "--no-audit", "--no-fund"], {
|
|
2362
2357
|
cwd: PKG_ROOT,
|
|
2363
2358
|
stdio: "inherit",
|
|
@@ -2728,10 +2723,7 @@ async function cmdDoctor(options = {}) {
|
|
|
2728
2723
|
}
|
|
2729
2724
|
} else {
|
|
2730
2725
|
// --fix 없이는 개수만 보고
|
|
2731
|
-
const
|
|
2732
|
-
"node:child_process"
|
|
2733
|
-
);
|
|
2734
|
-
const countStr = execSyncDoctor(
|
|
2726
|
+
const countStr = execSync(
|
|
2735
2727
|
`powershell -NoProfile -WindowStyle Hidden -Command "(Get-Process node -ErrorAction SilentlyContinue).Count"`,
|
|
2736
2728
|
{ encoding: "utf8", timeout: 5000 },
|
|
2737
2729
|
).trim();
|
|
@@ -2875,9 +2867,22 @@ async function cmdDoctor(options = {}) {
|
|
|
2875
2867
|
}
|
|
2876
2868
|
|
|
2877
2869
|
for (const row of mismatchRows) {
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2870
|
+
if (isHeaderOnlyMismatch(row)) {
|
|
2871
|
+
warn(`${row.label}: ${row.name} 헤더 불일치 (${row.headerStatus})`);
|
|
2872
|
+
info(`expected ${row.expectedHeaderNames.join(", ") || "없음"}`);
|
|
2873
|
+
info(`actual ${row.actualHeaderNames.join(", ") || "없음"}`);
|
|
2874
|
+
continue;
|
|
2875
|
+
}
|
|
2876
|
+
warn(`${row.label}: ${row.name} 설정 불일치`);
|
|
2877
|
+
info(
|
|
2878
|
+
`expected ${mcpTarget(row.expectedUrl, row.expectedCommand, row.expectedArgs)}`,
|
|
2879
|
+
);
|
|
2880
|
+
const actualTarget = mcpTarget(
|
|
2881
|
+
row.actualUrl,
|
|
2882
|
+
row.actualCommand,
|
|
2883
|
+
row.actualArgs,
|
|
2884
|
+
);
|
|
2885
|
+
if (actualTarget) info(`actual ${actualTarget}`);
|
|
2881
2886
|
}
|
|
2882
2887
|
|
|
2883
2888
|
for (const row of missingFileRows) {
|
|
@@ -3070,6 +3075,44 @@ async function cmdDoctor(options = {}) {
|
|
|
3070
3075
|
: "남은 triflux command hook 없음",
|
|
3071
3076
|
);
|
|
3072
3077
|
}
|
|
3078
|
+
|
|
3079
|
+
// 옛 setup 이 agy hooks.json 에 넣은 triflux-session 훅. 스크립트가 사라져 실행되면 실패한다.
|
|
3080
|
+
// 옛 설치기 모양과 같은 것만 지우고, 다른 모양은 사용자가 정리하도록 경고만 한다.
|
|
3081
|
+
const agyHooks = cleanupAgyHooks({ dryRun: !fix });
|
|
3082
|
+
const agyRemaining = agyHooks.ok && fix ? 0 : agyHooks.removed;
|
|
3083
|
+
const agyLeftover = agyHooks.leftover;
|
|
3084
|
+
addDoctorCheck(report, {
|
|
3085
|
+
name: "legacy-agy-hooks",
|
|
3086
|
+
status: !agyHooks.ok
|
|
3087
|
+
? "error"
|
|
3088
|
+
: agyRemaining + agyLeftover.length > 0
|
|
3089
|
+
? "issues"
|
|
3090
|
+
: "ok",
|
|
3091
|
+
remaining: agyRemaining,
|
|
3092
|
+
leftover: agyLeftover,
|
|
3093
|
+
path: agyHooks.hooksPath,
|
|
3094
|
+
...(agyHooks.ok ? {} : { error: agyHooks.error }),
|
|
3095
|
+
...(agyRemaining > 0 ? { fix: "tfx doctor --fix" } : {}),
|
|
3096
|
+
});
|
|
3097
|
+
if (!agyHooks.ok) {
|
|
3098
|
+
fail(`agy 훅 점검 실패: ${agyHooks.error}`);
|
|
3099
|
+
issues++;
|
|
3100
|
+
} else {
|
|
3101
|
+
if (agyRemaining > 0) {
|
|
3102
|
+
warn(
|
|
3103
|
+
"agy hooks.json 에 옛 triflux-session 훅이 남음: tfx doctor --fix",
|
|
3104
|
+
);
|
|
3105
|
+
issues += agyRemaining;
|
|
3106
|
+
} else if (fix && agyHooks.changed) {
|
|
3107
|
+
ok("agy 옛 triflux-session 훅 정리됨");
|
|
3108
|
+
}
|
|
3109
|
+
if (agyLeftover.length > 0) {
|
|
3110
|
+
warn(
|
|
3111
|
+
`agy 훅 ${agyLeftover.join(", ")} 이 지워진 agy-session-hook.mjs 를 가리킴. 직접 정리: ${agyHooks.hooksPath}`,
|
|
3112
|
+
);
|
|
3113
|
+
issues += agyLeftover.length;
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3073
3116
|
if (fix) {
|
|
3074
3117
|
try {
|
|
3075
3118
|
execFileSync(
|
|
@@ -3159,9 +3202,7 @@ async function cmdUpdate(args = []) {
|
|
|
3159
3202
|
return;
|
|
3160
3203
|
}
|
|
3161
3204
|
|
|
3162
|
-
|
|
3163
|
-
const tagLabel = isDev ? ` ${YELLOW}--dev${RESET}` : "";
|
|
3164
|
-
console.log(`\n${BOLD}triflux update${RESET}${tagLabel}\n`);
|
|
3205
|
+
console.log(`\n${BOLD}triflux update${RESET}\n`);
|
|
3165
3206
|
|
|
3166
3207
|
// 1. 설치 방식 감지
|
|
3167
3208
|
const pluginsFile = join(CLAUDE_DIR, "plugins", "installed_plugins.json");
|
|
@@ -3259,9 +3300,7 @@ async function cmdUpdate(args = []) {
|
|
|
3259
3300
|
break;
|
|
3260
3301
|
}
|
|
3261
3302
|
case "npm-global": {
|
|
3262
|
-
const npmCmd =
|
|
3263
|
-
? "npm install -g triflux@dev"
|
|
3264
|
-
: "npm install -g triflux@latest";
|
|
3303
|
+
const npmCmd = "npm install -g triflux@latest";
|
|
3265
3304
|
let result;
|
|
3266
3305
|
try {
|
|
3267
3306
|
result = execSync(npmCmd, {
|
|
@@ -3284,14 +3323,12 @@ async function cmdUpdate(args = []) {
|
|
|
3284
3323
|
.trim()
|
|
3285
3324
|
.split(/\r?\n/)[0];
|
|
3286
3325
|
}
|
|
3287
|
-
ok(`${npmCmd}
|
|
3326
|
+
ok(`${npmCmd}: ${result || "완료"}`);
|
|
3288
3327
|
updated = true;
|
|
3289
3328
|
break;
|
|
3290
3329
|
}
|
|
3291
3330
|
case "npm-local": {
|
|
3292
|
-
const npmLocalCmd =
|
|
3293
|
-
? "npm install triflux@dev"
|
|
3294
|
-
: "npm update triflux";
|
|
3331
|
+
const npmLocalCmd = "npm update triflux";
|
|
3295
3332
|
const result = execSync(npmLocalCmd, {
|
|
3296
3333
|
encoding: "utf8",
|
|
3297
3334
|
timeout: 60000,
|
|
@@ -3301,9 +3338,7 @@ async function cmdUpdate(args = []) {
|
|
|
3301
3338
|
})
|
|
3302
3339
|
.trim()
|
|
3303
3340
|
.split(/\r?\n/)[0];
|
|
3304
|
-
ok(
|
|
3305
|
-
`${isDev ? "npm install triflux@dev" : "npm update triflux"} — ${result || "완료"}`,
|
|
3306
|
-
);
|
|
3341
|
+
ok(`${npmLocalCmd}: ${result || "완료"}`);
|
|
3307
3342
|
updated = true;
|
|
3308
3343
|
break;
|
|
3309
3344
|
}
|
|
@@ -3702,10 +3737,8 @@ function cmdHelp() {
|
|
|
3702
3737
|
${DIM} --help${RESET} ${GRAY}--audit, --diagnose 등 전체 옵션${RESET}
|
|
3703
3738
|
${WHITE_BRIGHT}tfx mcp${RESET} ${GRAY}MCP registry 관리 (list/sync/add/remove)${RESET}
|
|
3704
3739
|
${WHITE_BRIGHT}tfx update${RESET} ${GRAY}최신 안정 버전으로 업데이트${RESET}
|
|
3705
|
-
${DIM} --dev / dev${RESET} ${GRAY}dev 태그로 업데이트${RESET}
|
|
3706
3740
|
${WHITE_BRIGHT}tfx list${RESET} ${GRAY}설치된 스킬 목록${RESET}
|
|
3707
3741
|
${WHITE_BRIGHT}tfx multi${RESET} ${GRAY}멀티-CLI 팀 모드 (tmux)${RESET}
|
|
3708
|
-
${WHITE_BRIGHT}tfx cto${RESET} ${GRAY}저장소 스냅숏 수집과 조회 (collect/status/hygiene)${RESET}
|
|
3709
3742
|
${WHITE_BRIGHT}tfx version${RESET} ${GRAY}버전 표시${RESET}
|
|
3710
3743
|
${WHITE_BRIGHT}tfx-live${RESET} ${GRAY}Claude·Codex 라이브 세션 (tfx-live --help)${RESET}
|
|
3711
3744
|
|
|
@@ -3761,7 +3794,7 @@ async function main() {
|
|
|
3761
3794
|
printCommandHelp("setup");
|
|
3762
3795
|
return;
|
|
3763
3796
|
}
|
|
3764
|
-
cmdSetup({
|
|
3797
|
+
await cmdSetup({
|
|
3765
3798
|
dryRun: cmdArgs.includes("--dry-run"),
|
|
3766
3799
|
fromUpdate: cmdArgs.includes("--from-update"),
|
|
3767
3800
|
mods: cmdArgs.includes("--mods"),
|
|
@@ -3789,6 +3822,7 @@ async function main() {
|
|
|
3789
3822
|
} catch (e) {
|
|
3790
3823
|
process.stdout.write(e.stdout || "");
|
|
3791
3824
|
if (e.stderr) process.stderr.write(e.stderr);
|
|
3825
|
+
process.exitCode = Number.isInteger(e.status) ? e.status : EXIT_ERROR;
|
|
3792
3826
|
}
|
|
3793
3827
|
return;
|
|
3794
3828
|
}
|
|
@@ -3857,18 +3891,6 @@ async function main() {
|
|
|
3857
3891
|
}
|
|
3858
3892
|
cmdList({ json: JSON_OUTPUT });
|
|
3859
3893
|
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
3894
|
case "multi": {
|
|
3873
3895
|
if (cmdArgs.some(isHelpArg)) {
|
|
3874
3896
|
const { pathToFileURL } = await import("node:url");
|
package/config/mcp-registry.json
CHANGED