triflux 10.13.7 → 10.13.9

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/bin/triflux.mjs CHANGED
@@ -318,9 +318,10 @@ const CLI_COMMAND_SCHEMAS = Object.freeze({
318
318
  ],
319
319
  },
320
320
  swarm: {
321
- usage: "tfx swarm <prd-path> [--dry-run|--json|--filter <shard>]",
321
+ usage: "tfx swarm [run] <prd-path> [--dry-run|--json|--filter <shard>]",
322
322
  description: "PRD 기반 멀티모델 x 멀티기기 스웜 실행 (#93)",
323
323
  subcommands: {
324
+ run: "tfx swarm run <prd-path> — verb 명시 실행 (기본 경로와 동일)",
324
325
  plan: "tfx swarm plan <prd-path> [--json] — 실행 없이 계획만 출력",
325
326
  list: "tfx swarm list [--json] — 활성 스웜 세션 조회 (synapse status)",
326
327
  status: "tfx swarm status [--json] — list alias",
@@ -4760,7 +4761,9 @@ ${updateNotice}
4760
4761
  ${WHITE_BRIGHT}tfx tray${RESET} ${GRAY}Windows 시스템 트레이 실행${RESET}
4761
4762
  ${DIM} --detach${RESET} ${GRAY}백그라운드 트레이 프로세스로 분리${RESET}
4762
4763
  ${WHITE_BRIGHT}tfx multi${RESET} ${GRAY}멀티-CLI 팀 모드 (tmux + Hub)${RESET}
4763
- ${WHITE_BRIGHT}tfx swarm${RESET} ${GRAY}PRD 기반 worktree 격리 병렬 실행${RESET}
4764
+ ${WHITE_BRIGHT}tfx swarm${RESET} ${GRAY}PRD 기반 worktree 격리 병렬 실행 (run/plan/list)${RESET}
4765
+ ${WHITE_BRIGHT}tfx synapse${RESET} ${GRAY}스웜 세션 registry 조회 / lease 관리${RESET}
4766
+ ${WHITE_BRIGHT}tfx why${RESET} ${GRAY}경로의 마지막 커밋 X-Intent 트레일러 추출${RESET}
4764
4767
  ${WHITE_BRIGHT}tfx codex-team${RESET} ${GRAY}Codex 전용 팀 모드 (기본 lead/agents: codex)${RESET}
4765
4768
  ${WHITE_BRIGHT}tfx notion-read${RESET} ${GRAY}Notion 페이지 → 마크다운 (Codex/Gemini MCP)${RESET}
4766
4769
  ${WHITE_BRIGHT}tfx version${RESET} ${GRAY}버전 표시${RESET}
@@ -5829,6 +5832,7 @@ async function main() {
5829
5832
  ${WHITE_BRIGHT}${s.usage}${RESET}
5830
5833
 
5831
5834
  ${BOLD}Subcommands${RESET}
5835
+ ${WHITE_BRIGHT}tfx swarm run <prd>${RESET} ${GRAY}${s.subcommands.run}${RESET}
5832
5836
  ${WHITE_BRIGHT}tfx swarm plan <prd>${RESET} ${GRAY}${s.subcommands.plan}${RESET}
5833
5837
  ${WHITE_BRIGHT}tfx swarm list${RESET} ${GRAY}${s.subcommands.list}${RESET}
5834
5838
  ${WHITE_BRIGHT}tfx swarm status${RESET} ${GRAY}${s.subcommands.status}${RESET}
@@ -5850,11 +5854,15 @@ ${s.options.map((o) => ` ${DIM}${o.name.padEnd(16)}${RESET} ${GRAY}${o.descri
5850
5854
  await cmdSwarmPlan(cmdArgs.slice(1), { json: JSON_OUTPUT });
5851
5855
  return;
5852
5856
  }
5857
+ if (sub === "run") {
5858
+ await cmdSwarmRun(cmdArgs.slice(1), { json: JSON_OUTPUT });
5859
+ return;
5860
+ }
5853
5861
  if (!sub || sub.startsWith("--")) {
5854
5862
  throw createCliError("PRD 경로가 필요합니다", {
5855
5863
  exitCode: EXIT_ARG_ERROR,
5856
5864
  reason: "argError",
5857
- fix: "tfx swarm <prd-path> [--dry-run|--json|--filter <shard>]",
5865
+ fix: "tfx swarm [run] <prd-path> [--dry-run|--json|--filter <shard>]",
5858
5866
  });
5859
5867
  }
5860
5868
  await cmdSwarmRun(cmdArgs, { json: JSON_OUTPUT });
@@ -13,6 +13,54 @@ const RED = "\u001b[91m";
13
13
  const YELLOW = "\u001b[93m";
14
14
  const GRAY = "\u001b[90m";
15
15
 
16
+ /**
17
+ * #116-C: non-TTY background 환경에서 `tfx swarm` 실행은 codex worker spawn 이
18
+ * 무한 hang 한다 (stdin TTY 대기 또는 hub MCP lease race).
19
+ *
20
+ * - stdout/stdin 모두 non-TTY 이면 fail-fast — 명시 복구 경로 안내.
21
+ * - `TFX_ALLOW_NON_TTY_SWARM=1` opt-in 시 경고만 남기고 통과 (테스트/CI).
22
+ * - pure function — 테스트하기 쉽게 deps 주입 가능.
23
+ *
24
+ * @param {{
25
+ * stdoutIsTTY?: boolean,
26
+ * stdinIsTTY?: boolean,
27
+ * env?: Record<string,string|undefined>,
28
+ * }} [deps]
29
+ * @returns {{ ok: boolean, optIn: boolean, warnings: string[], reason?: string }}
30
+ */
31
+ export function assertTtyForSwarm(deps = {}) {
32
+ const stdoutIsTTY =
33
+ typeof deps.stdoutIsTTY === "boolean"
34
+ ? deps.stdoutIsTTY
35
+ : Boolean(process.stdout.isTTY);
36
+ const stdinIsTTY =
37
+ typeof deps.stdinIsTTY === "boolean"
38
+ ? deps.stdinIsTTY
39
+ : Boolean(process.stdin.isTTY);
40
+ const env = deps.env || process.env;
41
+ const warnings = [];
42
+
43
+ if (stdoutIsTTY || stdinIsTTY) {
44
+ return { ok: true, optIn: false, warnings };
45
+ }
46
+
47
+ if (env.TFX_ALLOW_NON_TTY_SWARM === "1") {
48
+ warnings.push(
49
+ "non-TTY 환경 감지 — TFX_ALLOW_NON_TTY_SWARM=1 opt-in 으로 진행합니다. codex worker spawn hang 가능성 존재 (#116-C).",
50
+ );
51
+ return { ok: true, optIn: true, warnings };
52
+ }
53
+
54
+ const reason =
55
+ "tfx swarm 은 TTY 가 필요합니다 — non-TTY 환경 (run_in_background, nohup 등) 에서 codex worker spawn 이 hang 합니다 (#116-C).\n" +
56
+ " 복구 경로:\n" +
57
+ " 1) 터미널에서 직접 실행: tfx swarm <prd>\n" +
58
+ " 2) tmux 경로: tfx multi --teammate-mode tmux --auto-attach --dashboard --assign ...\n" +
59
+ " 3) opt-in (위험): TFX_ALLOW_NON_TTY_SWARM=1 tfx swarm <prd>";
60
+
61
+ return { ok: false, optIn: false, warnings, reason };
62
+ }
63
+
16
64
  export function parseFlags(args) {
17
65
  const flags = {
18
66
  dryRun: false,
@@ -121,6 +169,14 @@ export async function cmdSwarmRun(args, { json = false } = {}) {
121
169
  return;
122
170
  }
123
171
 
172
+ const ttyGate = assertTtyForSwarm();
173
+ for (const w of ttyGate.warnings) {
174
+ console.error(` ${YELLOW}⚠${RESET} ${w}`);
175
+ }
176
+ if (!ttyGate.ok) {
177
+ throw new Error(ttyGate.reason);
178
+ }
179
+
124
180
  const logsDir =
125
181
  flags.logsDir ||
126
182
  join(process.cwd(), ".triflux", "swarm-logs", `run-${Date.now()}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "triflux",
3
- "version": "10.13.7",
3
+ "version": "10.13.9",
4
4
  "description": "CLI-first multi-model orchestrator for Claude Code — route tasks to Codex, Gemini, and Claude",
5
5
  "type": "module",
6
6
  "bin": {