triflux 10.3.4 → 10.7.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 (329) hide show
  1. package/LICENSE +21 -21
  2. package/bin/tfx-doctor-tui.mjs +1 -1
  3. package/bin/tfx-doctor.mjs +6 -1
  4. package/bin/tfx-profile.mjs +1 -1
  5. package/bin/tfx-setup-tui.mjs +1 -1
  6. package/bin/tfx-setup.mjs +6 -1
  7. package/bin/triflux.mjs +2396 -1140
  8. package/hooks/agent-route-guard.mjs +12 -8
  9. package/hooks/cross-review-tracker.mjs +21 -8
  10. package/hooks/error-context.mjs +19 -7
  11. package/hooks/hook-adaptive-collector.mjs +18 -16
  12. package/hooks/hook-manager.mjs +93 -32
  13. package/hooks/hook-orchestrator.mjs +108 -24
  14. package/hooks/hook-registry.json +11 -0
  15. package/hooks/keyword-rules.json +6 -10
  16. package/hooks/lib/resolve-root.mjs +1 -1
  17. package/hooks/mcp-config-watcher.mjs +6 -2
  18. package/hooks/pipeline-stop.mjs +3 -6
  19. package/hooks/safety-guard.mjs +99 -28
  20. package/hooks/session-start-fast.mjs +143 -0
  21. package/hooks/subagent-verifier.mjs +5 -4
  22. package/hub/account-broker.mjs +256 -60
  23. package/hub/adaptive-diagnostic.mjs +75 -48
  24. package/hub/adaptive-inject.mjs +95 -57
  25. package/hub/adaptive-memory.mjs +156 -42
  26. package/hub/adaptive.mjs +60 -31
  27. package/hub/assign-callbacks.mjs +67 -30
  28. package/hub/bridge.mjs +0 -1
  29. package/hub/cli-adapter-base.mjs +200 -48
  30. package/hub/codex-adapter.mjs +76 -96
  31. package/hub/codex-compat.mjs +3 -3
  32. package/hub/codex-preflight.mjs +63 -37
  33. package/hub/delegator/contracts.mjs +19 -23
  34. package/hub/delegator/index.mjs +3 -3
  35. package/hub/delegator/service.mjs +88 -64
  36. package/hub/delegator/tool-definitions.mjs +5 -5
  37. package/hub/fullcycle.mjs +33 -17
  38. package/hub/gemini-adapter.mjs +69 -94
  39. package/hub/hitl.mjs +89 -30
  40. package/hub/intent.mjs +161 -38
  41. package/hub/lib/cache-guard.mjs +43 -17
  42. package/hub/lib/mcp-response-cache.mjs +66 -32
  43. package/hub/lib/memory-store.mjs +285 -111
  44. package/hub/lib/path-utils.mjs +35 -37
  45. package/hub/lib/process-utils.mjs +106 -37
  46. package/hub/lib/spawn-trace.mjs +527 -0
  47. package/hub/lib/ssh-command.mjs +34 -4
  48. package/hub/lib/ssh-retry.mjs +5 -1
  49. package/hub/lib/uuidv7.mjs +4 -3
  50. package/hub/memory-doctor.mjs +266 -106
  51. package/hub/middleware/request-logger.mjs +61 -34
  52. package/hub/paths.mjs +9 -9
  53. package/hub/pipeline/gates/confidence.mjs +34 -15
  54. package/hub/pipeline/gates/consensus.mjs +27 -15
  55. package/hub/pipeline/gates/index.mjs +7 -3
  56. package/hub/pipeline/gates/selfcheck.mjs +57 -19
  57. package/hub/pipeline/index.mjs +77 -42
  58. package/hub/pipeline/state.mjs +10 -10
  59. package/hub/pipeline/transitions.mjs +40 -23
  60. package/hub/platform.mjs +57 -48
  61. package/hub/promote-penalties.mjs +25 -7
  62. package/hub/quality/deslop.mjs +70 -49
  63. package/hub/research.mjs +32 -25
  64. package/hub/router.mjs +240 -107
  65. package/hub/routing/complexity.mjs +132 -29
  66. package/hub/routing/index.mjs +17 -12
  67. package/hub/routing/q-learning.mjs +76 -28
  68. package/hub/server.mjs +4 -4
  69. package/hub/session-fingerprint.mjs +126 -60
  70. package/hub/state.mjs +84 -43
  71. package/hub/store-adapter.mjs +59 -26
  72. package/hub/store.mjs +356 -153
  73. package/hub/team/agent-map.json +22 -7
  74. package/hub/team/ansi.mjs +186 -122
  75. package/hub/team/backend.mjs +28 -10
  76. package/hub/team/cli/commands/attach.mjs +29 -9
  77. package/hub/team/cli/commands/control.mjs +29 -8
  78. package/hub/team/cli/commands/debug.mjs +32 -11
  79. package/hub/team/cli/commands/focus.mjs +38 -11
  80. package/hub/team/cli/commands/interrupt.mjs +18 -6
  81. package/hub/team/cli/commands/kill.mjs +16 -5
  82. package/hub/team/cli/commands/list.mjs +11 -4
  83. package/hub/team/cli/commands/send.mjs +19 -6
  84. package/hub/team/cli/commands/start/index.mjs +154 -31
  85. package/hub/team/cli/commands/start/parse-args.mjs +38 -11
  86. package/hub/team/cli/commands/start/start-headless.mjs +112 -36
  87. package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
  88. package/hub/team/cli/commands/start/start-mux.mjs +70 -21
  89. package/hub/team/cli/commands/start/start-wt.mjs +29 -12
  90. package/hub/team/cli/commands/status.mjs +43 -14
  91. package/hub/team/cli/commands/stop.mjs +11 -4
  92. package/hub/team/cli/commands/task.mjs +8 -3
  93. package/hub/team/cli/commands/tasks.mjs +1 -1
  94. package/hub/team/cli/index.mjs +2 -2
  95. package/hub/team/cli/manifest.mjs +38 -8
  96. package/hub/team/cli/render.mjs +30 -8
  97. package/hub/team/cli/services/attach-fallback.mjs +31 -11
  98. package/hub/team/cli/services/hub-client.mjs +42 -14
  99. package/hub/team/cli/services/member-selector.mjs +11 -4
  100. package/hub/team/cli/services/native-control.mjs +48 -21
  101. package/hub/team/cli/services/runtime-mode.mjs +2 -1
  102. package/hub/team/cli/services/state-store.mjs +25 -8
  103. package/hub/team/cli/services/task-model.mjs +16 -6
  104. package/hub/team/conductor-mesh-bridge.mjs +24 -23
  105. package/hub/team/conductor.mjs +8 -4
  106. package/hub/team/dashboard-anchor.mjs +4 -5
  107. package/hub/team/dashboard-layout.mjs +3 -1
  108. package/hub/team/dashboard-open.mjs +41 -21
  109. package/hub/team/dashboard.mjs +76 -28
  110. package/hub/team/event-log.mjs +18 -10
  111. package/hub/team/handoff.mjs +31 -15
  112. package/hub/team/headless.mjs +2 -1
  113. package/hub/team/health-probe.mjs +69 -54
  114. package/hub/team/launcher-template.mjs +16 -13
  115. package/hub/team/native-supervisor.mjs +65 -21
  116. package/hub/team/native.mjs +74 -35
  117. package/hub/team/nativeProxy.mjs +184 -113
  118. package/hub/team/notify.mjs +119 -76
  119. package/hub/team/orchestrator.mjs +9 -4
  120. package/hub/team/pane.mjs +12 -7
  121. package/hub/team/process-cleanup.mjs +25 -16
  122. package/hub/team/psmux.mjs +491 -201
  123. package/hub/team/remote-probe.mjs +68 -52
  124. package/hub/team/remote-session.mjs +117 -59
  125. package/hub/team/remote-watcher.mjs +61 -33
  126. package/hub/team/routing.mjs +51 -25
  127. package/hub/team/runtime-strategy.mjs +3 -1
  128. package/hub/team/session.mjs +98 -34
  129. package/hub/team/staleState.mjs +72 -30
  130. package/hub/team/swarm-locks.mjs +15 -13
  131. package/hub/team/swarm-planner.mjs +32 -21
  132. package/hub/team/swarm-reconciler.mjs +48 -23
  133. package/hub/team/tui-lite.mjs +266 -68
  134. package/hub/team/tui-remote-adapter.mjs +14 -10
  135. package/hub/team/tui-viewer.mjs +99 -43
  136. package/hub/team/tui.mjs +708 -271
  137. package/hub/team/worktree-lifecycle.mjs +152 -58
  138. package/hub/team/wt-manager.mjs +24 -14
  139. package/hub/token-mode.mjs +71 -71
  140. package/hub/tray.mjs +66 -23
  141. package/hub/workers/claude-worker.mjs +162 -118
  142. package/hub/workers/codex-mcp.mjs +192 -141
  143. package/hub/workers/delegator-mcp.mjs +507 -333
  144. package/hub/workers/factory.mjs +8 -8
  145. package/hub/workers/gemini-worker.mjs +115 -84
  146. package/hub/workers/interface.mjs +6 -1
  147. package/hub/workers/worker-utils.mjs +21 -14
  148. package/hud/colors.mjs +27 -9
  149. package/hud/constants.mjs +162 -26
  150. package/hud/context-monitor.mjs +82 -41
  151. package/hud/hud-qos-status.mjs +129 -49
  152. package/hud/mission-board.mjs +6 -3
  153. package/hud/providers/claude.mjs +226 -115
  154. package/hud/providers/codex.mjs +62 -22
  155. package/hud/providers/gemini.mjs +168 -56
  156. package/hud/renderers.mjs +384 -119
  157. package/hud/terminal.mjs +101 -31
  158. package/hud/utils.mjs +78 -38
  159. package/mesh/index.mjs +11 -5
  160. package/mesh/mesh-budget.mjs +18 -9
  161. package/mesh/mesh-heartbeat.mjs +1 -1
  162. package/mesh/mesh-queue.mjs +3 -5
  163. package/mesh/mesh-router.mjs +5 -4
  164. package/package.json +2 -1
  165. package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
  166. package/scripts/__tests__/keyword-detector.test.mjs +77 -28
  167. package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
  168. package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
  169. package/scripts/__tests__/remote-spawn.test.mjs +10 -4
  170. package/scripts/__tests__/session-start-fast.test.mjs +36 -0
  171. package/scripts/__tests__/skill-template.test.mjs +98 -50
  172. package/scripts/__tests__/smoke.test.mjs +1 -1
  173. package/scripts/__tests__/spawn-trace.test.mjs +102 -0
  174. package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
  175. package/scripts/cache-doctor.mjs +11 -4
  176. package/scripts/cache-warmup.mjs +96 -37
  177. package/scripts/claudemd-sync.mjs +27 -17
  178. package/scripts/codex-gateway-preflight.mjs +52 -37
  179. package/scripts/codex-mcp-gateway-sync.mjs +59 -39
  180. package/scripts/completions/tfx.bash +47 -47
  181. package/scripts/completions/tfx.fish +44 -44
  182. package/scripts/completions/tfx.zsh +83 -83
  183. package/scripts/config-audit.mjs +232 -0
  184. package/scripts/convert-to-tmpl.mjs +54 -0
  185. package/scripts/cross-review-gate.mjs +35 -12
  186. package/scripts/cross-review-tracker.mjs +21 -8
  187. package/scripts/demo.mjs +35 -17
  188. package/scripts/doctor-diagnose.mjs +284 -0
  189. package/scripts/gen-skill-docs.mjs +7 -2
  190. package/scripts/gen-skill-manifest.mjs +2 -1
  191. package/scripts/headless-guard.mjs +86 -48
  192. package/scripts/hub-ensure.mjs +45 -26
  193. package/scripts/keyword-detector.mjs +41 -20
  194. package/scripts/keyword-rules-expander.mjs +47 -30
  195. package/scripts/lib/claudemd-scanner.mjs +6 -1
  196. package/scripts/lib/context.mjs +3 -3
  197. package/scripts/lib/cross-review-utils.mjs +6 -3
  198. package/scripts/lib/env-probe.mjs +47 -28
  199. package/scripts/lib/gemini-profiles.mjs +44 -10
  200. package/scripts/lib/handoff.mjs +33 -17
  201. package/scripts/lib/hook-utils.mjs +8 -6
  202. package/scripts/lib/keyword-rules.mjs +43 -19
  203. package/scripts/lib/logger.mjs +24 -24
  204. package/scripts/lib/mcp-filter.mjs +377 -239
  205. package/scripts/lib/mcp-guard-engine.mjs +194 -79
  206. package/scripts/lib/mcp-manifest.mjs +23 -13
  207. package/scripts/lib/mcp-server-catalog.mjs +300 -63
  208. package/scripts/lib/psmux-info.mjs +11 -6
  209. package/scripts/lib/remote-spawn-transfer.mjs +44 -14
  210. package/scripts/lib/skill-template.mjs +30 -7
  211. package/scripts/mcp-check.mjs +58 -39
  212. package/scripts/mcp-gateway-config.mjs +83 -39
  213. package/scripts/mcp-gateway-ensure.mjs +43 -35
  214. package/scripts/mcp-gateway-integration-test.mjs +70 -58
  215. package/scripts/mcp-gateway-start.mjs +126 -60
  216. package/scripts/mcp-gateway-verify.mjs +24 -22
  217. package/scripts/mcp-safety-guard.mjs +44 -11
  218. package/scripts/notion-read.mjs +199 -84
  219. package/scripts/pack.mjs +94 -89
  220. package/scripts/preflight-cache.mjs +27 -10
  221. package/scripts/preinstall.mjs +42 -13
  222. package/scripts/remote-spawn.mjs +309 -94
  223. package/scripts/run.cjs +8 -5
  224. package/scripts/session-spawn-helper.mjs +130 -39
  225. package/scripts/session-stale-cleanup.mjs +123 -0
  226. package/scripts/setup.mjs +941 -492
  227. package/scripts/test-lock.mjs +20 -7
  228. package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
  229. package/scripts/tfx-batch-stats.mjs +32 -11
  230. package/scripts/tfx-gate-activate.mjs +11 -4
  231. package/scripts/tfx-route-post.mjs +87 -20
  232. package/scripts/tfx-route-worker.mjs +57 -51
  233. package/scripts/tfx-route.sh +41 -124
  234. package/scripts/tmp-cleanup.mjs +21 -7
  235. package/scripts/token-snapshot.mjs +204 -85
  236. package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
  237. package/skills/.omc/state/idle-notif-cooldown.json +3 -0
  238. package/skills/.omc/state/last-tool-error.json +7 -0
  239. package/skills/.omc/state/subagent-tracking.json +7 -0
  240. package/skills/_templates/base.md +1 -6
  241. package/skills/merge-worktree/SKILL.md.tmpl +144 -0
  242. package/skills/shared/telemetry-segment.md +6 -0
  243. package/skills/star-prompt/SKILL.md.tmpl +222 -0
  244. package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
  245. package/skills/tfx-analysis/skill.json +1 -6
  246. package/skills/tfx-auto/SKILL.md +1 -0
  247. package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
  248. package/skills/tfx-auto-codex/skill.json +1 -3
  249. package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
  250. package/skills/tfx-autopilot/skill.json +1 -5
  251. package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
  252. package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
  253. package/skills/tfx-autoroute/skill.json +1 -7
  254. package/skills/tfx-codex/SKILL.md +1 -0
  255. package/skills/tfx-codex/skill.json +1 -3
  256. package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
  257. package/skills/tfx-codex-swarm/evals/evals.json +1 -1
  258. package/skills/tfx-codex-swarm/skill.json +1 -4
  259. package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
  260. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
  261. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
  262. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
  263. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
  264. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
  265. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
  266. package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
  267. package/skills/tfx-debate/SKILL.md.tmpl +192 -0
  268. package/skills/tfx-debate/skill.json +1 -7
  269. package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
  270. package/skills/tfx-deep-analysis/skill.json +1 -5
  271. package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
  272. package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
  273. package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
  274. package/skills/tfx-deep-qa/skill.json +1 -6
  275. package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
  276. package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
  277. package/skills/tfx-doctor/SKILL.md +21 -0
  278. package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
  279. package/skills/tfx-doctor/skill.json +1 -3
  280. package/skills/tfx-find/SKILL.md +1 -0
  281. package/skills/tfx-forge/SKILL.md.tmpl +187 -0
  282. package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
  283. package/skills/tfx-fullcycle/skill.json +1 -6
  284. package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
  285. package/skills/tfx-gemini/skill.json +1 -3
  286. package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
  287. package/skills/tfx-hooks/skill.json +1 -3
  288. package/skills/tfx-hub/SKILL.md.tmpl +212 -0
  289. package/skills/tfx-hub/skill.json +1 -3
  290. package/skills/tfx-index/SKILL.md +1 -0
  291. package/skills/tfx-index/skill.json +1 -6
  292. package/skills/tfx-interview/SKILL.md.tmpl +285 -0
  293. package/skills/tfx-multi/SKILL.md.tmpl +183 -0
  294. package/skills/tfx-multi/skill.json +1 -3
  295. package/skills/tfx-panel/SKILL.md.tmpl +189 -0
  296. package/skills/tfx-panel/skill.json +1 -7
  297. package/skills/tfx-persist/SKILL.md.tmpl +270 -0
  298. package/skills/tfx-persist/skill.json +1 -7
  299. package/skills/tfx-plan/SKILL.md +1 -0
  300. package/skills/tfx-plan/skill.json +1 -6
  301. package/skills/tfx-profile/SKILL.md.tmpl +239 -0
  302. package/skills/tfx-profile/skill.json +1 -3
  303. package/skills/tfx-prune/SKILL.md.tmpl +200 -0
  304. package/skills/tfx-prune/skill.json +1 -7
  305. package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
  306. package/skills/tfx-psmux-rules/skill.json +1 -4
  307. package/skills/tfx-qa/SKILL.md +1 -0
  308. package/skills/tfx-qa/skill.json +1 -6
  309. package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
  310. package/skills/tfx-ralph/skill.json +1 -4
  311. package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
  312. package/skills/tfx-remote-setup/skill.json +1 -3
  313. package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
  314. package/skills/tfx-remote-spawn/references/hosts.json +16 -0
  315. package/skills/tfx-remote-spawn/skill.json +1 -4
  316. package/skills/tfx-research/SKILL.md +1 -0
  317. package/skills/tfx-review/SKILL.md +1 -0
  318. package/skills/tfx-review/skill.json +1 -6
  319. package/skills/tfx-setup/SKILL.md.tmpl +504 -0
  320. package/skills/tfx-setup/skill.json +1 -3
  321. package/skills/tfx-swarm/SKILL.md +22 -0
  322. package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
  323. package/tui/codex-profile.mjs +88 -33
  324. package/tui/core.mjs +45 -15
  325. package/tui/doctor.mjs +75 -28
  326. package/tui/gemini-profile.mjs +74 -29
  327. package/tui/monitor-data.mjs +8 -4
  328. package/tui/monitor.mjs +71 -27
  329. package/tui/setup.mjs +133 -42
@@ -9,9 +9,16 @@
9
9
  * node token-snapshot.mjs report <session-id|all>
10
10
  */
11
11
 
12
- import { readFileSync, writeFileSync, existsSync, readdirSync, mkdirSync, statSync } from "node:fs";
13
- import { join, dirname } from "node:path";
12
+ import {
13
+ existsSync,
14
+ mkdirSync,
15
+ readdirSync,
16
+ readFileSync,
17
+ statSync,
18
+ writeFileSync,
19
+ } from "node:fs";
14
20
  import { homedir } from "node:os";
21
+ import { dirname, join } from "node:path";
15
22
  import { fileURLToPath } from "node:url";
16
23
 
17
24
  const HOME = homedir();
@@ -25,13 +32,13 @@ const PRICING = {
25
32
  claude_sonnet: { input: 3, output: 15 },
26
33
  claude_opus: { input: 15, output: 75 },
27
34
  codex: { input: 0, output: 0 },
28
- gemini_flash: { input: 0.10, output: 0.40 },
35
+ gemini_flash: { input: 0.1, output: 0.4 },
29
36
  };
30
37
 
31
38
  // Claude 캐시 가격 ($/MTok) — 오케스트레이션 비용 정밀 계산용
32
39
  const CLAUDE_CACHE_PRICING = {
33
- claude_sonnet: { cache_write: 3.75, cache_read: 0.30 },
34
- claude_opus: { cache_write: 18.75, cache_read: 1.50 },
40
+ claude_sonnet: { cache_write: 3.75, cache_read: 0.3 },
41
+ claude_opus: { cache_write: 18.75, cache_read: 1.5 },
35
42
  };
36
43
 
37
44
  // 에이전트 → Claude 대체 모델
@@ -62,15 +69,20 @@ const CLI_COST_MAP = {
62
69
  // ── 유틸리티 ──
63
70
  function readJson(filePath, fallback = null) {
64
71
  if (!existsSync(filePath)) return fallback;
65
- try { return JSON.parse(readFileSync(filePath, "utf-8")); }
66
- catch { return fallback; }
72
+ try {
73
+ return JSON.parse(readFileSync(filePath, "utf-8"));
74
+ } catch {
75
+ return fallback;
76
+ }
67
77
  }
68
78
 
69
79
  function writeJsonSafe(filePath, data) {
70
80
  try {
71
81
  mkdirSync(dirname(filePath), { recursive: true });
72
82
  writeFileSync(filePath, JSON.stringify(data, null, 2));
73
- } catch (e) { console.error(`[token-snapshot] 쓰기 실패: ${e.message}`); }
83
+ } catch (e) {
84
+ console.error(`[token-snapshot] 쓰기 실패: ${e.message}`);
85
+ }
74
86
  }
75
87
 
76
88
  function formatTokenCount(n) {
@@ -85,7 +97,9 @@ function formatCost(dollars) {
85
97
  }
86
98
 
87
99
  function calcCost(tokens, pricing) {
88
- return (tokens.input * pricing.input + tokens.output * pricing.output) / 1_000_000;
100
+ return (
101
+ (tokens.input * pricing.input + tokens.output * pricing.output) / 1_000_000
102
+ );
89
103
  }
90
104
 
91
105
  // ── Codex 세션 스캔 ──
@@ -107,8 +121,11 @@ function scanCodexSessions() {
107
121
  if (!existsSync(dayDir)) continue;
108
122
 
109
123
  let files;
110
- try { files = readdirSync(dayDir).filter(f => f.endsWith(".jsonl")); }
111
- catch { continue; }
124
+ try {
125
+ files = readdirSync(dayDir).filter((f) => f.endsWith(".jsonl"));
126
+ } catch {
127
+ continue;
128
+ }
112
129
 
113
130
  for (const file of files) {
114
131
  const filepath = join(dayDir, file);
@@ -124,18 +141,31 @@ function scanCodexSessions() {
124
141
  sessions[filepath] = {
125
142
  input: t.input_tokens || t.input || 0,
126
143
  output: t.output_tokens || t.output || 0,
127
- total: t.total_tokens || t.total || ((t.input_tokens || t.input || 0) + (t.output_tokens || t.output || 0)),
144
+ total:
145
+ t.total_tokens ||
146
+ t.total ||
147
+ (t.input_tokens || t.input || 0) +
148
+ (t.output_tokens || t.output || 0),
128
149
  timestamp: evt.timestamp || stat.mtimeMs,
129
150
  };
130
151
  break;
131
152
  }
132
- } catch { /* 라인 파싱 실패 */ }
153
+ } catch {
154
+ /* 라인 파싱 실패 */
155
+ }
133
156
  }
134
157
  // 토큰 이벤트 없는 파일도 기록 (존재 추적용)
135
158
  if (!sessions[filepath]) {
136
- sessions[filepath] = { input: 0, output: 0, total: 0, timestamp: stat.mtimeMs };
159
+ sessions[filepath] = {
160
+ input: 0,
161
+ output: 0,
162
+ total: 0,
163
+ timestamp: stat.mtimeMs,
164
+ };
137
165
  }
138
- } catch { /* 파일 읽기 실패 */ }
166
+ } catch {
167
+ /* 파일 읽기 실패 */
168
+ }
139
169
  }
140
170
  }
141
171
  return sessions;
@@ -155,14 +185,19 @@ function scanGeminiSessions() {
155
185
  if (!existsSync(chatsDir)) continue;
156
186
 
157
187
  let files;
158
- try { files = readdirSync(chatsDir).filter(f => f.endsWith(".json")); }
159
- catch { continue; }
188
+ try {
189
+ files = readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
190
+ } catch {
191
+ continue;
192
+ }
160
193
 
161
194
  for (const file of files) {
162
195
  const filepath = join(chatsDir, file);
163
196
  try {
164
197
  const data = JSON.parse(readFileSync(filepath, "utf-8"));
165
- let input = 0, output = 0, model = "unknown";
198
+ let input = 0,
199
+ output = 0,
200
+ model = "unknown";
166
201
  for (const msg of data.messages || []) {
167
202
  if (msg.tokens) {
168
203
  input += msg.tokens.input || 0;
@@ -171,13 +206,20 @@ function scanGeminiSessions() {
171
206
  if (msg.model) model = msg.model;
172
207
  }
173
208
  sessions[filepath] = {
174
- input, output, total: input + output,
175
- model, lastUpdated: data.lastUpdated || null,
209
+ input,
210
+ output,
211
+ total: input + output,
212
+ model,
213
+ lastUpdated: data.lastUpdated || null,
176
214
  };
177
- } catch { /* 무시 */ }
215
+ } catch {
216
+ /* 무시 */
217
+ }
178
218
  }
179
219
  }
180
- } catch { /* 무시 */ }
220
+ } catch {
221
+ /* 무시 */
222
+ }
181
223
  return sessions;
182
224
  }
183
225
 
@@ -193,12 +235,19 @@ function scanClaudeSessions() {
193
235
  for (const proj of projects) {
194
236
  const projDir = join(projectsDir, proj);
195
237
  let stat;
196
- try { stat = statSync(projDir); } catch { continue; }
238
+ try {
239
+ stat = statSync(projDir);
240
+ } catch {
241
+ continue;
242
+ }
197
243
  if (!stat.isDirectory()) continue;
198
244
 
199
245
  let files;
200
- try { files = readdirSync(projDir).filter(f => f.endsWith(".jsonl")); }
201
- catch { continue; }
246
+ try {
247
+ files = readdirSync(projDir).filter((f) => f.endsWith(".jsonl"));
248
+ } catch {
249
+ continue;
250
+ }
202
251
 
203
252
  for (const file of files) {
204
253
  const filepath = join(projDir, file);
@@ -231,11 +280,16 @@ function scanClaudeSessions() {
231
280
  cache_creation: msg.usage.cache_creation_input_tokens || 0,
232
281
  cache_read: msg.usage.cache_read_input_tokens || 0,
233
282
  };
234
- } catch { /* 라인 파싱 실패 */ }
283
+ } catch {
284
+ /* 라인 파싱 실패 */
285
+ }
235
286
  }
236
287
 
237
288
  // requestId별 usage 합산
238
- let input = 0, output = 0, cache_creation = 0, cache_read = 0;
289
+ let input = 0,
290
+ output = 0,
291
+ cache_creation = 0,
292
+ cache_read = 0;
239
293
  for (const u of Object.values(reqUsage)) {
240
294
  input += u.input;
241
295
  output += u.output;
@@ -246,16 +300,24 @@ function scanClaudeSessions() {
246
300
  const total = input + output + cache_creation + cache_read;
247
301
  if (total > 0) {
248
302
  sessions[filepath] = {
249
- input, output, cache_creation, cache_read,
250
- total, model,
303
+ input,
304
+ output,
305
+ cache_creation,
306
+ cache_read,
307
+ total,
308
+ model,
251
309
  timestamp: fileStat.mtimeMs,
252
310
  requests: Object.keys(reqUsage).length,
253
311
  };
254
312
  }
255
- } catch { /* 파일 읽기 실패 */ }
313
+ } catch {
314
+ /* 파일 읽기 실패 */
315
+ }
256
316
  }
257
317
  }
258
- } catch { /* 무시 */ }
318
+ } catch {
319
+ /* 무시 */
320
+ }
259
321
  return sessions;
260
322
  }
261
323
 
@@ -283,9 +345,15 @@ function takeSnapshot(label) {
283
345
  const outPath = join(SNAPSHOTS_DIR, `${label}.json`);
284
346
  writeJsonSafe(outPath, snapshot);
285
347
  console.log(`[snapshot] ${label} 저장 완료`);
286
- console.log(` Codex: ${snapshot.summary.codex_files}파일, ${formatTokenCount(snapshot.summary.codex_total)} tokens`);
287
- console.log(` Gemini: ${snapshot.summary.gemini_files}파일, ${formatTokenCount(snapshot.summary.gemini_total)} tokens`);
288
- console.log(` Claude: ${snapshot.summary.claude_files}파일, ${formatTokenCount(snapshot.summary.claude_total)} tokens`);
348
+ console.log(
349
+ ` Codex: ${snapshot.summary.codex_files}파일, ${formatTokenCount(snapshot.summary.codex_total)} tokens`,
350
+ );
351
+ console.log(
352
+ ` Gemini: ${snapshot.summary.gemini_files}파일, ${formatTokenCount(snapshot.summary.gemini_total)} tokens`,
353
+ );
354
+ console.log(
355
+ ` Claude: ${snapshot.summary.claude_files}파일, ${formatTokenCount(snapshot.summary.claude_total)} tokens`,
356
+ );
289
357
  return snapshot;
290
358
  }
291
359
 
@@ -298,12 +366,23 @@ function computeDiff(preLabel, postLabel, options = {}) {
298
366
  process.exit(1);
299
367
  }
300
368
 
301
- const delta = { codex: {}, gemini: {}, claude: {}, total: { input: 0, output: 0, total: 0 } };
369
+ const delta = {
370
+ codex: {},
371
+ gemini: {},
372
+ claude: {},
373
+ total: { input: 0, output: 0, total: 0 },
374
+ };
302
375
 
303
376
  // Claude diff — 오케스트레이션 오버헤드 측정
304
377
  const preClaude = pre.claude || {};
305
378
  const postClaude = post.claude || {};
306
- const claudeOverhead = { input: 0, output: 0, cache_creation: 0, cache_read: 0, total: 0 };
379
+ const claudeOverhead = {
380
+ input: 0,
381
+ output: 0,
382
+ cache_creation: 0,
383
+ cache_read: 0,
384
+ total: 0,
385
+ };
307
386
  for (const [fp, postData] of Object.entries(postClaude)) {
308
387
  const preData = preClaude[fp];
309
388
  if (!preData) {
@@ -319,7 +398,8 @@ function computeDiff(preLabel, postLabel, options = {}) {
319
398
  const d = {
320
399
  input: (postData.input || 0) - (preData.input || 0),
321
400
  output: (postData.output || 0) - (preData.output || 0),
322
- cache_creation: (postData.cache_creation || 0) - (preData.cache_creation || 0),
401
+ cache_creation:
402
+ (postData.cache_creation || 0) - (preData.cache_creation || 0),
323
403
  cache_read: (postData.cache_read || 0) - (preData.cache_read || 0),
324
404
  total: postData.total - preData.total,
325
405
  type: "increased",
@@ -389,7 +469,10 @@ function computeDiff(preLabel, postLabel, options = {}) {
389
469
  const savings = estimateSavings(delta.total, agent, cli, claudeOverhead);
390
470
 
391
471
  const result = {
392
- preLabel, postLabel, agent, cli,
472
+ preLabel,
473
+ postLabel,
474
+ agent,
475
+ cli,
393
476
  timestamp: new Date().toISOString(),
394
477
  delta,
395
478
  savings,
@@ -400,7 +483,12 @@ function computeDiff(preLabel, postLabel, options = {}) {
400
483
 
401
484
  // 누적 절약액 업데이트 (HUD ts: 표시용)
402
485
  const accPath = join(STATE_DIR, "savings-total.json");
403
- const acc = readJson(accPath, { totalSaved: 0, totalClaudeCost: 0, totalActualCost: 0, diffCount: 0 });
486
+ const acc = readJson(accPath, {
487
+ totalSaved: 0,
488
+ totalClaudeCost: 0,
489
+ totalActualCost: 0,
490
+ diffCount: 0,
491
+ });
404
492
  acc.totalSaved += savings.saved;
405
493
  acc.totalClaudeCost += savings.claudeCost;
406
494
  acc.totalActualCost += savings.actualCost;
@@ -410,10 +498,16 @@ function computeDiff(preLabel, postLabel, options = {}) {
410
498
 
411
499
  console.log(`[diff] ${preLabel} → ${postLabel}`);
412
500
  console.log(` Agent: ${agent} (${cli})`);
413
- console.log(` 외부 CLI 토큰: ${formatTokenCount(delta.total.input)} input, ${formatTokenCount(delta.total.output)} output`);
414
- console.log(` Claude 오케스트레이션: ${formatTokenCount(claudeOverhead.total)} tokens (오버헤드 ${formatCost(savings.overheadCost)})`);
501
+ console.log(
502
+ ` 외부 CLI 토큰: ${formatTokenCount(delta.total.input)} input, ${formatTokenCount(delta.total.output)} output`,
503
+ );
504
+ console.log(
505
+ ` Claude 오케스트레이션: ${formatTokenCount(claudeOverhead.total)} tokens (오버헤드 ${formatCost(savings.overheadCost)})`,
506
+ );
415
507
  console.log(` Claude-only 비용(추정): ${formatCost(savings.claudeCost)}`);
416
- console.log(` 실제 비용: ${formatCost(savings.actualCost)} (외부 CLI ${formatCost(savings.cliCost)} + 오케스트레이션 ${formatCost(savings.overheadCost)})`);
508
+ console.log(
509
+ ` 실제 비용: ${formatCost(savings.actualCost)} (외부 CLI ${formatCost(savings.cliCost)} + 오케스트레이션 ${formatCost(savings.overheadCost)})`,
510
+ );
417
511
  console.log(` 순절약: ${formatCost(savings.saved)}`);
418
512
  return result;
419
513
  }
@@ -440,9 +534,12 @@ function estimateSavings(tokens, agent, cli, claudeOverhead = null) {
440
534
  claudePricing,
441
535
  );
442
536
  // 캐시 비용 (cache_creation은 write 가격, cache_read는 read 가격)
443
- const cachePricing = CLAUDE_CACHE_PRICING[claudeModel] || CLAUDE_CACHE_PRICING.claude_sonnet;
444
- overheadCost += (claudeOverhead.cache_creation * cachePricing.cache_write) / 1_000_000;
445
- overheadCost += (claudeOverhead.cache_read * cachePricing.cache_read) / 1_000_000;
537
+ const cachePricing =
538
+ CLAUDE_CACHE_PRICING[claudeModel] || CLAUDE_CACHE_PRICING.claude_sonnet;
539
+ overheadCost +=
540
+ (claudeOverhead.cache_creation * cachePricing.cache_write) / 1_000_000;
541
+ overheadCost +=
542
+ (claudeOverhead.cache_read * cachePricing.cache_read) / 1_000_000;
446
543
  }
447
544
 
448
545
  // 실제 총비용 = 외부 CLI 비용 + Claude 오케스트레이션 비용
@@ -468,7 +565,7 @@ function generateReport(sessionId) {
468
565
  process.exit(1);
469
566
  }
470
567
 
471
- const files = readdirSync(DIFFS_DIR).filter(f => f.endsWith(".json"));
568
+ const files = readdirSync(DIFFS_DIR).filter((f) => f.endsWith(".json"));
472
569
  const diffs = [];
473
570
  for (const file of files) {
474
571
  const data = readJson(join(DIFFS_DIR, file));
@@ -483,7 +580,10 @@ function generateReport(sessionId) {
483
580
  return null;
484
581
  }
485
582
 
486
- let totalClaudeCost = 0, totalActualCost = 0, totalSaved = 0, totalOverhead = 0;
583
+ let totalClaudeCost = 0,
584
+ totalActualCost = 0,
585
+ totalSaved = 0,
586
+ totalOverhead = 0;
487
587
  const rows = diffs.map((d, i) => {
488
588
  const s = d.savings;
489
589
  totalClaudeCost += s.claudeCost;
@@ -509,11 +609,17 @@ function generateReport(sessionId) {
509
609
  const reportData = {
510
610
  sessionId,
511
611
  timestamp: new Date().toISOString(),
512
- diffs: diffs.map(d => ({
513
- ...d.savings, agent: d.agent, cli: d.cli,
612
+ diffs: diffs.map((d) => ({
613
+ ...d.savings,
614
+ agent: d.agent,
615
+ cli: d.cli,
514
616
  labels: `${d.preLabel}→${d.postLabel}`,
515
617
  })),
516
- totals: { claudeCost: totalClaudeCost, actualCost: totalActualCost, saved: totalSaved },
618
+ totals: {
619
+ claudeCost: totalClaudeCost,
620
+ actualCost: totalActualCost,
621
+ saved: totalSaved,
622
+ },
517
623
  markdown: report,
518
624
  };
519
625
  writeJsonSafe(join(REPORTS_DIR, `${sessionId}.json`), reportData);
@@ -521,48 +627,62 @@ function generateReport(sessionId) {
521
627
  }
522
628
 
523
629
  // ── Named exports (파이프라인 벤치마크 훅용) ──
524
- export { takeSnapshot, computeDiff, estimateSavings, formatTokenCount, formatCost, DIFFS_DIR, STATE_DIR };
630
+ export {
631
+ computeDiff,
632
+ DIFFS_DIR,
633
+ estimateSavings,
634
+ formatCost,
635
+ formatTokenCount,
636
+ STATE_DIR,
637
+ takeSnapshot,
638
+ };
525
639
 
526
640
  // ── CLI 핸들러 (직접 실행 시에만) ──
527
641
  const __filename = fileURLToPath(import.meta.url);
528
- const isDirectRun = process.argv[1] && join(dirname(process.argv[1])) === dirname(__filename)
529
- && process.argv[1].endsWith("token-snapshot.mjs");
642
+ const isDirectRun =
643
+ process.argv[1] &&
644
+ join(dirname(process.argv[1])) === dirname(__filename) &&
645
+ process.argv[1].endsWith("token-snapshot.mjs");
530
646
 
531
647
  if (!isDirectRun) {
532
648
  // imported as module — skip CLI
533
649
  } else {
534
-
535
- const [,, command, ...args] = process.argv;
536
-
537
- switch (command) {
538
- case "snapshot": {
539
- const label = args[0];
540
- if (!label) { console.error("사용법: token-snapshot.mjs snapshot <label>"); process.exit(1); }
541
- takeSnapshot(label);
542
- break;
543
- }
544
- case "diff": {
545
- const [preLabel, postLabel, ...rest] = args;
546
- if (!preLabel || !postLabel) {
547
- console.error("사용법: token-snapshot.mjs diff <pre> <post> [--agent X] [--cli Y] [--id Z]");
548
- process.exit(1);
650
+ const [, , command, ...args] = process.argv;
651
+
652
+ switch (command) {
653
+ case "snapshot": {
654
+ const label = args[0];
655
+ if (!label) {
656
+ console.error("사용법: token-snapshot.mjs snapshot <label>");
657
+ process.exit(1);
658
+ }
659
+ takeSnapshot(label);
660
+ break;
549
661
  }
550
- const options = {};
551
- for (let i = 0; i < rest.length; i++) {
552
- if (rest[i] === "--agent" && rest[i + 1]) options.agent = rest[++i];
553
- else if (rest[i] === "--cli" && rest[i + 1]) options.cli = rest[++i];
554
- else if (rest[i] === "--id" && rest[i + 1]) options.id = rest[++i];
662
+ case "diff": {
663
+ const [preLabel, postLabel, ...rest] = args;
664
+ if (!preLabel || !postLabel) {
665
+ console.error(
666
+ "사용법: token-snapshot.mjs diff <pre> <post> [--agent X] [--cli Y] [--id Z]",
667
+ );
668
+ process.exit(1);
669
+ }
670
+ const options = {};
671
+ for (let i = 0; i < rest.length; i++) {
672
+ if (rest[i] === "--agent" && rest[i + 1]) options.agent = rest[++i];
673
+ else if (rest[i] === "--cli" && rest[i + 1]) options.cli = rest[++i];
674
+ else if (rest[i] === "--id" && rest[i + 1]) options.id = rest[++i];
675
+ }
676
+ computeDiff(preLabel, postLabel, options);
677
+ break;
555
678
  }
556
- computeDiff(preLabel, postLabel, options);
557
- break;
558
- }
559
- case "report": {
560
- const sessionId = args[0] || "all";
561
- generateReport(sessionId);
562
- break;
563
- }
564
- default:
565
- console.log(`cx-auto Token Savings Tracker
679
+ case "report": {
680
+ const sessionId = args[0] || "all";
681
+ generateReport(sessionId);
682
+ break;
683
+ }
684
+ default:
685
+ console.log(`cx-auto Token Savings Tracker
566
686
 
567
687
  사용법:
568
688
  node token-snapshot.mjs snapshot <label> 스냅샷 캡처
@@ -570,6 +690,5 @@ switch (command) {
570
690
  [--agent <agent>] [--cli <cli>] [--id <id>]
571
691
  node token-snapshot.mjs report <session-id> 종합 보고서 생성
572
692
  (session-id 대신 "all"로 전체 보고서)`);
573
- }
574
-
693
+ }
575
694
  } // end isDirectRun guard
@@ -0,0 +1 @@
1
+ {"t":0,"agent":"adb34b0","agent_type":"general-purpose","event":"agent_stop","success":true}
@@ -0,0 +1,3 @@
1
+ {
2
+ "lastSentAt": "2026-03-29T03:41:07.256Z"
3
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "tool_name": "Bash",
3
+ "tool_input_preview": "{\"command\":\"ls -la \\\"C:/Users/tellang/Desktop/Projects/triflux/skills/tfx-deslop/\\\" 2>/dev/null; echo \\\"===\\\"; ls -la \\\"C:/Users/tellang/Desktop/Projects/triflux/skills/tfx-codebase-search/\\\" 2>/dev/n...",
4
+ "error": "Exit code 2\n===",
5
+ "timestamp": "2026-03-29T03:40:35.913Z",
6
+ "retry_count": 1
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "agents": [],
3
+ "total_spawned": 0,
4
+ "total_completed": 0,
5
+ "total_failed": 0,
6
+ "last_updated": "2026-03-29T03:41:34.338Z"
7
+ }
@@ -1,9 +1,4 @@
1
1
  > **ARGUMENTS 처리**: 이 스킬이 `ARGUMENTS: <값>`과 함께 호출되면, 해당 값을 사용자 입력으로 취급하여
2
2
  > 워크플로우의 첫 단계 입력으로 사용한다. ARGUMENTS가 비어있거나 없으면 기존 절차대로 사용자에게 입력을 요청한다.
3
3
 
4
- > **Telemetry**
5
- >
6
- > - Skill: `{{SKILL_NAME}}`
7
- > - Description: `{{SKILL_DESCRIPTION}}`
8
- > - Session: 요청별 식별자를 유지해 단계별 실행 로그를 추적한다.
9
- > - Errors: 실패 시 원인/복구/재시도 여부를 구조화해 기록한다.
4
+ {{#include shared/telemetry-segment.md}}
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: merge-worktree
3
+ description: "워크트리 브랜치를 main으로 squash-merge + conventional commit 자동 생성. codex-swarm 워크트리 자동 인식. '머지해', 'merge worktree', '워크트리 머지', '결과 수집', 'squash merge' 요청에 사용."
4
+ argument-hint: "[target-branch]"
5
+ disable-model-invocation: true
6
+ ---
7
+
8
+ # Merge Worktree
9
+
10
+ 워크트리 브랜치를 대상 브랜치로 squash-merge하고 conventional commit 메시지를 자동 작성한다.
11
+
12
+ ## Current context
13
+
14
+ * Git dir: `!git rev-parse --git-dir`
15
+ * Current branch: `!git branch --show-current`
16
+ * Recent commits: `!git log --oneline -20`
17
+ * Working tree status: `!git status --short`
18
+
19
+ ## Instructions
20
+
21
+ ### Phase 1: Validation
22
+
23
+ 1. **Worktree 확인**: `git rev-parse --git-dir` 출력에 `/worktrees/`가 포함되어야 한다. 아니면 중지.
24
+
25
+ 2. **현재 브랜치 확인**: `git branch --show-current`
26
+
27
+ 3. **대상 브랜치 결정**:
28
+ * `$ARGUMENTS`가 있으면 해당 브랜치 사용
29
+ * 없으면 `main` 존재 확인, 없으면 `master`
30
+
31
+ 4. **원본 레포 경로 확인**: `git rev-parse --git-common-dir`의 부모 디렉토리
32
+
33
+ 5. **클린 상태 확인**: `git status --porcelain`이 비어있어야 한다. 미커밋 변경이 있으면 먼저 커밋/스태시 안내.
34
+
35
+ ### Phase 2: Research
36
+
37
+ 1. **커밋 이력**: `git log --oneline <target>..HEAD`
38
+
39
+ 2. **변경 파일 요약**: `git diff <target>...HEAD --stat`
40
+
41
+ 3. **전체 diff**: `git diff <target>...HEAD` — 꼼꼼히 읽는다.
42
+
43
+ 4. **핵심 파일 읽기**: 가장 큰 변경, 신규 파일, 삭제 파일을 Read로 확인.
44
+
45
+ 5. **변경 분류**:
46
+ * Features (신규 기능)
47
+ * Fixes (버그 수정)
48
+ * Refactors (구조 변경)
49
+ * Tests (테스트)
50
+ * Docs (문서)
51
+ * Config/Chore (빌드, CI, 의존성)
52
+
53
+ 6. **dominant type 결정**: `feat`, `fix`, `refactor`, `docs`, `chore`, `test` 중 하나
54
+
55
+ ### Phase 3: 대상 브랜치 준비
56
+
57
+ 1. **대상 브랜치 최근 커밋 확인**: `git -C <원본레포> log --oneline -10 <target>`
58
+
59
+ 2. **WIP 커밋 감지**: `wip:`, `auto-commit`, `WIP` 시작 커밋이 있으면 사용자에게 경고.
60
+
61
+ 3. **최신 fetch**: `git -C <원본레포> fetch origin <target> 2>/dev/null`
62
+
63
+ ### Phase 4: Squash Merge
64
+
65
+ 1. **대상 브랜치 checkout**:
66
+ ```
67
+ git -C <원본레포> checkout <target>
68
+ ```
69
+
70
+ 2. **squash merge 실행**:
71
+ ```
72
+ git -C <원본레포> merge --squash <워크트리브랜치>
73
+ ```
74
+
75
+ 3. **충돌 처리**: 충돌 발생 시 충돌 파일 목록 + 마커를 보여주고 **중지**. 자동 해결 시도 금지.
76
+
77
+ ### Phase 5: 커밋 메시지 작성 + 커밋
78
+
79
+ Phase 2 분석 기반으로 아래 구조의 커밋 메시지를 작성한다:
80
+
81
+ ```
82
+ <type>: <명령형 요약, 72자 이내, 마침표 없음>
83
+
84
+ <무엇을 왜 했는지 2-4문장. 동기와 접근 방식 중심.>
85
+
86
+ Changes:
87
+ * <그룹별 변경 사항>
88
+ * <하위 항목은 서브 불릿>
89
+ ```
90
+
91
+ **규칙:**
92
+ * `<type>`은 `feat`, `fix`, `refactor`, `docs`, `chore`, `test` 중 하나
93
+ * 여러 유형이 섞이면 dominant 사용
94
+ * 요약: 명령형 ("add", "fix", "refactor"), 마침표 없음, 72자 제한
95
+ * 본문: *왜*와 *맥락*, *무엇*만이 아님
96
+ * Changes: 관련 항목 그룹핑, 중요한 것 먼저
97
+ * Co-Authored-By 푸터 **절대 추가 금지** (글로벌 설정 `includeCoAuthoredBy: false`)
98
+
99
+ **커밋 실행**:
100
+ ```bash
101
+ git -C <원본레포> commit -m "$(cat <<'EOF'
102
+ <커밋 메시지>
103
+ EOF
104
+ )"
105
+ ```
106
+
107
+ ### Phase 6: 정리 + 검증
108
+
109
+ 1. **커밋 확인**: `git -C <원본레포> log --oneline -3`
110
+
111
+ 2. **워크트리 자동 정리**:
112
+ ```bash
113
+ git -C <원본레포> worktree remove <워크트리경로>
114
+ git -C <원본레포> branch -d <워크트리브랜치>
115
+ ```
116
+
117
+ 3. **codex-swarm 정리 감지**: 워크트리 경로가 `.codex-swarm/wt-*` 패턴이면:
118
+ * 같은 `.codex-swarm/` 디렉토리에 다른 워크트리가 남아있는지 확인
119
+ * 모든 워크트리가 머지 완료되었으면 `.codex-swarm/` 전체 정리 제안
120
+ * `git worktree prune` 실행
121
+
122
+ 4. **결과 보고**:
123
+ * 커밋 해시 + 요약
124
+ * 머지 대상 브랜치
125
+ * 워크트리 정리 완료 여부
126
+ * push 안내 (`git push`)
127
+
128
+ ## codex-swarm 연동
129
+
130
+ 이 스킬은 `tfx-codex-swarm`의 Step 10 "결과 수집"에서 자동으로 호출된다.
131
+ codex-swarm이 완료한 각 워크트리에 대해 순차적으로 실행:
132
+
133
+ ```
134
+ 각 워크트리에 대해:
135
+ 1. 워크트리로 cd
136
+ 2. /merge-worktree main
137
+ 3. 다음 워크트리로 이동
138
+ ```
139
+
140
+ ## 주의사항
141
+
142
+ * force-push, destructive 연산은 사용자 확인 없이 절대 실행 금지
143
+ * pre-commit hook 건너뛰기(`--no-verify`) 금지
144
+ * 예상치 못한 상황이면 추측하지 말고 **중지 후 설명**
@@ -0,0 +1,6 @@
1
+ > **Telemetry**
2
+ >
3
+ > - Skill: `{{SKILL_NAME}}`
4
+ > - Description: `{{SKILL_DESCRIPTION}}`
5
+ > - Session: 요청별 식별자를 유지해 단계별 실행 로그를 추적한다.
6
+ > - Errors: 실패 시 원인/복구/재시도 여부를 구조화해 기록한다.