@triflux/remote 10.50.2 → 10.52.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.
Files changed (134) hide show
  1. package/cto/brief.mjs +0 -20
  2. package/cto/collect.mjs +1 -200
  3. package/cto/current.schema.json +2 -23
  4. package/cto/hygiene.mjs +2 -61
  5. package/cto/lake-root.mjs +0 -56
  6. package/cto/status.mjs +5 -187
  7. package/hub/index.mjs +0 -17
  8. package/hub/team/agent-map.json +0 -1
  9. package/hub/team/agents-row-attach.py +120 -0
  10. package/hub/team/agents-row.mjs +200 -0
  11. package/hub/team/ansi.mjs +1 -35
  12. package/hub/team/backend.mjs +9 -145
  13. package/hub/team/claude-daemon-control.mjs +1 -2
  14. package/hub/team/claude-session-projection.mjs +5 -73
  15. package/hub/team/cli/commands/attach.mjs +1 -17
  16. package/hub/team/cli/commands/control.mjs +12 -38
  17. package/hub/team/cli/commands/debug.mjs +2 -39
  18. package/hub/team/cli/commands/focus.mjs +5 -31
  19. package/hub/team/cli/commands/interrupt.mjs +6 -25
  20. package/hub/team/cli/commands/kill.mjs +1 -30
  21. package/hub/team/cli/commands/list.mjs +0 -16
  22. package/hub/team/cli/commands/send.mjs +6 -26
  23. package/hub/team/cli/commands/start/index.mjs +42 -133
  24. package/hub/team/cli/commands/start/parse-args.mjs +15 -40
  25. package/hub/team/cli/commands/start/start-headless.mjs +138 -105
  26. package/hub/team/cli/commands/start/start-mux.mjs +2 -7
  27. package/hub/team/cli/commands/status.mjs +2 -57
  28. package/hub/team/cli/commands/stop.mjs +14 -19
  29. package/hub/team/cli/help.mjs +4 -5
  30. package/hub/team/cli/manifest.mjs +5 -1
  31. package/hub/team/cli/render.mjs +0 -12
  32. package/hub/team/cli/services/runtime-mode.mjs +27 -98
  33. package/hub/team/cli/services/state-store.mjs +5 -20
  34. package/hub/team/handoff.mjs +3 -34
  35. package/hub/team/headless.mjs +108 -1296
  36. package/hub/team/orchestrator.mjs +4 -30
  37. package/hub/team/pane.mjs +6 -28
  38. package/hub/team/psmux.mjs +3 -34
  39. package/hub/team/remote-session.mjs +1 -2
  40. package/hub/team/session-context.mjs +1 -1
  41. package/hub/team/session.mjs +7 -91
  42. package/hub/team/tui-core.mjs +1 -1
  43. package/hub/team/tui-viewer.mjs +11 -58
  44. package/hub/team/tui.mjs +8 -306
  45. package/hub/team/uds-orchestrator.mjs +2 -580
  46. package/hub/team/wt-manager.mjs +2 -2
  47. package/hub/workers/factory.mjs +6 -84
  48. package/hub/workers/interface.mjs +1 -7
  49. package/hub/workers/lib/jsonrpc-core.mjs +11 -0
  50. package/hub/workers/lib/jsonrpc-ws-uds.mjs +5 -8
  51. package/package.json +3 -8
  52. package/scripts/lib/agent-route-policy.mjs +143 -62
  53. package/scripts/lib/cli-claude.mjs +0 -14
  54. package/scripts/lib/env-probe.mjs +2 -131
  55. package/scripts/lib/gemini-profiles.mjs +2 -69
  56. package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
  57. package/scripts/lib/machine-profile.mjs +3 -17
  58. package/scripts/lib/mcp-filter.mjs +109 -53
  59. package/scripts/lib/mcp-guard-engine.mjs +40 -98
  60. package/scripts/lib/mcp-health.mjs +2 -2
  61. package/scripts/lib/mcp-server-catalog.mjs +2 -2
  62. package/scripts/lib/psmux-info.mjs +11 -59
  63. package/scripts/lib/skill-template.mjs +0 -245
  64. package/hub/hub-lifecycle.mjs +0 -248
  65. package/hub/pipe.mjs +0 -825
  66. package/hub/promote-penalties.mjs +0 -138
  67. package/hub/public/dashboard.html +0 -416
  68. package/hub/schema.sql +0 -209
  69. package/hub/server.mjs +0 -3048
  70. package/hub/store-adapter.mjs +0 -182
  71. package/hub/store.mjs +0 -1736
  72. package/hub/team/build-worker-prompt.mjs +0 -92
  73. package/hub/team/check-mcp-hub.mjs +0 -49
  74. package/hub/team/claude-native-bridge.mjs +0 -987
  75. package/hub/team/cli/commands/start/start-in-process.mjs +0 -50
  76. package/hub/team/cli/commands/start/start-wt.mjs +0 -86
  77. package/hub/team/cli/services/hub-client.mjs +0 -264
  78. package/hub/team/cli/services/native-control.mjs +0 -145
  79. package/hub/team/codex-review.mjs +0 -520
  80. package/hub/team/conductor-mesh-bridge.mjs +0 -122
  81. package/hub/team/conductor-registry.mjs +0 -121
  82. package/hub/team/conductor.mjs +0 -1529
  83. package/hub/team/consensus-meta.mjs +0 -176
  84. package/hub/team/cto-auto-collect.mjs +0 -115
  85. package/hub/team/cto-pull.mjs +0 -31
  86. package/hub/team/daemon-pty-tmux-bridge.mjs +0 -146
  87. package/hub/team/dashboard.mjs +0 -322
  88. package/hub/team/event-log.mjs +0 -84
  89. package/hub/team/execution-mode.mjs +0 -324
  90. package/hub/team/extract-completion-payload.mjs +0 -110
  91. package/hub/team/headless-bridge-session.mjs +0 -17
  92. package/hub/team/health-probe.mjs +0 -435
  93. package/hub/team/interactive-native-launcher.mjs +0 -119
  94. package/hub/team/interactive-native-worker.mjs +0 -80
  95. package/hub/team/interactive-tui-transport.mjs +0 -479
  96. package/hub/team/launcher-template.mjs +0 -144
  97. package/hub/team/lead-control.mjs +0 -191
  98. package/hub/team/mcp-selector.mjs +0 -145
  99. package/hub/team/native-supervisor.mjs +0 -460
  100. package/hub/team/native.mjs +0 -727
  101. package/hub/team/recovery-store.mjs +0 -149
  102. package/hub/team/remote-probe.mjs +0 -292
  103. package/hub/team/remote-watcher.mjs +0 -506
  104. package/hub/team/runtime-strategy.mjs +0 -200
  105. package/hub/team/schemas/worker-completion.json +0 -39
  106. package/hub/team/sentinel-capture.mjs +0 -127
  107. package/hub/team/session-sync.mjs +0 -316
  108. package/hub/team/swarm-cli.mjs +0 -390
  109. package/hub/team/swarm-hypervisor.mjs +0 -2206
  110. package/hub/team/swarm-intent.mjs +0 -216
  111. package/hub/team/swarm-locks.mjs +0 -335
  112. package/hub/team/swarm-planner.mjs +0 -371
  113. package/hub/team/swarm-preflight.mjs +0 -366
  114. package/hub/team/swarm-reconciler.mjs +0 -242
  115. package/hub/team/synapse-cli.mjs +0 -458
  116. package/hub/team/synapse-http.mjs +0 -311
  117. package/hub/team/synapse-registry.mjs +0 -618
  118. package/hub/team/tui-remote-adapter.mjs +0 -377
  119. package/hub/team/tui-synapse.mjs +0 -223
  120. package/hub/team/worker-completion-validator.mjs +0 -79
  121. package/hub/team/worker-sandbox.mjs +0 -125
  122. package/hub/team/worktree-lifecycle.mjs +0 -639
  123. package/hub/team/wt-templates.json +0 -43
  124. package/hub/tools.mjs +0 -900
  125. package/hub/workers/antigravity-route-worker.mjs +0 -179
  126. package/hub/workers/codex-app-server-worker.mjs +0 -1067
  127. package/hub/workers/delegator-mcp.mjs +0 -1098
  128. package/hub/workers/lib/jsonrpc-stdio.mjs +0 -204
  129. package/scripts/lib/claudemd-scanner.mjs +0 -223
  130. package/scripts/lib/context.mjs +0 -67
  131. package/scripts/lib/dynamic-route-cli.mjs +0 -107
  132. package/scripts/lib/handoff.mjs +0 -187
  133. package/scripts/lib/logger.mjs +0 -122
  134. package/scripts/lib/stealth-fetch.mjs +0 -176
@@ -1,153 +1,17 @@
1
- // hub/team/backend.mjs — CLI 백엔드 추상화 레이어
2
- // 각 CLI(codex/antigravity/claude)의 명령 빌드 로직을 클래스로 캡슐화한다.
3
- // v7.2.2
4
- import { writeFileSync } from "node:fs";
5
- import { createRequire } from "node:module";
6
-
7
- import { buildExecArgs } from "@triflux/core/hub/codex-adapter.mjs";
8
1
  import { IS_WINDOWS } from "@triflux/core/hub/platform.mjs";
2
+ import { powershellSingleQuote, shellQuote } from "./terminal-opener.mjs";
9
3
 
10
- export function buildGeminiCommand(prompt, resultFile, { isWindows } = {}) {
11
- return buildAntigravityCommand(prompt, resultFile, { isWindows });
12
- }
13
-
14
- export function buildAntigravityCommand(
15
- prompt,
16
- resultFile,
17
- { isWindows } = {},
18
- ) {
19
- // Persist prompt to a sibling file so the shell never interpolates raw
20
- // prompt content. Without this the Unix branch let `$()`, backticks, `\\`,
21
- // and `"` reach the shell verbatim (P1: shell injection).
22
- const promptFile = `${resultFile}.prompt`;
23
- writeFileSync(promptFile, prompt);
24
- // agy 1.1.27 부터 --print 는 값(프롬프트)이 필수이고 stdin 프롬프트는 거부된다.
25
- // 파일 내용을 셸이 재해석하지 않는 형태(따옴표 안 명령 치환 / Get-Content 식)로 값에 넣는다.
26
- if (isWindows) {
27
- return `agy --dangerously-skip-permissions --print (Get-Content -Raw '${promptFile}') > '${resultFile}' 2>'${resultFile}.err'`;
28
- }
29
- return `agy --dangerously-skip-permissions --print "$(cat '${promptFile}')" > '${resultFile}' 2>'${resultFile}.err'`;
30
- }
31
-
32
- const _require = createRequire(import.meta.url);
33
-
34
- // ── 백엔드 클래스 ──────────────────────────────────────────────────────────
35
-
36
- export class CodexBackend {
37
- name() {
38
- return "codex";
39
- }
40
- command() {
41
- return "codex";
42
- }
43
-
44
- /**
45
- * @param {string} prompt — 프롬프트 (프롬프트 파일 경로가 아닌 PowerShell 표현식)
46
- * @param {string} resultFile — 결과 저장 경로
47
- * @param {object} [opts]
48
- * @returns {string} PowerShell 명령 (cls 제외)
49
- */
50
- buildArgs(prompt, resultFile, opts = {}) {
51
- return buildExecArgs({ prompt, resultFile, ...opts });
52
- }
53
-
54
- env() {
55
- return {};
56
- }
57
- }
58
-
59
- export class GeminiBackend {
60
- name() {
61
- return "gemini";
62
- }
63
- command() {
64
- return "agy";
65
- }
66
-
67
- buildArgs(prompt, resultFile, opts = {}) {
68
- return buildGeminiCommand(prompt, resultFile, { isWindows: IS_WINDOWS });
69
- }
70
-
71
- env() {
72
- return {};
4
+ class ClaudeBackend {
5
+ buildArgs(prompt, resultFile, { isWindows = IS_WINDOWS } = {}) {
6
+ const quote = isWindows ? powershellSingleQuote : shellQuote;
7
+ const args = ["--print", prompt, "--output-format", "text"];
8
+ return `claude ${args.map(quote).join(" ")} > ${quote(resultFile)} 2>&1`;
73
9
  }
74
10
  }
75
11
 
76
- export class ClaudeBackend {
77
- name() {
78
- return "claude";
79
- }
80
- command() {
81
- return "claude";
82
- }
83
-
84
- buildArgs(prompt, resultFile, opts = {}) {
85
- return `claude --print ${prompt} --output-format text > '${resultFile}' 2>&1`;
86
- }
12
+ const claude = new ClaudeBackend();
87
13
 
88
- env() {
89
- return {};
90
- }
91
- }
92
-
93
- export class AntigravityBackend {
94
- name() {
95
- return "antigravity";
96
- }
97
- command() {
98
- return "agy";
99
- }
100
-
101
- buildArgs(prompt, resultFile, opts = {}) {
102
- return buildAntigravityCommand(prompt, resultFile, {
103
- isWindows: IS_WINDOWS,
104
- ...opts,
105
- });
106
- }
107
-
108
- env() {
109
- return {};
110
- }
111
- }
112
-
113
- // ── 레지스트리 ─────────────────────────────────────────────────────────────
114
-
115
- /** @type {Map<string, CodexBackend|GeminiBackend|ClaudeBackend|AntigravityBackend>} */
116
- const backends = new Map([
117
- ["codex", new CodexBackend()],
118
- ["gemini", new GeminiBackend()],
119
- ["claude", new ClaudeBackend()],
120
- ["antigravity", new AntigravityBackend()],
121
- ]);
122
-
123
- /**
124
- * 백엔드 이름으로 조회한다.
125
- * @param {string} name — "codex" | "gemini" | "claude" | "antigravity"
126
- * @returns {CodexBackend|GeminiBackend|ClaudeBackend|AntigravityBackend}
127
- * @throws {Error} 등록되지 않은 이름
128
- */
129
14
  export function getBackend(name) {
130
- const b = backends.get(name);
131
- if (!b) throw new Error(`지원하지 않는 CLI: ${name}`);
132
- return b;
133
- }
134
-
135
- /**
136
- * 에이전트명 또는 CLI명을 Backend로 해석한다.
137
- * agent-map.json을 통해 에이전트명 → CLI명으로 변환 후 레지스트리에서 조회한다.
138
- * @param {string} agentOrCli — "executor", "codex", "designer" 등
139
- * @returns {CodexBackend|GeminiBackend|ClaudeBackend|AntigravityBackend}
140
- */
141
- export function getBackendForAgent(agentOrCli) {
142
- const agentMap = _require("./agent-map.json");
143
- const cliName = agentMap[agentOrCli] || agentOrCli;
144
- return getBackend(cliName);
145
- }
146
-
147
- /**
148
- * 등록된 모든 백엔드를 반환한다.
149
- * @returns {Array<CodexBackend|GeminiBackend|ClaudeBackend|AntigravityBackend>}
150
- */
151
- export function listBackends() {
152
- return Array.from(backends.values());
15
+ if (name !== "claude") throw new Error(`지원하지 않는 CLI: ${name}`);
16
+ return claude;
153
17
  }
@@ -1775,8 +1775,7 @@ export async function dispatchClaudeDaemonJob({
1775
1775
  /**
1776
1776
  * dispatchClaudeDaemonJob 의 대칭 정리 경로.
1777
1777
  * removeProjection + killDaemonJob (+ optional removeClaudeJobState) 를
1778
- * 모두 시도하며, 각 스텝 오류는 기존 close()/cleanupDaemonDispatches 와
1779
- * 동일하게 .catch 로 삼킨다.
1778
+ * 모두 시도하며, 각 스텝 오류는 .catch 로 삼킨다.
1780
1779
  */
1781
1780
  export async function teardownClaudeDaemonJob({
1782
1781
  controlSock,
@@ -1,12 +1,11 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
 
4
- export function normalizeClaudeBridgeSessionId(bridgeSessionId, short) {
4
+ // 실제 bridge ID 가 없으면 만들어 내지 않는다. 추정 ID 는 붙을 수 있는 세션처럼 보이게 한다.
5
+ export function normalizeClaudeBridgeSessionId(bridgeSessionId) {
5
6
  const value = String(bridgeSessionId || "").trim();
6
- if (value.startsWith("session_")) return value;
7
7
  if (value.startsWith("cse_")) return `session_${value.slice(4)}`;
8
- if (value) return value;
9
- return `session_${short}`;
8
+ return value;
10
9
  }
11
10
 
12
11
  export function buildClaudeSessionProjection({
@@ -30,6 +29,7 @@ export function buildClaudeSessionProjection({
30
29
  if (!cwd) throw new Error("cwd is required");
31
30
  if (!name) throw new Error("name is required");
32
31
  if (!agent) throw new Error("agent is required");
32
+ const bridge = normalizeClaudeBridgeSessionId(bridgeSessionId);
33
33
  return {
34
34
  pid,
35
35
  sessionId,
@@ -45,7 +45,7 @@ export function buildClaudeSessionProjection({
45
45
  jobId: short,
46
46
  status,
47
47
  updatedAt,
48
- bridgeSessionId: normalizeClaudeBridgeSessionId(bridgeSessionId, short),
48
+ ...(bridge ? { bridgeSessionId: bridge } : {}),
49
49
  };
50
50
  }
51
51
 
@@ -58,74 +58,6 @@ export async function writeClaudeSessionProjection(sessionsDir, projection) {
58
58
  return sessionPath;
59
59
  }
60
60
 
61
- export async function updateClaudeSessionProjection(sessionPath, patch) {
62
- const parsed = JSON.parse(await fs.readFile(sessionPath, "utf8"));
63
- const next = {
64
- ...parsed,
65
- ...patch,
66
- updatedAt: patch.updatedAt || Date.now(),
67
- };
68
- await fs.writeFile(sessionPath, `${JSON.stringify(next)}\n`, "utf8");
69
- return next;
70
- }
71
-
72
- function projectionSessionId(projection) {
73
- return String(
74
- projection?.sessionId ?? projection?.session_id ?? projection?.id ?? "",
75
- ).trim();
76
- }
77
-
78
- export async function findClaudeSessionProjectionBySessionId(
79
- sessionsDir,
80
- sessionId,
81
- ) {
82
- const expected = String(sessionId || "").trim();
83
- if (!sessionsDir || !expected) return null;
84
- let entries;
85
- try {
86
- entries = await fs.readdir(sessionsDir, { withFileTypes: true });
87
- } catch (error) {
88
- if (error?.code === "ENOENT") return null;
89
- throw error;
90
- }
91
-
92
- for (const entry of entries) {
93
- if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
94
- const filePath = path.join(sessionsDir, entry.name);
95
- try {
96
- const projection = JSON.parse(await fs.readFile(filePath, "utf8"));
97
- if (projectionSessionId(projection) === expected) {
98
- return { path: filePath, projection };
99
- }
100
- } catch (error) {
101
- if (error instanceof SyntaxError) continue;
102
- if (error?.code === "ENOENT") continue;
103
- throw error;
104
- }
105
- }
106
- return null;
107
- }
108
-
109
- export async function refreshClaudeSessionProjectionCwd({
110
- sessionsDir,
111
- sessionId,
112
- cwd,
113
- updatedAt = Date.now(),
114
- } = {}) {
115
- const nextCwd = String(cwd || "").trim();
116
- if (!nextCwd) return { updated: false, reason: "missing_cwd" };
117
- const found = await findClaudeSessionProjectionBySessionId(
118
- sessionsDir,
119
- sessionId,
120
- );
121
- if (!found) return { updated: false, reason: "projection_not_found" };
122
- const projection = await updateClaudeSessionProjection(found.path, {
123
- cwd: nextCwd,
124
- updatedAt,
125
- });
126
- return { updated: true, path: found.path, projection };
127
- }
128
-
129
61
  export async function removeClaudeSessionProjection(sessionPath) {
130
62
  await fs.rm(sessionPath, { force: true });
131
63
  }
@@ -6,11 +6,7 @@ import {
6
6
  launchAttachInWindowsTerminal,
7
7
  wantsWtAttachFallback,
8
8
  } from "../services/attach-fallback.mjs";
9
- import {
10
- isNativeMode,
11
- isTeamAlive,
12
- isWtMode,
13
- } from "../services/runtime-mode.mjs";
9
+ import { isTeamAlive } from "../services/runtime-mode.mjs";
14
10
  import { loadTeamState } from "../services/state-store.mjs";
15
11
 
16
12
  export async function teamAttach(args = []) {
@@ -19,18 +15,6 @@ export async function teamAttach(args = []) {
19
15
  console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
20
16
  return;
21
17
  }
22
- if (isNativeMode(state)) {
23
- console.log(
24
- `\n ${DIM}in-process 모드는 별도 attach가 없습니다.${RESET}\n ${DIM}상태 확인: tfx multi status${RESET}\n`,
25
- );
26
- return;
27
- }
28
- if (isWtMode(state)) {
29
- console.log(
30
- `\n ${DIM}wt 모드는 attach 개념이 없습니다 (Windows Terminal pane가 독립 실행됨).${RESET}\n ${DIM}재실행/정리는: tfx multi stop${RESET}\n`,
31
- );
32
- return;
33
- }
34
18
 
35
19
  try {
36
20
  attachSession(state.sessionName);
@@ -1,18 +1,15 @@
1
1
  import { injectPrompt, sendKeys } from "../../pane.mjs";
2
- import { DIM, RESET, WHITE, YELLOW } from "../../shared.mjs";
3
- import { ok, warn } from "../render.mjs";
4
- import { publishLeadControl } from "../services/hub-client.mjs";
2
+ import { DIM, RESET, WHITE } from "../../shared.mjs";
3
+ import { ok } from "../render.mjs";
5
4
  import { resolveMember } from "../services/member-selector.mjs";
6
- import { nativeRequest } from "../services/native-control.mjs";
7
- import {
8
- isNativeMode,
9
- isTeamAlive,
10
- isWtMode,
11
- } from "../services/runtime-mode.mjs";
5
+ import { isTeamAlive } from "../services/runtime-mode.mjs";
12
6
  import { loadTeamState } from "../services/state-store.mjs";
13
7
 
14
8
  export async function teamControl(args = []) {
15
9
  const state = loadTeamState();
10
+ if (state?.teammateMode === "headless") {
11
+ throw new Error("headless 실행에는 지원하지 않는다");
12
+ }
16
13
  if (!state || !isTeamAlive(state)) {
17
14
  console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
18
15
  return;
@@ -30,35 +27,12 @@ export async function teamControl(args = []) {
30
27
  );
31
28
  return;
32
29
  }
33
- if (isWtMode(state)) {
34
- console.log(
35
- `\n ${YELLOW}⚠${RESET} wt 모드는 Hub direct/control 주입 경로가 비활성입니다.\n ${DIM}수동 제어: 해당 pane에서 직접 명령/인터럽트를 수행하세요.${RESET}\n`,
36
- );
37
- return;
38
- }
39
-
40
- let directOk = false;
41
- if (isNativeMode(state)) {
42
- directOk = !!(
43
- await nativeRequest(state, "/control", {
44
- member: member.name,
45
- command,
46
- reason,
47
- })
48
- )?.ok;
49
- } else {
50
- injectPrompt(
51
- member.pane,
52
- `[LEAD CONTROL] command=${command}${reason ? ` reason=${reason}` : ""}`,
53
- );
54
- if (command === "interrupt") sendKeys(member.pane, "C-c");
55
- directOk = true;
56
- }
30
+ injectPrompt(
31
+ member.pane,
32
+ `[LEAD CONTROL] command=${command}${reason ? ` reason=${reason}` : ""}`,
33
+ );
34
+ if (command === "interrupt") sendKeys(member.pane, "C-c");
57
35
 
58
- const published = await publishLeadControl(state, member, command, reason);
59
- if (directOk && published)
60
- ok(`${member.name} 제어 전송 (${command}, direct + hub)`);
61
- else if (directOk) ok(`${member.name} 제어 전송 (${command}, direct only)`);
62
- else warn(`${member.name} 제어 전송 실패 (${command})`);
36
+ ok(`${member.name} 제어 전송 (${command})`);
63
37
  console.log("");
64
38
  }
@@ -2,19 +2,11 @@ import {
2
2
  capturePaneOutput,
3
3
  detectMultiplexer,
4
4
  getSessionAttachedCount,
5
- hasWindowsTerminal,
6
- hasWindowsTerminalSession,
7
5
  listSessions,
8
6
  } from "../../session.mjs";
9
7
  import { AMBER, BOLD, DIM, RESET } from "../../shared.mjs";
10
- import { formatCompletionSuffix } from "../render.mjs";
11
- import { getHubInfo, nativeGetStatus } from "../services/hub-client.mjs";
12
- import {
13
- isNativeMode,
14
- isTeamAlive,
15
- isWtMode,
16
- } from "../services/runtime-mode.mjs";
17
- import { loadTeamState, TEAM_PROFILE } from "../services/state-store.mjs";
8
+ import { isTeamAlive } from "../services/runtime-mode.mjs";
9
+ import { loadTeamState } from "../services/state-store.mjs";
18
10
 
19
11
  export async function teamDebug(args = []) {
20
12
  const state = loadTeamState();
@@ -23,7 +15,6 @@ export async function teamDebug(args = []) {
23
15
  flagIndex === -1
24
16
  ? 20
25
17
  : Math.max(3, parseInt(args[flagIndex + 1] || "20", 10) || 20);
26
- const hub = await getHubInfo();
27
18
 
28
19
  console.log(`\n ${AMBER}${BOLD}⬡ Team Debug${RESET}\n`);
29
20
  console.log(` platform: ${process.platform}`);
@@ -32,8 +23,6 @@ export async function teamDebug(args = []) {
32
23
  ` tty: stdout=${!!process.stdout.isTTY}, stdin=${!!process.stdin.isTTY}`,
33
24
  );
34
25
  console.log(` mux: ${detectMultiplexer() || "none"}`);
35
- console.log(` hub-pid: ${hub ? `${hub.pid}` : "-"}`);
36
- console.log(` hub-url: ${hub?.url || "-"}`);
37
26
  const sessions = listSessions();
38
27
  console.log(` sessions: ${sessions.length ? sessions.join(", ") : "-"}`);
39
28
 
@@ -44,7 +33,6 @@ export async function teamDebug(args = []) {
44
33
 
45
34
  console.log(`\n ${BOLD}state${RESET}`);
46
35
  console.log(` session: ${state.sessionName}`);
47
- console.log(` profile: ${state.profile || TEAM_PROFILE}`);
48
36
  console.log(` mode: ${state.teammateMode || "tmux"}`);
49
37
  console.log(` lead: ${state.lead}`);
50
38
  console.log(` agents: ${(state.agents || []).join(", ")}`);
@@ -53,31 +41,6 @@ export async function teamDebug(args = []) {
53
41
  ` attached: ${getSessionAttachedCount(state.sessionName) ?? "-"}`,
54
42
  );
55
43
 
56
- if (isWtMode(state)) {
57
- console.log(`\n ${BOLD}wt-session${RESET}`);
58
- console.log(` window: ${state?.wt?.windowId ?? 0}`);
59
- console.log(` layout: ${state?.wt?.layout || state?.layout || "-"}`);
60
- console.log(
61
- ` panes: ${state?.wt?.paneCount ?? (state.members || []).length}`,
62
- );
63
- console.log(` wt.exe: ${hasWindowsTerminal() ? "yes" : "no"}`);
64
- console.log(` WT_SESSION:${hasWindowsTerminalSession() ? "yes" : "no"}`);
65
- console.log("");
66
- return;
67
- }
68
-
69
- if (isNativeMode(state)) {
70
- console.log(`\n ${BOLD}native-members${RESET}`);
71
- const members = (await nativeGetStatus(state))?.data?.members || [];
72
- if (!members.length) console.log(` ${DIM}(no data)${RESET}`);
73
- for (const member of members)
74
- console.log(
75
- ` - ${member.name}: ${member.status}${formatCompletionSuffix(member)}${member.lastPreview ? ` ${DIM}${member.lastPreview}${RESET}` : ""}`,
76
- );
77
- console.log("");
78
- return;
79
- }
80
-
81
44
  console.log(
82
45
  `\n ${BOLD}pane-tail${RESET} ${DIM}(last ${lines} lines)${RESET}`,
83
46
  );
@@ -1,4 +1,4 @@
1
- import { attachSession, focusPane, focusWtPane } from "../../session.mjs";
1
+ import { attachSession, focusPane } from "../../session.mjs";
2
2
  import { DIM, RESET, WHITE } from "../../shared.mjs";
3
3
  import { fail, ok, warn } from "../render.mjs";
4
4
  import {
@@ -7,25 +7,18 @@ import {
7
7
  wantsWtAttachFallback,
8
8
  } from "../services/attach-fallback.mjs";
9
9
  import { resolveMember } from "../services/member-selector.mjs";
10
- import {
11
- isNativeMode,
12
- isTeamAlive,
13
- isWtMode,
14
- } from "../services/runtime-mode.mjs";
10
+ import { isTeamAlive } from "../services/runtime-mode.mjs";
15
11
  import { loadTeamState } from "../services/state-store.mjs";
16
12
 
17
13
  export async function teamFocus(args = []) {
18
14
  const state = loadTeamState();
15
+ if (state?.teammateMode === "headless") {
16
+ throw new Error("headless 실행에는 지원하지 않는다");
17
+ }
19
18
  if (!state || !isTeamAlive(state)) {
20
19
  console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
21
20
  return;
22
21
  }
23
- if (isNativeMode(state)) {
24
- console.log(
25
- `\n ${DIM}in-process 모드는 focus/attach 개념이 없습니다.${RESET}\n ${DIM}직접 지시: tfx multi send <대상> "메시지"${RESET}\n`,
26
- );
27
- return;
28
- }
29
22
 
30
23
  const member = resolveMember(state, args[0]);
31
24
  if (!member) {
@@ -35,25 +28,6 @@ export async function teamFocus(args = []) {
35
28
  return;
36
29
  }
37
30
 
38
- if (isWtMode(state)) {
39
- const paneIndex = Number(/^wt:(\d+)$/.exec(member.pane || "")?.[1]);
40
- if (!Number.isFinite(paneIndex)) {
41
- console.log(
42
- `\n ${DIM}wt pane 인덱스 파싱 실패: ${member.pane}${RESET}\n`,
43
- );
44
- return;
45
- }
46
- if (
47
- focusWtPane(paneIndex, {
48
- layout: state?.wt?.layout || state?.layout || "1xN",
49
- })
50
- )
51
- ok(`${member.name} pane 포커스 이동 (wt)`);
52
- else warn("wt pane 포커스 이동 실패 (WT_SESSION/wt.exe 상태 확인 필요)");
53
- console.log("");
54
- return;
55
- }
56
-
57
31
  focusPane(member.pane, { zoom: false });
58
32
  try {
59
33
  attachSession(state.sessionName);
@@ -1,17 +1,15 @@
1
1
  import { sendKeys } from "../../pane.mjs";
2
- import { DIM, RESET, WHITE, YELLOW } from "../../shared.mjs";
3
- import { ok, warn } from "../render.mjs";
2
+ import { DIM, RESET, WHITE } from "../../shared.mjs";
3
+ import { ok } from "../render.mjs";
4
4
  import { resolveMember } from "../services/member-selector.mjs";
5
- import { nativeRequest } from "../services/native-control.mjs";
6
- import {
7
- isNativeMode,
8
- isTeamAlive,
9
- isWtMode,
10
- } from "../services/runtime-mode.mjs";
5
+ import { isTeamAlive } from "../services/runtime-mode.mjs";
11
6
  import { loadTeamState } from "../services/state-store.mjs";
12
7
 
13
8
  export async function teamInterrupt(args = []) {
14
9
  const state = loadTeamState();
10
+ if (state?.teammateMode === "headless") {
11
+ throw new Error("headless 실행에는 지원하지 않는다");
12
+ }
15
13
  if (!state || !isTeamAlive(state)) {
16
14
  console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
17
15
  return;
@@ -24,23 +22,6 @@ export async function teamInterrupt(args = []) {
24
22
  );
25
23
  return;
26
24
  }
27
- if (isWtMode(state)) {
28
- console.log(
29
- `\n ${YELLOW}⚠${RESET} wt 모드에서는 pane stdin 주입이 지원되지 않아 interrupt를 자동 전송할 수 없습니다.\n ${DIM}수동으로 해당 pane에서 Ctrl+C를 입력하세요.${RESET}\n`,
30
- );
31
- return;
32
- }
33
-
34
- if (isNativeMode(state)) {
35
- const result = await nativeRequest(state, "/interrupt", {
36
- member: member.name,
37
- });
38
- (result?.ok ? ok : warn)(
39
- `${member.name} ${result?.ok ? "인터럽트 전송" : "인터럽트 실패"}`,
40
- );
41
- console.log("");
42
- return;
43
- }
44
25
 
45
26
  sendKeys(member.pane, "C-c");
46
27
  ok(`${member.name} 인터럽트 전송`);
@@ -1,39 +1,10 @@
1
- import { closeWtSession, killSession, listSessions } from "../../session.mjs";
1
+ import { killSession, listSessions } from "../../session.mjs";
2
2
  import { DIM, RESET } from "../../shared.mjs";
3
3
  import { ok } from "../render.mjs";
4
- import { nativeRequest } from "../services/native-control.mjs";
5
- import {
6
- isNativeMode,
7
- isTeamAlive,
8
- isWtMode,
9
- } from "../services/runtime-mode.mjs";
10
4
  import { clearTeamState, loadTeamState } from "../services/state-store.mjs";
11
5
 
12
6
  export async function teamKill() {
13
7
  const state = loadTeamState();
14
- if (state && isNativeMode(state) && isTeamAlive(state)) {
15
- await nativeRequest(state, "/stop", {});
16
- try {
17
- process.kill(state.native.supervisorPid, "SIGTERM");
18
- } catch {}
19
- clearTeamState(state.sessionId);
20
- ok(`종료: ${state.sessionName}`);
21
- console.log("");
22
- return;
23
- }
24
- if (state && isWtMode(state)) {
25
- const closed = closeWtSession({
26
- layout: state?.wt?.layout || state?.layout || "1xN",
27
- paneCount: state?.wt?.paneCount ?? (state.members || []).length,
28
- });
29
- clearTeamState(state.sessionId);
30
- ok(
31
- `종료: ${state.sessionName}${closed ? ` (${closed} panes closed)` : ""}`,
32
- );
33
- console.log("");
34
- return;
35
- }
36
-
37
8
  const sessions = listSessions();
38
9
  if (!sessions.length) {
39
10
  console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
@@ -1,23 +1,7 @@
1
1
  import { listSessions } from "../../session.mjs";
2
2
  import { AMBER, BOLD, DIM, GREEN, RESET } from "../../shared.mjs";
3
- import {
4
- isNativeMode,
5
- isTeamAlive,
6
- isWtMode,
7
- } from "../services/runtime-mode.mjs";
8
- import { loadTeamState } from "../services/state-store.mjs";
9
3
 
10
4
  export function teamList() {
11
- const state = loadTeamState();
12
- if (state && isTeamAlive(state) && (isNativeMode(state) || isWtMode(state))) {
13
- console.log(`\n ${AMBER}${BOLD}⬡ 팀 세션 목록${RESET}\n`);
14
- console.log(
15
- ` ${GREEN}●${RESET} ${state.sessionName} ${DIM}(${isNativeMode(state) ? "in-process" : "wt"})${RESET}`,
16
- );
17
- console.log("");
18
- return;
19
- }
20
-
21
5
  const sessions = listSessions();
22
6
  if (!sessions.length) {
23
7
  console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
@@ -1,17 +1,15 @@
1
1
  import { injectPrompt } from "../../pane.mjs";
2
- import { DIM, RESET, WHITE, YELLOW } from "../../shared.mjs";
3
- import { ok, warn } from "../render.mjs";
2
+ import { DIM, RESET, WHITE } from "../../shared.mjs";
3
+ import { ok } from "../render.mjs";
4
4
  import { resolveMember } from "../services/member-selector.mjs";
5
- import { nativeRequest } from "../services/native-control.mjs";
6
- import {
7
- isNativeMode,
8
- isTeamAlive,
9
- isWtMode,
10
- } from "../services/runtime-mode.mjs";
5
+ import { isTeamAlive } from "../services/runtime-mode.mjs";
11
6
  import { loadTeamState } from "../services/state-store.mjs";
12
7
 
13
8
  export async function teamSend(args = []) {
14
9
  const state = loadTeamState();
10
+ if (state?.teammateMode === "headless") {
11
+ throw new Error("headless 실행에는 지원하지 않는다");
12
+ }
15
13
  if (!state || !isTeamAlive(state)) {
16
14
  console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
17
15
  return;
@@ -25,24 +23,6 @@ export async function teamSend(args = []) {
25
23
  );
26
24
  return;
27
25
  }
28
- if (isWtMode(state)) {
29
- console.log(
30
- `\n ${YELLOW}⚠${RESET} wt 모드는 pane 프롬프트 자동 주입(send)이 지원되지 않습니다.\n ${DIM}수동 전달: 선택한 pane에 직접 붙여넣으세요.${RESET}\n`,
31
- );
32
- return;
33
- }
34
-
35
- if (isNativeMode(state)) {
36
- const result = await nativeRequest(state, "/send", {
37
- member: member.name,
38
- text: message,
39
- });
40
- (result?.ok ? ok : warn)(
41
- `${member.name}${result?.ok ? "에 메시지 주입 완료" : " 메시지 주입 실패"}`,
42
- );
43
- console.log("");
44
- return;
45
- }
46
26
 
47
27
  injectPrompt(member.pane, message);
48
28
  ok(`${member.name}에 메시지 주입 완료`);