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.
- package/LICENSE +21 -21
- package/bin/tfx-doctor-tui.mjs +1 -1
- package/bin/tfx-doctor.mjs +6 -1
- package/bin/tfx-profile.mjs +1 -1
- package/bin/tfx-setup-tui.mjs +1 -1
- package/bin/tfx-setup.mjs +6 -1
- package/bin/triflux.mjs +2396 -1140
- package/hooks/agent-route-guard.mjs +12 -8
- package/hooks/cross-review-tracker.mjs +21 -8
- package/hooks/error-context.mjs +19 -7
- package/hooks/hook-adaptive-collector.mjs +18 -16
- package/hooks/hook-manager.mjs +93 -32
- package/hooks/hook-orchestrator.mjs +108 -24
- package/hooks/hook-registry.json +11 -0
- package/hooks/keyword-rules.json +6 -10
- package/hooks/lib/resolve-root.mjs +1 -1
- package/hooks/mcp-config-watcher.mjs +6 -2
- package/hooks/pipeline-stop.mjs +3 -6
- package/hooks/safety-guard.mjs +99 -28
- package/hooks/session-start-fast.mjs +143 -0
- package/hooks/subagent-verifier.mjs +5 -4
- package/hub/account-broker.mjs +256 -60
- package/hub/adaptive-diagnostic.mjs +75 -48
- package/hub/adaptive-inject.mjs +95 -57
- package/hub/adaptive-memory.mjs +156 -42
- package/hub/adaptive.mjs +60 -31
- package/hub/assign-callbacks.mjs +67 -30
- package/hub/bridge.mjs +0 -1
- package/hub/cli-adapter-base.mjs +200 -48
- package/hub/codex-adapter.mjs +76 -96
- package/hub/codex-compat.mjs +3 -3
- package/hub/codex-preflight.mjs +63 -37
- package/hub/delegator/contracts.mjs +19 -23
- package/hub/delegator/index.mjs +3 -3
- package/hub/delegator/service.mjs +88 -64
- package/hub/delegator/tool-definitions.mjs +5 -5
- package/hub/fullcycle.mjs +33 -17
- package/hub/gemini-adapter.mjs +69 -94
- package/hub/hitl.mjs +89 -30
- package/hub/intent.mjs +161 -38
- package/hub/lib/cache-guard.mjs +43 -17
- package/hub/lib/mcp-response-cache.mjs +66 -32
- package/hub/lib/memory-store.mjs +285 -111
- package/hub/lib/path-utils.mjs +35 -37
- package/hub/lib/process-utils.mjs +106 -37
- package/hub/lib/spawn-trace.mjs +527 -0
- package/hub/lib/ssh-command.mjs +34 -4
- package/hub/lib/ssh-retry.mjs +5 -1
- package/hub/lib/uuidv7.mjs +4 -3
- package/hub/memory-doctor.mjs +266 -106
- package/hub/middleware/request-logger.mjs +61 -34
- package/hub/paths.mjs +9 -9
- package/hub/pipeline/gates/confidence.mjs +34 -15
- package/hub/pipeline/gates/consensus.mjs +27 -15
- package/hub/pipeline/gates/index.mjs +7 -3
- package/hub/pipeline/gates/selfcheck.mjs +57 -19
- package/hub/pipeline/index.mjs +77 -42
- package/hub/pipeline/state.mjs +10 -10
- package/hub/pipeline/transitions.mjs +40 -23
- package/hub/platform.mjs +57 -48
- package/hub/promote-penalties.mjs +25 -7
- package/hub/quality/deslop.mjs +70 -49
- package/hub/research.mjs +32 -25
- package/hub/router.mjs +240 -107
- package/hub/routing/complexity.mjs +132 -29
- package/hub/routing/index.mjs +17 -12
- package/hub/routing/q-learning.mjs +76 -28
- package/hub/server.mjs +4 -4
- package/hub/session-fingerprint.mjs +126 -60
- package/hub/state.mjs +84 -43
- package/hub/store-adapter.mjs +59 -26
- package/hub/store.mjs +356 -153
- package/hub/team/agent-map.json +22 -7
- package/hub/team/ansi.mjs +186 -122
- package/hub/team/backend.mjs +28 -10
- package/hub/team/cli/commands/attach.mjs +29 -9
- package/hub/team/cli/commands/control.mjs +29 -8
- package/hub/team/cli/commands/debug.mjs +32 -11
- package/hub/team/cli/commands/focus.mjs +38 -11
- package/hub/team/cli/commands/interrupt.mjs +18 -6
- package/hub/team/cli/commands/kill.mjs +16 -5
- package/hub/team/cli/commands/list.mjs +11 -4
- package/hub/team/cli/commands/send.mjs +19 -6
- package/hub/team/cli/commands/start/index.mjs +154 -31
- package/hub/team/cli/commands/start/parse-args.mjs +38 -11
- package/hub/team/cli/commands/start/start-headless.mjs +112 -36
- package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
- package/hub/team/cli/commands/start/start-mux.mjs +70 -21
- package/hub/team/cli/commands/start/start-wt.mjs +29 -12
- package/hub/team/cli/commands/status.mjs +43 -14
- package/hub/team/cli/commands/stop.mjs +11 -4
- package/hub/team/cli/commands/task.mjs +8 -3
- package/hub/team/cli/commands/tasks.mjs +1 -1
- package/hub/team/cli/index.mjs +2 -2
- package/hub/team/cli/manifest.mjs +38 -8
- package/hub/team/cli/render.mjs +30 -8
- package/hub/team/cli/services/attach-fallback.mjs +31 -11
- package/hub/team/cli/services/hub-client.mjs +42 -14
- package/hub/team/cli/services/member-selector.mjs +11 -4
- package/hub/team/cli/services/native-control.mjs +48 -21
- package/hub/team/cli/services/runtime-mode.mjs +2 -1
- package/hub/team/cli/services/state-store.mjs +25 -8
- package/hub/team/cli/services/task-model.mjs +16 -6
- package/hub/team/conductor-mesh-bridge.mjs +24 -23
- package/hub/team/conductor.mjs +8 -4
- package/hub/team/dashboard-anchor.mjs +4 -5
- package/hub/team/dashboard-layout.mjs +3 -1
- package/hub/team/dashboard-open.mjs +41 -21
- package/hub/team/dashboard.mjs +76 -28
- package/hub/team/event-log.mjs +18 -10
- package/hub/team/handoff.mjs +31 -15
- package/hub/team/headless.mjs +2 -1
- package/hub/team/health-probe.mjs +69 -54
- package/hub/team/launcher-template.mjs +16 -13
- package/hub/team/native-supervisor.mjs +65 -21
- package/hub/team/native.mjs +74 -35
- package/hub/team/nativeProxy.mjs +184 -113
- package/hub/team/notify.mjs +119 -76
- package/hub/team/orchestrator.mjs +9 -4
- package/hub/team/pane.mjs +12 -7
- package/hub/team/process-cleanup.mjs +25 -16
- package/hub/team/psmux.mjs +491 -201
- package/hub/team/remote-probe.mjs +68 -52
- package/hub/team/remote-session.mjs +117 -59
- package/hub/team/remote-watcher.mjs +61 -33
- package/hub/team/routing.mjs +51 -25
- package/hub/team/runtime-strategy.mjs +3 -1
- package/hub/team/session.mjs +98 -34
- package/hub/team/staleState.mjs +72 -30
- package/hub/team/swarm-locks.mjs +15 -13
- package/hub/team/swarm-planner.mjs +32 -21
- package/hub/team/swarm-reconciler.mjs +48 -23
- package/hub/team/tui-lite.mjs +266 -68
- package/hub/team/tui-remote-adapter.mjs +14 -10
- package/hub/team/tui-viewer.mjs +99 -43
- package/hub/team/tui.mjs +708 -271
- package/hub/team/worktree-lifecycle.mjs +152 -58
- package/hub/team/wt-manager.mjs +24 -14
- package/hub/token-mode.mjs +71 -71
- package/hub/tray.mjs +66 -23
- package/hub/workers/claude-worker.mjs +162 -118
- package/hub/workers/codex-mcp.mjs +192 -141
- package/hub/workers/delegator-mcp.mjs +507 -333
- package/hub/workers/factory.mjs +8 -8
- package/hub/workers/gemini-worker.mjs +115 -84
- package/hub/workers/interface.mjs +6 -1
- package/hub/workers/worker-utils.mjs +21 -14
- package/hud/colors.mjs +27 -9
- package/hud/constants.mjs +162 -26
- package/hud/context-monitor.mjs +82 -41
- package/hud/hud-qos-status.mjs +129 -49
- package/hud/mission-board.mjs +6 -3
- package/hud/providers/claude.mjs +226 -115
- package/hud/providers/codex.mjs +62 -22
- package/hud/providers/gemini.mjs +168 -56
- package/hud/renderers.mjs +384 -119
- package/hud/terminal.mjs +101 -31
- package/hud/utils.mjs +78 -38
- package/mesh/index.mjs +11 -5
- package/mesh/mesh-budget.mjs +18 -9
- package/mesh/mesh-heartbeat.mjs +1 -1
- package/mesh/mesh-queue.mjs +3 -5
- package/mesh/mesh-router.mjs +5 -4
- package/package.json +2 -1
- package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
- package/scripts/__tests__/keyword-detector.test.mjs +77 -28
- package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
- package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
- package/scripts/__tests__/remote-spawn.test.mjs +10 -4
- package/scripts/__tests__/session-start-fast.test.mjs +36 -0
- package/scripts/__tests__/skill-template.test.mjs +98 -50
- package/scripts/__tests__/smoke.test.mjs +1 -1
- package/scripts/__tests__/spawn-trace.test.mjs +102 -0
- package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
- package/scripts/cache-doctor.mjs +11 -4
- package/scripts/cache-warmup.mjs +96 -37
- package/scripts/claudemd-sync.mjs +27 -17
- package/scripts/codex-gateway-preflight.mjs +52 -37
- package/scripts/codex-mcp-gateway-sync.mjs +59 -39
- package/scripts/completions/tfx.bash +47 -47
- package/scripts/completions/tfx.fish +44 -44
- package/scripts/completions/tfx.zsh +83 -83
- package/scripts/config-audit.mjs +232 -0
- package/scripts/convert-to-tmpl.mjs +54 -0
- package/scripts/cross-review-gate.mjs +35 -12
- package/scripts/cross-review-tracker.mjs +21 -8
- package/scripts/demo.mjs +35 -17
- package/scripts/doctor-diagnose.mjs +284 -0
- package/scripts/gen-skill-docs.mjs +7 -2
- package/scripts/gen-skill-manifest.mjs +2 -1
- package/scripts/headless-guard.mjs +86 -48
- package/scripts/hub-ensure.mjs +45 -26
- package/scripts/keyword-detector.mjs +41 -20
- package/scripts/keyword-rules-expander.mjs +47 -30
- package/scripts/lib/claudemd-scanner.mjs +6 -1
- package/scripts/lib/context.mjs +3 -3
- package/scripts/lib/cross-review-utils.mjs +6 -3
- package/scripts/lib/env-probe.mjs +47 -28
- package/scripts/lib/gemini-profiles.mjs +44 -10
- package/scripts/lib/handoff.mjs +33 -17
- package/scripts/lib/hook-utils.mjs +8 -6
- package/scripts/lib/keyword-rules.mjs +43 -19
- package/scripts/lib/logger.mjs +24 -24
- package/scripts/lib/mcp-filter.mjs +377 -239
- package/scripts/lib/mcp-guard-engine.mjs +194 -79
- package/scripts/lib/mcp-manifest.mjs +23 -13
- package/scripts/lib/mcp-server-catalog.mjs +300 -63
- package/scripts/lib/psmux-info.mjs +11 -6
- package/scripts/lib/remote-spawn-transfer.mjs +44 -14
- package/scripts/lib/skill-template.mjs +30 -7
- package/scripts/mcp-check.mjs +58 -39
- package/scripts/mcp-gateway-config.mjs +83 -39
- package/scripts/mcp-gateway-ensure.mjs +43 -35
- package/scripts/mcp-gateway-integration-test.mjs +70 -58
- package/scripts/mcp-gateway-start.mjs +126 -60
- package/scripts/mcp-gateway-verify.mjs +24 -22
- package/scripts/mcp-safety-guard.mjs +44 -11
- package/scripts/notion-read.mjs +199 -84
- package/scripts/pack.mjs +94 -89
- package/scripts/preflight-cache.mjs +27 -10
- package/scripts/preinstall.mjs +42 -13
- package/scripts/remote-spawn.mjs +309 -94
- package/scripts/run.cjs +8 -5
- package/scripts/session-spawn-helper.mjs +130 -39
- package/scripts/session-stale-cleanup.mjs +123 -0
- package/scripts/setup.mjs +941 -492
- package/scripts/test-lock.mjs +20 -7
- package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
- package/scripts/tfx-batch-stats.mjs +32 -11
- package/scripts/tfx-gate-activate.mjs +11 -4
- package/scripts/tfx-route-post.mjs +87 -20
- package/scripts/tfx-route-worker.mjs +57 -51
- package/scripts/tfx-route.sh +41 -124
- package/scripts/tmp-cleanup.mjs +21 -7
- package/scripts/token-snapshot.mjs +204 -85
- package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
- package/skills/.omc/state/idle-notif-cooldown.json +3 -0
- package/skills/.omc/state/last-tool-error.json +7 -0
- package/skills/.omc/state/subagent-tracking.json +7 -0
- package/skills/_templates/base.md +1 -6
- package/skills/merge-worktree/SKILL.md.tmpl +144 -0
- package/skills/shared/telemetry-segment.md +6 -0
- package/skills/star-prompt/SKILL.md.tmpl +222 -0
- package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
- package/skills/tfx-analysis/skill.json +1 -6
- package/skills/tfx-auto/SKILL.md +1 -0
- package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
- package/skills/tfx-auto-codex/skill.json +1 -3
- package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
- package/skills/tfx-autopilot/skill.json +1 -5
- package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
- package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
- package/skills/tfx-autoroute/skill.json +1 -7
- package/skills/tfx-codex/SKILL.md +1 -0
- package/skills/tfx-codex/skill.json +1 -3
- package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
- package/skills/tfx-codex-swarm/evals/evals.json +1 -1
- package/skills/tfx-codex-swarm/skill.json +1 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
- package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
- package/skills/tfx-debate/SKILL.md.tmpl +192 -0
- package/skills/tfx-debate/skill.json +1 -7
- package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
- package/skills/tfx-deep-analysis/skill.json +1 -5
- package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
- package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
- package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
- package/skills/tfx-deep-qa/skill.json +1 -6
- package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
- package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
- package/skills/tfx-doctor/SKILL.md +21 -0
- package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
- package/skills/tfx-doctor/skill.json +1 -3
- package/skills/tfx-find/SKILL.md +1 -0
- package/skills/tfx-forge/SKILL.md.tmpl +187 -0
- package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
- package/skills/tfx-fullcycle/skill.json +1 -6
- package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
- package/skills/tfx-gemini/skill.json +1 -3
- package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
- package/skills/tfx-hooks/skill.json +1 -3
- package/skills/tfx-hub/SKILL.md.tmpl +212 -0
- package/skills/tfx-hub/skill.json +1 -3
- package/skills/tfx-index/SKILL.md +1 -0
- package/skills/tfx-index/skill.json +1 -6
- package/skills/tfx-interview/SKILL.md.tmpl +285 -0
- package/skills/tfx-multi/SKILL.md.tmpl +183 -0
- package/skills/tfx-multi/skill.json +1 -3
- package/skills/tfx-panel/SKILL.md.tmpl +189 -0
- package/skills/tfx-panel/skill.json +1 -7
- package/skills/tfx-persist/SKILL.md.tmpl +270 -0
- package/skills/tfx-persist/skill.json +1 -7
- package/skills/tfx-plan/SKILL.md +1 -0
- package/skills/tfx-plan/skill.json +1 -6
- package/skills/tfx-profile/SKILL.md.tmpl +239 -0
- package/skills/tfx-profile/skill.json +1 -3
- package/skills/tfx-prune/SKILL.md.tmpl +200 -0
- package/skills/tfx-prune/skill.json +1 -7
- package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
- package/skills/tfx-psmux-rules/skill.json +1 -4
- package/skills/tfx-qa/SKILL.md +1 -0
- package/skills/tfx-qa/skill.json +1 -6
- package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
- package/skills/tfx-ralph/skill.json +1 -4
- package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
- package/skills/tfx-remote-setup/skill.json +1 -3
- package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
- package/skills/tfx-remote-spawn/references/hosts.json +16 -0
- package/skills/tfx-remote-spawn/skill.json +1 -4
- package/skills/tfx-research/SKILL.md +1 -0
- package/skills/tfx-review/SKILL.md +1 -0
- package/skills/tfx-review/skill.json +1 -6
- package/skills/tfx-setup/SKILL.md.tmpl +504 -0
- package/skills/tfx-setup/skill.json +1 -3
- package/skills/tfx-swarm/SKILL.md +22 -0
- package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
- package/tui/codex-profile.mjs +88 -33
- package/tui/core.mjs +45 -15
- package/tui/doctor.mjs +75 -28
- package/tui/gemini-profile.mjs +74 -29
- package/tui/monitor-data.mjs +8 -4
- package/tui/monitor.mjs +71 -27
- 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 {
|
|
13
|
-
|
|
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.
|
|
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.
|
|
34
|
-
claude_opus: { cache_write: 18.75, cache_read: 1.
|
|
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 {
|
|
66
|
-
|
|
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) {
|
|
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 (
|
|
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 {
|
|
111
|
-
|
|
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:
|
|
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] = {
|
|
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 {
|
|
159
|
-
|
|
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,
|
|
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,
|
|
175
|
-
|
|
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 {
|
|
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 {
|
|
201
|
-
|
|
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,
|
|
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,
|
|
250
|
-
|
|
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(
|
|
287
|
-
|
|
288
|
-
|
|
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 = {
|
|
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 = {
|
|
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:
|
|
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,
|
|
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, {
|
|
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(
|
|
414
|
-
|
|
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(
|
|
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 =
|
|
444
|
-
|
|
445
|
-
overheadCost +=
|
|
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,
|
|
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,
|
|
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: {
|
|
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 {
|
|
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 =
|
|
529
|
-
|
|
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
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
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
|
-
|
|
551
|
-
|
|
552
|
-
if (
|
|
553
|
-
|
|
554
|
-
|
|
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
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
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,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
|
+
}
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
> **ARGUMENTS 처리**: 이 스킬이 `ARGUMENTS: <값>`과 함께 호출되면, 해당 값을 사용자 입력으로 취급하여
|
|
2
2
|
> 워크플로우의 첫 단계 입력으로 사용한다. ARGUMENTS가 비어있거나 없으면 기존 절차대로 사용자에게 입력을 요청한다.
|
|
3
3
|
|
|
4
|
-
|
|
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
|
+
* 예상치 못한 상황이면 추측하지 말고 **중지 후 설명**
|