triflux 10.0.0 → 10.0.2
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/CLAUDE.md +171 -0
- package/README.md +32 -15
- package/bin/triflux.mjs +62 -5
- package/hooks/agent-route-guard.mjs +109 -0
- package/hooks/cross-review-tracker.mjs +122 -0
- package/hooks/error-context.mjs +148 -0
- package/hooks/hook-adaptive-collector.mjs +86 -0
- package/hooks/hook-manager.mjs +365 -0
- package/hooks/hook-orchestrator.mjs +312 -0
- package/hooks/hook-registry.json +246 -0
- package/hooks/hooks.json +89 -0
- package/hooks/keyword-rules.json +574 -0
- package/hooks/lib/resolve-root.mjs +59 -0
- package/hooks/mcp-config-watcher.mjs +80 -0
- package/hooks/pipeline-stop.mjs +76 -0
- package/hooks/safety-guard.mjs +169 -0
- package/hooks/subagent-verifier.mjs +80 -0
- package/hub/account-broker.mjs +251 -0
- package/hub/adaptive-diagnostic.mjs +323 -0
- package/hub/adaptive-inject.mjs +186 -0
- package/hub/adaptive-memory.mjs +163 -0
- package/hub/adaptive.mjs +143 -0
- package/hub/assign-callbacks.mjs +133 -0
- package/hub/bridge.mjs +799 -0
- package/hub/cli-adapter-base.mjs +280 -0
- package/hub/codex-adapter.mjs +199 -0
- package/hub/codex-compat.mjs +11 -0
- package/hub/codex-preflight.mjs +166 -0
- package/hub/delegator/contracts.mjs +37 -0
- package/hub/delegator/index.mjs +14 -0
- package/hub/delegator/schema/delegator-tools.schema.json +250 -0
- package/hub/delegator/service.mjs +307 -0
- package/hub/delegator/tool-definitions.mjs +35 -0
- package/hub/fullcycle.mjs +96 -0
- package/hub/gemini-adapter.mjs +180 -0
- package/hub/hitl.mjs +143 -0
- package/hub/intent.mjs +193 -0
- package/hub/lib/cache-guard.mjs +114 -0
- package/hub/lib/known-errors.json +72 -0
- package/hub/lib/memory-store.mjs +748 -0
- package/hub/lib/process-utils.mjs +361 -0
- package/hub/lib/ssh-command.mjs +211 -0
- package/hub/lib/ssh-retry.mjs +59 -0
- package/hub/lib/uuidv7.mjs +44 -0
- package/hub/memory-doctor.mjs +480 -0
- package/hub/middleware/request-logger.mjs +161 -0
- package/hub/paths.mjs +30 -0
- package/hub/pipe.mjs +664 -0
- package/hub/pipeline/gates/confidence.mjs +56 -0
- package/hub/pipeline/gates/consensus.mjs +94 -0
- package/hub/pipeline/gates/index.mjs +5 -0
- package/hub/pipeline/gates/selfcheck.mjs +82 -0
- package/hub/pipeline/index.mjs +318 -0
- package/hub/pipeline/state.mjs +191 -0
- package/hub/pipeline/transitions.mjs +124 -0
- package/hub/platform.mjs +225 -0
- package/hub/public/dashboard.html +355 -0
- package/hub/public/tray-icon.ico +0 -0
- package/hub/public/tray-icon.png +0 -0
- package/hub/quality/deslop.mjs +253 -0
- package/hub/reflexion.mjs +372 -0
- package/hub/research.mjs +146 -0
- package/hub/router.mjs +791 -0
- package/hub/routing/complexity.mjs +166 -0
- package/hub/routing/index.mjs +117 -0
- package/hub/routing/q-learning.mjs +336 -0
- package/hub/schema.sql +148 -0
- package/hub/server.mjs +1264 -0
- package/hub/session-fingerprint.mjs +352 -0
- package/hub/state.mjs +258 -0
- package/hub/store-adapter.mjs +118 -0
- package/hub/store.mjs +857 -0
- package/hub/team/agent-map.json +11 -0
- package/hub/team/ansi.mjs +379 -0
- package/hub/team/backend.mjs +90 -0
- package/hub/team/cli/commands/attach.mjs +37 -0
- package/hub/team/cli/commands/control.mjs +43 -0
- package/hub/team/cli/commands/debug.mjs +74 -0
- package/hub/team/cli/commands/focus.mjs +53 -0
- package/hub/team/cli/commands/interrupt.mjs +36 -0
- package/hub/team/cli/commands/kill.mjs +37 -0
- package/hub/team/cli/commands/list.mjs +24 -0
- package/hub/team/cli/commands/send.mjs +37 -0
- package/hub/team/cli/commands/start/index.mjs +106 -0
- package/hub/team/cli/commands/start/parse-args.mjs +130 -0
- package/hub/team/cli/commands/start/start-headless.mjs +109 -0
- package/hub/team/cli/commands/start/start-in-process.mjs +40 -0
- package/hub/team/cli/commands/start/start-mux.mjs +73 -0
- package/hub/team/cli/commands/start/start-wt.mjs +69 -0
- package/hub/team/cli/commands/status.mjs +87 -0
- package/hub/team/cli/commands/stop.mjs +31 -0
- package/hub/team/cli/commands/task.mjs +30 -0
- package/hub/team/cli/commands/tasks.mjs +13 -0
- package/hub/team/cli/help.mjs +42 -0
- package/hub/team/cli/index.mjs +41 -0
- package/hub/team/cli/manifest.mjs +29 -0
- package/hub/team/cli/render.mjs +30 -0
- package/hub/team/cli/services/attach-fallback.mjs +54 -0
- package/hub/team/cli/services/hub-client.mjs +227 -0
- package/hub/team/cli/services/member-selector.mjs +30 -0
- package/hub/team/cli/services/native-control.mjs +117 -0
- package/hub/team/cli/services/runtime-mode.mjs +62 -0
- package/hub/team/cli/services/state-store.mjs +48 -0
- package/hub/team/cli/services/task-model.mjs +30 -0
- package/hub/team/conductor-mesh-bridge.mjs +121 -0
- package/hub/team/conductor.mjs +671 -0
- package/hub/team/dashboard-anchor.mjs +14 -0
- package/hub/team/dashboard-layout.mjs +33 -0
- package/hub/team/dashboard-open.mjs +153 -0
- package/hub/team/dashboard.mjs +274 -0
- package/hub/team/event-log.mjs +76 -0
- package/hub/team/handoff.mjs +303 -0
- package/hub/team/headless.mjs +1156 -0
- package/hub/team/health-probe.mjs +272 -0
- package/hub/team/launcher-template.mjs +95 -0
- package/hub/team/lead-control.mjs +104 -0
- package/hub/team/native-supervisor.mjs +392 -0
- package/hub/team/native.mjs +649 -0
- package/hub/team/nativeProxy.mjs +688 -0
- package/hub/team/notify.mjs +293 -0
- package/hub/team/orchestrator.mjs +161 -0
- package/hub/team/pane.mjs +153 -0
- package/hub/team/process-cleanup.mjs +342 -0
- package/hub/team/psmux.mjs +1354 -0
- package/hub/team/remote-probe.mjs +276 -0
- package/hub/team/remote-session.mjs +299 -0
- package/hub/team/remote-watcher.mjs +478 -0
- package/hub/team/routing.mjs +223 -0
- package/hub/team/session-sync.mjs +169 -0
- package/hub/team/session.mjs +611 -0
- package/hub/team/shared.mjs +13 -0
- package/hub/team/staleState.mjs +361 -0
- package/hub/team/swarm-hypervisor.mjs +589 -0
- package/hub/team/swarm-locks.mjs +204 -0
- package/hub/team/swarm-planner.mjs +260 -0
- package/hub/team/swarm-reconciler.mjs +137 -0
- package/hub/team/tui-lite.mjs +380 -0
- package/hub/team/tui-remote-adapter.mjs +393 -0
- package/hub/team/tui-viewer.mjs +463 -0
- package/hub/team/tui.mjs +1449 -0
- package/hub/team/worktree-lifecycle.mjs +193 -0
- package/hub/team/wt-manager.mjs +407 -0
- package/hub/team/wt-templates.json +43 -0
- package/hub/team-bridge.mjs +27 -0
- package/hub/token-mode.mjs +224 -0
- package/hub/tools.mjs +636 -0
- package/hub/tray.mjs +376 -0
- package/hub/workers/claude-worker.mjs +475 -0
- package/hub/workers/codex-mcp.mjs +507 -0
- package/hub/workers/delegator-mcp.mjs +1076 -0
- package/hub/workers/factory.mjs +21 -0
- package/hub/workers/gemini-worker.mjs +374 -0
- package/hub/workers/interface.mjs +52 -0
- package/hub/workers/worker-utils.mjs +104 -0
- package/hud/colors.mjs +88 -0
- package/hud/constants.mjs +88 -0
- package/hud/context-monitor.mjs +403 -0
- package/hud/hud-qos-status.mjs +210 -0
- package/hud/providers/claude.mjs +314 -0
- package/hud/providers/codex.mjs +151 -0
- package/hud/providers/gemini.mjs +320 -0
- package/hud/renderers.mjs +442 -0
- package/hud/terminal.mjs +140 -0
- package/hud/utils.mjs +313 -0
- package/mesh/index.mjs +63 -0
- package/mesh/mesh-budget.mjs +128 -0
- package/mesh/mesh-heartbeat.mjs +100 -0
- package/mesh/mesh-protocol.mjs +96 -0
- package/mesh/mesh-queue.mjs +165 -0
- package/mesh/mesh-registry.mjs +78 -0
- package/mesh/mesh-router.mjs +76 -0
- package/package.json +8 -1
- package/references/hosts.json +33 -0
- package/scripts/__tests__/gen-skill-docs.test.mjs +87 -0
- package/scripts/__tests__/keyword-detector.test.mjs +234 -0
- package/scripts/__tests__/mcp-guard-engine.test.mjs +118 -0
- package/scripts/__tests__/remote-spawn-transfer.test.mjs +117 -0
- package/scripts/__tests__/remote-spawn.test.mjs +92 -0
- package/scripts/__tests__/skill-template.test.mjs +193 -0
- package/scripts/__tests__/smoke.test.mjs +34 -0
- package/scripts/cache-buildup.mjs +30 -0
- package/scripts/cache-doctor.mjs +149 -0
- package/scripts/cache-warmup.mjs +557 -0
- package/scripts/claudemd-sync.mjs +148 -0
- package/scripts/cli-route.sh +3 -0
- package/scripts/completions/tfx.bash +47 -0
- package/scripts/completions/tfx.fish +44 -0
- package/scripts/completions/tfx.zsh +83 -0
- package/scripts/cross-review-gate.mjs +126 -0
- package/scripts/cross-review-tracker.mjs +238 -0
- package/scripts/gen-skill-docs.mjs +111 -0
- package/scripts/headless-guard-fast.sh +21 -0
- package/scripts/headless-guard.mjs +360 -0
- package/scripts/hub-ensure.mjs +120 -0
- package/scripts/keyword-detector.mjs +272 -0
- package/scripts/keyword-rules-expander.mjs +521 -0
- package/scripts/lib/claudemd-scanner.mjs +218 -0
- package/scripts/lib/context.mjs +67 -0
- package/scripts/lib/cross-review-utils.mjs +51 -0
- package/scripts/lib/env-probe.mjs +241 -0
- package/scripts/lib/gemini-profiles.mjs +85 -0
- package/scripts/lib/handoff.mjs +171 -0
- package/scripts/lib/hook-utils.mjs +14 -0
- package/scripts/lib/keyword-rules.mjs +166 -0
- package/scripts/lib/logger.mjs +105 -0
- package/scripts/lib/mcp-filter.mjs +739 -0
- package/scripts/lib/mcp-guard-engine.mjs +954 -0
- package/scripts/lib/mcp-manifest.mjs +79 -0
- package/scripts/lib/mcp-server-catalog.mjs +118 -0
- package/scripts/lib/psmux-info.mjs +119 -0
- package/scripts/lib/remote-spawn-transfer.mjs +196 -0
- package/scripts/lib/skill-template.mjs +326 -0
- package/scripts/mcp-check.mjs +237 -0
- package/scripts/mcp-cleanup.ps1 +17 -0
- package/scripts/mcp-gateway-config.mjs +207 -0
- package/scripts/mcp-gateway-ensure.mjs +85 -0
- package/scripts/mcp-gateway-integration-test.mjs +228 -0
- package/scripts/mcp-gateway-start.mjs +226 -0
- package/scripts/mcp-gateway-start.ps1 +141 -0
- package/scripts/mcp-gateway-verify.mjs +77 -0
- package/scripts/mcp-safety-guard.mjs +44 -0
- package/scripts/notion-read.mjs +556 -0
- package/scripts/pack.mjs +295 -0
- package/scripts/preflight-cache.mjs +69 -0
- package/scripts/preinstall.mjs +96 -0
- package/scripts/remote-spawn.mjs +1376 -0
- package/scripts/run.cjs +79 -0
- package/scripts/session-spawn-helper.mjs +185 -0
- package/scripts/setup.mjs +1178 -0
- package/scripts/test-lock.mjs +71 -0
- package/scripts/test-tfx-route-no-claude-native.mjs +57 -0
- package/scripts/tfx-batch-stats.mjs +96 -0
- package/scripts/tfx-gate-activate.mjs +89 -0
- package/scripts/tfx-route-post.mjs +505 -0
- package/scripts/tfx-route-worker.mjs +223 -0
- package/scripts/tfx-route.sh +2014 -0
- package/scripts/tmp-cleanup.mjs +103 -0
- package/scripts/token-snapshot.mjs +575 -0
- package/skills/tfx-auto/SKILL.md.tmpl +2 -3
- package/skills/tfx-autoresearch/SKILL.md +6 -5
- package/skills/tfx-codex/SKILL.md.tmpl +2 -3
- package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +33 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/eval_metadata.json +42 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +11 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/outputs/analysis.md +87 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/outputs/classification.md +35 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/outputs/commands.sh +275 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/outputs/routing.md +56 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/timing.json +5 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +11 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/outputs/analysis.md +92 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/outputs/classification.md +71 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/outputs/commands.sh +264 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/outputs/routing.md +113 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/timing.json +5 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/eval_metadata.json +32 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +9 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/outputs/analysis.md +96 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/outputs/classification.md +38 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/outputs/commands.sh +151 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/outputs/routing.md +51 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/timing.json +5 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +9 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/outputs/analysis.md +127 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/outputs/classification.md +57 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/outputs/commands.sh +129 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/outputs/routing.md +84 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/timing.json +5 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/eval_metadata.json +27 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +8 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/outputs/analysis.md +98 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/outputs/classification.md +65 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/outputs/commands.sh +123 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/outputs/routing.md +66 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/timing.json +5 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +8 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/outputs/analysis.md +88 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/outputs/classification.md +40 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/outputs/commands.sh +130 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/outputs/routing.md +61 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/timing.json +5 -0
- package/skills/tfx-deep-interview/SKILL.md +1 -2
- package/skills/tfx-plan/SKILL.md.tmpl +2 -3
- package/skills/tfx-psmux-rules/SKILL.md +11 -2
- package/skills/tfx-qa/SKILL.md.tmpl +2 -3
- package/skills/tfx-remote-spawn/SKILL.md +8 -11
- package/skills/tfx-research/SKILL.md.tmpl +2 -3
- package/skills/tfx-review/SKILL.md.tmpl +2 -3
- package/skills/tfx-workspace/async-tests/run-tests.sh +203 -0
- package/skills/tfx-workspace/evals/evals.json +79 -0
- package/skills/tfx-workspace/iteration-1/benchmark.json +162 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +11 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +9 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +154 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +9 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +126 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +11 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +9 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +119 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +9 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +115 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +10 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +8 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +86 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +8 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +81 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +12 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +316 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +352 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/review.html +1325 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +12 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +97 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +94 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +12 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +209 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +10 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +193 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-2/benchmark.json +62 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +13 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +11 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +382 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +11 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +333 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-2/review.html +1325 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +217 -0
- package/skills/{tfx-auto-codex/SKILL.md.tmpl → tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md} +3 -31
- package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +65 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +94 -0
- package/skills/{tfx-gemini/SKILL.md.tmpl → tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md} +6 -14
- package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +133 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +426 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +101 -0
- package/skills/merge-worktree/SKILL.md.tmpl +0 -144
- package/skills/shared/arguments-processing.md +0 -2
- package/skills/shared/mandatory-rules.md +0 -6
- package/skills/shared/telemetry-segment.md +0 -6
- package/skills/star-prompt/SKILL.md.tmpl +0 -122
- package/skills/tfx-analysis/SKILL.md.tmpl +0 -106
- package/skills/tfx-analysis/skill.json +0 -11
- package/skills/tfx-auto/skill.json +0 -26
- package/skills/tfx-auto-codex/skill.json +0 -8
- package/skills/tfx-autopilot/SKILL.md.tmpl +0 -115
- package/skills/tfx-autopilot/skill.json +0 -10
- package/skills/tfx-autoresearch/SKILL.md.tmpl +0 -135
- package/skills/tfx-autoresearch/skill.json +0 -14
- package/skills/tfx-autoroute/SKILL.md.tmpl +0 -188
- package/skills/tfx-autoroute/skill.json +0 -12
- package/skills/tfx-codex/skill.json +0 -8
- package/skills/tfx-codex-swarm/SKILL.md.tmpl +0 -16
- package/skills/tfx-codex-swarm/skill.json +0 -5
- package/skills/tfx-consensus/SKILL.md.tmpl +0 -145
- package/skills/tfx-consensus/skill.json +0 -8
- package/skills/tfx-debate/SKILL.md.tmpl +0 -191
- package/skills/tfx-debate/skill.json +0 -12
- package/skills/tfx-deep-analysis/SKILL.md.tmpl +0 -227
- package/skills/tfx-deep-analysis/skill.json +0 -10
- package/skills/tfx-deep-interview/SKILL.md.tmpl +0 -203
- package/skills/tfx-deep-interview/skill.json +0 -12
- package/skills/tfx-deep-plan/SKILL.md.tmpl +0 -281
- package/skills/tfx-deep-plan/skill.json +0 -13
- package/skills/tfx-deep-qa/SKILL.md.tmpl +0 -164
- package/skills/tfx-deep-qa/skill.json +0 -11
- package/skills/tfx-deep-research/SKILL.md.tmpl +0 -216
- package/skills/tfx-deep-research/skill.json +0 -14
- package/skills/tfx-deep-review/SKILL.md.tmpl +0 -178
- package/skills/tfx-deep-review/skill.json +0 -12
- package/skills/tfx-doctor/SKILL.md.tmpl +0 -172
- package/skills/tfx-doctor/skill.json +0 -8
- package/skills/tfx-find/skill.json +0 -12
- package/skills/tfx-forge/SKILL.md.tmpl +0 -187
- package/skills/tfx-forge/skill.json +0 -12
- package/skills/tfx-fullcycle/SKILL.md.tmpl +0 -285
- package/skills/tfx-fullcycle/skill.json +0 -11
- package/skills/tfx-gemini/skill.json +0 -8
- package/skills/tfx-hooks/SKILL.md.tmpl +0 -216
- package/skills/tfx-hooks/skill.json +0 -8
- package/skills/tfx-hub/SKILL.md.tmpl +0 -212
- package/skills/tfx-hub/skill.json +0 -8
- package/skills/tfx-index/skill.json +0 -11
- package/skills/tfx-interview/SKILL.md.tmpl +0 -284
- package/skills/tfx-interview/skill.json +0 -12
- package/skills/tfx-multi/SKILL.md.tmpl +0 -183
- package/skills/tfx-multi/skill.json +0 -8
- package/skills/tfx-panel/SKILL.md.tmpl +0 -188
- package/skills/tfx-panel/skill.json +0 -12
- package/skills/tfx-persist/SKILL.md.tmpl +0 -269
- package/skills/tfx-persist/skill.json +0 -12
- package/skills/tfx-plan/skill.json +0 -11
- package/skills/tfx-profile/SKILL.md.tmpl +0 -239
- package/skills/tfx-profile/skill.json +0 -8
- package/skills/tfx-prune/SKILL.md.tmpl +0 -199
- package/skills/tfx-prune/skill.json +0 -12
- package/skills/tfx-psmux-rules/SKILL.md.tmpl +0 -317
- package/skills/tfx-psmux-rules/skill.json +0 -8
- package/skills/tfx-qa/skill.json +0 -11
- package/skills/tfx-ralph/SKILL.md.tmpl +0 -27
- package/skills/tfx-ralph/skill.json +0 -8
- package/skills/tfx-remote-setup/SKILL.md.tmpl +0 -576
- package/skills/tfx-remote-setup/skill.json +0 -8
- package/skills/tfx-remote-spawn/SKILL.md.tmpl +0 -263
- package/skills/tfx-remote-spawn/skill.json +0 -9
- package/skills/tfx-research/skill.json +0 -13
- package/skills/tfx-review/skill.json +0 -11
- package/skills/tfx-setup/SKILL.md.tmpl +0 -380
- package/skills/tfx-setup/skill.json +0 -8
- package/skills/tfx-swarm/SKILL.md.tmpl +0 -154
- package/skills/tfx-swarm/skill.json +0 -5
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { TFX_START, TFX_END, OMC_END, writeSection } from "./lib/claudemd-scanner.mjs";
|
|
5
|
+
|
|
6
|
+
const PROJECT_ROOT = fileURLToPath(new URL("..", import.meta.url));
|
|
7
|
+
const PROJECT_CLAUDE_MD_PATH = join(PROJECT_ROOT, "CLAUDE.md");
|
|
8
|
+
const ROUTING_TAG_OPEN = "<routing>";
|
|
9
|
+
const ROUTING_TAG_CLOSE = "</routing>";
|
|
10
|
+
// Legacy heading fallback
|
|
11
|
+
const ROUTING_SECTION_HEADING = "## triflux CLI 라우팅";
|
|
12
|
+
|
|
13
|
+
function findRoutingSection(markdown) {
|
|
14
|
+
const content = String(markdown || "");
|
|
15
|
+
|
|
16
|
+
// XML 태그 기반 (우선)
|
|
17
|
+
const openIdx = content.indexOf(ROUTING_TAG_OPEN);
|
|
18
|
+
const closeIdx = content.indexOf(ROUTING_TAG_CLOSE);
|
|
19
|
+
if (openIdx !== -1 && closeIdx !== -1 && closeIdx > openIdx) {
|
|
20
|
+
const endIndex = closeIdx + ROUTING_TAG_CLOSE.length;
|
|
21
|
+
return {
|
|
22
|
+
found: true,
|
|
23
|
+
startIndex: openIdx,
|
|
24
|
+
endIndex: content[endIndex] === "\n" ? endIndex + 1 : endIndex,
|
|
25
|
+
section: content.slice(openIdx, endIndex),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Legacy heading fallback
|
|
30
|
+
const headingPattern = new RegExp(`(^|\\n)${ROUTING_SECTION_HEADING.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}(?=\\n|$)`, "u");
|
|
31
|
+
const match = headingPattern.exec(content);
|
|
32
|
+
|
|
33
|
+
if (!match) {
|
|
34
|
+
return { found: false, startIndex: -1, endIndex: -1, section: "" };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const startIndex = match.index + match[1].length;
|
|
38
|
+
const nextHeadingIndex = content.indexOf("\n## ", startIndex + ROUTING_SECTION_HEADING.length);
|
|
39
|
+
const endIndex = nextHeadingIndex === -1 ? content.length : nextHeadingIndex + 1;
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
found: true,
|
|
43
|
+
startIndex,
|
|
44
|
+
endIndex,
|
|
45
|
+
section: content.slice(startIndex, endIndex),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizeRoutingSection(routingTable) {
|
|
50
|
+
const section = String(routingTable || "").trim();
|
|
51
|
+
return section ? `${section}\n` : "";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function buildNextMarkdown(currentMarkdown, routingSection) {
|
|
55
|
+
const current = String(currentMarkdown || "");
|
|
56
|
+
const nextSection = normalizeRoutingSection(routingSection);
|
|
57
|
+
const existing = findRoutingSection(current);
|
|
58
|
+
|
|
59
|
+
if (existing.found) {
|
|
60
|
+
return `${current.slice(0, existing.startIndex)}${nextSection}${current.slice(existing.endIndex)}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!current) {
|
|
64
|
+
return nextSection;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const separator = current.endsWith("\n\n") ? "" : current.endsWith("\n") ? "\n" : "\n\n";
|
|
68
|
+
return `${current}${separator}${nextSection}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function toSkippedResult(path, reason) {
|
|
72
|
+
return { action: "unchanged", path, skipped: true, reason };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function getLatestRoutingTable() {
|
|
76
|
+
if (!existsSync(PROJECT_CLAUDE_MD_PATH)) {
|
|
77
|
+
throw new Error(`project CLAUDE.md not found: ${PROJECT_CLAUDE_MD_PATH}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const projectMarkdown = readFileSync(PROJECT_CLAUDE_MD_PATH, "utf8");
|
|
81
|
+
const section = findRoutingSection(projectMarkdown);
|
|
82
|
+
|
|
83
|
+
if (!section.found) {
|
|
84
|
+
throw new Error(`routing section not found in: ${PROJECT_CLAUDE_MD_PATH}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return section.section.trim();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function ensureTfxSection(claudeMdPath, routingTable) {
|
|
91
|
+
if (!existsSync(claudeMdPath)) {
|
|
92
|
+
return toSkippedResult(claudeMdPath, "missing_file");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const currentMarkdown = readFileSync(claudeMdPath, "utf8");
|
|
96
|
+
const nextMarkdown = buildNextMarkdown(currentMarkdown, routingTable);
|
|
97
|
+
|
|
98
|
+
if (nextMarkdown === currentMarkdown) {
|
|
99
|
+
return { action: "unchanged", path: claudeMdPath };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
writeFileSync(claudeMdPath, nextMarkdown, "utf8");
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
action: findRoutingSection(currentMarkdown).found ? "updated" : "created",
|
|
106
|
+
path: claudeMdPath,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function ensureTfxCrown(claudeMdPath, options = {}) {
|
|
111
|
+
const absolutePath = resolve(claudeMdPath);
|
|
112
|
+
if (!existsSync(absolutePath)) {
|
|
113
|
+
return toSkippedResult(absolutePath, "missing_file");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const content = readFileSync(absolutePath, "utf8");
|
|
117
|
+
const startIdx = content.indexOf(TFX_START);
|
|
118
|
+
const omcEndIdx = content.indexOf(OMC_END);
|
|
119
|
+
|
|
120
|
+
if (startIdx === -1) {
|
|
121
|
+
const result = writeSection(absolutePath, options);
|
|
122
|
+
return { action: result.action, path: absolutePath };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const expectedPos = omcEndIdx !== -1 ? omcEndIdx + OMC_END.length : 0;
|
|
126
|
+
const textBefore = content.slice(expectedPos, startIdx).trim();
|
|
127
|
+
|
|
128
|
+
if (textBefore.length === 0) {
|
|
129
|
+
return { action: "unchanged", path: absolutePath };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const result = writeSection(absolutePath, options);
|
|
133
|
+
return { action: "repositioned", path: absolutePath, detail: result.action };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function ensureGlobalClaudeRoutingSection(claudeDir) {
|
|
137
|
+
const claudeMdPath = join(claudeDir, "CLAUDE.md");
|
|
138
|
+
if (!existsSync(claudeMdPath)) {
|
|
139
|
+
return toSkippedResult(claudeMdPath, "missing_file");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
return ensureTfxSection(claudeMdPath, getLatestRoutingTable());
|
|
144
|
+
} catch (error) {
|
|
145
|
+
const reason = error instanceof Error ? error.message : "routing_table_unavailable";
|
|
146
|
+
return toSkippedResult(claudeMdPath, reason);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Installation: source /path/to/tfx.bash 또는 ~/.bashrc에 추가
|
|
3
|
+
|
|
4
|
+
_tfx_completion() {
|
|
5
|
+
local cur prev words cword
|
|
6
|
+
COMPREPLY=()
|
|
7
|
+
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
8
|
+
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
9
|
+
words=("${COMP_WORDS[@]}")
|
|
10
|
+
cword=$COMP_CWORD
|
|
11
|
+
|
|
12
|
+
local commands="setup doctor multi hub auto codex gemini"
|
|
13
|
+
local multi_cmds="status stop kill attach list"
|
|
14
|
+
local hub_cmds="start stop status restart"
|
|
15
|
+
local flags="--thorough --quick --tmux --psmux --agents --no-attach --timeout"
|
|
16
|
+
|
|
17
|
+
if [[ $cword -eq 1 ]]; then
|
|
18
|
+
COMPREPLY=( $(compgen -W "${commands}" -- "$cur") )
|
|
19
|
+
return 0
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
local cmd="${words[1]}"
|
|
23
|
+
case "${cmd}" in
|
|
24
|
+
multi)
|
|
25
|
+
if [[ $cword -eq 2 && ! "$cur" == -* ]]; then
|
|
26
|
+
COMPREPLY=( $(compgen -W "${multi_cmds}" -- "$cur") )
|
|
27
|
+
else
|
|
28
|
+
COMPREPLY=( $(compgen -W "${flags}" -- "$cur") )
|
|
29
|
+
fi
|
|
30
|
+
;;
|
|
31
|
+
hub)
|
|
32
|
+
if [[ $cword -eq 2 ]]; then
|
|
33
|
+
COMPREPLY=( $(compgen -W "${hub_cmds}" -- "$cur") )
|
|
34
|
+
fi
|
|
35
|
+
;;
|
|
36
|
+
doctor)
|
|
37
|
+
COMPREPLY=( $(compgen -W "--fix --reset" -- "$cur") )
|
|
38
|
+
;;
|
|
39
|
+
setup|auto|codex|gemini)
|
|
40
|
+
if [[ "$cur" == -* ]]; then
|
|
41
|
+
COMPREPLY=( $(compgen -W "${flags}" -- "$cur") )
|
|
42
|
+
fi
|
|
43
|
+
;;
|
|
44
|
+
esac
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
complete -F _tfx_completion tfx
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Installation: ~/.config/fish/completions/에 복사
|
|
2
|
+
# e.g., cp /path/to/tfx.fish ~/.config/fish/completions/tfx.fish
|
|
3
|
+
|
|
4
|
+
set -l commands setup doctor multi hub auto codex gemini
|
|
5
|
+
set -l multi_cmds status stop kill attach list
|
|
6
|
+
set -l hub_cmds start stop status restart
|
|
7
|
+
|
|
8
|
+
complete -c tfx -f
|
|
9
|
+
|
|
10
|
+
# Subcommands
|
|
11
|
+
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "setup" -d "Setup and sync files"
|
|
12
|
+
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "doctor" -d "Diagnose CLI and issues"
|
|
13
|
+
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "multi" -d "Multi-CLI team mode"
|
|
14
|
+
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "hub" -d "MCP message bus management"
|
|
15
|
+
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "auto" -d "Auto mode"
|
|
16
|
+
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "codex" -d "Codex mode"
|
|
17
|
+
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "gemini" -d "Gemini mode"
|
|
18
|
+
|
|
19
|
+
# Doctor flags
|
|
20
|
+
complete -c tfx -n "__fish_seen_subcommand_from doctor" -l fix -d "Auto fix issues"
|
|
21
|
+
complete -c tfx -n "__fish_seen_subcommand_from doctor" -l reset -d "Reset all caches"
|
|
22
|
+
|
|
23
|
+
# Multi subcommands
|
|
24
|
+
complete -c tfx -n "__fish_seen_subcommand_from multi; and not __fish_seen_subcommand_from $multi_cmds" -a "status"
|
|
25
|
+
complete -c tfx -n "__fish_seen_subcommand_from multi; and not __fish_seen_subcommand_from $multi_cmds" -a "stop"
|
|
26
|
+
complete -c tfx -n "__fish_seen_subcommand_from multi; and not __fish_seen_subcommand_from $multi_cmds" -a "kill"
|
|
27
|
+
complete -c tfx -n "__fish_seen_subcommand_from multi; and not __fish_seen_subcommand_from $multi_cmds" -a "attach"
|
|
28
|
+
complete -c tfx -n "__fish_seen_subcommand_from multi; and not __fish_seen_subcommand_from $multi_cmds" -a "list"
|
|
29
|
+
|
|
30
|
+
# Hub subcommands
|
|
31
|
+
complete -c tfx -n "__fish_seen_subcommand_from hub; and not __fish_seen_subcommand_from $hub_cmds" -a "start"
|
|
32
|
+
complete -c tfx -n "__fish_seen_subcommand_from hub; and not __fish_seen_subcommand_from $hub_cmds" -a "stop"
|
|
33
|
+
complete -c tfx -n "__fish_seen_subcommand_from hub; and not __fish_seen_subcommand_from $hub_cmds" -a "status"
|
|
34
|
+
complete -c tfx -n "__fish_seen_subcommand_from hub; and not __fish_seen_subcommand_from $hub_cmds" -a "restart"
|
|
35
|
+
|
|
36
|
+
# Global or multi flags
|
|
37
|
+
set -l flags_cond "__fish_seen_subcommand_from setup multi auto codex gemini"
|
|
38
|
+
complete -c tfx -n "$flags_cond" -l thorough -d "Thorough execution"
|
|
39
|
+
complete -c tfx -n "$flags_cond" -l quick -d "Quick execution"
|
|
40
|
+
complete -c tfx -n "$flags_cond" -l tmux -d "Use tmux"
|
|
41
|
+
complete -c tfx -n "$flags_cond" -l psmux -d "Use psmux"
|
|
42
|
+
complete -c tfx -n "$flags_cond" -l agents -d "Specify agents"
|
|
43
|
+
complete -c tfx -n "$flags_cond" -l no-attach -d "Do not attach"
|
|
44
|
+
complete -c tfx -n "$flags_cond" -l timeout -d "Set timeout"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#compdef tfx
|
|
2
|
+
# Installation: fpath에 추가 후 compinit
|
|
3
|
+
# e.g., fpath=(/path/to/dir $fpath) && compinit
|
|
4
|
+
|
|
5
|
+
_tfx() {
|
|
6
|
+
local line state
|
|
7
|
+
local -a commands multi_cmds hub_cmds flags
|
|
8
|
+
|
|
9
|
+
commands=(
|
|
10
|
+
'setup:Setup and sync files'
|
|
11
|
+
'doctor:Diagnose CLI and issues'
|
|
12
|
+
'multi:Multi-CLI team mode'
|
|
13
|
+
'hub:MCP message bus management'
|
|
14
|
+
'auto:Auto mode'
|
|
15
|
+
'codex:Codex mode'
|
|
16
|
+
'gemini:Gemini mode'
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
multi_cmds=(
|
|
20
|
+
'status:Show status'
|
|
21
|
+
'stop:Stop multi'
|
|
22
|
+
'kill:Kill multi'
|
|
23
|
+
'attach:Attach to multi'
|
|
24
|
+
'list:List multi sessions'
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
hub_cmds=(
|
|
28
|
+
'start:Start hub'
|
|
29
|
+
'stop:Stop hub'
|
|
30
|
+
'status:Show hub status'
|
|
31
|
+
'restart:Restart hub'
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
_arguments -C \
|
|
35
|
+
'1: :->cmds' \
|
|
36
|
+
'*: :->args'
|
|
37
|
+
|
|
38
|
+
case $state in
|
|
39
|
+
cmds)
|
|
40
|
+
_describe -t commands 'tfx commands' commands
|
|
41
|
+
;;
|
|
42
|
+
args)
|
|
43
|
+
case $words[2] in
|
|
44
|
+
multi)
|
|
45
|
+
if (( CURRENT == 3 )) && [[ $words[CURRENT] != -* ]]; then
|
|
46
|
+
_describe -t multi_cmds 'multi commands' multi_cmds
|
|
47
|
+
else
|
|
48
|
+
_arguments \
|
|
49
|
+
'--thorough[Thorough execution]' \
|
|
50
|
+
'--quick[Quick execution]' \
|
|
51
|
+
'--tmux[Use tmux]' \
|
|
52
|
+
'--psmux[Use psmux]' \
|
|
53
|
+
'--agents[Specify agents]' \
|
|
54
|
+
'--no-attach[Do not attach]' \
|
|
55
|
+
'--timeout[Set timeout]'
|
|
56
|
+
fi
|
|
57
|
+
;;
|
|
58
|
+
hub)
|
|
59
|
+
if (( CURRENT == 3 )); then
|
|
60
|
+
_describe -t hub_cmds 'hub commands' hub_cmds
|
|
61
|
+
fi
|
|
62
|
+
;;
|
|
63
|
+
doctor)
|
|
64
|
+
_arguments \
|
|
65
|
+
'--fix[Auto fix issues]' \
|
|
66
|
+
'--reset[Reset all caches]'
|
|
67
|
+
;;
|
|
68
|
+
*)
|
|
69
|
+
_arguments \
|
|
70
|
+
'--thorough[Thorough execution]' \
|
|
71
|
+
'--quick[Quick execution]' \
|
|
72
|
+
'--tmux[Use tmux]' \
|
|
73
|
+
'--psmux[Use psmux]' \
|
|
74
|
+
'--agents[Specify agents]' \
|
|
75
|
+
'--no-attach[Do not attach]' \
|
|
76
|
+
'--timeout[Set timeout]'
|
|
77
|
+
;;
|
|
78
|
+
esac
|
|
79
|
+
;;
|
|
80
|
+
esac
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_tfx "$@"
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { nudge, deny } from "./lib/hook-utils.mjs";
|
|
6
|
+
import {
|
|
7
|
+
readStdin,
|
|
8
|
+
parseJson,
|
|
9
|
+
nowSec,
|
|
10
|
+
resolveBaseDir,
|
|
11
|
+
shouldTrackPath,
|
|
12
|
+
expectedReviewer,
|
|
13
|
+
SESSION_TTL_SEC,
|
|
14
|
+
STATE_REL_PATH,
|
|
15
|
+
} from "./lib/cross-review-utils.mjs";
|
|
16
|
+
|
|
17
|
+
function loadState(statePath) {
|
|
18
|
+
if (!existsSync(statePath)) return null;
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const state = JSON.parse(readFileSync(statePath, "utf8"));
|
|
22
|
+
const startedAt = Number(state?.session_start || 0);
|
|
23
|
+
const expired = !startedAt || nowSec() - startedAt > SESSION_TTL_SEC;
|
|
24
|
+
if (expired) {
|
|
25
|
+
try {
|
|
26
|
+
unlinkSync(statePath);
|
|
27
|
+
} catch {}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return state;
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isGitCommitCommand(command) {
|
|
37
|
+
if (typeof command !== "string") return false;
|
|
38
|
+
return /\bgit\s+commit\b/i.test(command);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function summarizePending(entries) {
|
|
42
|
+
return entries
|
|
43
|
+
.map((item) => {
|
|
44
|
+
const reviewer = item.expectedReviewer || "cross-reviewer";
|
|
45
|
+
return ` * ${item.path} (author=${item.author}, reviewer=${reviewer})`;
|
|
46
|
+
})
|
|
47
|
+
.join("\n");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function main() {
|
|
51
|
+
if (process.env.TFX_SKIP_CROSS_REVIEW === "1") {
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const raw = await readStdin();
|
|
56
|
+
if (!raw.trim()) process.exit(0);
|
|
57
|
+
|
|
58
|
+
const payload = parseJson(raw);
|
|
59
|
+
if (!payload) process.exit(0);
|
|
60
|
+
|
|
61
|
+
const toolName = payload.tool_name || "";
|
|
62
|
+
const toolInput = payload.tool_input || {};
|
|
63
|
+
|
|
64
|
+
if (toolName !== "Bash") process.exit(0);
|
|
65
|
+
if (!isGitCommitCommand(toolInput.command || "")) process.exit(0);
|
|
66
|
+
|
|
67
|
+
// cwd 전파: tracker와 동일한 resolveBaseDir 사용
|
|
68
|
+
const baseDir = resolveBaseDir(payload);
|
|
69
|
+
const statePath = join(baseDir, STATE_REL_PATH);
|
|
70
|
+
|
|
71
|
+
const state = loadState(statePath);
|
|
72
|
+
if (!state || !state.files || typeof state.files !== "object") process.exit(0);
|
|
73
|
+
|
|
74
|
+
const pending = [];
|
|
75
|
+
const selfApproved = [];
|
|
76
|
+
|
|
77
|
+
for (const [path, info] of Object.entries(state.files)) {
|
|
78
|
+
if (!shouldTrackPath(path)) continue;
|
|
79
|
+
const meta = info && typeof info === "object" ? info : {};
|
|
80
|
+
const author = String(meta.author || "").toLowerCase();
|
|
81
|
+
const reviewer = String(meta.reviewer || "").toLowerCase();
|
|
82
|
+
const reviewed = meta.reviewed === true;
|
|
83
|
+
const requiredReviewer = expectedReviewer(author);
|
|
84
|
+
|
|
85
|
+
// tracker가 설정한 self_approved 플래그 명시적 체크
|
|
86
|
+
if (meta.self_approved === true) {
|
|
87
|
+
selfApproved.push({ path, author, reviewer: meta.reviewer || author, expectedReviewer: requiredReviewer });
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (reviewed && reviewer && reviewer === author) {
|
|
92
|
+
selfApproved.push({ path, author, reviewer, expectedReviewer: requiredReviewer });
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (reviewed && requiredReviewer && reviewer && reviewer !== requiredReviewer) {
|
|
97
|
+
selfApproved.push({ path, author, reviewer, expectedReviewer: requiredReviewer });
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!reviewed) {
|
|
102
|
+
pending.push({ path, author, expectedReviewer: requiredReviewer });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (selfApproved.length > 0) {
|
|
107
|
+
const lines = selfApproved
|
|
108
|
+
.map((item) => ` * ${item.path} (author=${item.author}, reviewer=${item.reviewer}, required=${item.expectedReviewer || "n/a"})`)
|
|
109
|
+
.join("\n");
|
|
110
|
+
deny(
|
|
111
|
+
`[cross-review] self-approve 차단: 동일/비허용 reviewer가 감지되었습니다.\n${lines}\n` +
|
|
112
|
+
"규칙: author=claude -> reviewer=codex, author=codex -> reviewer=claude",
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (pending.length > 0) {
|
|
117
|
+
nudge(
|
|
118
|
+
`[cross-review] git commit 전에 교차 검증이 필요합니다.\n${summarizePending(pending)}\n` +
|
|
119
|
+
"규칙: author=claude -> reviewer=codex, author=codex -> reviewer=claude",
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
process.exit(0);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
main().catch(() => process.exit(0));
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, isAbsolute, join, relative } from "node:path";
|
|
5
|
+
import {
|
|
6
|
+
readStdin,
|
|
7
|
+
parseJson,
|
|
8
|
+
nowSec,
|
|
9
|
+
resolveBaseDir,
|
|
10
|
+
shouldTrackPath,
|
|
11
|
+
expectedReviewer,
|
|
12
|
+
SESSION_TTL_SEC,
|
|
13
|
+
STATE_REL_PATH,
|
|
14
|
+
} from "./lib/cross-review-utils.mjs";
|
|
15
|
+
|
|
16
|
+
function resolveStatePath(baseDir) {
|
|
17
|
+
return join(baseDir, STATE_REL_PATH);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function createEmptyState() {
|
|
21
|
+
return {
|
|
22
|
+
files: {},
|
|
23
|
+
session_start: nowSec(),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function loadState(statePath) {
|
|
28
|
+
if (!existsSync(statePath)) return createEmptyState();
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const parsed = JSON.parse(readFileSync(statePath, "utf8"));
|
|
32
|
+
const sessionStart = Number(parsed?.session_start || 0);
|
|
33
|
+
const expired = !sessionStart || nowSec() - sessionStart > SESSION_TTL_SEC;
|
|
34
|
+
if (expired) {
|
|
35
|
+
try {
|
|
36
|
+
unlinkSync(statePath);
|
|
37
|
+
} catch {}
|
|
38
|
+
return createEmptyState();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
files: parsed?.files && typeof parsed.files === "object" ? parsed.files : {},
|
|
43
|
+
session_start: sessionStart,
|
|
44
|
+
};
|
|
45
|
+
} catch {
|
|
46
|
+
return createEmptyState();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function saveState(statePath, state) {
|
|
51
|
+
mkdirSync(dirname(statePath), { recursive: true });
|
|
52
|
+
writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\n`, "utf8");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function normalizePath(filePath, baseDir) {
|
|
56
|
+
if (typeof filePath !== "string" || !filePath.trim()) return "";
|
|
57
|
+
|
|
58
|
+
const raw = filePath.trim();
|
|
59
|
+
let normalized = raw;
|
|
60
|
+
|
|
61
|
+
if (isAbsolute(raw)) {
|
|
62
|
+
const relPath = relative(baseDir, raw);
|
|
63
|
+
if (relPath.startsWith("..")) return "";
|
|
64
|
+
normalized = relPath;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return normalized.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function extractFilePath(toolInput) {
|
|
71
|
+
if (!toolInput || typeof toolInput !== "object") return "";
|
|
72
|
+
const candidate = toolInput.file_path ?? toolInput.path ?? toolInput.filePath ?? "";
|
|
73
|
+
return typeof candidate === "string" ? candidate : "";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function extractCandidatePaths(payload, baseDir) {
|
|
77
|
+
const candidates = new Set();
|
|
78
|
+
|
|
79
|
+
const looksLikePath = (value) => {
|
|
80
|
+
if (typeof value !== "string") return false;
|
|
81
|
+
const trimmed = value.trim();
|
|
82
|
+
if (!trimmed || /\s/.test(trimmed)) return false;
|
|
83
|
+
if (trimmed.length > 260) return false;
|
|
84
|
+
if (!trimmed.includes(".") && !trimmed.includes("/") && !trimmed.includes("\\")) return false;
|
|
85
|
+
return /^[./\\A-Za-z0-9_-]/.test(trimmed);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const addPath = (value) => {
|
|
89
|
+
if (!looksLikePath(value)) return;
|
|
90
|
+
const normalized = normalizePath(value, baseDir);
|
|
91
|
+
if (shouldTrackPath(normalized)) candidates.add(normalized);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const scanValue = (value, depth = 0) => {
|
|
95
|
+
if (depth > 3 || value == null) return;
|
|
96
|
+
if (typeof value === "string") {
|
|
97
|
+
addPath(value);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (Array.isArray(value)) {
|
|
101
|
+
for (const item of value) scanValue(item, depth + 1);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (typeof value !== "object") return;
|
|
105
|
+
|
|
106
|
+
for (const [key, child] of Object.entries(value)) {
|
|
107
|
+
const keyLower = key.toLowerCase();
|
|
108
|
+
if (keyLower.includes("file") || keyLower.includes("path")) {
|
|
109
|
+
scanValue(child, depth + 1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
addPath(extractFilePath(payload?.tool_input));
|
|
115
|
+
|
|
116
|
+
scanValue(payload?.tool_response);
|
|
117
|
+
scanValue(payload?.tool_output);
|
|
118
|
+
scanValue(payload?.result);
|
|
119
|
+
scanValue(payload?.output);
|
|
120
|
+
|
|
121
|
+
return [...candidates];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function collectStrings(value, out = [], depth = 0) {
|
|
125
|
+
if (depth > 4) return out;
|
|
126
|
+
if (typeof value === "string") {
|
|
127
|
+
out.push(value);
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
if (!value || typeof value !== "object") return out;
|
|
131
|
+
if (Array.isArray(value)) {
|
|
132
|
+
for (const item of value) collectStrings(item, out, depth + 1);
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
for (const key of Object.keys(value)) {
|
|
137
|
+
collectStrings(value[key], out, depth + 1);
|
|
138
|
+
}
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function detectCliActor(payload) {
|
|
143
|
+
const lines = collectStrings(payload).join("\n");
|
|
144
|
+
const match = lines.match(/\bcli\s*[:=]\s*(claude|codex|gemini)\b/i);
|
|
145
|
+
return match ? match[1].toLowerCase() : "";
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function detectAuthor(payload) {
|
|
149
|
+
const actor = detectCliActor(payload);
|
|
150
|
+
if (actor) return actor;
|
|
151
|
+
return "claude";
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function applyReviewer(state, reviewer, ts) {
|
|
155
|
+
for (const [filePath, meta] of Object.entries(state.files)) {
|
|
156
|
+
if (!meta || typeof meta !== "object") continue;
|
|
157
|
+
if (!shouldTrackPath(filePath)) continue;
|
|
158
|
+
|
|
159
|
+
const author = String(meta.author || "").toLowerCase();
|
|
160
|
+
const expected = expectedReviewer(author);
|
|
161
|
+
|
|
162
|
+
if (expected && reviewer === expected) {
|
|
163
|
+
meta.reviewed = true;
|
|
164
|
+
meta.reviewer = reviewer;
|
|
165
|
+
meta.reviewed_ts = ts;
|
|
166
|
+
delete meta.self_approved;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (reviewer === author) {
|
|
171
|
+
meta.reviewed = false;
|
|
172
|
+
meta.reviewer = reviewer;
|
|
173
|
+
meta.reviewed_ts = ts;
|
|
174
|
+
meta.self_approved = true;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function main() {
|
|
180
|
+
if (process.env.TFX_SKIP_CROSS_REVIEW === "1") {
|
|
181
|
+
process.exit(0);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const raw = await readStdin();
|
|
185
|
+
if (!raw.trim()) {
|
|
186
|
+
process.exit(0);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const payload = parseJson(raw);
|
|
190
|
+
if (!payload) {
|
|
191
|
+
process.exit(0);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const baseDir = resolveBaseDir(payload);
|
|
195
|
+
const statePath = resolveStatePath(baseDir);
|
|
196
|
+
const state = loadState(statePath);
|
|
197
|
+
const toolName = payload.tool_name || "";
|
|
198
|
+
const ts = nowSec();
|
|
199
|
+
let changed = false;
|
|
200
|
+
|
|
201
|
+
if (toolName === "Edit" || toolName === "Write") {
|
|
202
|
+
const toolInput = payload.tool_input || {};
|
|
203
|
+
const normalizedPath = normalizePath(extractFilePath(toolInput), baseDir);
|
|
204
|
+
if (shouldTrackPath(normalizedPath)) {
|
|
205
|
+
state.files[normalizedPath] = {
|
|
206
|
+
author: detectAuthor(payload),
|
|
207
|
+
ts,
|
|
208
|
+
reviewed: false,
|
|
209
|
+
};
|
|
210
|
+
changed = true;
|
|
211
|
+
}
|
|
212
|
+
} else if (toolName === "Bash") {
|
|
213
|
+
const actor = detectCliActor(payload);
|
|
214
|
+
if (actor) {
|
|
215
|
+
const paths = extractCandidatePaths(payload, baseDir);
|
|
216
|
+
if (paths.length > 0) {
|
|
217
|
+
for (const path of paths) {
|
|
218
|
+
state.files[path] = {
|
|
219
|
+
author: actor,
|
|
220
|
+
ts,
|
|
221
|
+
reviewed: false,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
applyReviewer(state, actor, ts);
|
|
226
|
+
}
|
|
227
|
+
changed = true;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (changed) {
|
|
232
|
+
saveState(statePath, state);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
process.exit(0);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
main().catch(() => process.exit(0));
|