triflux 10.4.0 → 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 (391) hide show
  1. package/.claude-plugin/marketplace.json +34 -0
  2. package/.claude-plugin/plugin.json +22 -0
  3. package/bin/tfx-doctor-tui.mjs +1 -1
  4. package/bin/tfx-doctor.mjs +6 -1
  5. package/bin/tfx-profile.mjs +1 -1
  6. package/bin/tfx-setup-tui.mjs +1 -1
  7. package/bin/tfx-setup.mjs +6 -1
  8. package/bin/triflux.mjs +2396 -1140
  9. package/config/mcp-registry.json +29 -0
  10. package/hooks/agent-route-guard.mjs +12 -8
  11. package/hooks/cross-review-tracker.mjs +21 -8
  12. package/hooks/error-context.mjs +19 -7
  13. package/hooks/hook-adaptive-collector.mjs +18 -16
  14. package/hooks/hook-manager.mjs +93 -32
  15. package/hooks/hook-orchestrator.mjs +108 -24
  16. package/hooks/hook-registry.json +267 -256
  17. package/hooks/keyword-rules.json +6 -10
  18. package/hooks/lib/resolve-root.mjs +1 -1
  19. package/hooks/mcp-config-watcher.mjs +6 -2
  20. package/hooks/pipeline-stop.mjs +3 -6
  21. package/hooks/safety-guard.mjs +99 -28
  22. package/hooks/session-start-fast.mjs +143 -0
  23. package/hooks/subagent-verifier.mjs +5 -4
  24. package/hub/account-broker.mjs +256 -60
  25. package/hub/adaptive-diagnostic.mjs +75 -48
  26. package/hub/adaptive-inject.mjs +96 -58
  27. package/hub/adaptive-memory.mjs +156 -42
  28. package/hub/adaptive.mjs +60 -31
  29. package/hub/assign-callbacks.mjs +163 -126
  30. package/hub/bridge.mjs +0 -1
  31. package/hub/cli-adapter-base.mjs +200 -48
  32. package/hub/codex-adapter.mjs +76 -96
  33. package/hub/codex-compat.mjs +3 -3
  34. package/hub/codex-preflight.mjs +63 -37
  35. package/hub/delegator/contracts.mjs +19 -23
  36. package/hub/delegator/index.mjs +14 -14
  37. package/hub/delegator/service.mjs +88 -64
  38. package/hub/delegator/tool-definitions.mjs +35 -35
  39. package/hub/fullcycle.mjs +33 -17
  40. package/hub/gemini-adapter.mjs +69 -94
  41. package/hub/hitl.mjs +202 -143
  42. package/hub/intent.mjs +161 -38
  43. package/hub/lib/cache-guard.mjs +43 -17
  44. package/hub/lib/mcp-response-cache.mjs +66 -32
  45. package/hub/lib/memory-store.mjs +285 -111
  46. package/hub/lib/path-utils.mjs +35 -37
  47. package/hub/lib/process-utils.mjs +106 -37
  48. package/hub/lib/spawn-trace.mjs +527 -0
  49. package/hub/lib/ssh-command.mjs +34 -4
  50. package/hub/lib/ssh-retry.mjs +5 -1
  51. package/hub/lib/uuidv7.mjs +4 -3
  52. package/hub/memory-doctor.mjs +266 -106
  53. package/hub/middleware/request-logger.mjs +61 -34
  54. package/hub/paths.mjs +9 -9
  55. package/hub/pipeline/gates/confidence.mjs +34 -15
  56. package/hub/pipeline/gates/consensus.mjs +27 -15
  57. package/hub/pipeline/gates/index.mjs +7 -3
  58. package/hub/pipeline/gates/selfcheck.mjs +57 -19
  59. package/hub/pipeline/index.mjs +77 -42
  60. package/hub/pipeline/state.mjs +10 -10
  61. package/hub/pipeline/transitions.mjs +40 -23
  62. package/hub/platform.mjs +57 -48
  63. package/hub/promote-penalties.mjs +25 -7
  64. package/hub/quality/deslop.mjs +70 -49
  65. package/hub/research.mjs +32 -25
  66. package/hub/router.mjs +924 -791
  67. package/hub/routing/complexity.mjs +132 -29
  68. package/hub/routing/index.mjs +17 -12
  69. package/hub/routing/q-learning.mjs +76 -28
  70. package/hub/server.mjs +4 -4
  71. package/hub/session-fingerprint.mjs +127 -61
  72. package/hub/state.mjs +84 -43
  73. package/hub/store-adapter.mjs +59 -26
  74. package/hub/store.mjs +356 -153
  75. package/hub/team/agent-map.json +22 -7
  76. package/hub/team/ansi.mjs +186 -122
  77. package/hub/team/backend.mjs +28 -10
  78. package/hub/team/cli/commands/attach.mjs +57 -37
  79. package/hub/team/cli/commands/control.mjs +29 -8
  80. package/hub/team/cli/commands/debug.mjs +95 -74
  81. package/hub/team/cli/commands/focus.mjs +80 -53
  82. package/hub/team/cli/commands/interrupt.mjs +18 -6
  83. package/hub/team/cli/commands/kill.mjs +16 -5
  84. package/hub/team/cli/commands/list.mjs +31 -24
  85. package/hub/team/cli/commands/send.mjs +19 -6
  86. package/hub/team/cli/commands/start/index.mjs +154 -31
  87. package/hub/team/cli/commands/start/parse-args.mjs +38 -11
  88. package/hub/team/cli/commands/start/start-headless.mjs +112 -36
  89. package/hub/team/cli/commands/start/start-in-process.mjs +50 -40
  90. package/hub/team/cli/commands/start/start-mux.mjs +122 -73
  91. package/hub/team/cli/commands/start/start-wt.mjs +86 -69
  92. package/hub/team/cli/commands/status.mjs +43 -14
  93. package/hub/team/cli/commands/stop.mjs +11 -4
  94. package/hub/team/cli/commands/task.mjs +8 -3
  95. package/hub/team/cli/commands/tasks.mjs +13 -13
  96. package/hub/team/cli/index.mjs +2 -2
  97. package/hub/team/cli/manifest.mjs +38 -8
  98. package/hub/team/cli/render.mjs +52 -30
  99. package/hub/team/cli/services/attach-fallback.mjs +74 -54
  100. package/hub/team/cli/services/hub-client.mjs +42 -14
  101. package/hub/team/cli/services/member-selector.mjs +37 -30
  102. package/hub/team/cli/services/native-control.mjs +143 -116
  103. package/hub/team/cli/services/runtime-mode.mjs +2 -1
  104. package/hub/team/cli/services/state-store.mjs +25 -8
  105. package/hub/team/cli/services/task-model.mjs +40 -30
  106. package/hub/team/conductor-mesh-bridge.mjs +24 -23
  107. package/hub/team/conductor.mjs +8 -4
  108. package/hub/team/dashboard-anchor.mjs +4 -5
  109. package/hub/team/dashboard-layout.mjs +3 -1
  110. package/hub/team/dashboard-open.mjs +41 -21
  111. package/hub/team/dashboard.mjs +76 -28
  112. package/hub/team/event-log.mjs +18 -10
  113. package/hub/team/handoff.mjs +31 -15
  114. package/hub/team/headless.mjs +2 -1
  115. package/hub/team/health-probe.mjs +69 -54
  116. package/hub/team/launcher-template.mjs +16 -13
  117. package/hub/team/native-supervisor.mjs +65 -21
  118. package/hub/team/native.mjs +74 -35
  119. package/hub/team/nativeProxy.mjs +184 -113
  120. package/hub/team/notify.mjs +120 -77
  121. package/hub/team/orchestrator.mjs +166 -161
  122. package/hub/team/pane.mjs +12 -7
  123. package/hub/team/process-cleanup.mjs +25 -16
  124. package/hub/team/psmux.mjs +491 -201
  125. package/hub/team/remote-probe.mjs +68 -52
  126. package/hub/team/remote-session.mjs +117 -59
  127. package/hub/team/remote-watcher.mjs +61 -33
  128. package/hub/team/routing.mjs +51 -25
  129. package/hub/team/runtime-strategy.mjs +3 -1
  130. package/hub/team/session.mjs +675 -611
  131. package/hub/team/shared.mjs +13 -13
  132. package/hub/team/staleState.mjs +72 -30
  133. package/hub/team/swarm-locks.mjs +15 -13
  134. package/hub/team/swarm-planner.mjs +32 -21
  135. package/hub/team/swarm-reconciler.mjs +48 -23
  136. package/hub/team/tui-lite.mjs +266 -68
  137. package/hub/team/tui-remote-adapter.mjs +14 -10
  138. package/hub/team/tui-viewer.mjs +99 -43
  139. package/hub/team/tui.mjs +708 -271
  140. package/hub/team/worktree-lifecycle.mjs +152 -58
  141. package/hub/team/wt-manager.mjs +24 -14
  142. package/hub/token-mode.mjs +71 -71
  143. package/hub/tray.mjs +411 -368
  144. package/hub/workers/claude-worker.mjs +162 -118
  145. package/hub/workers/codex-mcp.mjs +558 -507
  146. package/hub/workers/delegator-mcp.mjs +507 -333
  147. package/hub/workers/factory.mjs +21 -21
  148. package/hub/workers/gemini-worker.mjs +115 -84
  149. package/hub/workers/interface.mjs +6 -1
  150. package/hub/workers/worker-utils.mjs +21 -14
  151. package/hud/colors.mjs +27 -9
  152. package/hud/constants.mjs +162 -26
  153. package/hud/context-monitor.mjs +82 -41
  154. package/hud/hud-qos-status.mjs +129 -49
  155. package/hud/mission-board.mjs +6 -3
  156. package/hud/providers/claude.mjs +226 -115
  157. package/hud/providers/codex.mjs +62 -22
  158. package/hud/providers/gemini.mjs +168 -56
  159. package/hud/renderers.mjs +384 -119
  160. package/hud/terminal.mjs +101 -31
  161. package/hud/utils.mjs +78 -38
  162. package/mesh/index.mjs +11 -5
  163. package/mesh/mesh-budget.mjs +18 -9
  164. package/mesh/mesh-heartbeat.mjs +1 -1
  165. package/mesh/mesh-queue.mjs +3 -5
  166. package/mesh/mesh-router.mjs +5 -4
  167. package/package.json +56 -21
  168. package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
  169. package/scripts/__tests__/keyword-detector.test.mjs +77 -28
  170. package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
  171. package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
  172. package/scripts/__tests__/remote-spawn.test.mjs +10 -4
  173. package/scripts/__tests__/session-start-fast.test.mjs +36 -0
  174. package/scripts/__tests__/skill-template.test.mjs +98 -50
  175. package/scripts/__tests__/smoke.test.mjs +1 -1
  176. package/scripts/__tests__/spawn-trace.test.mjs +102 -0
  177. package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
  178. package/scripts/cache-doctor.mjs +11 -4
  179. package/scripts/cache-warmup.mjs +96 -37
  180. package/scripts/claudemd-sync.mjs +27 -17
  181. package/scripts/codex-gateway-preflight.mjs +52 -37
  182. package/scripts/codex-mcp-gateway-sync.mjs +59 -39
  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 +139 -120
  193. package/scripts/keyword-detector.mjs +293 -272
  194. package/scripts/keyword-rules-expander.mjs +538 -521
  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 +355 -118
  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 +669 -554
  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 +61 -57
  229. package/scripts/tfx-batch-stats.mjs +117 -96
  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/idle-notif-cooldown.json +1 -1
  237. package/skills/.omc/state/last-tool-error.json +1 -1
  238. package/skills/.omc/state/subagent-tracking.json +1 -1
  239. package/skills/_templates/base.md +1 -6
  240. package/skills/merge-worktree/SKILL.md.tmpl +144 -0
  241. package/skills/shared/telemetry-segment.md +6 -0
  242. package/skills/star-prompt/SKILL.md.tmpl +222 -0
  243. package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
  244. package/skills/tfx-analysis/skill.json +1 -6
  245. package/skills/tfx-auto/SKILL.md +1 -0
  246. package/skills/{tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md → tfx-auto-codex/SKILL.md.tmpl} +33 -4
  247. package/skills/tfx-auto-codex/skill.json +1 -3
  248. package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
  249. package/skills/tfx-autopilot/skill.json +1 -5
  250. package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
  251. package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
  252. package/skills/tfx-autoroute/skill.json +1 -7
  253. package/skills/tfx-codex/SKILL.md +1 -0
  254. package/skills/tfx-codex/skill.json +1 -3
  255. package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
  256. package/skills/tfx-codex-swarm/evals/evals.json +1 -1
  257. package/skills/tfx-codex-swarm/skill.json +1 -4
  258. package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
  259. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
  260. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
  261. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
  262. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
  263. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
  264. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
  265. package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
  266. package/skills/tfx-debate/SKILL.md.tmpl +192 -0
  267. package/skills/tfx-debate/skill.json +1 -7
  268. package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
  269. package/skills/tfx-deep-analysis/skill.json +1 -5
  270. package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
  271. package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
  272. package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
  273. package/skills/tfx-deep-qa/skill.json +1 -6
  274. package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
  275. package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
  276. package/skills/tfx-doctor/SKILL.md +21 -0
  277. package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
  278. package/skills/tfx-doctor/skill.json +1 -3
  279. package/skills/tfx-find/SKILL.md +1 -0
  280. package/skills/tfx-forge/SKILL.md.tmpl +187 -0
  281. package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
  282. package/skills/tfx-fullcycle/skill.json +1 -6
  283. package/skills/{tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md → tfx-gemini/SKILL.md.tmpl} +16 -7
  284. package/skills/tfx-gemini/skill.json +1 -3
  285. package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
  286. package/skills/tfx-hooks/skill.json +1 -3
  287. package/skills/tfx-hub/SKILL.md.tmpl +212 -0
  288. package/skills/tfx-hub/skill.json +1 -3
  289. package/skills/tfx-index/SKILL.md +1 -0
  290. package/skills/tfx-index/skill.json +1 -6
  291. package/skills/tfx-interview/SKILL.md.tmpl +285 -0
  292. package/skills/tfx-multi/SKILL.md.tmpl +183 -0
  293. package/skills/tfx-multi/skill.json +1 -3
  294. package/skills/tfx-panel/SKILL.md.tmpl +189 -0
  295. package/skills/tfx-panel/skill.json +1 -7
  296. package/skills/tfx-persist/SKILL.md.tmpl +270 -0
  297. package/skills/tfx-persist/skill.json +1 -7
  298. package/skills/tfx-plan/SKILL.md +1 -0
  299. package/skills/tfx-plan/skill.json +1 -6
  300. package/skills/tfx-profile/SKILL.md.tmpl +239 -0
  301. package/skills/tfx-profile/skill.json +1 -3
  302. package/skills/tfx-prune/SKILL.md.tmpl +200 -0
  303. package/skills/tfx-prune/skill.json +1 -7
  304. package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
  305. package/skills/tfx-psmux-rules/skill.json +1 -4
  306. package/skills/tfx-qa/SKILL.md +1 -0
  307. package/skills/tfx-qa/skill.json +1 -6
  308. package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
  309. package/skills/tfx-ralph/skill.json +1 -4
  310. package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
  311. package/skills/tfx-remote-setup/skill.json +1 -3
  312. package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
  313. package/skills/tfx-remote-spawn/references/hosts.json +16 -16
  314. package/skills/tfx-remote-spawn/skill.json +1 -4
  315. package/skills/tfx-research/SKILL.md +1 -0
  316. package/skills/tfx-review/SKILL.md +1 -0
  317. package/skills/tfx-review/skill.json +1 -6
  318. package/skills/tfx-setup/SKILL.md.tmpl +504 -0
  319. package/skills/tfx-setup/skill.json +1 -3
  320. package/skills/tfx-swarm/SKILL.md +22 -0
  321. package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
  322. package/tui/codex-profile.mjs +457 -0
  323. package/tui/core.mjs +266 -0
  324. package/tui/doctor.mjs +375 -0
  325. package/tui/gemini-profile.mjs +299 -0
  326. package/tui/monitor-data.mjs +152 -0
  327. package/tui/monitor.mjs +339 -0
  328. package/tui/setup.mjs +533 -0
  329. package/CLAUDE.md +0 -193
  330. package/references/hosts.json +0 -33
  331. package/skills/tfx-workspace/async-tests/run-tests.sh +0 -203
  332. package/skills/tfx-workspace/evals/evals.json +0 -79
  333. package/skills/tfx-workspace/iteration-1/benchmark.json +0 -162
  334. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +0 -11
  335. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +0 -9
  336. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +0 -154
  337. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +0 -5
  338. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +0 -9
  339. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +0 -126
  340. package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +0 -5
  341. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +0 -11
  342. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +0 -9
  343. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +0 -119
  344. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +0 -5
  345. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +0 -9
  346. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +0 -115
  347. package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +0 -5
  348. package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +0 -10
  349. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +0 -8
  350. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +0 -86
  351. package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +0 -5
  352. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +0 -8
  353. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +0 -81
  354. package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +0 -5
  355. package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +0 -12
  356. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +0 -10
  357. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +0 -316
  358. package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +0 -5
  359. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +0 -10
  360. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +0 -352
  361. package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +0 -5
  362. package/skills/tfx-workspace/iteration-1/review.html +0 -1325
  363. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +0 -12
  364. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +0 -10
  365. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +0 -97
  366. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +0 -5
  367. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +0 -10
  368. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +0 -94
  369. package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +0 -5
  370. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +0 -12
  371. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +0 -10
  372. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +0 -209
  373. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +0 -5
  374. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +0 -10
  375. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +0 -193
  376. package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +0 -5
  377. package/skills/tfx-workspace/iteration-2/benchmark.json +0 -62
  378. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +0 -13
  379. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +0 -11
  380. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +0 -382
  381. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +0 -5
  382. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +0 -11
  383. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +0 -333
  384. package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +0 -5
  385. package/skills/tfx-workspace/iteration-2/review.html +0 -1325
  386. package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +0 -217
  387. package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +0 -65
  388. package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +0 -94
  389. package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +0 -133
  390. package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +0 -426
  391. package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +0 -101
@@ -1,6 +1,7 @@
1
1
  // hub/team/nativeProxy.mjs
2
2
  // Claude Native Teams 파일을 Hub tool/REST에서 안전하게 읽고 쓰기 위한 유틸.
3
3
 
4
+ import { randomUUID } from "node:crypto";
4
5
  import {
5
6
  existsSync,
6
7
  mkdirSync,
@@ -8,24 +9,23 @@ import {
8
9
  rmSync,
9
10
  unlinkSync,
10
11
  writeFileSync,
11
- } from 'node:fs';
12
+ } from "node:fs";
12
13
  import {
13
14
  open as openFile,
14
15
  readdir,
15
16
  readFile,
16
17
  stat,
17
18
  unlink as unlinkFile,
18
- } from 'node:fs/promises';
19
- import { basename, dirname, join } from 'node:path';
20
- import { homedir } from 'node:os';
21
- import { randomUUID } from 'node:crypto';
22
- import { isPidAlive } from '../lib/process-utils.mjs';
23
- import { IS_WINDOWS } from '../platform.mjs';
19
+ } from "node:fs/promises";
20
+ import { homedir } from "node:os";
21
+ import { basename, dirname, join } from "node:path";
22
+ import { isPidAlive } from "../lib/process-utils.mjs";
23
+ import { IS_WINDOWS } from "../platform.mjs";
24
24
 
25
25
  const TEAM_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
26
- const CLAUDE_HOME = join(homedir(), '.claude');
27
- const TEAMS_ROOT = join(CLAUDE_HOME, 'teams');
28
- const TASKS_ROOT = join(CLAUDE_HOME, 'tasks');
26
+ const CLAUDE_HOME = join(homedir(), ".claude");
27
+ const TEAMS_ROOT = join(CLAUDE_HOME, "teams");
28
+ const TASKS_ROOT = join(CLAUDE_HOME, "tasks");
29
29
  const LOCK_STALE_MS = 30000;
30
30
 
31
31
  // ── 인메모리 캐시 (디렉토리 mtime 기반 무효화) ──
@@ -42,14 +42,14 @@ function err(code, message, extra = {}) {
42
42
  }
43
43
 
44
44
  function validateTeamName(teamName) {
45
- if (!TEAM_NAME_RE.test(String(teamName || ''))) {
46
- throw new Error('INVALID_TEAM_NAME');
45
+ if (!TEAM_NAME_RE.test(String(teamName || ""))) {
46
+ throw new Error("INVALID_TEAM_NAME");
47
47
  }
48
48
  }
49
49
 
50
50
  async function readJsonSafe(path) {
51
51
  try {
52
- return JSON.parse(await readFile(path, 'utf8'));
52
+ return JSON.parse(await readFile(path, "utf8"));
53
53
  } catch {
54
54
  return null;
55
55
  }
@@ -58,16 +58,20 @@ async function readJsonSafe(path) {
58
58
  function atomicWriteJson(path, value) {
59
59
  mkdirSync(dirname(path), { recursive: true });
60
60
  const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
61
- writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
61
+ writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\n`, "utf8");
62
62
  try {
63
63
  renameSync(tmp, path);
64
64
  } catch (e) {
65
65
  // Windows NTFS: 대상 파일 존재 시 rename 실패 가능 → 삭제 후 재시도
66
- if (IS_WINDOWS && (e.code === 'EPERM' || e.code === 'EEXIST')) {
67
- try { unlinkSync(path); } catch {}
66
+ if (IS_WINDOWS && (e.code === "EPERM" || e.code === "EEXIST")) {
67
+ try {
68
+ unlinkSync(path);
69
+ } catch {}
68
70
  renameSync(tmp, path);
69
71
  } else {
70
- try { unlinkSync(tmp); } catch {}
72
+ try {
73
+ unlinkSync(tmp);
74
+ } catch {}
71
75
  throw e;
72
76
  }
73
77
  }
@@ -87,29 +91,36 @@ async function readLockInfo(lockPath) {
87
91
 
88
92
  let parsed = null;
89
93
  try {
90
- parsed = JSON.parse(await readFile(lockPath, 'utf8'));
94
+ parsed = JSON.parse(await readFile(lockPath, "utf8"));
91
95
  } catch {}
92
96
 
93
97
  const now = Date.now();
94
98
  const createdAtMs = Number(
95
- parsed?.created_at_ms
96
- ?? parsed?.timestamp_ms
97
- ?? parsed?.timestamp
98
- ?? lockStat.mtimeMs,
99
+ parsed?.created_at_ms ??
100
+ parsed?.timestamp_ms ??
101
+ parsed?.timestamp ??
102
+ lockStat.mtimeMs,
99
103
  );
100
104
  const pid = Number(parsed?.pid);
101
105
 
102
106
  return {
103
- token: typeof parsed?.token === 'string' ? parsed.token : null,
107
+ token: typeof parsed?.token === "string" ? parsed.token : null,
104
108
  pid: Number.isInteger(pid) && pid > 0 ? pid : null,
105
- created_at_ms: Number.isFinite(createdAtMs) ? createdAtMs : lockStat.mtimeMs,
109
+ created_at_ms: Number.isFinite(createdAtMs)
110
+ ? createdAtMs
111
+ : lockStat.mtimeMs,
106
112
  mtime_ms: lockStat.mtimeMs,
107
- age_ms: Math.max(0, now - (Number.isFinite(createdAtMs) ? createdAtMs : lockStat.mtimeMs)),
113
+ age_ms: Math.max(
114
+ 0,
115
+ now - (Number.isFinite(createdAtMs) ? createdAtMs : lockStat.mtimeMs),
116
+ ),
108
117
  };
109
118
  }
110
119
 
111
120
  async function releaseFileLock(lockPath, token, handle) {
112
- try { await handle?.close(); } catch {}
121
+ try {
122
+ await handle?.close();
123
+ } catch {}
113
124
 
114
125
  try {
115
126
  const current = await readLockInfo(lockPath);
@@ -119,7 +130,13 @@ async function releaseFileLock(lockPath, token, handle) {
119
130
  } catch {}
120
131
  }
121
132
 
122
- async function withFileLock(lockPath, fn, retries = 20, delayMs = 25, staleMs = LOCK_STALE_MS) {
133
+ async function withFileLock(
134
+ lockPath,
135
+ fn,
136
+ retries = 20,
137
+ delayMs = 25,
138
+ staleMs = LOCK_STALE_MS,
139
+ ) {
123
140
  mkdirSync(dirname(lockPath), { recursive: true });
124
141
  const lockOwner = {
125
142
  pid: process.pid,
@@ -132,9 +149,9 @@ async function withFileLock(lockPath, fn, retries = 20, delayMs = 25, staleMs =
132
149
 
133
150
  for (let i = 0; i < retries; i += 1) {
134
151
  try {
135
- handle = await openFile(lockPath, 'wx');
152
+ handle = await openFile(lockPath, "wx");
136
153
  try {
137
- await handle.writeFile(`${JSON.stringify(lockOwner)}\n`, 'utf8');
154
+ await handle.writeFile(`${JSON.stringify(lockOwner)}\n`, "utf8");
138
155
  } catch (writeError) {
139
156
  await releaseFileLock(lockPath, lockOwner.token, handle);
140
157
  throw writeError;
@@ -142,7 +159,7 @@ async function withFileLock(lockPath, fn, retries = 20, delayMs = 25, staleMs =
142
159
  break;
143
160
  } catch (e) {
144
161
  lastError = e;
145
- if (e?.code !== 'EEXIST') throw e;
162
+ if (e?.code !== "EEXIST") throw e;
146
163
 
147
164
  const current = await readLockInfo(lockPath);
148
165
  const staleByAge = !current || current.age_ms > staleMs;
@@ -152,7 +169,7 @@ async function withFileLock(lockPath, fn, retries = 20, delayMs = 25, staleMs =
152
169
  await unlinkFile(lockPath);
153
170
  continue;
154
171
  } catch (unlinkError) {
155
- if (unlinkError?.code === 'ENOENT') continue;
172
+ if (unlinkError?.code === "ENOENT") continue;
156
173
  lastError = unlinkError;
157
174
  }
158
175
  }
@@ -174,19 +191,21 @@ async function withFileLock(lockPath, fn, retries = 20, delayMs = 25, staleMs =
174
191
  }
175
192
 
176
193
  function getLeadSessionId(config) {
177
- return config?.leadSessionId
178
- || config?.lead_session_id
179
- || config?.lead?.lead_session_id
180
- || config?.lead?.sessionId
181
- || null;
194
+ return (
195
+ config?.leadSessionId ||
196
+ config?.lead_session_id ||
197
+ config?.lead?.lead_session_id ||
198
+ config?.lead?.sessionId ||
199
+ null
200
+ );
182
201
  }
183
202
 
184
203
  export async function resolveTeamPaths(teamName) {
185
204
  validateTeamName(teamName);
186
205
 
187
206
  const teamDir = join(TEAMS_ROOT, teamName);
188
- const configPath = join(teamDir, 'config.json');
189
- const inboxesDir = join(teamDir, 'inboxes');
207
+ const configPath = join(teamDir, "config.json");
208
+ const inboxesDir = join(teamDir, "inboxes");
190
209
  const config = await readJsonSafe(configPath);
191
210
  const leadSessionId = getLeadSessionId(config);
192
211
 
@@ -194,12 +213,12 @@ export async function resolveTeamPaths(teamName) {
194
213
  const byLeadSession = leadSessionId ? join(TASKS_ROOT, leadSessionId) : null;
195
214
 
196
215
  let tasksDir = byTeam;
197
- let tasksDirResolution = 'not_found';
216
+ let tasksDirResolution = "not_found";
198
217
  if (existsSync(byTeam)) {
199
- tasksDirResolution = 'team_name';
218
+ tasksDirResolution = "team_name";
200
219
  } else if (byLeadSession && existsSync(byLeadSession)) {
201
220
  tasksDir = byLeadSession;
202
- tasksDirResolution = 'lead_session_id';
221
+ tasksDirResolution = "lead_session_id";
203
222
  }
204
223
 
205
224
  return {
@@ -222,7 +241,7 @@ export async function forceCleanupTeam(teamName) {
222
241
  } catch {
223
242
  paths = {
224
243
  team_dir: join(TEAMS_ROOT, teamName),
225
- config_path: join(TEAMS_ROOT, teamName, 'config.json'),
244
+ config_path: join(TEAMS_ROOT, teamName, "config.json"),
226
245
  tasks_dir: join(TASKS_ROOT, teamName),
227
246
  lead_session_id: null,
228
247
  config: null,
@@ -230,11 +249,14 @@ export async function forceCleanupTeam(teamName) {
230
249
  }
231
250
 
232
251
  try {
233
- const config = paths.config || await readJsonSafe(paths.config_path);
252
+ const config = paths.config || (await readJsonSafe(paths.config_path));
234
253
  if (config && Array.isArray(config.members)) {
235
254
  atomicWriteJson(paths.config_path, {
236
255
  ...config,
237
- members: config.members.map((member) => ({ ...member, isActive: false })),
256
+ members: config.members.map((member) => ({
257
+ ...member,
258
+ isActive: false,
259
+ })),
238
260
  });
239
261
  }
240
262
  } catch {}
@@ -261,7 +283,11 @@ async function collectTaskFiles(tasksDir) {
261
283
 
262
284
  // 디렉토리 mtime 기반 캐시 — O(N) I/O를 반복 호출 시 O(1)로 축소
263
285
  let dirMtime;
264
- try { dirMtime = (await stat(tasksDir)).mtimeMs; } catch { return []; }
286
+ try {
287
+ dirMtime = (await stat(tasksDir)).mtimeMs;
288
+ } catch {
289
+ return [];
290
+ }
265
291
 
266
292
  const cached = _dirCache.get(tasksDir);
267
293
  if (cached && cached.mtimeMs === dirMtime) {
@@ -270,9 +296,9 @@ async function collectTaskFiles(tasksDir) {
270
296
 
271
297
  const entries = await readdir(tasksDir);
272
298
  const files = entries
273
- .filter((name) => name.endsWith('.json'))
274
- .filter((name) => !name.endsWith('.lock'))
275
- .filter((name) => name !== '.highwatermark')
299
+ .filter((name) => name.endsWith(".json"))
300
+ .filter((name) => !name.endsWith(".lock"))
301
+ .filter((name) => name !== ".highwatermark")
276
302
  .map((name) => join(tasksDir, name));
277
303
 
278
304
  _dirCache.set(tasksDir, { mtimeMs: dirMtime, files });
@@ -311,12 +337,12 @@ async function locateTaskFile(tasksDir, taskId) {
311
337
  // 캐시된 collectTaskFiles로 풀 스캔
312
338
  const files = await collectTaskFiles(tasksDir);
313
339
  for (const file of files) {
314
- if (basename(file, '.json') === taskId) {
340
+ if (basename(file, ".json") === taskId) {
315
341
  _taskIdIndex.set(taskId, file);
316
342
  return file;
317
343
  }
318
344
  const json = await readJsonSafe(file);
319
- if (json && String(json.id || '') === taskId) {
345
+ if (json && String(json.id || "") === taskId) {
320
346
  _taskIdIndex.set(taskId, file);
321
347
  return file;
322
348
  }
@@ -325,7 +351,7 @@ async function locateTaskFile(tasksDir, taskId) {
325
351
  }
326
352
 
327
353
  function isObject(v) {
328
- return !!v && typeof v === 'object' && !Array.isArray(v);
354
+ return !!v && typeof v === "object" && !Array.isArray(v);
329
355
  }
330
356
 
331
357
  export async function teamInfo(args = {}) {
@@ -333,19 +359,22 @@ export async function teamInfo(args = {}) {
333
359
  try {
334
360
  validateTeamName(team_name);
335
361
  } catch {
336
- return err('INVALID_TEAM_NAME', 'team_name 형식이 올바르지 않습니다');
362
+ return err("INVALID_TEAM_NAME", "team_name 형식이 올바르지 않습니다");
337
363
  }
338
364
 
339
365
  const paths = await resolveTeamPaths(team_name);
340
366
  if (!existsSync(paths.team_dir)) {
341
- return err('TEAM_NOT_FOUND', `팀 디렉토리가 없습니다: ${paths.team_dir}`);
367
+ return err("TEAM_NOT_FOUND", `팀 디렉토리가 없습니다: ${paths.team_dir}`);
342
368
  }
343
369
 
344
- const members = Array.isArray(paths.config?.members) ? paths.config.members : [];
345
- const leadAgentId = paths.config?.leadAgentId
346
- || paths.config?.lead_agent_id
347
- || members[0]?.agentId
348
- || null;
370
+ const members = Array.isArray(paths.config?.members)
371
+ ? paths.config.members
372
+ : [];
373
+ const leadAgentId =
374
+ paths.config?.leadAgentId ||
375
+ paths.config?.lead_agent_id ||
376
+ members[0]?.agentId ||
377
+ null;
349
378
 
350
379
  return {
351
380
  ok: true,
@@ -359,14 +388,16 @@ export async function teamInfo(args = {}) {
359
388
  lead_session_id: paths.lead_session_id,
360
389
  },
361
390
  ...(include_members ? { members } : {}),
362
- ...(include_paths ? {
363
- paths: {
364
- config_path: paths.config_path,
365
- tasks_dir: paths.tasks_dir,
366
- inboxes_dir: paths.inboxes_dir,
367
- tasks_dir_resolution: paths.tasks_dir_resolution,
368
- },
369
- } : {}),
391
+ ...(include_paths
392
+ ? {
393
+ paths: {
394
+ config_path: paths.config_path,
395
+ tasks_dir: paths.tasks_dir,
396
+ inboxes_dir: paths.inboxes_dir,
397
+ tasks_dir_resolution: paths.tasks_dir_resolution,
398
+ },
399
+ }
400
+ : {}),
370
401
  },
371
402
  };
372
403
  }
@@ -383,19 +414,24 @@ export async function teamTaskList(args = {}) {
383
414
  try {
384
415
  validateTeamName(team_name);
385
416
  } catch {
386
- return err('INVALID_TEAM_NAME', 'team_name 형식이 올바르지 않습니다');
417
+ return err("INVALID_TEAM_NAME", "team_name 형식이 올바르지 않습니다");
387
418
  }
388
419
 
389
420
  const paths = await resolveTeamPaths(team_name);
390
- if (paths.tasks_dir_resolution === 'not_found') {
391
- return err('TASKS_DIR_NOT_FOUND', `task 디렉토리를 찾지 못했습니다: ${team_name}`);
421
+ if (paths.tasks_dir_resolution === "not_found") {
422
+ return err(
423
+ "TASKS_DIR_NOT_FOUND",
424
+ `task 디렉토리를 찾지 못했습니다: ${team_name}`,
425
+ );
392
426
  }
393
427
 
394
428
  const statusSet = new Set((statuses || []).map((s) => String(s)));
395
429
  const maxCount = Math.max(1, Math.min(Number(limit) || 200, 1000));
396
430
  let parseWarnings = 0;
397
431
  const files = await collectTaskFiles(paths.tasks_dir);
398
- const records = await Promise.all(files.map((file) => readTaskFileCached(file)));
432
+ const records = await Promise.all(
433
+ files.map((file) => readTaskFileCached(file)),
434
+ );
399
435
 
400
436
  const tasks = [];
401
437
  for (const { file, mtimeMs: fileMtime, json } of records) {
@@ -405,8 +441,9 @@ export async function teamTaskList(args = {}) {
405
441
  }
406
442
 
407
443
  if (!include_internal && json?.metadata?._internal === true) continue;
408
- if (owner && String(json.owner || '') !== String(owner)) continue;
409
- if (statusSet.size > 0 && !statusSet.has(String(json.status || ''))) continue;
444
+ if (owner && String(json.owner || "") !== String(owner)) continue;
445
+ if (statusSet.size > 0 && !statusSet.has(String(json.status || "")))
446
+ continue;
410
447
 
411
448
  tasks.push({
412
449
  ...json,
@@ -431,18 +468,26 @@ export async function teamTaskList(args = {}) {
431
468
  }
432
469
 
433
470
  // status 화이트리스트 (Claude Code API 호환)
434
- const VALID_STATUSES = new Set(['pending', 'in_progress', 'completed', 'deleted']);
471
+ const VALID_STATUSES = new Set([
472
+ "pending",
473
+ "in_progress",
474
+ "completed",
475
+ "deleted",
476
+ ]);
435
477
 
436
478
  export async function teamTaskUpdate(args = {}) {
437
479
  // "failed" → "completed" + metadata.result 자동 매핑
438
- if (String(args.status || '') === 'failed') {
480
+ if (String(args.status || "") === "failed") {
439
481
  args = {
440
482
  ...args,
441
- status: 'completed',
442
- metadata_patch: { ...(args.metadata_patch || {}), result: 'failed' },
483
+ status: "completed",
484
+ metadata_patch: { ...(args.metadata_patch || {}), result: "failed" },
443
485
  };
444
486
  } else if (args.status != null && !VALID_STATUSES.has(String(args.status))) {
445
- return err('INVALID_STATUS', `유효하지 않은 status: ${args.status}. 허용: ${[...VALID_STATUSES].join(', ')}`);
487
+ return err(
488
+ "INVALID_STATUS",
489
+ `유효하지 않은 status: ${args.status}. 허용: ${[...VALID_STATUSES].join(", ")}`,
490
+ );
446
491
  }
447
492
 
448
493
  const {
@@ -464,21 +509,24 @@ export async function teamTaskUpdate(args = {}) {
464
509
  try {
465
510
  validateTeamName(team_name);
466
511
  } catch {
467
- return err('INVALID_TEAM_NAME', 'team_name 형식이 올바르지 않습니다');
512
+ return err("INVALID_TEAM_NAME", "team_name 형식이 올바르지 않습니다");
468
513
  }
469
514
 
470
- if (!String(task_id || '').trim()) {
471
- return err('INVALID_TASK_ID', 'task_id가 필요합니다');
515
+ if (!String(task_id || "").trim()) {
516
+ return err("INVALID_TASK_ID", "task_id가 필요합니다");
472
517
  }
473
518
 
474
519
  const paths = await resolveTeamPaths(team_name);
475
- if (paths.tasks_dir_resolution === 'not_found') {
476
- return err('TASKS_DIR_NOT_FOUND', `task 디렉토리를 찾지 못했습니다: ${team_name}`);
520
+ if (paths.tasks_dir_resolution === "not_found") {
521
+ return err(
522
+ "TASKS_DIR_NOT_FOUND",
523
+ `task 디렉토리를 찾지 못했습니다: ${team_name}`,
524
+ );
477
525
  }
478
526
 
479
527
  const taskFile = await locateTaskFile(paths.tasks_dir, String(task_id));
480
528
  if (!taskFile) {
481
- return err('TASK_NOT_FOUND', `task를 찾지 못했습니다: ${task_id}`);
529
+ return err("TASK_NOT_FOUND", `task를 찾지 못했습니다: ${task_id}`);
482
530
  }
483
531
 
484
532
  const lockFile = `${taskFile}.lock`;
@@ -487,14 +535,19 @@ export async function teamTaskUpdate(args = {}) {
487
535
  return await withFileLock(lockFile, async () => {
488
536
  const before = await readJsonSafe(taskFile);
489
537
  if (!before || !isObject(before)) {
490
- return err('INVALID_TASK_FILE', `task 파일 파싱 실패: ${taskFile}`);
538
+ return err("INVALID_TASK_FILE", `task 파일 파싱 실패: ${taskFile}`);
491
539
  }
492
540
 
493
541
  let beforeMtime = Date.now();
494
- try { beforeMtime = (await stat(taskFile)).mtimeMs; } catch {}
495
-
496
- if (if_match_mtime_ms != null && Number(if_match_mtime_ms) !== Number(beforeMtime)) {
497
- return err('MTIME_CONFLICT', 'if_match_mtime_ms가 일치하지 않습니다', {
542
+ try {
543
+ beforeMtime = (await stat(taskFile)).mtimeMs;
544
+ } catch {}
545
+
546
+ if (
547
+ if_match_mtime_ms != null &&
548
+ Number(if_match_mtime_ms) !== Number(beforeMtime)
549
+ ) {
550
+ return err("MTIME_CONFLICT", "if_match_mtime_ms가 일치하지 않습니다", {
498
551
  task_file: taskFile,
499
552
  mtime_ms: beforeMtime,
500
553
  });
@@ -505,13 +558,16 @@ export async function teamTaskUpdate(args = {}) {
505
558
  let updated = false;
506
559
 
507
560
  if (claim) {
508
- const requestedOwner = String(owner || actor || '');
509
- const ownerNow = String(before.owner || '');
510
- const ownerCompatible = ownerNow === '' || requestedOwner === '' || ownerNow === requestedOwner;
511
- const statusPending = String(before.status || '') === 'pending';
561
+ const requestedOwner = String(owner || actor || "");
562
+ const ownerNow = String(before.owner || "");
563
+ const ownerCompatible =
564
+ ownerNow === "" ||
565
+ requestedOwner === "" ||
566
+ ownerNow === requestedOwner;
567
+ const statusPending = String(before.status || "") === "pending";
512
568
 
513
569
  if (!statusPending || !ownerCompatible) {
514
- return err('CLAIM_CONFLICT', 'task claim 충돌', {
570
+ return err("CLAIM_CONFLICT", "task claim 충돌", {
515
571
  task_before: before,
516
572
  task_file: taskFile,
517
573
  mtime_ms: beforeMtime,
@@ -519,28 +575,34 @@ export async function teamTaskUpdate(args = {}) {
519
575
  }
520
576
 
521
577
  if (requestedOwner) after.owner = requestedOwner;
522
- after.status = status || 'in_progress';
578
+ after.status = status || "in_progress";
523
579
  claimed = true;
524
580
  updated = true;
525
581
  }
526
582
 
527
- if (owner != null && String(after.owner || '') !== String(owner)) {
583
+ if (owner != null && String(after.owner || "") !== String(owner)) {
528
584
  after.owner = owner;
529
585
  updated = true;
530
586
  }
531
- if (status != null && String(after.status || '') !== String(status)) {
587
+ if (status != null && String(after.status || "") !== String(status)) {
532
588
  after.status = status;
533
589
  updated = true;
534
590
  }
535
- if (subject != null && String(after.subject || '') !== String(subject)) {
591
+ if (subject != null && String(after.subject || "") !== String(subject)) {
536
592
  after.subject = subject;
537
593
  updated = true;
538
594
  }
539
- if (description != null && String(after.description || '') !== String(description)) {
595
+ if (
596
+ description != null &&
597
+ String(after.description || "") !== String(description)
598
+ ) {
540
599
  after.description = description;
541
600
  updated = true;
542
601
  }
543
- if (activeForm != null && String(after.activeForm || '') !== String(activeForm)) {
602
+ if (
603
+ activeForm != null &&
604
+ String(after.activeForm || "") !== String(activeForm)
605
+ ) {
544
606
  after.activeForm = activeForm;
545
607
  updated = true;
546
608
  }
@@ -555,7 +617,9 @@ export async function teamTaskUpdate(args = {}) {
555
617
  }
556
618
 
557
619
  if (Array.isArray(add_blocked_by) && add_blocked_by.length > 0) {
558
- const current = Array.isArray(after.blockedBy) ? [...after.blockedBy] : [];
620
+ const current = Array.isArray(after.blockedBy)
621
+ ? [...after.blockedBy]
622
+ : [];
559
623
  for (const item of add_blocked_by) {
560
624
  if (!current.includes(item)) current.push(item);
561
625
  }
@@ -577,7 +641,9 @@ export async function teamTaskUpdate(args = {}) {
577
641
  }
578
642
 
579
643
  let afterMtime = beforeMtime;
580
- try { afterMtime = (await stat(taskFile)).mtimeMs; } catch {}
644
+ try {
645
+ afterMtime = (await stat(taskFile)).mtimeMs;
646
+ } catch {}
581
647
 
582
648
  return {
583
649
  ok: true,
@@ -592,36 +658,38 @@ export async function teamTaskUpdate(args = {}) {
592
658
  };
593
659
  });
594
660
  } catch (e) {
595
- return err('TASK_UPDATE_FAILED', e.message);
661
+ return err("TASK_UPDATE_FAILED", e.message);
596
662
  }
597
663
  }
598
664
 
599
665
  function sanitizeRecipientName(v) {
600
- return String(v || 'team-lead').replace(/[\\/:*?"<>|]/g, '_');
666
+ return String(v || "team-lead").replace(/[\\/:*?"<>|]/g, "_");
601
667
  }
602
668
 
603
669
  export async function teamSendMessage(args = {}) {
604
670
  const {
605
671
  team_name,
606
672
  from,
607
- to = 'team-lead',
673
+ to = "team-lead",
608
674
  text,
609
675
  summary,
610
- color = 'blue',
676
+ color = "blue",
611
677
  } = args;
612
678
 
613
679
  try {
614
680
  validateTeamName(team_name);
615
681
  } catch {
616
- return err('INVALID_TEAM_NAME', 'team_name 형식이 올바르지 않습니다');
682
+ return err("INVALID_TEAM_NAME", "team_name 형식이 올바르지 않습니다");
617
683
  }
618
684
 
619
- if (!String(from || '').trim()) return err('INVALID_FROM', 'from이 필요합니다');
620
- if (!String(text || '').trim()) return err('INVALID_TEXT', 'text가 필요합니다');
685
+ if (!String(from || "").trim())
686
+ return err("INVALID_FROM", "from이 필요합니다");
687
+ if (!String(text || "").trim())
688
+ return err("INVALID_TEXT", "text가 필요합니다");
621
689
 
622
690
  const paths = await resolveTeamPaths(team_name);
623
691
  if (!existsSync(paths.team_dir)) {
624
- return err('TEAM_NOT_FOUND', `팀 디렉토리가 없습니다: ${paths.team_dir}`);
692
+ return err("TEAM_NOT_FOUND", `팀 디렉토리가 없습니다: ${paths.team_dir}`);
625
693
  }
626
694
 
627
695
  const recipient = sanitizeRecipientName(to);
@@ -640,7 +708,7 @@ export async function teamSendMessage(args = {}) {
640
708
  text: String(text),
641
709
  ...(summary ? { summary: String(summary) } : {}),
642
710
  timestamp: new Date().toISOString(),
643
- color: String(color || 'blue'),
711
+ color: String(color || "blue"),
644
712
  read: false,
645
713
  };
646
714
  list.push(message);
@@ -650,8 +718,11 @@ export async function teamSendMessage(args = {}) {
650
718
  if (list.length > MAX_INBOX) {
651
719
  const ONE_HOUR_MS = 3600000;
652
720
  const cutoff = Date.now() - ONE_HOUR_MS;
653
- const pruned = list.filter((m) =>
654
- m?.read !== true || !m?.timestamp || new Date(m.timestamp).getTime() > cutoff
721
+ const pruned = list.filter(
722
+ (m) =>
723
+ m?.read !== true ||
724
+ !m?.timestamp ||
725
+ new Date(m.timestamp).getTime() > cutoff,
655
726
  );
656
727
  list.length = 0;
657
728
  list.push(...pruned);
@@ -676,7 +747,7 @@ export async function teamSendMessage(args = {}) {
676
747
  },
677
748
  };
678
749
  } catch (e) {
679
- return err('SEND_MESSAGE_FAILED', e.message);
750
+ return err("SEND_MESSAGE_FAILED", e.message);
680
751
  }
681
752
  }
682
753