triflux 10.4.0 → 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/.claude-plugin/marketplace.json +34 -0
- package/.claude-plugin/plugin.json +22 -0
- 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/config/mcp-registry.json +29 -0
- 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 +267 -256
- 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 +96 -58
- package/hub/adaptive-memory.mjs +156 -42
- package/hub/adaptive.mjs +60 -31
- package/hub/assign-callbacks.mjs +163 -126
- 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 +14 -14
- package/hub/delegator/service.mjs +88 -64
- package/hub/delegator/tool-definitions.mjs +35 -35
- package/hub/fullcycle.mjs +33 -17
- package/hub/gemini-adapter.mjs +69 -94
- package/hub/hitl.mjs +202 -143
- 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 +924 -791
- 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 +127 -61
- 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 +57 -37
- package/hub/team/cli/commands/control.mjs +29 -8
- package/hub/team/cli/commands/debug.mjs +95 -74
- package/hub/team/cli/commands/focus.mjs +80 -53
- 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 +31 -24
- 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 +50 -40
- package/hub/team/cli/commands/start/start-mux.mjs +122 -73
- package/hub/team/cli/commands/start/start-wt.mjs +86 -69
- 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 +13 -13
- package/hub/team/cli/index.mjs +2 -2
- package/hub/team/cli/manifest.mjs +38 -8
- package/hub/team/cli/render.mjs +52 -30
- package/hub/team/cli/services/attach-fallback.mjs +74 -54
- package/hub/team/cli/services/hub-client.mjs +42 -14
- package/hub/team/cli/services/member-selector.mjs +37 -30
- package/hub/team/cli/services/native-control.mjs +143 -116
- 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 +40 -30
- 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 +120 -77
- package/hub/team/orchestrator.mjs +166 -161
- 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 +675 -611
- package/hub/team/shared.mjs +13 -13
- 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 +411 -368
- package/hub/workers/claude-worker.mjs +162 -118
- package/hub/workers/codex-mcp.mjs +558 -507
- package/hub/workers/delegator-mcp.mjs +507 -333
- package/hub/workers/factory.mjs +21 -21
- 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 +56 -21
- 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/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 +139 -120
- package/scripts/keyword-detector.mjs +293 -272
- package/scripts/keyword-rules-expander.mjs +538 -521
- 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 +355 -118
- 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 +669 -554
- 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 +61 -57
- package/scripts/tfx-batch-stats.mjs +117 -96
- 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/idle-notif-cooldown.json +1 -1
- package/skills/.omc/state/last-tool-error.json +1 -1
- package/skills/.omc/state/subagent-tracking.json +1 -1
- 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-workspace/skill-snapshot/tfx-auto-codex/SKILL.md → tfx-auto-codex/SKILL.md.tmpl} +33 -4
- 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-workspace/skill-snapshot/tfx-gemini/SKILL.md → tfx-gemini/SKILL.md.tmpl} +16 -7
- 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 -16
- 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 +457 -0
- package/tui/core.mjs +266 -0
- package/tui/doctor.mjs +375 -0
- package/tui/gemini-profile.mjs +299 -0
- package/tui/monitor-data.mjs +152 -0
- package/tui/monitor.mjs +339 -0
- package/tui/setup.mjs +533 -0
- package/CLAUDE.md +0 -193
- package/references/hosts.json +0 -33
- package/skills/tfx-workspace/async-tests/run-tests.sh +0 -203
- package/skills/tfx-workspace/evals/evals.json +0 -79
- package/skills/tfx-workspace/iteration-1/benchmark.json +0 -162
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +0 -11
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +0 -9
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +0 -154
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +0 -9
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +0 -126
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +0 -11
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +0 -9
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +0 -119
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +0 -9
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +0 -115
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +0 -10
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +0 -8
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +0 -86
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +0 -8
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +0 -81
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +0 -12
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +0 -10
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +0 -316
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +0 -10
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +0 -352
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/review.html +0 -1325
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +0 -12
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +0 -10
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +0 -97
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +0 -10
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +0 -94
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +0 -12
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +0 -10
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +0 -209
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +0 -10
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +0 -193
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-2/benchmark.json +0 -62
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +0 -13
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +0 -11
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +0 -382
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +0 -11
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +0 -333
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-2/review.html +0 -1325
- package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +0 -217
- package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +0 -65
- package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +0 -94
- package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +0 -133
- package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +0 -426
- package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +0 -101
package/hub/memory-doctor.mjs
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
copyFileSync,
|
|
3
|
+
existsSync,
|
|
4
|
+
lstatSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
readdirSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
renameSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { basename, join, resolve } from "node:path";
|
|
3
12
|
|
|
4
13
|
// ── Constants ──
|
|
5
14
|
|
|
6
|
-
const SEVERITY = Object.freeze({ P0:
|
|
15
|
+
const SEVERITY = Object.freeze({ P0: "P0", P1: "P1", P2: "P2" });
|
|
7
16
|
const WEIGHT = Object.freeze({ P0: 2.0, P1: 0.5, P2: 0.2 });
|
|
8
17
|
|
|
9
18
|
const MEMORY_LINK_RE = /\[([^\]]*)\]\(([^)]+\.md)\)/gu;
|
|
@@ -27,34 +36,46 @@ const LATIN_RE = /[a-zA-Z]{3,}/u;
|
|
|
27
36
|
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
|
|
28
37
|
|
|
29
38
|
function parseFrontmatter(content) {
|
|
30
|
-
const match = FRONTMATTER_RE.exec(String(content ||
|
|
39
|
+
const match = FRONTMATTER_RE.exec(String(content || ""));
|
|
31
40
|
if (!match) return { found: false, fields: {}, body: content };
|
|
32
41
|
|
|
33
42
|
const fields = {};
|
|
34
|
-
for (const line of match[1].split(
|
|
35
|
-
const sep = line.indexOf(
|
|
43
|
+
for (const line of match[1].split("\n")) {
|
|
44
|
+
const sep = line.indexOf(":");
|
|
36
45
|
if (sep === -1) continue;
|
|
37
46
|
const key = line.slice(0, sep).trim();
|
|
38
47
|
const val = line.slice(sep + 1).trim();
|
|
39
48
|
if (key) fields[key] = val;
|
|
40
49
|
}
|
|
41
|
-
return {
|
|
50
|
+
return {
|
|
51
|
+
found: true,
|
|
52
|
+
fields,
|
|
53
|
+
body: String(content).slice(match[0].length).trim(),
|
|
54
|
+
};
|
|
42
55
|
}
|
|
43
56
|
|
|
44
57
|
function safeReadFile(filePath) {
|
|
45
58
|
try {
|
|
46
|
-
return existsSync(filePath) ? readFileSync(filePath,
|
|
47
|
-
} catch {
|
|
59
|
+
return existsSync(filePath) ? readFileSync(filePath, "utf8") : null;
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
48
63
|
}
|
|
49
64
|
|
|
50
65
|
function safeReadDir(dirPath) {
|
|
51
66
|
try {
|
|
52
67
|
return existsSync(dirPath) ? readdirSync(dirPath) : [];
|
|
53
|
-
} catch {
|
|
68
|
+
} catch {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
54
71
|
}
|
|
55
72
|
|
|
56
73
|
function isCI() {
|
|
57
|
-
return
|
|
74
|
+
return (
|
|
75
|
+
process.env.CI === "true" ||
|
|
76
|
+
process.env.DOCKER === "true" ||
|
|
77
|
+
(process.env.TFX_POSTINSTALL_FIX !== "1" && process.env.CI != null)
|
|
78
|
+
);
|
|
58
79
|
}
|
|
59
80
|
|
|
60
81
|
function makeCheckResult(id, name, severity, autofix, issues, error = null) {
|
|
@@ -72,10 +93,16 @@ function makeCheckResult(id, name, severity, autofix, issues, error = null) {
|
|
|
72
93
|
// ── Check Functions ──
|
|
73
94
|
|
|
74
95
|
function checkOrphanFiles(memoryDir) {
|
|
75
|
-
const indexPath = join(memoryDir,
|
|
96
|
+
const indexPath = join(memoryDir, "MEMORY.md");
|
|
76
97
|
const indexContent = safeReadFile(indexPath);
|
|
77
98
|
if (indexContent == null) {
|
|
78
|
-
return makeCheckResult(
|
|
99
|
+
return makeCheckResult(
|
|
100
|
+
"orphan-files",
|
|
101
|
+
"고아 파일 감지",
|
|
102
|
+
SEVERITY.P0,
|
|
103
|
+
true,
|
|
104
|
+
[],
|
|
105
|
+
);
|
|
79
106
|
}
|
|
80
107
|
|
|
81
108
|
const indexed = new Set();
|
|
@@ -85,18 +112,25 @@ function checkOrphanFiles(memoryDir) {
|
|
|
85
112
|
indexed.add(m[2]);
|
|
86
113
|
}
|
|
87
114
|
|
|
88
|
-
const diskFiles = safeReadDir(memoryDir)
|
|
89
|
-
|
|
115
|
+
const diskFiles = safeReadDir(memoryDir).filter(
|
|
116
|
+
(f) => f.endsWith(".md") && f !== "MEMORY.md",
|
|
117
|
+
);
|
|
90
118
|
|
|
91
119
|
const orphans = diskFiles
|
|
92
|
-
.filter(f => !indexed.has(f))
|
|
93
|
-
.map(f => ({ file: f, detail:
|
|
94
|
-
|
|
95
|
-
return makeCheckResult(
|
|
120
|
+
.filter((f) => !indexed.has(f))
|
|
121
|
+
.map((f) => ({ file: f, detail: "MEMORY.md 인덱스에 미등록" }));
|
|
122
|
+
|
|
123
|
+
return makeCheckResult(
|
|
124
|
+
"orphan-files",
|
|
125
|
+
"고아 파일 감지",
|
|
126
|
+
SEVERITY.P0,
|
|
127
|
+
true,
|
|
128
|
+
orphans,
|
|
129
|
+
);
|
|
96
130
|
}
|
|
97
131
|
|
|
98
132
|
function checkPathsYamlBug(rulesDir) {
|
|
99
|
-
const files = safeReadDir(rulesDir).filter(f => f.endsWith(
|
|
133
|
+
const files = safeReadDir(rulesDir).filter((f) => f.endsWith(".md"));
|
|
100
134
|
const issues = [];
|
|
101
135
|
|
|
102
136
|
for (const file of files) {
|
|
@@ -108,46 +142,73 @@ function checkPathsYamlBug(rulesDir) {
|
|
|
108
142
|
|
|
109
143
|
const rawFm = FRONTMATTER_RE.exec(content);
|
|
110
144
|
if (rawFm && PATHS_YAML_RE.test(rawFm[1])) {
|
|
111
|
-
issues.push({
|
|
145
|
+
issues.push({
|
|
146
|
+
file,
|
|
147
|
+
detail: "paths: YAML 배열 사용 (동작 안 함 — globs: CSV로 변환 필요)",
|
|
148
|
+
});
|
|
112
149
|
}
|
|
113
150
|
}
|
|
114
151
|
|
|
115
|
-
return makeCheckResult(
|
|
152
|
+
return makeCheckResult(
|
|
153
|
+
"paths-yaml-bug",
|
|
154
|
+
"paths: YAML 배열 버그",
|
|
155
|
+
SEVERITY.P0,
|
|
156
|
+
true,
|
|
157
|
+
issues,
|
|
158
|
+
);
|
|
116
159
|
}
|
|
117
160
|
|
|
118
161
|
function checkTrifluxResidue(projectDir, claudeDir) {
|
|
119
162
|
const issues = [];
|
|
120
163
|
|
|
121
|
-
const omcState = join(projectDir,
|
|
164
|
+
const omcState = join(projectDir, ".omc", "state");
|
|
122
165
|
if (existsSync(omcState)) {
|
|
123
166
|
const now = Date.now();
|
|
124
167
|
const staleThreshold = 7 * 24 * 60 * 60 * 1000;
|
|
125
|
-
for (const f of safeReadDir(omcState).filter(f => f.endsWith(
|
|
168
|
+
for (const f of safeReadDir(omcState).filter((f) => f.endsWith(".json"))) {
|
|
126
169
|
try {
|
|
127
170
|
const stat = lstatSync(join(omcState, f));
|
|
128
171
|
if (now - stat.mtimeMs > staleThreshold) {
|
|
129
|
-
issues.push({
|
|
172
|
+
issues.push({
|
|
173
|
+
file: `.omc/state/${f}`,
|
|
174
|
+
detail: "7일 이상 된 stale state 파일",
|
|
175
|
+
});
|
|
130
176
|
}
|
|
131
|
-
} catch {
|
|
177
|
+
} catch {
|
|
178
|
+
/* skip */
|
|
179
|
+
}
|
|
132
180
|
}
|
|
133
181
|
}
|
|
134
182
|
|
|
135
|
-
const scriptsDir = join(claudeDir,
|
|
183
|
+
const scriptsDir = join(claudeDir, "scripts");
|
|
136
184
|
for (const f of safeReadDir(scriptsDir)) {
|
|
137
185
|
try {
|
|
138
186
|
const fullPath = join(scriptsDir, f);
|
|
139
187
|
const stat = lstatSync(fullPath);
|
|
140
188
|
if (stat.isSymbolicLink() && !existsSync(fullPath)) {
|
|
141
|
-
issues.push({
|
|
189
|
+
issues.push({
|
|
190
|
+
file: `~/.claude/scripts/${f}`,
|
|
191
|
+
detail: "깨진 심볼릭 링크",
|
|
192
|
+
});
|
|
142
193
|
}
|
|
143
|
-
} catch {
|
|
194
|
+
} catch {
|
|
195
|
+
/* skip */
|
|
196
|
+
}
|
|
144
197
|
}
|
|
145
198
|
|
|
146
|
-
return makeCheckResult(
|
|
199
|
+
return makeCheckResult(
|
|
200
|
+
"triflux-residue",
|
|
201
|
+
"기존 triflux 잔재",
|
|
202
|
+
SEVERITY.P0,
|
|
203
|
+
true,
|
|
204
|
+
issues,
|
|
205
|
+
);
|
|
147
206
|
}
|
|
148
207
|
|
|
149
208
|
function checkRuleViolation(memoryDir) {
|
|
150
|
-
const files = safeReadDir(memoryDir).filter(
|
|
209
|
+
const files = safeReadDir(memoryDir).filter(
|
|
210
|
+
(f) => f.endsWith(".md") && f !== "MEMORY.md",
|
|
211
|
+
);
|
|
151
212
|
const issues = [];
|
|
152
213
|
|
|
153
214
|
for (const file of files) {
|
|
@@ -156,17 +217,25 @@ function checkRuleViolation(memoryDir) {
|
|
|
156
217
|
|
|
157
218
|
for (const pattern of RULE_VIOLATION_PATTERNS) {
|
|
158
219
|
if (pattern.test(content)) {
|
|
159
|
-
issues.push({ file, detail:
|
|
220
|
+
issues.push({ file, detail: "코드에서 도출 가능한 정보 포함" });
|
|
160
221
|
break;
|
|
161
222
|
}
|
|
162
223
|
}
|
|
163
224
|
}
|
|
164
225
|
|
|
165
|
-
return makeCheckResult(
|
|
226
|
+
return makeCheckResult(
|
|
227
|
+
"rule-violation",
|
|
228
|
+
"저장 규칙 위반",
|
|
229
|
+
SEVERITY.P1,
|
|
230
|
+
false,
|
|
231
|
+
issues,
|
|
232
|
+
);
|
|
166
233
|
}
|
|
167
234
|
|
|
168
235
|
function checkStaleReferences(memoryDir, projectDir) {
|
|
169
|
-
const files = safeReadDir(memoryDir).filter(
|
|
236
|
+
const files = safeReadDir(memoryDir).filter(
|
|
237
|
+
(f) => f.endsWith(".md") && f !== "MEMORY.md",
|
|
238
|
+
);
|
|
170
239
|
const issues = [];
|
|
171
240
|
const fileRefRe = /(?:^|\s)([a-zA-Z][\w/.%-]+\.[a-z]{1,5})(?::(\d+))?/gu;
|
|
172
241
|
|
|
@@ -178,19 +247,32 @@ function checkStaleReferences(memoryDir, projectDir) {
|
|
|
178
247
|
const re = new RegExp(fileRefRe.source, fileRefRe.flags);
|
|
179
248
|
while ((m = re.exec(content)) !== null) {
|
|
180
249
|
const refPath = m[1];
|
|
181
|
-
if (
|
|
250
|
+
if (
|
|
251
|
+
refPath.startsWith("http") ||
|
|
252
|
+
refPath.startsWith("node:") ||
|
|
253
|
+
refPath.includes("node_modules")
|
|
254
|
+
)
|
|
255
|
+
continue;
|
|
182
256
|
const fullPath = resolve(projectDir, refPath);
|
|
183
|
-
if (!existsSync(fullPath) && refPath.includes(
|
|
257
|
+
if (!existsSync(fullPath) && refPath.includes("/")) {
|
|
184
258
|
issues.push({ file, detail: `존재하지 않는 파일 참조: ${refPath}` });
|
|
185
259
|
}
|
|
186
260
|
}
|
|
187
261
|
}
|
|
188
262
|
|
|
189
|
-
return makeCheckResult(
|
|
263
|
+
return makeCheckResult(
|
|
264
|
+
"stale-references",
|
|
265
|
+
"stale 참조",
|
|
266
|
+
SEVERITY.P1,
|
|
267
|
+
false,
|
|
268
|
+
issues,
|
|
269
|
+
);
|
|
190
270
|
}
|
|
191
271
|
|
|
192
272
|
function checkLanguageInconsistency(memoryDir) {
|
|
193
|
-
const files = safeReadDir(memoryDir).filter(
|
|
273
|
+
const files = safeReadDir(memoryDir).filter(
|
|
274
|
+
(f) => f.endsWith(".md") && f !== "MEMORY.md",
|
|
275
|
+
);
|
|
194
276
|
const issues = [];
|
|
195
277
|
|
|
196
278
|
let koCount = 0;
|
|
@@ -202,16 +284,16 @@ function checkLanguageInconsistency(memoryDir) {
|
|
|
202
284
|
const fm = parseFrontmatter(content);
|
|
203
285
|
if (!fm.found) continue;
|
|
204
286
|
|
|
205
|
-
const nameVal = fm.fields.name ||
|
|
206
|
-
const descVal = fm.fields.description ||
|
|
287
|
+
const nameVal = fm.fields.name || "";
|
|
288
|
+
const descVal = fm.fields.description || "";
|
|
207
289
|
const combined = `${nameVal} ${descVal}`;
|
|
208
290
|
|
|
209
291
|
if (HANGUL_RE.test(combined)) koCount++;
|
|
210
292
|
else if (LATIN_RE.test(combined)) enCount++;
|
|
211
293
|
}
|
|
212
294
|
|
|
213
|
-
const majorLang = koCount >= enCount ?
|
|
214
|
-
const isKoMajor = majorLang ===
|
|
295
|
+
const majorLang = koCount >= enCount ? "ko" : "en";
|
|
296
|
+
const isKoMajor = majorLang === "ko";
|
|
215
297
|
|
|
216
298
|
for (const file of files) {
|
|
217
299
|
const content = safeReadFile(join(memoryDir, file));
|
|
@@ -219,7 +301,7 @@ function checkLanguageInconsistency(memoryDir) {
|
|
|
219
301
|
const fm = parseFrontmatter(content);
|
|
220
302
|
if (!fm.found || !fm.fields.name) continue;
|
|
221
303
|
|
|
222
|
-
const combined = `${fm.fields.name} ${fm.fields.description ||
|
|
304
|
+
const combined = `${fm.fields.name} ${fm.fields.description || ""}`;
|
|
223
305
|
const hasKo = HANGUL_RE.test(combined);
|
|
224
306
|
const hasEn = LATIN_RE.test(combined);
|
|
225
307
|
|
|
@@ -230,47 +312,75 @@ function checkLanguageInconsistency(memoryDir) {
|
|
|
230
312
|
}
|
|
231
313
|
}
|
|
232
314
|
|
|
233
|
-
return makeCheckResult(
|
|
315
|
+
return makeCheckResult(
|
|
316
|
+
"language-inconsistency",
|
|
317
|
+
"언어 혼용",
|
|
318
|
+
SEVERITY.P2,
|
|
319
|
+
false,
|
|
320
|
+
issues,
|
|
321
|
+
);
|
|
234
322
|
}
|
|
235
323
|
|
|
236
324
|
function checkOversizedFiles(memoryDir) {
|
|
237
|
-
const files = safeReadDir(memoryDir).filter(
|
|
325
|
+
const files = safeReadDir(memoryDir).filter(
|
|
326
|
+
(f) => f.endsWith(".md") && f !== "MEMORY.md",
|
|
327
|
+
);
|
|
238
328
|
const issues = [];
|
|
239
329
|
|
|
240
330
|
for (const file of files) {
|
|
241
331
|
const content = safeReadFile(join(memoryDir, file));
|
|
242
332
|
if (content == null) continue;
|
|
243
|
-
const lineCount = content.split(
|
|
333
|
+
const lineCount = content.split("\n").length;
|
|
244
334
|
if (lineCount > 50) {
|
|
245
335
|
issues.push({ file, detail: `${lineCount}줄 (50줄 초과)` });
|
|
246
336
|
}
|
|
247
337
|
}
|
|
248
338
|
|
|
249
|
-
return makeCheckResult(
|
|
339
|
+
return makeCheckResult(
|
|
340
|
+
"oversized-files",
|
|
341
|
+
"과대 파일",
|
|
342
|
+
SEVERITY.P2,
|
|
343
|
+
false,
|
|
344
|
+
issues,
|
|
345
|
+
);
|
|
250
346
|
}
|
|
251
347
|
|
|
252
348
|
function checkMissingUserMemory(memoryDir) {
|
|
253
|
-
const files = safeReadDir(memoryDir).filter(
|
|
349
|
+
const files = safeReadDir(memoryDir).filter(
|
|
350
|
+
(f) => f.endsWith(".md") && f !== "MEMORY.md",
|
|
351
|
+
);
|
|
254
352
|
const issues = [];
|
|
255
353
|
|
|
256
|
-
const hasUserType = files.some(file => {
|
|
354
|
+
const hasUserType = files.some((file) => {
|
|
257
355
|
const content = safeReadFile(join(memoryDir, file));
|
|
258
356
|
if (content == null) return false;
|
|
259
357
|
const fm = parseFrontmatter(content);
|
|
260
|
-
return fm.found && fm.fields.type ===
|
|
358
|
+
return fm.found && fm.fields.type === "user";
|
|
261
359
|
});
|
|
262
360
|
|
|
263
361
|
if (!hasUserType && files.length > 0) {
|
|
264
|
-
issues.push({
|
|
362
|
+
issues.push({
|
|
363
|
+
file: "memory/",
|
|
364
|
+
detail: "type: user 메모리 파일 없음 — 사용자 프로필 미등록",
|
|
365
|
+
});
|
|
265
366
|
}
|
|
266
367
|
|
|
267
|
-
return makeCheckResult(
|
|
368
|
+
return makeCheckResult(
|
|
369
|
+
"missing-user-memory",
|
|
370
|
+
"user 타입 메모리 부재",
|
|
371
|
+
SEVERITY.P2,
|
|
372
|
+
true,
|
|
373
|
+
issues,
|
|
374
|
+
);
|
|
268
375
|
}
|
|
269
376
|
|
|
270
377
|
// ── Fix Functions ──
|
|
271
378
|
|
|
272
379
|
function createBackup(files, backupDir) {
|
|
273
|
-
const timestamp = new Date()
|
|
380
|
+
const timestamp = new Date()
|
|
381
|
+
.toISOString()
|
|
382
|
+
.replace(/[:.]/gu, "-")
|
|
383
|
+
.slice(0, 19);
|
|
274
384
|
const dir = join(backupDir, timestamp);
|
|
275
385
|
mkdirSync(dir, { recursive: true });
|
|
276
386
|
|
|
@@ -282,32 +392,44 @@ function createBackup(files, backupDir) {
|
|
|
282
392
|
backed.push({ src, dst });
|
|
283
393
|
}
|
|
284
394
|
|
|
285
|
-
writeFileSync(
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
395
|
+
writeFileSync(
|
|
396
|
+
join(dir, "manifest.json"),
|
|
397
|
+
JSON.stringify(
|
|
398
|
+
{
|
|
399
|
+
timestamp,
|
|
400
|
+
files: backed,
|
|
401
|
+
restore: "tfx memory-doctor --undo",
|
|
402
|
+
},
|
|
403
|
+
null,
|
|
404
|
+
2,
|
|
405
|
+
),
|
|
406
|
+
"utf8",
|
|
407
|
+
);
|
|
290
408
|
|
|
291
409
|
return { dir, files: backed };
|
|
292
410
|
}
|
|
293
411
|
|
|
294
412
|
function fixOrphanFiles(memoryDir, issues) {
|
|
295
|
-
const indexPath = join(memoryDir,
|
|
413
|
+
const indexPath = join(memoryDir, "MEMORY.md");
|
|
296
414
|
const content = safeReadFile(indexPath);
|
|
297
|
-
if (content == null) return { action:
|
|
415
|
+
if (content == null) return { action: "skipped", fixed: [] };
|
|
298
416
|
|
|
299
417
|
const additions = issues.map(({ file }) => {
|
|
300
|
-
const fm = parseFrontmatter(safeReadFile(join(memoryDir, file)) ||
|
|
301
|
-
const desc = fm.fields.description ||
|
|
418
|
+
const fm = parseFrontmatter(safeReadFile(join(memoryDir, file)) || "");
|
|
419
|
+
const desc = fm.fields.description || "(설명 없음)";
|
|
302
420
|
return `- [${fm.fields.name || file}](${file}) — ${desc}`;
|
|
303
421
|
});
|
|
304
422
|
|
|
305
|
-
if (additions.length === 0) return { action:
|
|
423
|
+
if (additions.length === 0) return { action: "unchanged", fixed: [] };
|
|
306
424
|
|
|
307
|
-
const separator = content.endsWith(
|
|
308
|
-
writeFileSync(
|
|
425
|
+
const separator = content.endsWith("\n") ? "" : "\n";
|
|
426
|
+
writeFileSync(
|
|
427
|
+
indexPath,
|
|
428
|
+
`${content}${separator}${additions.join("\n")}\n`,
|
|
429
|
+
"utf8",
|
|
430
|
+
);
|
|
309
431
|
|
|
310
|
-
return { action:
|
|
432
|
+
return { action: "fixed", fixed: issues.map((i) => i.file) };
|
|
311
433
|
}
|
|
312
434
|
|
|
313
435
|
function fixPathsYamlBug(rulesDir, issues) {
|
|
@@ -326,21 +448,24 @@ function fixPathsYamlBug(rulesDir, issues) {
|
|
|
326
448
|
if (!pathsMatch) continue;
|
|
327
449
|
|
|
328
450
|
const values = [];
|
|
329
|
-
for (const line of pathsMatch[0].split(
|
|
330
|
-
const trimmed = line
|
|
331
|
-
|
|
451
|
+
for (const line of pathsMatch[0].split("\n")) {
|
|
452
|
+
const trimmed = line
|
|
453
|
+
.replace(/^\s*-\s*/, "")
|
|
454
|
+
.replace(/^["']|["']$/gu, "")
|
|
455
|
+
.trim();
|
|
456
|
+
if (trimmed && trimmed !== "paths:") values.push(trimmed);
|
|
332
457
|
}
|
|
333
458
|
|
|
334
459
|
if (values.length === 0) continue;
|
|
335
460
|
|
|
336
|
-
const globsLine = `globs: ${values.join(
|
|
461
|
+
const globsLine = `globs: ${values.join(", ")}`;
|
|
337
462
|
const nextFm = fmRaw.replace(pathsMatch[0], globsLine);
|
|
338
463
|
const nextContent = `---\n${nextFm}\n---${content.slice(fmMatch[0].length)}`;
|
|
339
|
-
writeFileSync(filePath, nextContent,
|
|
464
|
+
writeFileSync(filePath, nextContent, "utf8");
|
|
340
465
|
fixed.push(file);
|
|
341
466
|
}
|
|
342
467
|
|
|
343
|
-
return { action: fixed.length > 0 ?
|
|
468
|
+
return { action: fixed.length > 0 ? "fixed" : "unchanged", fixed };
|
|
344
469
|
}
|
|
345
470
|
|
|
346
471
|
function fixTrifluxResidue(issues) {
|
|
@@ -348,17 +473,21 @@ function fixTrifluxResidue(issues) {
|
|
|
348
473
|
|
|
349
474
|
for (const { file } of issues) {
|
|
350
475
|
try {
|
|
351
|
-
const fullPath = resolve(
|
|
476
|
+
const fullPath = resolve(
|
|
477
|
+
file.replace(/^~\/\.claude/u, join(process.env.HOME || "", ".claude")),
|
|
478
|
+
);
|
|
352
479
|
if (!existsSync(fullPath)) continue;
|
|
353
480
|
|
|
354
|
-
const archiveDir = join(resolve(
|
|
481
|
+
const archiveDir = join(resolve(".tfx", "archive"));
|
|
355
482
|
mkdirSync(archiveDir, { recursive: true });
|
|
356
483
|
renameSync(fullPath, join(archiveDir, basename(fullPath)));
|
|
357
484
|
fixed.push(file);
|
|
358
|
-
} catch {
|
|
485
|
+
} catch {
|
|
486
|
+
/* skip unresolvable */
|
|
487
|
+
}
|
|
359
488
|
}
|
|
360
489
|
|
|
361
|
-
return { action: fixed.length > 0 ?
|
|
490
|
+
return { action: fixed.length > 0 ? "fixed" : "unchanged", fixed };
|
|
362
491
|
}
|
|
363
492
|
|
|
364
493
|
// ── Health Score ──
|
|
@@ -388,20 +517,22 @@ const ALL_CHECKS = [
|
|
|
388
517
|
];
|
|
389
518
|
|
|
390
519
|
const FIX_MAP = {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
520
|
+
"orphan-files": fixOrphanFiles,
|
|
521
|
+
"paths-yaml-bug": fixPathsYamlBug,
|
|
522
|
+
"triflux-residue": fixTrifluxResidue,
|
|
394
523
|
};
|
|
395
524
|
|
|
396
525
|
export function createMemoryDoctor(options = {}) {
|
|
397
|
-
const memoryDir = resolve(options.memoryDir ||
|
|
398
|
-
const rulesDir = resolve(options.rulesDir ||
|
|
526
|
+
const memoryDir = resolve(options.memoryDir || "");
|
|
527
|
+
const rulesDir = resolve(options.rulesDir || "");
|
|
399
528
|
const projectDir = resolve(options.projectDir || process.cwd());
|
|
400
|
-
const claudeDir = resolve(options.claudeDir ||
|
|
401
|
-
const backupDir = resolve(
|
|
529
|
+
const claudeDir = resolve(options.claudeDir || "");
|
|
530
|
+
const backupDir = resolve(
|
|
531
|
+
options.backupDir || join(projectDir, ".tfx", "backups"),
|
|
532
|
+
);
|
|
402
533
|
|
|
403
534
|
function scan() {
|
|
404
|
-
const checks = ALL_CHECKS.map(fn => {
|
|
535
|
+
const checks = ALL_CHECKS.map((fn) => {
|
|
405
536
|
try {
|
|
406
537
|
if (fn === checkOrphanFiles) return fn(memoryDir);
|
|
407
538
|
if (fn === checkPathsYamlBug) return fn(rulesDir);
|
|
@@ -413,16 +544,26 @@ export function createMemoryDoctor(options = {}) {
|
|
|
413
544
|
if (fn === checkMissingUserMemory) return fn(memoryDir);
|
|
414
545
|
return fn(memoryDir);
|
|
415
546
|
} catch (err) {
|
|
416
|
-
return makeCheckResult(
|
|
417
|
-
fn.name
|
|
547
|
+
return makeCheckResult(
|
|
548
|
+
fn.name
|
|
549
|
+
.replace("check", "")
|
|
550
|
+
.replace(/([A-Z])/gu, "-$1")
|
|
551
|
+
.toLowerCase()
|
|
552
|
+
.replace(/^-/u, ""),
|
|
553
|
+
fn.name,
|
|
554
|
+
SEVERITY.P2,
|
|
555
|
+
false,
|
|
556
|
+
[],
|
|
557
|
+
err.message,
|
|
558
|
+
);
|
|
418
559
|
}
|
|
419
560
|
});
|
|
420
561
|
|
|
421
562
|
const healthScore = computeHealthScore(checks);
|
|
422
563
|
const summary = {
|
|
423
|
-
p0: checks.filter(c => c.severity === SEVERITY.P0 && !c.passed).length,
|
|
424
|
-
p1: checks.filter(c => c.severity === SEVERITY.P1 && !c.passed).length,
|
|
425
|
-
p2: checks.filter(c => c.severity === SEVERITY.P2 && !c.passed).length,
|
|
564
|
+
p0: checks.filter((c) => c.severity === SEVERITY.P0 && !c.passed).length,
|
|
565
|
+
p1: checks.filter((c) => c.severity === SEVERITY.P1 && !c.passed).length,
|
|
566
|
+
p2: checks.filter((c) => c.severity === SEVERITY.P2 && !c.passed).length,
|
|
426
567
|
};
|
|
427
568
|
|
|
428
569
|
return Object.freeze({ checks, healthScore, summary });
|
|
@@ -430,30 +571,44 @@ export function createMemoryDoctor(options = {}) {
|
|
|
430
571
|
|
|
431
572
|
function fix(checkId, opts = {}) {
|
|
432
573
|
if (isCI() && !opts.force) {
|
|
433
|
-
return { action:
|
|
574
|
+
return { action: "skipped_ci", backup: null, fixed: [] };
|
|
434
575
|
}
|
|
435
576
|
|
|
436
577
|
const { checks } = scan();
|
|
437
|
-
const check = checks.find(c => c.id === checkId);
|
|
438
|
-
if (!check || check.passed)
|
|
439
|
-
|
|
578
|
+
const check = checks.find((c) => c.id === checkId);
|
|
579
|
+
if (!check || check.passed)
|
|
580
|
+
return { action: "no_issues", backup: null, fixed: [] };
|
|
581
|
+
if (!check.autofix)
|
|
582
|
+
return { action: "manual_only", backup: null, fixed: [] };
|
|
440
583
|
|
|
441
584
|
const fixFn = FIX_MAP[checkId];
|
|
442
|
-
if (!fixFn) return { action:
|
|
585
|
+
if (!fixFn) return { action: "no_fixer", backup: null, fixed: [] };
|
|
443
586
|
|
|
444
587
|
const affectedFiles = check.issues
|
|
445
|
-
.map(i => {
|
|
446
|
-
const candidate = join(
|
|
588
|
+
.map((i) => {
|
|
589
|
+
const candidate = join(
|
|
590
|
+
checkId === "orphan-files" ? memoryDir : rulesDir,
|
|
591
|
+
i.file,
|
|
592
|
+
);
|
|
447
593
|
return existsSync(candidate) ? candidate : null;
|
|
448
594
|
})
|
|
449
595
|
.filter(Boolean);
|
|
450
596
|
|
|
451
|
-
const indexFile =
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
597
|
+
const indexFile =
|
|
598
|
+
checkId === "orphan-files" ? join(memoryDir, "MEMORY.md") : null;
|
|
599
|
+
const backupFiles = indexFile
|
|
600
|
+
? [indexFile, ...affectedFiles]
|
|
601
|
+
: affectedFiles;
|
|
602
|
+
|
|
603
|
+
const backup =
|
|
604
|
+
backupFiles.length > 0 ? createBackup(backupFiles, backupDir) : null;
|
|
605
|
+
|
|
606
|
+
const dir =
|
|
607
|
+
checkId === "orphan-files"
|
|
608
|
+
? memoryDir
|
|
609
|
+
: checkId === "paths-yaml-bug"
|
|
610
|
+
? rulesDir
|
|
611
|
+
: projectDir;
|
|
457
612
|
const result = fixFn(dir, check.issues);
|
|
458
613
|
|
|
459
614
|
return { ...result, backup: backup?.dir || null };
|
|
@@ -465,16 +620,21 @@ export function createMemoryDoctor(options = {}) {
|
|
|
465
620
|
}
|
|
466
621
|
|
|
467
622
|
const { checks } = scan();
|
|
468
|
-
const targets = checks.filter(c => {
|
|
623
|
+
const targets = checks.filter((c) => {
|
|
469
624
|
if (opts.severity && c.severity !== opts.severity) return false;
|
|
470
625
|
return c.autofix && !c.passed;
|
|
471
626
|
});
|
|
472
627
|
|
|
473
|
-
const results = targets.map(c => ({ id: c.id, ...fix(c.id, opts) }));
|
|
628
|
+
const results = targets.map((c) => ({ id: c.id, ...fix(c.id, opts) }));
|
|
474
629
|
return { results };
|
|
475
630
|
}
|
|
476
631
|
|
|
477
|
-
return Object.freeze({
|
|
632
|
+
return Object.freeze({
|
|
633
|
+
scan,
|
|
634
|
+
fix,
|
|
635
|
+
fixAll,
|
|
636
|
+
getHealthScore: computeHealthScore,
|
|
637
|
+
});
|
|
478
638
|
}
|
|
479
639
|
|
|
480
|
-
export { computeHealthScore,
|
|
640
|
+
export { computeHealthScore, isCI, parseFrontmatter };
|