triflux 10.44.0 → 10.46.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/README.md +1 -1
- package/adapters/codex/skills/tfx-harness/SKILL.md +45 -9
- package/bin/tfx-live.mjs +1298 -100
- package/bin/triflux.mjs +9 -6
- package/config/routing-policy.json +1 -1
- package/hooks/codex-session-hook.mjs +67 -1
- package/hooks/cto-north-star-brief.mjs +3 -16
- package/hooks/error-context.mjs +3 -6
- package/hooks/hook-manager.mjs +1 -1
- package/hooks/hook-registry.json +0 -11
- package/hooks/keyword-rules.json +8 -4
- package/hooks/permission-safe-allow.mjs +36 -2
- package/hooks/session-start-fast.mjs +50 -1
- package/hooks/session-start-lake.mjs +3 -16
- package/hub/bridge.mjs +111 -5
- package/hub/dynamic-routing-engine.mjs +1 -1
- package/hub/gemini-adapter.mjs +29 -11
- package/hub/lib/memory-store.mjs +66 -30
- package/hub/lib/tfx-route-args.mjs +41 -4
- package/hub/pipe.mjs +10 -15
- package/hub/reflexion.mjs +0 -57
- package/hub/router.mjs +202 -24
- package/hub/schema.sql +1 -0
- package/hub/server.mjs +172 -93
- package/hub/store.mjs +106 -33
- package/hub/team/backend.mjs +4 -2
- package/hub/team/claude-daemon-control.mjs +2 -0
- package/hub/team/cli/commands/start/start-headless.mjs +2 -0
- package/hub/team/cli/commands/start/start-mux.mjs +24 -3
- package/hub/team/cli/services/runtime-mode.mjs +23 -9
- package/hub/team/codex-review.mjs +2 -3
- package/hub/team/conductor.mjs +80 -24
- package/hub/team/cto-auto-collect.mjs +2 -6
- package/hub/team/cto-pull.mjs +4 -0
- package/hub/team/dashboard-open.mjs +1 -0
- package/hub/team/execution-mode.mjs +38 -2
- package/hub/team/headless.mjs +493 -82
- package/hub/team/interactive-tui-transport.mjs +15 -1
- package/hub/team/launcher-template.mjs +19 -2
- package/hub/team/orchestrator.mjs +36 -9
- package/hub/team/pane.mjs +176 -14
- package/hub/team/psmux.mjs +79 -70
- package/hub/team/retry-state-machine.mjs +47 -16
- package/hub/team/session.mjs +57 -0
- package/hub/team/terminal-opener.mjs +68 -13
- package/hub/team/tui-viewer.mjs +65 -14
- package/hub/team/uds-orchestrator.mjs +384 -83
- package/hub/tools.mjs +6 -0
- package/hub/workers/codex-mcp.mjs +35 -6
- package/hud/cli-policy.mjs +69 -0
- package/hud/constants.mjs +20 -17
- package/hud/context-monitor.mjs +31 -21
- package/hud/hud-qos-status.mjs +81 -91
- package/hud/providers/claude.mjs +150 -84
- package/hud/providers/codex.mjs +10 -1
- package/hud/renderers.mjs +21 -59
- package/hud/utils.mjs +1 -15
- package/package.json +1 -1
- package/scripts/__tests__/keyword-detector.test.mjs +89 -1
- package/scripts/__tests__/release-governance.test.mjs +329 -12
- package/scripts/__tests__/release-publish-ancestor.test.mjs +67 -0
- package/scripts/__tests__/release-verify-wait.test.mjs +169 -0
- package/scripts/__tests__/resolve-auto-release.test.mjs +216 -0
- package/scripts/__tests__/skill-surface.test.mjs +6 -4
- package/scripts/__tests__/tfx-route-bash-node-parity.test.mjs +7 -5
- package/scripts/__tests__/tfx-route-node-entry.test.mjs +27 -21
- package/scripts/claude-login-detect.mjs +107 -25
- package/scripts/ensure-agy-hooks.mjs +31 -1
- package/scripts/keyword-detector.mjs +11 -1
- package/scripts/lib/agent-route-policy.mjs +47 -39
- package/scripts/lib/cli-agy.mjs +24 -4
- package/scripts/lib/codex-profile-config.mjs +45 -13
- package/scripts/lib/codex-recovery.sh +19 -0
- package/scripts/lib/gemini-profiles.mjs +113 -29
- package/scripts/lib/keyword-rules.mjs +12 -0
- package/scripts/lib/logger.mjs +60 -43
- package/scripts/lib/machine-profile.mjs +293 -0
- package/scripts/lib/mcp-health.mjs +44 -18
- package/scripts/notion-read.mjs +2 -1
- package/scripts/release/check-packages-mirror.mjs +53 -1
- package/scripts/release/lib.mjs +1 -2
- package/scripts/release/prepare.mjs +24 -9
- package/scripts/release/publish.mjs +16 -1
- package/scripts/release/resolve-auto-release.mjs +136 -0
- package/scripts/release/verify.mjs +74 -25
- package/scripts/remote-spawn.mjs +830 -32
- package/scripts/session-stale-cleanup.mjs +0 -1
- package/scripts/setup.mjs +487 -150
- package/scripts/sync-hub-mcp-settings.mjs +11 -3
- package/scripts/test-lock.mjs +15 -1
- package/scripts/tfx-gate-activate.mjs +4 -4
- package/scripts/tfx-route-post.mjs +90 -13
- package/scripts/tfx-route.sh +520 -141
- package/skills/merge-worktree/SKILL.md +3 -3
- package/skills/star-prompt/skill.json +1 -1
- package/skills/tfx-analysis/SKILL.md +8 -9
- package/skills/tfx-analysis/skill.json +1 -10
- package/skills/tfx-auto/SKILL.md +208 -50
- package/skills/tfx-auto/skill.json +2 -23
- package/skills/tfx-doctor/skill.json +0 -3
- package/skills/tfx-find/skill.json +0 -7
- package/skills/tfx-forge/SKILL.md +2 -2
- package/skills/tfx-forge/skill.json +0 -7
- package/skills/tfx-goal-clarify/skill.json +5 -0
- package/skills/tfx-harness/SKILL.md +26 -3
- package/skills/tfx-harness/skill.json +5 -0
- package/skills/tfx-hooks/SKILL.md +0 -2
- package/skills/tfx-hooks/skill.json +0 -3
- package/skills/tfx-hub/SKILL.md +1 -6
- package/skills/tfx-hub/skill.json +0 -3
- package/skills/tfx-index/skill.json +0 -6
- package/skills/tfx-interview/SKILL.md +0 -2
- package/skills/tfx-live/SKILL.md +131 -5
- package/skills/tfx-live/skill.json +0 -5
- package/skills/tfx-plan/SKILL.md +7 -8
- package/skills/tfx-plan/skill.json +1 -12
- package/skills/tfx-profile/SKILL.md +15 -13
- package/skills/tfx-profile/skill.json +0 -3
- package/skills/tfx-prune/SKILL.md +4 -6
- package/skills/tfx-qa/SKILL.md +7 -7
- package/skills/tfx-qa/skill.json +1 -11
- package/skills/tfx-ralph/SKILL.md +8 -11
- package/skills/tfx-remote/SKILL.md +2 -2
- package/skills/tfx-remote/skill.json +0 -3
- package/skills/tfx-research/SKILL.md +2 -2
- package/skills/tfx-research/skill.json +1 -16
- package/skills/tfx-review/SKILL.md +11 -11
- package/skills/tfx-review/skill.json +1 -11
- package/skills/tfx-setup/SKILL.md +5 -6
- package/skills/tfx-setup/skill.json +0 -3
- package/skills/tfx-ship/SKILL.md +43 -36
- package/skills/tfx-ship/skill.json +1 -9
- package/skills/tfx-wt/skill.json +0 -7
- package/tui/codex-profile.mjs +3 -3
- package/tui/gemini-profile.mjs +11 -24
- package/tui/monitor.mjs +1 -0
- package/tui/setup.mjs +7 -6
- package/scripts/headless-guard-fast.sh +0 -29
- package/scripts/headless-guard.mjs +0 -523
- package/skills/tfx-autopilot/SKILL.md +0 -51
- package/skills/tfx-autopilot/skill.json +0 -12
- package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +0 -75
- 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 -39
- 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 -39
- 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 -29
- 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 -29
- 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 -24
- 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 -20
- 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-consensus/SKILL.md +0 -56
- package/skills/tfx-consensus/skill.json +0 -8
- package/skills/tfx-debate/SKILL.md +0 -60
- package/skills/tfx-debate/skill.json +0 -14
- package/skills/tfx-fullcycle/SKILL.md +0 -47
- package/skills/tfx-fullcycle/skill.json +0 -13
- package/skills/tfx-multi/SKILL.md +0 -45
- package/skills/tfx-multi/references/agent-wrapper-rules.md +0 -94
- package/skills/tfx-multi/references/thorough-pipeline.md +0 -105
- package/skills/tfx-multi/skill.json +0 -10
- package/skills/tfx-panel/SKILL.md +0 -60
- package/skills/tfx-panel/skill.json +0 -14
- package/skills/tfx-persist/SKILL.md +0 -63
- package/skills/tfx-persist/skill.json +0 -14
- package/skills/tfx-psmux-rules/SKILL.md +0 -41
- package/skills/tfx-psmux-rules/skill.json +0 -10
- package/skills/tfx-remote-setup/SKILL.md +0 -42
- package/skills/tfx-remote-setup/skill.json +0 -10
- package/skills/tfx-remote-spawn/SKILL.md +0 -52
- package/skills/tfx-remote-spawn/skill.json +0 -10
- package/skills/tfx-swarm/SKILL.md +0 -49
- package/skills/tfx-swarm/skill.json +0 -13
package/bin/triflux.mjs
CHANGED
|
@@ -148,7 +148,7 @@ const NORMALIZED_ARGS = RAW_ARGS.filter((arg) => arg !== "--json");
|
|
|
148
148
|
const CLI_COMMAND_SCHEMAS = Object.freeze({
|
|
149
149
|
auto: {
|
|
150
150
|
usage:
|
|
151
|
-
"tfx auto [--cli auto|codex|antigravity|claude] [--mode quick|deep|consensus] [--parallel 1|N|swarm] [--json]",
|
|
151
|
+
"tfx auto [--cli auto|codex|antigravity|claude] [--mode quick|deep|consensus|live] [--rounds N] [--parallel 1|N|swarm] [--json]",
|
|
152
152
|
description:
|
|
153
153
|
"tfx-auto 라우팅 결정을 CLI에서 미리보기/직렬화 (실행 skill front door와 같은 flag surface)",
|
|
154
154
|
options: [
|
|
@@ -160,7 +160,12 @@ const CLI_COMMAND_SCHEMAS = Object.freeze({
|
|
|
160
160
|
{
|
|
161
161
|
name: "--mode <name>",
|
|
162
162
|
type: "string",
|
|
163
|
-
description: "라우팅 모드: quick|deep|consensus",
|
|
163
|
+
description: "라우팅 모드: quick|deep|consensus|live",
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: "--rounds <N>",
|
|
167
|
+
type: "number",
|
|
168
|
+
description: "live peer 왕복 횟수 (기본 4, --mode live 전용)",
|
|
164
169
|
},
|
|
165
170
|
{
|
|
166
171
|
name: "--parallel <1|N|swarm>",
|
|
@@ -2212,9 +2217,7 @@ function cmdSetup(options = {}) {
|
|
|
2212
2217
|
if (codexHarnessSync.action === "synced") {
|
|
2213
2218
|
ok("Codex tfx-harness adapter: 동기화됨");
|
|
2214
2219
|
} else if (codexHarnessSync.action === "skipped") {
|
|
2215
|
-
warn(
|
|
2216
|
-
`Codex tfx-harness adapter: 사용자 스킬 보존 (backup: ${codexHarnessSync.backupDir})`,
|
|
2217
|
-
);
|
|
2220
|
+
warn("Codex tfx-harness adapter: 사용자 스킬 보존 (관리 파일 변경 없음)");
|
|
2218
2221
|
}
|
|
2219
2222
|
|
|
2220
2223
|
// ── psmux 기본 셸 자동 수정 (cmd.exe → PowerShell) ──
|
|
@@ -7522,7 +7525,7 @@ async function main() {
|
|
|
7522
7525
|
return;
|
|
7523
7526
|
}
|
|
7524
7527
|
const { createMonitor } = await import("../tui/monitor.mjs");
|
|
7525
|
-
const mon = createMonitor();
|
|
7528
|
+
const mon = createMonitor({ targetPane: process.env.TMUX_PANE });
|
|
7526
7529
|
await mon.start();
|
|
7527
7530
|
break;
|
|
7528
7531
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
3
4
|
import { argv, exit, stdin, stdout } from "node:process";
|
|
4
|
-
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
6
|
import { drainPendingSynapse as defaultDrainPendingSynapse } from "../hub/team/synapse-http.mjs";
|
|
6
7
|
import {
|
|
7
8
|
heartbeatInteractiveSession as defaultHeartbeatInteractiveSession,
|
|
@@ -51,6 +52,66 @@ function normalizeMode(mode, payload) {
|
|
|
51
52
|
return "";
|
|
52
53
|
}
|
|
53
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Start a detached bridge call so Codex/OMX presence reaches both the Synapse
|
|
57
|
+
* session registry and the hub agents table. This is intentionally independent
|
|
58
|
+
* of the legacy session-start work below: a missing or unavailable hub must
|
|
59
|
+
* never delay or fail SessionStart.
|
|
60
|
+
*/
|
|
61
|
+
export function launchCodexPresenceRegistration(payload, opts = {}) {
|
|
62
|
+
try {
|
|
63
|
+
const sessionId = String(payload?.session_id || "").trim();
|
|
64
|
+
if (!sessionId) return false;
|
|
65
|
+
|
|
66
|
+
const cwd =
|
|
67
|
+
typeof payload?.cwd === "string" && payload.cwd.trim()
|
|
68
|
+
? payload.cwd
|
|
69
|
+
: process.cwd();
|
|
70
|
+
const bridgePath =
|
|
71
|
+
opts.bridgePath ||
|
|
72
|
+
fileURLToPath(new URL("../hub/bridge.mjs", import.meta.url));
|
|
73
|
+
const spawnFn = opts.spawnFn || spawn;
|
|
74
|
+
const canonicalSessionId = String(process.env.OMX_SESSION_ID || "").trim();
|
|
75
|
+
const tmuxSession = String(
|
|
76
|
+
process.env.OMX_TMUX_SESSION_NAME || process.env.OMX_TMUX_SESSION || "",
|
|
77
|
+
).trim();
|
|
78
|
+
const tmuxPane = String(process.env.TMUX_PANE || "").trim();
|
|
79
|
+
const args = [
|
|
80
|
+
bridgePath,
|
|
81
|
+
"register",
|
|
82
|
+
"--session-id",
|
|
83
|
+
sessionId,
|
|
84
|
+
"--cwd",
|
|
85
|
+
cwd,
|
|
86
|
+
"--worktree-path",
|
|
87
|
+
cwd,
|
|
88
|
+
"--session-kind",
|
|
89
|
+
"interactive",
|
|
90
|
+
"--host",
|
|
91
|
+
"local",
|
|
92
|
+
"--codex-session-id",
|
|
93
|
+
sessionId,
|
|
94
|
+
];
|
|
95
|
+
if (canonicalSessionId && canonicalSessionId !== sessionId) {
|
|
96
|
+
args.push("--omx-session-id", canonicalSessionId);
|
|
97
|
+
}
|
|
98
|
+
if (tmuxSession) args.push("--tmux-session", tmuxSession);
|
|
99
|
+
if (tmuxPane) args.push("--tmux-pane", tmuxPane);
|
|
100
|
+
|
|
101
|
+
const child = spawnFn(process.execPath, args, {
|
|
102
|
+
detached: true,
|
|
103
|
+
stdio: "ignore",
|
|
104
|
+
windowsHide: true,
|
|
105
|
+
env: process.env,
|
|
106
|
+
});
|
|
107
|
+
child?.once?.("error", () => {});
|
|
108
|
+
child?.unref?.();
|
|
109
|
+
return true;
|
|
110
|
+
} catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
54
115
|
function swallowStdoutWrite(_chunk, encodingOrCallback, callback) {
|
|
55
116
|
const done =
|
|
56
117
|
typeof encodingOrCallback === "function" ? encodingOrCallback : callback;
|
|
@@ -91,10 +152,15 @@ export async function runCodexSessionHook(stdinData, opts = {}) {
|
|
|
91
152
|
opts.heartbeatInteractiveSession || defaultHeartbeatInteractiveSession;
|
|
92
153
|
const drainPendingSynapse =
|
|
93
154
|
opts.drainPendingSynapse || defaultDrainPendingSynapse;
|
|
155
|
+
const launchPresenceRegistration =
|
|
156
|
+
opts.launchPresenceRegistration || launchCodexPresenceRegistration;
|
|
94
157
|
|
|
95
158
|
try {
|
|
96
159
|
await runHookSideEffectsWithStdoutSuppressed(async () => {
|
|
97
160
|
if (mode === "register") {
|
|
161
|
+
try {
|
|
162
|
+
launchPresenceRegistration(parsed.payload);
|
|
163
|
+
} catch {}
|
|
98
164
|
try {
|
|
99
165
|
await hubEnsureRun(stdinData);
|
|
100
166
|
} catch {}
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
} from "node:fs";
|
|
12
12
|
import { dirname, join, resolve } from "node:path";
|
|
13
13
|
|
|
14
|
+
import { resolveRoleControlSnapshot } from "../hub/lib/cto-env.mjs";
|
|
15
|
+
|
|
14
16
|
const MAX_CONTEXT_BYTES = 2048;
|
|
15
17
|
const HEADER = "[CTO NORTH STAR - READ ONLY]";
|
|
16
18
|
const INSTRUCTION =
|
|
@@ -45,21 +47,6 @@ function sha256(input) {
|
|
|
45
47
|
return createHash("sha256").update(input).digest("hex");
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
function ctoNorthStarEnabled() {
|
|
49
|
-
switch (process.env.TFX_CTO_NORTH_STAR || "1") {
|
|
50
|
-
case "0":
|
|
51
|
-
case "false":
|
|
52
|
-
case "FALSE":
|
|
53
|
-
case "off":
|
|
54
|
-
case "OFF":
|
|
55
|
-
case "no":
|
|
56
|
-
case "NO":
|
|
57
|
-
return false;
|
|
58
|
-
default:
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
50
|
function wrapBrief(brief) {
|
|
64
51
|
return [HEADER, INSTRUCTION, BEGIN, brief, END].join("\n");
|
|
65
52
|
}
|
|
@@ -89,7 +76,7 @@ function writeMarker(markerPath, state) {
|
|
|
89
76
|
}
|
|
90
77
|
|
|
91
78
|
function main() {
|
|
92
|
-
if (!
|
|
79
|
+
if (!resolveRoleControlSnapshot().north_star_enabled) return;
|
|
93
80
|
|
|
94
81
|
const input = readStdinJson();
|
|
95
82
|
if (input.hook_event_name && input.hook_event_name !== "UserPromptSubmit") {
|
package/hooks/error-context.mjs
CHANGED
|
@@ -129,9 +129,8 @@ function main() {
|
|
|
129
129
|
JSON.stringify(input.tool_result || ""),
|
|
130
130
|
].join("\n");
|
|
131
131
|
|
|
132
|
-
// ── reflexion 적응형 학습: safety-guard
|
|
133
|
-
const isSafetyBlock =
|
|
134
|
-
/\[(?:safety-guard|headless-guard)\].*(?:BLOCKED|차단)/i.test(errorText);
|
|
132
|
+
// ── reflexion 적응형 학습: safety-guard 차단을 패널티로 기록 ──
|
|
133
|
+
const isSafetyBlock = /\[safety-guard\].*(?:BLOCKED|차단)/i.test(errorText);
|
|
135
134
|
if (isSafetyBlock) {
|
|
136
135
|
try {
|
|
137
136
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
@@ -147,9 +146,7 @@ function main() {
|
|
|
147
146
|
errorText.match(/\[.*?\]\s*(.{0,120})/)?.[1] ||
|
|
148
147
|
errorText.slice(0, 120),
|
|
149
148
|
command_preview: command.slice(0, 200),
|
|
150
|
-
source:
|
|
151
|
-
? "safety-guard"
|
|
152
|
-
: "headless-guard",
|
|
149
|
+
source: "safety-guard",
|
|
153
150
|
};
|
|
154
151
|
writeFileSync(penaltyFile, JSON.stringify(entry) + "\n", { flag: "a" });
|
|
155
152
|
} catch {
|
package/hooks/hook-manager.mjs
CHANGED
|
@@ -113,7 +113,7 @@ function identifySource(cmd) {
|
|
|
113
113
|
if (/oh-my-claudecode|omc/i.test(cmd)) return "omc";
|
|
114
114
|
if (/session-vault/i.test(cmd)) return "session-vault";
|
|
115
115
|
if (/compact-helper/i.test(cmd)) return "compact-helper";
|
|
116
|
-
if (/
|
|
116
|
+
if (/tfx-gate/i.test(cmd)) return "omc";
|
|
117
117
|
if (/mcp-cleanup/i.test(cmd)) return "system";
|
|
118
118
|
return "unknown";
|
|
119
119
|
}
|
package/hooks/hook-registry.json
CHANGED
|
@@ -68,17 +68,6 @@
|
|
|
68
68
|
"blocking": true,
|
|
69
69
|
"description": "git commit 전 교차 리뷰 미검증 파일 차단/경고"
|
|
70
70
|
},
|
|
71
|
-
{
|
|
72
|
-
"id": "omc-headless-guard",
|
|
73
|
-
"source": "omc",
|
|
74
|
-
"matcher": "Bash|Agent",
|
|
75
|
-
"command": "bash \"${HOME}/.claude/scripts/headless-guard-fast.sh\"",
|
|
76
|
-
"priority": 50,
|
|
77
|
-
"enabled": true,
|
|
78
|
-
"timeout": 3,
|
|
79
|
-
"blocking": false,
|
|
80
|
-
"description": "primary multiplexer headless 가드"
|
|
81
|
-
},
|
|
82
71
|
{
|
|
83
72
|
"id": "omc-tfx-gate-activate",
|
|
84
73
|
"source": "omc",
|
package/hooks/keyword-rules.json
CHANGED
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"flags": "i"
|
|
56
56
|
}
|
|
57
57
|
],
|
|
58
|
-
"skill": "tfx-
|
|
58
|
+
"skill": "tfx-auto",
|
|
59
59
|
"priority": 1,
|
|
60
60
|
"supersedes": ["tfx-codex"],
|
|
61
61
|
"exclusive": false,
|
|
@@ -412,6 +412,7 @@
|
|
|
412
412
|
}
|
|
413
413
|
],
|
|
414
414
|
"skill": "tfx-wt",
|
|
415
|
+
"platform": ["win32"],
|
|
415
416
|
"priority": 1,
|
|
416
417
|
"supersedes": ["tfx-unified"],
|
|
417
418
|
"exclusive": false,
|
|
@@ -431,6 +432,7 @@
|
|
|
431
432
|
}
|
|
432
433
|
],
|
|
433
434
|
"skill": "tfx-wt",
|
|
435
|
+
"platform": ["win32"],
|
|
434
436
|
"priority": 1,
|
|
435
437
|
"supersedes": ["tfx-unified"],
|
|
436
438
|
"exclusive": false,
|
|
@@ -450,6 +452,7 @@
|
|
|
450
452
|
}
|
|
451
453
|
],
|
|
452
454
|
"skill": "tfx-wt",
|
|
455
|
+
"platform": ["win32"],
|
|
453
456
|
"priority": 1,
|
|
454
457
|
"supersedes": ["tfx-unified"],
|
|
455
458
|
"exclusive": false,
|
|
@@ -469,6 +472,7 @@
|
|
|
469
472
|
}
|
|
470
473
|
],
|
|
471
474
|
"skill": "tfx-wt",
|
|
475
|
+
"platform": ["win32"],
|
|
472
476
|
"priority": 1,
|
|
473
477
|
"supersedes": ["tfx-unified"],
|
|
474
478
|
"exclusive": false,
|
|
@@ -681,11 +685,11 @@
|
|
|
681
685
|
"flags": "i"
|
|
682
686
|
},
|
|
683
687
|
{
|
|
684
|
-
"source": "\\b(?:
|
|
688
|
+
"source": "\\b(?:resume|pick\\s*up|where\\s*was\\s*I)\\b",
|
|
685
689
|
"flags": "i"
|
|
686
690
|
}
|
|
687
691
|
],
|
|
688
|
-
"skill": "
|
|
692
|
+
"skill": "gstack-context-restore",
|
|
689
693
|
"priority": 5,
|
|
690
694
|
"supersedes": [],
|
|
691
695
|
"exclusive": false,
|
|
@@ -719,7 +723,7 @@
|
|
|
719
723
|
"flags": "i"
|
|
720
724
|
},
|
|
721
725
|
{
|
|
722
|
-
"source": "\\
|
|
726
|
+
"source": "(?<![\\w-])ship\\b(?!.*tfx)",
|
|
723
727
|
"flags": "i"
|
|
724
728
|
}
|
|
725
729
|
],
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import { stdin, stdout } from "node:process";
|
|
4
4
|
|
|
5
5
|
const META_PATTERNS = [/&&/, /\|\|/, /[;|<>`]/, /\$\(/];
|
|
6
|
+
const SHELL_EXPANSION_PATTERN =
|
|
7
|
+
/(?:^|[^\\])\$(?:[A-Za-z_][A-Za-z0-9_]*|\{[^}]*\}|[?*#@$!0-9-])|(?:^|\s)~[A-Za-z0-9_-]*(?=\/|$)/;
|
|
6
8
|
const RISKY_PATTERNS = [
|
|
7
9
|
/(^|\s)rm(\s|$)/,
|
|
8
10
|
/(^|\s)mv(\s|$)/,
|
|
@@ -19,6 +21,25 @@ const RISKY_PATTERNS = [
|
|
|
19
21
|
/(^|\s)pnpm\s+publish(\s|$)/,
|
|
20
22
|
/(^|\s)yarn\s+publish(\s|$)/,
|
|
21
23
|
];
|
|
24
|
+
const SEARCH_EXTERNAL_INPUT_FLAGS = new Set([
|
|
25
|
+
"--exclude-from",
|
|
26
|
+
"--file",
|
|
27
|
+
"--files-from",
|
|
28
|
+
"--ignore-file",
|
|
29
|
+
"--pre",
|
|
30
|
+
"--pre-glob",
|
|
31
|
+
]);
|
|
32
|
+
const FIND_SIDE_EFFECT_OPERATORS = new Set([
|
|
33
|
+
"-delete",
|
|
34
|
+
"-exec",
|
|
35
|
+
"-execdir",
|
|
36
|
+
"-fprint",
|
|
37
|
+
"-fprint0",
|
|
38
|
+
"-fprintf",
|
|
39
|
+
"-fls",
|
|
40
|
+
"-ok",
|
|
41
|
+
"-okdir",
|
|
42
|
+
]);
|
|
22
43
|
|
|
23
44
|
function silentlyExit() {
|
|
24
45
|
process.exit(0);
|
|
@@ -57,10 +78,21 @@ function isRepoRelativePath(value) {
|
|
|
57
78
|
function hasUnsafeSurface(command) {
|
|
58
79
|
return (
|
|
59
80
|
META_PATTERNS.some((pattern) => pattern.test(command)) ||
|
|
81
|
+
SHELL_EXPANSION_PATTERN.test(command) ||
|
|
60
82
|
RISKY_PATTERNS.some((pattern) => pattern.test(command))
|
|
61
83
|
);
|
|
62
84
|
}
|
|
63
85
|
|
|
86
|
+
function isExternalSearchInputFlag(word) {
|
|
87
|
+
return (
|
|
88
|
+
SEARCH_EXTERNAL_INPUT_FLAGS.has(word) ||
|
|
89
|
+
[...SEARCH_EXTERNAL_INPUT_FLAGS].some((flag) =>
|
|
90
|
+
word.startsWith(`${flag}=`),
|
|
91
|
+
) ||
|
|
92
|
+
/^-[^-]*f/.test(word)
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
64
96
|
function allowGit(words, command) {
|
|
65
97
|
if (command === "git status" || command === "git status --short")
|
|
66
98
|
return "git status";
|
|
@@ -97,8 +129,7 @@ function allowListing(words) {
|
|
|
97
129
|
|
|
98
130
|
if (words[0] === "find") {
|
|
99
131
|
const args = words.slice(1);
|
|
100
|
-
if (args.some((word) =>
|
|
101
|
-
return null;
|
|
132
|
+
if (args.some((word) => FIND_SIDE_EFFECT_OPERATORS.has(word))) return null;
|
|
102
133
|
const firstOption = args.findIndex((word) => word.startsWith("-"));
|
|
103
134
|
const pathArgs = (
|
|
104
135
|
firstOption === -1 ? args : args.slice(0, firstOption)
|
|
@@ -118,7 +149,10 @@ function allowSearch(words) {
|
|
|
118
149
|
if (
|
|
119
150
|
args.some(
|
|
120
151
|
(word) =>
|
|
152
|
+
isExternalSearchInputFlag(word) ||
|
|
121
153
|
word.startsWith("/") ||
|
|
154
|
+
word.startsWith("~") ||
|
|
155
|
+
word.startsWith("$") ||
|
|
122
156
|
word === ".." ||
|
|
123
157
|
word.startsWith("../") ||
|
|
124
158
|
word.includes("/../"),
|
|
@@ -20,6 +20,10 @@ import {
|
|
|
20
20
|
registerSynapseSession,
|
|
21
21
|
} from "../hub/team/synapse-http.mjs";
|
|
22
22
|
import { createModuleLogger } from "../scripts/lib/logger.mjs";
|
|
23
|
+
import {
|
|
24
|
+
formatCliPolicyLine,
|
|
25
|
+
resolveCliPolicy,
|
|
26
|
+
} from "../scripts/lib/machine-profile.mjs";
|
|
23
27
|
|
|
24
28
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
25
29
|
const SCRIPTS = join(__dirname, "..", "scripts");
|
|
@@ -314,6 +318,36 @@ export function heartbeatInteractiveSession(stdinData, seams = {}) {
|
|
|
314
318
|
}
|
|
315
319
|
}
|
|
316
320
|
|
|
321
|
+
/**
|
|
322
|
+
* TFX_DISABLE_* SSOT 를 읽어 세션 문맥에 넣을 cli-policy 한 줄을 만든다.
|
|
323
|
+
*
|
|
324
|
+
* 형식은 scripts/lib/machine-profile.mjs 의 formatCliPolicyLine 이 고정한다.
|
|
325
|
+
* 리더는 파일 하나를 동기 읽기하므로 ms 단위이고, 어떤 실패도 빈 문자열로
|
|
326
|
+
* 흡수해서 SessionStart 가 이 단계 때문에 죽지 않게 한다.
|
|
327
|
+
*
|
|
328
|
+
* 프로파일 파싱 경고나 0/1 밖의 값이 있으면 둘째 줄로 함께 노출한다. 경고를
|
|
329
|
+
* 삼키면 잘못 적은 프로파일이 조용히 "허용"으로 읽혀서 원인을 못 찾는다.
|
|
330
|
+
*
|
|
331
|
+
* @param {NodeJS.ProcessEnv} [env] 판독할 환경변수
|
|
332
|
+
* @param {{ resolve?: Function }} [opts] resolve 는 리더 대체 주입용 테스트 시임
|
|
333
|
+
* @returns {string} 한 줄 또는 두 줄, 실패 시 빈 문자열
|
|
334
|
+
*/
|
|
335
|
+
export function buildCliPolicyContext(env = process.env, opts = {}) {
|
|
336
|
+
try {
|
|
337
|
+
const resolvePolicy = opts.resolve || resolveCliPolicy;
|
|
338
|
+
const policy = resolvePolicy(env, opts);
|
|
339
|
+
if (!policy) return "";
|
|
340
|
+
const line = formatCliPolicyLine(policy);
|
|
341
|
+
const warnings = Array.isArray(policy.warnings) ? policy.warnings : [];
|
|
342
|
+
if (warnings.length === 0) return line;
|
|
343
|
+
// 경고에 줄바꿈이 섞이면 additionalContext 에 라벨 없는 줄이 생기므로 한 줄로 접는다.
|
|
344
|
+
const folded = warnings.join("; ").replace(/[\r\n]+/gu, " ");
|
|
345
|
+
return `${line}\ncli-policy-warning: ${folded}`;
|
|
346
|
+
} catch {
|
|
347
|
+
return "";
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
317
351
|
/**
|
|
318
352
|
* BLOCKING 훅을 순차 실행. 하나라도 throw하면 로그만 남기고 계속.
|
|
319
353
|
* @param {string} stdinData
|
|
@@ -388,6 +422,21 @@ async function runBlocking(stdinData) {
|
|
|
388
422
|
);
|
|
389
423
|
}
|
|
390
424
|
|
|
425
|
+
// 4. cli-policy: TFX_DISABLE_* SSOT 를 세션 문맥에 한 줄로 주입
|
|
426
|
+
try {
|
|
427
|
+
const t0 = performance.now();
|
|
428
|
+
const line = buildCliPolicyContext();
|
|
429
|
+
const dur = performance.now() - t0;
|
|
430
|
+
timings.push({ hook: "cli-policy", dur_ms: Math.round(dur) });
|
|
431
|
+
if (line) output.stdout += line + "\n";
|
|
432
|
+
log.info({ hook: "cli-policy", dur_ms: Math.round(dur) }, "hook.completed");
|
|
433
|
+
} catch (err) {
|
|
434
|
+
log.error(
|
|
435
|
+
{ hook: "cli-policy", err: String(err.message || err) },
|
|
436
|
+
"hook.failed",
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
|
|
391
440
|
return { ...output, timings };
|
|
392
441
|
}
|
|
393
442
|
|
|
@@ -509,7 +558,7 @@ export async function execute(stdinData, externalHooks = []) {
|
|
|
509
558
|
log.info(
|
|
510
559
|
{
|
|
511
560
|
total_ms: Math.round(totalDur),
|
|
512
|
-
blocking_count:
|
|
561
|
+
blocking_count: 4,
|
|
513
562
|
deferred_count: 2,
|
|
514
563
|
bg_count: 1,
|
|
515
564
|
},
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
5
5
|
import { join, resolve } from "node:path";
|
|
6
6
|
|
|
7
|
+
import { resolveRoleControlSnapshot } from "../hub/lib/cto-env.mjs";
|
|
8
|
+
|
|
7
9
|
const MAX_CONTEXT_BYTES = 2048;
|
|
8
10
|
const HEADER = "[CTO NORTH STAR - READ ONLY]";
|
|
9
11
|
const INSTRUCTION =
|
|
@@ -34,21 +36,6 @@ function capUtf8(input, maxBytes) {
|
|
|
34
36
|
return output;
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
function ctoNorthStarEnabled() {
|
|
38
|
-
switch (process.env.TFX_CTO_NORTH_STAR || "1") {
|
|
39
|
-
case "0":
|
|
40
|
-
case "false":
|
|
41
|
-
case "FALSE":
|
|
42
|
-
case "off":
|
|
43
|
-
case "OFF":
|
|
44
|
-
case "no":
|
|
45
|
-
case "NO":
|
|
46
|
-
return false;
|
|
47
|
-
default:
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
39
|
function wrapBrief(brief) {
|
|
53
40
|
return [HEADER, INSTRUCTION, BEGIN, brief, END].join("\n");
|
|
54
41
|
}
|
|
@@ -60,7 +47,7 @@ function buildAdditionalContext(brief) {
|
|
|
60
47
|
}
|
|
61
48
|
|
|
62
49
|
function main() {
|
|
63
|
-
if (!
|
|
50
|
+
if (!resolveRoleControlSnapshot().north_star_enabled) return;
|
|
64
51
|
|
|
65
52
|
const input = readStdinJson();
|
|
66
53
|
if (input.hook_event_name && input.hook_event_name !== "SessionStart") {
|