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
package/hub/intent.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// hub/intent.mjs — Intent Classification Engine
|
|
2
2
|
// 사용자 요청의 "진짜 의도"를 분석 → 카테고리 분류 → 최적 에이전트/모델 자동 선택
|
|
3
3
|
|
|
4
|
-
import { execFileSync } from
|
|
5
|
-
import crypto from
|
|
6
|
-
import { whichCommand } from
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
import crypto from "node:crypto";
|
|
6
|
+
import { whichCommand } from "./platform.mjs";
|
|
7
7
|
|
|
8
8
|
/** 캐시 엔트리: { category, confidence, ts } */
|
|
9
9
|
const _intentCache = new Map();
|
|
@@ -15,12 +15,12 @@ let _codexAvailable = null;
|
|
|
15
15
|
/** @experimental 런타임 미연결 — 향후 통합 예정 */
|
|
16
16
|
function _isCodexAvailable() {
|
|
17
17
|
if (_codexAvailable !== null) return _codexAvailable;
|
|
18
|
-
_codexAvailable = Boolean(whichCommand(
|
|
18
|
+
_codexAvailable = Boolean(whichCommand("codex"));
|
|
19
19
|
return _codexAvailable;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function _promptHash(prompt) {
|
|
23
|
-
return crypto.createHash(
|
|
23
|
+
return crypto.createHash("md5").update(prompt).digest("hex");
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
function _getCached(hash) {
|
|
@@ -36,16 +36,17 @@ function _getCached(hash) {
|
|
|
36
36
|
function _tryCodexClassify(prompt) {
|
|
37
37
|
try {
|
|
38
38
|
const raw = execFileSync(
|
|
39
|
-
|
|
40
|
-
[
|
|
41
|
-
{ timeout: 8000, encoding:
|
|
39
|
+
"codex",
|
|
40
|
+
["exec", `Classify intent: ${prompt}. Reply JSON: {intent, confidence}`],
|
|
41
|
+
{ timeout: 8000, encoding: "utf8" },
|
|
42
42
|
);
|
|
43
43
|
// JSON 블록 추출 (응답에 다른 텍스트가 섞일 수 있음)
|
|
44
44
|
const match = raw.match(/\{[\s\S]*?\}/);
|
|
45
45
|
if (!match) return null;
|
|
46
46
|
const parsed = JSON.parse(match[0]);
|
|
47
|
-
const intent = typeof parsed.intent ===
|
|
48
|
-
const confidence =
|
|
47
|
+
const intent = typeof parsed.intent === "string" ? parsed.intent : null;
|
|
48
|
+
const confidence =
|
|
49
|
+
typeof parsed.confidence === "number" ? parsed.confidence : null;
|
|
49
50
|
if (!intent || confidence === null) return null;
|
|
50
51
|
// intent가 알려진 카테고리여야 함
|
|
51
52
|
if (!INTENT_CATEGORIES[intent]) return null;
|
|
@@ -60,30 +61,144 @@ function _tryCodexClassify(prompt) {
|
|
|
60
61
|
* triflux 특화 의도 카테고리 (10개)
|
|
61
62
|
*/
|
|
62
63
|
export const INTENT_CATEGORIES = {
|
|
63
|
-
implement:
|
|
64
|
-
debug:
|
|
65
|
-
analyze:
|
|
66
|
-
design:
|
|
67
|
-
review:
|
|
68
|
-
document:
|
|
69
|
-
research:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
implement: { agent: "executor", mcp: "implement", effort: "codex53_high" },
|
|
65
|
+
debug: { agent: "debugger", mcp: "implement", effort: "codex53_high" },
|
|
66
|
+
analyze: { agent: "analyst", mcp: "analyze", effort: "gpt54_xhigh" },
|
|
67
|
+
design: { agent: "architect", mcp: "analyze", effort: "gpt54_xhigh" },
|
|
68
|
+
review: { agent: "code-reviewer", mcp: "review", effort: "codex53_high" },
|
|
69
|
+
document: { agent: "writer", mcp: "docs", effort: "pro" },
|
|
70
|
+
research: { agent: "scientist", mcp: "analyze", effort: "codex53_high" },
|
|
71
|
+
"quick-fix": {
|
|
72
|
+
agent: "build-fixer",
|
|
73
|
+
mcp: "implement",
|
|
74
|
+
effort: "codex53_low",
|
|
75
|
+
},
|
|
76
|
+
explain: { agent: "writer", mcp: "docs", effort: "flash" },
|
|
77
|
+
test: { agent: "test-engineer", mcp: null, effort: null },
|
|
73
78
|
};
|
|
74
79
|
|
|
75
80
|
/** @internal 키워드 → 카테고리 매핑 패턴 */
|
|
76
81
|
const KEYWORD_PATTERNS = [
|
|
77
|
-
{
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
{
|
|
83
|
+
category: "implement",
|
|
84
|
+
keywords: [
|
|
85
|
+
"구현",
|
|
86
|
+
"만들",
|
|
87
|
+
"추가",
|
|
88
|
+
"생성",
|
|
89
|
+
"작성",
|
|
90
|
+
"빌드",
|
|
91
|
+
"implement",
|
|
92
|
+
"create",
|
|
93
|
+
"add",
|
|
94
|
+
"build",
|
|
95
|
+
"make",
|
|
96
|
+
"develop",
|
|
97
|
+
],
|
|
98
|
+
weight: 1.0,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
category: "debug",
|
|
102
|
+
keywords: [
|
|
103
|
+
"버그",
|
|
104
|
+
"에러",
|
|
105
|
+
"오류",
|
|
106
|
+
"고쳐",
|
|
107
|
+
"수정",
|
|
108
|
+
"디버그",
|
|
109
|
+
"fix",
|
|
110
|
+
"bug",
|
|
111
|
+
"error",
|
|
112
|
+
"debug",
|
|
113
|
+
"troubleshoot",
|
|
114
|
+
"crash",
|
|
115
|
+
"broken",
|
|
116
|
+
],
|
|
117
|
+
weight: 1.0,
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
category: "analyze",
|
|
121
|
+
keywords: [
|
|
122
|
+
"분석",
|
|
123
|
+
"조사",
|
|
124
|
+
"파악",
|
|
125
|
+
"analyze",
|
|
126
|
+
"investigate",
|
|
127
|
+
"examine",
|
|
128
|
+
"inspect",
|
|
129
|
+
],
|
|
130
|
+
weight: 0.9,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
category: "design",
|
|
134
|
+
keywords: [
|
|
135
|
+
"설계",
|
|
136
|
+
"아키텍처",
|
|
137
|
+
"디자인",
|
|
138
|
+
"구조",
|
|
139
|
+
"design",
|
|
140
|
+
"architect",
|
|
141
|
+
"structure",
|
|
142
|
+
],
|
|
143
|
+
weight: 0.9,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
category: "review",
|
|
147
|
+
keywords: ["리뷰", "검토", "코드리뷰", "review", "code review", "audit"],
|
|
148
|
+
weight: 1.0,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
category: "document",
|
|
152
|
+
keywords: [
|
|
153
|
+
"문서",
|
|
154
|
+
"도큐먼트",
|
|
155
|
+
"문서화",
|
|
156
|
+
"document",
|
|
157
|
+
"docs",
|
|
158
|
+
"documentation",
|
|
159
|
+
"readme",
|
|
160
|
+
],
|
|
161
|
+
weight: 0.9,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
category: "research",
|
|
165
|
+
keywords: ["리서치", "연구", "탐색", "research", "explore", "study"],
|
|
166
|
+
weight: 0.8,
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
category: "quick-fix",
|
|
170
|
+
keywords: ["빠르게", "간단히", "급한", "quick fix", "hotfix", "quick"],
|
|
171
|
+
weight: 0.85,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
category: "explain",
|
|
175
|
+
keywords: [
|
|
176
|
+
"설명",
|
|
177
|
+
"뭐야",
|
|
178
|
+
"알려",
|
|
179
|
+
"이해",
|
|
180
|
+
"explain",
|
|
181
|
+
"what is",
|
|
182
|
+
"how does",
|
|
183
|
+
"tell me",
|
|
184
|
+
"describe",
|
|
185
|
+
],
|
|
186
|
+
weight: 1.0,
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
category: "test",
|
|
190
|
+
keywords: [
|
|
191
|
+
"테스트",
|
|
192
|
+
"테스팅",
|
|
193
|
+
"시험",
|
|
194
|
+
"검증",
|
|
195
|
+
"test",
|
|
196
|
+
"testing",
|
|
197
|
+
"spec",
|
|
198
|
+
"unit test",
|
|
199
|
+
],
|
|
200
|
+
weight: 1.0,
|
|
201
|
+
},
|
|
87
202
|
];
|
|
88
203
|
|
|
89
204
|
/**
|
|
@@ -93,8 +208,8 @@ const KEYWORD_PATTERNS = [
|
|
|
93
208
|
* @returns {{ category: string, confidence: number }}
|
|
94
209
|
*/
|
|
95
210
|
export function quickClassify(prompt) {
|
|
96
|
-
if (!prompt || typeof prompt !==
|
|
97
|
-
return { category:
|
|
211
|
+
if (!prompt || typeof prompt !== "string" || !prompt.trim()) {
|
|
212
|
+
return { category: "implement", confidence: 0.1 };
|
|
98
213
|
}
|
|
99
214
|
|
|
100
215
|
const lower = prompt.toLowerCase().trim();
|
|
@@ -118,11 +233,14 @@ export function quickClassify(prompt) {
|
|
|
118
233
|
}
|
|
119
234
|
|
|
120
235
|
if (!bestCategory) {
|
|
121
|
-
return { category:
|
|
236
|
+
return { category: "implement", confidence: 0.3 };
|
|
122
237
|
}
|
|
123
238
|
|
|
124
239
|
// 매칭 품질 기반 신뢰도 (0.5~0.95 범위) — matchCount 기준으로 정규화 (3개 매칭이면 최대)
|
|
125
|
-
const confidence = Math.min(
|
|
240
|
+
const confidence = Math.min(
|
|
241
|
+
0.95,
|
|
242
|
+
0.5 + (Math.min(bestMatchCount, 3) / 3) * 0.45,
|
|
243
|
+
);
|
|
126
244
|
return { category: bestCategory, confidence };
|
|
127
245
|
}
|
|
128
246
|
|
|
@@ -135,17 +253,22 @@ export function quickClassify(prompt) {
|
|
|
135
253
|
* @returns {{ category: string, confidence: number, reasoning: string, routing: { agent: string, mcp: string|null, effort: string|null } }}
|
|
136
254
|
*/
|
|
137
255
|
export function classifyIntent(prompt) {
|
|
138
|
-
const hash = _promptHash(String(prompt ??
|
|
256
|
+
const hash = _promptHash(String(prompt ?? ""));
|
|
139
257
|
|
|
140
258
|
// 캐시 확인
|
|
141
259
|
const cached = _getCached(hash);
|
|
142
260
|
if (cached) {
|
|
143
|
-
const routing =
|
|
261
|
+
const routing =
|
|
262
|
+
INTENT_CATEGORIES[cached.category] || INTENT_CATEGORIES.implement;
|
|
144
263
|
return {
|
|
145
264
|
category: cached.category,
|
|
146
265
|
confidence: cached.confidence,
|
|
147
266
|
reasoning: `cache-hit: ${cached.category} (${cached.confidence.toFixed(2)})`,
|
|
148
|
-
routing: {
|
|
267
|
+
routing: {
|
|
268
|
+
agent: routing.agent,
|
|
269
|
+
mcp: routing.mcp,
|
|
270
|
+
effort: routing.effort,
|
|
271
|
+
},
|
|
149
272
|
};
|
|
150
273
|
}
|
|
151
274
|
|
|
@@ -161,7 +284,7 @@ export function classifyIntent(prompt) {
|
|
|
161
284
|
reasoning = `keyword-match: ${category} (${confidence.toFixed(2)})`;
|
|
162
285
|
} else if (_isCodexAvailable()) {
|
|
163
286
|
// Codex triage
|
|
164
|
-
const codexResult = _tryCodexClassify(String(prompt ??
|
|
287
|
+
const codexResult = _tryCodexClassify(String(prompt ?? ""));
|
|
165
288
|
if (codexResult && codexResult.confidence > 0.8) {
|
|
166
289
|
category = codexResult.category;
|
|
167
290
|
confidence = codexResult.confidence;
|
package/hub/lib/cache-guard.mjs
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
accessSync,
|
|
3
|
+
constants,
|
|
4
|
+
existsSync,
|
|
5
|
+
readdirSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
} from "node:fs";
|
|
2
8
|
import http from "node:http";
|
|
3
9
|
import https from "node:https";
|
|
4
10
|
import { basename, join, relative } from "node:path";
|
|
@@ -7,7 +13,10 @@ const NETWORK_TIMEOUT_MS = 3_000;
|
|
|
7
13
|
|
|
8
14
|
function toIssuePath(cacheDir, filePath) {
|
|
9
15
|
const relPath = relative(cacheDir, filePath);
|
|
10
|
-
return (relPath && relPath.length > 0 ? relPath : basename(filePath)).replace(
|
|
16
|
+
return (relPath && relPath.length > 0 ? relPath : basename(filePath)).replace(
|
|
17
|
+
/\\/g,
|
|
18
|
+
"/",
|
|
19
|
+
);
|
|
11
20
|
}
|
|
12
21
|
|
|
13
22
|
function collectCacheFiles(cacheDir) {
|
|
@@ -45,19 +54,28 @@ function probeUrl(url) {
|
|
|
45
54
|
|
|
46
55
|
try {
|
|
47
56
|
const parsed = new URL(url);
|
|
48
|
-
const transport =
|
|
57
|
+
const transport =
|
|
58
|
+
parsed.protocol === "https:"
|
|
59
|
+
? https
|
|
60
|
+
: parsed.protocol === "http:"
|
|
61
|
+
? http
|
|
62
|
+
: null;
|
|
49
63
|
if (!transport) {
|
|
50
64
|
finish(false);
|
|
51
65
|
return;
|
|
52
66
|
}
|
|
53
67
|
|
|
54
|
-
const request = transport.request(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
68
|
+
const request = transport.request(
|
|
69
|
+
parsed,
|
|
70
|
+
{
|
|
71
|
+
method: "HEAD",
|
|
72
|
+
headers: { "user-agent": "triflux-cache-guard" },
|
|
73
|
+
},
|
|
74
|
+
(response) => {
|
|
75
|
+
response.resume();
|
|
76
|
+
finish(true);
|
|
77
|
+
},
|
|
78
|
+
);
|
|
61
79
|
|
|
62
80
|
request.setTimeout(NETWORK_TIMEOUT_MS, () => {
|
|
63
81
|
request.destroy(new Error("timeout"));
|
|
@@ -88,23 +106,31 @@ export function validateRuntimeCachePaths(cacheDir) {
|
|
|
88
106
|
} catch (error) {
|
|
89
107
|
return {
|
|
90
108
|
ok: false,
|
|
91
|
-
issues: [
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
109
|
+
issues: [
|
|
110
|
+
{
|
|
111
|
+
file: ".",
|
|
112
|
+
error: error instanceof Error ? error.message : String(error),
|
|
113
|
+
},
|
|
114
|
+
],
|
|
95
115
|
};
|
|
96
116
|
}
|
|
97
117
|
}
|
|
98
118
|
|
|
99
119
|
export async function checkNetworkAvailability(urls) {
|
|
100
|
-
const targets = [
|
|
120
|
+
const targets = [
|
|
121
|
+
...new Set((Array.isArray(urls) ? urls : []).filter(Boolean)),
|
|
122
|
+
];
|
|
101
123
|
if (targets.length === 0) {
|
|
102
124
|
return { online: true, reachable: [], unreachable: [] };
|
|
103
125
|
}
|
|
104
126
|
|
|
105
127
|
const results = await Promise.all(targets.map((url) => probeUrl(url)));
|
|
106
|
-
const reachable = results
|
|
107
|
-
|
|
128
|
+
const reachable = results
|
|
129
|
+
.filter((result) => result.ok)
|
|
130
|
+
.map((result) => result.url);
|
|
131
|
+
const unreachable = results
|
|
132
|
+
.filter((result) => !result.ok)
|
|
133
|
+
.map((result) => result.url);
|
|
108
134
|
|
|
109
135
|
return {
|
|
110
136
|
online: unreachable.length === 0,
|
|
@@ -2,29 +2,36 @@
|
|
|
2
2
|
// Deep 스킬에서 3개 모델이 같은 context7 문서를 요청하면 1번만 호출, 나머지는 캐시 hit.
|
|
3
3
|
// 파일 기반 영속 + 메모리 기반 핫캐시 이중 구조.
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import {
|
|
7
|
+
existsSync,
|
|
8
|
+
mkdirSync,
|
|
9
|
+
readdirSync,
|
|
10
|
+
readFileSync,
|
|
11
|
+
unlinkSync,
|
|
12
|
+
writeFileSync,
|
|
13
|
+
} from "node:fs";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
|
|
17
|
+
const DEFAULT_CACHE_DIR = join(homedir(), ".triflux", "mcp-cache");
|
|
11
18
|
const DEFAULT_MAX_ENTRIES = 500;
|
|
12
19
|
|
|
13
20
|
// 서버별 TTL (ms)
|
|
14
21
|
const SERVER_TTL = Object.freeze({
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
context7: 60 * 60 * 1000, // 1시간 — 라이브러리 문서는 자주 안 바뀜
|
|
23
|
+
"brave-search": 10 * 60 * 1000, // 10분 — 웹 검색
|
|
24
|
+
exa: 10 * 60 * 1000, // 10분
|
|
25
|
+
tavily: 10 * 60 * 1000, // 10분
|
|
26
|
+
serena: 5 * 60 * 1000, // 5분 — 코드 분석 (코드 변경 가능)
|
|
27
|
+
jira: 2 * 60 * 1000, // 2분 — 이슈 상태 자주 변경
|
|
28
|
+
notion: 5 * 60 * 1000, // 5분
|
|
29
|
+
_default: 5 * 60 * 1000, // 기본 5분
|
|
23
30
|
});
|
|
24
31
|
|
|
25
32
|
function hashKey(server, method, params) {
|
|
26
33
|
const payload = JSON.stringify({ server, method, params });
|
|
27
|
-
return createHash(
|
|
34
|
+
return createHash("sha256").update(payload).digest("hex").slice(0, 16);
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
/**
|
|
@@ -82,7 +89,7 @@ export function createMcpResponseCache(opts = {}) {
|
|
|
82
89
|
const path = getCachePath(key);
|
|
83
90
|
if (existsSync(path)) {
|
|
84
91
|
try {
|
|
85
|
-
const entry = JSON.parse(readFileSync(path,
|
|
92
|
+
const entry = JSON.parse(readFileSync(path, "utf8"));
|
|
86
93
|
if (Date.now() - entry.ts < ttl) {
|
|
87
94
|
hits++;
|
|
88
95
|
hot.set(key, entry);
|
|
@@ -90,8 +97,14 @@ export function createMcpResponseCache(opts = {}) {
|
|
|
90
97
|
return { hit: true, data: entry.data };
|
|
91
98
|
}
|
|
92
99
|
// expired — 삭제
|
|
93
|
-
try {
|
|
94
|
-
|
|
100
|
+
try {
|
|
101
|
+
unlinkSync(path);
|
|
102
|
+
} catch {
|
|
103
|
+
/* ignore */
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
/* corrupt file */
|
|
107
|
+
}
|
|
95
108
|
}
|
|
96
109
|
|
|
97
110
|
misses++;
|
|
@@ -115,8 +128,10 @@ export function createMcpResponseCache(opts = {}) {
|
|
|
115
128
|
|
|
116
129
|
// 파일
|
|
117
130
|
try {
|
|
118
|
-
writeFileSync(getCachePath(key), JSON.stringify(entry),
|
|
119
|
-
} catch {
|
|
131
|
+
writeFileSync(getCachePath(key), JSON.stringify(entry), "utf8");
|
|
132
|
+
} catch {
|
|
133
|
+
/* best-effort */
|
|
134
|
+
}
|
|
120
135
|
}
|
|
121
136
|
|
|
122
137
|
function evictIfNeeded() {
|
|
@@ -134,21 +149,28 @@ export function createMcpResponseCache(opts = {}) {
|
|
|
134
149
|
let pruned = 0;
|
|
135
150
|
try {
|
|
136
151
|
for (const name of readdirSync(cacheDir)) {
|
|
137
|
-
if (!name.endsWith(
|
|
152
|
+
if (!name.endsWith(".json")) continue;
|
|
138
153
|
const path = join(cacheDir, name);
|
|
139
154
|
try {
|
|
140
|
-
const entry = JSON.parse(readFileSync(path,
|
|
141
|
-
const ttl = getTtl(entry.server ||
|
|
155
|
+
const entry = JSON.parse(readFileSync(path, "utf8"));
|
|
156
|
+
const ttl = getTtl(entry.server || "_default");
|
|
142
157
|
if (Date.now() - entry.ts >= ttl) {
|
|
143
158
|
unlinkSync(path);
|
|
144
159
|
pruned++;
|
|
145
160
|
}
|
|
146
161
|
} catch {
|
|
147
162
|
// corrupt — 삭제
|
|
148
|
-
try {
|
|
163
|
+
try {
|
|
164
|
+
unlinkSync(path);
|
|
165
|
+
pruned++;
|
|
166
|
+
} catch {
|
|
167
|
+
/* ignore */
|
|
168
|
+
}
|
|
149
169
|
}
|
|
150
170
|
}
|
|
151
|
-
} catch {
|
|
171
|
+
} catch {
|
|
172
|
+
/* ignore */
|
|
173
|
+
}
|
|
152
174
|
return pruned;
|
|
153
175
|
}
|
|
154
176
|
|
|
@@ -159,8 +181,12 @@ export function createMcpResponseCache(opts = {}) {
|
|
|
159
181
|
function stats() {
|
|
160
182
|
let diskFiles = 0;
|
|
161
183
|
try {
|
|
162
|
-
diskFiles = readdirSync(cacheDir).filter((n) =>
|
|
163
|
-
|
|
184
|
+
diskFiles = readdirSync(cacheDir).filter((n) =>
|
|
185
|
+
n.endsWith(".json"),
|
|
186
|
+
).length;
|
|
187
|
+
} catch {
|
|
188
|
+
/* ignore */
|
|
189
|
+
}
|
|
164
190
|
|
|
165
191
|
const total = hits + misses;
|
|
166
192
|
return {
|
|
@@ -168,7 +194,7 @@ export function createMcpResponseCache(opts = {}) {
|
|
|
168
194
|
misses,
|
|
169
195
|
hotSize: hot.size,
|
|
170
196
|
diskFiles,
|
|
171
|
-
hitRate: total > 0 ? `${((hits / total) * 100).toFixed(1)}%` :
|
|
197
|
+
hitRate: total > 0 ? `${((hits / total) * 100).toFixed(1)}%` : "0%",
|
|
172
198
|
};
|
|
173
199
|
}
|
|
174
200
|
|
|
@@ -177,11 +203,17 @@ export function createMcpResponseCache(opts = {}) {
|
|
|
177
203
|
hot.clear();
|
|
178
204
|
try {
|
|
179
205
|
for (const name of readdirSync(cacheDir)) {
|
|
180
|
-
if (name.endsWith(
|
|
181
|
-
try {
|
|
206
|
+
if (name.endsWith(".json")) {
|
|
207
|
+
try {
|
|
208
|
+
unlinkSync(join(cacheDir, name));
|
|
209
|
+
} catch {
|
|
210
|
+
/* ignore */
|
|
211
|
+
}
|
|
182
212
|
}
|
|
183
213
|
}
|
|
184
|
-
} catch {
|
|
214
|
+
} catch {
|
|
215
|
+
/* ignore */
|
|
216
|
+
}
|
|
185
217
|
hits = 0;
|
|
186
218
|
misses = 0;
|
|
187
219
|
}
|
|
@@ -192,7 +224,9 @@ export function createMcpResponseCache(opts = {}) {
|
|
|
192
224
|
prune,
|
|
193
225
|
stats,
|
|
194
226
|
clear,
|
|
195
|
-
get cacheDir() {
|
|
227
|
+
get cacheDir() {
|
|
228
|
+
return cacheDir;
|
|
229
|
+
},
|
|
196
230
|
});
|
|
197
231
|
}
|
|
198
232
|
|