triflux 10.0.0-alpha.1 → 10.0.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/bin/triflux.mjs +290 -25
- package/package.json +1 -1
- package/skills/_templates/base.md +9 -0
- package/skills/_templates/deep.md +6 -0
- package/skills/merge-worktree/SKILL.md.tmpl +144 -0
- package/skills/shared/arguments-processing.md +2 -0
- package/skills/shared/mandatory-rules.md +6 -0
- package/skills/shared/telemetry-segment.md +6 -0
- package/skills/star-prompt/SKILL.md.tmpl +122 -0
- package/skills/tfx-analysis/SKILL.md.tmpl +106 -0
- package/skills/tfx-analysis/skill.json +11 -0
- package/skills/tfx-auto/SKILL.md +8 -0
- package/skills/{tfx-workspace/skill-snapshot/tfx-auto/SKILL.md → tfx-auto/SKILL.md.tmpl} +91 -21
- package/skills/tfx-auto/skill.json +26 -0
- package/skills/{tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md → tfx-auto-codex/SKILL.md.tmpl} +31 -3
- package/skills/tfx-auto-codex/skill.json +8 -0
- package/skills/tfx-autopilot/SKILL.md.tmpl +115 -0
- package/skills/tfx-autopilot/skill.json +10 -0
- package/skills/tfx-autoresearch/SKILL.md +5 -6
- package/skills/tfx-autoresearch/SKILL.md.tmpl +135 -0
- package/skills/tfx-autoresearch/skill.json +14 -0
- package/skills/tfx-autoroute/SKILL.md.tmpl +188 -0
- package/skills/tfx-autoroute/skill.json +12 -0
- package/skills/tfx-codex/SKILL.md +8 -0
- package/skills/{tfx-workspace/skill-snapshot/tfx-codex/SKILL.md → tfx-codex/SKILL.md.tmpl} +15 -7
- package/skills/tfx-codex/skill.json +8 -0
- package/skills/tfx-codex-swarm/SKILL.md +10 -627
- package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
- package/skills/tfx-codex-swarm/skill.json +5 -0
- package/skills/tfx-consensus/SKILL.md.tmpl +145 -0
- package/skills/tfx-consensus/skill.json +8 -0
- package/skills/tfx-debate/SKILL.md.tmpl +191 -0
- package/skills/tfx-debate/skill.json +12 -0
- package/skills/tfx-deep-analysis/SKILL.md.tmpl +227 -0
- package/skills/tfx-deep-analysis/skill.json +10 -0
- package/skills/tfx-deep-interview/SKILL.md +2 -1
- package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
- package/skills/tfx-deep-interview/skill.json +12 -0
- package/skills/tfx-deep-plan/SKILL.md.tmpl +281 -0
- package/skills/tfx-deep-plan/skill.json +13 -0
- package/skills/tfx-deep-qa/SKILL.md.tmpl +164 -0
- package/skills/tfx-deep-qa/skill.json +11 -0
- package/skills/tfx-deep-research/SKILL.md.tmpl +216 -0
- package/skills/tfx-deep-research/skill.json +14 -0
- package/skills/tfx-deep-review/SKILL.md.tmpl +178 -0
- package/skills/tfx-deep-review/skill.json +12 -0
- package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
- package/skills/tfx-doctor/skill.json +8 -0
- package/skills/tfx-find/SKILL.md +12 -1
- package/skills/tfx-find/SKILL.md.tmpl +129 -0
- package/skills/tfx-find/skill.json +12 -0
- package/skills/tfx-forge/SKILL.md.tmpl +187 -0
- package/skills/tfx-forge/skill.json +12 -0
- package/skills/tfx-fullcycle/SKILL.md.tmpl +285 -0
- package/skills/tfx-fullcycle/skill.json +11 -0
- package/skills/{tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md → tfx-gemini/SKILL.md.tmpl} +14 -6
- package/skills/tfx-gemini/skill.json +8 -0
- package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
- package/skills/tfx-hooks/skill.json +8 -0
- package/skills/tfx-hub/SKILL.md.tmpl +212 -0
- package/skills/tfx-hub/skill.json +8 -0
- package/skills/tfx-index/SKILL.md +12 -1
- package/skills/tfx-index/SKILL.md.tmpl +148 -0
- package/skills/tfx-index/skill.json +11 -0
- package/skills/tfx-interview/SKILL.md.tmpl +284 -0
- package/skills/tfx-interview/skill.json +12 -0
- package/skills/tfx-multi/SKILL.md.tmpl +183 -0
- package/skills/tfx-multi/skill.json +8 -0
- package/skills/tfx-panel/SKILL.md.tmpl +188 -0
- package/skills/tfx-panel/skill.json +12 -0
- package/skills/tfx-persist/SKILL.md.tmpl +269 -0
- package/skills/tfx-persist/skill.json +12 -0
- package/skills/tfx-plan/SKILL.md +8 -0
- package/skills/tfx-plan/SKILL.md.tmpl +68 -0
- package/skills/tfx-plan/skill.json +11 -0
- package/skills/tfx-profile/SKILL.md.tmpl +239 -0
- package/skills/tfx-profile/skill.json +8 -0
- package/skills/tfx-prune/SKILL.md.tmpl +199 -0
- package/skills/tfx-prune/skill.json +12 -0
- package/skills/tfx-psmux-rules/SKILL.md.tmpl +317 -0
- package/skills/tfx-psmux-rules/skill.json +8 -0
- package/skills/tfx-qa/SKILL.md +8 -0
- package/skills/tfx-qa/SKILL.md.tmpl +122 -0
- package/skills/tfx-qa/skill.json +11 -0
- package/skills/tfx-ralph/SKILL.md.tmpl +27 -0
- package/skills/tfx-ralph/skill.json +8 -0
- package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
- package/skills/tfx-remote-setup/skill.json +8 -0
- package/skills/tfx-remote-spawn/SKILL.md +11 -8
- package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
- package/skills/tfx-remote-spawn/skill.json +9 -0
- package/skills/tfx-research/SKILL.md +8 -0
- package/skills/tfx-research/SKILL.md.tmpl +149 -0
- package/skills/tfx-research/skill.json +13 -0
- package/skills/tfx-review/SKILL.md +8 -0
- package/skills/tfx-review/SKILL.md.tmpl +57 -0
- package/skills/tfx-review/skill.json +11 -0
- package/skills/tfx-setup/SKILL.md.tmpl +380 -0
- package/skills/tfx-setup/skill.json +8 -0
- package/skills/tfx-swarm/SKILL.md +154 -0
- package/skills/tfx-swarm/SKILL.md.tmpl +154 -0
- package/skills/tfx-swarm/skill.json +5 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +0 -33
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/eval_metadata.json +0 -42
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +0 -11
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/outputs/analysis.md +0 -87
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/outputs/classification.md +0 -35
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/outputs/commands.sh +0 -275
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/outputs/routing.md +0 -56
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/timing.json +0 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +0 -11
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/outputs/analysis.md +0 -92
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/outputs/classification.md +0 -71
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/outputs/commands.sh +0 -264
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/outputs/routing.md +0 -113
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/timing.json +0 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/eval_metadata.json +0 -32
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +0 -9
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/outputs/analysis.md +0 -96
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/outputs/classification.md +0 -38
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/outputs/commands.sh +0 -151
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/outputs/routing.md +0 -51
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/timing.json +0 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +0 -9
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/outputs/analysis.md +0 -127
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/outputs/classification.md +0 -57
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/outputs/commands.sh +0 -129
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/outputs/routing.md +0 -84
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/timing.json +0 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/eval_metadata.json +0 -27
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +0 -8
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/outputs/analysis.md +0 -98
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/outputs/classification.md +0 -65
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/outputs/commands.sh +0 -123
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/outputs/routing.md +0 -66
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/timing.json +0 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +0 -8
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/outputs/analysis.md +0 -88
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/outputs/classification.md +0 -40
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/outputs/commands.sh +0 -130
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/outputs/routing.md +0 -61
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/timing.json +0 -5
- package/skills/tfx-workspace/async-tests/run-tests.sh +0 -203
- package/skills/tfx-workspace/evals/evals.json +0 -79
- package/skills/tfx-workspace/iteration-1/benchmark.json +0 -162
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +0 -11
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +0 -9
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +0 -154
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +0 -9
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +0 -126
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +0 -11
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +0 -9
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +0 -119
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +0 -9
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +0 -115
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +0 -10
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +0 -8
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +0 -86
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +0 -8
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +0 -81
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +0 -12
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +0 -10
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +0 -316
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +0 -10
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +0 -352
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/review.html +0 -1325
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +0 -12
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +0 -10
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +0 -97
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +0 -10
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +0 -94
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +0 -12
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +0 -10
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +0 -209
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +0 -10
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +0 -193
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-2/benchmark.json +0 -62
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +0 -13
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +0 -11
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +0 -382
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +0 -11
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +0 -333
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-2/review.html +0 -1325
- package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +0 -94
- package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +0 -133
- package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +0 -426
- package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +0 -101
package/bin/triflux.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// triflux CLI — setup, doctor, version
|
|
3
3
|
import { copyFileSync, existsSync, readFileSync, readSync, writeFileSync, mkdirSync, chmodSync, readdirSync, unlinkSync, statSync, openSync, closeSync } from "fs";
|
|
4
|
-
import { join, dirname, basename } from "path";
|
|
4
|
+
import { join, dirname, basename, resolve } from "path";
|
|
5
5
|
import { homedir, tmpdir } from "os";
|
|
6
6
|
import { execSync, execFileSync, spawn } from "child_process";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
@@ -11,14 +11,17 @@ import { detectMultiplexer, getSessionAttachedCount, killSession, listSessions,
|
|
|
11
11
|
import { forceCleanupTeam } from "../hub/team/nativeProxy.mjs";
|
|
12
12
|
import { cleanupStaleOmcTeams, inspectStaleOmcTeams } from "../hub/team/staleState.mjs";
|
|
13
13
|
import { getPipelineStateDbPath } from "../hub/pipeline/state.mjs";
|
|
14
|
+
import { serializeHandoff } from "../scripts/lib/handoff.mjs";
|
|
14
15
|
import { ensureGeminiProfiles } from "../scripts/lib/gemini-profiles.mjs";
|
|
15
16
|
import { probePsmuxSupport, formatPsmuxInstallGuidance, formatPsmuxUpdateGuidance } from "../scripts/lib/psmux-info.mjs";
|
|
16
17
|
import {
|
|
17
18
|
addRegistryServer,
|
|
19
|
+
createDefaultRegistry,
|
|
18
20
|
inspectRegistry,
|
|
19
21
|
inspectRegistryStatus,
|
|
20
22
|
removeRegistryServer,
|
|
21
23
|
removeServerFromTargets,
|
|
24
|
+
saveRegistry,
|
|
22
25
|
syncRegistryTargets,
|
|
23
26
|
} from "../scripts/lib/mcp-guard-engine.mjs";
|
|
24
27
|
import {
|
|
@@ -28,7 +31,13 @@ import {
|
|
|
28
31
|
extractManagedHookFilename, getManagedRegistryHooks, ensureHooksInSettings,
|
|
29
32
|
ensureCodexHubServerConfig,
|
|
30
33
|
} from "../scripts/setup.mjs";
|
|
34
|
+
import {
|
|
35
|
+
ensureGlobalClaudeRoutingSection,
|
|
36
|
+
ensureTfxSection,
|
|
37
|
+
getLatestRoutingTable,
|
|
38
|
+
} from "../scripts/claudemd-sync.mjs";
|
|
31
39
|
import { cleanupTmpFiles } from "../scripts/tmp-cleanup.mjs";
|
|
40
|
+
import { checkNetworkAvailability, validateRuntimeCachePaths } from "../hub/lib/cache-guard.mjs";
|
|
32
41
|
|
|
33
42
|
const PKG_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
34
43
|
const CLAUDE_DIR = join(homedir(), ".claude");
|
|
@@ -98,6 +107,17 @@ const CLI_COMMAND_SCHEMAS = Object.freeze({
|
|
|
98
107
|
{ name: "--json", type: "boolean", description: "버전 정보를 JSON으로 출력" },
|
|
99
108
|
],
|
|
100
109
|
},
|
|
110
|
+
handoff: {
|
|
111
|
+
usage: "tfx handoff [--target local|remote] [--decision <text>] [--decision-file <path>] [--output <path>] [--json]",
|
|
112
|
+
description: "현재 작업 컨텍스트를 세션 핸드오프 프롬프트로 직렬화",
|
|
113
|
+
options: [
|
|
114
|
+
{ name: "--target", type: "string", description: "주입 대상 (local|remote, 기본값 remote)" },
|
|
115
|
+
{ name: "--decision", type: "string", description: "핸드오프 결정사항 (반복 지정 가능)" },
|
|
116
|
+
{ name: "--decision-file", type: "string", description: "결정사항 파일 (라인/불릿 단위)" },
|
|
117
|
+
{ name: "--output", type: "string", description: "생성한 핸드오프 프롬프트 저장 경로" },
|
|
118
|
+
{ name: "--json", type: "boolean", description: "핸드오프 결과를 JSON으로 출력" },
|
|
119
|
+
],
|
|
120
|
+
},
|
|
101
121
|
list: {
|
|
102
122
|
usage: "tfx list [--json]",
|
|
103
123
|
description: "패키지 스킬과 사용자 스킬 목록 표시",
|
|
@@ -522,6 +542,26 @@ function describeSyncAction(src, dst, label) {
|
|
|
522
542
|
};
|
|
523
543
|
}
|
|
524
544
|
|
|
545
|
+
function syncClaudeRoutingSectionsForCli() {
|
|
546
|
+
try {
|
|
547
|
+
const routingTable = getLatestRoutingTable();
|
|
548
|
+
return [
|
|
549
|
+
ensureTfxSection(join(PKG_ROOT, "CLAUDE.md"), routingTable),
|
|
550
|
+
ensureGlobalClaudeRoutingSection(CLAUDE_DIR),
|
|
551
|
+
];
|
|
552
|
+
} catch (error) {
|
|
553
|
+
const reason = error instanceof Error ? error.message : "routing_sync_failed";
|
|
554
|
+
return [{ action: "unchanged", path: join(PKG_ROOT, "CLAUDE.md"), skipped: true, reason }];
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function getClaudeRoutingSyncSummary(results) {
|
|
559
|
+
return results.reduce((summary, result) => ({
|
|
560
|
+
changed: summary.changed + (result.action === "created" || result.action === "updated" ? 1 : 0),
|
|
561
|
+
skipped: summary.skipped + (result.skipped ? 1 : 0),
|
|
562
|
+
}), { changed: 0, skipped: 0 });
|
|
563
|
+
}
|
|
564
|
+
|
|
525
565
|
// ── 크로스 셸 진단 ──
|
|
526
566
|
|
|
527
567
|
function checkCliCrossShell(cmd, installHint) {
|
|
@@ -691,7 +731,7 @@ function buildSetupDryRunPlan() {
|
|
|
691
731
|
}
|
|
692
732
|
|
|
693
733
|
function cmdSetup(options = {}) {
|
|
694
|
-
const { dryRun = false, overrideVersion } = options;
|
|
734
|
+
const { dryRun = false, overrideVersion, skipClaudeMdSync = false } = options;
|
|
695
735
|
if (dryRun) {
|
|
696
736
|
printJson(buildSetupDryRunPlan());
|
|
697
737
|
return;
|
|
@@ -781,6 +821,21 @@ function cmdSetup(options = {}) {
|
|
|
781
821
|
// ── 결과 추적 ──
|
|
782
822
|
const summary = [];
|
|
783
823
|
|
|
824
|
+
if (!skipClaudeMdSync) {
|
|
825
|
+
const claudeRoutingResults = syncClaudeRoutingSectionsForCli();
|
|
826
|
+
const claudeRoutingSummary = getClaudeRoutingSyncSummary(claudeRoutingResults);
|
|
827
|
+
if (claudeRoutingSummary.changed > 0) {
|
|
828
|
+
ok(`CLAUDE.md 라우팅: ${claudeRoutingSummary.changed}개 파일 반영`);
|
|
829
|
+
summary.push({ item: "CLAUDE.md 라우팅", status: "✅", detail: `${claudeRoutingSummary.changed}개 파일 반영` });
|
|
830
|
+
} else if (claudeRoutingSummary.skipped > 0) {
|
|
831
|
+
ok("CLAUDE.md 라우팅: 대상 파일 없음 (건너뜀)");
|
|
832
|
+
summary.push({ item: "CLAUDE.md 라우팅", status: "⏭️", detail: "대상 파일 없음" });
|
|
833
|
+
} else {
|
|
834
|
+
ok("CLAUDE.md 라우팅: 최신 상태");
|
|
835
|
+
summary.push({ item: "CLAUDE.md 라우팅", status: "✅", detail: "최신 상태" });
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
784
839
|
const codexProfileResult = ensureCodexProfiles();
|
|
785
840
|
if (!codexProfileResult.ok) {
|
|
786
841
|
warn(`Codex profiles 설정 실패: ${codexProfileResult.message}`);
|
|
@@ -1128,13 +1183,10 @@ function buildMcpStatusRows(statusInfo) {
|
|
|
1128
1183
|
}
|
|
1129
1184
|
|
|
1130
1185
|
function ensureValidRegistryState() {
|
|
1131
|
-
|
|
1186
|
+
let registryState = inspectRegistry();
|
|
1132
1187
|
if (!registryState.exists) {
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
reason: "configError",
|
|
1136
|
-
fix: "config/mcp-registry.json을 복원하거나 `tfx mcp add <name> --url <url>`로 다시 생성하세요.",
|
|
1137
|
-
});
|
|
1188
|
+
saveRegistry(createDefaultRegistry());
|
|
1189
|
+
registryState = inspectRegistry();
|
|
1138
1190
|
}
|
|
1139
1191
|
if (!registryState.valid) {
|
|
1140
1192
|
throw createCliError(`MCP registry invalid: ${registryState.errors.join("; ")}`, {
|
|
@@ -1335,9 +1387,13 @@ async function cmdDoctor(options = {}) {
|
|
|
1335
1387
|
warn(`MCP registry 자동 동기화 실패: ${error.message}`);
|
|
1336
1388
|
}
|
|
1337
1389
|
} else if (registryStateForFix.exists) {
|
|
1338
|
-
|
|
1390
|
+
saveRegistry(createDefaultRegistry());
|
|
1391
|
+
report.actions.push({ type: "mcp-registry-reset", status: "ok" });
|
|
1392
|
+
ok("MCP registry 손상 → 기본값으로 재생성됨");
|
|
1339
1393
|
} else {
|
|
1340
|
-
|
|
1394
|
+
saveRegistry(createDefaultRegistry());
|
|
1395
|
+
report.actions.push({ type: "mcp-registry-create", status: "ok" });
|
|
1396
|
+
ok("MCP registry 없음 → 기본값으로 자동 생성됨");
|
|
1341
1397
|
}
|
|
1342
1398
|
console.log(`\n ${LINE}`);
|
|
1343
1399
|
info("수정 완료 — 아래 진단 결과를 확인하세요");
|
|
@@ -2157,28 +2213,27 @@ async function cmdDoctor(options = {}) {
|
|
|
2157
2213
|
// ── MCP 중앙 레지스트리 ──
|
|
2158
2214
|
section("MCP Registry");
|
|
2159
2215
|
{
|
|
2160
|
-
|
|
2216
|
+
let registryState = inspectRegistry();
|
|
2161
2217
|
if (!registryState.exists) {
|
|
2218
|
+
saveRegistry(createDefaultRegistry());
|
|
2219
|
+
registryState = inspectRegistry();
|
|
2162
2220
|
addDoctorCheck(report, {
|
|
2163
2221
|
name: "mcp-registry",
|
|
2164
|
-
status: "
|
|
2222
|
+
status: "fixed",
|
|
2165
2223
|
path: registryState.path,
|
|
2166
|
-
|
|
2224
|
+
action: "기본값으로 자동 생성됨",
|
|
2167
2225
|
});
|
|
2168
|
-
|
|
2169
|
-
info(`path: ${registryState.path}`);
|
|
2170
|
-
issues++;
|
|
2226
|
+
ok("mcp-registry.json 없음 → 기본값으로 자동 생성됨");
|
|
2171
2227
|
} else if (!registryState.valid) {
|
|
2228
|
+
saveRegistry(createDefaultRegistry());
|
|
2229
|
+
registryState = inspectRegistry();
|
|
2172
2230
|
addDoctorCheck(report, {
|
|
2173
2231
|
name: "mcp-registry",
|
|
2174
|
-
status: "
|
|
2232
|
+
status: "fixed",
|
|
2175
2233
|
path: registryState.path,
|
|
2176
|
-
|
|
2177
|
-
fix: "config/mcp-registry.json 구조를 수정하세요.",
|
|
2234
|
+
action: "손상 감지 → 기본값으로 재생성됨",
|
|
2178
2235
|
});
|
|
2179
|
-
|
|
2180
|
-
for (const entry of registryState.errors) info(entry);
|
|
2181
|
-
issues++;
|
|
2236
|
+
warn("mcp-registry.json 손상 → 기본값으로 재생성됨");
|
|
2182
2237
|
} else {
|
|
2183
2238
|
const statusInfo = inspectRegistryStatus(registryState.registry);
|
|
2184
2239
|
const invalidConfigs = statusInfo.configs.filter((config) => config.parseError);
|
|
@@ -2434,7 +2489,56 @@ async function cmdDoctor(options = {}) {
|
|
|
2434
2489
|
});
|
|
2435
2490
|
}
|
|
2436
2491
|
|
|
2437
|
-
function
|
|
2492
|
+
function normalizeRemoteReachabilityUrl(remoteUrl) {
|
|
2493
|
+
if (!remoteUrl) return null;
|
|
2494
|
+
if (/^https?:\/\//iu.test(remoteUrl)) {
|
|
2495
|
+
try {
|
|
2496
|
+
return new URL(remoteUrl).origin;
|
|
2497
|
+
} catch {
|
|
2498
|
+
return null;
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
const scpMatch = /^git@([^:]+):/iu.exec(remoteUrl);
|
|
2502
|
+
if (scpMatch) return `https://${scpMatch[1]}`;
|
|
2503
|
+
if (/^ssh:\/\//iu.test(remoteUrl)) {
|
|
2504
|
+
try {
|
|
2505
|
+
return `https://${new URL(remoteUrl).hostname}`;
|
|
2506
|
+
} catch {
|
|
2507
|
+
return null;
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
return null;
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
function resolveGitUpdateUrl(repoDir) {
|
|
2514
|
+
try {
|
|
2515
|
+
const remoteUrl = execSync("git remote get-url origin", {
|
|
2516
|
+
encoding: "utf8",
|
|
2517
|
+
timeout: 10_000,
|
|
2518
|
+
cwd: repoDir,
|
|
2519
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
2520
|
+
windowsHide: true,
|
|
2521
|
+
}).trim();
|
|
2522
|
+
return normalizeRemoteReachabilityUrl(remoteUrl);
|
|
2523
|
+
} catch {
|
|
2524
|
+
return null;
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2528
|
+
function resolveUpdateTargets({ installMode, pluginPath }) {
|
|
2529
|
+
const repoDir = installMode === "plugin" ? (pluginPath || PKG_ROOT) : PKG_ROOT;
|
|
2530
|
+
const gitUrl = resolveGitUpdateUrl(repoDir);
|
|
2531
|
+
|
|
2532
|
+
if (installMode === "npm-global" || installMode === "npm-local") {
|
|
2533
|
+
return ["https://registry.npmjs.org/triflux"];
|
|
2534
|
+
}
|
|
2535
|
+
if (installMode === "plugin" || installMode === "git-local") {
|
|
2536
|
+
return gitUrl ? [gitUrl] : ["https://github.com"];
|
|
2537
|
+
}
|
|
2538
|
+
return [];
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
async function cmdUpdate() {
|
|
2438
2542
|
const isDev = isDevUpdateRequested(NORMALIZED_ARGS);
|
|
2439
2543
|
const tagLabel = isDev ? ` ${YELLOW}--dev${RESET}` : "";
|
|
2440
2544
|
console.log(`\n${BOLD}triflux update${RESET}${tagLabel}\n`);
|
|
@@ -2490,6 +2594,27 @@ function cmdUpdate() {
|
|
|
2490
2594
|
|
|
2491
2595
|
info(`검색: ${installMode === "plugin" ? "플러그인" : installMode === "npm-global" ? "npm global" : installMode === "npm-local" ? "npm local" : installMode === "git-local" ? "git 로컬 저장소" : "알 수 없음"} 설치 감지`);
|
|
2492
2596
|
|
|
2597
|
+
const networkTargets = resolveUpdateTargets({ installMode, pluginPath });
|
|
2598
|
+
if (networkTargets.length > 0) {
|
|
2599
|
+
const networkStatus = await checkNetworkAvailability(networkTargets);
|
|
2600
|
+
if (!networkStatus.online) {
|
|
2601
|
+
fail(`네트워크 확인 실패: ${networkStatus.unreachable.join(", ")}`);
|
|
2602
|
+
info("네트워크 연결을 확인한 뒤 다시 시도하세요.");
|
|
2603
|
+
return;
|
|
2604
|
+
}
|
|
2605
|
+
ok(`네트워크 확인 완료 (${networkStatus.reachable.join(", ")})`);
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2608
|
+
const cacheValidation = validateRuntimeCachePaths(join(CLAUDE_DIR, "cache"));
|
|
2609
|
+
if (!cacheValidation.ok) {
|
|
2610
|
+
warn(`런타임 캐시 검증 이슈 ${cacheValidation.issues.length}건 발견`);
|
|
2611
|
+
for (const issue of cacheValidation.issues) {
|
|
2612
|
+
info(`${issue.file}: ${issue.error}`);
|
|
2613
|
+
}
|
|
2614
|
+
} else {
|
|
2615
|
+
ok("런타임 캐시 검증 완료");
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2493
2618
|
// 2. 설치 방식에 따라 업데이트
|
|
2494
2619
|
const oldVer = PKG.version;
|
|
2495
2620
|
let updated = false;
|
|
@@ -2654,9 +2779,23 @@ function cmdUpdate() {
|
|
|
2654
2779
|
}
|
|
2655
2780
|
}
|
|
2656
2781
|
|
|
2782
|
+
// ── Post-update: CLAUDE.md 라우팅 동기화 ──
|
|
2783
|
+
console.log(`\n${CYAN}── CLAUDE.md 라우팅 동기화 ──${RESET}`);
|
|
2784
|
+
{
|
|
2785
|
+
const claudeRoutingResults = syncClaudeRoutingSectionsForCli();
|
|
2786
|
+
const claudeRoutingSummary = getClaudeRoutingSyncSummary(claudeRoutingResults);
|
|
2787
|
+
if (claudeRoutingSummary.changed > 0) {
|
|
2788
|
+
ok(`CLAUDE.md 라우팅 ${claudeRoutingSummary.changed}개 파일 반영`);
|
|
2789
|
+
} else if (claudeRoutingSummary.skipped > 0) {
|
|
2790
|
+
ok("CLAUDE.md 라우팅 대상 파일 없음 (건너뜀)");
|
|
2791
|
+
} else {
|
|
2792
|
+
ok("CLAUDE.md 라우팅 최신 상태");
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2657
2796
|
// ── Post-update: 설정 동기화 ──
|
|
2658
2797
|
console.log(`\n${CYAN}── 설정 동기화 ──${RESET}`);
|
|
2659
|
-
cmdSetup({ fromUpdate: true, overrideVersion: newVer });
|
|
2798
|
+
cmdSetup({ fromUpdate: true, overrideVersion: newVer, skipClaudeMdSync: true });
|
|
2660
2799
|
|
|
2661
2800
|
// ── Post-update: 훅 오케스트레이터 적용 ──
|
|
2662
2801
|
{
|
|
@@ -2783,6 +2922,128 @@ function cmdVersion(options = {}) {
|
|
|
2783
2922
|
console.log("");
|
|
2784
2923
|
}
|
|
2785
2924
|
|
|
2925
|
+
function cmdHandoff(args = [], options = {}) {
|
|
2926
|
+
const { json = false } = options;
|
|
2927
|
+
const parsed = {
|
|
2928
|
+
target: "remote",
|
|
2929
|
+
decisions: [],
|
|
2930
|
+
decisionFile: null,
|
|
2931
|
+
output: null,
|
|
2932
|
+
cwd: process.cwd(),
|
|
2933
|
+
};
|
|
2934
|
+
|
|
2935
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
2936
|
+
const arg = args[index];
|
|
2937
|
+
const next = args[index + 1];
|
|
2938
|
+
|
|
2939
|
+
if (arg === "--target") {
|
|
2940
|
+
if (!next || next.startsWith("-")) {
|
|
2941
|
+
throw createCliError("--target 값이 필요합니다 (local|remote)", {
|
|
2942
|
+
exitCode: EXIT_ARG_ERROR,
|
|
2943
|
+
reason: "argError",
|
|
2944
|
+
fix: "tfx handoff --target remote",
|
|
2945
|
+
});
|
|
2946
|
+
}
|
|
2947
|
+
if (!["local", "remote"].includes(next)) {
|
|
2948
|
+
throw createCliError(`지원하지 않는 --target 값: ${next}`, {
|
|
2949
|
+
exitCode: EXIT_ARG_ERROR,
|
|
2950
|
+
reason: "argError",
|
|
2951
|
+
fix: "tfx handoff --target local|remote",
|
|
2952
|
+
});
|
|
2953
|
+
}
|
|
2954
|
+
parsed.target = next;
|
|
2955
|
+
index += 1;
|
|
2956
|
+
continue;
|
|
2957
|
+
}
|
|
2958
|
+
|
|
2959
|
+
if (arg === "--decision") {
|
|
2960
|
+
if (!next || next.startsWith("-")) {
|
|
2961
|
+
throw createCliError("--decision 값이 필요합니다", {
|
|
2962
|
+
exitCode: EXIT_ARG_ERROR,
|
|
2963
|
+
reason: "argError",
|
|
2964
|
+
fix: "tfx handoff --decision \"결정사항\"",
|
|
2965
|
+
});
|
|
2966
|
+
}
|
|
2967
|
+
parsed.decisions.push(next);
|
|
2968
|
+
index += 1;
|
|
2969
|
+
continue;
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2972
|
+
if (arg === "--decision-file") {
|
|
2973
|
+
if (!next || next.startsWith("-")) {
|
|
2974
|
+
throw createCliError("--decision-file 경로가 필요합니다", {
|
|
2975
|
+
exitCode: EXIT_ARG_ERROR,
|
|
2976
|
+
reason: "argError",
|
|
2977
|
+
fix: "tfx handoff --decision-file .omx/notepad.md",
|
|
2978
|
+
});
|
|
2979
|
+
}
|
|
2980
|
+
parsed.decisionFile = resolve(next);
|
|
2981
|
+
index += 1;
|
|
2982
|
+
continue;
|
|
2983
|
+
}
|
|
2984
|
+
|
|
2985
|
+
if (arg === "--output" || arg === "--out") {
|
|
2986
|
+
if (!next || next.startsWith("-")) {
|
|
2987
|
+
throw createCliError(`${arg} 경로가 필요합니다`, {
|
|
2988
|
+
exitCode: EXIT_ARG_ERROR,
|
|
2989
|
+
reason: "argError",
|
|
2990
|
+
fix: "tfx handoff --output .omx/handoff.md",
|
|
2991
|
+
});
|
|
2992
|
+
}
|
|
2993
|
+
parsed.output = resolve(next);
|
|
2994
|
+
index += 1;
|
|
2995
|
+
continue;
|
|
2996
|
+
}
|
|
2997
|
+
|
|
2998
|
+
if (arg === "--cwd") {
|
|
2999
|
+
if (!next || next.startsWith("-")) {
|
|
3000
|
+
throw createCliError("--cwd 경로가 필요합니다", {
|
|
3001
|
+
exitCode: EXIT_ARG_ERROR,
|
|
3002
|
+
reason: "argError",
|
|
3003
|
+
fix: "tfx handoff --cwd <project-path>",
|
|
3004
|
+
});
|
|
3005
|
+
}
|
|
3006
|
+
parsed.cwd = resolve(next);
|
|
3007
|
+
index += 1;
|
|
3008
|
+
continue;
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
throw createCliError(`알 수 없는 handoff 옵션: ${arg}`, {
|
|
3012
|
+
exitCode: EXIT_ARG_ERROR,
|
|
3013
|
+
reason: "argError",
|
|
3014
|
+
fix: "tfx handoff --target remote --output .omx/handoff.md",
|
|
3015
|
+
});
|
|
3016
|
+
}
|
|
3017
|
+
|
|
3018
|
+
const result = serializeHandoff({
|
|
3019
|
+
target: parsed.target,
|
|
3020
|
+
decisions: parsed.decisions,
|
|
3021
|
+
decisionFile: parsed.decisionFile,
|
|
3022
|
+
cwd: parsed.cwd,
|
|
3023
|
+
});
|
|
3024
|
+
|
|
3025
|
+
if (parsed.output) {
|
|
3026
|
+
const outputDir = dirname(parsed.output);
|
|
3027
|
+
if (!existsSync(outputDir)) mkdirSync(outputDir, { recursive: true });
|
|
3028
|
+
writeFileSync(parsed.output, `${result.prompt}\n`, "utf8");
|
|
3029
|
+
}
|
|
3030
|
+
|
|
3031
|
+
if (json) {
|
|
3032
|
+
printJson({
|
|
3033
|
+
handoff: {
|
|
3034
|
+
...result,
|
|
3035
|
+
...(parsed.output ? { output: parsed.output } : {}),
|
|
3036
|
+
},
|
|
3037
|
+
});
|
|
3038
|
+
return;
|
|
3039
|
+
}
|
|
3040
|
+
|
|
3041
|
+
console.log(result.prompt);
|
|
3042
|
+
if (parsed.output) {
|
|
3043
|
+
console.log(`\n${DIM}saved:${RESET} ${parsed.output}`);
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
|
|
2786
3047
|
function cmdSchema(args = []) {
|
|
2787
3048
|
const bundle = loadDelegatorSchemaBundle();
|
|
2788
3049
|
const selector = String(args[0] || "").trim();
|
|
@@ -3054,6 +3315,7 @@ ${updateNotice}
|
|
|
3054
3315
|
${WHITE_BRIGHT}tfx update${RESET} ${GRAY}최신 안정 버전으로 업데이트${RESET}
|
|
3055
3316
|
${DIM} --dev / dev${RESET} ${GRAY}dev 태그로 업데이트${RESET}
|
|
3056
3317
|
${WHITE_BRIGHT}tfx list${RESET} ${GRAY}설치된 스킬 목록${RESET}
|
|
3318
|
+
${WHITE_BRIGHT}tfx handoff${RESET} ${GRAY}현재 컨텍스트를 원격/로컬 핸드오프 프롬프트로 생성${RESET}
|
|
3057
3319
|
${WHITE_BRIGHT}tfx schema${RESET} ${GRAY}CLI/Hub schema JSON 출력${RESET}
|
|
3058
3320
|
${WHITE_BRIGHT}tfx hub${RESET} ${GRAY}MCP 메시지 버스 관리 (start/stop/status)${RESET}
|
|
3059
3321
|
${WHITE_BRIGHT}tfx tray${RESET} ${GRAY}Windows 시스템 트레이 실행${RESET}
|
|
@@ -3553,12 +3815,15 @@ async function main() {
|
|
|
3553
3815
|
cmdSchema(cmdArgs);
|
|
3554
3816
|
return;
|
|
3555
3817
|
case "update":
|
|
3556
|
-
cmdUpdate();
|
|
3818
|
+
await cmdUpdate();
|
|
3557
3819
|
return;
|
|
3558
3820
|
case "list":
|
|
3559
3821
|
case "ls":
|
|
3560
3822
|
cmdList({ json: JSON_OUTPUT });
|
|
3561
3823
|
return;
|
|
3824
|
+
case "handoff":
|
|
3825
|
+
cmdHandoff(cmdArgs, { json: JSON_OUTPUT });
|
|
3826
|
+
return;
|
|
3562
3827
|
case "hub":
|
|
3563
3828
|
await cmdHub(cmdArgs, { json: JSON_OUTPUT && (cmdArgs[0] || "status") === "status" });
|
|
3564
3829
|
return;
|
package/package.json
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
> **ARGUMENTS 처리**: 이 스킬이 `ARGUMENTS: <값>`과 함께 호출되면, 해당 값을 사용자 입력으로 취급하여
|
|
2
|
+
> 워크플로우의 첫 단계 입력으로 사용한다. ARGUMENTS가 비어있거나 없으면 기존 절차대로 사용자에게 입력을 요청한다.
|
|
3
|
+
|
|
4
|
+
> **Telemetry**
|
|
5
|
+
>
|
|
6
|
+
> - Skill: `{{SKILL_NAME}}`
|
|
7
|
+
> - Description: `{{SKILL_DESCRIPTION}}`
|
|
8
|
+
> - Session: 요청별 식별자를 유지해 단계별 실행 로그를 추적한다.
|
|
9
|
+
> - Errors: 실패 시 원인/복구/재시도 여부를 구조화해 기록한다.
|
|
@@ -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
|
+
> headless-guard가 이 규칙 위반을 **자동 차단**한다. 우회 불가.
|
|
2
|
+
|
|
3
|
+
1. **`codex exec` / `gemini -p` 직접 호출 절대 금지**
|
|
4
|
+
2. Codex·Gemini 작업은 직접 CLI 대신 반드시 TFX 래퍼(이 계열 스킬에서는 `Bash("tfx multi --teammate-mode headless --auto-attach --dashboard --assign 'cli:프롬프트:역할' --timeout 600")`)로만 실행
|
|
5
|
+
3. Claude 작업은 `Agent(run_in_background=true)`
|
|
6
|
+
4. 교차 검토/병렬 단계에서는 Bash + Agent를 같은 메시지에서 동시 호출
|