triflux 9.7.13 → 9.8.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 (50) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.ko.md +2 -0
  4. package/README.md +2 -0
  5. package/bin/triflux.mjs +297 -47
  6. package/hooks/hook-registry.json +4 -4
  7. package/hub/fullcycle.mjs +96 -0
  8. package/hub/paths.mjs +30 -28
  9. package/hub/pipeline/index.mjs +318 -318
  10. package/hub/schema.sql +146 -146
  11. package/hub/team/cli/commands/kill.mjs +37 -37
  12. package/hub/team/cli/commands/stop.mjs +31 -31
  13. package/hub/team/cli/commands/task.mjs +30 -30
  14. package/hub/team/cli/services/hub-client.mjs +208 -208
  15. package/hub/team/cli/services/native-control.mjs +118 -118
  16. package/hub/team/cli/services/runtime-mode.mjs +62 -62
  17. package/hub/team/cli/services/state-store.mjs +48 -48
  18. package/hub/team/dashboard.mjs +274 -274
  19. package/hub/team/native.mjs +649 -649
  20. package/hub/team/psmux.mjs +68 -13
  21. package/hub/tools.mjs +554 -554
  22. package/hub/workers/claude-worker.mjs +423 -423
  23. package/hub/workers/codex-mcp.mjs +410 -410
  24. package/hub/workers/gemini-worker.mjs +429 -429
  25. package/hub/workers/interface.mjs +40 -40
  26. package/package.json +1 -1
  27. package/scripts/__tests__/remote-spawn-transfer.test.mjs +1 -1
  28. package/scripts/cache-warmup.mjs +1 -0
  29. package/scripts/claude-logged.ps1 +54 -0
  30. package/scripts/demo-tui.mjs +59 -0
  31. package/scripts/headless-guard.mjs +4 -7
  32. package/scripts/hub-ensure.mjs +120 -120
  33. package/scripts/lib/psmux-info.mjs +119 -0
  34. package/scripts/lib/remote-spawn-transfer.mjs +1 -1
  35. package/scripts/setup.mjs +150 -6
  36. package/scripts/tfx-route-post.mjs +90 -13
  37. package/scripts/token-snapshot.mjs +575 -575
  38. package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
  39. package/skills/.omc/state/idle-notif-cooldown.json +3 -0
  40. package/skills/.omc/state/last-tool-error.json +7 -0
  41. package/skills/.omc/state/subagent-tracking.json +7 -0
  42. package/skills/tfx-codex-swarm/SKILL.md +40 -5
  43. package/skills/tfx-codex-swarm/mcp-daemon/register-autostart.ps1 +32 -0
  44. package/skills/tfx-doctor/SKILL.md +3 -0
  45. package/skills/tfx-fullcycle/SKILL.md +79 -4
  46. package/skills/tfx-hub/SKILL.md +3 -1
  47. package/skills/tfx-psmux-rules/SKILL.md +53 -31
  48. package/skills/tfx-remote-spawn/references/hosts.json +16 -16
  49. package/skills/tfx-setup/SKILL.md +9 -0
  50. package/tui/doctor.mjs +1 -0
package/hub/schema.sql CHANGED
@@ -1,146 +1,146 @@
1
- -- tfx-hub 상태 저장소 스키마
2
- -- SQLite WAL 모드 기반 메시지 버스
3
-
4
- -- 에이전트 등록 테이블
5
- CREATE TABLE IF NOT EXISTS agents (
6
- agent_id TEXT PRIMARY KEY,
7
- cli TEXT NOT NULL CHECK (cli IN ('codex','gemini','claude','other')),
8
- pid INTEGER,
9
- capabilities_json TEXT NOT NULL DEFAULT '[]',
10
- topics_json TEXT NOT NULL DEFAULT '[]',
11
- last_seen_ms INTEGER NOT NULL,
12
- lease_expires_ms INTEGER NOT NULL,
13
- status TEXT NOT NULL CHECK (status IN ('online','stale','offline')),
14
- metadata_json TEXT NOT NULL DEFAULT '{}'
15
- );
16
-
17
- -- 메시지 테이블
18
- CREATE TABLE IF NOT EXISTS messages (
19
- id TEXT PRIMARY KEY,
20
- type TEXT NOT NULL CHECK (type IN ('request','response','event','handoff','human_request','human_response','system')),
21
- from_agent TEXT NOT NULL,
22
- to_agent TEXT NOT NULL,
23
- topic TEXT NOT NULL,
24
- priority INTEGER NOT NULL CHECK (priority BETWEEN 1 AND 9),
25
- ttl_ms INTEGER NOT NULL,
26
- created_at_ms INTEGER NOT NULL,
27
- expires_at_ms INTEGER NOT NULL,
28
- correlation_id TEXT NOT NULL,
29
- trace_id TEXT NOT NULL,
30
- payload_json TEXT NOT NULL DEFAULT '{}',
31
- status TEXT NOT NULL CHECK (status IN ('queued','delivered','acked','expired','dead_letter'))
32
- );
33
-
34
- -- 메시지 수신함 (배달 추적)
35
- CREATE TABLE IF NOT EXISTS message_inbox (
36
- delivery_id INTEGER PRIMARY KEY AUTOINCREMENT,
37
- message_id TEXT NOT NULL,
38
- agent_id TEXT NOT NULL,
39
- delivered_at_ms INTEGER,
40
- acked_at_ms INTEGER,
41
- attempts INTEGER NOT NULL DEFAULT 0,
42
- UNIQUE(message_id, agent_id),
43
- FOREIGN KEY(message_id) REFERENCES messages(id) ON DELETE CASCADE
44
- );
45
-
46
- -- 사용자 입력 요청 테이블
47
- CREATE TABLE IF NOT EXISTS human_requests (
48
- request_id TEXT PRIMARY KEY,
49
- requester_agent TEXT NOT NULL,
50
- kind TEXT NOT NULL CHECK (kind IN ('captcha','approval','credential','choice','text')),
51
- prompt TEXT NOT NULL,
52
- schema_json TEXT NOT NULL DEFAULT '{}',
53
- state TEXT NOT NULL CHECK (state IN ('pending','accepted','declined','cancelled','timed_out')),
54
- deadline_ms INTEGER NOT NULL,
55
- default_action TEXT NOT NULL CHECK (default_action IN ('decline','cancel','timeout_continue')),
56
- correlation_id TEXT NOT NULL,
57
- trace_id TEXT NOT NULL,
58
- response_json TEXT
59
- );
60
-
61
- -- 데드 레터 큐
62
- CREATE TABLE IF NOT EXISTS dead_letters (
63
- message_id TEXT PRIMARY KEY,
64
- reason TEXT NOT NULL,
65
- failed_at_ms INTEGER NOT NULL,
66
- last_error TEXT
67
- );
68
-
69
- -- 인덱스
70
- CREATE INDEX IF NOT EXISTS idx_messages_status ON messages(status);
71
- CREATE INDEX IF NOT EXISTS idx_messages_to_agent ON messages(to_agent, status);
72
- CREATE INDEX IF NOT EXISTS idx_messages_correlation ON messages(correlation_id);
73
- CREATE INDEX IF NOT EXISTS idx_messages_trace ON messages(trace_id);
74
- CREATE INDEX IF NOT EXISTS idx_messages_expires ON messages(expires_at_ms);
75
- CREATE INDEX IF NOT EXISTS idx_messages_priority ON messages(priority DESC, created_at_ms ASC);
76
- CREATE INDEX IF NOT EXISTS idx_inbox_agent ON message_inbox(agent_id, delivered_at_ms);
77
- CREATE INDEX IF NOT EXISTS idx_inbox_message ON message_inbox(message_id);
78
- CREATE INDEX IF NOT EXISTS idx_human_requests_state ON human_requests(state);
79
- CREATE INDEX IF NOT EXISTS idx_agents_status ON agents(status);
80
- CREATE INDEX IF NOT EXISTS idx_agents_lease ON agents(lease_expires_ms);
81
-
82
- -- Assign Job 테이블
83
- CREATE TABLE IF NOT EXISTS assign_jobs (
84
- job_id TEXT PRIMARY KEY,
85
- supervisor_agent TEXT NOT NULL,
86
- worker_agent TEXT NOT NULL,
87
- topic TEXT NOT NULL DEFAULT 'assign.job',
88
- task TEXT NOT NULL DEFAULT '',
89
- payload_json TEXT NOT NULL DEFAULT '{}',
90
- status TEXT NOT NULL CHECK (status IN ('queued','running','succeeded','failed','timed_out')),
91
- attempt INTEGER NOT NULL DEFAULT 1,
92
- retry_count INTEGER NOT NULL DEFAULT 0,
93
- max_retries INTEGER NOT NULL DEFAULT 0,
94
- priority INTEGER NOT NULL DEFAULT 5 CHECK (priority BETWEEN 1 AND 9),
95
- ttl_ms INTEGER NOT NULL DEFAULT 600000,
96
- timeout_ms INTEGER NOT NULL DEFAULT 600000,
97
- deadline_ms INTEGER,
98
- trace_id TEXT NOT NULL,
99
- correlation_id TEXT NOT NULL,
100
- last_message_id TEXT,
101
- result_json TEXT,
102
- error_json TEXT,
103
- created_at_ms INTEGER NOT NULL,
104
- updated_at_ms INTEGER NOT NULL,
105
- started_at_ms INTEGER,
106
- completed_at_ms INTEGER,
107
- last_retry_at_ms INTEGER
108
- );
109
-
110
- CREATE INDEX IF NOT EXISTS idx_assign_jobs_status ON assign_jobs(status, updated_at_ms DESC);
111
- CREATE INDEX IF NOT EXISTS idx_assign_jobs_supervisor ON assign_jobs(supervisor_agent, updated_at_ms DESC);
112
- CREATE INDEX IF NOT EXISTS idx_assign_jobs_worker ON assign_jobs(worker_agent, updated_at_ms DESC);
113
- CREATE INDEX IF NOT EXISTS idx_assign_jobs_deadline ON assign_jobs(deadline_ms, status);
114
-
115
- -- 파이프라인 상태 테이블 (Phase 2)
116
- CREATE TABLE IF NOT EXISTS pipeline_state (
117
- team_name TEXT PRIMARY KEY,
118
- phase TEXT NOT NULL DEFAULT 'plan',
119
- fix_attempt INTEGER DEFAULT 0,
120
- fix_max INTEGER DEFAULT 3,
121
- ralph_iteration INTEGER DEFAULT 0,
122
- ralph_max INTEGER DEFAULT 10,
123
- artifacts TEXT DEFAULT '{}',
124
- phase_history TEXT DEFAULT '[]',
125
- created_at INTEGER,
126
- updated_at INTEGER
127
- );
128
-
129
- -- Reflexion 에러 학습 테이블
130
- CREATE TABLE IF NOT EXISTS reflexion_entries (
131
- id TEXT PRIMARY KEY,
132
- error_pattern TEXT NOT NULL, -- 에러 시그니처 (정규화)
133
- error_message TEXT NOT NULL, -- 원본 에러 메시지
134
- context_json TEXT NOT NULL DEFAULT '{}', -- { file, function, cli, agent }
135
- solution TEXT NOT NULL, -- 해결책 설명
136
- solution_code TEXT, -- 해결 코드 스니펫 (있으면)
137
- confidence REAL NOT NULL DEFAULT 0.5, -- 솔루션 신뢰도 (0-1)
138
- hit_count INTEGER NOT NULL DEFAULT 1, -- 매칭 횟수
139
- success_count INTEGER NOT NULL DEFAULT 0, -- 성공 횟수
140
- last_hit_ms INTEGER NOT NULL,
141
- created_at_ms INTEGER NOT NULL,
142
- updated_at_ms INTEGER NOT NULL
143
- );
144
-
145
- CREATE INDEX IF NOT EXISTS idx_reflexion_pattern ON reflexion_entries(error_pattern);
146
- CREATE INDEX IF NOT EXISTS idx_reflexion_confidence ON reflexion_entries(confidence DESC);
1
+ -- tfx-hub 상태 저장소 스키마
2
+ -- SQLite WAL 모드 기반 메시지 버스
3
+
4
+ -- 에이전트 등록 테이블
5
+ CREATE TABLE IF NOT EXISTS agents (
6
+ agent_id TEXT PRIMARY KEY,
7
+ cli TEXT NOT NULL CHECK (cli IN ('codex','gemini','claude','other')),
8
+ pid INTEGER,
9
+ capabilities_json TEXT NOT NULL DEFAULT '[]',
10
+ topics_json TEXT NOT NULL DEFAULT '[]',
11
+ last_seen_ms INTEGER NOT NULL,
12
+ lease_expires_ms INTEGER NOT NULL,
13
+ status TEXT NOT NULL CHECK (status IN ('online','stale','offline')),
14
+ metadata_json TEXT NOT NULL DEFAULT '{}'
15
+ );
16
+
17
+ -- 메시지 테이블
18
+ CREATE TABLE IF NOT EXISTS messages (
19
+ id TEXT PRIMARY KEY,
20
+ type TEXT NOT NULL CHECK (type IN ('request','response','event','handoff','human_request','human_response','system')),
21
+ from_agent TEXT NOT NULL,
22
+ to_agent TEXT NOT NULL,
23
+ topic TEXT NOT NULL,
24
+ priority INTEGER NOT NULL CHECK (priority BETWEEN 1 AND 9),
25
+ ttl_ms INTEGER NOT NULL,
26
+ created_at_ms INTEGER NOT NULL,
27
+ expires_at_ms INTEGER NOT NULL,
28
+ correlation_id TEXT NOT NULL,
29
+ trace_id TEXT NOT NULL,
30
+ payload_json TEXT NOT NULL DEFAULT '{}',
31
+ status TEXT NOT NULL CHECK (status IN ('queued','delivered','acked','expired','dead_letter'))
32
+ );
33
+
34
+ -- 메시지 수신함 (배달 추적)
35
+ CREATE TABLE IF NOT EXISTS message_inbox (
36
+ delivery_id INTEGER PRIMARY KEY AUTOINCREMENT,
37
+ message_id TEXT NOT NULL,
38
+ agent_id TEXT NOT NULL,
39
+ delivered_at_ms INTEGER,
40
+ acked_at_ms INTEGER,
41
+ attempts INTEGER NOT NULL DEFAULT 0,
42
+ UNIQUE(message_id, agent_id),
43
+ FOREIGN KEY(message_id) REFERENCES messages(id) ON DELETE CASCADE
44
+ );
45
+
46
+ -- 사용자 입력 요청 테이블
47
+ CREATE TABLE IF NOT EXISTS human_requests (
48
+ request_id TEXT PRIMARY KEY,
49
+ requester_agent TEXT NOT NULL,
50
+ kind TEXT NOT NULL CHECK (kind IN ('captcha','approval','credential','choice','text')),
51
+ prompt TEXT NOT NULL,
52
+ schema_json TEXT NOT NULL DEFAULT '{}',
53
+ state TEXT NOT NULL CHECK (state IN ('pending','accepted','declined','cancelled','timed_out')),
54
+ deadline_ms INTEGER NOT NULL,
55
+ default_action TEXT NOT NULL CHECK (default_action IN ('decline','cancel','timeout_continue')),
56
+ correlation_id TEXT NOT NULL,
57
+ trace_id TEXT NOT NULL,
58
+ response_json TEXT
59
+ );
60
+
61
+ -- 데드 레터 큐
62
+ CREATE TABLE IF NOT EXISTS dead_letters (
63
+ message_id TEXT PRIMARY KEY,
64
+ reason TEXT NOT NULL,
65
+ failed_at_ms INTEGER NOT NULL,
66
+ last_error TEXT
67
+ );
68
+
69
+ -- 인덱스
70
+ CREATE INDEX IF NOT EXISTS idx_messages_status ON messages(status);
71
+ CREATE INDEX IF NOT EXISTS idx_messages_to_agent ON messages(to_agent, status);
72
+ CREATE INDEX IF NOT EXISTS idx_messages_correlation ON messages(correlation_id);
73
+ CREATE INDEX IF NOT EXISTS idx_messages_trace ON messages(trace_id);
74
+ CREATE INDEX IF NOT EXISTS idx_messages_expires ON messages(expires_at_ms);
75
+ CREATE INDEX IF NOT EXISTS idx_messages_priority ON messages(priority DESC, created_at_ms ASC);
76
+ CREATE INDEX IF NOT EXISTS idx_inbox_agent ON message_inbox(agent_id, delivered_at_ms);
77
+ CREATE INDEX IF NOT EXISTS idx_inbox_message ON message_inbox(message_id);
78
+ CREATE INDEX IF NOT EXISTS idx_human_requests_state ON human_requests(state);
79
+ CREATE INDEX IF NOT EXISTS idx_agents_status ON agents(status);
80
+ CREATE INDEX IF NOT EXISTS idx_agents_lease ON agents(lease_expires_ms);
81
+
82
+ -- Assign Job 테이블
83
+ CREATE TABLE IF NOT EXISTS assign_jobs (
84
+ job_id TEXT PRIMARY KEY,
85
+ supervisor_agent TEXT NOT NULL,
86
+ worker_agent TEXT NOT NULL,
87
+ topic TEXT NOT NULL DEFAULT 'assign.job',
88
+ task TEXT NOT NULL DEFAULT '',
89
+ payload_json TEXT NOT NULL DEFAULT '{}',
90
+ status TEXT NOT NULL CHECK (status IN ('queued','running','succeeded','failed','timed_out')),
91
+ attempt INTEGER NOT NULL DEFAULT 1,
92
+ retry_count INTEGER NOT NULL DEFAULT 0,
93
+ max_retries INTEGER NOT NULL DEFAULT 0,
94
+ priority INTEGER NOT NULL DEFAULT 5 CHECK (priority BETWEEN 1 AND 9),
95
+ ttl_ms INTEGER NOT NULL DEFAULT 600000,
96
+ timeout_ms INTEGER NOT NULL DEFAULT 600000,
97
+ deadline_ms INTEGER,
98
+ trace_id TEXT NOT NULL,
99
+ correlation_id TEXT NOT NULL,
100
+ last_message_id TEXT,
101
+ result_json TEXT,
102
+ error_json TEXT,
103
+ created_at_ms INTEGER NOT NULL,
104
+ updated_at_ms INTEGER NOT NULL,
105
+ started_at_ms INTEGER,
106
+ completed_at_ms INTEGER,
107
+ last_retry_at_ms INTEGER
108
+ );
109
+
110
+ CREATE INDEX IF NOT EXISTS idx_assign_jobs_status ON assign_jobs(status, updated_at_ms DESC);
111
+ CREATE INDEX IF NOT EXISTS idx_assign_jobs_supervisor ON assign_jobs(supervisor_agent, updated_at_ms DESC);
112
+ CREATE INDEX IF NOT EXISTS idx_assign_jobs_worker ON assign_jobs(worker_agent, updated_at_ms DESC);
113
+ CREATE INDEX IF NOT EXISTS idx_assign_jobs_deadline ON assign_jobs(deadline_ms, status);
114
+
115
+ -- 파이프라인 상태 테이블 (Phase 2)
116
+ CREATE TABLE IF NOT EXISTS pipeline_state (
117
+ team_name TEXT PRIMARY KEY,
118
+ phase TEXT NOT NULL DEFAULT 'plan',
119
+ fix_attempt INTEGER DEFAULT 0,
120
+ fix_max INTEGER DEFAULT 3,
121
+ ralph_iteration INTEGER DEFAULT 0,
122
+ ralph_max INTEGER DEFAULT 10,
123
+ artifacts TEXT DEFAULT '{}',
124
+ phase_history TEXT DEFAULT '[]',
125
+ created_at INTEGER,
126
+ updated_at INTEGER
127
+ );
128
+
129
+ -- Reflexion 에러 학습 테이블
130
+ CREATE TABLE IF NOT EXISTS reflexion_entries (
131
+ id TEXT PRIMARY KEY,
132
+ error_pattern TEXT NOT NULL, -- 에러 시그니처 (정규화)
133
+ error_message TEXT NOT NULL, -- 원본 에러 메시지
134
+ context_json TEXT NOT NULL DEFAULT '{}', -- { file, function, cli, agent }
135
+ solution TEXT NOT NULL, -- 해결책 설명
136
+ solution_code TEXT, -- 해결 코드 스니펫 (있으면)
137
+ confidence REAL NOT NULL DEFAULT 0.5, -- 솔루션 신뢰도 (0-1)
138
+ hit_count INTEGER NOT NULL DEFAULT 1, -- 매칭 횟수
139
+ success_count INTEGER NOT NULL DEFAULT 0, -- 성공 횟수
140
+ last_hit_ms INTEGER NOT NULL,
141
+ created_at_ms INTEGER NOT NULL,
142
+ updated_at_ms INTEGER NOT NULL
143
+ );
144
+
145
+ CREATE INDEX IF NOT EXISTS idx_reflexion_pattern ON reflexion_entries(error_pattern);
146
+ CREATE INDEX IF NOT EXISTS idx_reflexion_confidence ON reflexion_entries(confidence DESC);
@@ -1,37 +1,37 @@
1
- import { closeWtSession, killSession, listSessions } from "../../session.mjs";
2
- import { DIM, RESET } from "../../shared.mjs";
3
- import { nativeRequest } from "../services/native-control.mjs";
4
- import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
5
- import { clearTeamState, loadTeamState } from "../services/state-store.mjs";
6
- import { ok } from "../render.mjs";
7
-
8
- export async function teamKill() {
9
- const state = loadTeamState();
10
- if (state && isNativeMode(state) && isTeamAlive(state)) {
11
- await nativeRequest(state, "/stop", {});
12
- try { process.kill(state.native.supervisorPid, "SIGTERM"); } catch {}
13
- clearTeamState(state.sessionId);
14
- ok(`종료: ${state.sessionName}`);
15
- console.log("");
16
- return;
17
- }
18
- if (state && isWtMode(state)) {
19
- const closed = closeWtSession({ layout: state?.wt?.layout || state?.layout || "1xN", paneCount: state?.wt?.paneCount ?? (state.members || []).length });
20
- clearTeamState(state.sessionId);
21
- ok(`종료: ${state.sessionName}${closed ? ` (${closed} panes closed)` : ""}`);
22
- console.log("");
23
- return;
24
- }
25
-
26
- const sessions = listSessions();
27
- if (!sessions.length) {
28
- console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
29
- return;
30
- }
31
- for (const session of sessions) {
32
- killSession(session);
33
- ok(`종료: ${session}`);
34
- }
35
- clearTeamState(state?.sessionId);
36
- console.log("");
37
- }
1
+ import { closeWtSession, killSession, listSessions } from "../../session.mjs";
2
+ import { DIM, RESET } from "../../shared.mjs";
3
+ import { nativeRequest } from "../services/native-control.mjs";
4
+ import { isNativeMode, isTeamAlive, isWtMode } from "../services/runtime-mode.mjs";
5
+ import { clearTeamState, loadTeamState } from "../services/state-store.mjs";
6
+ import { ok } from "../render.mjs";
7
+
8
+ export async function teamKill() {
9
+ const state = loadTeamState();
10
+ if (state && isNativeMode(state) && isTeamAlive(state)) {
11
+ await nativeRequest(state, "/stop", {});
12
+ try { process.kill(state.native.supervisorPid, "SIGTERM"); } catch {}
13
+ clearTeamState(state.sessionId);
14
+ ok(`종료: ${state.sessionName}`);
15
+ console.log("");
16
+ return;
17
+ }
18
+ if (state && isWtMode(state)) {
19
+ const closed = closeWtSession({ layout: state?.wt?.layout || state?.layout || "1xN", paneCount: state?.wt?.paneCount ?? (state.members || []).length });
20
+ clearTeamState(state.sessionId);
21
+ ok(`종료: ${state.sessionName}${closed ? ` (${closed} panes closed)` : ""}`);
22
+ console.log("");
23
+ return;
24
+ }
25
+
26
+ const sessions = listSessions();
27
+ if (!sessions.length) {
28
+ console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
29
+ return;
30
+ }
31
+ for (const session of sessions) {
32
+ killSession(session);
33
+ ok(`종료: ${session}`);
34
+ }
35
+ clearTeamState(state?.sessionId);
36
+ console.log("");
37
+ }
@@ -1,31 +1,31 @@
1
- import { closeWtSession, killSession, sessionExists } from "../../session.mjs";
2
- import { DIM, RESET } from "../../shared.mjs";
3
- import { nativeRequest } from "../services/native-control.mjs";
4
- import { isNativeMode, isWtMode } from "../services/runtime-mode.mjs";
5
- import { clearTeamState, loadTeamState } from "../services/state-store.mjs";
6
- import { ok } from "../render.mjs";
7
-
8
- export async function teamStop() {
9
- const state = loadTeamState();
10
- if (!state) {
11
- console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
12
- return;
13
- }
14
-
15
- if (isNativeMode(state)) {
16
- await nativeRequest(state, "/stop", {});
17
- try { process.kill(state.native.supervisorPid, "SIGTERM"); } catch {}
18
- ok(`세션 종료: ${state.sessionName}`);
19
- } else if (isWtMode(state)) {
20
- const closed = closeWtSession({ layout: state?.wt?.layout || state?.layout || "1xN", paneCount: state?.wt?.paneCount ?? (state.members || []).length });
21
- ok(`세션 종료: ${state.sessionName}${closed ? ` (${closed} panes closed)` : ""}`);
22
- } else if (sessionExists(state.sessionName)) {
23
- killSession(state.sessionName);
24
- ok(`세션 종료: ${state.sessionName}`);
25
- } else {
26
- console.log(` ${DIM}세션 이미 종료됨${RESET}`);
27
- }
28
-
29
- clearTeamState(state.sessionId);
30
- console.log("");
31
- }
1
+ import { closeWtSession, killSession, sessionExists } from "../../session.mjs";
2
+ import { DIM, RESET } from "../../shared.mjs";
3
+ import { nativeRequest } from "../services/native-control.mjs";
4
+ import { isNativeMode, isWtMode } from "../services/runtime-mode.mjs";
5
+ import { clearTeamState, loadTeamState } from "../services/state-store.mjs";
6
+ import { ok } from "../render.mjs";
7
+
8
+ export async function teamStop() {
9
+ const state = loadTeamState();
10
+ if (!state) {
11
+ console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
12
+ return;
13
+ }
14
+
15
+ if (isNativeMode(state)) {
16
+ await nativeRequest(state, "/stop", {});
17
+ try { process.kill(state.native.supervisorPid, "SIGTERM"); } catch {}
18
+ ok(`세션 종료: ${state.sessionName}`);
19
+ } else if (isWtMode(state)) {
20
+ const closed = closeWtSession({ layout: state?.wt?.layout || state?.layout || "1xN", paneCount: state?.wt?.paneCount ?? (state.members || []).length });
21
+ ok(`세션 종료: ${state.sessionName}${closed ? ` (${closed} panes closed)` : ""}`);
22
+ } else if (sessionExists(state.sessionName)) {
23
+ killSession(state.sessionName);
24
+ ok(`세션 종료: ${state.sessionName}`);
25
+ } else {
26
+ console.log(` ${DIM}세션 이미 종료됨${RESET}`);
27
+ }
28
+
29
+ clearTeamState(state.sessionId);
30
+ console.log("");
31
+ }
@@ -1,30 +1,30 @@
1
- import { DIM, RESET, WHITE } from "../../shared.mjs";
2
- import { isTeamAlive } from "../services/runtime-mode.mjs";
3
- import { loadTeamState, saveTeamState } from "../services/state-store.mjs";
4
- import { normalizeTaskStatus, updateTaskStatus } from "../services/task-model.mjs";
5
- import { ok } from "../render.mjs";
6
-
7
- export function teamTaskUpdate(args = []) {
8
- const state = loadTeamState();
9
- if (!state || !isTeamAlive(state)) {
10
- console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
11
- return;
12
- }
13
-
14
- const nextStatus = normalizeTaskStatus(args[0]);
15
- const taskId = String(args[1] || "").toUpperCase();
16
- if (!nextStatus || !taskId) {
17
- console.log(`\n 사용법: ${WHITE}tfx multi task <pending|progress|done> <T1>${RESET}\n`);
18
- return;
19
- }
20
-
21
- const updated = updateTaskStatus(state.tasks || [], taskId, nextStatus);
22
- if (!updated.target) {
23
- console.log(`\n ${DIM}태스크를 찾을 수 없음: ${taskId}${RESET}\n`);
24
- return;
25
- }
26
-
27
- saveTeamState({ ...state, tasks: updated.tasks }, state.sessionId);
28
- ok(`${updated.target.id} 상태 갱신: ${nextStatus}`);
29
- console.log("");
30
- }
1
+ import { DIM, RESET, WHITE } from "../../shared.mjs";
2
+ import { isTeamAlive } from "../services/runtime-mode.mjs";
3
+ import { loadTeamState, saveTeamState } from "../services/state-store.mjs";
4
+ import { normalizeTaskStatus, updateTaskStatus } from "../services/task-model.mjs";
5
+ import { ok } from "../render.mjs";
6
+
7
+ export function teamTaskUpdate(args = []) {
8
+ const state = loadTeamState();
9
+ if (!state || !isTeamAlive(state)) {
10
+ console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
11
+ return;
12
+ }
13
+
14
+ const nextStatus = normalizeTaskStatus(args[0]);
15
+ const taskId = String(args[1] || "").toUpperCase();
16
+ if (!nextStatus || !taskId) {
17
+ console.log(`\n 사용법: ${WHITE}tfx multi task <pending|progress|done> <T1>${RESET}\n`);
18
+ return;
19
+ }
20
+
21
+ const updated = updateTaskStatus(state.tasks || [], taskId, nextStatus);
22
+ if (!updated.target) {
23
+ console.log(`\n ${DIM}태스크를 찾을 수 없음: ${taskId}${RESET}\n`);
24
+ return;
25
+ }
26
+
27
+ saveTeamState({ ...state, tasks: updated.tasks }, state.sessionId);
28
+ ok(`${updated.target.id} 상태 갱신: ${nextStatus}`);
29
+ console.log("");
30
+ }