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/hud/terminal.mjs
CHANGED
|
@@ -2,51 +2,111 @@
|
|
|
2
2
|
// 터미널 감지 / 4-Tier 적응형 렌더링
|
|
3
3
|
// ============================================================================
|
|
4
4
|
import { execSync } from "node:child_process";
|
|
5
|
-
import {
|
|
5
|
+
import { coloredBar, DIM, GAUGE_WIDTH, RESET } from "./colors.mjs";
|
|
6
|
+
import {
|
|
7
|
+
COMPACT_COLS_THRESHOLD,
|
|
8
|
+
HUD_CONFIG_PATH,
|
|
9
|
+
MINIMAL_COLS_THRESHOLD,
|
|
10
|
+
} from "./constants.mjs";
|
|
6
11
|
import { readJson } from "./utils.mjs";
|
|
7
|
-
import { HUD_CONFIG_PATH, COMPACT_COLS_THRESHOLD, MINIMAL_COLS_THRESHOLD } from "./constants.mjs";
|
|
8
12
|
|
|
9
13
|
let _cachedColumns = 0;
|
|
10
14
|
export function getTerminalColumns() {
|
|
11
15
|
if (_cachedColumns > 0) return _cachedColumns;
|
|
12
16
|
const envCols = Number(process.env.COLUMNS);
|
|
13
|
-
if (envCols > 0) {
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
if (envCols > 0) {
|
|
18
|
+
_cachedColumns = envCols;
|
|
19
|
+
return _cachedColumns;
|
|
20
|
+
}
|
|
21
|
+
if (process.stdout.columns) {
|
|
22
|
+
_cachedColumns = process.stdout.columns;
|
|
23
|
+
return _cachedColumns;
|
|
24
|
+
}
|
|
25
|
+
if (process.stderr.columns) {
|
|
26
|
+
_cachedColumns = process.stderr.columns;
|
|
27
|
+
return _cachedColumns;
|
|
28
|
+
}
|
|
16
29
|
try {
|
|
17
30
|
if (process.platform === "win32") {
|
|
18
|
-
const raw = execSync("mode con", {
|
|
19
|
-
|
|
20
|
-
|
|
31
|
+
const raw = execSync("mode con", {
|
|
32
|
+
timeout: 2000,
|
|
33
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
34
|
+
windowsHide: true,
|
|
35
|
+
}).toString();
|
|
36
|
+
const m =
|
|
37
|
+
raw.match(/Columns[^:]*:\s*(\d+)/i) || raw.match(/열[^:]*:\s*(\d+)/);
|
|
38
|
+
if (m) {
|
|
39
|
+
_cachedColumns = Number(m[1]);
|
|
40
|
+
return _cachedColumns;
|
|
41
|
+
}
|
|
21
42
|
} else {
|
|
22
|
-
const raw = execSync(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
43
|
+
const raw = execSync(
|
|
44
|
+
"tput cols 2>/dev/null || stty size 2>/dev/null | awk '{print $2}'",
|
|
45
|
+
{
|
|
46
|
+
timeout: 2000,
|
|
47
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
48
|
+
},
|
|
49
|
+
)
|
|
50
|
+
.toString()
|
|
51
|
+
.trim();
|
|
52
|
+
if (raw && !Number.isNaN(Number(raw))) {
|
|
53
|
+
_cachedColumns = Number(raw);
|
|
54
|
+
return _cachedColumns;
|
|
55
|
+
}
|
|
26
56
|
}
|
|
27
|
-
} catch {
|
|
57
|
+
} catch {
|
|
58
|
+
/* 감지 실패 */
|
|
59
|
+
}
|
|
28
60
|
return 0;
|
|
29
61
|
}
|
|
30
62
|
|
|
31
63
|
let _cachedRows = 0;
|
|
32
64
|
export function getTerminalRows() {
|
|
33
65
|
if (_cachedRows > 0) return _cachedRows;
|
|
34
|
-
if (process.stdout.rows) {
|
|
35
|
-
|
|
66
|
+
if (process.stdout.rows) {
|
|
67
|
+
_cachedRows = process.stdout.rows;
|
|
68
|
+
return _cachedRows;
|
|
69
|
+
}
|
|
70
|
+
if (process.stderr.rows) {
|
|
71
|
+
_cachedRows = process.stderr.rows;
|
|
72
|
+
return _cachedRows;
|
|
73
|
+
}
|
|
36
74
|
const envLines = Number(process.env.LINES);
|
|
37
|
-
if (envLines > 0) {
|
|
75
|
+
if (envLines > 0) {
|
|
76
|
+
_cachedRows = envLines;
|
|
77
|
+
return _cachedRows;
|
|
78
|
+
}
|
|
38
79
|
try {
|
|
39
80
|
if (process.platform === "win32") {
|
|
40
|
-
const raw = execSync("mode con", {
|
|
41
|
-
|
|
42
|
-
|
|
81
|
+
const raw = execSync("mode con", {
|
|
82
|
+
timeout: 2000,
|
|
83
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
84
|
+
windowsHide: true,
|
|
85
|
+
}).toString();
|
|
86
|
+
const m =
|
|
87
|
+
raw.match(/Lines[^:]*:\s*(\d+)/i) || raw.match(/줄[^:]*:\s*(\d+)/);
|
|
88
|
+
if (m) {
|
|
89
|
+
_cachedRows = Number(m[1]);
|
|
90
|
+
return _cachedRows;
|
|
91
|
+
}
|
|
43
92
|
} else {
|
|
44
|
-
const raw = execSync(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
93
|
+
const raw = execSync(
|
|
94
|
+
"tput lines 2>/dev/null || stty size 2>/dev/null | awk '{print $1}'",
|
|
95
|
+
{
|
|
96
|
+
timeout: 2000,
|
|
97
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
98
|
+
},
|
|
99
|
+
)
|
|
100
|
+
.toString()
|
|
101
|
+
.trim();
|
|
102
|
+
if (raw && !Number.isNaN(Number(raw))) {
|
|
103
|
+
_cachedRows = Number(raw);
|
|
104
|
+
return _cachedRows;
|
|
105
|
+
}
|
|
48
106
|
}
|
|
49
|
-
} catch {
|
|
107
|
+
} catch {
|
|
108
|
+
/* 감지 실패 */
|
|
109
|
+
}
|
|
50
110
|
return 0;
|
|
51
111
|
}
|
|
52
112
|
|
|
@@ -60,12 +120,15 @@ export function detectCompactMode() {
|
|
|
60
120
|
if (process.env.OMC_HUD_COMPACT === "0") return false;
|
|
61
121
|
// 3. 설정 파일 (~/.omc/config/hud.json)
|
|
62
122
|
const hudConfig = readJson(HUD_CONFIG_PATH, null);
|
|
63
|
-
if (hudConfig?.compact === true || hudConfig?.compact === "always")
|
|
64
|
-
|
|
123
|
+
if (hudConfig?.compact === true || hudConfig?.compact === "always")
|
|
124
|
+
return true;
|
|
125
|
+
if (hudConfig?.compact === false || hudConfig?.compact === "never")
|
|
126
|
+
return false;
|
|
65
127
|
// 4. maxLines < 3이면 자동 컴팩트 (알림 배너 공존 대응)
|
|
66
128
|
if (Number(hudConfig?.lines) > 0 && Number(hudConfig?.lines) < 3) return true;
|
|
67
129
|
// 5. 터미널 폭 자동 감지 (TTY 있을 때만 유효)
|
|
68
|
-
const threshold =
|
|
130
|
+
const threshold =
|
|
131
|
+
Number(hudConfig?.compactThreshold) || COMPACT_COLS_THRESHOLD;
|
|
69
132
|
const cols = getTerminalColumns();
|
|
70
133
|
if (cols > 0 && cols < threshold) return true;
|
|
71
134
|
return false;
|
|
@@ -95,7 +158,8 @@ export function selectTier(stdin, claudeUsage = null) {
|
|
|
95
158
|
|
|
96
159
|
// 1) 명시적 tier 강제 설정
|
|
97
160
|
const forcedTier = hudConfig?.tier;
|
|
98
|
-
if (["full", "compact", "minimal", "micro", "nano"].includes(forcedTier))
|
|
161
|
+
if (["full", "compact", "minimal", "micro", "nano"].includes(forcedTier))
|
|
162
|
+
return forcedTier;
|
|
99
163
|
|
|
100
164
|
// 1.5) maxLines=1 → nano (1줄 모드: 알림 배너/분할화면 대응)
|
|
101
165
|
if (Number(hudConfig?.lines) === 1) return "nano";
|
|
@@ -123,14 +187,20 @@ export function selectTier(stdin, claudeUsage = null) {
|
|
|
123
187
|
|
|
124
188
|
// full tier 전용: 게이지 바 접두사 (normal 이하 tier에서는 빈 문자열)
|
|
125
189
|
export function tierBar(currentTier, percent, baseColor = null) {
|
|
126
|
-
return currentTier === "full"
|
|
190
|
+
return currentTier === "full"
|
|
191
|
+
? coloredBar(percent, GAUGE_WIDTH, baseColor) + " "
|
|
192
|
+
: "";
|
|
127
193
|
}
|
|
128
194
|
export function tierDimBar(currentTier) {
|
|
129
|
-
return currentTier === "full"
|
|
195
|
+
return currentTier === "full"
|
|
196
|
+
? DIM + "░".repeat(GAUGE_WIDTH) + RESET + " "
|
|
197
|
+
: "";
|
|
130
198
|
}
|
|
131
199
|
// Gemini ∞% 전용: 무한 쿼터이므로 dim 회색 바
|
|
132
200
|
export function tierInfBar(currentTier) {
|
|
133
|
-
return currentTier === "full"
|
|
201
|
+
return currentTier === "full"
|
|
202
|
+
? DIM + "█".repeat(GAUGE_WIDTH) + RESET + " "
|
|
203
|
+
: "";
|
|
134
204
|
}
|
|
135
205
|
|
|
136
206
|
// 테스트 지원: 캐시 초기화
|
package/hud/utils.mjs
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
// ============================================================================
|
|
2
2
|
// 유틸리티 함수
|
|
3
3
|
// ============================================================================
|
|
4
|
-
|
|
5
|
-
import { dirname } from "node:path";
|
|
4
|
+
|
|
6
5
|
import { createHash } from "node:crypto";
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { dirname } from "node:path";
|
|
7
8
|
import {
|
|
8
|
-
PERCENT_CELL_WIDTH,
|
|
9
|
+
PERCENT_CELL_WIDTH,
|
|
10
|
+
SV_CELL_WIDTH,
|
|
11
|
+
TIME_CELL_INNER_WIDTH,
|
|
9
12
|
} from "./constants.mjs";
|
|
10
13
|
|
|
11
14
|
export async function readStdinJson() {
|
|
@@ -20,8 +23,15 @@ export async function readStdinJson() {
|
|
|
20
23
|
process.stdin.on("end", () => {
|
|
21
24
|
clearTimeout(timeout);
|
|
22
25
|
const raw = chunks.join("").trim();
|
|
23
|
-
if (!raw) {
|
|
24
|
-
|
|
26
|
+
if (!raw) {
|
|
27
|
+
resolve({});
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
resolve(JSON.parse(raw));
|
|
32
|
+
} catch {
|
|
33
|
+
resolve({});
|
|
34
|
+
}
|
|
25
35
|
});
|
|
26
36
|
process.stdin.on("error", () => {
|
|
27
37
|
clearTimeout(timeout);
|
|
@@ -33,14 +43,20 @@ export async function readStdinJson() {
|
|
|
33
43
|
|
|
34
44
|
export function readJson(filePath, fallback) {
|
|
35
45
|
if (!existsSync(filePath)) return fallback;
|
|
36
|
-
try {
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
48
|
+
} catch {
|
|
49
|
+
return fallback;
|
|
50
|
+
}
|
|
37
51
|
}
|
|
38
52
|
|
|
39
53
|
export function writeJsonSafe(filePath, data) {
|
|
40
54
|
try {
|
|
41
55
|
mkdirSync(dirname(filePath), { recursive: true });
|
|
42
56
|
writeFileSync(filePath, JSON.stringify(data), { mode: 0o600 });
|
|
43
|
-
} catch {
|
|
57
|
+
} catch {
|
|
58
|
+
/* 쓰기 실패 무시 */
|
|
59
|
+
}
|
|
44
60
|
}
|
|
45
61
|
|
|
46
62
|
// .omc/ → .claude/cache/ 마이그레이션: 새 경로 우선, 없으면 레거시 읽고 복사
|
|
@@ -48,7 +64,10 @@ export function readJsonMigrate(newPath, legacyPath, fallback) {
|
|
|
48
64
|
const data = readJson(newPath, null);
|
|
49
65
|
if (data != null) return data;
|
|
50
66
|
const legacy = readJson(legacyPath, null);
|
|
51
|
-
if (legacy != null) {
|
|
67
|
+
if (legacy != null) {
|
|
68
|
+
writeJsonSafe(newPath, legacy);
|
|
69
|
+
return legacy;
|
|
70
|
+
}
|
|
52
71
|
return fallback;
|
|
53
72
|
}
|
|
54
73
|
|
|
@@ -109,7 +128,10 @@ export function truncateAnsi(text, maxWidth) {
|
|
|
109
128
|
}
|
|
110
129
|
|
|
111
130
|
export function makeHash(text) {
|
|
112
|
-
return createHash("sha256")
|
|
131
|
+
return createHash("sha256")
|
|
132
|
+
.update(String(text || ""), "utf8")
|
|
133
|
+
.digest("hex")
|
|
134
|
+
.slice(0, 16);
|
|
113
135
|
}
|
|
114
136
|
|
|
115
137
|
export function clampPercent(value) {
|
|
@@ -177,11 +199,13 @@ export function formatTokenCount(n) {
|
|
|
177
199
|
|
|
178
200
|
export function getContextPercent(stdin) {
|
|
179
201
|
const nativePercent = stdin?.context_window?.used_percentage;
|
|
180
|
-
if (typeof nativePercent === "number" && Number.isFinite(nativePercent))
|
|
202
|
+
if (typeof nativePercent === "number" && Number.isFinite(nativePercent))
|
|
203
|
+
return clampPercent(nativePercent);
|
|
181
204
|
const usage = stdin?.context_window?.current_usage || {};
|
|
182
|
-
const totalTokens =
|
|
183
|
-
|
|
184
|
-
|
|
205
|
+
const totalTokens =
|
|
206
|
+
Number(usage.input_tokens || 0) +
|
|
207
|
+
Number(usage.cache_creation_input_tokens || 0) +
|
|
208
|
+
Number(usage.cache_read_input_tokens || 0);
|
|
185
209
|
const capacity = Number(stdin?.context_window?.context_window_size || 0);
|
|
186
210
|
if (!capacity || capacity <= 0) return 0;
|
|
187
211
|
return clampPercent((totalTokens / capacity) * 100);
|
|
@@ -197,9 +221,10 @@ export function advanceToNextCycle(epochMs, cycleMs) {
|
|
|
197
221
|
|
|
198
222
|
function parseResetDate(isoOrUnix) {
|
|
199
223
|
if (!isoOrUnix) return null;
|
|
200
|
-
const date =
|
|
201
|
-
|
|
202
|
-
|
|
224
|
+
const date =
|
|
225
|
+
typeof isoOrUnix === "string"
|
|
226
|
+
? new Date(isoOrUnix)
|
|
227
|
+
: new Date(isoOrUnix * 1000);
|
|
203
228
|
return Number.isNaN(date.getTime()) ? null : date;
|
|
204
229
|
}
|
|
205
230
|
|
|
@@ -261,9 +286,13 @@ export function decodeJwtEmail(idToken) {
|
|
|
261
286
|
let payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
262
287
|
while (payload.length % 4) payload += "=";
|
|
263
288
|
try {
|
|
264
|
-
const decoded = JSON.parse(
|
|
289
|
+
const decoded = JSON.parse(
|
|
290
|
+
Buffer.from(payload, "base64").toString("utf-8"),
|
|
291
|
+
);
|
|
265
292
|
return decoded.email || null;
|
|
266
|
-
} catch {
|
|
293
|
+
} catch {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
267
296
|
}
|
|
268
297
|
|
|
269
298
|
// HTTPS POST (타임아웃 포함) — https 모듈은 호출자가 주입
|
|
@@ -272,26 +301,35 @@ export function createHttpsPost(https, timeoutMs) {
|
|
|
272
301
|
return new Promise((resolve) => {
|
|
273
302
|
const urlObj = new URL(url);
|
|
274
303
|
const data = JSON.stringify(body);
|
|
275
|
-
const req = https.request(
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
304
|
+
const req = https.request(
|
|
305
|
+
{
|
|
306
|
+
hostname: urlObj.hostname,
|
|
307
|
+
path: urlObj.pathname + urlObj.search,
|
|
308
|
+
method: "POST",
|
|
309
|
+
headers: {
|
|
310
|
+
"Content-Type": "application/json",
|
|
311
|
+
Authorization: `Bearer ${accessToken}`,
|
|
312
|
+
"Content-Length": Buffer.byteLength(data),
|
|
313
|
+
},
|
|
314
|
+
timeout: timeoutMs,
|
|
283
315
|
},
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
316
|
+
(res) => {
|
|
317
|
+
const chunks = [];
|
|
318
|
+
res.on("data", (c) => chunks.push(c));
|
|
319
|
+
res.on("end", () => {
|
|
320
|
+
try {
|
|
321
|
+
resolve(JSON.parse(Buffer.concat(chunks).toString()));
|
|
322
|
+
} catch {
|
|
323
|
+
resolve(null);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
},
|
|
327
|
+
);
|
|
293
328
|
req.on("error", () => resolve(null));
|
|
294
|
-
req.on("timeout", () => {
|
|
329
|
+
req.on("timeout", () => {
|
|
330
|
+
req.destroy();
|
|
331
|
+
resolve(null);
|
|
332
|
+
});
|
|
295
333
|
req.write(data);
|
|
296
334
|
req.end();
|
|
297
335
|
});
|
|
@@ -301,8 +339,10 @@ export function createHttpsPost(https, timeoutMs) {
|
|
|
301
339
|
// sv 퍼센트 포맷 (1000+ → k 표기, 5자 고정폭)
|
|
302
340
|
export function formatSvPct(value) {
|
|
303
341
|
if (value == null) return "--%".padStart(SV_CELL_WIDTH);
|
|
304
|
-
if (value >= 10000)
|
|
305
|
-
|
|
342
|
+
if (value >= 10000)
|
|
343
|
+
return `${Math.round(value / 1000)}k%`.padStart(SV_CELL_WIDTH);
|
|
344
|
+
if (value >= 1000)
|
|
345
|
+
return `${(value / 1000).toFixed(1)}k%`.padStart(SV_CELL_WIDTH);
|
|
306
346
|
return `${value}%`.padStart(SV_CELL_WIDTH);
|
|
307
347
|
}
|
|
308
348
|
|
package/mesh/index.mjs
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { readdirSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
|
-
export { MSG_TYPES, createMessage, serialize, deserialize, validate } from "./mesh-protocol.mjs";
|
|
5
|
-
export { createRegistry } from "./mesh-registry.mjs";
|
|
6
4
|
export { createMeshBudget } from "./mesh-budget.mjs";
|
|
7
|
-
export { routeMessage, routeOrDeadLetter } from "./mesh-router.mjs";
|
|
8
|
-
export { createMessageQueue } from "./mesh-queue.mjs";
|
|
9
5
|
export { createHeartbeatMonitor } from "./mesh-heartbeat.mjs";
|
|
6
|
+
export {
|
|
7
|
+
createMessage,
|
|
8
|
+
deserialize,
|
|
9
|
+
MSG_TYPES,
|
|
10
|
+
serialize,
|
|
11
|
+
validate,
|
|
12
|
+
} from "./mesh-protocol.mjs";
|
|
13
|
+
export { createMessageQueue } from "./mesh-queue.mjs";
|
|
14
|
+
export { createRegistry } from "./mesh-registry.mjs";
|
|
15
|
+
export { routeMessage, routeOrDeadLetter } from "./mesh-router.mjs";
|
|
10
16
|
|
|
11
17
|
/**
|
|
12
18
|
* Loads skills assigned to a specific agent from a skills directory.
|
|
@@ -54,7 +60,7 @@ export async function loadSkillsForAgent(agentId, skillsDir) {
|
|
|
54
60
|
continue;
|
|
55
61
|
}
|
|
56
62
|
|
|
57
|
-
if (skillContent
|
|
63
|
+
if (skillContent?.includes(agentId)) {
|
|
58
64
|
skills.push(skillName);
|
|
59
65
|
}
|
|
60
66
|
}
|
package/mesh/mesh-budget.mjs
CHANGED
|
@@ -28,9 +28,11 @@ function clampPercent(value) {
|
|
|
28
28
|
*/
|
|
29
29
|
function classifyLevel(percent) {
|
|
30
30
|
const p = clampPercent(percent);
|
|
31
|
-
if (p >= WARNING_LEVELS.critical)
|
|
31
|
+
if (p >= WARNING_LEVELS.critical)
|
|
32
|
+
return { level: "critical", message: "에이전트 분할 또는 세션 교체 권장" };
|
|
32
33
|
if (p >= WARNING_LEVELS.warn) return { level: "warn", message: "압축 권장" };
|
|
33
|
-
if (p >= WARNING_LEVELS.info)
|
|
34
|
+
if (p >= WARNING_LEVELS.info)
|
|
35
|
+
return { level: "info", message: "컨텍스트 절반 이상 사용" };
|
|
34
36
|
return { level: "ok", message: "" };
|
|
35
37
|
}
|
|
36
38
|
|
|
@@ -78,9 +80,10 @@ export function createMeshBudget() {
|
|
|
78
80
|
budgets.set(agentId, updated);
|
|
79
81
|
|
|
80
82
|
const remaining = Math.max(0, updated.allocated - updated.consumed);
|
|
81
|
-
const percent =
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
const percent =
|
|
84
|
+
updated.allocated > 0
|
|
85
|
+
? clampPercent((updated.consumed / updated.allocated) * 100)
|
|
86
|
+
: 100;
|
|
84
87
|
const { level } = classifyLevel(percent);
|
|
85
88
|
return { remaining, percent, level };
|
|
86
89
|
}
|
|
@@ -96,11 +99,17 @@ export function createMeshBudget() {
|
|
|
96
99
|
return { allocated: 0, consumed: 0, remaining: 0, level: "ok" };
|
|
97
100
|
}
|
|
98
101
|
const remaining = Math.max(0, budget.allocated - budget.consumed);
|
|
99
|
-
const percent =
|
|
100
|
-
|
|
101
|
-
|
|
102
|
+
const percent =
|
|
103
|
+
budget.allocated > 0
|
|
104
|
+
? clampPercent((budget.consumed / budget.allocated) * 100)
|
|
105
|
+
: 0;
|
|
102
106
|
const { level } = classifyLevel(percent);
|
|
103
|
-
return {
|
|
107
|
+
return {
|
|
108
|
+
allocated: budget.allocated,
|
|
109
|
+
consumed: budget.consumed,
|
|
110
|
+
remaining,
|
|
111
|
+
level,
|
|
112
|
+
};
|
|
104
113
|
}
|
|
105
114
|
|
|
106
115
|
/**
|
package/mesh/mesh-heartbeat.mjs
CHANGED
|
@@ -48,7 +48,7 @@ export function createHeartbeatMonitor(registry, opts = {}) {
|
|
|
48
48
|
|
|
49
49
|
for (const agent of registered) {
|
|
50
50
|
const lastBeat = heartbeats.get(agent.agentId);
|
|
51
|
-
if (lastBeat === undefined ||
|
|
51
|
+
if (lastBeat === undefined || now - lastBeat >= threshold) {
|
|
52
52
|
stale.push(agent.agentId);
|
|
53
53
|
}
|
|
54
54
|
}
|
package/mesh/mesh-queue.mjs
CHANGED
|
@@ -10,10 +10,8 @@ const DEFAULT_TTL_MS = 0; // 0 = no expiry
|
|
|
10
10
|
* @returns {object} Queue API
|
|
11
11
|
*/
|
|
12
12
|
export function createMessageQueue(opts = {}) {
|
|
13
|
-
const {
|
|
14
|
-
|
|
15
|
-
ttlMs = DEFAULT_TTL_MS,
|
|
16
|
-
} = opts;
|
|
13
|
+
const { maxQueueSize = DEFAULT_MAX_QUEUE_SIZE, ttlMs = DEFAULT_TTL_MS } =
|
|
14
|
+
opts;
|
|
17
15
|
|
|
18
16
|
/** @type {Map<string, Array<{ message: object, enqueuedAt: number }>>} */
|
|
19
17
|
const queues = new Map();
|
|
@@ -39,7 +37,7 @@ export function createMessageQueue(opts = {}) {
|
|
|
39
37
|
*/
|
|
40
38
|
function purgeExpired(q, now) {
|
|
41
39
|
if (ttlMs <= 0) return;
|
|
42
|
-
while (q.length > 0 &&
|
|
40
|
+
while (q.length > 0 && now - q[0].enqueuedAt > ttlMs) {
|
|
43
41
|
q.shift();
|
|
44
42
|
}
|
|
45
43
|
}
|
package/mesh/mesh-router.mjs
CHANGED
|
@@ -23,9 +23,7 @@ export function routeMessage(message, registry) {
|
|
|
23
23
|
// Broadcast
|
|
24
24
|
if (to === "*") {
|
|
25
25
|
const all = registry.listAll();
|
|
26
|
-
const targets = all
|
|
27
|
-
.map((a) => a.agentId)
|
|
28
|
-
.filter((id) => id !== from);
|
|
26
|
+
const targets = all.map((a) => a.agentId).filter((id) => id !== from);
|
|
29
27
|
if (targets.length === 0) {
|
|
30
28
|
return { routed: false, reason: "broadcast: no agents registered" };
|
|
31
29
|
}
|
|
@@ -40,7 +38,10 @@ export function routeMessage(message, registry) {
|
|
|
40
38
|
}
|
|
41
39
|
const targets = registry.discover(capability);
|
|
42
40
|
if (targets.length === 0) {
|
|
43
|
-
return {
|
|
41
|
+
return {
|
|
42
|
+
routed: false,
|
|
43
|
+
reason: `capability: no agents with "${capability}"`,
|
|
44
|
+
};
|
|
44
45
|
}
|
|
45
46
|
return { routed: true, targets };
|
|
46
47
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "triflux",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.7.0",
|
|
4
4
|
"description": "CLI-first multi-model orchestrator for Claude Code — route tasks to Codex, Gemini, and Claude",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"test:integration": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 tests/integration/**/*.test.mjs",
|
|
51
51
|
"test:route-smoke": "node scripts/test-lock.mjs --test scripts/test-tfx-route-no-claude-native.mjs",
|
|
52
52
|
"test:contract": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 tests/contract/**/*.test.mjs",
|
|
53
|
+
"test:coverage": "node --experimental-test-coverage --test-coverage-lines=60 --test-coverage-functions=60 --test --test-force-exit --test-concurrency=8 \"tests/**/*.test.mjs\"",
|
|
53
54
|
"gen:skill-docs": "node scripts/gen-skill-docs.mjs",
|
|
54
55
|
"gen:skill-manifest": "node scripts/gen-skill-manifest.mjs"
|
|
55
56
|
},
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
mkdtempSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
rmSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
} from "node:fs";
|
|
3
10
|
import { tmpdir } from "node:os";
|
|
4
11
|
import { join } from "node:path";
|
|
5
12
|
import { describe, it } from "node:test";
|
|
@@ -22,7 +29,11 @@ describe("gen-skill-docs", () => {
|
|
|
22
29
|
mkdirSync(templatesDir, { recursive: true });
|
|
23
30
|
mkdirSync(skillDir, { recursive: true });
|
|
24
31
|
|
|
25
|
-
writeFileSync(
|
|
32
|
+
writeFileSync(
|
|
33
|
+
join(templatesDir, "base.md"),
|
|
34
|
+
"BASE {{SKILL_NAME}}",
|
|
35
|
+
"utf8",
|
|
36
|
+
);
|
|
26
37
|
writeFileSync(join(templatesDir, "deep.md"), "DEEP BLOCK", "utf8");
|
|
27
38
|
|
|
28
39
|
writeFileSync(
|
|
@@ -40,7 +51,11 @@ describe("gen-skill-docs", () => {
|
|
|
40
51
|
"utf8",
|
|
41
52
|
);
|
|
42
53
|
|
|
43
|
-
const result = generateSkillDocs({
|
|
54
|
+
const result = generateSkillDocs({
|
|
55
|
+
skillsDir,
|
|
56
|
+
templatesDir,
|
|
57
|
+
write: true,
|
|
58
|
+
});
|
|
44
59
|
assert.equal(result.count, 1);
|
|
45
60
|
|
|
46
61
|
const output = readFileSync(join(skillDir, "SKILL.md"), "utf8");
|
|
@@ -62,7 +77,11 @@ describe("gen-skill-docs", () => {
|
|
|
62
77
|
mkdirSync(skillDir, { recursive: true });
|
|
63
78
|
|
|
64
79
|
writeFileSync(join(templatesDir, "base.md"), "BASE", "utf8");
|
|
65
|
-
writeFileSync(
|
|
80
|
+
writeFileSync(
|
|
81
|
+
join(templatesDir, "deep.md"),
|
|
82
|
+
"DEEP {{SKILL_NAME}}",
|
|
83
|
+
"utf8",
|
|
84
|
+
);
|
|
66
85
|
writeFileSync(
|
|
67
86
|
join(skillDir, "SKILL.md.tmpl"),
|
|
68
87
|
[
|
|
@@ -99,14 +118,22 @@ describe("gen-skill-docs", () => {
|
|
|
99
118
|
mkdirSync(skillDir, { recursive: true });
|
|
100
119
|
|
|
101
120
|
writeFileSync(join(sharedDir, "args.md"), "ARGS={{SKILL_NAME}}", "utf8");
|
|
102
|
-
writeFileSync(
|
|
121
|
+
writeFileSync(
|
|
122
|
+
join(templatesDir, "base.md"),
|
|
123
|
+
"{{#include shared/args.md}}",
|
|
124
|
+
"utf8",
|
|
125
|
+
);
|
|
103
126
|
writeFileSync(
|
|
104
127
|
join(skillDir, "SKILL.md.tmpl"),
|
|
105
128
|
"---\nname: tfx-inc\ndescription: inc test\n---\n{{> base}}\nend",
|
|
106
129
|
"utf8",
|
|
107
130
|
);
|
|
108
131
|
|
|
109
|
-
const result = generateSkillDocs({
|
|
132
|
+
const result = generateSkillDocs({
|
|
133
|
+
skillsDir,
|
|
134
|
+
templatesDir,
|
|
135
|
+
write: true,
|
|
136
|
+
});
|
|
110
137
|
assert.equal(result.count, 1);
|
|
111
138
|
|
|
112
139
|
const output = readFileSync(join(skillDir, "SKILL.md"), "utf8");
|
|
@@ -145,7 +172,9 @@ describe("gen-skill-manifest", () => {
|
|
|
145
172
|
const result = generateSkillManifests({ skillsDir, write: true });
|
|
146
173
|
assert.equal(result.count, 1);
|
|
147
174
|
|
|
148
|
-
const manifest = JSON.parse(
|
|
175
|
+
const manifest = JSON.parse(
|
|
176
|
+
readFileSync(join(skillDir, "skill.json"), "utf8"),
|
|
177
|
+
);
|
|
149
178
|
assert.equal(manifest.name, "tfx-manifest-test");
|
|
150
179
|
assert.equal(manifest.description, "test manifest");
|
|
151
180
|
assert.deepEqual(manifest.triggers, ["test", "manifest"]);
|